1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
#include <doctest/doctest.h>
#include <vector>
#include "wireframe/l7/dns.hpp"
using namespace wireframe::net;
namespace {
// "example.com" A query, id=0x129d - the same shape as the real query
// captured live over tailscale0 while testing the DNS dissector against
// tshark (id 0x129d / 4765 matched tshark's independent decode exactly).
std::vector<unsigned char> example_com_query() {
return {
0x12, 0x9d, // id = 4765
0x01, 0x00, // flags: RD=1
0x00, 0x01, // qdcount = 1
0x00, 0x00, // ancount = 0
0x00, 0x00, // nscount = 0
0x00, 0x00, // arcount = 0
7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0,
0x00, 0x01, // qtype = A
0x00, 0x01, // qclass = IN
};
}
} // namespace
TEST_CASE("parse_dns decodes a query") {
auto msg = parse_dns(example_com_query());
REQUIRE(msg.has_value());
CHECK(msg->header.id == 4765);
CHECK_FALSE(msg->header.is_response);
CHECK(msg->header.qdcount == 1);
REQUIRE(msg->question.has_value());
CHECK(msg->question->name == "example.com");
CHECK(msg->question->qtype == 1);
}
TEST_CASE("parse_dns decodes a response") {
std::vector<unsigned char> bytes = {
0x12, 0x9d,
0x81, 0x80, // flags: QR=1 (response), RD=1, RA=1
0x00, 0x01, // qdcount = 1
0x00, 0x02, // ancount = 2
0x00, 0x00,
0x00, 0x00,
7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0,
0x00, 0x01, 0x00, 0x01,
};
auto msg = parse_dns(bytes);
REQUIRE(msg.has_value());
CHECK(msg->header.is_response);
CHECK(msg->header.ancount == 2);
}
TEST_CASE("parse_dns rejects a truncated header") {
std::vector<unsigned char> bytes(5, 0);
CHECK_FALSE(parse_dns(bytes).has_value());
}
TEST_CASE("read_dns_name rejects a compression pointer") {
std::vector<unsigned char> bytes = {0xC0, 0x0C}; // pointer: unsupported by design
CHECK_FALSE(read_dns_name(bytes, 0).has_value());
}
TEST_CASE("DnsDissector claims port 53 and its summary matches parse_dns") {
DnsDissector dissector;
CHECK(dissector.port() == kDnsPort);
auto summary = dissector.summarize(example_com_query());
REQUIRE(summary.has_value());
CHECK(summary->substr(0, 9) == "DNS query");
CHECK(summary->find("example.com") != std::string::npos);
}
TEST_CASE("DnsDissector::summarize returns nullopt for a truncated payload") {
DnsDissector dissector;
std::vector<unsigned char> bytes(5, 0);
CHECK_FALSE(dissector.summarize(bytes).has_value());
}
|