Commit 231827eb authored by Omid Azizi's avatar Omid Azizi
Browse files

DNS Tracing: Add record types and cnames to output

Summary:
More context for the user.

Now the json output will have the record type (e.g. A vs AAAA).

Also cname records (aliases) are output, similar to nslookup.

Test Plan: Test cases updated.

Reviewers: yzhao, #engineering

Reviewed By: yzhao, #engineering

Differential Revision: https://phab.corp.pixielabs.ai/D6736

GitOrigin-RevId: 50eb291676fcd4e9b27987bb973ea58903c341fb
parent 29cd17cb
Showing with 76 additions and 25 deletions
+76 -25
......@@ -92,13 +92,14 @@ TEST_F(DNSTraceTest, Capture) {
MatchesRegex(
R"(\{"txid":[0-9]+,"qr":0,"opcode":0,"aa":0,"tc":0,"rd":1,"ra":0,"ad":1,"cd":0,"rcode":0,)"
R"("num_queries":1,"num_answers":0,"num_auth":0,"num_addl":1\})"));
EXPECT_EQ(req_body, R"({"queries":[{"name":"server.dnstest.com"}]})");
EXPECT_EQ(req_body, R"({"queries":[{"name":"server.dnstest.com","type":"A"}]})");
EXPECT_THAT(
resp_hdr,
MatchesRegex(
R"(\{"txid":[0-9]+,"qr":1,"opcode":0,"aa":1,"tc":0,"rd":1,"ra":1,"ad":0,"cd":0,"rcode":0,)"
R"("num_queries":1,"num_answers":1,"num_auth":0,"num_addl":1\})"));
EXPECT_EQ(resp_body, R"({"answers":[{"name":"server.dnstest.com","addr":"192.168.32.200"}]})");
EXPECT_EQ(resp_body,
R"({"answers":[{"name":"server.dnstest.com","type":"A","addr":"192.168.32.200"}]})");
}
}
......
......@@ -21,13 +21,14 @@ namespace dns {
class PxDnsParserListener : public DnsParserListener {
public:
void onDnsRec(in_addr addr, std::string name, std::string path) override {
records_.emplace_back(
DNSRecord{{InetAddrFamily::kIPv4, addr}, std::move(name), std::move(path)});
void onDnsRec(std::string name, in_addr addr) override {
records_.emplace_back(DNSRecord{std::move(name), "", {InetAddrFamily::kIPv4, addr}});
}
void onDnsRec(in6_addr addr, std::string name, std::string path) override {
records_.emplace_back(
DNSRecord{{InetAddrFamily::kIPv6, addr}, std::move(name), std::move(path)});
void onDnsRec(std::string name, in6_addr addr) override {
records_.emplace_back(DNSRecord{std::move(name), "", {InetAddrFamily::kIPv6, addr}});
}
void onDnsRec(std::string name, std::string cname) override {
records_.emplace_back(DNSRecord{std::move(name), std::move(cname), {}});
}
std::vector<DNSRecord> records_;
......
......@@ -208,7 +208,7 @@ TEST_F(DNSParserTest, BasicReq2) {
EXPECT_EQ(frames[0].records[0].addr.AddrStr(), "0.0.0.0");
}
TEST_F(DNSParserTest, MultipleResponses) {
TEST_F(DNSParserTest, CNameAndMultipleResponses) {
auto frame_view = CreateStringView<char>(CharArrayStringView<uint8_t>(kRespFrame2));
std::deque<Frame> frames;
......@@ -223,23 +223,31 @@ TEST_F(DNSParserTest, MultipleResponses) {
EXPECT_EQ(frames[0].header.num_answers, 5);
EXPECT_EQ(frames[0].header.num_auth, 0);
EXPECT_EQ(frames[0].header.num_addl, 0);
EXPECT_EQ(frames[0].records.size(), 4);
ASSERT_EQ(frames[0].records.size(), 5);
EXPECT_EQ(frames[0].records[0].name, "www.yahoo.com");
EXPECT_EQ(frames[0].records[0].addr.family, InetAddrFamily::kIPv4);
EXPECT_EQ(frames[0].records[0].addr.AddrStr(), "98.137.11.164");
EXPECT_EQ(frames[0].records[0].addr.family, InetAddrFamily::kUnspecified);
EXPECT_EQ(frames[0].records[0].cname, "new-fp-shed.wg1.b.yahoo.com");
EXPECT_EQ(frames[0].records[1].name, "www.yahoo.com");
EXPECT_EQ(frames[0].records[1].name, "new-fp-shed.wg1.b.yahoo.com");
EXPECT_EQ(frames[0].records[1].addr.family, InetAddrFamily::kIPv4);
EXPECT_EQ(frames[0].records[1].addr.AddrStr(), "74.6.231.20");
EXPECT_EQ(frames[0].records[1].addr.AddrStr(), "98.137.11.164");
EXPECT_EQ(frames[0].records[1].cname, "");
EXPECT_EQ(frames[0].records[2].name, "www.yahoo.com");
EXPECT_EQ(frames[0].records[2].name, "new-fp-shed.wg1.b.yahoo.com");
EXPECT_EQ(frames[0].records[2].addr.family, InetAddrFamily::kIPv4);
EXPECT_EQ(frames[0].records[2].addr.AddrStr(), "74.6.231.21");
EXPECT_EQ(frames[0].records[2].addr.AddrStr(), "74.6.231.20");
EXPECT_EQ(frames[0].records[2].cname, "");
EXPECT_EQ(frames[0].records[3].name, "www.yahoo.com");
EXPECT_EQ(frames[0].records[3].name, "new-fp-shed.wg1.b.yahoo.com");
EXPECT_EQ(frames[0].records[3].addr.family, InetAddrFamily::kIPv4);
EXPECT_EQ(frames[0].records[3].addr.AddrStr(), "98.137.11.163");
EXPECT_EQ(frames[0].records[3].addr.AddrStr(), "74.6.231.21");
EXPECT_EQ(frames[0].records[3].cname, "");
EXPECT_EQ(frames[0].records[4].name, "new-fp-shed.wg1.b.yahoo.com");
EXPECT_EQ(frames[0].records[4].addr.family, InetAddrFamily::kIPv4);
EXPECT_EQ(frames[0].records[4].addr.AddrStr(), "98.137.11.163");
EXPECT_EQ(frames[0].records[4].cname, "");
}
TEST_F(DNSParserTest, IncompleteHeader) {
......
......@@ -50,6 +50,26 @@ std::string HeaderToJSONString(const DNSHeader& header) {
return std::string(sb.GetString());
}
std::string_view DNSRecordTypeName(InetAddrFamily addr_family) {
constexpr std::string_view kDNSRecordTypeA = "A";
constexpr std::string_view kDNSRecordTypeAAAA = "AAAA";
constexpr std::string_view kDNSRecordTypeUnknown = "";
std::string_view type_name = "";
switch (addr_family) {
case InetAddrFamily::kIPv4:
type_name = kDNSRecordTypeA;
break;
case InetAddrFamily::kIPv6:
type_name = kDNSRecordTypeAAAA;
break;
default:
type_name = kDNSRecordTypeUnknown;
}
return type_name;
}
void ProcessReq(const Frame& req_frame, Request* req) {
req->timestamp_ns = req_frame.timestamp_ns;
req->header = HeaderToJSONString(req_frame.header);
......@@ -64,9 +84,12 @@ void ProcessReq(const Frame& req_frame, Request* req) {
rapidjson::Value queries(rapidjson::kArrayType);
for (const auto& r : req_frame.records) {
const std::string& name = r.name;
std::string_view type_name = DNSRecordTypeName(r.addr.family);
rapidjson::Value query(rapidjson::kObjectType);
query.AddMember("name", rapidjson::StringRef(name.data(), name.size()), d.GetAllocator());
query.AddMember("type", rapidjson::StringRef(type_name.data(), type_name.size()),
d.GetAllocator());
queries.PushBack(query, d.GetAllocator());
}
......@@ -93,12 +116,26 @@ void ProcessResp(const Frame& resp_frame, Response* resp) {
rapidjson::Value answers(rapidjson::kArrayType);
for (const auto& r : resp_frame.records) {
const std::string& name = r.name;
addr_strs.push_back(r.addr.AddrStr());
const std::string& addr = addr_strs.back();
std::string_view type_name;
rapidjson::Value answer(rapidjson::kObjectType);
answer.AddMember("name", rapidjson::StringRef(name.data(), name.size()), d.GetAllocator());
answer.AddMember("addr", rapidjson::StringRef(addr.data(), addr.size()), d.GetAllocator());
if (!r.cname.empty()) {
type_name = "CNAME";
answer.AddMember("type", rapidjson::StringRef(type_name.data(), type_name.size()),
d.GetAllocator());
answer.AddMember("cname", rapidjson::StringRef(r.cname.data(), r.cname.size()),
d.GetAllocator());
} else {
addr_strs.push_back(r.addr.AddrStr());
const std::string& addr = addr_strs.back();
std::string_view type_name = DNSRecordTypeName(r.addr.family);
answer.AddMember("type", rapidjson::StringRef(type_name.data(), type_name.size()),
d.GetAllocator());
answer.AddMember("addr", rapidjson::StringRef(addr.data(), addr.size()), d.GetAllocator());
}
answers.PushBack(answer, d.GetAllocator());
}
......
......@@ -64,7 +64,7 @@ TEST(DnsStitcherTest, RecordOutput) {
ip_addr.addr = addr_tmp;
std::vector<DNSRecord> dns_records;
dns_records.push_back(DNSRecord{ip_addr, "pixie.ai", ""});
dns_records.push_back(DNSRecord{"pixie.ai", "", ip_addr});
int t = 0;
Frame req0_frame = CreateReqFrame(++t, 0);
......@@ -91,7 +91,7 @@ TEST(DnsStitcherTest, RecordOutput) {
EXPECT_EQ(record.resp.header,
R"({"txid":0,"qr":1,"opcode":0,"aa":0,"tc":0,"rd":1,"ra":1,"ad":0,"cd":0,"rcode":0,)"
R"("num_queries":1,"num_answers":1,"num_auth":0,"num_addl":0})");
EXPECT_EQ(record.resp.msg, R"({"answers":[{"name":"pixie.ai","addr":"1.2.3.4"}]})");
EXPECT_EQ(record.resp.msg, R"({"answers":[{"name":"pixie.ai","type":"A","addr":"1.2.3.4"}]})");
}
TEST(DnsStitcherTest, OutOfOrderMatching) {
......
......@@ -66,9 +66,13 @@ constexpr int kRcodeWidth = 4;
// Typically it is the answer to a query (e.g. from name->addr).
// Spec: https://www.ietf.org/rfc/rfc1035.txt
struct DNSRecord {
InetAddr addr;
std::string name;
std::string path;
// cname and addr are mutually exclusive.
// Either a record provdes a cname (an alias to another record), or it resolves the address.
// TODO(oazizi): Consider using std::variant.
std::string cname;
InetAddr addr;
};
struct Frame : public FrameBase {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment