You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
869 lines
34 KiB
869 lines
34 KiB
// 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.
|
|
|
|
#include "discovery/mdns/mdns_responder.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "discovery/common/config.h"
|
|
#include "discovery/mdns/mdns_probe_manager.h"
|
|
#include "discovery/mdns/mdns_random.h"
|
|
#include "discovery/mdns/mdns_receiver.h"
|
|
#include "discovery/mdns/mdns_records.h"
|
|
#include "discovery/mdns/mdns_sender.h"
|
|
#include "platform/test/fake_clock.h"
|
|
#include "platform/test/fake_task_runner.h"
|
|
#include "platform/test/fake_udp_socket.h"
|
|
|
|
namespace openscreen {
|
|
namespace discovery {
|
|
namespace {
|
|
|
|
constexpr Clock::duration kMaximumSharedRecordResponseDelayMs(120 * 1000);
|
|
|
|
bool ContainsRecordType(const std::vector<MdnsRecord>& records, DnsType type) {
|
|
return std::find_if(records.begin(), records.end(),
|
|
[type](const MdnsRecord& record) {
|
|
return record.dns_type() == type;
|
|
}) != records.end();
|
|
}
|
|
|
|
void CheckSingleNsecRecordType(const MdnsMessage& message, DnsType type) {
|
|
ASSERT_EQ(message.answers().size(), size_t{1});
|
|
const MdnsRecord record = message.answers()[0];
|
|
|
|
ASSERT_EQ(record.dns_type(), DnsType::kNSEC);
|
|
const NsecRecordRdata& rdata = absl::get<NsecRecordRdata>(record.rdata());
|
|
|
|
ASSERT_EQ(rdata.types().size(), size_t{1});
|
|
EXPECT_EQ(rdata.types()[0], type);
|
|
}
|
|
|
|
void CheckPtrDomain(const MdnsRecord& record, const DomainName& domain) {
|
|
ASSERT_EQ(record.dns_type(), DnsType::kPTR);
|
|
const PtrRecordRdata& rdata = absl::get<PtrRecordRdata>(record.rdata());
|
|
|
|
EXPECT_EQ(rdata.ptr_domain(), domain);
|
|
}
|
|
|
|
void ExpectContainsNsecRecordType(const std::vector<MdnsRecord>& records,
|
|
DnsType type) {
|
|
auto it = std::find_if(
|
|
records.begin(), records.end(), [type](const MdnsRecord& record) {
|
|
if (record.dns_type() != DnsType::kNSEC) {
|
|
return false;
|
|
}
|
|
|
|
const NsecRecordRdata& rdata =
|
|
absl::get<NsecRecordRdata>(record.rdata());
|
|
return rdata.types().size() == 1 && rdata.types()[0] == type;
|
|
});
|
|
EXPECT_TRUE(it != records.end());
|
|
}
|
|
|
|
} // namespace
|
|
|
|
using testing::_;
|
|
using testing::Args;
|
|
using testing::Invoke;
|
|
using testing::Return;
|
|
using testing::StrictMock;
|
|
|
|
class MockRecordHandler : public MdnsResponder::RecordHandler {
|
|
public:
|
|
void AddRecord(MdnsRecord record) { records_.push_back(record); }
|
|
|
|
MOCK_METHOD3(HasRecords, bool(const DomainName&, DnsType, DnsClass));
|
|
|
|
std::vector<MdnsRecord::ConstRef> GetRecords(const DomainName& name,
|
|
DnsType type,
|
|
DnsClass clazz) override {
|
|
std::vector<MdnsRecord::ConstRef> records;
|
|
for (const auto& record : records_) {
|
|
if (type == DnsType::kANY || record.dns_type() == type) {
|
|
records.push_back(record);
|
|
}
|
|
}
|
|
|
|
return records;
|
|
}
|
|
|
|
std::vector<MdnsRecord::ConstRef> GetPtrRecords(DnsClass clazz) override {
|
|
std::vector<MdnsRecord::ConstRef> records;
|
|
for (const auto& record : records_) {
|
|
if (record.dns_type() == DnsType::kPTR) {
|
|
records.push_back(record);
|
|
}
|
|
}
|
|
|
|
return records;
|
|
}
|
|
|
|
private:
|
|
std::vector<MdnsRecord> records_;
|
|
};
|
|
|
|
class MockMdnsSender : public MdnsSender {
|
|
public:
|
|
explicit MockMdnsSender(UdpSocket* socket) : MdnsSender(socket) {}
|
|
|
|
MOCK_METHOD1(SendMulticast, Error(const MdnsMessage& message));
|
|
MOCK_METHOD2(SendMessage,
|
|
Error(const MdnsMessage& message, const IPEndpoint& endpoint));
|
|
};
|
|
|
|
class MockProbeManager : public MdnsProbeManager {
|
|
public:
|
|
MOCK_CONST_METHOD1(IsDomainClaimed, bool(const DomainName&));
|
|
MOCK_METHOD2(RespondToProbeQuery,
|
|
void(const MdnsMessage&, const IPEndpoint&));
|
|
};
|
|
|
|
class MdnsResponderTest : public testing::Test {
|
|
public:
|
|
MdnsResponderTest()
|
|
: clock_(Clock::now()),
|
|
task_runner_(&clock_),
|
|
socket_(&task_runner_),
|
|
sender_(&socket_),
|
|
receiver_(config_),
|
|
responder_(&record_handler_,
|
|
&probe_manager_,
|
|
&sender_,
|
|
&receiver_,
|
|
&task_runner_,
|
|
FakeClock::now,
|
|
&random_,
|
|
config_) {}
|
|
|
|
protected:
|
|
MdnsRecord GetFakePtrRecord(const DomainName& target) {
|
|
DomainName name(++target.labels().begin(), target.labels().end());
|
|
PtrRecordRdata rdata(target);
|
|
return MdnsRecord(std::move(name), DnsType::kPTR, DnsClass::kIN,
|
|
RecordType::kUnique, std::chrono::seconds(0), rdata);
|
|
}
|
|
|
|
MdnsRecord GetFakeSrvRecord(const DomainName& name) {
|
|
SrvRecordRdata rdata(0, 0, 80, name);
|
|
return MdnsRecord(name, DnsType::kSRV, DnsClass::kIN, RecordType::kUnique,
|
|
std::chrono::seconds(0), rdata);
|
|
}
|
|
|
|
MdnsRecord GetFakeTxtRecord(const DomainName& name) {
|
|
TxtRecordRdata rdata;
|
|
return MdnsRecord(name, DnsType::kTXT, DnsClass::kIN, RecordType::kUnique,
|
|
std::chrono::seconds(0), rdata);
|
|
}
|
|
|
|
MdnsRecord GetFakeARecord(const DomainName& name) {
|
|
ARecordRdata rdata(IPAddress(192, 168, 0, 0));
|
|
return MdnsRecord(name, DnsType::kA, DnsClass::kIN, RecordType::kUnique,
|
|
std::chrono::seconds(0), rdata);
|
|
}
|
|
|
|
MdnsRecord GetFakeAAAARecord(const DomainName& name) {
|
|
AAAARecordRdata rdata(IPAddress(1, 2, 3, 4, 5, 6, 7, 8));
|
|
return MdnsRecord(name, DnsType::kAAAA, DnsClass::kIN, RecordType::kUnique,
|
|
std::chrono::seconds(0), rdata);
|
|
}
|
|
|
|
void OnMessageReceived(const MdnsMessage& message, const IPEndpoint& src) {
|
|
responder_.OnMessageReceived(message, src);
|
|
}
|
|
|
|
void QueryForRecordTypeWhenNonePresent(DnsType type) {
|
|
MdnsQuestion question(domain_, type, DnsClass::kANY,
|
|
ResponseType::kMulticast);
|
|
MdnsMessage message(0, MessageType::Query);
|
|
message.AddQuestion(question);
|
|
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([type](const MdnsMessage& msg) -> Error {
|
|
CheckSingleNsecRecordType(msg, type);
|
|
return Error::None();
|
|
});
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
MdnsMessage CreateMulticastMdnsQuery(DnsType type) {
|
|
MdnsQuestion question(domain_, type, DnsClass::kANY,
|
|
ResponseType::kMulticast);
|
|
MdnsMessage message(0, MessageType::Query);
|
|
message.AddQuestion(std::move(question));
|
|
|
|
return message;
|
|
}
|
|
|
|
MdnsMessage CreateTypeEnumerationQuery() {
|
|
MdnsQuestion question(type_enumeration_domain_, DnsType::kPTR,
|
|
DnsClass::kANY, ResponseType::kMulticast);
|
|
MdnsMessage message(0, MessageType::Query);
|
|
message.AddQuestion(std::move(question));
|
|
|
|
return message;
|
|
}
|
|
|
|
const Config config_;
|
|
FakeClock clock_;
|
|
FakeTaskRunner task_runner_;
|
|
FakeUdpSocket socket_;
|
|
StrictMock<MockMdnsSender> sender_;
|
|
StrictMock<MockRecordHandler> record_handler_;
|
|
StrictMock<MockProbeManager> probe_manager_;
|
|
MdnsReceiver receiver_;
|
|
MdnsRandom random_;
|
|
MdnsResponder responder_;
|
|
|
|
DomainName domain_{"instance", "_googlecast", "_tcp", "local"};
|
|
DomainName type_enumeration_domain_{"_services", "_dns-sd", "_udp", "local"};
|
|
IPEndpoint endpoint_{IPAddress(192, 168, 0, 0), 80};
|
|
};
|
|
|
|
// Validate that when records may be sent from multiple receivers, the broadcast
|
|
// is delayed and it is not delayed otherwise.
|
|
TEST_F(MdnsResponderTest, OwnedRecordsSentImmediately) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kANY);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_)).Times(1);
|
|
OnMessageReceived(message, endpoint_);
|
|
testing::Mock::VerifyAndClearExpectations(&sender_);
|
|
testing::Mock::VerifyAndClearExpectations(&record_handler_);
|
|
testing::Mock::VerifyAndClearExpectations(&probe_manager_);
|
|
|
|
EXPECT_CALL(sender_, SendMulticast(_)).Times(0);
|
|
clock_.Advance(Clock::duration(kMaximumSharedRecordResponseDelayMs));
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, NonOwnedRecordsDelayed) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kANY);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(false));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_)).Times(0);
|
|
OnMessageReceived(message, endpoint_);
|
|
testing::Mock::VerifyAndClearExpectations(&sender_);
|
|
testing::Mock::VerifyAndClearExpectations(&record_handler_);
|
|
testing::Mock::VerifyAndClearExpectations(&probe_manager_);
|
|
|
|
EXPECT_CALL(sender_, SendMulticast(_)).Times(1);
|
|
clock_.Advance(Clock::duration(kMaximumSharedRecordResponseDelayMs));
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, MultipleQuestionsProcessed) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kANY);
|
|
MdnsQuestion question2(domain_, DnsType::kANY, DnsClass::kANY,
|
|
ResponseType::kMulticast);
|
|
message.AddQuestion(std::move(question2));
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_))
|
|
.WillOnce(Return(true))
|
|
.WillOnce(Return(false));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_)).Times(1);
|
|
OnMessageReceived(message, endpoint_);
|
|
testing::Mock::VerifyAndClearExpectations(&sender_);
|
|
testing::Mock::VerifyAndClearExpectations(&record_handler_);
|
|
testing::Mock::VerifyAndClearExpectations(&probe_manager_);
|
|
|
|
EXPECT_CALL(sender_, SendMulticast(_)).Times(1);
|
|
clock_.Advance(Clock::duration(kMaximumSharedRecordResponseDelayMs));
|
|
}
|
|
|
|
// Validate that the correct messaging scheme (unicast vs multicast) is used.
|
|
TEST_F(MdnsResponderTest, UnicastMessageSentOverUnicast) {
|
|
MdnsQuestion question(domain_, DnsType::kANY, DnsClass::kANY,
|
|
ResponseType::kUnicast);
|
|
MdnsMessage message(0, MessageType::Query);
|
|
message.AddQuestion(question);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
EXPECT_CALL(sender_, SendMessage(_, endpoint_)).Times(1);
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, MulticastMessageSentOverMulticast) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kANY);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_)).Times(1);
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
// Validate that records are added as expected based on the query type, and that
|
|
// additional records are populated as specified in RFC 6762 and 6763.
|
|
TEST_F(MdnsResponderTest, AnyQueryResultsAllApplied) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kANY);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeTxtRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeARecord(domain_));
|
|
record_handler_.AddRecord(GetFakeAAAARecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
EXPECT_EQ(message.additional_records().size(), size_t{0});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{4});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kSRV));
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kTXT));
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kA));
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kAAAA));
|
|
EXPECT_FALSE(ContainsRecordType(message.answers(), DnsType::kPTR));
|
|
return Error::None();
|
|
});
|
|
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, PtrQueryResultsApplied) {
|
|
DomainName ptr_domain{"_googlecast", "_tcp", "local"};
|
|
MdnsQuestion question(ptr_domain, DnsType::kPTR, DnsClass::kANY,
|
|
ResponseType::kMulticast);
|
|
MdnsMessage message(0, MessageType::Query);
|
|
message.AddQuestion(question);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakePtrRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeTxtRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeARecord(domain_));
|
|
record_handler_.AddRecord(GetFakeAAAARecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
EXPECT_EQ(message.additional_records().size(), size_t{4});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kPTR));
|
|
|
|
const auto& records = message.additional_records();
|
|
EXPECT_EQ(records.size(), size_t{4});
|
|
EXPECT_TRUE(ContainsRecordType(records, DnsType::kSRV));
|
|
EXPECT_TRUE(ContainsRecordType(records, DnsType::kTXT));
|
|
EXPECT_TRUE(ContainsRecordType(records, DnsType::kA));
|
|
EXPECT_TRUE(ContainsRecordType(records, DnsType::kAAAA));
|
|
EXPECT_FALSE(ContainsRecordType(records, DnsType::kPTR));
|
|
|
|
return Error::None();
|
|
});
|
|
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, SrvQueryResultsApplied) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kSRV);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakePtrRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeTxtRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeARecord(domain_));
|
|
record_handler_.AddRecord(GetFakeAAAARecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
EXPECT_EQ(message.additional_records().size(), size_t{2});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kSRV));
|
|
|
|
const auto& records = message.additional_records();
|
|
EXPECT_EQ(records.size(), size_t{2});
|
|
EXPECT_TRUE(ContainsRecordType(records, DnsType::kA));
|
|
EXPECT_TRUE(ContainsRecordType(records, DnsType::kAAAA));
|
|
EXPECT_FALSE(ContainsRecordType(records, DnsType::kSRV));
|
|
EXPECT_FALSE(ContainsRecordType(records, DnsType::kTXT));
|
|
EXPECT_FALSE(ContainsRecordType(records, DnsType::kPTR));
|
|
|
|
return Error::None();
|
|
});
|
|
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, AQueryResultsApplied) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kA);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakePtrRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeTxtRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeARecord(domain_));
|
|
record_handler_.AddRecord(GetFakeAAAARecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
EXPECT_EQ(message.additional_records().size(), size_t{1});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kA));
|
|
|
|
const auto& records = message.additional_records();
|
|
EXPECT_EQ(records.size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(records, DnsType::kAAAA));
|
|
EXPECT_FALSE(ContainsRecordType(records, DnsType::kA));
|
|
EXPECT_FALSE(ContainsRecordType(records, DnsType::kSRV));
|
|
EXPECT_FALSE(ContainsRecordType(records, DnsType::kTXT));
|
|
EXPECT_FALSE(ContainsRecordType(records, DnsType::kPTR));
|
|
|
|
return Error::None();
|
|
});
|
|
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, AAAAQueryResultsApplied) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kAAAA);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakePtrRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeTxtRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeARecord(domain_));
|
|
record_handler_.AddRecord(GetFakeAAAARecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
EXPECT_EQ(message.additional_records().size(), size_t{1});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kAAAA));
|
|
|
|
const auto& records = message.additional_records();
|
|
EXPECT_EQ(records.size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(records, DnsType::kA));
|
|
EXPECT_FALSE(ContainsRecordType(records, DnsType::kAAAA));
|
|
EXPECT_FALSE(ContainsRecordType(records, DnsType::kSRV));
|
|
EXPECT_FALSE(ContainsRecordType(records, DnsType::kTXT));
|
|
EXPECT_FALSE(ContainsRecordType(records, DnsType::kPTR));
|
|
|
|
return Error::None();
|
|
});
|
|
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, MessageOnlySentIfAnswerNotKnown) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kAAAA);
|
|
MdnsRecord aaaa_record = GetFakeAAAARecord(domain_);
|
|
message.AddAnswer(aaaa_record);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakePtrRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeTxtRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeARecord(domain_));
|
|
record_handler_.AddRecord(aaaa_record);
|
|
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, RecordOnlySentIfNotKnown) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kANY);
|
|
MdnsRecord aaaa_record = GetFakeAAAARecord(domain_);
|
|
message.AddAnswer(aaaa_record);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakeARecord(domain_));
|
|
record_handler_.AddRecord(aaaa_record);
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
EXPECT_EQ(message.additional_records().size(), size_t{0});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kA));
|
|
return Error::None();
|
|
});
|
|
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, RecordOnlySentIfNotKnownMultiplePackets) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kANY);
|
|
message.set_truncated();
|
|
|
|
MdnsMessage message2(1, MessageType::Query);
|
|
MdnsRecord aaaa_record = GetFakeAAAARecord(domain_);
|
|
message2.AddAnswer(aaaa_record);
|
|
|
|
OnMessageReceived(message, endpoint_);
|
|
OnMessageReceived(message2, endpoint_);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakeARecord(domain_));
|
|
record_handler_.AddRecord(aaaa_record);
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
EXPECT_EQ(message.additional_records().size(), size_t{0});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kA));
|
|
return Error::None();
|
|
});
|
|
clock_.Advance(std::chrono::seconds(1));
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, RecordOnlySentIfNotKnownMultiplePacketsOutOfOrder) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kANY);
|
|
message.set_truncated();
|
|
|
|
MdnsMessage message2(2, MessageType::Query);
|
|
MdnsRecord aaaa_record = GetFakeAAAARecord(domain_);
|
|
message2.AddAnswer(aaaa_record);
|
|
message2.set_truncated();
|
|
|
|
MdnsMessage message3(3, MessageType::Query);
|
|
MdnsRecord a_record = GetFakeARecord(domain_);
|
|
message3.AddAnswer(a_record);
|
|
|
|
OnMessageReceived(message2, endpoint_);
|
|
OnMessageReceived(message3, endpoint_);
|
|
OnMessageReceived(message, endpoint_);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(a_record);
|
|
record_handler_.AddRecord(aaaa_record);
|
|
record_handler_.AddRecord(aaaa_record);
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
EXPECT_EQ(message.additional_records().size(), size_t{0});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kSRV));
|
|
return Error::None();
|
|
});
|
|
clock_.Advance(std::chrono::seconds(1));
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, RecordSentForMultiPacketsSuppressionIfMoreNotFound) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kANY);
|
|
MdnsRecord aaaa_record = GetFakeAAAARecord(domain_);
|
|
message.AddAnswer(aaaa_record);
|
|
message.set_truncated();
|
|
|
|
OnMessageReceived(message, endpoint_);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakeARecord(domain_));
|
|
record_handler_.AddRecord(aaaa_record);
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
EXPECT_EQ(message.additional_records().size(), size_t{0});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kA));
|
|
return Error::None();
|
|
});
|
|
clock_.Advance(std::chrono::seconds(1));
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, RecordNotSentForMultiPacketsSuppressionIfNoQuery) {
|
|
MdnsMessage message(1, MessageType::Query);
|
|
MdnsRecord aaaa_record = GetFakeAAAARecord(domain_);
|
|
message.AddAnswer(aaaa_record);
|
|
|
|
OnMessageReceived(message, endpoint_);
|
|
clock_.Advance(std::chrono::seconds(1));
|
|
}
|
|
|
|
// Validate NSEC records are used correctly.
|
|
TEST_F(MdnsResponderTest, QueryForRecordTypesWhenNonePresent) {
|
|
QueryForRecordTypeWhenNonePresent(DnsType::kANY);
|
|
QueryForRecordTypeWhenNonePresent(DnsType::kSRV);
|
|
QueryForRecordTypeWhenNonePresent(DnsType::kTXT);
|
|
QueryForRecordTypeWhenNonePresent(DnsType::kA);
|
|
QueryForRecordTypeWhenNonePresent(DnsType::kAAAA);
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, AAAAQueryGiveANsec) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kAAAA);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakePtrRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeTxtRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeAAAARecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kAAAA));
|
|
|
|
EXPECT_EQ(message.additional_records().size(), size_t{1});
|
|
ExpectContainsNsecRecordType(message.additional_records(), DnsType::kA);
|
|
|
|
return Error::None();
|
|
});
|
|
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, AQueryGiveAAAANsec) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kA);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakePtrRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeTxtRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeARecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kA));
|
|
|
|
EXPECT_EQ(message.additional_records().size(), size_t{1});
|
|
ExpectContainsNsecRecordType(message.additional_records(),
|
|
DnsType::kAAAA);
|
|
|
|
return Error::None();
|
|
});
|
|
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, SrvQueryGiveCorrectNsecForNoAOrAAAA) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kSRV);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakePtrRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeTxtRecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kSRV));
|
|
|
|
EXPECT_EQ(message.additional_records().size(), size_t{2});
|
|
ExpectContainsNsecRecordType(message.additional_records(), DnsType::kA);
|
|
ExpectContainsNsecRecordType(message.additional_records(),
|
|
DnsType::kAAAA);
|
|
|
|
return Error::None();
|
|
});
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, SrvQueryGiveCorrectNsec) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kSRV);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakePtrRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeTxtRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeARecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kSRV));
|
|
|
|
EXPECT_EQ(message.additional_records().size(), size_t{2});
|
|
EXPECT_TRUE(
|
|
ContainsRecordType(message.additional_records(), DnsType::kA));
|
|
ExpectContainsNsecRecordType(message.additional_records(),
|
|
DnsType::kAAAA);
|
|
|
|
return Error::None();
|
|
});
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, PtrQueryGiveCorrectNsecForNoPtrOrSrv) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kPTR);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakePtrRecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kPTR));
|
|
|
|
EXPECT_EQ(message.additional_records().size(), size_t{2});
|
|
ExpectContainsNsecRecordType(message.additional_records(),
|
|
DnsType::kTXT);
|
|
ExpectContainsNsecRecordType(message.additional_records(),
|
|
DnsType::kSRV);
|
|
|
|
return Error::None();
|
|
});
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, PtrQueryGiveCorrectNsecForOnlyPtr) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kPTR);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakePtrRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeTxtRecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kPTR));
|
|
|
|
EXPECT_EQ(message.additional_records().size(), size_t{2});
|
|
EXPECT_TRUE(
|
|
ContainsRecordType(message.additional_records(), DnsType::kTXT));
|
|
ExpectContainsNsecRecordType(message.additional_records(),
|
|
DnsType::kSRV);
|
|
|
|
return Error::None();
|
|
});
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, PtrQueryGiveCorrectNsecForOnlySrv) {
|
|
MdnsMessage message = CreateMulticastMdnsQuery(DnsType::kPTR);
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(true));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
record_handler_.AddRecord(GetFakePtrRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kPTR));
|
|
|
|
EXPECT_EQ(message.additional_records().size(), size_t{4});
|
|
EXPECT_TRUE(
|
|
ContainsRecordType(message.additional_records(), DnsType::kSRV));
|
|
ExpectContainsNsecRecordType(message.additional_records(),
|
|
DnsType::kTXT);
|
|
ExpectContainsNsecRecordType(message.additional_records(), DnsType::kA);
|
|
ExpectContainsNsecRecordType(message.additional_records(),
|
|
DnsType::kAAAA);
|
|
|
|
return Error::None();
|
|
});
|
|
OnMessageReceived(message, endpoint_);
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, EnumerateAllQuery) {
|
|
MdnsMessage message = CreateTypeEnumerationQuery();
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(false));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
const auto ptr = GetFakePtrRecord(domain_);
|
|
record_handler_.AddRecord(ptr);
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeTxtRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeARecord(domain_));
|
|
OnMessageReceived(message, endpoint_);
|
|
|
|
EXPECT_CALL(sender_, SendMulticast(_))
|
|
.WillOnce([this, &ptr](const MdnsMessage& message) -> Error {
|
|
EXPECT_EQ(message.questions().size(), size_t{0});
|
|
EXPECT_EQ(message.authority_records().size(), size_t{0});
|
|
|
|
EXPECT_EQ(message.answers().size(), size_t{1});
|
|
EXPECT_TRUE(ContainsRecordType(message.answers(), DnsType::kPTR));
|
|
EXPECT_EQ(message.answers()[0].name(), type_enumeration_domain_);
|
|
CheckPtrDomain(message.answers()[0], ptr.name());
|
|
return Error::None();
|
|
});
|
|
clock_.Advance(Clock::duration(kMaximumSharedRecordResponseDelayMs));
|
|
}
|
|
|
|
TEST_F(MdnsResponderTest, EnumerateAllQueryNoResults) {
|
|
MdnsMessage message = CreateTypeEnumerationQuery();
|
|
|
|
EXPECT_CALL(probe_manager_, IsDomainClaimed(_)).WillOnce(Return(false));
|
|
EXPECT_CALL(record_handler_, HasRecords(_, _, _))
|
|
.WillRepeatedly(Return(true));
|
|
const auto ptr = GetFakePtrRecord(domain_);
|
|
record_handler_.AddRecord(GetFakeSrvRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeTxtRecord(domain_));
|
|
record_handler_.AddRecord(GetFakeARecord(domain_));
|
|
OnMessageReceived(message, endpoint_);
|
|
clock_.Advance(Clock::duration(kMaximumSharedRecordResponseDelayMs));
|
|
}
|
|
|
|
} // namespace discovery
|
|
} // namespace openscreen
|