]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsdistdist/test-dnsdistnghttp2_cc.cc
rec: Update new b-root-server.net addresses in built-in hints.
[thirdparty/pdns.git] / pdns / dnsdistdist / test-dnsdistnghttp2_cc.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
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.
8 *
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.
12 *
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.
17 *
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.
21 */
22 #define BOOST_TEST_DYN_LINK
23 #define BOOST_TEST_NO_MAIN
24
25 #include <boost/test/unit_test.hpp>
26
27 #include "dnswriter.hh"
28 #include "dnsdist.hh"
29 #include "dnsdist-proxy-protocol.hh"
30 #include "dnsdist-rings.hh"
31 #include "dnsdist-nghttp2.hh"
32 #include "sstuff.hh"
33
34 #ifdef HAVE_NGHTTP2
35 #include <nghttp2/nghttp2.h>
36
37 BOOST_AUTO_TEST_SUITE(test_dnsdistnghttp2_cc)
38
39 struct ExpectedStep
40 {
41 public:
42 enum class ExpectedRequest
43 {
44 handshakeClient,
45 readFromClient,
46 writeToClient,
47 closeClient,
48 connectToBackend,
49 readFromBackend,
50 writeToBackend,
51 closeBackend
52 };
53
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)
56 {
57 }
58
59 std::function<void(int descriptor)> cb{nullptr};
60 ExpectedRequest request;
61 IOState nextState;
62 size_t bytes{0};
63 };
64
65 struct ExpectedData
66 {
67 PacketBuffer d_query;
68 PacketBuffer d_response;
69 };
70
71 static std::deque<ExpectedStep> s_steps;
72 static std::map<uint16_t, ExpectedData> s_responses;
73 static std::unique_ptr<FDMultiplexer> s_mplexer;
74
75 std::ostream& operator<<(std::ostream& os, const ExpectedStep::ExpectedRequest d);
76
77 std::ostream& operator<<(std::ostream& os, const ExpectedStep::ExpectedRequest d)
78 {
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));
81 return os;
82 }
83
84 class DOHConnection
85 {
86 public:
87 DOHConnection(bool needProxyProtocol) :
88 d_session(std::unique_ptr<nghttp2_session, void (*)(nghttp2_session*)>(nullptr, nghttp2_session_del)), d_needProxyProtocol(needProxyProtocol)
89 {
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);
93 cbs = nullptr;
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);
101
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));
105 }
106
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;
116
117 size_t submitIncoming(const PacketBuffer& data, size_t pos, size_t toWrite)
118 {
119 size_t consumed = 0;
120 if (d_needProxyProtocol) {
121 do {
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);
126 pos += toConsume;
127 toWrite -= toConsume;
128 consumed += toConsume;
129
130 bytesRemaining = isProxyHeaderComplete(d_proxyProtocolBuffer);
131 if (bytesRemaining > 0) {
132 d_needProxyProtocol = false;
133 }
134 else if (bytesRemaining == 0) {
135 throw("Fatal error while parsing proxy protocol payload");
136 }
137 }
138 else if (bytesRemaining == 0) {
139 throw("Fatal error while parsing proxy protocol payload");
140 }
141
142 if (toWrite == 0) {
143 return consumed;
144 }
145 } while (d_needProxyProtocol && toWrite > 0);
146 }
147
148 ssize_t readlen = nghttp2_session_mem_recv(d_session.get(), &data.at(pos), toWrite);
149 if (readlen < 0) {
150 throw("Fatal error while submitting: " + std::string(nghttp2_strerror(static_cast<int>(readlen))));
151 }
152
153 /* just in case, see if we have anything to send */
154 int rv = nghttp2_session_send(d_session.get());
155 if (rv != 0) {
156 throw("Fatal error while sending: " + std::string(nghttp2_strerror(rv)));
157 }
158
159 return readlen;
160 }
161
162 void submitResponse(uint32_t streamId, PacketBuffer& data)
163 {
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);
169 size_t toCopy = 0;
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);
174 }
175
176 if (buffer->size() == 0) {
177 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
178 }
179 // cerr<<"submitting response data of size "<<toCopy<<" for stream "<<stream_id<<endl;
180 return toCopy;
181 };
182
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);
189 }
190
191 void submitError(uint32_t streamId, uint16_t status, const std::string& msg)
192 {
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}};
195
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);
201 }
202
203 void submitGoAway()
204 {
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);
210 }
211
212 private:
213 static ssize_t send_callback(nghttp2_session* session, const uint8_t* data, size_t length, int flags, void* user_data)
214 {
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());
230 }
231 }
232 }
233
234 conn->d_serverOutBuffer.insert(conn->d_serverOutBuffer.end(), data, data + length);
235 return static_cast<ssize_t>(length);
236 }
237
238 static int on_frame_recv_callback(nghttp2_session* session, const nghttp2_frame* frame, void* user_data)
239 {
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) {
243 #if 0
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;
248 return 0;
249 }
250 #endif
251
252 auto& query = conn->d_queries.at(frame->hd.stream_id);
253 BOOST_REQUIRE_GT(query.size(), sizeof(dnsheader));
254 auto dh = reinterpret_cast<const dnsheader*>(query.data());
255 uint16_t id = ntohs(dh->id);
256 // cerr<<"got query ID "<<id<<endl;
257
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;
263 BOOST_CHECK(false);
264 }
265 }
266
267 DNSName qname(reinterpret_cast<const char*>(query.data()), query.size(), sizeof(dnsheader), false);
268 if (qname == DNSName("goaway.powerdns.com.")) {
269 conn->submitGoAway();
270 }
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");
274 }
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));
282 }
283 else {
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));
286 }
287 conn->d_queries.erase(frame->hd.stream_id);
288 }
289
290 return 0;
291 }
292
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)
294 {
295 DOHConnection* conn = reinterpret_cast<DOHConnection*>(user_data);
296 auto& query = conn->d_queries[stream_id];
297 query.insert(query.end(), data, data + len);
298 return 0;
299 }
300
301 static int on_stream_close_callback(nghttp2_session* session, int32_t stream_id, uint32_t error_code, void* user_data)
302 {
303 if (error_code == 0) {
304 return 0;
305 }
306
307 return 0;
308 }
309 };
310
311 static std::map<int, std::unique_ptr<DOHConnection>> s_connectionBuffers;
312
313 class MockupTLSConnection : public TLSConnection
314 {
315 public:
316 MockupTLSConnection(int descriptor, bool client = false, bool needProxyProtocol = false) :
317 d_descriptor(descriptor), d_client(client)
318 {
319 s_connectionBuffers[d_descriptor] = std::make_unique<DOHConnection>(needProxyProtocol);
320 }
321
322 ~MockupTLSConnection() {}
323
324 IOState tryHandshake() override
325 {
326 auto step = getStep();
327 BOOST_REQUIRE_EQUAL(step.request, ExpectedStep::ExpectedRequest::handshakeClient);
328
329 return step.nextState;
330 }
331
332 IOState tryWrite(const PacketBuffer& buffer, size_t& pos, size_t toWrite) override
333 {
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);
337
338 if (step.bytes == 0) {
339 if (step.nextState == IOState::NeedWrite) {
340 return step.nextState;
341 }
342 throw std::runtime_error("Remote host closed the connection");
343 }
344
345 toWrite -= pos;
346 BOOST_REQUIRE_GE(buffer.size(), pos + toWrite);
347
348 if (step.bytes < toWrite) {
349 toWrite = step.bytes;
350 }
351
352 conn->submitIncoming(buffer, pos, toWrite);
353 pos += toWrite;
354
355 return step.nextState;
356 }
357
358 IOState tryRead(PacketBuffer& buffer, size_t& pos, size_t toRead, bool allowIncomplete = false) override
359 {
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);
363
364 if (step.bytes == 0) {
365 if (step.nextState == IOState::NeedRead) {
366 return step.nextState;
367 }
368 throw std::runtime_error("Remote host closed the connection");
369 }
370
371 auto& externalBuffer = conn->d_serverOutBuffer;
372 toRead -= pos;
373
374 if (step.bytes < toRead) {
375 toRead = step.bytes;
376 }
377 if (allowIncomplete) {
378 if (toRead > externalBuffer.size()) {
379 toRead = externalBuffer.size();
380 }
381 }
382 else {
383 BOOST_REQUIRE_GE(externalBuffer.size(), toRead);
384 }
385
386 BOOST_REQUIRE_GE(buffer.size(), toRead);
387
388 std::copy(externalBuffer.begin(), externalBuffer.begin() + toRead, buffer.begin() + pos);
389 pos += toRead;
390 externalBuffer.erase(externalBuffer.begin(), externalBuffer.begin() + toRead);
391
392 return step.nextState;
393 }
394
395 IOState tryConnect(bool fastOpen, const ComboAddress& remote) override
396 {
397 auto step = getStep();
398 BOOST_REQUIRE_EQUAL(step.request, ExpectedStep::ExpectedRequest::connectToBackend);
399
400 return step.nextState;
401 }
402
403 void close() override
404 {
405 auto step = getStep();
406 BOOST_REQUIRE_EQUAL(step.request, !d_client ? ExpectedStep::ExpectedRequest::closeClient : ExpectedStep::ExpectedRequest::closeBackend);
407 }
408
409 bool isUsable() const override
410 {
411 return true;
412 }
413
414 std::string getServerNameIndication() const override
415 {
416 return "";
417 }
418
419 std::vector<uint8_t> getNextProtocol() const override
420 {
421 return std::vector<uint8_t>();
422 }
423
424 LibsslTLSVersion getTLSVersion() const override
425 {
426 return LibsslTLSVersion::TLS13;
427 }
428
429 bool hasSessionBeenResumed() const override
430 {
431 return false;
432 }
433
434 std::vector<std::unique_ptr<TLSSession>> getSessions() override
435 {
436 return {};
437 }
438
439 void setSession(std::unique_ptr<TLSSession>& session) override
440 {
441 }
442
443 std::vector<int> getAsyncFDs() override
444 {
445 return {};
446 }
447
448 /* unused in that context, don't bother */
449 void doHandshake() override
450 {
451 }
452
453 void connect(bool fastOpen, const ComboAddress& remote, const struct timeval& timeout) override
454 {
455 }
456
457 size_t read(void* buffer, size_t bufferSize, const struct timeval& readTimeout, const struct timeval& totalTimeout = {0, 0}, bool allowIncomplete = false) override
458 {
459 return 0;
460 }
461
462 size_t write(const void* buffer, size_t bufferSize, const struct timeval& writeTimeout) override
463 {
464 return 0;
465 }
466
467 private:
468 ExpectedStep getStep() const
469 {
470 BOOST_REQUIRE(!s_steps.empty());
471 auto step = s_steps.front();
472 s_steps.pop_front();
473
474 if (step.cb) {
475 step.cb(d_descriptor);
476 }
477
478 return step;
479 }
480
481 const int d_descriptor;
482 bool d_client{false};
483 };
484
485 #include "test-dnsdistnghttp2_common.hh"
486
487 class MockupQuerySender : public TCPQuerySender
488 {
489 public:
490 bool active() const override
491 {
492 return true;
493 }
494
495 void handleResponse(const struct timeval& now, TCPResponse&& response) override
496 {
497 if (d_customHandler) {
498 d_customHandler(d_id, now, std::move(response));
499 return;
500 }
501
502 BOOST_REQUIRE_GT(response.d_buffer.size(), sizeof(dnsheader));
503 auto dh = reinterpret_cast<const dnsheader*>(response.d_buffer.data());
504 uint16_t id = ntohs(dh->id);
505
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;
512 BOOST_CHECK(false);
513 }
514 }
515
516 if (expected.d_response != response.d_buffer) {
517 BOOST_REQUIRE(false);
518 }
519 d_valid = true;
520 }
521
522 void handleXFRResponse([[maybe_unused]] const struct timeval& now, [[maybe_unused]] TCPResponse&& response) override
523 {
524 }
525
526 void notifyIOError([[maybe_unused]] const struct timeval& now, [[maybe_unused]] TCPResponse&& response) override
527 {
528 d_error = true;
529 }
530
531 std::function<void(uint16_t id, const struct timeval& now, TCPResponse&& response)> d_customHandler;
532 uint16_t d_id{0};
533 bool d_valid{false};
534 bool d_error{false};
535 };
536
537 struct TestFixture
538 {
539 TestFixture()
540 {
541 s_steps.clear();
542 s_responses.clear();
543 s_mplexer = std::make_unique<MockupFDMultiplexer>();
544 }
545 ~TestFixture()
546 {
547 clearH2Connections();
548 s_steps.clear();
549 s_responses.clear();
550 s_mplexer.reset();
551 }
552 };
553
554 BOOST_FIXTURE_TEST_CASE(test_SingleQuery, TestFixture)
555 {
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);
560
561 struct timeval now;
562 gettimeofday(&now, nullptr);
563
564 size_t counter = 1;
565 DNSName name("powerdns.com.");
566 PacketBuffer query;
567 GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
568 pwQ.getHeader()->rd = 1;
569 pwQ.getHeader()->id = htons(counter);
570
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);
579 pwR.commit();
580
581 s_responses[counter] = {query, response};
582
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;
588
589 auto sender = std::make_shared<MockupQuerySender>();
590 sender->d_id = counter;
591 InternalQuery internalQuery(std::move(query), InternalQueryState());
592
593 s_steps = {
594 {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
595 /* opening */
596 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
597 /* settings */
598 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
599 /* headers */
600 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
601 /* data */
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);
605 }},
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);
610 }},
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);
615 }},
616 {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
617 {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
618 };
619
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);
623
624 while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) {
625 s_mplexer->run(&now);
626 }
627 BOOST_CHECK_EQUAL(sender->d_valid, true);
628 }
629
630 BOOST_FIXTURE_TEST_CASE(test_ConcurrentQueries, TestFixture)
631 {
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);
636
637 struct timeval now;
638 gettimeofday(&now, nullptr);
639
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;
645
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.");
650 PacketBuffer query;
651 GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
652 pwQ.getHeader()->rd = 1;
653 pwQ.getHeader()->id = htons(counter);
654
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);
663 pwR.commit();
664
665 s_responses[counter] = {query, response};
666
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)});
671 }
672
673 s_steps = {
674 {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
675 /* opening */
676 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
677 /* settings */
678 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
679 /* headers */
680 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
681 /* data */
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);
685 }},
686 /* headers */
687 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
688 /* data */
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);
692 }},
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);
699 }},
700 {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
701 {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
702 };
703
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);
708 }
709
710 while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) {
711 s_mplexer->run(&now);
712 }
713
714 for (auto& query : queries) {
715 BOOST_CHECK_EQUAL(query.first->d_valid, true);
716 }
717 }
718
719 BOOST_FIXTURE_TEST_CASE(test_ConnectionReuse, TestFixture)
720 {
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);
725
726 struct timeval now;
727 gettimeofday(&now, nullptr);
728
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;
734
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.");
739 PacketBuffer query;
740 GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
741 pwQ.getHeader()->rd = 1;
742 pwQ.getHeader()->id = htons(counter);
743
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);
752 pwR.commit();
753
754 s_responses[counter] = {query, response};
755
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)});
760 }
761
762 bool firstQueryDone = false;
763 s_steps = {
764 {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
765 /* opening */
766 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
767 /* settings */
768 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
769 /* headers */
770 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
771 /* data */
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);
775 }},
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;
781 }},
782 /* headers */
783 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
784 }},
785 /* data */
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);
789 }},
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();
795 }},
796 {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
797 };
798
799 {
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);
804
805 while (!firstQueryDone && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) {
806 s_mplexer->run(&now);
807 }
808
809 BOOST_CHECK_EQUAL(query.first->d_valid, true);
810 BOOST_CHECK_EQUAL(firstQueryDone, true);
811 }
812
813 {
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);
818
819 while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) {
820 s_mplexer->run(&now);
821 }
822
823 BOOST_CHECK_EQUAL(query.first->d_valid, true);
824 }
825 }
826
827 BOOST_FIXTURE_TEST_CASE(test_InvalidDNSAnswer, TestFixture)
828 {
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);
833
834 struct timeval now;
835 gettimeofday(&now, nullptr);
836
837 size_t counter = 1;
838 DNSName name("powerdns.com.");
839 PacketBuffer query;
840 GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
841 pwQ.getHeader()->rd = 1;
842 pwQ.getHeader()->id = htons(counter);
843
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);
852 pwR.commit();
853
854 /* TRUNCATE the answer */
855 response.resize(11);
856 s_responses[counter] = {query, response};
857
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;
863
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");
871 };
872 InternalQuery internalQuery(std::move(query), InternalQueryState());
873
874 s_steps = {
875 {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
876 /* opening */
877 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
878 /* settings */
879 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
880 /* headers */
881 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
882 /* data */
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);
886 }},
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();
894 }},
895 {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
896 };
897
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);
901
902 while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) {
903 s_mplexer->run(&now);
904 }
905 BOOST_CHECK_EQUAL(sender->d_valid, false);
906 }
907
908 BOOST_FIXTURE_TEST_CASE(test_TimeoutWhileWriting, TestFixture)
909 {
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);
914
915 struct timeval now;
916 gettimeofday(&now, nullptr);
917
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;
923
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.");
928 PacketBuffer query;
929 GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
930 pwQ.getHeader()->rd = 1;
931 pwQ.getHeader()->id = htons(counter);
932
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);
941 pwR.commit();
942
943 s_responses[counter] = {query, response};
944
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)});
949 }
950
951 bool timeout = false;
952 s_steps = {
953 {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
954 /* opening */
955 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
956 /* settings */
957 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
958 /* headers */
959 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
960 /* data */
961 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
962 /* headers */
963 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
964 /* data */
965 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::NeedWrite, std::numeric_limits<size_t>::max(), [&timeout](int desc) {
966 timeout = true;
967 }},
968 {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
969 };
970
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);
975 }
976
977 while (!timeout && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) {
978 s_mplexer->run(&now);
979 }
980
981 struct timeval later = now;
982 later.tv_sec += backend->d_config.tcpSendTimeout + 1;
983
984 auto expiredConns = handleH2Timeouts(*s_mplexer, later);
985 BOOST_CHECK_EQUAL(expiredConns, 1U);
986
987 for (auto& query : queries) {
988 BOOST_CHECK_EQUAL(query.first->d_valid, false);
989 BOOST_CHECK_EQUAL(query.first->d_error, true);
990 }
991
992 BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
993 }
994
995 BOOST_FIXTURE_TEST_CASE(test_TimeoutWhileReading, TestFixture)
996 {
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);
1001
1002 struct timeval now;
1003 gettimeofday(&now, nullptr);
1004
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;
1010
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.");
1015 PacketBuffer query;
1016 GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
1017 pwQ.getHeader()->rd = 1;
1018 pwQ.getHeader()->id = htons(counter);
1019
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);
1028 pwR.commit();
1029
1030 s_responses[counter] = {query, response};
1031
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)});
1036 }
1037
1038 bool timeout = false;
1039 s_steps = {
1040 {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1041 /* opening */
1042 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1043 /* settings */
1044 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1045 /* headers */
1046 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1047 /* data */
1048 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1049 /* headers */
1050 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1051 /* data */
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 */
1054 timeout = true;
1055 }},
1056 {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1057 };
1058
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);
1063 }
1064
1065 while (!timeout && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) {
1066 s_mplexer->run(&now);
1067 }
1068
1069 struct timeval later = now;
1070 later.tv_sec += backend->d_config.tcpRecvTimeout + 1;
1071
1072 auto expiredConns = handleH2Timeouts(*s_mplexer, later);
1073 BOOST_CHECK_EQUAL(expiredConns, 1U);
1074
1075 for (auto& query : queries) {
1076 BOOST_CHECK_EQUAL(query.first->d_valid, false);
1077 BOOST_CHECK_EQUAL(query.first->d_error, true);
1078 }
1079 BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
1080 }
1081
1082 BOOST_FIXTURE_TEST_CASE(test_ShortWrite, TestFixture)
1083 {
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);
1088
1089 struct timeval now;
1090 gettimeofday(&now, nullptr);
1091
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;
1097
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.");
1102 PacketBuffer query;
1103 GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
1104 pwQ.getHeader()->rd = 1;
1105 pwQ.getHeader()->id = htons(counter);
1106
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);
1115 pwR.commit();
1116
1117 s_responses[counter] = {query, response};
1118
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)});
1123 }
1124
1125 bool done = false;
1126 s_steps = {
1127 {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1128 /* opening */
1129 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1130 /* settings */
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);
1134 }},
1135 /* settings (second attempt) + headers + data + headers (second query) + data */
1136 {
1137 ExpectedStep::ExpectedRequest::writeToBackend,
1138 IOState::Done,
1139 std::numeric_limits<size_t>::max(),
1140 },
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);
1147 done = true;
1148 }},
1149 {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1150 };
1151
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);
1156 }
1157
1158 while (!done && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) {
1159 s_mplexer->run(&now);
1160 }
1161
1162 for (auto& query : queries) {
1163 BOOST_CHECK_EQUAL(query.first->d_valid, true);
1164 }
1165
1166 BOOST_CHECK_EQUAL(clearH2Connections(), 1U);
1167 }
1168
1169 BOOST_FIXTURE_TEST_CASE(test_ShortRead, TestFixture)
1170 {
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);
1175
1176 struct timeval now;
1177 gettimeofday(&now, nullptr);
1178
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;
1184
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.");
1189 PacketBuffer query;
1190 GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
1191 pwQ.getHeader()->rd = 1;
1192 pwQ.getHeader()->id = htons(counter);
1193
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);
1202 pwR.commit();
1203
1204 s_responses[counter] = {query, response};
1205
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)});
1210 }
1211
1212 bool done = false;
1213 s_steps = {
1214 {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1215 /* opening */
1216 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1217 /* settings */
1218 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1219 /* headers */
1220 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1221 /* data */
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);
1225 }},
1226 /* headers */
1227 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1228 /* data */
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);
1232 }},
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);
1241 done = true;
1242 }},
1243 {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1244 };
1245
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);
1250 }
1251
1252 while (!done && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) {
1253 s_mplexer->run(&now);
1254 }
1255
1256 for (auto& query : queries) {
1257 BOOST_CHECK_EQUAL(query.first->d_valid, true);
1258 }
1259
1260 BOOST_CHECK_EQUAL(clearH2Connections(), 1U);
1261 }
1262
1263 BOOST_FIXTURE_TEST_CASE(test_ConnectionClosedWhileReading, TestFixture)
1264 {
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);
1269
1270 struct timeval now;
1271 gettimeofday(&now, nullptr);
1272
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;
1278
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.");
1283 PacketBuffer query;
1284 GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
1285 pwQ.getHeader()->rd = 1;
1286 pwQ.getHeader()->id = htons(counter);
1287
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);
1296 pwR.commit();
1297
1298 s_responses[counter] = {query, response};
1299
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)});
1304 }
1305
1306 s_steps = {
1307 {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1308 /* opening */
1309 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1310 /* settings */
1311 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1312 /* headers */
1313 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1314 /* data */
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);
1318 }},
1319 /* headers */
1320 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1321 /* data */
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);
1325 }},
1326 /* read settings, headers and responses from the server */
1327 {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, 0},
1328 {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1329 };
1330
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);
1335 }
1336
1337 while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) {
1338 s_mplexer->run(&now);
1339 }
1340
1341 for (auto& query : queries) {
1342 BOOST_CHECK_EQUAL(query.first->d_valid, false);
1343 BOOST_CHECK_EQUAL(query.first->d_error, true);
1344 }
1345
1346 BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
1347 }
1348
1349 BOOST_FIXTURE_TEST_CASE(test_ConnectionClosedWhileWriting, TestFixture)
1350 {
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);
1355
1356 struct timeval now;
1357 gettimeofday(&now, nullptr);
1358
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;
1364
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.");
1369 PacketBuffer query;
1370 GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
1371 pwQ.getHeader()->rd = 1;
1372 pwQ.getHeader()->id = htons(counter);
1373
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);
1382 pwR.commit();
1383
1384 s_responses[counter] = {query, response};
1385
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)});
1390 }
1391
1392 bool done = false;
1393 s_steps = {
1394 {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1395 /* opening */
1396 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1397 /* settings */
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},
1403 /* opening */
1404 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1405 /* settings */
1406 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1407 /* headers */
1408 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1409 /* data */
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);
1413 }},
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);
1420 done = true;
1421 }},
1422 {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1423 };
1424
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);
1429 }
1430
1431 while (!done && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) {
1432 s_mplexer->run(&now);
1433 }
1434
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);
1439
1440 BOOST_CHECK_EQUAL(clearH2Connections(), 1U);
1441 }
1442
1443 BOOST_FIXTURE_TEST_CASE(test_GoAwayFromServer, TestFixture)
1444 {
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);
1449
1450 struct timeval now;
1451 gettimeofday(&now, nullptr);
1452
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;
1460
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.");
1465 PacketBuffer query;
1466 GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
1467 pwQ.getHeader()->rd = 1;
1468 pwQ.getHeader()->id = htons(counter);
1469
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);
1478 pwR.commit();
1479
1480 s_responses[counter] = {query, response};
1481
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)});
1486 }
1487
1488 s_steps = {
1489 {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1490 /* opening */
1491 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1492 /* settings */
1493 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1494 /* headers */
1495 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1496 /* data */
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);
1500 }},
1501 /* headers */
1502 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1503 /* data */
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);
1507 }},
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},
1511 /* opening */
1512 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1513 /* settings */
1514 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1515 /* headers */
1516 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1517 /* data */
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);
1521 }},
1522 /* headers */
1523 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1524 /* data */
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);
1528 }},
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},
1534 };
1535
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);
1540 }
1541
1542 while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) {
1543 s_mplexer->run(&now);
1544 }
1545
1546 for (auto& query : queries) {
1547 BOOST_CHECK_EQUAL(query.first->d_valid, false);
1548 BOOST_CHECK_EQUAL(query.first->d_error, true);
1549 }
1550
1551 BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
1552 }
1553
1554 BOOST_FIXTURE_TEST_CASE(test_HTTP500FromServer, TestFixture)
1555 {
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);
1560
1561 struct timeval now;
1562 gettimeofday(&now, nullptr);
1563
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;
1569
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.");
1574 PacketBuffer query;
1575 GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
1576 pwQ.getHeader()->rd = 1;
1577 pwQ.getHeader()->id = htons(counter);
1578
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);
1587 pwR.commit();
1588
1589 s_responses[counter] = {query, response};
1590
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)});
1595 }
1596
1597 bool done = false;
1598 s_steps = {
1599 {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1600 /* opening */
1601 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1602 /* settings */
1603 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1604 /* headers */
1605 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1606 /* data */
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);
1610 }},
1611 /* headers */
1612 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1613 /* data */
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);
1617 }},
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);
1624 done = true;
1625 }},
1626 {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1627 };
1628
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);
1633 }
1634
1635 while (!done && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) {
1636 s_mplexer->run(&now);
1637 }
1638
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);
1643
1644 BOOST_CHECK_EQUAL(clearH2Connections(), 1U);
1645 }
1646
1647 BOOST_FIXTURE_TEST_CASE(test_WrongStreamID, TestFixture)
1648 {
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);
1653
1654 struct timeval now;
1655 gettimeofday(&now, nullptr);
1656
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;
1662
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.");
1667 PacketBuffer query;
1668 GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
1669 pwQ.getHeader()->rd = 1;
1670 pwQ.getHeader()->id = htons(counter);
1671
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);
1680 pwR.commit();
1681
1682 s_responses[counter] = {query, response};
1683
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)});
1688 }
1689
1690 bool timeout = false;
1691 s_steps = {
1692 {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1693 /* opening */
1694 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1695 /* settings */
1696 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1697 /* headers */
1698 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1699 /* data */
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);
1703 }},
1704 /* headers */
1705 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1706 /* data */
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);
1710 }},
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 */
1718 timeout = true;
1719 }},
1720 {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1721 };
1722
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);
1727 }
1728
1729 while (!timeout && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) {
1730 s_mplexer->run(&now);
1731 }
1732
1733 struct timeval later = now;
1734 later.tv_sec += backend->d_config.tcpRecvTimeout + 1;
1735
1736 auto expiredConns = handleH2Timeouts(*s_mplexer, later);
1737 BOOST_CHECK_EQUAL(expiredConns, 1U);
1738
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);
1743
1744 BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
1745 }
1746
1747 BOOST_FIXTURE_TEST_CASE(test_ProxyProtocol, TestFixture)
1748 {
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);
1754
1755 struct timeval now;
1756 gettimeofday(&now, nullptr);
1757
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;
1764
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.");
1769 PacketBuffer query;
1770 GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
1771 pwQ.getHeader()->rd = 1;
1772 pwQ.getHeader()->id = htons(counter);
1773
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);
1782 pwR.commit();
1783
1784 s_responses[counter] = {query, response};
1785
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)});
1792 }
1793
1794 s_steps = {
1795 {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1796 /* proxy protocol data + opening */
1797 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1798 /* settings */
1799 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1800 /* headers */
1801 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1802 /* data */
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);
1806 }},
1807 {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1808 /* proxy protocol data + opening */
1809 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1810 /* settings */
1811 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1812 /* headers */
1813 {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1814 /* data */
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);
1818 }},
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},
1829 };
1830
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);
1835 }
1836
1837 while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) {
1838 s_mplexer->run(&now);
1839 }
1840
1841 for (auto& query : queries) {
1842 BOOST_CHECK_EQUAL(query.first->d_valid, true);
1843 }
1844
1845 BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
1846 }
1847
1848 BOOST_AUTO_TEST_SUITE_END();
1849 #endif /* HAVE_NGHTTP2 */