From: Remi Gacogne Date: Thu, 18 Feb 2021 16:45:06 +0000 (+0100) Subject: dnsdist: Add a unit test for outgoing proxy protocol over TCP X-Git-Tag: dnsdist-1.6.0-alpha2~11^2~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e14bf7c519e388a366957de894bed8464d939f75;p=thirdparty%2Fpdns.git dnsdist: Add a unit test for outgoing proxy protocol over TCP --- diff --git a/pdns/dnsdistdist/test-dnsdisttcp_cc.cc b/pdns/dnsdistdist/test-dnsdisttcp_cc.cc index 344eea6076..58b1693159 100644 --- a/pdns/dnsdistdist/test-dnsdisttcp_cc.cc +++ b/pdns/dnsdistdist/test-dnsdisttcp_cc.cc @@ -792,7 +792,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnection_BackendNoOOOR) query.insert(query.begin(), sizeBytes, sizeBytes + 2); auto backend = std::make_shared(ComboAddress("192.0.2.42:53"), ComboAddress("0.0.0.0:0"), 0, std::string(), 1, false); - backend->d_tlsCtx = tlsCtx;; + backend->d_tlsCtx = tlsCtx; g_proxyProtocolACL.clear(); @@ -2180,7 +2180,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR) s_readBuffer.insert(s_readBuffer.end(), queries.at(1).begin(), queries.at(1).end()); // only the first query is passed to the backend - expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(0).begin(), queries.at(0).end());; + expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(0).begin(), queries.at(0).end()); s_writeBuffer.clear(); s_backendReadBuffer.clear(); @@ -2259,7 +2259,7 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR) s_readBuffer.insert(s_readBuffer.end(), queries.at(1).begin(), queries.at(1).end()); // only the first query is passed to the backend - expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(0).begin(), queries.at(0).end());; + expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(0).begin(), queries.at(0).end()); s_writeBuffer.clear(); s_backendReadBuffer.clear(); @@ -2332,9 +2332,9 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR) s_readBuffer.insert(s_readBuffer.end(), queries.at(1).begin(), queries.at(1).end()); s_readBuffer.insert(s_readBuffer.end(), queries.at(4).begin(), queries.at(4).end()); - expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(0).begin(), queries.at(0).end());; - expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(1).begin(), queries.at(1).end());; - expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(4).begin(), queries.at(4).end());; + expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(0).begin(), queries.at(0).end()); + expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(1).begin(), queries.at(1).end()); + expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(4).begin(), queries.at(4).end()); s_writeBuffer.clear(); @@ -2475,9 +2475,9 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR) 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()); - expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(0).begin(), queries.at(0).end());; - expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(1).begin(), queries.at(1).end());; - expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(2).begin(), queries.at(2).end());; + expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(0).begin(), queries.at(0).end()); + expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(1).begin(), queries.at(1).end()); + expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(2).begin(), queries.at(2).end()); s_writeBuffer.clear(); @@ -2581,8 +2581,6 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR) return true; }; - g_verbose = true; - auto state = std::make_shared(ConnectionInfo(&localCS), threadData, now); IncomingTCPConnectionState::handleIO(state, now); while (threadData.mplexer->getWatchedFDCount(false) != 0 || threadData.mplexer->getWatchedFDCount(true) != 0) { @@ -2696,8 +2694,6 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR) return true; }; - g_verbose = 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)) { @@ -2723,6 +2719,134 @@ BOOST_AUTO_TEST_CASE(test_IncomingConnectionOOOR_BackendOOOR) /* we need to clear them now, otherwise we end up with dangling pointers to the steps via the TLS context, etc */ IncomingTCPConnectionState::clearAllDownstreamConnections(); } + + { + TEST_NAME("=> Outgoing proxy protocol, 3 queries to the backend, first response is sent, connection closed while reading the second one"); + + PacketBuffer expectedWriteBuffer; + PacketBuffer expectedBackendWriteBuffer; + + auto proxyPayload = makeProxyHeader(true, ComboAddress("0.0.0.0"), local, {}); + BOOST_REQUIRE_GT(proxyPayload.size(), s_proxyProtocolMinimumHeaderSize); + + 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()); + + auto proxyEnabledBackend = std::make_shared(ComboAddress("192.0.2.42:53"), ComboAddress("0.0.0.0:0"), 0, std::string(), 1, false); + proxyEnabledBackend->d_tlsCtx = tlsCtx; + /* enable out-of-order on the backend side as well */ + proxyEnabledBackend->d_maxInFlightQueriesPerConn = 65536; + proxyEnabledBackend-> useProxyProtocol = true; + + expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), proxyPayload.begin(), proxyPayload.end()); + expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(0).begin(), queries.at(0).end()); + expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(1).begin(), queries.at(1).end()); + expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(2).begin(), queries.at(2).end()); + expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), proxyPayload.begin(), proxyPayload.end()); + /* we are using an unordered_map, so all bets are off here :-/ */ + expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(2).begin(), queries.at(2).end()); + expectedBackendWriteBuffer.insert(expectedBackendWriteBuffer.end(), queries.at(1).begin(), queries.at(1).end()); + + s_writeBuffer.clear(); + + s_backendReadBuffer.clear(); + s_backendReadBuffer.insert(s_backendReadBuffer.end(), responses.at(0).begin(), responses.at(0).end()); + s_backendReadBuffer.insert(s_backendReadBuffer.end(), responses.at(1).begin(), responses.at(1).end()); + s_backendReadBuffer.insert(s_backendReadBuffer.end(), responses.at(2).begin(), responses.at(2).end()); + + s_backendWriteBuffer.clear(); + + expectedWriteBuffer = s_backendReadBuffer; + + 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 }, + /* sending query (1) to the backend */ + { ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, proxyPayload.size() + queries.at(0).size() }, + /* got the response */ + { ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, 2 }, + { ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, responses.at(0).size() }, + /* sending the response (1) to the client */ + { ExpectedStep::ExpectedRequest::writeToClient, IOState::Done, responses.at(0).size() }, + /* reading a second query from the client */ + { ExpectedStep::ExpectedRequest::readFromClient, IOState::Done, 2 }, + { ExpectedStep::ExpectedRequest::readFromClient, IOState::Done, queries.at(1).size() - 2 }, + /* sending query (2) to the backend */ + { ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, queries.at(1).size() }, + /* backend is not ready yet */ + { ExpectedStep::ExpectedRequest::readFromBackend, IOState::NeedRead, 0 }, + /* reading a third query from the client */ + { ExpectedStep::ExpectedRequest::readFromClient, IOState::Done, 2 }, + { ExpectedStep::ExpectedRequest::readFromClient, IOState::Done, queries.at(2).size() - 2 }, + /* sending query (3) to the backend */ + { ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, queries.at(2).size() }, + /* backend is not ready yet, but the descriptor becomes ready */ + { ExpectedStep::ExpectedRequest::readFromBackend, IOState::NeedRead, 0, [&threadData](int desc, const ExpectedStep& step) { + /* the backend descriptor becomes ready */ + dynamic_cast(threadData.mplexer.get())->setReady(desc); + }}, + /* nothing from the client */ + { ExpectedStep::ExpectedRequest::readFromClient, IOState::NeedRead, 0 }, + /* nothing from the client (again) */ + { ExpectedStep::ExpectedRequest::readFromClient, IOState::NeedRead, 0 }, + /* backend closes the connection on us */ + { ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, 0 }, + { ExpectedStep::ExpectedRequest::closeBackend, IOState::Done }, + /* opening a new connection to the backend */ + { ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done }, + /* sending query (2) to the backend */ + { ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, proxyPayload.size() + queries.at(1).size() }, + /* sending query (3) to the backend */ + { ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, queries.at(2).size() }, + /* got the response for 2 */ + { ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, 2 }, + { ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, responses.at(1).size() }, + /* sending the response (2) to the client */ + { ExpectedStep::ExpectedRequest::writeToClient, IOState::Done, responses.at(1).size() }, + /* still nothing from the client */ + { ExpectedStep::ExpectedRequest::readFromClient, IOState::NeedRead, 0 }, + /* got the response for 3 */ + { ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, 2 }, + { ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, responses.at(2).size() }, + /* sending the response (3) to the client */ + { ExpectedStep::ExpectedRequest::writeToClient, IOState::Done, responses.at(2).size() }, + /* client closes the connection */ + { ExpectedStep::ExpectedRequest::readFromClient, IOState::Done, 0 }, + /* closing the client connection */ + { ExpectedStep::ExpectedRequest::closeClient, IOState::Done, 0 }, + /* closing the backend connection */ + { ExpectedStep::ExpectedRequest::closeBackend, IOState::Done, 0 }, + }; + + s_processQuery = [proxyEnabledBackend,&counter](DNSQuestion& dq, ClientState& cs, LocalHolders& holders, std::shared_ptr& selectedBackend) -> ProcessQueryResult { + selectedBackend = proxyEnabledBackend; + 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 (threadData.mplexer->getWatchedFDCount(false) != 0 || threadData.mplexer->getWatchedFDCount(true) != 0) { + threadData.mplexer->run(&now); + } + + BOOST_CHECK_EQUAL(s_writeBuffer.size(), expectedWriteBuffer.size()); + BOOST_CHECK(s_writeBuffer == expectedWriteBuffer); + BOOST_CHECK_EQUAL(s_backendWriteBuffer.size(), expectedBackendWriteBuffer.size()); + BOOST_CHECK(s_backendWriteBuffer == expectedBackendWriteBuffer); + + /* 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: