#include <algorithm>
#include <cmath>
#include <numeric>
#include <queue>
#include <string>
#include <utility>
#include "services/network/mdns_responder.h"
#include "base/big_endian.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/safe_conversions.h"
#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
#include "base/sys_byteorder.h"
#include "base/time/default_tick_clock.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/uuid.h"
#include "net/base/address_family.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_address.h"
#include "net/base/net_errors.h"
#include "net/dns/dns_names_util.h"
#include "net/dns/dns_response.h"
#include "net/dns/dns_util.h"
#include "net/dns/mdns_client.h"
#include "net/dns/public/dns_protocol.h"
#include "net/dns/public/util.h"
#include "net/dns/record_parsed.h"
#include "net/dns/record_rdata.h"
#include "net/socket/datagram_server_socket.h"
#include "net/socket/udp_server_socket.h"
#include "services/network/public/cpp/features.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace network {
namespace {
using MdnsResponderServiceError = MdnsResponderManager::ServiceError;
const base::TimeDelta kMinIntervalBetweenSameRecord = base::Seconds(1);
const base::TimeDelta kMinIntervalBetweenMdnsResponses = base::Seconds(1);
const base::TimeDelta kDefaultTtlForRecordWithHostname = base::Seconds(120);
const int kMinNumAnnouncementsToSend = 2;
const uint8_t kMaxMdnsResponseRetries = 2;
const uint8_t kSendQueueCapacity = 100;
const base::TimeDelta kMaxScheduledDelay = base::Seconds(10);
const char kMdnsNameGeneratorServiceInstanceName[] =
"Generated-Names._mdns_name_generator._udp.local";
const uint16_t kMaxTxtRecordSizeInBytes = 1300;
const int kMaxKeySizeInTxtRecord = 9;
const char kKeyPrefixInTxtRecord[] = "name";
const char kTxtversLine[] = "\x9txtvers=1";
const base::TimeDelta kMinRandDelayForSharedResult = base::Milliseconds(20);
const base::TimeDelta kMaxRandDelayForSharedResult = base::Milliseconds(120);
class RandomUuidNameGenerator
: public network::MdnsResponderManager::NameGenerator {
public:
std::string CreateName() override {
return base::Uuid::GenerateRandomV4().AsLowercaseString();
}
};
bool QueryTypeAndAddressFamilyAreCompatible(uint16_t qtype,
net::AddressFamily af) {
switch (qtype) {
case net::dns_protocol::kTypeA:
return af == net::ADDRESS_FAMILY_IPV4;
case net::dns_protocol::kTypeAAAA:
return af == net::ADDRESS_FAMILY_IPV6;
case net::dns_protocol::kTypeANY:
return af == net::ADDRESS_FAMILY_IPV4 || af == net::ADDRESS_FAMILY_IPV6;
default:
return false;
}
}
std::vector<net::DnsResourceRecord> CreateAddressResourceRecords(
const std::map<std::string, net::IPAddress>& name_addr_map,
const base::TimeDelta& ttl) {
std::vector<net::DnsResourceRecord> address_records;
for (const auto& name_addr_pair : name_addr_map) {
const auto& ip = name_addr_pair.second;
DCHECK(ip.IsIPv4() || ip.IsIPv6());
net::DnsResourceRecord record;
record.name = name_addr_pair.first;
record.type = (ip.IsIPv4() ? net::dns_protocol::kTypeA
: net::dns_protocol::kTypeAAAA);
record.klass =
net::dns_protocol::kClassIN | net::dns_protocol::kFlagCacheFlush;
record.ttl = base::checked_cast<uint32_t>(ttl.InSeconds());
record.SetOwnedRdata(net::IPAddressToPackedString(ip));
address_records.push_back(std::move(record));
}
return address_records;
}
std::string CreateNsecRdata(const net::IPAddress& addr,
uint16_t containing_nsec_rr_offset) {
DCHECK(addr.IsIPv4() || addr.IsIPv6());
std::string next_domain_name =
net::CreateNamePointer(containing_nsec_rr_offset);
DCHECK_EQ(2u, next_domain_name.size());
if (addr.IsIPv4())
return next_domain_name + std::string("\x00\x01\x40", 3);
return next_domain_name + std::string("\x00\x04\x00\x00\x00\x08", 6);
}
std::vector<net::DnsResourceRecord> CreateNsecResourceRecords(
const std::map<std::string, net::IPAddress>& name_addr_map,
uint16_t first_nsec_rr_offset) {
std::vector<net::DnsResourceRecord> nsec_records;
uint16_t cur_rr_offset = first_nsec_rr_offset;
for (const auto& name_addr_pair : name_addr_map) {
net::DnsResourceRecord record;
record.name = name_addr_pair.first;
record.type = net::dns_protocol::kTypeNSEC;
record.klass =
net::dns_protocol::kClassIN | net::dns_protocol::kFlagCacheFlush;
record.ttl = kDefaultTtlForRecordWithHostname.InSeconds();
record.SetOwnedRdata(CreateNsecRdata(name_addr_pair.second, cur_rr_offset));
cur_rr_offset += record.CalculateRecordSize();
nsec_records.push_back(std::move(record));
}
return nsec_records;
}
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 ;
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 +
(idx > 0 ? static_cast<int>(log10(static_cast<double>(idx))) + 1 : 1);
DCHECK_LE(key_size, kMaxKeySizeInTxtRecord);
const int line_size =
2 + key_size + name.size();
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;
}
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());
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;
txt.klass = net::dns_protocol::kClassIN;
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) {
return query.qtype() == net::dns_protocol::kTypeANY;
}
void ReportServiceError(MdnsResponderServiceError error) {
UMA_HISTOGRAM_ENUMERATION("NetworkService.MdnsResponder.ServiceError", error);
}
struct PendingPacket {
PendingPacket(scoped_refptr<net::IOBufferWithSize> buf,
scoped_refptr<MdnsResponseSendOption> option,
const base::TimeTicks& send_ready_time)
: buf(std::move(buf)),
option(std::move(option)),
send_ready_time(send_ready_time) {}
bool operator<(const PendingPacket& other) const {
return send_ready_time > other.send_ready_time;
}
scoped_refptr<net::IOBufferWithSize> buf;
scoped_refptr<MdnsResponseSendOption> option;
base::TimeTicks send_ready_time;
};
base::TimeDelta GetRandTimeDelta(const base::TimeDelta& min,
const base::TimeDelta& max) {
DCHECK_LE(min, max);
return base::Microseconds(
base::RandInt(min.InMicroseconds(), max.InMicroseconds()));
}
}
namespace mdns_helper {
scoped_refptr<net::IOBufferWithSize> CreateResolutionResponse(
const base::TimeDelta& ttl,
const std::map<std::string, net::IPAddress>& name_addr_map) {
DCHECK(!name_addr_map.empty());
std::vector<net::DnsResourceRecord> answers =
CreateAddressResourceRecords(name_addr_map, ttl);
std::vector<net::DnsResourceRecord> additional_records;
if (!ttl.is_zero()) {
uint16_t cur_size = std::accumulate(
answers.begin(), answers.end(), sizeof(net::dns_protocol::Header),
[](size_t cur_size, const net::DnsResourceRecord& answer) {
return cur_size + answer.CalculateRecordSize();
});
additional_records = CreateNsecResourceRecords(name_addr_map, cur_size);
}
net::DnsResponse response(0 , true , answers,
{} , additional_records,
absl::nullopt );
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> CreateNegativeResponse(
const std::map<std::string, net::IPAddress>& name_addr_map) {
DCHECK(!name_addr_map.empty());
std::vector<net::DnsResourceRecord> nsec_records = CreateNsecResourceRecords(
name_addr_map, sizeof(net::dns_protocol::Header));
std::vector<net::DnsResourceRecord> additional_records =
CreateAddressResourceRecords(name_addr_map,
kDefaultTtlForRecordWithHostname);
net::DnsResponse response(0 , true ,
nsec_records, {} ,
additional_records, absl::nullopt );
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, true, answers,
{},
{},
absl::nullopt,
net::dns_protocol::kRcodeNOERROR,
true,
false);
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;
}
}
class MdnsResponderManager::SocketHandler {
public:
SocketHandler(uint16_t id,
std::unique_ptr<net::DatagramServerSocket> socket,
MdnsResponderManager* responder_manager)
: id_(id),
scheduler_(std::make_unique<ResponseScheduler>(this)),
socket_(std::move(socket)),
responder_manager_(responder_manager),
io_buffer_(base::MakeRefCounted<net::IOBufferWithSize>(
net::dns_protocol::kMaxMulticastSize + 1)) {}
SocketHandler(const SocketHandler&) = delete;
SocketHandler& operator=(const SocketHandler&) = delete;
~SocketHandler() = default;
int Start() {
net::IPEndPoint end_point;
int rv = socket_->GetLocalAddress(&end_point);
if (rv != net::OK) {
return rv;
}
DCHECK(end_point.GetFamily() == net::ADDRESS_FAMILY_IPV4 ||
end_point.GetFamily() == net::ADDRESS_FAMILY_IPV6);
multicast_addr_ =
net::dns_util::GetMdnsGroupEndPoint(end_point.GetFamily());
int result = DoReadLoop();
if (result == net::ERR_IO_PENDING) {
return net::OK;
}
return result;
}
bool Send(scoped_refptr<net::IOBufferWithSize> buf,
scoped_refptr<MdnsResponseSendOption> option);
int DoSend(PendingPacket pending_packet);
uint16_t id() const { return id_; }
void SetTickClockForTesting(const base::TickClock* tick_clock);
private:
class ResponseScheduler;
int HandlePacket(int result);
int DoReadLoop() {
int result;
do {
result = socket_->RecvFrom(
io_buffer_.get(), io_buffer_->size(), &recv_addr_,
base::BindOnce(&MdnsResponderManager::SocketHandler::OnRead,
base::Unretained(this)));
result = HandlePacket(result);
} while (result >= 0);
return result;
}
void OnRead(int 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);
}
uint16_t id_;
std::unique_ptr<ResponseScheduler> scheduler_;
std::unique_ptr<net::DatagramServerSocket> socket_;
const raw_ptr<MdnsResponderManager> responder_manager_;
scoped_refptr<net::IOBufferWithSize> io_buffer_;
net::IPEndPoint recv_addr_;
net::IPEndPoint multicast_addr_;
base::WeakPtrFactory<SocketHandler> weak_factory_{this};
};
class MdnsResponderManager::SocketHandler::ResponseScheduler {
public:
enum class RateLimitScheme {
PER_RESPONSE,
PER_RECORD,
NO_LIMIT,
};
explicit ResponseScheduler(MdnsResponderManager::SocketHandler* handler)
: handler_(handler),
tick_clock_(base::DefaultTickClock::GetInstance()),
dispatch_timer_(std::make_unique<base::OneShotTimer>(tick_clock_)),
next_available_time_per_resp_sched_(tick_clock_->NowTicks()) {}
ResponseScheduler(const ResponseScheduler&) = delete;
ResponseScheduler& operator=(const ResponseScheduler&) = delete;
~ResponseScheduler() { dispatch_timer_->Stop(); }
bool ScheduleNextSend(scoped_refptr<net::IOBufferWithSize> buf,
scoped_refptr<MdnsResponseSendOption> option);
void OnResponseSent(PendingPacket pending_packet, int result) {
DCHECK(send_pending_);
send_pending_ = false;
scoped_refptr<MdnsResponseSendOption>& option = pending_packet.option;
if (result < 0) {
VLOG(1) << "Socket send error, socket=" << handler_->id()
<< ", error=" << result;
if (CanBeRetriedAfterSendFailure(*option)) {
++option->num_send_retries_done;
send_queue_.push(std::move(pending_packet));
} else {
VLOG(1) << "Response cannot be sent after " << kMaxMdnsResponseRetries
<< " retries.";
}
}
DispatchPendingPackets();
}
void SetTickClockForTesting(const base::TickClock* tick_clock) {
tick_clock_ = tick_clock;
dispatch_timer_ = std::make_unique<base::OneShotTimer>(tick_clock_);
next_available_time_per_resp_sched_ = tick_clock_->NowTicks();
next_available_time_for_name_.clear();
}
base::WeakPtr<ResponseScheduler> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
private:
RateLimitScheme GetRateLimitSchemeForClass(
MdnsResponseSendOption::ResponseClass klass) {
switch (klass) {
case MdnsResponseSendOption::ResponseClass::ANNOUNCEMENT:
case MdnsResponseSendOption::ResponseClass::GOODBYE:
return RateLimitScheme::PER_RESPONSE;
case MdnsResponseSendOption::ResponseClass::NEGATIVE:
case MdnsResponseSendOption::ResponseClass::REGULAR_RESOLUTION:
return RateLimitScheme::PER_RECORD;
case MdnsResponseSendOption::ResponseClass::PROBE_RESOLUTION:
return RateLimitScheme::NO_LIMIT;
case MdnsResponseSendOption::ResponseClass::UNSPECIFIED:
NOTREACHED();
return RateLimitScheme::PER_RESPONSE;
}
}
absl::optional<base::TimeDelta>
ComputeResponseDelayAndUpdateNextAvailableTime(
RateLimitScheme rate_limit_scheme,
const MdnsResponseSendOption& option);
void DispatchPendingPackets();
bool CanBeRetriedAfterSendFailure(const MdnsResponseSendOption& option) {
if (option.num_send_retries_done >= kMaxMdnsResponseRetries)
return false;
if (option.klass == MdnsResponseSendOption::ResponseClass::ANNOUNCEMENT ||
option.klass == MdnsResponseSendOption::ResponseClass::GOODBYE ||
option.klass == MdnsResponseSendOption::ResponseClass::PROBE_RESOLUTION)
return true;
return false;
}
const raw_ptr<MdnsResponderManager::SocketHandler> handler_;
raw_ptr<const base::TickClock> tick_clock_;
std::unique_ptr<base::OneShotTimer> dispatch_timer_;
std::map<std::string, base::TimeTicks> next_available_time_for_name_;
base::TimeTicks next_available_time_per_resp_sched_;
bool send_pending_ = false;
std::priority_queue<PendingPacket> send_queue_;
base::WeakPtrFactory<ResponseScheduler> weak_factory_{this};
};
bool MdnsResponderManager::SocketHandler::Send(
scoped_refptr<net::IOBufferWithSize> buf,
scoped_refptr<MdnsResponseSendOption> option) {
return scheduler_->ScheduleNextSend(std::move(buf), std::move(option));
}
int MdnsResponderManager::SocketHandler::DoSend(PendingPacket pending_packet) {
auto* buf_data = pending_packet.buf.get();
size_t buf_size = pending_packet.buf->size();
return socket_->SendTo(
buf_data, buf_size, multicast_addr_,
base::BindOnce(&ResponseScheduler::OnResponseSent,
scheduler_->GetWeakPtr(), std::move(pending_packet)));
}
void MdnsResponderManager::SocketHandler::SetTickClockForTesting(
const base::TickClock* tick_clock) {
scheduler_->SetTickClockForTesting(tick_clock);
}
bool MdnsResponderManager::SocketHandler::ResponseScheduler::ScheduleNextSend(
scoped_refptr<net::IOBufferWithSize> buf,
scoped_refptr<MdnsResponseSendOption> option) {
if (send_queue_.size() >= kSendQueueCapacity) {
VLOG(1)
<< "mDNS packet discarded after reaching the capacity of send queue.";
return false;
}
auto rate_limit_scheme = GetRateLimitSchemeForClass(option->klass);
absl::optional<base::TimeDelta> delay;
if (rate_limit_scheme == RateLimitScheme::NO_LIMIT) {
delay = base::TimeDelta();
} else {
delay = ComputeResponseDelayAndUpdateNextAvailableTime(rate_limit_scheme,
*option);
}
if (!delay)
return false;
PendingPacket pending_packet(std::move(buf), std::move(option),
tick_clock_->NowTicks() + delay.value());
send_queue_.push(std::move(pending_packet));
DispatchPendingPackets();
return true;
}
absl::optional<base::TimeDelta> MdnsResponderManager::SocketHandler::
ResponseScheduler::ComputeResponseDelayAndUpdateNextAvailableTime(
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();
if (rate_limit_scheme == RateLimitScheme::PER_RESPONSE) {
auto delay =
std::max(next_available_time_per_resp_sched_ - now, base::TimeDelta()) +
extra_delay_for_shared_result;
if (delay > kMaxScheduledDelay)
return absl::nullopt;
next_available_time_per_resp_sched_ =
now + delay + kMinIntervalBetweenMdnsResponses;
return delay;
}
DCHECK(rate_limit_scheme == RateLimitScheme::PER_RECORD);
DCHECK(!option.names_for_rate_limit.empty());
auto next_available_time_for_response = now;
for (const auto& name : option.names_for_rate_limit) {
next_available_time_for_response = std::max(
next_available_time_for_response, next_available_time_for_name_[name]);
}
base::TimeDelta delay =
std::max(next_available_time_for_response - now, base::TimeDelta()) +
extra_delay_for_shared_result;
if (delay > kMaxScheduledDelay)
return absl::nullopt;
for (const auto& name : option.names_for_rate_limit) {
next_available_time_for_name_[name] =
next_available_time_for_response + kMinIntervalBetweenSameRecord;
}
return delay;
}
void MdnsResponderManager::SocketHandler::ResponseScheduler::
DispatchPendingPackets() {
while (!send_pending_ && !send_queue_.empty()) {
const auto now = tick_clock_->NowTicks();
const auto next_send_ready_time = send_queue_.top().send_ready_time;
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;
} else if (rv < net::OK) {
VLOG(1) << "mDNS packet discarded due to socket send error, socket="
<< handler_->id() << ", error=" << rv;
}
} else {
const base::TimeDelta time_to_next_packet = next_send_ready_time - now;
dispatch_timer_->Start(
FROM_HERE, time_to_next_packet,
base::BindOnce(&MdnsResponderManager::SocketHandler::
ResponseScheduler::DispatchPendingPackets,
GetWeakPtr()));
return;
}
}
}
MdnsResponseSendOption::MdnsResponseSendOption() = default;
MdnsResponseSendOption::~MdnsResponseSendOption() = default;
constexpr base::TimeDelta MdnsResponderManager::kManagerStartThrottleDelay;
MdnsResponderManager::MdnsResponderManager() : MdnsResponderManager(nullptr) {}
MdnsResponderManager::MdnsResponderManager(
net::MDnsSocketFactory* socket_factory)
: socket_factory_(socket_factory),
name_generator_(std::make_unique<RandomUuidNameGenerator>()) {
if (!socket_factory_) {
owned_socket_factory_ = net::MDnsSocketFactory::CreateDefault();
socket_factory_ = owned_socket_factory_.get();
}
StartIfNeeded();
}
MdnsResponderManager::~MdnsResponderManager() {
SendGoodbyePacketForMdnsNameGeneratorServiceIfNecessary();
responders_.clear();
}
void MdnsResponderManager::StartIfNeeded() {
if (start_result_ == SocketHandlerStartResult::ALL_SUCCESS ||
start_result_ == SocketHandlerStartResult::PARTIAL_SUCCESS) {
return;
}
if (!throttled_start_end_.is_null() &&
tick_clock_->NowTicks() < throttled_start_end_) {
DCHECK(start_result_ == SocketHandlerStartResult::ALL_FAILURE);
return;
}
throttled_start_end_ = base::TimeTicks();
VLOG(1) << "Starting mDNS responder manager.";
DCHECK(start_result_ == SocketHandlerStartResult::UNSPECIFIED ||
start_result_ == SocketHandlerStartResult::ALL_FAILURE);
DCHECK(socket_handler_by_id_.empty());
std::vector<std::unique_ptr<net::DatagramServerSocket>> sockets;
socket_factory_->CreateSockets(&sockets);
uint16_t next_available_id = 1;
for (std::unique_ptr<net::DatagramServerSocket>& socket : sockets) {
socket_handler_by_id_.emplace(
next_available_id,
std::make_unique<MdnsResponderManager::SocketHandler>(
next_available_id, std::move(socket), this));
++next_available_id;
}
for (auto it = socket_handler_by_id_.begin();
it != socket_handler_by_id_.end();) {
int rv = it->second->Start();
if (rv == net::OK) {
++it;
} else {
VLOG(1) << "Start failed, socket=" << it->second->id()
<< ", error=" << rv;
it = socket_handler_by_id_.erase(it);
}
}
size_t num_started_socket_handlers = socket_handler_by_id_.size();
if (socket_handler_by_id_.empty()) {
start_result_ = SocketHandlerStartResult::ALL_FAILURE;
throttled_start_end_ = tick_clock_->NowTicks() + kManagerStartThrottleDelay;
LOG(ERROR) << "mDNS responder manager failed to start.";
ReportServiceError(MdnsResponderServiceError::kFailToStartManager);
return;
}
if (num_started_socket_handlers == next_available_id) {
start_result_ = SocketHandlerStartResult::ALL_SUCCESS;
return;
}
start_result_ = SocketHandlerStartResult::PARTIAL_SUCCESS;
}
void MdnsResponderManager::CreateMdnsResponder(
mojo::PendingReceiver<mojom::MdnsResponder> receiver) {
StartIfNeeded();
if (start_result_ == SocketHandlerStartResult::UNSPECIFIED ||
start_result_ == SocketHandlerStartResult::ALL_FAILURE) {
LOG(ERROR) << "The mDNS responder manager is not started yet.";
ReportServiceError(MdnsResponderServiceError::kFailToCreateResponder);
receiver = mojo::NullReceiver();
return;
}
auto responder = std::make_unique<MdnsResponder>(std::move(receiver), this);
responders_.insert(std::move(responder));
}
bool MdnsResponderManager::Send(scoped_refptr<net::IOBufferWithSize> buf,
scoped_refptr<MdnsResponseSendOption> option) {
DCHECK(buf != nullptr);
bool all_success = true;
if (option->send_socket_handler_ids.empty()) {
for (auto& id_handler_pair : socket_handler_by_id_)
all_success &= id_handler_pair.second->Send(buf, option);
return all_success;
}
for (auto id : option->send_socket_handler_ids) {
DCHECK(socket_handler_by_id_.find(id) != socket_handler_by_id_.end());
all_success &= socket_handler_by_id_[id]->Send(buf, option);
}
return all_success;
}
void MdnsResponderManager::OnMojoConnectionError(MdnsResponder* responder) {
auto it = responders_.find(responder);
DCHECK(it != responders_.end());
responders_.erase(it);
}
void MdnsResponderManager::SetNameGeneratorForTesting(
std::unique_ptr<MdnsResponderManager::NameGenerator> name_generator) {
name_generator_ = std::move(name_generator);
for (auto& responder : responders_)
responder->SetNameGeneratorForTesting(name_generator_.get());
}
void MdnsResponderManager::SetTickClockForTesting(
const base::TickClock* tick_clock) {
tick_clock_ = tick_clock;
for (auto& id_handler_pair : socket_handler_by_id_) {
id_handler_pair.second->SetTickClockForTesting(tick_clock);
}
}
void MdnsResponderManager::HandleAddressNameConflictIfAny(
const std::map<std::string, std::set<net::IPAddress>>& external_maps) {
for (const auto& name_to_addresses : external_maps) {
for (auto& responder : responders_) {
if (responder->HasConflictWithExternalResolution(
name_to_addresses.first, {name_to_addresses.second})) {
OnMojoConnectionError(responder.get());
break;
}
}
}
}
void MdnsResponderManager::HandleTxtNameConflict() {
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) {
const absl::optional<std::string> qname =
net::dns_names_util::NetworkToDottedName(query.qname());
if (base::FeatureList::IsEnabled(
features::kMdnsResponderGeneratedNameListing)) {
if (should_respond_to_generator_service_query_ && qname &&
qname.value() == kMdnsNameGeneratorServiceInstanceName) {
HandleMdnsNameGeneratorServiceQuery(query, recv_socket_handler_id);
return;
}
}
for (auto& responder : responders_)
responder->OnMdnsQueryReceived(query, recv_socket_handler_id);
}
void MdnsResponderManager::OnSocketHandlerReadError(uint16_t socket_handler_id,
int result) {
VLOG(1) << "Socket read error, socket=" << socket_handler_id
<< ", error=" << result;
DCHECK(IsFatalError(result));
auto it = socket_handler_by_id_.find(socket_handler_id);
DCHECK(it != socket_handler_by_id_.end());
socket_handler_by_id_.erase(it);
if (socket_handler_by_id_.empty()) {
LOG(ERROR)
<< "All socket handlers failed. Restarting the mDNS responder manager.";
ReportServiceError(MdnsResponderServiceError::kFatalSocketHandlerError);
start_result_ = MdnsResponderManager::SocketHandlerStartResult::UNSPECIFIED;
DCHECK(throttled_start_end_.is_null());
StartIfNeeded();
}
}
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 true;
}
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;
}
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_;
}
void MdnsResponderManager::
SendGoodbyePacketForMdnsNameGeneratorServiceIfNecessary() {
if (names_in_last_generator_response_.empty())
return;
auto option = base::MakeRefCounted<MdnsResponseSendOption>();
option->klass = MdnsResponseSendOption::ResponseClass::GOODBYE;
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_);
return result;
}
net::DnsResponse response(io_buffer_.get(), io_buffer_->size());
if (!response.InitParseWithoutQuery(io_buffer_->size()) ||
response.answer_count() == 0)
return result;
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)
has_txt_record_conflict = true;
break;
}
default:
break;
}
}
responder_manager_->HandleAddressNameConflictIfAny(external_address_maps);
if (has_txt_record_conflict)
responder_manager_->HandleTxtNameConflict();
return result;
}
MdnsResponder::MdnsResponder(
mojo::PendingReceiver<mojom::MdnsResponder> receiver,
MdnsResponderManager* manager)
: receiver_(this, std::move(receiver)),
manager_(manager),
name_generator_(manager_->name_generator()) {
receiver_.set_disconnect_handler(
base::BindOnce(&MdnsResponderManager::OnMojoConnectionError,
base::Unretained(manager_), this));
}
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_);
}
void MdnsResponder::CreateNameForAddress(
const net::IPAddress& address,
mojom::MdnsResponder::CreateNameForAddressCallback callback) {
DCHECK(address.IsValid() || address.empty());
if (!address.IsValid()) {
LOG(ERROR) << "Invalid IP address to create a name for";
ReportServiceError(MdnsResponderServiceError::kInvalidIpToRegisterName);
receiver_.reset();
manager_->OnMojoConnectionError(this);
return;
}
std::string name;
auto it = FindNameCreatedForAddress(address);
bool announcement_sched_at_least_once = false;
if (it == name_addr_map_.end()) {
name = name_generator_->CreateName() + ".local";
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;
std::map<std::string, net::IPAddress> map_to_announce({{name, address}});
auto option = base::MakeRefCounted<MdnsResponseSendOption>();
option->klass = MdnsResponseSendOption::ResponseClass::ANNOUNCEMENT;
for (int i = 0; i < kMinNumAnnouncementsToSend; ++i) {
bool announcement_scheduled = SendMdnsResponse(
mdns_helper::CreateResolutionResponse(
kDefaultTtlForRecordWithHostname, map_to_announce),
option);
announcement_sched_at_least_once |= announcement_scheduled;
if (!announcement_scheduled)
break;
}
} else {
name = it->first;
DCHECK(name_refcount_map_.find(name) != name_refcount_map_.end());
++name_refcount_map_[name];
}
std::move(callback).Run(name, announcement_sched_at_least_once);
}
void MdnsResponder::RemoveNameForAddress(
const net::IPAddress& address,
mojom::MdnsResponder::RemoveNameForAddressCallback callback) {
DCHECK(address.IsValid() || address.empty());
auto it = FindNameCreatedForAddress(address);
if (it == name_addr_map_.end()) {
std::move(callback).Run(false , false );
return;
}
std::string name = it->first;
DCHECK(name_refcount_map_.find(name) != name_refcount_map_.end());
auto refcount = --name_refcount_map_[name];
bool goodbye_scheduled = false;
if (refcount == 0) {
goodbye_scheduled = SendGoodbyePacketForNameAddressMap({*it});
bool rv = manager_->RemoveName(name);
DCHECK(rv);
name_refcount_map_.erase(name);
name_addr_map_.erase(it);
}
DCHECK(refcount == 0 || !goodbye_scheduled);
std::move(callback).Run(refcount == 0, goodbye_scheduled);
}
void MdnsResponder::OnMdnsQueryReceived(const net::DnsQuery& query,
uint16_t recv_socket_handler_id) {
absl::optional<std::string> dotted_name_to_resolve =
net::dns_names_util::NetworkToDottedName(query.qname());
if (!dotted_name_to_resolve)
return;
auto it = name_addr_map_.find(dotted_name_to_resolve.value());
if (it == name_addr_map_.end())
return;
std::map<std::string, net::IPAddress> map_to_respond({*it});
auto option = base::MakeRefCounted<MdnsResponseSendOption>();
option->send_socket_handler_ids.insert(recv_socket_handler_id);
option->names_for_rate_limit.insert(it->first);
if (!QueryTypeAndAddressFamilyAreCompatible(query.qtype(),
GetAddressFamily(it->second))) {
option->klass = MdnsResponseSendOption::ResponseClass::NEGATIVE;
SendMdnsResponse(mdns_helper::CreateNegativeResponse(map_to_respond),
std::move(option));
return;
}
if (IsProbeQuery(query))
option->klass = MdnsResponseSendOption::ResponseClass::PROBE_RESOLUTION;
else
option->klass = MdnsResponseSendOption::ResponseClass::REGULAR_RESOLUTION;
SendMdnsResponse(mdns_helper::CreateResolutionResponse(
kDefaultTtlForRecordWithHostname, map_to_respond),
std::move(option));
}
bool MdnsResponder::HasConflictWithExternalResolution(
const std::string& name,
const std::set<net::IPAddress>& external_mapped_addreses) {
DCHECK(!external_mapped_addreses.empty());
auto matching_record_it = name_addr_map_.find(name);
if (matching_record_it == name_addr_map_.end())
return false;
if (external_mapped_addreses.size() == 1 &&
*external_mapped_addreses.begin() == matching_record_it->second) {
VLOG(1) << "Received an external response for an owned record.";
return false;
}
LOG(ERROR) << "Received conflicting resolution for name: " << name;
ReportServiceError(MdnsResponderServiceError::kConflictingNameResolution);
return true;
}
bool MdnsResponder::SendMdnsResponse(
scoped_refptr<net::IOBufferWithSize> response,
scoped_refptr<MdnsResponseSendOption> option) {
DCHECK_NE(MdnsResponseSendOption::ResponseClass::UNSPECIFIED, option->klass);
return manager_->Send(std::move(response), std::move(option));
}
bool MdnsResponder::SendGoodbyePacketForNameAddressMap(
const std::map<std::string, net::IPAddress>& name_addr_map) {
if (name_addr_map.empty())
return false;
auto option = base::MakeRefCounted<MdnsResponseSendOption>();
option->klass = MdnsResponseSendOption::ResponseClass::GOODBYE;
return SendMdnsResponse(mdns_helper::CreateResolutionResponse(
base::TimeDelta() , name_addr_map),
std::move(option));
}
std::map<std::string, net::IPAddress>::iterator
MdnsResponder::FindNameCreatedForAddress(const net::IPAddress& address) {
auto ret = name_addr_map_.end();
size_t count = 0;
for (auto it = name_addr_map_.begin(); it != name_addr_map_.end(); ++it) {
if (it->second == address) {
ret = it;
++count;
DCHECK_LE(count, 1u);
}
}
return ret;
}
}