blob: 78157ac3171f346fad37f5dfff6bc6cbc0ed7726 [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 "src/connectivity/network/mdns/service/agents/instance_prober.h"
#include <lib/zx/time.h>
#include <gtest/gtest.h>
#include "src/connectivity/network/mdns/service/test/agent_test.h"
namespace mdns {
namespace test {
class InstanceProberTest : public AgentTest {
public:
InstanceProberTest() {}
protected:
// Expects that a probe for the specified instance on the specified host has been sent.
void ExpectProbe(const std::string& instance_full_name, const std::string& host_full_name,
inet::IpPort port, Media media, IpVersions ip_versions) {
auto message = ExpectOutboundMessage(ReplyAddress::Multicast(media, ip_versions));
ExpectQuestion(message.get(), instance_full_name, DnsType::kAny, DnsClass::kIn, true);
auto resource = ExpectResource(message.get(), MdnsResourceSection::kAuthority,
instance_full_name, DnsType::kSrv);
EXPECT_EQ(port, resource->srv_.port_);
EXPECT_EQ(host_full_name, resource->srv_.target_.dotted_string_);
ExpectNoOtherQuestionOrResource(message.get());
}
};
constexpr char kServiceName[] = "_testservice._tcp.";
constexpr char kInstanceName[] = "testinstance";
constexpr char kInstanceFullName[] = "testinstance._testservice._tcp.local.";
const inet::IpPort kPort = inet::IpPort::From_uint16_t(1234);
constexpr uint32_t kInterfaceId = 1;
// Tests nominal behavior of the prober when there are no conflicts.
TEST_F(InstanceProberTest, Nominal) {
bool callback_called = false;
bool callback_success;
// We need to make_shared here, because |RemoveSelf| calls |shared_from_this|.
auto under_test = std::make_shared<InstanceProber>(
this, kServiceName, kInstanceName, kLocalHostFullName, kPort, Media::kBoth, IpVersions::kBoth,
[&callback_called, &callback_success](bool success) {
callback_called = true;
callback_success = success;
});
SetAgent(*under_test);
under_test->Start(kLocalHostFullName);
// Expect three probe messages, the first after a delay of 0~250ms...
ExpectPostTaskForTimeAndInvoke(zx::msec(0), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kBoth, IpVersions::kBoth);
EXPECT_FALSE(callback_called);
// ...the second after a delay of 250ms...
ExpectPostTaskForTimeAndInvoke(zx::msec(250), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kBoth, IpVersions::kBoth);
EXPECT_FALSE(callback_called);
// ...and the third after a delay of 250ms.
ExpectPostTaskForTimeAndInvoke(zx::msec(250), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kBoth, IpVersions::kBoth);
EXPECT_FALSE(callback_called);
ExpectPostTaskForTimeAndInvoke(zx::msec(250), zx::msec(250));
EXPECT_TRUE(callback_called);
EXPECT_TRUE(callback_success);
ExpectRemoveAgentCall();
ExpectNoOther();
}
// Tests behavior of the prober when there is a conflict.
TEST_F(InstanceProberTest, Conflict) {
bool callback_called = false;
bool callback_success;
// We need to make_shared here, because |RemoveSelf| calls |shared_from_this|.
auto under_test = std::make_shared<InstanceProber>(
this, kServiceName, kInstanceName, kLocalHostFullName, kPort, Media::kBoth, IpVersions::kBoth,
[&callback_called, &callback_success](bool success) {
callback_called = true;
callback_success = success;
});
SetAgent(*under_test);
under_test->Start(kLocalHostFullName);
// Expect the first probe message after a delay of 0~250ms.
ExpectPostTaskForTimeAndInvoke(zx::msec(0), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kBoth, IpVersions::kBoth);
ExpectPostTaskForTime(zx::msec(250), zx::msec(250));
EXPECT_FALSE(callback_called);
// Send a reply.
ReplyAddress sender_address(
inet::SocketAddress(192, 168, 1, 1, inet::IpPort::From_uint16_t(5353)),
inet::IpAddress(192, 168, 1, 100), kInterfaceId, Media::kWireless, IpVersions::kV4);
DnsResource resource(kInstanceFullName, DnsType::kSrv);
resource.srv_.port_ = kPort;
resource.srv_.target_ = DnsName(kLocalHostFullName);
under_test->ReceiveResource(resource, MdnsResourceSection::kAnswer, sender_address);
// Callback and removal are run in a task.
ExpectPostTaskForTimeAndInvoke(zx::msec(0), zx::msec(0));
// Expect the probe to fail (conflict).
EXPECT_TRUE(callback_called);
EXPECT_FALSE(callback_success);
ExpectRemoveAgentCall();
ExpectNoOther();
}
// Tests nominal behavior of the prober configured for wired-only.
TEST_F(InstanceProberTest, NominalWiredOnly) {
bool callback_called = false;
bool callback_success;
// We need to make_shared here, because |RemoveSelf| calls |shared_from_this|.
auto under_test = std::make_shared<InstanceProber>(
this, kServiceName, kInstanceName, kLocalHostFullName, kPort, Media::kWired,
IpVersions::kBoth, [&callback_called, &callback_success](bool success) {
callback_called = true;
callback_success = success;
});
SetAgent(*under_test);
under_test->Start(kLocalHostFullName);
// Expect three probe messages, the first after a delay of 0~250ms...
ExpectPostTaskForTimeAndInvoke(zx::msec(0), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kWired, IpVersions::kBoth);
EXPECT_FALSE(callback_called);
// ...the second after a delay of 250ms...
ExpectPostTaskForTimeAndInvoke(zx::msec(250), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kWired, IpVersions::kBoth);
EXPECT_FALSE(callback_called);
// ...and the third after a delay of 250ms.
ExpectPostTaskForTimeAndInvoke(zx::msec(250), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kWired, IpVersions::kBoth);
EXPECT_FALSE(callback_called);
ExpectPostTaskForTimeAndInvoke(zx::msec(250), zx::msec(250));
EXPECT_TRUE(callback_called);
EXPECT_TRUE(callback_success);
ExpectRemoveAgentCall();
ExpectNoOther();
}
// Tests nominal behavior of the prober configured for wireless-only.
TEST_F(InstanceProberTest, NominalWirelessOnly) {
bool callback_called = false;
bool callback_success;
// We need to make_shared here, because |RemoveSelf| calls |shared_from_this|.
auto under_test = std::make_shared<InstanceProber>(
this, kServiceName, kInstanceName, kLocalHostFullName, kPort, Media::kWireless,
IpVersions::kBoth, [&callback_called, &callback_success](bool success) {
callback_called = true;
callback_success = success;
});
SetAgent(*under_test);
under_test->Start(kLocalHostFullName);
// Expect three probe messages, the first after a delay of 0~250ms...
ExpectPostTaskForTimeAndInvoke(zx::msec(0), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kWireless, IpVersions::kBoth);
EXPECT_FALSE(callback_called);
// ...the second after a delay of 250ms...
ExpectPostTaskForTimeAndInvoke(zx::msec(250), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kWireless, IpVersions::kBoth);
EXPECT_FALSE(callback_called);
// ...and the third after a delay of 250ms.
ExpectPostTaskForTimeAndInvoke(zx::msec(250), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kWireless, IpVersions::kBoth);
EXPECT_FALSE(callback_called);
ExpectPostTaskForTimeAndInvoke(zx::msec(250), zx::msec(250));
EXPECT_TRUE(callback_called);
EXPECT_TRUE(callback_success);
ExpectRemoveAgentCall();
ExpectNoOther();
}
// Tests nominal behavior of the prober configured for IPv4 only.
TEST_F(InstanceProberTest, NominalV4) {
bool callback_called = false;
bool callback_success;
// We need to make_shared here, because |RemoveSelf| calls |shared_from_this|.
auto under_test = std::make_shared<InstanceProber>(
this, kServiceName, kInstanceName, kLocalHostFullName, kPort, Media::kBoth, IpVersions::kV4,
[&callback_called, &callback_success](bool success) {
callback_called = true;
callback_success = success;
});
SetAgent(*under_test);
under_test->Start(kLocalHostFullName);
// Expect three probe messages, the first after a delay of 0~250ms...
ExpectPostTaskForTimeAndInvoke(zx::msec(0), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kBoth, IpVersions::kV4);
EXPECT_FALSE(callback_called);
// ...the second after a delay of 250ms...
ExpectPostTaskForTimeAndInvoke(zx::msec(250), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kBoth, IpVersions::kV4);
EXPECT_FALSE(callback_called);
// ...and the third after a delay of 250ms.
ExpectPostTaskForTimeAndInvoke(zx::msec(250), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kBoth, IpVersions::kV4);
EXPECT_FALSE(callback_called);
ExpectPostTaskForTimeAndInvoke(zx::msec(250), zx::msec(250));
EXPECT_TRUE(callback_called);
EXPECT_TRUE(callback_success);
ExpectRemoveAgentCall();
ExpectNoOther();
}
// Tests nominal behavior of the prober configured for IPv6 only.
TEST_F(InstanceProberTest, NominalV6) {
bool callback_called = false;
bool callback_success;
// We need to make_shared here, because |RemoveSelf| calls |shared_from_this|.
auto under_test = std::make_shared<InstanceProber>(
this, kServiceName, kInstanceName, kLocalHostFullName, kPort, Media::kBoth, IpVersions::kV6,
[&callback_called, &callback_success](bool success) {
callback_called = true;
callback_success = success;
});
SetAgent(*under_test);
under_test->Start(kLocalHostFullName);
// Expect three probe messages, the first after a delay of 0~250ms...
ExpectPostTaskForTimeAndInvoke(zx::msec(0), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kBoth, IpVersions::kV6);
EXPECT_FALSE(callback_called);
// ...the second after a delay of 250ms...
ExpectPostTaskForTimeAndInvoke(zx::msec(250), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kBoth, IpVersions::kV6);
EXPECT_FALSE(callback_called);
// ...and the third after a delay of 250ms.
ExpectPostTaskForTimeAndInvoke(zx::msec(250), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kBoth, IpVersions::kV6);
EXPECT_FALSE(callback_called);
ExpectPostTaskForTimeAndInvoke(zx::msec(250), zx::msec(250));
EXPECT_TRUE(callback_called);
EXPECT_TRUE(callback_success);
ExpectRemoveAgentCall();
ExpectNoOther();
}
// Tests behavior of a wired-only/IPv6-only prober when there is a conflict.
TEST_F(InstanceProberTest, ConflictWiredV6) {
bool callback_called = false;
bool callback_success;
// We need to make_shared here, because |RemoveSelf| calls |shared_from_this|.
auto under_test = std::make_shared<InstanceProber>(
this, kServiceName, kInstanceName, kLocalHostFullName, kPort, Media::kWired, IpVersions::kV6,
[&callback_called, &callback_success](bool success) {
callback_called = true;
callback_success = success;
});
SetAgent(*under_test);
under_test->Start(kLocalHostFullName);
// Expect the first probe message after a delay of 0~250ms.
ExpectPostTaskForTimeAndInvoke(zx::msec(0), zx::msec(250));
ExpectProbe(kInstanceFullName, kLocalHostFullName, kPort, Media::kWired, IpVersions::kV6);
ExpectPostTaskForTime(zx::msec(250), zx::msec(250));
EXPECT_FALSE(callback_called);
DnsResource resource(kInstanceFullName, DnsType::kSrv);
resource.srv_.port_ = kPort;
resource.srv_.target_ = DnsName(kLocalHostFullName);
// Send a reply wired/V4. Expect it to be ignored, because it's V4
ReplyAddress sender_address0(
inet::SocketAddress(192, 168, 1, 1, inet::IpPort::From_uint16_t(5353)),
inet::IpAddress(192, 168, 1, 100), kInterfaceId, Media::kWired, IpVersions::kV4);
under_test->ReceiveResource(resource, MdnsResourceSection::kAnswer, sender_address0);
EXPECT_FALSE(callback_called);
ExpectNoOther();
// Send a reply wireless/V6. Expect it to be ignored, because it's wireless
ReplyAddress sender_address1(inet::SocketAddress(0xfe80, 1, inet::IpPort::From_uint16_t(5353)),
inet::IpAddress(0xfe80, 100), kInterfaceId, Media::kWireless,
IpVersions::kV6);
under_test->ReceiveResource(resource, MdnsResourceSection::kAnswer, sender_address1);
EXPECT_FALSE(callback_called);
ExpectNoOther();
// Send a reply wired/V6. Expect it to be ignored, because it's wireless
ReplyAddress sender_address2(inet::SocketAddress(0xfe80, 1, inet::IpPort::From_uint16_t(5353)),
inet::IpAddress(0xfe80, 100), kInterfaceId, Media::kWired,
IpVersions::kV6);
under_test->ReceiveResource(resource, MdnsResourceSection::kAnswer, sender_address2);
// Callback and removal are run in a task.
ExpectPostTaskForTimeAndInvoke(zx::msec(0), zx::msec(0));
// Expect the probe to fail (conflict).
EXPECT_TRUE(callback_called);
EXPECT_FALSE(callback_success);
ExpectRemoveAgentCall();
ExpectNoOther();
}
} // namespace test
} // namespace mdns