mDNS: Add more detailed logging

This CL adds more detailed logging to help debug ongoing issues.

bug: b/157683753
Change-Id: Ibee54a379c95773571cd3e7ec0095aa5f4ac9152
Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/2223828
Commit-Queue: Ryan Keane <rwkeane@google.com>
Reviewed-by: Jordan Bayles <jophba@chromium.org>
diff --git a/discovery/mdns/mdns_querier.cc b/discovery/mdns/mdns_querier.cc
index 800aef4..7152be2 100644
--- a/discovery/mdns/mdns_querier.cc
+++ b/discovery/mdns/mdns_querier.cc
@@ -372,9 +372,7 @@
   for (const MdnsRecord& record : message.answers()) {
     if (ShouldAnswerRecordBeProcessed(record)) {
       ProcessRecord(record);
-      OSP_DVLOG << "\tProcessing answer record for domain '"
-                << record.name().ToString() << "' of type '"
-                << record.dns_type() << "'...";
+      OSP_DVLOG << "\tProcessing answer record (" << record.ToString() << ")";
       found_relevant_records = true;
       processed_count++;
     }
@@ -385,9 +383,8 @@
   // individual records relevant to this querier to update the cache.
   for (const MdnsRecord& record : message.additional_records()) {
     if (found_relevant_records || ShouldAnswerRecordBeProcessed(record)) {
-      OSP_DVLOG << "\tProcessing additional record for domain '"
-                << record.name().ToString() << "' of type '"
-                << record.dns_type() << "'...";
+      OSP_DVLOG << "\tProcessing additional record (" << record.ToString()
+                << ")";
       ProcessRecord(record);
       processed_count++;
     }
diff --git a/discovery/mdns/mdns_receiver.cc b/discovery/mdns/mdns_receiver.cc
index b21d748..bb8634d 100644
--- a/discovery/mdns/mdns_receiver.cc
+++ b/discovery/mdns/mdns_receiver.cc
@@ -4,6 +4,8 @@
 
 #include "discovery/mdns/mdns_receiver.h"
 
+#include <utility>
+
 #include "discovery/mdns/mdns_reader.h"
 #include "util/trace_logging.h"
 
@@ -66,6 +68,7 @@
   MdnsReader reader(config_, packet.data(), packet.size());
   MdnsMessage message;
   if (!reader.Read(&message)) {
+    OSP_DVLOG << "mDNS message failed to parse...";
     return;
   }
 
@@ -74,13 +77,14 @@
       client->OnMessageReceived(message);
     }
     if (response_clients_.empty()) {
-      OSP_DVLOG << "Response message dropped. No response client registered...";
+      OSP_DVLOG
+          << "mDNS response message dropped. No response client registered...";
     }
   } else {
     if (query_callback_) {
       query_callback_(message, packet.source());
     } else {
-      OSP_DVLOG << "Query message dropped. No query client registered...";
+      OSP_DVLOG << "mDNS query message dropped. No query client registered...";
     }
   }
 }
diff --git a/discovery/mdns/mdns_records.cc b/discovery/mdns/mdns_records.cc
index a04c269..8be7fe0 100644
--- a/discovery/mdns/mdns_records.cc
+++ b/discovery/mdns/mdns_records.cc
@@ -5,6 +5,9 @@
 #include "discovery/mdns/mdns_records.h"
 
 #include <cctype>
+#include <limits>
+#include <sstream>
+#include <vector>
 
 #include "absl/strings/ascii.h"
 #include "absl/strings/match.h"
@@ -562,6 +565,34 @@
   return name_.MaxWireSize() + absl::visit(wire_size_visitor, rdata_) + 8;
 }
 
+std::string MdnsRecord::ToString() const {
+  std::stringstream ss;
+  ss << "name: '" << name_.ToString() << "'";
+  ss << ", type: " << dns_type_;
+
+  if (dns_type_ == DnsType::kPTR) {
+    const DomainName& target = absl::get<PtrRecordRdata>(rdata_).ptr_domain();
+    ss << ", target: '" << target.ToString() << "'";
+  } else if (dns_type_ == DnsType::kSRV) {
+    const DomainName& target = absl::get<SrvRecordRdata>(rdata_).target();
+    ss << ", target: '" << target.ToString() << "'";
+  } else if (dns_type_ == DnsType::kNSEC) {
+    const auto& nsec_rdata = absl::get<NsecRecordRdata>(rdata_);
+    std::vector<DnsType> types = nsec_rdata.types();
+    ss << ", representing [";
+    if (!types.empty()) {
+      auto it = types.begin();
+      ss << *it++;
+      while (it != types.end()) {
+        ss << ", " << *it++;
+      }
+      ss << "]";
+    }
+  }
+
+  return ss.str();
+}
+
 MdnsRecord CreateAddressRecord(DomainName name, const IPAddress& address) {
   Rdata rdata;
   DnsType type;
diff --git a/discovery/mdns/mdns_records.h b/discovery/mdns/mdns_records.h
index 8e9ac24..696eaf8 100644
--- a/discovery/mdns/mdns_records.h
+++ b/discovery/mdns/mdns_records.h
@@ -419,6 +419,8 @@
                       record.ttl_.count(), record.rdata_);
   }
 
+  std::string ToString() const;
+
  private:
   static bool IsValidConfig(const DomainName& name,
                             DnsType dns_type,
diff --git a/discovery/mdns/mdns_responder.cc b/discovery/mdns/mdns_responder.cc
index 3627f2f..faef9a1 100644
--- a/discovery/mdns/mdns_responder.cc
+++ b/discovery/mdns/mdns_responder.cc
@@ -513,7 +513,7 @@
   for (const auto& question : questions) {
     OSP_DVLOG << "\tProcessing mDNS Query for domain: '"
               << question.name().ToString() << "', type: '"
-              << question.dns_type() << "'";
+              << question.dns_type() << "' from '" << src << "'";
 
     // NSEC records should not be queried for.
     if (question.dns_type() == DnsType::kNSEC) {
@@ -586,18 +586,25 @@
     // method is called. Exclusive ownership cannot be gained for a record which
     // has previously been published, and if this host is the exclusive owner
     // then this method will have been called without any delay on the task
-    // runner
+    // runner.
     ApplyQueryResults(&message, record_handler_, question.name(), known_answers,
                       question.dns_type(), question.dns_class(),
                       is_exclusive_owner);
   }
 
   // Send the response only if it contains answers to the query.
+  OSP_DVLOG << "\tCompleted Processing mDNS Query for domain: '"
+            << question.name().ToString() << "', type: '" << question.dns_type()
+            << "', with " << message.answers().size() << " results:";
+  for (const auto& record : message.answers()) {
+    OSP_DVLOG << "\t\tanswer (" << record.ToString() << ")";
+  }
+  for (const auto& record : message.additional_records()) {
+    OSP_DVLOG << "\t\tadditional record ('" << record.ToString() << ")";
+  }
+
   if (!message.answers().empty()) {
-    OSP_DVLOG << "\tmDNS Query processed and response sent!";
     send_response(message);
-  } else {
-    OSP_DVLOG << "\tmDNS Query processed and no response sent!";
   }
 }
 
diff --git a/platform/BUILD.gn b/platform/BUILD.gn
index 375ba41..f510fbf 100644
--- a/platform/BUILD.gn
+++ b/platform/BUILD.gn
@@ -202,6 +202,7 @@
     "base/error_unittest.cc",
     "base/ip_address_unittest.cc",
     "base/location_unittest.cc",
+    "base/udp_packet_unittest.cc",
   ]
 
   # The socket integration tests assume that you can Bind with UDP sockets,
diff --git a/platform/base/udp_packet.cc b/platform/base/udp_packet.cc
index 6cbb0da..8470893 100644
--- a/platform/base/udp_packet.cc
+++ b/platform/base/udp_packet.cc
@@ -1,10 +1,11 @@
 // Copyright 2019 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file
+// found in the LICENSE file.
 
 #include "platform/base/udp_packet.h"
 
 #include <cassert>
+#include <sstream>
 
 namespace openscreen {
 
@@ -26,4 +27,17 @@
 
 UdpPacket& UdpPacket::operator=(UdpPacket&& other) = default;
 
+std::string UdpPacket::ToString() const {
+  // TODO(issuetracker.google.com/158660166): Change to use shared hex-to-string
+  // method.
+  static constexpr char hex[] = "0123456789ABCDEF";
+  std::stringstream ss;
+  ss << "[";
+  for (auto it = begin(); it != end(); it++) {
+    ss << hex[*it / 16] << hex[*it % 16] << " ";
+  }
+  ss << "]";
+  return ss.str();
+}
+
 }  // namespace openscreen
diff --git a/platform/base/udp_packet.h b/platform/base/udp_packet.h
index a8fcce0..3a3b262 100644
--- a/platform/base/udp_packet.h
+++ b/platform/base/udp_packet.h
@@ -1,12 +1,13 @@
 // Copyright 2019 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file
+// found in the LICENSE file.
 
 #ifndef PLATFORM_BASE_UDP_PACKET_H_
 #define PLATFORM_BASE_UDP_PACKET_H_
 
 #include <stdint.h>
 
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -44,6 +45,8 @@
   UdpSocket* socket() const { return socket_; }
   void set_socket(UdpSocket* socket) { socket_ = socket; }
 
+  std::string ToString() const;
+
   static constexpr size_type kUdpMaxPacketSize = 1 << 16;
 
  private:
diff --git a/platform/base/udp_packet_unittest.cc b/platform/base/udp_packet_unittest.cc
new file mode 100644
index 0000000..8494957
--- /dev/null
+++ b/platform/base/udp_packet_unittest.cc
@@ -0,0 +1,34 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "platform/base/udp_packet.h"
+
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace openscreen {
+
+TEST(UdpPacketTest, ValidateToStringNormalCase) {
+  UdpPacket packet{0x73, 0xC7, 0x00, 0x14, 0xFF, 0x2C};
+  std::string result = packet.ToString();
+  EXPECT_EQ(result, "[73 C7 00 14 FF 2C ]");
+
+  UdpPacket packet2{0x1, 0x2, 0x3, 0x4, 0x5};
+  result = packet2.ToString();
+  EXPECT_EQ(result, "[01 02 03 04 05 ]");
+
+  UdpPacket packet3{0x0, 0x0, 0x0};
+  result = packet3.ToString();
+  EXPECT_EQ(result, "[00 00 00 ]");
+}
+
+TEST(UdpPacketTest, ValidateToStringEmpty) {
+  UdpPacket packet{};
+  std::string result = packet.ToString();
+  EXPECT_EQ(result, "[]");
+}
+
+}  // namespace openscreen