Merge to M77: Reland "Enable the mDNS responder to list names as a TXT record."

This is a reland of a4da6e0708527daab7af78e6dda530d39fb77427

Original change's description:
> Enable the mDNS responder to list names as a TXT record.
>
> After this change, the mDNS responder will respond to queries for TXT
> records associated with the query name
> "Generated-Names._mdns_name_generator._udp.local" by providing the list
> of currently registered mDNS names (as version-4 UUIDs). The TXT record
> follows the DNS-SD key-value pair format in RFC 6763. The cache-flush
> bit is not in these records so that multiple Chrome instances in the
> same network would consider their responses belonging to a shared set
> of resource records associated with the same query name.
>
> This CL also fixes a bug in handling read errors of mDNS sockets.
>
> [email protected]
>
> Bug: 979022, 990704
> Change-Id: I47d92fff10f14e910ba6fec97d99501421b3eab2
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1709152
> Commit-Queue: Qingsi Wang <[email protected]>
> Reviewed-by: Eric Orth <[email protected]>
> Cr-Commit-Position: refs/heads/master@{#684822}

[email protected]

(cherry picked from commit 8dc66e136993db71f2422cc32b561783da735173)

Bug: 979022, 990704
Change-Id: Ib8b4464b852c3716ab1cc829cfddb5aab59a0689
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1742648
Reviewed-by: Eric Orth <[email protected]>
Commit-Queue: Qingsi Wang <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#684947}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1745125
Reviewed-by: Qingsi Wang <[email protected]>
Cr-Commit-Position: refs/branch-heads/3865@{#305}
Cr-Branched-From: 0cdcc6158160790658d1f033d3db873603250124-refs/heads/master@{#681094}
diff --git a/net/dns/dns_response.cc b/net/dns/dns_response.cc
index 4dcf286..1f46101 100644
--- a/net/dns/dns_response.cc
+++ b/net/dns/dns_response.cc
@@ -32,10 +32,6 @@
 
 const uint8_t kRcodeMask = 0xf;
 
-// RFC 1035, Section 4.1.3.
-// TYPE (2 bytes) + CLASS (2 bytes) + TTL (4 bytes) + RDLENGTH (2 bytes)
-const size_t kResourceRecordSizeInBytesWithoutNameAndRData = 10;
-
 }  // namespace
 
 DnsResourceRecord::DnsResourceRecord() = default;
@@ -109,7 +105,7 @@
   // 1 byte (with dot) or 2 bytes larger in size. See RFC 1035, Section 3.1 and
   // DNSDomainFromDot.
   return name.size() + (has_final_dot ? 1 : 2) +
-         kResourceRecordSizeInBytesWithoutNameAndRData +
+         net::dns_protocol::kResourceRecordSizeInBytesWithoutNameAndRData +
          (owned_rdata.empty() ? rdata.size() : owned_rdata.size());
 }
 
diff --git a/net/dns/public/dns_protocol.h b/net/dns/public/dns_protocol.h
index 6e5b688e..29582d7 100644
--- a/net/dns/public/dns_protocol.h
+++ b/net/dns/public/dns_protocol.h
@@ -122,10 +122,20 @@
 // medium's MTU, and must be under 9000 bytes
 static const int kMaxMulticastSize = 9000;
 
+// RFC 1035, Section 4.1.3.
+// TYPE (2 bytes) + CLASS (2 bytes) + TTL (4 bytes) + RDLENGTH (2 bytes)
+static const int kResourceRecordSizeInBytesWithoutNameAndRData = 10;
+
 // DNS class types.
 //
 // https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-2
 static const uint16_t kClassIN = 1;
+// RFC 6762, Section 10.2.
+//
+// For resource records sent through mDNS, the top bit of the class field in a
+// resource record is repurposed to the cache-flush bit. This bit should only be
+// used in mDNS transactions.
+static const uint16_t kFlagCacheFlush = 0x8000;
 
 // DNS resource record types.
 //
diff --git a/services/network/mdns_responder.cc b/services/network/mdns_responder.cc
index f0bb36e..3f8bdaf 100644
--- a/services/network/mdns_responder.cc
+++ b/services/network/mdns_responder.cc
@@ -3,20 +3,22 @@
 // found in the LICENSE file.
 
 #include <algorithm>
+#include <cmath>
 #include <numeric>
 #include <queue>
 #include <utility>
 
 #include "services/network/mdns_responder.h"
 
+#include "base/big_endian.h"
 #include "base/bind.h"
 #include "base/guid.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/optional.h"
-#include "base/single_thread_task_runner.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/rand_util.h"
 #include "base/stl_util.h"
-#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
 #include "base/sys_byteorder.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/time/default_tick_clock.h"
@@ -70,12 +72,6 @@
 // RFC 6762, Section 8.3.
 const int kMinNumAnnouncementsToSend = 2;
 
-// RFC 6762, Section 10.2.
-//
-// The top bit of the class field in a resource record is repurposed to the
-// cache-flush bit.
-const uint16_t kFlagCacheFlush = 0x8000;
-
 // Maximum number of retries for the same response due to send failure.
 const uint8_t kMaxMdnsResponseRetries = 2;
 // The capacity of the send queue for packets blocked by an incomplete send.
@@ -83,6 +79,35 @@
 // Maximum delay allowed for per-response rate-limited responses.
 const base::TimeDelta kMaxScheduledDelay = base::TimeDelta::FromSeconds(10);
 
+// The query name of the mDNS name generator service.
+const char kMdnsNameGeneratorServiceInstanceName[] =
+    "Generated-Names._mdns_name_generator._udp.local";
+
+// RFC 6763, the TXT record is recommended to be under 1300 bytes to fit in a
+// single 1500-byte Ethernet packet.
+//
+// Currently we only construct a TXT record in the response to an mDNS name
+// generator service query. The record consists of a list of owned names, and
+// this list is truncated as necessary to stay within the size limit. See
+// |CreateTxtRdataWithNames| below for the detail.
+const uint16_t kMaxTxtRecordSizeInBytes = 1300;
+// RFC 6763, Section 6.4, the key in a kv pair in a DNS-SD TXT record should be
+// no more than 9 characters long.
+const int kMaxKeySizeInTxtRecord = 9;
+// The prefix of the key used in the TXT record to list mDNS names.
+const char kKeyPrefixInTxtRecord[] = "name";
+// Version tag in the TXT record.
+const char kTxtversLine[] = "\x9txtvers=1";
+
+// RFC 6762, Section 6, a response that may contain an answer as a member of a
+// shared resource record set, should be delayed uniformly and randomly in the
+// range of 20-120 ms. This delay is applied in addition to the scheduled delay
+// by rate limiting.
+const base::TimeDelta kMinRandDelayForSharedResult =
+    base::TimeDelta::FromMilliseconds(20);
+const base::TimeDelta kMaxRandDelayForSharedResult =
+    base::TimeDelta::FromMilliseconds(120);
+
 class RandomUuidNameGenerator
     : public network::MdnsResponderManager::NameGenerator {
  public:
@@ -121,11 +146,10 @@
                                : net::dns_protocol::kTypeAAAA);
     // Set the cache-flush bit to assert that this information is the truth and
     // the whole truth.
-    record.klass = net::dns_protocol::kClassIN | kFlagCacheFlush;
-    int64_t ttl_seconds = ttl.InSeconds();
+    record.klass =
+        net::dns_protocol::kClassIN | net::dns_protocol::kFlagCacheFlush;
     // TTL in a resource record is 32-bit.
-    DCHECK(ttl_seconds >= 0 && ttl_seconds <= 0x0ffffffff);
-    record.ttl = ttl_seconds;
+    record.ttl = base::checked_cast<uint32_t>(ttl.InSeconds());
     record.SetOwnedRdata(net::IPAddressToPackedString(ip));
     address_records.push_back(std::move(record));
   }
@@ -177,7 +201,8 @@
     record.type = net::dns_protocol::kTypeNSEC;
     // Set the cache-flush bit to assert that this information is the truth and
     // the whole truth.
-    record.klass = net::dns_protocol::kClassIN | kFlagCacheFlush;
+    record.klass =
+        net::dns_protocol::kClassIN | net::dns_protocol::kFlagCacheFlush;
     // RFC 6762, Section 6.1. TTL should be the same as that of what the record
     // would have.
     record.ttl = kDefaultTtlForRecordWithHostname.InSeconds();
@@ -188,6 +213,76 @@
   return nsec_records;
 }
 
+// Creates TXT RDATA as a list of key-value pairs subject to a size limit. The
+// key is in the format "name0", "name1" and so on, and the value is the name.
+std::string CreateTxtRdataWithNames(const std::set<std::string>& names,
+                                    uint16_t txt_rdata_size_limit) {
+  DCHECK(!names.empty());
+  DCHECK_GT(txt_rdata_size_limit, sizeof(kTxtversLine));
+  int remaining_budget =
+      txt_rdata_size_limit - sizeof(kTxtversLine) + 1 /* null terminator */;
+  std::string txt_rdata;
+  size_t prev_txt_rdata_size = 0;
+  uint16_t idx = 0;
+  for (const std::string& name : names) {
+    const int key_size =
+        sizeof(kKeyPrefixInTxtRecord) - 1 /* null terminator */ +
+        (idx > 0 ? static_cast<int>(log10(static_cast<double>(idx))) + 1 : 1);
+    // RFC 6763, Section 6.4, the key should be no more than nine characters
+    // long.
+    DCHECK_LE(key_size, kMaxKeySizeInTxtRecord);
+    // Each TXT line consists of a length octet followed by as many characters,
+    // and as a result each line cannot exceed 256 characters.
+    const int line_size =
+        2 /* length octet and "=" sign */ + key_size + name.size();
+    // Each name should be guaranteed to have no more than 245 characters to
+    // meet the line length limit. See the comment before |NameGenerator|.
+    DCHECK_LE(line_size - 1, std::numeric_limits<uint8_t>::max());
+    remaining_budget -= line_size;
+    if (remaining_budget <= 0) {
+      VLOG(1) << "TXT RDATA size limit exceeded. Stopped appending lines in "
+                 "the response.";
+      break;
+    }
+
+    // Note that c_str() is null terminated.
+    //
+    // E.g. \x13name0=example.local
+    base::StringAppendF(&txt_rdata, "%c%s%d=%s", line_size - 1,
+                        kKeyPrefixInTxtRecord, idx, name.c_str());
+    DCHECK_EQ(txt_rdata.size(), prev_txt_rdata_size + line_size);
+    prev_txt_rdata_size = txt_rdata.size();
+    ++idx;
+  }
+
+  DCHECK(!txt_rdata.empty());
+  // Note that the size of the version tag line has been deducted from the
+  // budget before we add lines of names.
+  txt_rdata += kTxtversLine;
+
+  return txt_rdata;
+}
+
+net::DnsResourceRecord CreateTxtRecordWithNames(
+    const base::TimeDelta& ttl,
+    const std::string& service_instance_name,
+    const std::set<std::string>& names) {
+  net::DnsResourceRecord txt;
+  txt.name = service_instance_name;
+  txt.type = net::dns_protocol::kTypeTXT;
+  // The cache-flush bit is not set so that the responses from other Chrome
+  // instances are not considered conflicts. See the conflict detection in
+  // |SocketHandler::HandlePacket|.
+  txt.klass = net::dns_protocol::kClassIN;
+  // TTL in a resource record is 32-bit.
+  txt.ttl = base::checked_cast<uint32_t>(ttl.InSeconds());
+  uint16_t txt_rdata_size_limit =
+      kMaxTxtRecordSizeInBytes - service_instance_name.size() -
+      net::dns_protocol::kResourceRecordSizeInBytesWithoutNameAndRData;
+  txt.SetOwnedRdata(CreateTxtRdataWithNames(names, txt_rdata_size_limit));
+  return txt;
+}
+
 bool IsProbeQuery(const net::DnsQuery& query) {
   // TODO(qingsi): RFC 6762, the proper way to detect a probe query is
   // to check if
@@ -221,6 +316,15 @@
   base::TimeTicks send_ready_time;
 };
 
+// Returns a random TimeDelta between |min| and |max| following the uniform
+// distribution.
+base::TimeDelta GetRandTimeDelta(const base::TimeDelta& min,
+                                 const base::TimeDelta& max) {
+  DCHECK_LE(min, max);
+  return base::TimeDelta::FromMicroseconds(
+      base::RandInt(min.InMicroseconds(), max.InMicroseconds()));
+}
+
 }  // namespace
 
 
@@ -246,10 +350,9 @@
   //
   // Section 6. mDNS responses MUST NOT contain any questions.
   // Section 18.1. In mDNS responses, ID MUST be set to zero.
-  net::DnsResponse response(
-      0 /* id */, true /* is_authoritative */, answers,
-      std::vector<net::DnsResourceRecord>() /* authority_records */,
-      additional_records, base::nullopt /* query */, 0 /* rcode */);
+  net::DnsResponse response(0 /* id */, true /* is_authoritative */, answers,
+                            {} /* authority_records */, additional_records,
+                            base::nullopt /* query */);
   DCHECK(response.io_buffer() != nullptr);
   auto buf =
       base::MakeRefCounted<net::IOBufferWithSize>(response.io_buffer_size());
@@ -265,10 +368,28 @@
   std::vector<net::DnsResourceRecord> additional_records =
       CreateAddressResourceRecords(name_addr_map,
                                    kDefaultTtlForRecordWithHostname);
-  net::DnsResponse response(
-      0 /* id */, true /* is_authoritative */, nsec_records,
-      std::vector<net::DnsResourceRecord>() /* authority_records */,
-      additional_records, base::nullopt /* query */, 0 /* rcode */);
+  net::DnsResponse response(0 /* id */, true /* is_authoritative */,
+                            nsec_records, {} /* authority_records */,
+                            additional_records, base::nullopt /* query */);
+  DCHECK(response.io_buffer() != nullptr);
+  auto buf =
+      base::MakeRefCounted<net::IOBufferWithSize>(response.io_buffer_size());
+  memcpy(buf->data(), response.io_buffer()->data(), response.io_buffer_size());
+  return buf;
+}
+
+scoped_refptr<net::IOBufferWithSize>
+CreateResponseToMdnsNameGeneratorServiceQuery(
+    const base::TimeDelta& ttl,
+    const std::set<std::string>& mdns_names) {
+  std::vector<net::DnsResourceRecord> answers(
+      1, CreateTxtRecordWithNames(ttl, kMdnsNameGeneratorServiceInstanceName,
+                                  mdns_names));
+
+  net::DnsResponse response(0 /* id */, true /* is_authoritative */, answers,
+                            {} /* authority_records */,
+                            {} /* additional_records */,
+                            base::nullopt /* query */);
   DCHECK(response.io_buffer() != nullptr);
   auto buf =
       base::MakeRefCounted<net::IOBufferWithSize>(response.io_buffer_size());
@@ -321,13 +442,14 @@
 
   void SetTickClockForTesting(const base::TickClock* tick_clock);
 
-  base::WeakPtr<SocketHandler> GetWeakPtr() {
-    return weak_factory_.GetWeakPtr();
-  }
-
  private:
   class ResponseScheduler;
 
+  // Returns the effective result after handling. In particular, if |result|
+  // represents a non-fatal error that is not ERR_IO_PENDING, it will be
+  // converted to net::OK and returned.
+  int HandlePacket(int result);
+
   int DoReadLoop() {
     int result;
     do {
@@ -339,9 +461,12 @@
           base::BindOnce(&MdnsResponderManager::SocketHandler::OnRead,
                          base::Unretained(this)));
       // Process synchronous return from RecvFrom.
-      HandlePacket(result);
+      result = HandlePacket(result);
     } while (result >= 0);
 
+    // Note that since |HandlePacket| converts a non-fatal error that is not
+    // ERR_IO_PENDING to OK, |result| returned is either ERR_IO_PENDING or a
+    // fatal error.
     return result;
   }
 
@@ -349,14 +474,18 @@
   // positive, or a network stack error code if negative. Zero indicates either
   // net::OK or zero bytes read.
   void OnRead(int result) {
-    if (result >= 0) {
-      HandlePacket(result);
-      DoReadLoop();
-    } else {
-      responder_manager_->OnSocketHandlerReadError(id_, result);
-    }
+    result = HandlePacket(result);
+    DCHECK_NE(result, net::ERR_IO_PENDING);
+
+    if (result >= 0)
+      result = DoReadLoop();
+
+    if (result == net::ERR_IO_PENDING)
+      return;
+
+    DCHECK(responder_manager_->IsFatalError(result));
+    responder_manager_->OnSocketHandlerReadError(id_, result);
   }
-  void HandlePacket(int result);
 
   uint16_t id_;
   std::unique_ptr<ResponseScheduler> scheduler_;
@@ -572,17 +701,24 @@
         RateLimitScheme rate_limit_scheme,
         const MdnsResponseSendOption& option) {
   auto now = tick_clock_->NowTicks();
+  const auto extra_delay_for_shared_result =
+      option.shared_result ? GetRandTimeDelta(kMinRandDelayForSharedResult,
+                                              kMaxRandDelayForSharedResult)
+                           : base::TimeDelta();
+
   // RFC 6762 requires the rate limiting applied on a per-record basis. When a
-  // response contains multiple records, each identified by the name, we
-  // compute the delay as the maximum delay of records contained. See the
-  // definition of RateLimitScheme::PER_RECORD.
+  // response contains multiple records, each identified by the name, we compute
+  // the delay as the maximum delay of records contained. See the definition of
+  // RateLimitScheme::PER_RECORD.
   //
   // For responses that are triggered via the Mojo connection, we perform more
   // restrictive rate limiting on a per-response basis. See the
   // definition of RateLimitScheme::PER_RESPONSE.
   if (rate_limit_scheme == RateLimitScheme::PER_RESPONSE) {
     auto delay =
-        std::max(next_available_time_per_resp_sched_ - now, base::TimeDelta());
+        std::max(next_available_time_per_resp_sched_ - now, base::TimeDelta()) +
+        extra_delay_for_shared_result;
+
     if (delay > kMaxScheduledDelay)
       return base::nullopt;
 
@@ -619,7 +755,9 @@
         next_available_time_for_response, next_available_time_for_name_[name]);
   }
   base::TimeDelta delay =
-      std::max(next_available_time_for_response - now, base::TimeDelta());
+      std::max(next_available_time_for_response - now, base::TimeDelta()) +
+      extra_delay_for_shared_result;
+
   if (delay > kMaxScheduledDelay)
     return base::nullopt;
 
@@ -638,6 +776,10 @@
     if (now >= next_send_ready_time) {
       auto pending_packet = std::move(send_queue_.top());
       send_queue_.pop();
+      const auto& option = pending_packet.option;
+      if (option->cancelled_callback && option->cancelled_callback->Run())
+        continue;
+
       int rv = handler_->DoSend(std::move(pending_packet));
       if (rv == net::ERR_IO_PENDING) {
         send_pending_ = true;
@@ -649,7 +791,7 @@
       // We have no packet due; post a task to flush the send queue later.
       //
       // Note that the owning handler of this scheduler may be removed if it
-      // encounters read error as we process in OnSocketHandlerReadError. We
+      // encounters read error as we process in |OnSocketHandlerReadError|. We
       // should guarantee any posted task can be cancelled if the scheduler goes
       // away, which we do via the weak pointer.
       const base::TimeDelta time_to_next_packet = next_send_ready_time - now;
@@ -680,6 +822,10 @@
 }
 
 MdnsResponderManager::~MdnsResponderManager() {
+  // Note that sending the goodbye is best-effort since it may have a non-zero
+  // delay because of backlogged responses from rate-limiting. Delayed send will
+  // be cancelled after the manager is destroyed.
+  SendGoodbyePacketForMdnsNameGeneratorServiceIfNecessary();
   // When destroyed, each responder will send out Goodbye messages for owned
   // names via the back pointer to the manager. As a result, we should destroy
   // the remaining responders before the manager is destroyed.
@@ -781,8 +927,9 @@
   }
 }
 
