From: Remi Gacogne Date: Thu, 18 Feb 2021 17:11:30 +0000 (+0100) Subject: dnsdist: Add a unit test for TCP I/O errors with pending queries X-Git-Tag: dnsdist-1.6.0-alpha2~11^2~7 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=faaf1b48fd328e32836f5f665b4fb1622ea8b9fb;p=thirdparty%2Fpdns.git dnsdist: Add a unit test for TCP I/O errors with pending queries --- diff --git a/pdns/dnsdistdist/test-dnsdisttcp_cc.cc b/pdns/dnsdistdist/test-dnsdisttcp_cc.cc index 58b1693159..e5e1f4b6e3 100644 --- a/pdns/dnsdistdist/test-dnsdisttcp_cc.cc +++ b/pdns/dnsdistdist/test-dnsdisttcp_cc.cc @@ -2847,6 +2847,90 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR) /* we should have nothing to clear since the connection cannot be reused due to the Proxy Protocol payload */ BOOST_CHECK_EQUAL(IncomingTCPConnectionState::clearAllDownstreamConnections(), 0); } + + { + TEST_NAME("=> I/O error with the backend with queries not sent to the backend yet"); + + PacketBuffer expectedWriteBuffer; + PacketBuffer expectedBackendWriteBuffer; + + s_readBuffer.clear(); + s_readBuffer.insert(s_readBuffer.end(), queries.at(0).begin(), queries.at(0).end()); + s_readBuffer.insert(s_readBuffer.end(), queries.at(1).begin(), queries.at(1).end()); + s_readBuffer.insert(s_readBuffer.end(), queries.at(2).begin(), queries.at(2).end()); + + s_writeBuffer.clear(); + + s_backendReadBuffer.clear(); + + s_backendWriteBuffer.clear(); + + /* make sure that the backend's timeout is shorter than the client's */ + backend->tcpSendTimeout = 1; + g_tcpRecvTimeout = 5; + + bool timeout = false; + s_steps = { + { ExpectedStep::ExpectedRequest::handshakeClient, IOState::Done }, + /* reading a query from the client (1) */ + { ExpectedStep::ExpectedRequest::readFromClient, IOState::Done, 2 }, + { ExpectedStep::ExpectedRequest::readFromClient, IOState::Done, queries.at(0).size() - 2 }, + /* opening a connection to the backend */ + { ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done }, + /* backend is not ready yet */ + { ExpectedStep::ExpectedRequest::writeToBackend, IOState::NeedWrite, 0 }, + /* reading a second query from the client */ + { ExpectedStep::ExpectedRequest::readFromClient, IOState::Done, 2 }, + { ExpectedStep::ExpectedRequest::readFromClient, IOState::Done, queries.at(1).size() - 2 }, + /* reading a third query from the client */ + { ExpectedStep::ExpectedRequest::readFromClient, IOState::Done, 2 }, + { ExpectedStep::ExpectedRequest::readFromClient, IOState::Done, queries.at(2).size() - 2, [&timeout](int desc, const ExpectedStep& step) { + timeout = true; + } }, + /* trying to read more from the client but nothing to read */ + { ExpectedStep::ExpectedRequest::readFromClient, IOState::NeedRead, 0 }, + /* closing the client connection */ + { ExpectedStep::ExpectedRequest::closeClient, IOState::Done, 0 }, + /* closing the backend connection */ + { ExpectedStep::ExpectedRequest::closeBackend, IOState::Done, 0 }, + }; + + s_processQuery = [backend,&counter](DNSQuestion& dq, ClientState& cs, LocalHolders& holders, std::shared_ptr& selectedBackend) -> ProcessQueryResult { + selectedBackend = backend; + return ProcessQueryResult::PassToBackend; + }; + s_processResponse = [](PacketBuffer& response, LocalStateHolder >& localRespRulactions, DNSResponse& dr, bool muted) -> bool { + return true; + }; + + auto state = std::make_shared(ConnectionInfo(&localCS), threadData, now); + IncomingTCPConnectionState::handleIO(state, now); + while (!timeout && (threadData.mplexer->getWatchedFDCount(false) != 0 || threadData.mplexer->getWatchedFDCount(true) != 0)) { + threadData.mplexer->run(&now); + } + + struct timeval later = now; + later.tv_sec += backend->tcpSendTimeout + 1; + auto expiredConns = threadData.mplexer->getTimeouts(later, true); + BOOST_CHECK_EQUAL(expiredConns.size(), 1U); + for (const auto& cbData : expiredConns) { + if (cbData.second.type() == typeid(std::shared_ptr)) { + auto cbState = boost::any_cast>(cbData.second); + cbState->handleTimeout(later, true); + } + } + + BOOST_CHECK_EQUAL(s_writeBuffer.size(), 0U); + BOOST_CHECK_EQUAL(s_backendWriteBuffer.size(), 0U); + + /* restore */ + backend->tcpSendTimeout = 30; + g_tcpRecvTimeout = 2; + + /* we need to clear them now, otherwise we end up with dangling pointers to the steps via the TLS context, etc */ + /* we should have nothing to clear since the connection cannot be reused due to the Proxy Protocol payload */ + BOOST_CHECK_EQUAL(IncomingTCPConnectionState::clearAllDownstreamConnections(), 0); + } } #warning TODO: