2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #define BOOST_TEST_DYN_LINK
23 #define BOOST_TEST_NO_MAIN
25 #include <boost/test/unit_test.hpp>
27 #include "dnswriter.hh"
29 #include "dnsdist-proxy-protocol.hh"
30 #include "dnsdist-rings.hh"
31 #include "dnsdist-nghttp2.hh"
34 #if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
35 #include <nghttp2/nghttp2.h>
37 BOOST_AUTO_TEST_SUITE(test_dnsdistnghttp2_cc
)
42 enum class ExpectedRequest
54 ExpectedStep(ExpectedRequest r
, IOState n
, size_t b
= 0, std::function
<void(int descriptor
)> fn
= nullptr) :
55 cb(fn
), request(r
), nextState(n
), bytes(b
)
59 std::function
<void(int descriptor
)> cb
{nullptr};
60 ExpectedRequest request
;
68 PacketBuffer d_response
;
71 static std::deque
<ExpectedStep
> s_steps
;
72 static std::map
<uint16_t, ExpectedData
> s_responses
;
73 static std::unique_ptr
<FDMultiplexer
> s_mplexer
;
75 std::ostream
& operator<<(std::ostream
& os
, const ExpectedStep::ExpectedRequest d
);
77 std::ostream
& operator<<(std::ostream
& os
, const ExpectedStep::ExpectedRequest d
)
79 static const std::vector
<std::string
> requests
= {"handshake with client", "read from client", "write to client", "close connection to client", "connect to the backend", "read from the backend", "write to the backend", "close connection to backend"};
80 os
<< requests
.at(static_cast<size_t>(d
));
87 DOHConnection(bool needProxyProtocol
) :
88 d_session(std::unique_ptr
<nghttp2_session
, void (*)(nghttp2_session
*)>(nullptr, nghttp2_session_del
)), d_needProxyProtocol(needProxyProtocol
)
90 nghttp2_session_callbacks
* cbs
= nullptr;
91 nghttp2_session_callbacks_new(&cbs
);
92 std::unique_ptr
<nghttp2_session_callbacks
, void (*)(nghttp2_session_callbacks
*)> callbacks(cbs
, nghttp2_session_callbacks_del
);
94 nghttp2_session_callbacks_set_send_callback(callbacks
.get(), send_callback
);
95 nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks
.get(), on_frame_recv_callback
);
96 nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks
.get(), on_data_chunk_recv_callback
);
97 nghttp2_session_callbacks_set_on_stream_close_callback(callbacks
.get(), on_stream_close_callback
);
98 nghttp2_session
* sess
= nullptr;
99 nghttp2_session_server_new(&sess
, callbacks
.get(), this);
100 d_session
= std::unique_ptr
<nghttp2_session
, void (*)(nghttp2_session
*)>(sess
, nghttp2_session_del
);
102 nghttp2_settings_entry iv
[1] = {
103 {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS
, 100}};
104 nghttp2_submit_settings(d_session
.get(), NGHTTP2_FLAG_NONE
, iv
, sizeof(iv
) / sizeof(*iv
));
107 PacketBuffer d_serverOutBuffer
;
108 PacketBuffer d_proxyProtocolBuffer
;
109 std::map
<uint32_t, PacketBuffer
> d_queries
;
110 std::map
<uint32_t, PacketBuffer
> d_responses
;
111 std::unique_ptr
<nghttp2_session
, void (*)(nghttp2_session
*)> d_session
;
112 /* used to replace the stream ID in outgoing frames. Ugly but the library does not let us
113 test weird cases without that */
114 std::map
<uint32_t, uint32_t> d_idMapping
;
115 bool d_needProxyProtocol
;
117 size_t submitIncoming(const PacketBuffer
& data
, size_t pos
, size_t toWrite
)
120 if (d_needProxyProtocol
) {
122 auto bytesRemaining
= isProxyHeaderComplete(d_proxyProtocolBuffer
);
123 if (bytesRemaining
< 0) {
124 size_t toConsume
= toWrite
> static_cast<size_t>(-bytesRemaining
) ? static_cast<size_t>(-bytesRemaining
) : toWrite
;
125 d_proxyProtocolBuffer
.insert(d_proxyProtocolBuffer
.end(), data
.begin() + pos
, data
.begin() + pos
+ toConsume
);
127 toWrite
-= toConsume
;
128 consumed
+= toConsume
;
130 bytesRemaining
= isProxyHeaderComplete(d_proxyProtocolBuffer
);
131 if (bytesRemaining
> 0) {
132 d_needProxyProtocol
= false;
134 else if (bytesRemaining
== 0) {
135 throw("Fatal error while parsing proxy protocol payload");
138 else if (bytesRemaining
== 0) {
139 throw("Fatal error while parsing proxy protocol payload");
145 } while (d_needProxyProtocol
&& toWrite
> 0);
148 ssize_t readlen
= nghttp2_session_mem_recv(d_session
.get(), &data
.at(pos
), toWrite
);
150 throw("Fatal error while submitting: " + std::string(nghttp2_strerror(static_cast<int>(readlen
))));
153 /* just in case, see if we have anything to send */
154 int rv
= nghttp2_session_send(d_session
.get());
156 throw("Fatal error while sending: " + std::string(nghttp2_strerror(rv
)));
162 void submitResponse(uint32_t streamId
, PacketBuffer
& data
)
164 const nghttp2_nv hdrs
[] = {{(uint8_t*)":status", (uint8_t*)"200", sizeof(":status") - 1, sizeof("200") - 1, NGHTTP2_NV_FLAG_NONE
}};
165 nghttp2_data_provider dataProvider
;
166 dataProvider
.source
.ptr
= &data
;
167 dataProvider
.read_callback
= [](nghttp2_session
* session
, int32_t stream_id
, uint8_t* buf
, size_t length
, uint32_t* data_flags
, nghttp2_data_source
* source
, void* user_data
) -> ssize_t
{
168 auto buffer
= reinterpret_cast<PacketBuffer
*>(source
->ptr
);
170 if (buffer
->size() > 0) {
171 toCopy
= length
> buffer
->size() ? buffer
->size() : length
;
172 memcpy(buf
, &buffer
->at(0), toCopy
);
173 buffer
->erase(buffer
->begin(), buffer
->begin() + toCopy
);
176 if (buffer
->size() == 0) {
177 *data_flags
|= NGHTTP2_DATA_FLAG_EOF
;
179 // cerr<<"submitting response data of size "<<toCopy<<" for stream "<<stream_id<<endl;
183 int rv
= nghttp2_submit_response(d_session
.get(), streamId
, hdrs
, sizeof(hdrs
) / sizeof(*hdrs
), &dataProvider
);
184 // cerr<<"Submitting response for stream ID "<<streamId<<": "<<rv<<endl;
185 BOOST_CHECK_EQUAL(rv
, 0);
186 /* just in case, see if we have anything to send */
187 rv
= nghttp2_session_send(d_session
.get());
188 BOOST_CHECK_EQUAL(rv
, 0);
191 void submitError(uint32_t streamId
, uint16_t status
, const std::string
& msg
)
193 const std::string statusStr
= std::to_string(status
);
194 const nghttp2_nv hdrs
[] = {{(uint8_t*)":status", (uint8_t*)statusStr
.c_str(), sizeof(":status") - 1, statusStr
.size(), NGHTTP2_NV_FLAG_NONE
}};
196 int rv
= nghttp2_submit_response(d_session
.get(), streamId
, hdrs
, sizeof(hdrs
) / sizeof(*hdrs
), nullptr);
197 BOOST_CHECK_EQUAL(rv
, 0);
198 /* just in case, see if we have anything to send */
199 rv
= nghttp2_session_send(d_session
.get());
200 BOOST_CHECK_EQUAL(rv
, 0);
205 int rv
= nghttp2_submit_goaway(d_session
.get(), NGHTTP2_FLAG_NONE
, 0, NGHTTP2_INTERNAL_ERROR
, nullptr, 0);
206 BOOST_CHECK_EQUAL(rv
, 0);
207 /* just in case, see if we have anything to send */
208 rv
= nghttp2_session_send(d_session
.get());
209 BOOST_CHECK_EQUAL(rv
, 0);
213 static ssize_t
send_callback(nghttp2_session
* session
, const uint8_t* data
, size_t length
, int flags
, void* user_data
)
215 DOHConnection
* conn
= reinterpret_cast<DOHConnection
*>(user_data
);
216 // cerr<<"inserting "<<length<<" bytes into the server output buffer of size "<<conn->d_serverOutBuffer.size()<<endl;
217 if (!conn
->d_idMapping
.empty() && length
> 9) {
218 /* frame type == DATA */
219 if (data
[3] == NGHTTP2_DATA
) {
220 uint32_t streamId
= 0;
221 memcpy(&streamId
, &data
[5], sizeof(streamId
));
222 const auto it
= conn
->d_idMapping
.find(ntohl(streamId
));
223 if (it
!= conn
->d_idMapping
.end()) {
224 streamId
= htonl(it
->second
);
225 std::vector
<uint8_t> editedData(length
);
226 std::copy(data
, data
+ length
, editedData
.begin());
227 memcpy(&editedData
.at(5), &streamId
, sizeof(streamId
));
228 conn
->d_serverOutBuffer
.insert(conn
->d_serverOutBuffer
.end(), editedData
.data(), editedData
.data() + length
);
229 return static_cast<ssize_t
>(editedData
.size());
234 conn
->d_serverOutBuffer
.insert(conn
->d_serverOutBuffer
.end(), data
, data
+ length
);
235 return static_cast<ssize_t
>(length
);
238 static int on_frame_recv_callback(nghttp2_session
* session
, const nghttp2_frame
* frame
, void* user_data
)
240 DOHConnection
* conn
= reinterpret_cast<DOHConnection
*>(user_data
);
241 // cerr<<"Frame type is "<<std::to_string(frame->hd.type)<<endl;
242 if ((frame
->hd
.type
== NGHTTP2_HEADERS
|| frame
->hd
.type
== NGHTTP2_DATA
) && frame
->hd
.flags
& NGHTTP2_FLAG_END_STREAM
) {
244 auto stream_data
= nghttp2_session_get_stream_user_data(session
, frame
->hd
.stream_id
);
245 /* For DATA and HEADERS frame, this callback may be called after on_stream_close_callback. Check that stream still alive. */
246 if (stream_data
== nullptr) {
247 cerr
<<"unable to find stream data!"<<endl
;
252 auto& query
= conn
->d_queries
.at(frame
->hd
.stream_id
);
253 BOOST_REQUIRE_GT(query
.size(), sizeof(dnsheader
));
254 const dnsheader_aligned
dh(query
.data());
255 uint16_t id
= ntohs(dh
->id
);
256 // cerr<<"got query ID "<<id<<endl;
258 const auto& expected
= s_responses
.at(id
);
259 BOOST_REQUIRE_EQUAL(expected
.d_query
.size(), query
.size());
260 for (size_t idx
= 0; idx
< query
.size(); idx
++) {
261 if (expected
.d_query
.at(idx
) != query
.at(idx
)) {
262 cerr
<< "Mismatch at offset " << idx
<< ", expected " << std::to_string(query
.at(idx
)) << " got " << std::to_string(expected
.d_query
.at(idx
)) << endl
;
267 DNSName
qname(reinterpret_cast<const char*>(query
.data()), query
.size(), sizeof(dnsheader
), false);
268 if (qname
== DNSName("goaway.powerdns.com.")) {
269 conn
->submitGoAway();
271 else if (qname
== DNSName("500.powerdns.com.") && (id
% 2) == 0) {
272 /* we return a 500 on the first query only */
273 conn
->submitError(frame
->hd
.stream_id
, 500, "Server failure");
275 else if (qname
== DNSName("wrong-stream-id.powerdns.com.") && (id
% 2) == 0) {
276 /* we return a wrong stremad ID on the first query only */
277 BOOST_CHECK_EQUAL(frame
->hd
.stream_id
, 1);
278 conn
->d_responses
[frame
->hd
.stream_id
] = expected
.d_response
;
279 /* use an invalid stream ID! */
280 conn
->d_idMapping
[frame
->hd
.stream_id
] = frame
->hd
.stream_id
+ 4;
281 conn
->submitResponse(frame
->hd
.stream_id
, conn
->d_responses
.at(frame
->hd
.stream_id
));
284 conn
->d_responses
[frame
->hd
.stream_id
] = expected
.d_response
;
285 conn
->submitResponse(frame
->hd
.stream_id
, conn
->d_responses
.at(frame
->hd
.stream_id
));
287 conn
->d_queries
.erase(frame
->hd
.stream_id
);
293 static int on_data_chunk_recv_callback(nghttp2_session
* session
, uint8_t flags
, int32_t stream_id
, const uint8_t* data
, size_t len
, void* user_data
)
295 DOHConnection
* conn
= reinterpret_cast<DOHConnection
*>(user_data
);
296 auto& query
= conn
->d_queries
[stream_id
];
297 query
.insert(query
.end(), data
, data
+ len
);
301 static int on_stream_close_callback(nghttp2_session
* session
, int32_t stream_id
, uint32_t error_code
, void* user_data
)
303 if (error_code
== 0) {
311 static std::map
<int, std::unique_ptr
<DOHConnection
>> s_connectionBuffers
;
313 class MockupTLSConnection
: public TLSConnection
316 MockupTLSConnection(int descriptor
, bool client
= false, bool needProxyProtocol
= false) :
317 d_descriptor(descriptor
), d_client(client
)
319 s_connectionBuffers
[d_descriptor
] = std::make_unique
<DOHConnection
>(needProxyProtocol
);
322 ~MockupTLSConnection() {}
324 IOState
tryHandshake() override
326 auto step
= getStep();
327 BOOST_REQUIRE_EQUAL(step
.request
, ExpectedStep::ExpectedRequest::handshakeClient
);
329 return step
.nextState
;
332 IOState
tryWrite(const PacketBuffer
& buffer
, size_t& pos
, size_t toWrite
) override
334 auto& conn
= s_connectionBuffers
.at(d_descriptor
);
335 auto step
= getStep();
336 BOOST_REQUIRE_EQUAL(step
.request
, !d_client
? ExpectedStep::ExpectedRequest::writeToClient
: ExpectedStep::ExpectedRequest::writeToBackend
);
338 if (step
.bytes
== 0) {
339 if (step
.nextState
== IOState::NeedWrite
) {
340 return step
.nextState
;
342 throw std::runtime_error("Remote host closed the connection");
346 BOOST_REQUIRE_GE(buffer
.size(), pos
+ toWrite
);
348 if (step
.bytes
< toWrite
) {
349 toWrite
= step
.bytes
;
352 conn
->submitIncoming(buffer
, pos
, toWrite
);
355 return step
.nextState
;
358 IOState
tryRead(PacketBuffer
& buffer
, size_t& pos
, size_t toRead
, bool allowIncomplete
= false) override
360 auto& conn
= s_connectionBuffers
.at(d_descriptor
);
361 auto step
= getStep();
362 BOOST_REQUIRE_EQUAL(step
.request
, !d_client
? ExpectedStep::ExpectedRequest::readFromClient
: ExpectedStep::ExpectedRequest::readFromBackend
);
364 if (step
.bytes
== 0) {
365 if (step
.nextState
== IOState::NeedRead
) {
366 return step
.nextState
;
368 throw std::runtime_error("Remote host closed the connection");
371 auto& externalBuffer
= conn
->d_serverOutBuffer
;
374 if (step
.bytes
< toRead
) {
377 if (allowIncomplete
) {
378 if (toRead
> externalBuffer
.size()) {
379 toRead
= externalBuffer
.size();
383 BOOST_REQUIRE_GE(externalBuffer
.size(), toRead
);
386 BOOST_REQUIRE_GE(buffer
.size(), toRead
);
388 std::copy(externalBuffer
.begin(), externalBuffer
.begin() + toRead
, buffer
.begin() + pos
);
390 externalBuffer
.erase(externalBuffer
.begin(), externalBuffer
.begin() + toRead
);
392 return step
.nextState
;
395 IOState
tryConnect(bool fastOpen
, const ComboAddress
& remote
) override
397 auto step
= getStep();
398 BOOST_REQUIRE_EQUAL(step
.request
, ExpectedStep::ExpectedRequest::connectToBackend
);
400 return step
.nextState
;
403 void close() override
405 auto step
= getStep();
406 BOOST_REQUIRE_EQUAL(step
.request
, !d_client
? ExpectedStep::ExpectedRequest::closeClient
: ExpectedStep::ExpectedRequest::closeBackend
);
409 bool isUsable() const override
414 std::string
getServerNameIndication() const override
419 std::vector
<uint8_t> getNextProtocol() const override
421 return std::vector
<uint8_t>();
424 LibsslTLSVersion
getTLSVersion() const override
426 return LibsslTLSVersion::TLS13
;
429 bool hasSessionBeenResumed() const override
434 std::vector
<std::unique_ptr
<TLSSession
>> getSessions() override
439 void setSession(std::unique_ptr
<TLSSession
>& session
) override
443 std::vector
<int> getAsyncFDs() override
448 /* unused in that context, don't bother */
449 void doHandshake() override
453 void connect(bool fastOpen
, const ComboAddress
& remote
, const struct timeval
& timeout
) override
457 size_t read(void* buffer
, size_t bufferSize
, const struct timeval
& readTimeout
, const struct timeval
& totalTimeout
= {0, 0}, bool allowIncomplete
= false) override
462 size_t write(const void* buffer
, size_t bufferSize
, const struct timeval
& writeTimeout
) override
468 ExpectedStep
getStep() const
470 BOOST_REQUIRE(!s_steps
.empty());
471 auto step
= s_steps
.front();
475 step
.cb(d_descriptor
);
481 const int d_descriptor
;
482 bool d_client
{false};
485 #include "test-dnsdistnghttp2_common.hh"
487 class MockupQuerySender
: public TCPQuerySender
490 bool active() const override
495 void handleResponse(const struct timeval
& now
, TCPResponse
&& response
) override
497 if (d_customHandler
) {
498 d_customHandler(d_id
, now
, std::move(response
));
502 BOOST_REQUIRE_GT(response
.d_buffer
.size(), sizeof(dnsheader
));
503 const dnsheader_aligned
dh(response
.d_buffer
.data());
504 uint16_t id
= ntohs(dh
->id
);
506 BOOST_REQUIRE_EQUAL(id
, d_id
);
507 const auto& expected
= s_responses
.at(id
);
508 BOOST_REQUIRE_EQUAL(expected
.d_response
.size(), response
.d_buffer
.size());
509 for (size_t idx
= 0; idx
< response
.d_buffer
.size(); idx
++) {
510 if (expected
.d_response
.at(idx
) != response
.d_buffer
.at(idx
)) {
511 cerr
<< "Mismatch at offset " << idx
<< ", expected " << std::to_string(response
.d_buffer
.at(idx
)) << " got " << std::to_string(expected
.d_response
.at(idx
)) << endl
;
516 if (expected
.d_response
!= response
.d_buffer
) {
517 BOOST_REQUIRE(false);
522 void handleXFRResponse([[maybe_unused
]] const struct timeval
& now
, [[maybe_unused
]] TCPResponse
&& response
) override
526 void notifyIOError([[maybe_unused
]] const struct timeval
& now
, [[maybe_unused
]] TCPResponse
&& response
) override
531 std::function
<void(uint16_t id
, const struct timeval
& now
, TCPResponse
&& response
)> d_customHandler
;
543 s_mplexer
= std::make_unique
<MockupFDMultiplexer
>();
547 clearH2Connections();
554 BOOST_FIXTURE_TEST_CASE(test_SingleQuery
, TestFixture
)
556 auto local
= getBackendAddress("1", 80);
557 ClientState
localCS(local
, true, false, false, "", {});
558 auto tlsCtx
= std::make_shared
<MockupTLSCtx
>();
559 localCS
.tlsFrontend
= std::make_shared
<TLSFrontend
>(tlsCtx
);
562 gettimeofday(&now
, nullptr);
565 DNSName
name("powerdns.com.");
567 GenericDNSPacketWriter
<PacketBuffer
> pwQ(query
, name
, QType::A
, QClass::IN
, 0);
568 pwQ
.getHeader()->rd
= 1;
569 pwQ
.getHeader()->id
= htons(counter
);
571 PacketBuffer response
;
572 GenericDNSPacketWriter
<PacketBuffer
> pwR(response
, name
, QType::A
, QClass::IN
, 0);
573 pwR
.getHeader()->qr
= 1;
574 pwR
.getHeader()->rd
= 1;
575 pwR
.getHeader()->ra
= 1;
576 pwR
.getHeader()->id
= htons(counter
);
577 pwR
.startRecord(name
, QType::A
, 7200, QClass::IN
, DNSResourceRecord::ANSWER
);
578 pwR
.xfr32BitInt(0x01020304);
581 s_responses
[counter
] = {query
, response
};
583 auto backend
= std::make_shared
<DownstreamState
>(getBackendAddress("42", 53));
584 backend
->d_tlsCtx
= tlsCtx
;
585 backend
->d_config
.d_tlsSubjectName
= "backend.powerdns.com";
586 backend
->d_config
.d_dohPath
= "/dns-query";
587 backend
->d_config
.d_addXForwardedHeaders
= true;
589 auto sender
= std::make_shared
<MockupQuerySender
>();
590 sender
->d_id
= counter
;
591 InternalQuery
internalQuery(std::move(query
), InternalQueryState());
594 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
596 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
598 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
600 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
602 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
603 /* set the outgoing descriptor (backend connection) as ready */
604 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
606 /* read settings, headers and response from the server */
607 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
608 /* set the outgoing descriptor (backend connection) as NOT ready anymore */
609 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setNotReady(desc
);
611 /* acknowledge settings */
612 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
613 s_connectionBuffers
.at(desc
)->submitGoAway();
614 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
616 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
617 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
620 auto sliced
= std::shared_ptr
<TCPQuerySender
>(sender
);
621 bool result
= sendH2Query(backend
, s_mplexer
, sliced
, std::move(internalQuery
), false);
622 BOOST_CHECK_EQUAL(result
, true);
624 while (s_mplexer
->getWatchedFDCount(false) != 0 || s_mplexer
->getWatchedFDCount(true) != 0) {
625 s_mplexer
->run(&now
);
627 BOOST_CHECK_EQUAL(sender
->d_valid
, true);
630 BOOST_FIXTURE_TEST_CASE(test_ConcurrentQueries
, TestFixture
)
632 auto local
= getBackendAddress("1", 80);
633 ClientState
localCS(local
, true, false, false, "", {});
634 auto tlsCtx
= std::make_shared
<MockupTLSCtx
>();
635 localCS
.tlsFrontend
= std::make_shared
<TLSFrontend
>(tlsCtx
);
638 gettimeofday(&now
, nullptr);
640 auto backend
= std::make_shared
<DownstreamState
>(getBackendAddress("42", 53));
641 backend
->d_tlsCtx
= tlsCtx
;
642 backend
->d_config
.d_tlsSubjectName
= "backend.powerdns.com";
643 backend
->d_config
.d_dohPath
= "/dns-query";
644 backend
->d_config
.d_addXForwardedHeaders
= true;
646 size_t numberOfQueries
= 2;
647 std::vector
<std::pair
<std::shared_ptr
<MockupQuerySender
>, InternalQuery
>> queries
;
648 for (size_t counter
= 0; counter
< numberOfQueries
; counter
++) {
649 DNSName
name("powerdns.com.");
651 GenericDNSPacketWriter
<PacketBuffer
> pwQ(query
, name
, QType::A
, QClass::IN
, 0);
652 pwQ
.getHeader()->rd
= 1;
653 pwQ
.getHeader()->id
= htons(counter
);
655 PacketBuffer response
;
656 GenericDNSPacketWriter
<PacketBuffer
> pwR(response
, name
, QType::A
, QClass::IN
, 0);
657 pwR
.getHeader()->qr
= 1;
658 pwR
.getHeader()->rd
= 1;
659 pwR
.getHeader()->ra
= 1;
660 pwR
.getHeader()->id
= htons(counter
);
661 pwR
.startRecord(name
, QType::A
, 7200, QClass::IN
, DNSResourceRecord::ANSWER
);
662 pwR
.xfr32BitInt(0x01020304);
665 s_responses
[counter
] = {query
, response
};
667 auto sender
= std::make_shared
<MockupQuerySender
>();
668 sender
->d_id
= counter
;
669 InternalQuery
internalQuery(std::move(query
), InternalQueryState());
670 queries
.push_back({std::move(sender
), std::move(internalQuery
)});
674 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
676 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
678 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
680 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
682 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
683 /* set the outgoing descriptor (backend connection) as ready */
684 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
687 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
689 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
690 /* set the outgoing descriptor (backend connection) as ready */
691 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
693 /* read settings, headers and responses from the server */
694 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
695 /* acknowledge settings */
696 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
697 s_connectionBuffers
.at(desc
)->submitGoAway();
698 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
700 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
701 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
704 for (auto& query
: queries
) {
705 auto sliced
= std::static_pointer_cast
<TCPQuerySender
>(query
.first
);
706 bool result
= sendH2Query(backend
, s_mplexer
, sliced
, std::move(query
.second
), false);
707 BOOST_CHECK_EQUAL(result
, true);
710 while (s_mplexer
->getWatchedFDCount(false) != 0 || s_mplexer
->getWatchedFDCount(true) != 0) {
711 s_mplexer
->run(&now
);
714 for (auto& query
: queries
) {
715 BOOST_CHECK_EQUAL(query
.first
->d_valid
, true);
719 BOOST_FIXTURE_TEST_CASE(test_ConnectionReuse
, TestFixture
)
721 auto local
= getBackendAddress("1", 80);
722 ClientState
localCS(local
, true, false, false, "", {});
723 auto tlsCtx
= std::make_shared
<MockupTLSCtx
>();
724 localCS
.tlsFrontend
= std::make_shared
<TLSFrontend
>(tlsCtx
);
727 gettimeofday(&now
, nullptr);
729 auto backend
= std::make_shared
<DownstreamState
>(getBackendAddress("42", 53));
730 backend
->d_tlsCtx
= tlsCtx
;
731 backend
->d_config
.d_tlsSubjectName
= "backend.powerdns.com";
732 backend
->d_config
.d_dohPath
= "/dns-query";
733 backend
->d_config
.d_addXForwardedHeaders
= true;
735 size_t numberOfQueries
= 2;
736 std::vector
<std::pair
<std::shared_ptr
<MockupQuerySender
>, InternalQuery
>> queries
;
737 for (size_t counter
= 0; counter
< numberOfQueries
; counter
++) {
738 DNSName
name("powerdns.com.");
740 GenericDNSPacketWriter
<PacketBuffer
> pwQ(query
, name
, QType::A
, QClass::IN
, 0);
741 pwQ
.getHeader()->rd
= 1;
742 pwQ
.getHeader()->id
= htons(counter
);
744 PacketBuffer response
;
745 GenericDNSPacketWriter
<PacketBuffer
> pwR(response
, name
, QType::A
, QClass::IN
, 0);
746 pwR
.getHeader()->qr
= 1;
747 pwR
.getHeader()->rd
= 1;
748 pwR
.getHeader()->ra
= 1;
749 pwR
.getHeader()->id
= htons(counter
);
750 pwR
.startRecord(name
, QType::A
, 7200, QClass::IN
, DNSResourceRecord::ANSWER
);
751 pwR
.xfr32BitInt(0x01020304);
754 s_responses
[counter
] = {query
, response
};
756 auto sender
= std::make_shared
<MockupQuerySender
>();
757 sender
->d_id
= counter
;
758 InternalQuery
internalQuery(std::move(query
), InternalQueryState());
759 queries
.push_back({std::move(sender
), std::move(internalQuery
)});
762 bool firstQueryDone
= false;
764 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
766 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
768 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
770 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
772 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
773 /* set the outgoing descriptor (backend connection) as ready */
774 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
776 /* read settings, headers and responses from the server */
777 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
778 /* acknowledge settings */
779 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [&firstQueryDone
](int desc
) {
780 firstQueryDone
= true;
783 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
786 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
787 /* set the outgoing descriptor (backend connection) as ready */
788 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
790 /* read settings, headers and responses from the server */
791 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
792 /* later the backend sends a go away frame */
793 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
794 s_connectionBuffers
.at(desc
)->submitGoAway();
796 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
800 auto& query
= queries
.at(0);
801 auto sliced
= std::static_pointer_cast
<TCPQuerySender
>(query
.first
);
802 bool result
= sendH2Query(backend
, s_mplexer
, sliced
, std::move(query
.second
), false);
803 BOOST_CHECK_EQUAL(result
, true);
805 while (!firstQueryDone
&& (s_mplexer
->getWatchedFDCount(false) != 0 || s_mplexer
->getWatchedFDCount(true) != 0)) {
806 s_mplexer
->run(&now
);
809 BOOST_CHECK_EQUAL(query
.first
->d_valid
, true);
810 BOOST_CHECK_EQUAL(firstQueryDone
, true);
814 auto& query
= queries
.at(1);
815 auto sliced
= std::static_pointer_cast
<TCPQuerySender
>(query
.first
);
816 bool result
= sendH2Query(backend
, s_mplexer
, sliced
, std::move(query
.second
), false);
817 BOOST_CHECK_EQUAL(result
, true);
819 while (s_mplexer
->getWatchedFDCount(false) != 0 || s_mplexer
->getWatchedFDCount(true) != 0) {
820 s_mplexer
->run(&now
);
823 BOOST_CHECK_EQUAL(query
.first
->d_valid
, true);
827 BOOST_FIXTURE_TEST_CASE(test_InvalidDNSAnswer
, TestFixture
)
829 auto local
= getBackendAddress("1", 80);
830 ClientState
localCS(local
, true, false, false, "", {});
831 auto tlsCtx
= std::make_shared
<MockupTLSCtx
>();
832 localCS
.tlsFrontend
= std::make_shared
<TLSFrontend
>(tlsCtx
);
835 gettimeofday(&now
, nullptr);
838 DNSName
name("powerdns.com.");
840 GenericDNSPacketWriter
<PacketBuffer
> pwQ(query
, name
, QType::A
, QClass::IN
, 0);
841 pwQ
.getHeader()->rd
= 1;
842 pwQ
.getHeader()->id
= htons(counter
);
844 PacketBuffer response
;
845 GenericDNSPacketWriter
<PacketBuffer
> pwR(response
, name
, QType::A
, QClass::IN
, 0);
846 pwR
.getHeader()->qr
= 1;
847 pwR
.getHeader()->rd
= 1;
848 pwR
.getHeader()->ra
= 1;
849 pwR
.getHeader()->id
= htons(counter
);
850 pwR
.startRecord(name
, QType::A
, 7200, QClass::IN
, DNSResourceRecord::ANSWER
);
851 pwR
.xfr32BitInt(0x01020304);
854 /* TRUNCATE the answer */
856 s_responses
[counter
] = {query
, response
};
858 auto backend
= std::make_shared
<DownstreamState
>(getBackendAddress("42", 53));
859 backend
->d_tlsCtx
= tlsCtx
;
860 backend
->d_config
.d_tlsSubjectName
= "backend.powerdns.com";
861 backend
->d_config
.d_dohPath
= "/dns-query";
862 backend
->d_config
.d_addXForwardedHeaders
= true;
864 auto sender
= std::make_shared
<MockupQuerySender
>();
865 sender
->d_id
= counter
;
866 sender
->d_customHandler
= [](uint16_t id
, const struct timeval
&, TCPResponse
&& resp
) {
867 BOOST_CHECK_EQUAL(resp
.d_buffer
.size(), 11U);
868 /* simulate an exception, since DoH and UDP frontends will process the query right away,
869 while TCP and DoT will first pass it back to the TCP worker thread */
870 throw std::runtime_error("Invalid response");
872 InternalQuery
internalQuery(std::move(query
), InternalQueryState());
875 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
877 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
879 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
881 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
883 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
884 /* set the outgoing descriptor (backend connection) as ready */
885 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
887 /* read settings, headers and response from the server */
888 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
889 /* acknowledge settings */
890 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
891 /* try to read, the backend says to go away */
892 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
893 s_connectionBuffers
.at(desc
)->submitGoAway();
895 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
898 auto sliced
= std::shared_ptr
<TCPQuerySender
>(sender
);
899 bool result
= sendH2Query(backend
, s_mplexer
, sliced
, std::move(internalQuery
), false);
900 BOOST_CHECK_EQUAL(result
, true);
902 while (s_mplexer
->getWatchedFDCount(false) != 0 || s_mplexer
->getWatchedFDCount(true) != 0) {
903 s_mplexer
->run(&now
);
905 BOOST_CHECK_EQUAL(sender
->d_valid
, false);
908 BOOST_FIXTURE_TEST_CASE(test_TimeoutWhileWriting
, TestFixture
)
910 auto local
= getBackendAddress("1", 80);
911 ClientState
localCS(local
, true, false, false, "", {});
912 auto tlsCtx
= std::make_shared
<MockupTLSCtx
>();
913 localCS
.tlsFrontend
= std::make_shared
<TLSFrontend
>(tlsCtx
);
916 gettimeofday(&now
, nullptr);
918 auto backend
= std::make_shared
<DownstreamState
>(getBackendAddress("42", 53));
919 backend
->d_tlsCtx
= tlsCtx
;
920 backend
->d_config
.d_tlsSubjectName
= "backend.powerdns.com";
921 backend
->d_config
.d_dohPath
= "/dns-query";
922 backend
->d_config
.d_addXForwardedHeaders
= true;
924 size_t numberOfQueries
= 2;
925 std::vector
<std::pair
<std::shared_ptr
<MockupQuerySender
>, InternalQuery
>> queries
;
926 for (size_t counter
= 0; counter
< numberOfQueries
; counter
++) {
927 DNSName
name("powerdns.com.");
929 GenericDNSPacketWriter
<PacketBuffer
> pwQ(query
, name
, QType::A
, QClass::IN
, 0);
930 pwQ
.getHeader()->rd
= 1;
931 pwQ
.getHeader()->id
= htons(counter
);
933 PacketBuffer response
;
934 GenericDNSPacketWriter
<PacketBuffer
> pwR(response
, name
, QType::A
, QClass::IN
, 0);
935 pwR
.getHeader()->qr
= 1;
936 pwR
.getHeader()->rd
= 1;
937 pwR
.getHeader()->ra
= 1;
938 pwR
.getHeader()->id
= htons(counter
);
939 pwR
.startRecord(name
, QType::A
, 7200, QClass::IN
, DNSResourceRecord::ANSWER
);
940 pwR
.xfr32BitInt(0x01020304);
943 s_responses
[counter
] = {query
, response
};
945 auto sender
= std::make_shared
<MockupQuerySender
>();
946 sender
->d_id
= counter
;
947 InternalQuery
internalQuery(std::move(query
), InternalQueryState());
948 queries
.push_back({std::move(sender
), std::move(internalQuery
)});
951 bool timeout
= false;
953 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
955 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
957 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
959 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
961 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
963 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
965 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::NeedWrite
, std::numeric_limits
<size_t>::max(), [&timeout
](int desc
) {
968 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
971 for (auto& query
: queries
) {
972 auto sliced
= std::static_pointer_cast
<TCPQuerySender
>(query
.first
);
973 bool result
= sendH2Query(backend
, s_mplexer
, sliced
, std::move(query
.second
), false);
974 BOOST_CHECK_EQUAL(result
, true);
977 while (!timeout
&& (s_mplexer
->getWatchedFDCount(false) != 0 || s_mplexer
->getWatchedFDCount(true) != 0)) {
978 s_mplexer
->run(&now
);
981 struct timeval later
= now
;
982 later
.tv_sec
+= backend
->d_config
.tcpSendTimeout
+ 1;
984 auto expiredConns
= handleH2Timeouts(*s_mplexer
, later
);
985 BOOST_CHECK_EQUAL(expiredConns
, 1U);
987 for (auto& query
: queries
) {
988 BOOST_CHECK_EQUAL(query
.first
->d_valid
, false);
989 BOOST_CHECK_EQUAL(query
.first
->d_error
, true);
992 BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
995 BOOST_FIXTURE_TEST_CASE(test_TimeoutWhileReading
, TestFixture
)
997 auto local
= getBackendAddress("1", 80);
998 ClientState
localCS(local
, true, false, false, "", {});
999 auto tlsCtx
= std::make_shared
<MockupTLSCtx
>();
1000 localCS
.tlsFrontend
= std::make_shared
<TLSFrontend
>(tlsCtx
);
1003 gettimeofday(&now
, nullptr);
1005 auto backend
= std::make_shared
<DownstreamState
>(getBackendAddress("42", 53));
1006 backend
->d_tlsCtx
= tlsCtx
;
1007 backend
->d_config
.d_tlsSubjectName
= "backend.powerdns.com";
1008 backend
->d_config
.d_dohPath
= "/dns-query";
1009 backend
->d_config
.d_addXForwardedHeaders
= true;
1011 size_t numberOfQueries
= 2;
1012 std::vector
<std::pair
<std::shared_ptr
<MockupQuerySender
>, InternalQuery
>> queries
;
1013 for (size_t counter
= 0; counter
< numberOfQueries
; counter
++) {
1014 DNSName
name("powerdns.com.");
1016 GenericDNSPacketWriter
<PacketBuffer
> pwQ(query
, name
, QType::A
, QClass::IN
, 0);
1017 pwQ
.getHeader()->rd
= 1;
1018 pwQ
.getHeader()->id
= htons(counter
);
1020 PacketBuffer response
;
1021 GenericDNSPacketWriter
<PacketBuffer
> pwR(response
, name
, QType::A
, QClass::IN
, 0);
1022 pwR
.getHeader()->qr
= 1;
1023 pwR
.getHeader()->rd
= 1;
1024 pwR
.getHeader()->ra
= 1;
1025 pwR
.getHeader()->id
= htons(counter
);
1026 pwR
.startRecord(name
, QType::A
, 7200, QClass::IN
, DNSResourceRecord::ANSWER
);
1027 pwR
.xfr32BitInt(0x01020304);
1030 s_responses
[counter
] = {query
, response
};
1032 auto sender
= std::make_shared
<MockupQuerySender
>();
1033 sender
->d_id
= counter
;
1034 InternalQuery
internalQuery(std::move(query
), InternalQueryState());
1035 queries
.push_back({std::move(sender
), std::move(internalQuery
)});
1038 bool timeout
= false;
1040 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
1042 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1044 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1046 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1048 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1050 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1052 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [&timeout
](int desc
) {
1053 /* set the timeout flag now, since the timeout occurs while waiting for the descriptor to become readable */
1056 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
1059 for (auto& query
: queries
) {
1060 auto sliced
= std::static_pointer_cast
<TCPQuerySender
>(query
.first
);
1061 bool result
= sendH2Query(backend
, s_mplexer
, sliced
, std::move(query
.second
), false);
1062 BOOST_CHECK_EQUAL(result
, true);
1065 while (!timeout
&& (s_mplexer
->getWatchedFDCount(false) != 0 || s_mplexer
->getWatchedFDCount(true) != 0)) {
1066 s_mplexer
->run(&now
);
1069 struct timeval later
= now
;
1070 later
.tv_sec
+= backend
->d_config
.tcpRecvTimeout
+ 1;
1072 auto expiredConns
= handleH2Timeouts(*s_mplexer
, later
);
1073 BOOST_CHECK_EQUAL(expiredConns
, 1U);
1075 for (auto& query
: queries
) {
1076 BOOST_CHECK_EQUAL(query
.first
->d_valid
, false);
1077 BOOST_CHECK_EQUAL(query
.first
->d_error
, true);
1079 BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
1082 BOOST_FIXTURE_TEST_CASE(test_ShortWrite
, TestFixture
)
1084 auto local
= getBackendAddress("1", 80);
1085 ClientState
localCS(local
, true, false, false, "", {});
1086 auto tlsCtx
= std::make_shared
<MockupTLSCtx
>();
1087 localCS
.tlsFrontend
= std::make_shared
<TLSFrontend
>(tlsCtx
);
1090 gettimeofday(&now
, nullptr);
1092 auto backend
= std::make_shared
<DownstreamState
>(getBackendAddress("42", 53));
1093 backend
->d_tlsCtx
= tlsCtx
;
1094 backend
->d_config
.d_tlsSubjectName
= "backend.powerdns.com";
1095 backend
->d_config
.d_dohPath
= "/dns-query";
1096 backend
->d_config
.d_addXForwardedHeaders
= true;
1098 size_t numberOfQueries
= 2;
1099 std::vector
<std::pair
<std::shared_ptr
<MockupQuerySender
>, InternalQuery
>> queries
;
1100 for (size_t counter
= 0; counter
< numberOfQueries
; counter
++) {
1101 DNSName
name("powerdns.com.");
1103 GenericDNSPacketWriter
<PacketBuffer
> pwQ(query
, name
, QType::A
, QClass::IN
, 0);
1104 pwQ
.getHeader()->rd
= 1;
1105 pwQ
.getHeader()->id
= htons(counter
);
1107 PacketBuffer response
;
1108 GenericDNSPacketWriter
<PacketBuffer
> pwR(response
, name
, QType::A
, QClass::IN
, 0);
1109 pwR
.getHeader()->qr
= 1;
1110 pwR
.getHeader()->rd
= 1;
1111 pwR
.getHeader()->ra
= 1;
1112 pwR
.getHeader()->id
= htons(counter
);
1113 pwR
.startRecord(name
, QType::A
, 7200, QClass::IN
, DNSResourceRecord::ANSWER
);
1114 pwR
.xfr32BitInt(0x01020304);
1117 s_responses
[counter
] = {query
, response
};
1119 auto sender
= std::make_shared
<MockupQuerySender
>();
1120 sender
->d_id
= counter
;
1121 InternalQuery
internalQuery(std::move(query
), InternalQueryState());
1122 queries
.push_back({std::move(sender
), std::move(internalQuery
)});
1127 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
1129 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1131 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::NeedWrite
, 2, [](int desc
) {
1132 /* set the outgoing descriptor (backend connection) as ready */
1133 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
1135 /* settings (second attempt) + headers + data + headers (second query) + data */
1137 ExpectedStep::ExpectedRequest::writeToBackend
,
1139 std::numeric_limits
<size_t>::max(),
1141 /* read settings, headers and responses from the server */
1142 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1143 /* acknowledge settings */
1144 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [&done
](int desc
) {
1145 /* mark backend as not ready */
1146 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setNotReady(desc
);
1149 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
1152 for (auto& query
: queries
) {
1153 auto sliced
= std::static_pointer_cast
<TCPQuerySender
>(query
.first
);
1154 bool result
= sendH2Query(backend
, s_mplexer
, sliced
, std::move(query
.second
), false);
1155 BOOST_CHECK_EQUAL(result
, true);
1158 while (!done
&& (s_mplexer
->getWatchedFDCount(false) != 0 || s_mplexer
->getWatchedFDCount(true) != 0)) {
1159 s_mplexer
->run(&now
);
1162 for (auto& query
: queries
) {
1163 BOOST_CHECK_EQUAL(query
.first
->d_valid
, true);
1166 BOOST_CHECK_EQUAL(clearH2Connections(), 1U);
1169 BOOST_FIXTURE_TEST_CASE(test_ShortRead
, TestFixture
)
1171 auto local
= getBackendAddress("1", 80);
1172 ClientState
localCS(local
, true, false, false, "", {});
1173 auto tlsCtx
= std::make_shared
<MockupTLSCtx
>();
1174 localCS
.tlsFrontend
= std::make_shared
<TLSFrontend
>(tlsCtx
);
1177 gettimeofday(&now
, nullptr);
1179 auto backend
= std::make_shared
<DownstreamState
>(getBackendAddress("42", 53));
1180 backend
->d_tlsCtx
= tlsCtx
;
1181 backend
->d_config
.d_tlsSubjectName
= "backend.powerdns.com";
1182 backend
->d_config
.d_dohPath
= "/dns-query";
1183 backend
->d_config
.d_addXForwardedHeaders
= true;
1185 size_t numberOfQueries
= 2;
1186 std::vector
<std::pair
<std::shared_ptr
<MockupQuerySender
>, InternalQuery
>> queries
;
1187 for (size_t counter
= 0; counter
< numberOfQueries
; counter
++) {
1188 DNSName
name("powerdns.com.");
1190 GenericDNSPacketWriter
<PacketBuffer
> pwQ(query
, name
, QType::A
, QClass::IN
, 0);
1191 pwQ
.getHeader()->rd
= 1;
1192 pwQ
.getHeader()->id
= htons(counter
);
1194 PacketBuffer response
;
1195 GenericDNSPacketWriter
<PacketBuffer
> pwR(response
, name
, QType::A
, QClass::IN
, 0);
1196 pwR
.getHeader()->qr
= 1;
1197 pwR
.getHeader()->rd
= 1;
1198 pwR
.getHeader()->ra
= 1;
1199 pwR
.getHeader()->id
= htons(counter
);
1200 pwR
.startRecord(name
, QType::A
, 7200, QClass::IN
, DNSResourceRecord::ANSWER
);
1201 pwR
.xfr32BitInt(0x01020304);
1204 s_responses
[counter
] = {query
, response
};
1206 auto sender
= std::make_shared
<MockupQuerySender
>();
1207 sender
->d_id
= counter
;
1208 InternalQuery
internalQuery(std::move(query
), InternalQueryState());
1209 queries
.push_back({std::move(sender
), std::move(internalQuery
)});
1214 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
1216 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1218 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1220 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1222 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
1223 /* set the outgoing descriptor (backend connection) as ready */
1224 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
1227 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1229 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
1230 /* set the outgoing descriptor (backend connection) as ready */
1231 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
1233 /* read settings, headers and responses from the server */
1234 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::NeedRead
, 4},
1235 /* read settings, headers and responses (second attempt) */
1236 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1237 /* acknowledge settings */
1238 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [&done
](int desc
) {
1239 /* mark backend as not ready */
1240 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setNotReady(desc
);
1243 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
1246 for (auto& query
: queries
) {
1247 auto sliced
= std::static_pointer_cast
<TCPQuerySender
>(query
.first
);
1248 bool result
= sendH2Query(backend
, s_mplexer
, sliced
, std::move(query
.second
), false);
1249 BOOST_CHECK_EQUAL(result
, true);
1252 while (!done
&& (s_mplexer
->getWatchedFDCount(false) != 0 || s_mplexer
->getWatchedFDCount(true) != 0)) {
1253 s_mplexer
->run(&now
);
1256 for (auto& query
: queries
) {
1257 BOOST_CHECK_EQUAL(query
.first
->d_valid
, true);
1260 BOOST_CHECK_EQUAL(clearH2Connections(), 1U);
1263 BOOST_FIXTURE_TEST_CASE(test_ConnectionClosedWhileReading
, TestFixture
)
1265 auto local
= getBackendAddress("1", 80);
1266 ClientState
localCS(local
, true, false, false, "", {});
1267 auto tlsCtx
= std::make_shared
<MockupTLSCtx
>();
1268 localCS
.tlsFrontend
= std::make_shared
<TLSFrontend
>(tlsCtx
);
1271 gettimeofday(&now
, nullptr);
1273 auto backend
= std::make_shared
<DownstreamState
>(getBackendAddress("42", 53));
1274 backend
->d_tlsCtx
= tlsCtx
;
1275 backend
->d_config
.d_tlsSubjectName
= "backend.powerdns.com";
1276 backend
->d_config
.d_dohPath
= "/dns-query";
1277 backend
->d_config
.d_addXForwardedHeaders
= true;
1279 size_t numberOfQueries
= 2;
1280 std::vector
<std::pair
<std::shared_ptr
<MockupQuerySender
>, InternalQuery
>> queries
;
1281 for (size_t counter
= 0; counter
< numberOfQueries
; counter
++) {
1282 DNSName
name("powerdns.com.");
1284 GenericDNSPacketWriter
<PacketBuffer
> pwQ(query
, name
, QType::A
, QClass::IN
, 0);
1285 pwQ
.getHeader()->rd
= 1;
1286 pwQ
.getHeader()->id
= htons(counter
);
1288 PacketBuffer response
;
1289 GenericDNSPacketWriter
<PacketBuffer
> pwR(response
, name
, QType::A
, QClass::IN
, 0);
1290 pwR
.getHeader()->qr
= 1;
1291 pwR
.getHeader()->rd
= 1;
1292 pwR
.getHeader()->ra
= 1;
1293 pwR
.getHeader()->id
= htons(counter
);
1294 pwR
.startRecord(name
, QType::A
, 7200, QClass::IN
, DNSResourceRecord::ANSWER
);
1295 pwR
.xfr32BitInt(0x01020304);
1298 s_responses
[counter
] = {query
, response
};
1300 auto sender
= std::make_shared
<MockupQuerySender
>();
1301 sender
->d_id
= counter
;
1302 InternalQuery
internalQuery(std::move(query
), InternalQueryState());
1303 queries
.push_back({std::move(sender
), std::move(internalQuery
)});
1307 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
1309 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1311 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1313 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1315 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
1316 /* set the outgoing descriptor (backend connection) as ready */
1317 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
1320 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1322 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
1323 /* set the outgoing descriptor (backend connection) as ready */
1324 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
1326 /* read settings, headers and responses from the server */
1327 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, 0},
1328 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
1331 for (auto& query
: queries
) {
1332 auto sliced
= std::static_pointer_cast
<TCPQuerySender
>(query
.first
);
1333 bool result
= sendH2Query(backend
, s_mplexer
, sliced
, std::move(query
.second
), false);
1334 BOOST_CHECK_EQUAL(result
, true);
1337 while (s_mplexer
->getWatchedFDCount(false) != 0 || s_mplexer
->getWatchedFDCount(true) != 0) {
1338 s_mplexer
->run(&now
);
1341 for (auto& query
: queries
) {
1342 BOOST_CHECK_EQUAL(query
.first
->d_valid
, false);
1343 BOOST_CHECK_EQUAL(query
.first
->d_error
, true);
1346 BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
1349 BOOST_FIXTURE_TEST_CASE(test_ConnectionClosedWhileWriting
, TestFixture
)
1351 auto local
= getBackendAddress("1", 80);
1352 ClientState
localCS(local
, true, false, false, "", {});
1353 auto tlsCtx
= std::make_shared
<MockupTLSCtx
>();
1354 localCS
.tlsFrontend
= std::make_shared
<TLSFrontend
>(tlsCtx
);
1357 gettimeofday(&now
, nullptr);
1359 auto backend
= std::make_shared
<DownstreamState
>(getBackendAddress("42", 53));
1360 backend
->d_tlsCtx
= tlsCtx
;
1361 backend
->d_config
.d_tlsSubjectName
= "backend.powerdns.com";
1362 backend
->d_config
.d_dohPath
= "/dns-query";
1363 backend
->d_config
.d_addXForwardedHeaders
= true;
1365 size_t numberOfQueries
= 2;
1366 std::vector
<std::pair
<std::shared_ptr
<MockupQuerySender
>, InternalQuery
>> queries
;
1367 for (size_t counter
= 0; counter
< numberOfQueries
; counter
++) {
1368 DNSName
name("powerdns.com.");
1370 GenericDNSPacketWriter
<PacketBuffer
> pwQ(query
, name
, QType::A
, QClass::IN
, 0);
1371 pwQ
.getHeader()->rd
= 1;
1372 pwQ
.getHeader()->id
= htons(counter
);
1374 PacketBuffer response
;
1375 GenericDNSPacketWriter
<PacketBuffer
> pwR(response
, name
, QType::A
, QClass::IN
, 0);
1376 pwR
.getHeader()->qr
= 1;
1377 pwR
.getHeader()->rd
= 1;
1378 pwR
.getHeader()->ra
= 1;
1379 pwR
.getHeader()->id
= htons(counter
);
1380 pwR
.startRecord(name
, QType::A
, 7200, QClass::IN
, DNSResourceRecord::ANSWER
);
1381 pwR
.xfr32BitInt(0x01020304);
1384 s_responses
[counter
] = {query
, response
};
1386 auto sender
= std::make_shared
<MockupQuerySender
>();
1387 sender
->d_id
= counter
;
1388 InternalQuery
internalQuery(std::move(query
), InternalQueryState());
1389 queries
.push_back({std::move(sender
), std::move(internalQuery
)});
1394 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
1396 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1398 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1399 /* headers, connection is closed by the backend */
1400 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, 0},
1401 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
1402 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
1404 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1406 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1408 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1410 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
1411 /* set the outgoing descriptor (backend connection) as ready */
1412 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
1414 /* read settings, headers and response from the server */
1415 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1416 /* acknowledge settings */
1417 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [&done
](int desc
) {
1418 /* mark backend as not ready */
1419 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setNotReady(desc
);
1422 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
1425 for (auto& query
: queries
) {
1426 auto sliced
= std::static_pointer_cast
<TCPQuerySender
>(query
.first
);
1427 bool result
= sendH2Query(backend
, s_mplexer
, sliced
, std::move(query
.second
), false);
1428 BOOST_CHECK_EQUAL(result
, true);
1431 while (!done
&& (s_mplexer
->getWatchedFDCount(false) != 0 || s_mplexer
->getWatchedFDCount(true) != 0)) {
1432 s_mplexer
->run(&now
);
1435 BOOST_CHECK_EQUAL(queries
.at(0).first
->d_valid
, false);
1436 BOOST_CHECK_EQUAL(queries
.at(0).first
->d_error
, true);
1437 BOOST_CHECK_EQUAL(queries
.at(1).first
->d_valid
, true);
1438 BOOST_CHECK_EQUAL(queries
.at(1).first
->d_error
, false);
1440 BOOST_CHECK_EQUAL(clearH2Connections(), 1U);
1443 BOOST_FIXTURE_TEST_CASE(test_GoAwayFromServer
, TestFixture
)
1445 auto local
= getBackendAddress("1", 80);
1446 ClientState
localCS(local
, true, false, false, "", {});
1447 auto tlsCtx
= std::make_shared
<MockupTLSCtx
>();
1448 localCS
.tlsFrontend
= std::make_shared
<TLSFrontend
>(tlsCtx
);
1451 gettimeofday(&now
, nullptr);
1453 auto backend
= std::make_shared
<DownstreamState
>(getBackendAddress("42", 53));
1454 backend
->d_tlsCtx
= tlsCtx
;
1455 backend
->d_config
.d_tlsSubjectName
= "backend.powerdns.com";
1456 backend
->d_config
.d_dohPath
= "/dns-query";
1457 backend
->d_config
.d_addXForwardedHeaders
= true;
1458 /* set the number of reconnection attempts to a low value to not waste time */
1459 backend
->d_config
.d_retries
= 1;
1461 size_t numberOfQueries
= 2;
1462 std::vector
<std::pair
<std::shared_ptr
<MockupQuerySender
>, InternalQuery
>> queries
;
1463 for (size_t counter
= 0; counter
< numberOfQueries
; counter
++) {
1464 DNSName
name("goaway.powerdns.com.");
1466 GenericDNSPacketWriter
<PacketBuffer
> pwQ(query
, name
, QType::A
, QClass::IN
, 0);
1467 pwQ
.getHeader()->rd
= 1;
1468 pwQ
.getHeader()->id
= htons(counter
);
1470 PacketBuffer response
;
1471 GenericDNSPacketWriter
<PacketBuffer
> pwR(response
, name
, QType::A
, QClass::IN
, 0);
1472 pwR
.getHeader()->qr
= 1;
1473 pwR
.getHeader()->rd
= 1;
1474 pwR
.getHeader()->ra
= 1;
1475 pwR
.getHeader()->id
= htons(counter
);
1476 pwR
.startRecord(name
, QType::A
, 7200, QClass::IN
, DNSResourceRecord::ANSWER
);
1477 pwR
.xfr32BitInt(0x01020304);
1480 s_responses
[counter
] = {query
, response
};
1482 auto sender
= std::make_shared
<MockupQuerySender
>();
1483 sender
->d_id
= counter
;
1484 InternalQuery
internalQuery(std::move(query
), InternalQueryState());
1485 queries
.push_back({std::move(sender
), std::move(internalQuery
)});
1489 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
1491 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1493 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1495 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1497 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
1498 /* set the outgoing descriptor (backend connection) as ready */
1499 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
1502 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1504 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
1505 /* set the outgoing descriptor (backend connection) as ready */
1506 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
1508 /* read GO AWAY from the server (1) */
1509 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1510 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
1512 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1514 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1516 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1518 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
1519 /* set the outgoing descriptor (backend connection) as ready */
1520 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
1523 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1525 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
1526 /* set the outgoing descriptor (backend connection) as ready */
1527 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
1529 /* close the first connection. It happens now because the new connection was set up first, then that one destroyed */
1530 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
1531 /* read GO AWAY from the server (1) */
1532 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1533 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
1536 for (auto& query
: queries
) {
1537 auto sliced
= std::static_pointer_cast
<TCPQuerySender
>(query
.first
);
1538 bool result
= sendH2Query(backend
, s_mplexer
, sliced
, std::move(query
.second
), false);
1539 BOOST_CHECK_EQUAL(result
, true);
1542 while (s_mplexer
->getWatchedFDCount(false) != 0 || s_mplexer
->getWatchedFDCount(true) != 0) {
1543 s_mplexer
->run(&now
);
1546 for (auto& query
: queries
) {
1547 BOOST_CHECK_EQUAL(query
.first
->d_valid
, false);
1548 BOOST_CHECK_EQUAL(query
.first
->d_error
, true);
1551 BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
1554 BOOST_FIXTURE_TEST_CASE(test_HTTP500FromServer
, TestFixture
)
1556 auto local
= getBackendAddress("1", 80);
1557 ClientState
localCS(local
, true, false, false, "", {});
1558 auto tlsCtx
= std::make_shared
<MockupTLSCtx
>();
1559 localCS
.tlsFrontend
= std::make_shared
<TLSFrontend
>(tlsCtx
);
1562 gettimeofday(&now
, nullptr);
1564 auto backend
= std::make_shared
<DownstreamState
>(getBackendAddress("42", 53));
1565 backend
->d_tlsCtx
= tlsCtx
;
1566 backend
->d_config
.d_tlsSubjectName
= "backend.powerdns.com";
1567 backend
->d_config
.d_dohPath
= "/dns-query";
1568 backend
->d_config
.d_addXForwardedHeaders
= true;
1570 size_t numberOfQueries
= 2;
1571 std::vector
<std::pair
<std::shared_ptr
<MockupQuerySender
>, InternalQuery
>> queries
;
1572 for (size_t counter
= 0; counter
< numberOfQueries
; counter
++) {
1573 DNSName
name("500.powerdns.com.");
1575 GenericDNSPacketWriter
<PacketBuffer
> pwQ(query
, name
, QType::A
, QClass::IN
, 0);
1576 pwQ
.getHeader()->rd
= 1;
1577 pwQ
.getHeader()->id
= htons(counter
);
1579 PacketBuffer response
;
1580 GenericDNSPacketWriter
<PacketBuffer
> pwR(response
, name
, QType::A
, QClass::IN
, 0);
1581 pwR
.getHeader()->qr
= 1;
1582 pwR
.getHeader()->rd
= 1;
1583 pwR
.getHeader()->ra
= 1;
1584 pwR
.getHeader()->id
= htons(counter
);
1585 pwR
.startRecord(name
, QType::A
, 7200, QClass::IN
, DNSResourceRecord::ANSWER
);
1586 pwR
.xfr32BitInt(0x01020304);
1589 s_responses
[counter
] = {query
, response
};
1591 auto sender
= std::make_shared
<MockupQuerySender
>();
1592 sender
->d_id
= counter
;
1593 InternalQuery
internalQuery(std::move(query
), InternalQueryState());
1594 queries
.push_back({std::move(sender
), std::move(internalQuery
)});
1599 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
1601 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1603 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1605 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1607 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
1608 /* set the outgoing descriptor (backend connection) as ready */
1609 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
1612 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1614 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
1615 /* set the outgoing descriptor (backend connection) as ready */
1616 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
1618 /* read settings, headers and responses from the server */
1619 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1620 /* acknowledge settings */
1621 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [&done
](int desc
) {
1622 /* mark backend as not ready */
1623 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setNotReady(desc
);
1626 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
1629 for (auto& query
: queries
) {
1630 auto sliced
= std::static_pointer_cast
<TCPQuerySender
>(query
.first
);
1631 bool result
= sendH2Query(backend
, s_mplexer
, sliced
, std::move(query
.second
), false);
1632 BOOST_CHECK_EQUAL(result
, true);
1635 while (!done
&& (s_mplexer
->getWatchedFDCount(false) != 0 || s_mplexer
->getWatchedFDCount(true) != 0)) {
1636 s_mplexer
->run(&now
);
1639 BOOST_CHECK_EQUAL(queries
.at(0).first
->d_valid
, false);
1640 BOOST_CHECK_EQUAL(queries
.at(0).first
->d_error
, true);
1641 BOOST_CHECK_EQUAL(queries
.at(1).first
->d_valid
, true);
1642 BOOST_CHECK_EQUAL(queries
.at(1).first
->d_error
, false);
1644 BOOST_CHECK_EQUAL(clearH2Connections(), 1U);
1647 BOOST_FIXTURE_TEST_CASE(test_WrongStreamID
, TestFixture
)
1649 auto local
= getBackendAddress("1", 80);
1650 ClientState
localCS(local
, true, false, false, "", {});
1651 auto tlsCtx
= std::make_shared
<MockupTLSCtx
>();
1652 localCS
.tlsFrontend
= std::make_shared
<TLSFrontend
>(tlsCtx
);
1655 gettimeofday(&now
, nullptr);
1657 auto backend
= std::make_shared
<DownstreamState
>(getBackendAddress("42", 53));
1658 backend
->d_tlsCtx
= tlsCtx
;
1659 backend
->d_config
.d_tlsSubjectName
= "backend.powerdns.com";
1660 backend
->d_config
.d_dohPath
= "/dns-query";
1661 backend
->d_config
.d_addXForwardedHeaders
= true;
1663 size_t numberOfQueries
= 2;
1664 std::vector
<std::pair
<std::shared_ptr
<MockupQuerySender
>, InternalQuery
>> queries
;
1665 for (size_t counter
= 0; counter
< numberOfQueries
; counter
++) {
1666 DNSName
name("wrong-stream-id.powerdns.com.");
1668 GenericDNSPacketWriter
<PacketBuffer
> pwQ(query
, name
, QType::A
, QClass::IN
, 0);
1669 pwQ
.getHeader()->rd
= 1;
1670 pwQ
.getHeader()->id
= htons(counter
);
1672 PacketBuffer response
;
1673 GenericDNSPacketWriter
<PacketBuffer
> pwR(response
, name
, QType::A
, QClass::IN
, 0);
1674 pwR
.getHeader()->qr
= 1;
1675 pwR
.getHeader()->rd
= 1;
1676 pwR
.getHeader()->ra
= 1;
1677 pwR
.getHeader()->id
= htons(counter
);
1678 pwR
.startRecord(name
, QType::A
, 7200, QClass::IN
, DNSResourceRecord::ANSWER
);
1679 pwR
.xfr32BitInt(0x01020304);
1682 s_responses
[counter
] = {query
, response
};
1684 auto sender
= std::make_shared
<MockupQuerySender
>();
1685 sender
->d_id
= counter
;
1686 InternalQuery
internalQuery(std::move(query
), InternalQueryState());
1687 queries
.push_back({std::move(sender
), std::move(internalQuery
)});
1690 bool timeout
= false;
1692 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
1694 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1696 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1698 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1700 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
1701 /* set the outgoing descriptor (backend connection) as ready */
1702 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
1705 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1707 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
1708 /* set the outgoing descriptor (backend connection) as ready */
1709 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
1711 /* read settings, headers and responses from the server */
1712 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1713 /* acknowledge settings */
1714 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1715 /* read ends up as a time out since nghttp2 filters the frame with the wrong stream ID */
1716 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::NeedRead
, 0, [&timeout
](int desc
) {
1717 /* set the timeout flag now, since the timeout occurs while waiting for the descriptor to become readable */
1720 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
1723 for (auto& query
: queries
) {
1724 auto sliced
= std::static_pointer_cast
<TCPQuerySender
>(query
.first
);
1725 bool result
= sendH2Query(backend
, s_mplexer
, sliced
, std::move(query
.second
), false);
1726 BOOST_CHECK_EQUAL(result
, true);
1729 while (!timeout
&& (s_mplexer
->getWatchedFDCount(false) != 0 || s_mplexer
->getWatchedFDCount(true) != 0)) {
1730 s_mplexer
->run(&now
);
1733 struct timeval later
= now
;
1734 later
.tv_sec
+= backend
->d_config
.tcpRecvTimeout
+ 1;
1736 auto expiredConns
= handleH2Timeouts(*s_mplexer
, later
);
1737 BOOST_CHECK_EQUAL(expiredConns
, 1U);
1739 BOOST_CHECK_EQUAL(queries
.at(0).first
->d_valid
, false);
1740 BOOST_CHECK_EQUAL(queries
.at(0).first
->d_error
, true);
1741 BOOST_CHECK_EQUAL(queries
.at(1).first
->d_valid
, false);
1742 BOOST_CHECK_EQUAL(queries
.at(1).first
->d_error
, true);
1744 BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
1747 BOOST_FIXTURE_TEST_CASE(test_ProxyProtocol
, TestFixture
)
1749 auto local
= getBackendAddress("1", 80);
1750 ClientState
localCS(local
, true, false, false, "", {});
1751 auto tlsCtx
= std::make_shared
<MockupTLSCtx
>();
1752 tlsCtx
->d_needProxyProtocol
= true;
1753 localCS
.tlsFrontend
= std::make_shared
<TLSFrontend
>(tlsCtx
);
1756 gettimeofday(&now
, nullptr);
1758 auto backend
= std::make_shared
<DownstreamState
>(getBackendAddress("42", 53));
1759 backend
->d_tlsCtx
= tlsCtx
;
1760 backend
->d_config
.d_tlsSubjectName
= "backend.powerdns.com";
1761 backend
->d_config
.d_dohPath
= "/dns-query";
1762 backend
->d_config
.d_addXForwardedHeaders
= true;
1763 backend
->d_config
.useProxyProtocol
= true;
1765 size_t numberOfQueries
= 2;
1766 std::vector
<std::pair
<std::shared_ptr
<MockupQuerySender
>, InternalQuery
>> queries
;
1767 for (size_t counter
= 0; counter
< numberOfQueries
; counter
++) {
1768 DNSName
name("powerdns.com.");
1770 GenericDNSPacketWriter
<PacketBuffer
> pwQ(query
, name
, QType::A
, QClass::IN
, 0);
1771 pwQ
.getHeader()->rd
= 1;
1772 pwQ
.getHeader()->id
= htons(counter
);
1774 PacketBuffer response
;
1775 GenericDNSPacketWriter
<PacketBuffer
> pwR(response
, name
, QType::A
, QClass::IN
, 0);
1776 pwR
.getHeader()->qr
= 1;
1777 pwR
.getHeader()->rd
= 1;
1778 pwR
.getHeader()->ra
= 1;
1779 pwR
.getHeader()->id
= htons(counter
);
1780 pwR
.startRecord(name
, QType::A
, 7200, QClass::IN
, DNSResourceRecord::ANSWER
);
1781 pwR
.xfr32BitInt(0x01020304);
1784 s_responses
[counter
] = {query
, response
};
1786 auto sender
= std::make_shared
<MockupQuerySender
>();
1787 sender
->d_id
= counter
;
1788 std::string payload
= makeProxyHeader(counter
% 2, local
, local
, {});
1789 InternalQuery
internalQuery(std::move(query
), InternalQueryState());
1790 internalQuery
.d_proxyProtocolPayload
= std::move(payload
);
1791 queries
.push_back({std::move(sender
), std::move(internalQuery
)});
1795 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
1796 /* proxy protocol data + opening */
1797 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1799 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1801 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1803 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
1804 /* set the outgoing descriptor (backend connection) as ready */
1805 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
1807 {ExpectedStep::ExpectedRequest::connectToBackend
, IOState::Done
},
1808 /* proxy protocol data + opening */
1809 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1811 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1813 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1815 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max(), [](int desc
) {
1816 /* set the outgoing descriptor (backend connection) as ready */
1817 dynamic_cast<MockupFDMultiplexer
*>(s_mplexer
.get())->setReady(desc
);
1819 /* read settings, headers and responses from the server */
1820 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1821 /* acknowledge settings */
1822 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1823 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
1824 /* read settings, headers and responses from the server */
1825 {ExpectedStep::ExpectedRequest::readFromBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1826 /* acknowledge settings */
1827 {ExpectedStep::ExpectedRequest::writeToBackend
, IOState::Done
, std::numeric_limits
<size_t>::max()},
1828 {ExpectedStep::ExpectedRequest::closeBackend
, IOState::Done
},
1831 for (auto& query
: queries
) {
1832 auto sliced
= std::static_pointer_cast
<TCPQuerySender
>(query
.first
);
1833 bool result
= sendH2Query(backend
, s_mplexer
, sliced
, std::move(query
.second
), false);
1834 BOOST_CHECK_EQUAL(result
, true);
1837 while (s_mplexer
->getWatchedFDCount(false) != 0 || s_mplexer
->getWatchedFDCount(true) != 0) {
1838 s_mplexer
->run(&now
);
1841 for (auto& query
: queries
) {
1842 BOOST_CHECK_EQUAL(query
.first
->d_valid
, true);
1845 BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
1848 BOOST_AUTO_TEST_SUITE_END();
1849 #endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */