srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_dns.cpp
blob: 9a389bb1c73d8f9dac3e17b484ce0e7054b9926f (plain) (blame)
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());
}