-void MdnsResponderManager::HandleNameConflictIfAny(
+void MdnsResponderManager::HandleAddressNameConflictIfAny(
     const std::map<std::string, std::set<net::IPAddress>>& external_maps) {
+  // Handle conflicts in names for address records.
   for (const auto& name_to_addresses : external_maps) {
     for (auto& responder : responders_) {
       if (responder->HasConflictWithExternalResolution(
@@ -800,9 +947,31 @@
   }
 }
 
+void MdnsResponderManager::HandleTxtNameConflict() {
+  // We will no longer respond to queries to list the generated names. This also
+  // cancels the scheduled responses.
+  LOG(ERROR)
+      << "Stop responding to queries for the mDNS name generator service after "
+         "observing a name conflict from an external TXT record.";
+  should_respond_to_generator_service_query_ = false;
+}
+
 void MdnsResponderManager::OnMdnsQueryReceived(
     const net::DnsQuery& query,
     uint16_t recv_socket_handler_id) {
+  // TODO(qingsi): Ideally we should consolidate the handling of the service
+  // query using the same responder mechanism as after this block (i.e. there
+  // would be a responder owning the service instance name). The current
+  // responder only provides APIs to create address records, and hence limited
+  // to handle only such records. Once we have expanded the API surface to
+  // include the service publishing, the handling logic should be unified.
+  const std::string qname = net::DNSDomainToString(query.qname());
+  if (should_respond_to_generator_service_query_ &&
+      qname == kMdnsNameGeneratorServiceInstanceName) {
+    HandleMdnsNameGeneratorServiceQuery(query, recv_socket_handler_id);
+    return;
+  }
+
   for (auto& responder : responders_)
     responder->OnMdnsQueryReceived(query, recv_socket_handler_id);
 }
@@ -811,14 +980,13 @@
                                                     int result) {
   VLOG(1) << "Socket read error, socket=" << socket_handler_id
           << ", error=" << result;
-  if (IsNonFatalError(result))
-    return;
 
+  // We should not remove the socket handler for a non-fatal error.
+  DCHECK(IsFatalError(result));
   auto it = socket_handler_by_id_.find(socket_handler_id);
   DCHECK(it != socket_handler_by_id_.end());
-  // It is safe to remove the handler in error since this error handler is
-  // invoked by the callback after the asynchronous return of RecvFrom, when the
-  // handler has exited the read loop.
+  // It is safe to remove the handler in error since the handler has exited the
+  // read loop and is done with |OnRead|.
   socket_handler_by_id_.erase(it);
   if (socket_handler_by_id_.empty()) {
     LOG(ERROR)
@@ -829,51 +997,141 @@
   }
 }
 
-bool MdnsResponderManager::IsNonFatalError(int result) {
-  DCHECK(result < net::OK);
-  if (result == net::ERR_MSG_TOO_BIG)
-    return true;
+bool MdnsResponderManager::IsFatalError(int result) {
+  if (result >= 0)
+    return false;
+  if (result == net::ERR_MSG_TOO_BIG || result == net::ERR_IO_PENDING)
+    return false;
 
-  return false;
+  return true;
 }
 
-void MdnsResponderManager::SocketHandler::HandlePacket(int result) {
-  if (result <= 0)
+void MdnsResponderManager::HandleMdnsNameGeneratorServiceQuery(
+    const net::DnsQuery& query,
+    uint16_t recv_socket_handler_id) {
+  uint16_t qtype = query.qtype();
+  if (qtype != net::dns_protocol::kTypeTXT && !IsProbeQuery(query)) {
+    VLOG(1) << "The mDNS name generator service query is discarded. Only "
+               "queries for TXT records or probe queries are supported.";
     return;
+  }
+
+  if (names_.empty()) {
+    VLOG(1) << "The mDNS name generator service query is discarded. No "
+               "registered names to respond.";
+    return;
+  }
+
+  auto option = base::MakeRefCounted<MdnsResponseSendOption>();
+  option->send_socket_handler_ids.insert(recv_socket_handler_id);
+  option->names_for_rate_limit.insert(kMdnsNameGeneratorServiceInstanceName);
+  if (IsProbeQuery(query)) {
+    option->klass = MdnsResponseSendOption::ResponseClass::PROBE_RESOLUTION;
+  } else {
+    option->klass = MdnsResponseSendOption::ResponseClass::REGULAR_RESOLUTION;
+  }
+  // There can be other Chrome instances in the same network that would respond
+  // to this query.
+  option->shared_result = true;
+  option->cancelled_callback = base::BindRepeating(
+      [](base::WeakPtr<MdnsResponderManager> manager) {
+        return !manager || !manager->should_respond_to_generator_service_query_;
+      },
+      weak_factory_.GetWeakPtr());
+  Send(mdns_helper::CreateResponseToMdnsNameGeneratorServiceQuery(
+           kDefaultTtlForRecordWithHostname, names_),
+       std::move(option));
+  names_in_last_generator_response_ = names_;
+}
+
+// TODO(qingsi): When the list of owned names are updated, if we have ever sent
+// a response to the generator service query, we should send a goodbye for the
+// stale list of names and an update to advertise the new list. See RFC 6762,
+// Section 8.4. Currently we only send the goodbye when the manager is
+// destroyed. See the destructor of the manager.
+void MdnsResponderManager::
+    SendGoodbyePacketForMdnsNameGeneratorServiceIfNecessary() {
+  if (names_in_last_generator_response_.empty())
+    return;
+
+  auto option = base::MakeRefCounted<MdnsResponseSendOption>();
+  // Send on all interfaces by not setting the send socket.
+  option->klass = MdnsResponseSendOption::ResponseClass::GOODBYE;
+  // We do not set |shared_result| in the option for the goodbye to avoid the
+  // random delay. The delay would guarantee the cancelling of the scheduled
+  // send after the manager is destroyed.
+  Send(mdns_helper::CreateResponseToMdnsNameGeneratorServiceQuery(
+           base::TimeDelta(), names_in_last_generator_response_),
+       std::move(option));
+}
+
+int MdnsResponderManager::SocketHandler::HandlePacket(int result) {
+  if (result == 0 || result == net::ERR_IO_PENDING)
+    return result;
+  if (result < 0)
+    return responder_manager_->IsFatalError(result) ? result : net::OK;
 
   net::DnsQuery query(io_buffer_.get());
   bool parsed_as_query = query.Parse(result);
   if (parsed_as_query) {
     responder_manager_->OnMdnsQueryReceived(query, id_);
-  } else {
-    net::DnsResponse response(io_buffer_.get(), io_buffer_->size());
-    if (response.InitParseWithoutQuery(io_buffer_->size()) &&
-        response.answer_count() > 0) {
-      // There could be multiple records for the same name in the response.
-      std::map<std::string, std::set<net::IPAddress>> external_maps;
-      auto parser = response.Parser();
-      for (size_t i = 0; i < response.answer_count(); ++i) {
-        auto parsed_record =
-            net::RecordParsed::CreateFrom(&parser, base::Time::Now());
-        if (!parsed_record || !parsed_record->ttl())
-          continue;
+    return result;
+  }
 
-        switch (parsed_record->type()) {
-          case net::ARecordRdata::kType:
-            external_maps[parsed_record->name()].insert(
-                parsed_record->rdata<net::ARecordRdata>()->address());
-            break;
-          case net::AAAARecordRdata::kType:
-            external_maps[parsed_record->name()].insert(
-                parsed_record->rdata<net::AAAARecordRdata>()->address());
-            break;
-          default:
-            break;
-        }
+  net::DnsResponse response(io_buffer_.get(), io_buffer_->size());
+  if (!response.InitParseWithoutQuery(io_buffer_->size()) ||
+      response.answer_count() == 0)
+    return result;
+
+  // There could be multiple records for the same name in the response.
+  std::map<std::string, std::set<net::IPAddress>> external_address_maps;
+  bool has_txt_record_conflict = false;
+  auto parser = response.Parser();
+  DCHECK_GT(response.answer_count(), 0u);
+  for (size_t i = 0; i < response.answer_count(); ++i) {
+    auto parsed_record =
+        net::RecordParsed::CreateFrom(&parser, base::Time::Now());
+    if (!parsed_record || !parsed_record->ttl())
+      continue;
+
+    switch (parsed_record->type()) {
+      case net::ARecordRdata::kType:
+        external_address_maps[parsed_record->name()].insert(
+            parsed_record->rdata<net::ARecordRdata>()->address());
+        break;
+      case net::AAAARecordRdata::kType:
+        external_address_maps[parsed_record->name()].insert(
+            parsed_record->rdata<net::AAAARecordRdata>()->address());
+        break;
+      case net::TxtRecordRdata::kType: {
+        if (parsed_record->name() == kMdnsNameGeneratorServiceInstanceName &&
+            parsed_record->klass() & net::dns_protocol::kFlagCacheFlush)
+          // TODO(qingsi): Do not share the instance name once we implement the
+          // DNS-SD scheme for responding to service queries. For now we should
+          // also validate that the TXT record follows the same key/value pair
+          // scheme in |CreateTxtRdataWithNames| even if the cache-flush bit not
+          // set.
+          //
+          // We currently allow Chrome instances to share the same instance name
+          // for their lists of owned names, by not setting the cache-flush bit,
+          // and hence the above conflict detection logic. If net::MdnsClient is
+          // the intended receiver of these responses, it currently can not
+          // merge the responses from multiple instances. Once we move to fully
+          // implementing the DNS-SD scheme, this issue should be solved after
+          // we use distinct instance names in the replied SRV and TXT records.
+          has_txt_record_conflict = true;
+        break;
       }
-      responder_manager_->HandleNameConflictIfAny(external_maps);
+      default:
+        break;
     }
   }
+  responder_manager_->HandleAddressNameConflictIfAny(external_address_maps);
+
+  if (has_txt_record_conflict)
+    responder_manager_->HandleTxtNameConflict();
+
+  return result;
 }
 
 MdnsResponder::MdnsResponder(mojom::MdnsResponderRequest request,
@@ -887,6 +1145,10 @@
 }
 
 MdnsResponder::~MdnsResponder() {
+  for (const auto& name_addr_pair : name_addr_map_) {
+    bool rv = manager_->RemoveName(name_addr_pair.first);
+    DCHECK(rv);
+  }
   SendGoodbyePacketForNameAddressMap(name_addr_map_);
 }
 
@@ -906,10 +1168,9 @@
   bool announcement_sched_at_least_once = false;
   if (it == name_addr_map_.end()) {
     name = name_generator_->CreateName() + ".local";
-#ifdef DEBUG
     // The name should be uniquely owned by one instance of responders.
-    DCHECK(manager_->AddName(name));
-#endif
+    bool rv = manager_->AddName(name);
+    DCHECK(rv);
     name_addr_map_[name] = address;
     DCHECK(name_refcount_map_.find(name) == name_refcount_map_.end());
     name_refcount_map_[name] = 1;
@@ -953,11 +1214,10 @@
   bool goodbye_scheduled = false;
   if (refcount == 0) {
     goodbye_scheduled = SendGoodbyePacketForNameAddressMap({*it});
-#ifdef DEBUG
     // The name removed should be previously owned by one instance of
     // responders.
-    DCHECK(manager_->RemoveName(name));
-#endif
+    bool rv = manager_->RemoveName(name);
+    DCHECK(rv);
     name_refcount_map_.erase(name);
     name_addr_map_.erase(it);
   }
diff --git a/services/network/mdns_responder.h b/services/network/mdns_responder.h
index 69a92c2..a7e6f90 100644
--- a/services/network/mdns_responder.h
+++ b/services/network/mdns_responder.h
@@ -11,8 +11,11 @@
 #include <string>
 #include <vector>
 
+#include "base/callback.h"
 #include "base/containers/flat_set.h"
 #include "base/containers/unique_ptr_adapters.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "net/dns/dns_query.h"
 #include "net/dns/dns_response.h"
@@ -48,6 +51,19 @@
 COMPONENT_EXPORT(NETWORK_SERVICE)
 scoped_refptr<net::IOBufferWithSize> CreateNegativeResponse(
     const std::map<std::string, net::IPAddress>& name_addr_map);
+// Creates an mDNS response to an mDNS name generator service query.
+//
+// An mDNS name generator service query is a query for TXT records associated
+// with the name "Generated-Names._mdns_name_generator._udp.local". It is
+// similar to the service query defined in DNS-SD but is not an implementation
+// of the specification in RFC 6763. The Answer section of the response contains
+// a TXT record for the list of |mdns_names|. The cache-flush bit is set in the
+// TXT record if |cached_flush| is true.
+COMPONENT_EXPORT(NETWORK_SERVICE)
+scoped_refptr<net::IOBufferWithSize>
+CreateResponseToMdnsNameGeneratorServiceQuery(
+    const base::TimeDelta& ttl,
+    const std::set<std::string>& mdns_names);
 
 }  // namespace mdns_helper
 
@@ -73,6 +89,11 @@
   ResponseClass klass = ResponseClass::UNSPECIFIED;
   // The number of retries done for the same response due to send failure.
   uint8_t num_send_retries_done = 0;
+  // Indicates if the response includes a resource record that is a member of a
+  // shared resource record set.
+  bool shared_result = false;
+  // If not nullopt, returns true if the response to send is cancelled.
+  base::Optional<base::RepeatingCallback<bool()>> cancelled_callback;
 
  private:
   friend class RefCounted<MdnsResponseSendOption>;
@@ -89,6 +110,8 @@
  public:
   // Wraps a name generation method that can be configured in tests via
   // SetNameGeneratorForTesting below.
+  //
+  // The generated name by |CreateName| must be no more than 245 characters.
   class NameGenerator {
    public:
     virtual ~NameGenerator() = default;
@@ -123,11 +146,10 @@
   // Creates an instance of MdnsResponder for the binding request from an
   // InterfacePtr.
   void CreateMdnsResponder(mojom::MdnsResponderRequest request);
-#ifdef DEBUG
-  // The methods below are only used for extra uniqueness validation of names
-  // owned by responders. By default, we use the RandomUuidNameGenerator (see
-  // mdns_responder.cc), which probabilistically guarantees the uniqueness of
-  // generated names.
+  // The methods below are used to bookkeep names owned by responders, and
+  // also for the extra uniqueness validation of these names. By default,
+  // we use the RandomUuidNameGenerator (see mdns_responder.cc), which
+  // probabilistically guarantees the uniqueness of generated names.
   //
   // Adds a name to the set of all existing names generated by all responders
   // (i.e., names owned by an instance of responder). Return true if the name is
@@ -140,7 +162,6 @@
   // responders. Return true if the name exists in the set before the removal;
   // false otherwise.
   bool RemoveName(const std::string& name) { return names_.erase(name) == 1; }
-#endif
   // Sends an mDNS response in the wire format given by |buf|. See
   // MdnsResponseSendOption for configurable options in |option|.
   //
@@ -157,10 +178,15 @@
   // the local network.
   void OnMojoConnectionError(MdnsResponder* responder);
 
-  // Called when an external mDNS response is received and it contains
+  // Called when an external mDNS response is received and it contains address
   // records of names generated by an owned MdnsResponder instance.
-  void HandleNameConflictIfAny(
-      const std::map<std::string, std::set<net::IPAddress>>& external_maps);
+  void HandleAddressNameConflictIfAny(
+      const std::map<std::string, std::set<net::IPAddress>>&
+          external_address_maps);
+  // Called when an external mDNS response is received and it contains a TXT
+  // record for the mDNS name generator service instance name with the
+  // cache-flush bit set.
+  void HandleTxtNameConflict();
 
   NameGenerator* name_generator() const { return name_generator_.get(); }
   // Sets the name generator that is shared by all MdnsResponder instances.
@@ -198,7 +224,15 @@
   void OnMdnsQueryReceived(const net::DnsQuery& query,
                            uint16_t recv_socket_handler_id);
   void OnSocketHandlerReadError(uint16_t socket_handler_id, int result);
-  bool IsNonFatalError(int result);
+  bool IsFatalError(int result);
+  // Called when an mDNS name service query is received. The manager fills a
+  // list of registered names in a TXT record in the response.
+  void HandleMdnsNameGeneratorServiceQuery(const net::DnsQuery& query,
+                                           uint16_t recv_socket_handler_id);
+  // Sends a zero-TTL mDNS response with a TXT record of mDNS names from the
+  // last generator service response sent. No-op if no generator service
+  // response sent previously.
+  void SendGoodbyePacketForMdnsNameGeneratorServiceIfNecessary();
 
   std::unique_ptr<net::MDnsSocketFactory> owned_socket_factory_;
   net::MDnsSocketFactory* socket_factory_;
@@ -206,15 +240,20 @@
   std::map<uint16_t, std::unique_ptr<SocketHandler>> socket_handler_by_id_;
   SocketHandlerStartResult start_result_ =
       SocketHandlerStartResult::UNSPECIFIED;
-#ifdef DEBUG
-  // Used in debug only for the extra uniqueness validation of names generated
-  // by responders.
+  // Used to bookkeep all names owned by |responders_| for
+  // 1. the extra uniqueness validation of names generated by responders;
+  // 2. generating responses to the mDNS name generator service queries.
   std::set<std::string> names_;
-#endif
   std::unique_ptr<NameGenerator> name_generator_;
+  // The names used to create the last response to a generator service query.
+  std::set<std::string> names_in_last_generator_response_;
+  bool should_respond_to_generator_service_query_ = true;
+
   std::set<std::unique_ptr<MdnsResponder>, base::UniquePtrComparator>
       responders_;
 
+  base::WeakPtrFactory<MdnsResponderManager> weak_factory_{this};
+
   DISALLOW_COPY_AND_ASSIGN(MdnsResponderManager);
 };
 
diff --git a/services/network/mdns_responder_unittest.cc b/services/network/mdns_responder_unittest.cc
index c7f6070..dd8d31f 100644
--- a/services/network/mdns_responder_unittest.cc
+++ b/services/network/mdns_responder_unittest.cc
@@ -19,6 +19,7 @@
 #include "base/test/scoped_task_environment.h"
 #include "mojo/public/cpp/bindings/connector.h"
 #include "net/base/ip_address.h"
+#include "net/base/net_errors.h"
 #include "net/dns/dns_query.h"
 #include "net/dns/dns_response.h"
 #include "net/dns/dns_util.h"
@@ -54,6 +55,11 @@
 const char kServiceErrorHistogram[] =
     "NetworkService.MdnsResponder.ServiceError";
 
+// Keep in sync with |kMdnsNameGeneratorServiceInstanceName| in
+// mdns_responder.cc.
+const char kMdnsNameGeneratorServiceInstanceName[] =
+    "Generated-Names._mdns_name_generator._udp.local";
+
 std::string CreateMdnsQuery(uint16_t query_id,
                             const std::string& dotted_name,
                             uint16_t qtype = net::dns_protocol::kTypeA) {
@@ -79,6 +85,49 @@
   return std::string(buf->data(), buf->size());
 }
 
+std::string CreateResponseToMdnsNameGeneratorServiceQuery(
+    const base::TimeDelta& ttl,
+    const std::set<std::string>& names) {
+  auto buf =
+      network::mdns_helper::CreateResponseToMdnsNameGeneratorServiceQuery(
+          ttl, names);
+
+  DCHECK(buf != nullptr);
+  return std::string(buf->data(), buf->size());
+}
+
+std::string CreateResponseToMdnsNameGeneratorServiceQueryWithCacheFlush(
+    const std::set<std::string>& names) {
+  auto buf =
+      network::mdns_helper::CreateResponseToMdnsNameGeneratorServiceQuery(
+          kDefaultTtl, names);
+  // Deserialize to set the cache-flush bit before serializing again.
+  net::DnsResponse response(buf.get(), buf->size());
+  bool rv = response.InitParseWithoutQuery(buf->size());
+  DCHECK(rv);
+  DCHECK_EQ(response.answer_count(), 1u);
+  net::DnsResourceRecord txt_record;
+  rv = response.Parser().ReadRecord(&txt_record);
+  DCHECK(rv);
+  DCHECK_EQ(net::dns_protocol::kTypeTXT, txt_record.type);
+  txt_record.klass |= net::dns_protocol::kFlagCacheFlush;
+  // Parsed record does not own the RDATA. Copy the owned RDATA before
+  // constructing a new response.
+  const std::string owned_rdata(txt_record.rdata);
+  txt_record.SetOwnedRdata(owned_rdata);
+  std::vector<net::DnsResourceRecord> answers(1, txt_record);
+  net::DnsResponse response_cache_flush(0 /* id */, true /* is_authoritative */,
+                                        answers, {} /* authority_records */,
+                                        {} /* additional_records */,
+                                        base::nullopt /* query */);
+  DCHECK(response_cache_flush.io_buffer() != nullptr);
+  buf = base::MakeRefCounted<net::IOBufferWithSize>(
+      response_cache_flush.io_buffer_size());
+  memcpy(buf->data(), response_cache_flush.io_buffer()->data(),
+         response_cache_flush.io_buffer_size());
+  return std::string(buf->data(), buf->size());
+}
+
 // A mock mDNS socket factory to create sockets that can fail sending or
 // receiving packets.
 class MockFailingMdnsSocketFactory : public net::MDnsSocketFactory {
@@ -145,12 +194,13 @@
                  int size,
                  net::IPEndPoint* address,
                  net::CompletionRepeatingCallback callback) {
-    task_runner_->PostTask(
-        FROM_HERE,
-        base::BindOnce(
-            [](net::CompletionRepeatingCallback callback) { callback.Run(-1); },
-            callback));
-    return -1;
+    task_runner_->PostTask(FROM_HERE,
+                           base::BindOnce(
+                               [](net::CompletionRepeatingCallback callback) {
+                                 callback.Run(net::ERR_FAILED);
+                               },
+                               callback));
+    return net::ERR_IO_PENDING;
   }
 
  private:
@@ -299,6 +349,34 @@
   EXPECT_EQ(expected_response, actual_response);
 }
 
+TEST(CreateMdnsResponseTest,
+     SingleTxtRecordAnswerToMdnsNameGeneratorServiceQuery) {
+  const char response_data[] = {
+      0x00, 0x00,  // mDNS response ID mus be zero.
+      0x84, 0x00,  // flags, response with authoritative answer
+      0x00, 0x00,  // number of questions
+      0x00, 0x01,  // number of answer rr
+      0x00, 0x00,  // number of name server rr
+      0x00, 0x00,  // number of additional rr
+      0x0f, 'G',  'e',  'n',  'e',  'r', 'a',  't', 'e', 'd', '-', 'N',
+      'a',  'm',  'e',  's',  0x14, '_', 'm',  'd', 'n', 's', '_', 'n',
+      'a',  'm',  'e',  '_',  'g',  'e', 'n',  'e', 'r', 'a', 't', 'o',
+      'r',  0x04, '_',  'u',  'd',  'p', 0x05, 'l', 'o', 'c', 'a', 'l',
+      0x00,                    // null label
+      0x00, 0x10,              // type A Record
+      0x00, 0x01,              // class IN, cache-flush bit NOT set
+      0x00, 0x00, 0x00, 0x78,  // TTL, 120 seconds
+      0x00, 0x2e,              // rdlength, 46 bytes for the following kv pairs
+      0x0d, 'n',  'a',  'm',  'e',  '0', '=',  '1', '.', 'l', 'o', 'c',
+      'a',  'l',  0x15, 'n',  'a',  'm', 'e',  '1', '=', 'w', 'w', 'w',
+      '.',  'e',  'x',  'a',  'm',  'p', 'l',  'e', '.', 'c', 'o', 'm',
+      0x09, 't',  'x',  't',  'v',  'e', 'r',  's', '=', '1'};
+  std::string expected_response(response_data, sizeof(response_data));
+  std::string actual_response = CreateResponseToMdnsNameGeneratorServiceQuery(
+      kDefaultTtl, {"1.local", "www.example.com"});
+  EXPECT_EQ(expected_response, actual_response);
+}
+
 class SimpleNameGenerator : public MdnsResponderManager::NameGenerator {
  public:
   std::string CreateName() override {
@@ -587,6 +665,45 @@
   }
 }
 
+// Test that the mDNS responder service can respond to an mDNS name generator
+// service query with all existing names.
+TEST_F(MdnsResponderTest,
+       SendResponseToMdnsNameGeneratorServiceQueryWithAllExistingNames) {
+  const auto& addr1 = kPublicAddrs[0];
+  const auto& addr2 = kPublicAddrs[1];
+  // Let two names be created by different clients.
+  const std::string name1 = CreateNameForAddress(0, addr1);
+  const std::string name2 = CreateNameForAddress(1, addr2);
+
+  const std::string query = CreateMdnsQuery(
+      0, kMdnsNameGeneratorServiceInstanceName, net::dns_protocol::kTypeTXT);
+  // The response should contain both names.
+  const std::string expected_response1 =
+      CreateResponseToMdnsNameGeneratorServiceQuery(kDefaultTtl,
+                                                    {name1, name2});
+
+  EXPECT_CALL(socket_factory_, OnSendTo(expected_response1)).Times(1);
+  socket_factory_.SimulateReceive(
+      reinterpret_cast<const uint8_t*>(query.data()), query.size());
+  RunUntilNoTasksRemain();
+
+  // Remove |name2|.
+  std::string expected_goodbye =
+      CreateResolutionResponse(base::TimeDelta(), {{name2, addr2}});
+  // Goodbye on both interfaces.
+  EXPECT_CALL(socket_factory_, OnSendTo(expected_goodbye)).Times(2);
+  RemoveNameForAddressAndExpectDone(1 /* client_id */, addr2);
+  RunUntilNoTasksRemain();
+
+  // The response should contain only |name1|.
+  const std::string expected_response2 =
+      CreateResponseToMdnsNameGeneratorServiceQuery(kDefaultTtl, {name1});
+  EXPECT_CALL(socket_factory_, OnSendTo(expected_response2)).Times(1);
+  socket_factory_.SimulateReceive(
+      reinterpret_cast<const uint8_t*>(query.data()), query.size());
+  RunUntilNoTasksRemain();
+}
+
 // Test that the responder manager closes the connection after
 // an invalid IP address is given to create a name for.
 TEST_F(MdnsResponderTest,
@@ -607,7 +724,8 @@
 
 // Test that the responder manager closes the connection after observing
 // conflicting name resolution in the network.
-TEST_F(MdnsResponderTest, HostClosesMojoConnectionAfterObservingNameConflict) {
+TEST_F(MdnsResponderTest,
+       HostClosesMojoConnectionAfterObservingAddressNameConflict) {
   const auto& addr1 = kPublicAddrs[0];
   const auto& addr2 = kPublicAddrs[1];
   const auto name1 = CreateNameForAddress(0, addr1);
@@ -645,6 +763,144 @@
   RunUntilNoTasksRemain();
 }
 
+// Test that we stop sending response to the mDNS name generator service queries
+// when there is an external response carrying a TXT record with the same
+// service instance name and the cache-flush bit set.
+TEST_F(MdnsResponderTest,
+       StopRespondingToGeneratorServiceQueryAfterObservingTxtNameConflict) {
+  const auto& addr = kPublicAddrs[0];
+  const std::string name = CreateNameForAddress(0, addr);
+
+  const std::string query = CreateMdnsQuery(
+      0, kMdnsNameGeneratorServiceInstanceName, net::dns_protocol::kTypeTXT);
+  // Verify that we can respond to the service query.
+  const std::string expected_response =
+      CreateResponseToMdnsNameGeneratorServiceQuery(kDefaultTtl, {name});
+  EXPECT_CALL(socket_factory_, OnSendTo(expected_response)).Times(1);
+  socket_factory_.SimulateReceive(
+      reinterpret_cast<const uint8_t*>(query.data()), query.size());
+  RunUntilNoTasksRemain();
+
+  // Receive a conflicting response.
+  const std::string conflicting_response =
+      CreateResponseToMdnsNameGeneratorServiceQueryWithCacheFlush(
+          {"dummy.local"});
+  socket_factory_.SimulateReceive(
+      reinterpret_cast<const uint8_t*>(conflicting_response.data()),
+      conflicting_response.size());
+  RunUntilNoTasksRemain();
+
+  // We should have stopped responding to service queries.
+  EXPECT_CALL(socket_factory_, OnSendTo(expected_response)).Times(0);
+  socket_factory_.SimulateReceive(
+      reinterpret_cast<const uint8_t*>(query.data()), query.size());
+  RunUntilNoTasksRemain();
+}
+
+// Test that an external response with the same service instance name but
+// without the cache-flush bit set is not considered a conflict.
+TEST_F(MdnsResponderTest,
+       NoConflictResolutionIfCacheFlushBitSetInExternalResponse) {
+  const auto& addr = kPublicAddrs[0];
+  const std::string name = CreateNameForAddress(0, addr);
+
+  // Receive an external response to the same instance name but without the
+  // cache-flush bit set in the TXT record.
+  const std::string nonconflict_response =
+      CreateResponseToMdnsNameGeneratorServiceQuery(kDefaultTtl,
+                                                    {"dummy.local"});
+  socket_factory_.SimulateReceive(
+      reinterpret_cast<const uint8_t*>(nonconflict_response.data()),
+      nonconflict_response.size());
+  RunUntilNoTasksRemain();
+
+  const std::string query = CreateMdnsQuery(
+      0, kMdnsNameGeneratorServiceInstanceName, net::dns_protocol::kTypeTXT);
+  // We can still respond to the service query.
+  const std::string expected_response =
+      CreateResponseToMdnsNameGeneratorServiceQuery(kDefaultTtl, {name});
+  EXPECT_CALL(socket_factory_, OnSendTo(expected_response)).Times(1);
+  socket_factory_.SimulateReceive(
+      reinterpret_cast<const uint8_t*>(query.data()), query.size());
+  RunUntilNoTasksRemain();
+}
+
+// Test that scheduled responses to mDNS name generator service queries can be
+// cancelled after observing conflict with external records.
+TEST_F(MdnsResponderTest,
+       CancelResponseToGeneratorServiceQueryAfterObservingTxtNameConflict) {
+  const auto& addr = kPublicAddrs[0];
+  const std::string name = CreateNameForAddress(0, addr);
+
+  // Receive a series of queries so that we have delayed responses scheduled
+  // because of rate limiting. We will also receive a conflicting response after
+  // the first response sent to cancel the subsequent ones.
+  const std::string query = CreateMdnsQuery(
+      0, kMdnsNameGeneratorServiceInstanceName, net::dns_protocol::kTypeTXT);
+  const std::string expected_response =
+      CreateResponseToMdnsNameGeneratorServiceQuery(kDefaultTtl, {name});
+  // We should have only the first response sent and the rest cancelled after
+  // encountering the conflicting.
+  EXPECT_CALL(socket_factory_, OnSendTo(expected_response)).Times(1);
+  socket_factory_.SimulateReceive(
+      reinterpret_cast<const uint8_t*>(query.data()), query.size());
+  socket_factory_.SimulateReceive(
+      reinterpret_cast<const uint8_t*>(query.data()), query.size());
+  socket_factory_.SimulateReceive(
+      reinterpret_cast<const uint8_t*>(query.data()), query.size());
+  RunFor(base::TimeDelta::FromMilliseconds(900));
+
+  // Receive a conflicting response.
+  const std::string conflicting_response =
+      CreateResponseToMdnsNameGeneratorServiceQueryWithCacheFlush(
+          {"dummy.local"});
+  socket_factory_.SimulateReceive(
+      reinterpret_cast<const uint8_t*>(conflicting_response.data()),
+      conflicting_response.size());
+
+  RunUntilNoTasksRemain();
+}
+
+// Test that if we ever send any response to mDNS name generator service
+// queries, a goodbye packet is sent when the responder manager is destroyed.
+TEST_F(MdnsResponderTest,
+       SendGoodbyeForMdnsNameGeneratorServiceAfterManagerDestroyed) {
+  const auto& addr = kPublicAddrs[0];
+  const std::string name = CreateNameForAddress(0, addr);
+
+  const std::string query = CreateMdnsQuery(
+      0, kMdnsNameGeneratorServiceInstanceName, net::dns_protocol::kTypeTXT);
+  const std::string expected_response =
+      CreateResponseToMdnsNameGeneratorServiceQuery(kDefaultTtl, {name});
+  // Respond to a generator service query once.
+  EXPECT_CALL(socket_factory_, OnSendTo(expected_response)).Times(1);
+  socket_factory_.SimulateReceive(
+      reinterpret_cast<const uint8_t*>(query.data()), query.size());
+  RunFor(base::TimeDelta::FromMilliseconds(1000));
+
+  // Goodbye on both interfaces.
+  const std::string expected_goodbye =
+      CreateResponseToMdnsNameGeneratorServiceQuery(base::TimeDelta(), {name});
+  EXPECT_CALL(socket_factory_, OnSendTo(expected_goodbye)).Times(2);
+  host_manager_ = nullptr;
+  RunUntilNoTasksRemain();
+}
+
+// Test that we do not send any goodbye packet to flush TXT records of
+// owned names when the responder manager is destroyed, if we have not sent any
+// response to mDNS name generator service queries.
+TEST_F(MdnsResponderTest,
+       NoGoodbyeForMdnsNameGeneratorServiceIfNoPreviousServiceResponseSent) {
+  const auto& addr = kPublicAddrs[0];
+  const std::string name = CreateNameForAddress(0, addr);
+
+  const std::string goodbye =
+      CreateResponseToMdnsNameGeneratorServiceQuery(base::TimeDelta(), {name});
+  EXPECT_CALL(socket_factory_, OnSendTo(goodbye)).Times(0);
+  host_manager_ = nullptr;
+  RunUntilNoTasksRemain();
+}
+
 // Test that the responder host clears all name-address maps in one goodbye
 // message with zero TTL for a client after the Mojo connection between them is
 // lost.
@@ -807,6 +1063,38 @@
   RunUntilNoTasksRemain();
 }
 
+// Test that responses to the name generator service queries are sent following
+// the per-record rate limit, so that each message is separated by at least one
+// second.
+TEST_F(MdnsResponderTest,
+       MdnsNameGeneratorServiceResponsesAreRateLimitedPerRecord) {
+  const auto& addr = kPublicAddrs[0];
+  const std::string name = CreateNameForAddress(0, addr);
+  // After a name has been created for |addr|, let the responder receive
+  // queries. Their responses should be scheduled sequentially, each separated
+  // by at least one second. Note that responses to the mDNS name generator
+  // service queries have an extra delay of 20-120ms as the service instance
+  // name is shared among Chrome instances.
+  const std::string query = CreateMdnsQuery(
+      0, kMdnsNameGeneratorServiceInstanceName, net::dns_protocol::kTypeTXT);
+  socket_factory_.SimulateReceive(
+      reinterpret_cast<const uint8_t*>(query.data()), query.size());
+  socket_factory_.SimulateReceive(
+      reinterpret_cast<const uint8_t*>(query.data()), query.size());
+
+  const std::string expected_response =
+      CreateResponseToMdnsNameGeneratorServiceQuery(kDefaultTtl, {"0.local"});
+  // Response to the first query is sent right after the query is received.
+  EXPECT_CALL(socket_factory_, OnSendTo(expected_response)).Times(1);
+
+  // Response to the second received query will be delayed for another one
+  // second plus an extra delay of 20-120ms.
+  RunFor(base::TimeDelta::FromMilliseconds(1015));
+  EXPECT_CALL(socket_factory_, OnSendTo(expected_response)).Times(1);
+
+  RunUntilNoTasksRemain();
+}
+
 // Test that responses with resource records for name resolution are sent based
 // on a per-record rate limit.
 TEST_F(MdnsResponderTest, ResolutionResponsesAreRateLimitedPerRecord) {