#include #include #include "packeteer/l7/dns.hpp" using namespace packeteer::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 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 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 bytes(5, 0); CHECK_FALSE(parse_dns(bytes).has_value()); } TEST_CASE("read_dns_name rejects a compression pointer") { std::vector 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 bytes(5, 0); CHECK_FALSE(dissector.summarize(bytes).has_value()); }