Commit 956058ab authored by Qingsi Wang's avatar Qingsi Wang Committed by Commit Bot

Block following mDNS packets on an incomplete send in the mDNS responder

service and ignore msg-too-big read errors.

Blocked packets are buffered in a send queue with a limited capacity,
and are flushed after the previous send is complete.

We originally remove a socket when it encounters read errors and reboot
the service when no socket left. The msg-too-big error will be ignored
after this change.

Bug: 933869, 964951
Change-Id: I3665dd732526591f350834757bc0e6d1c0d6b3dd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1617614
Commit-Queue: Qingsi Wang <qingsi@chromium.org>
Reviewed-by: default avatarEric Orth <ericorth@chromium.org>
Cr-Commit-Position: refs/heads/master@{#662837}
parent 4367d762
This diff is collapsed.
...@@ -198,6 +198,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) MdnsResponderManager { ...@@ -198,6 +198,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) MdnsResponderManager {
void OnMdnsQueryReceived(const net::DnsQuery& query, void OnMdnsQueryReceived(const net::DnsQuery& query,
uint16_t recv_socket_handler_id); uint16_t recv_socket_handler_id);
void OnSocketHandlerReadError(uint16_t socket_handler_id, int result); void OnSocketHandlerReadError(uint16_t socket_handler_id, int result);
bool IsNonFatalError(int result);
std::unique_ptr<net::MDnsSocketFactory> owned_socket_factory_; std::unique_ptr<net::MDnsSocketFactory> owned_socket_factory_;
net::MDnsSocketFactory* socket_factory_; net::MDnsSocketFactory* socket_factory_;
......
...@@ -31,10 +31,11 @@ ...@@ -31,10 +31,11 @@
namespace network { namespace network {
namespace { namespace {
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::Invoke; using ::testing::Invoke;
using ::testing::NiceMock; using ::testing::NiceMock;
using ::testing::Return; using ::testing::Return;
using ::testing::_;
using ServiceError = MdnsResponderManager::ServiceError; using ServiceError = MdnsResponderManager::ServiceError;
const net::IPAddress kPublicAddrs[2] = {net::IPAddress(11, 11, 11, 11), const net::IPAddress kPublicAddrs[2] = {net::IPAddress(11, 11, 11, 11),
...@@ -107,6 +108,37 @@ class MockFailingMdnsSocketFactory : public net::MDnsSocketFactory { ...@@ -107,6 +108,37 @@ class MockFailingMdnsSocketFactory : public net::MDnsSocketFactory {
return -1; return -1;
} }
// Emulates IO blocking in sending packets if |BlockSend()| is called, in
// which case the completion callback is not invoked until |ResumeSend()| is
// called.
int MaybeBlockSend(const std::string& packet,
const std::string& address,
net::CompletionRepeatingCallback callback) {
OnSendTo(packet);
if (block_send_) {
blocked_packet_size_ = packet.size();
blocked_send_callback_ = std::move(callback);
} else {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce([](net::CompletionRepeatingCallback callback,
size_t packet_size) { callback.Run(packet_size); },
callback, packet.size()));
}
return -1;
}
void BlockSend() {
DCHECK(!block_send_);
block_send_ = true;
}
void ResumeSend() {
DCHECK(block_send_);
block_send_ = false;
blocked_send_callback_.Run(blocked_packet_size_);
}
// Emulates the asynchronous contract of invoking |callback| in the RecvFrom // Emulates the asynchronous contract of invoking |callback| in the RecvFrom
// primitive but failed receiving; // primitive but failed receiving;
int FailToRecv(net::IOBuffer* buffer, int FailToRecv(net::IOBuffer* buffer,
...@@ -122,6 +154,9 @@ class MockFailingMdnsSocketFactory : public net::MDnsSocketFactory { ...@@ -122,6 +154,9 @@ class MockFailingMdnsSocketFactory : public net::MDnsSocketFactory {
} }
private: private:
bool block_send_ = false;
size_t blocked_packet_size_ = 0;
net::CompletionRepeatingCallback blocked_send_callback_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
}; };
...@@ -283,6 +318,14 @@ class MdnsResponderTest : public testing::Test { ...@@ -283,6 +318,14 @@ class MdnsResponderTest : public testing::Test {
Reset(); Reset();
} }
~MdnsResponderTest() {
// Goodbye messages are scheduled when the responder service |host_manager_|
// is destroyed and can be synchronously sent if the rate limiting permits.
// See ResponseScheduler::DispatchPendingPackets().
EXPECT_CALL(socket_factory_, OnSendTo(_)).Times(AnyNumber());
EXPECT_CALL(failing_socket_factory_, OnSendTo(_)).Times(AnyNumber());
}
void Reset(bool use_failing_socket_factory = false) { void Reset(bool use_failing_socket_factory = false) {
client_[0].reset(); client_[0].reset();
client_[1].reset(); client_[1].reset();
...@@ -365,12 +408,12 @@ class MdnsResponderTest : public testing::Test { ...@@ -365,12 +408,12 @@ class MdnsResponderTest : public testing::Test {
base::test::ScopedTaskEnvironment scoped_task_environment_{ base::test::ScopedTaskEnvironment scoped_task_environment_{
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME}; base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
mojom::MdnsResponderPtr client_[2];
std::unique_ptr<MdnsResponderManager> host_manager_;
// Overrides the current thread task runner, so we can simulate the passage // Overrides the current thread task runner, so we can simulate the passage
// of time and avoid any actual sleeps. // of time and avoid any actual sleeps.
NiceMock<net::MockMDnsSocketFactory> socket_factory_; NiceMock<net::MockMDnsSocketFactory> socket_factory_;
NiceMock<MockFailingMdnsSocketFactory> failing_socket_factory_; NiceMock<MockFailingMdnsSocketFactory> failing_socket_factory_;
mojom::MdnsResponderPtr client_[2];
std::unique_ptr<MdnsResponderManager> host_manager_;
std::string last_name_created_; std::string last_name_created_;
}; };
...@@ -1013,4 +1056,58 @@ TEST_F(MdnsResponderTest, ManagerCanRestartAfterAllSocketHandlersFailToRead) { ...@@ -1013,4 +1056,58 @@ TEST_F(MdnsResponderTest, ManagerCanRestartAfterAllSocketHandlersFailToRead) {
tester.ExpectTotalCount(kServiceErrorHistogram, 2); tester.ExpectTotalCount(kServiceErrorHistogram, 2);
} }
// Test that sending packets on an interface can be blocked by an incomplete
// send on the same interface. Blocked packets are later flushed when sending is
// unblocked.
TEST_F(MdnsResponderTest, IncompleteSendBlocksFollowingSends) {
auto create_send_blocking_socket =
[this](std::vector<std::unique_ptr<net::DatagramServerSocket>>* sockets) {
auto socket =
std::make_unique<NiceMock<net::MockMDnsDatagramServerSocket>>(
net::ADDRESS_FAMILY_IPV4);
ON_CALL(*socket, SendToInternal(_, _, _))
.WillByDefault(
Invoke(&failing_socket_factory_,
&MockFailingMdnsSocketFactory::MaybeBlockSend));
ON_CALL(*socket, RecvFromInternal(_, _, _, _))
.WillByDefault(Return(-1));
sockets->push_back(std::move(socket));
};
EXPECT_CALL(failing_socket_factory_, CreateSockets(_))
.WillOnce(Invoke(create_send_blocking_socket));
Reset(true /* use_failing_socket_factory */);
const auto& addr1 = kPublicAddrs[0];
std::string expected_announcement1 =
CreateResolutionResponse(kDefaultTtl, {{"0.local", addr1}});
// Mocked CreateSockets above only creates one socket.
// We schedule to send the announcement for |kNumAnnouncementsPerInterface|
// times but the second announcement is blocked by the first one in this case.
EXPECT_CALL(failing_socket_factory_, OnSendTo(expected_announcement1))
.Times(1);
failing_socket_factory_.BlockSend();
const auto name1 = CreateNameForAddress(0, addr1);
RunUntilNoTasksRemain();
const auto& addr2 = kPublicAddrs[1];
std::string expected_announcement2 =
CreateResolutionResponse(kDefaultTtl, {{"1.local", addr2}});
// The announcement for the following name should also be blocked.
const auto name2 = CreateNameForAddress(0, addr2);
EXPECT_CALL(failing_socket_factory_, OnSendTo(expected_announcement2))
.Times(0);
RunUntilNoTasksRemain();
// We later unblock sending packets. Previously scheduled announcements should
// be flushed.
EXPECT_CALL(failing_socket_factory_, OnSendTo(expected_announcement1))
.Times(kNumAnnouncementsPerInterface - 1);
EXPECT_CALL(failing_socket_factory_, OnSendTo(expected_announcement2))
.Times(kNumAnnouncementsPerInterface);
failing_socket_factory_.ResumeSend();
RunUntilNoTasksRemain();
}
} // namespace network } // namespace network
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment