]>
Commit | Line | Data |
---|---|---|
f05cd66c RG |
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 | */ | |
1c2d079d | 22 | #ifndef BOOST_TEST_DYN_LINK |
f05cd66c | 23 | #define BOOST_TEST_DYN_LINK |
1c2d079d FM |
24 | #endif |
25 | ||
f05cd66c RG |
26 | #define BOOST_TEST_NO_MAIN |
27 | ||
28 | #include <boost/test/unit_test.hpp> | |
29 | ||
30 | #include "dnswriter.hh" | |
31 | #include "dnsdist.hh" | |
32 | #include "dnsdist-proxy-protocol.hh" | |
33 | #include "dnsdist-rings.hh" | |
34 | #include "dnsdist-nghttp2.hh" | |
690a9c40 | 35 | #include "sstuff.hh" |
f05cd66c | 36 | |
cf25b82b | 37 | #if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2) |
f05cd66c RG |
38 | #include <nghttp2/nghttp2.h> |
39 | ||
40 | BOOST_AUTO_TEST_SUITE(test_dnsdistnghttp2_cc) | |
41 | ||
42 | struct ExpectedStep | |
43 | { | |
44 | public: | |
1226f653 RG |
45 | enum class ExpectedRequest |
46 | { | |
47 | handshakeClient, | |
48 | readFromClient, | |
49 | writeToClient, | |
50 | closeClient, | |
51 | connectToBackend, | |
52 | readFromBackend, | |
53 | writeToBackend, | |
54 | closeBackend | |
55 | }; | |
f05cd66c | 56 | |
4e82c08c | 57 | ExpectedStep(ExpectedRequest r, IOState n, size_t b = 0, std::function<void(int descriptor)> fn = nullptr) : |
1226f653 | 58 | cb(fn), request(r), nextState(n), bytes(b) |
f05cd66c RG |
59 | { |
60 | } | |
61 | ||
4e82c08c | 62 | std::function<void(int descriptor)> cb{nullptr}; |
f05cd66c RG |
63 | ExpectedRequest request; |
64 | IOState nextState; | |
65 | size_t bytes{0}; | |
66 | }; | |
67 | ||
68 | struct ExpectedData | |
69 | { | |
70 | PacketBuffer d_query; | |
71 | PacketBuffer d_response; | |
72 | }; | |
73 | ||
1226f653 | 74 | static std::deque<ExpectedStep> s_steps; |
f05cd66c | 75 | static std::map<uint16_t, ExpectedData> s_responses; |
8298e5d4 | 76 | static std::unique_ptr<FDMultiplexer> s_mplexer; |
f05cd66c | 77 | |
1226f653 | 78 | std::ostream& operator<<(std::ostream& os, const ExpectedStep::ExpectedRequest d); |
f05cd66c | 79 | |
1226f653 | 80 | std::ostream& operator<<(std::ostream& os, const ExpectedStep::ExpectedRequest d) |
f05cd66c | 81 | { |
1226f653 RG |
82 | 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"}; |
83 | os << requests.at(static_cast<size_t>(d)); | |
f05cd66c RG |
84 | return os; |
85 | } | |
86 | ||
becad613 | 87 | class DOHConnection |
f05cd66c | 88 | { |
becad613 | 89 | public: |
0e6892c6 RG |
90 | DOHConnection(bool needProxyProtocol) : |
91 | d_session(std::unique_ptr<nghttp2_session, void (*)(nghttp2_session*)>(nullptr, nghttp2_session_del)), d_needProxyProtocol(needProxyProtocol) | |
f05cd66c RG |
92 | { |
93 | nghttp2_session_callbacks* cbs = nullptr; | |
94 | nghttp2_session_callbacks_new(&cbs); | |
95 | std::unique_ptr<nghttp2_session_callbacks, void (*)(nghttp2_session_callbacks*)> callbacks(cbs, nghttp2_session_callbacks_del); | |
96 | cbs = nullptr; | |
97 | nghttp2_session_callbacks_set_send_callback(callbacks.get(), send_callback); | |
98 | nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks.get(), on_frame_recv_callback); | |
99 | nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks.get(), on_data_chunk_recv_callback); | |
100 | nghttp2_session_callbacks_set_on_stream_close_callback(callbacks.get(), on_stream_close_callback); | |
101 | nghttp2_session* sess = nullptr; | |
102 | nghttp2_session_server_new(&sess, callbacks.get(), this); | |
103 | d_session = std::unique_ptr<nghttp2_session, void (*)(nghttp2_session*)>(sess, nghttp2_session_del); | |
104 | ||
105 | nghttp2_settings_entry iv[1] = { | |
106 | {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}}; | |
1226f653 | 107 | nghttp2_submit_settings(d_session.get(), NGHTTP2_FLAG_NONE, iv, sizeof(iv) / sizeof(*iv)); |
f05cd66c RG |
108 | } |
109 | ||
110 | PacketBuffer d_serverOutBuffer; | |
0e6892c6 | 111 | PacketBuffer d_proxyProtocolBuffer; |
1226f653 RG |
112 | std::map<uint32_t, PacketBuffer> d_queries; |
113 | std::map<uint32_t, PacketBuffer> d_responses; | |
114 | std::unique_ptr<nghttp2_session, void (*)(nghttp2_session*)> d_session; | |
f05cd66c RG |
115 | /* used to replace the stream ID in outgoing frames. Ugly but the library does not let us |
116 | test weird cases without that */ | |
1226f653 | 117 | std::map<uint32_t, uint32_t> d_idMapping; |
0e6892c6 | 118 | bool d_needProxyProtocol; |
f05cd66c RG |
119 | |
120 | size_t submitIncoming(const PacketBuffer& data, size_t pos, size_t toWrite) | |
121 | { | |
0e6892c6 RG |
122 | size_t consumed = 0; |
123 | if (d_needProxyProtocol) { | |
124 | do { | |
125 | auto bytesRemaining = isProxyHeaderComplete(d_proxyProtocolBuffer); | |
126 | if (bytesRemaining < 0) { | |
127 | size_t toConsume = toWrite > static_cast<size_t>(-bytesRemaining) ? static_cast<size_t>(-bytesRemaining) : toWrite; | |
128 | d_proxyProtocolBuffer.insert(d_proxyProtocolBuffer.end(), data.begin() + pos, data.begin() + pos + toConsume); | |
129 | pos += toConsume; | |
130 | toWrite -= toConsume; | |
131 | consumed += toConsume; | |
132 | ||
133 | bytesRemaining = isProxyHeaderComplete(d_proxyProtocolBuffer); | |
134 | if (bytesRemaining > 0) { | |
135 | d_needProxyProtocol = false; | |
136 | } | |
137 | else if (bytesRemaining == 0) { | |
138 | throw("Fatal error while parsing proxy protocol payload"); | |
139 | } | |
140 | } | |
141 | else if (bytesRemaining == 0) { | |
142 | throw("Fatal error while parsing proxy protocol payload"); | |
143 | } | |
144 | ||
145 | if (toWrite == 0) { | |
146 | return consumed; | |
147 | } | |
2a25c80f | 148 | } while (d_needProxyProtocol && toWrite > 0); |
0e6892c6 RG |
149 | } |
150 | ||
f05cd66c RG |
151 | ssize_t readlen = nghttp2_session_mem_recv(d_session.get(), &data.at(pos), toWrite); |
152 | if (readlen < 0) { | |
153 | throw("Fatal error while submitting: " + std::string(nghttp2_strerror(static_cast<int>(readlen)))); | |
154 | } | |
155 | ||
156 | /* just in case, see if we have anything to send */ | |
157 | int rv = nghttp2_session_send(d_session.get()); | |
158 | if (rv != 0) { | |
159 | throw("Fatal error while sending: " + std::string(nghttp2_strerror(rv))); | |
160 | } | |
161 | ||
162 | return readlen; | |
163 | } | |
164 | ||
165 | void submitResponse(uint32_t streamId, PacketBuffer& data) | |
166 | { | |
9f5bb193 | 167 | const nghttp2_nv hdrs[] = {{(uint8_t*)":status", (uint8_t*)"200", sizeof(":status") - 1, sizeof("200") - 1, NGHTTP2_NV_FLAG_NONE}}; |
f05cd66c RG |
168 | nghttp2_data_provider dataProvider; |
169 | dataProvider.source.ptr = &data; | |
170 | 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 { | |
171 | auto buffer = reinterpret_cast<PacketBuffer*>(source->ptr); | |
172 | size_t toCopy = 0; | |
173 | if (buffer->size() > 0) { | |
174 | toCopy = length > buffer->size() ? buffer->size() : length; | |
175 | memcpy(buf, &buffer->at(0), toCopy); | |
176 | buffer->erase(buffer->begin(), buffer->begin() + toCopy); | |
177 | } | |
178 | ||
179 | if (buffer->size() == 0) { | |
180 | *data_flags |= NGHTTP2_DATA_FLAG_EOF; | |
181 | } | |
182 | // cerr<<"submitting response data of size "<<toCopy<<" for stream "<<stream_id<<endl; | |
183 | return toCopy; | |
184 | }; | |
185 | ||
1226f653 | 186 | int rv = nghttp2_submit_response(d_session.get(), streamId, hdrs, sizeof(hdrs) / sizeof(*hdrs), &dataProvider); |
f05cd66c RG |
187 | // cerr<<"Submitting response for stream ID "<<streamId<<": "<<rv<<endl; |
188 | BOOST_CHECK_EQUAL(rv, 0); | |
becad613 RG |
189 | /* just in case, see if we have anything to send */ |
190 | rv = nghttp2_session_send(d_session.get()); | |
191 | BOOST_CHECK_EQUAL(rv, 0); | |
f05cd66c RG |
192 | } |
193 | ||
194 | void submitError(uint32_t streamId, uint16_t status, const std::string& msg) | |
195 | { | |
196 | const std::string statusStr = std::to_string(status); | |
9f5bb193 | 197 | const nghttp2_nv hdrs[] = {{(uint8_t*)":status", (uint8_t*)statusStr.c_str(), sizeof(":status") - 1, statusStr.size(), NGHTTP2_NV_FLAG_NONE}}; |
f05cd66c | 198 | |
1226f653 | 199 | int rv = nghttp2_submit_response(d_session.get(), streamId, hdrs, sizeof(hdrs) / sizeof(*hdrs), nullptr); |
f05cd66c | 200 | BOOST_CHECK_EQUAL(rv, 0); |
becad613 RG |
201 | /* just in case, see if we have anything to send */ |
202 | rv = nghttp2_session_send(d_session.get()); | |
203 | BOOST_CHECK_EQUAL(rv, 0); | |
f05cd66c RG |
204 | } |
205 | ||
206 | void submitGoAway() | |
207 | { | |
208 | int rv = nghttp2_submit_goaway(d_session.get(), NGHTTP2_FLAG_NONE, 0, NGHTTP2_INTERNAL_ERROR, nullptr, 0); | |
209 | BOOST_CHECK_EQUAL(rv, 0); | |
becad613 RG |
210 | /* just in case, see if we have anything to send */ |
211 | rv = nghttp2_session_send(d_session.get()); | |
212 | BOOST_CHECK_EQUAL(rv, 0); | |
f05cd66c RG |
213 | } |
214 | ||
215 | private: | |
216 | static ssize_t send_callback(nghttp2_session* session, const uint8_t* data, size_t length, int flags, void* user_data) | |
217 | { | |
218 | DOHConnection* conn = reinterpret_cast<DOHConnection*>(user_data); | |
219 | // cerr<<"inserting "<<length<<" bytes into the server output buffer of size "<<conn->d_serverOutBuffer.size()<<endl; | |
220 | if (!conn->d_idMapping.empty() && length > 9) { | |
221 | /* frame type == DATA */ | |
222 | if (data[3] == NGHTTP2_DATA) { | |
223 | uint32_t streamId = 0; | |
224 | memcpy(&streamId, &data[5], sizeof(streamId)); | |
225 | const auto it = conn->d_idMapping.find(ntohl(streamId)); | |
226 | if (it != conn->d_idMapping.end()) { | |
227 | streamId = htonl(it->second); | |
228 | std::vector<uint8_t> editedData(length); | |
229 | std::copy(data, data + length, editedData.begin()); | |
230 | memcpy(&editedData.at(5), &streamId, sizeof(streamId)); | |
231 | conn->d_serverOutBuffer.insert(conn->d_serverOutBuffer.end(), editedData.data(), editedData.data() + length); | |
232 | return static_cast<ssize_t>(editedData.size()); | |
233 | } | |
234 | } | |
235 | } | |
236 | ||
237 | conn->d_serverOutBuffer.insert(conn->d_serverOutBuffer.end(), data, data + length); | |
238 | return static_cast<ssize_t>(length); | |
239 | } | |
240 | ||
241 | static int on_frame_recv_callback(nghttp2_session* session, const nghttp2_frame* frame, void* user_data) | |
242 | { | |
243 | DOHConnection* conn = reinterpret_cast<DOHConnection*>(user_data); | |
244 | // cerr<<"Frame type is "<<std::to_string(frame->hd.type)<<endl; | |
245 | if ((frame->hd.type == NGHTTP2_HEADERS || frame->hd.type == NGHTTP2_DATA) && frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { | |
246 | #if 0 | |
247 | auto stream_data = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); | |
248 | /* For DATA and HEADERS frame, this callback may be called after on_stream_close_callback. Check that stream still alive. */ | |
249 | if (stream_data == nullptr) { | |
250 | cerr<<"unable to find stream data!"<<endl; | |
251 | return 0; | |
252 | } | |
253 | #endif | |
254 | ||
255 | auto& query = conn->d_queries.at(frame->hd.stream_id); | |
256 | BOOST_REQUIRE_GT(query.size(), sizeof(dnsheader)); | |
90686725 | 257 | const dnsheader_aligned dh(query.data()); |
f05cd66c RG |
258 | uint16_t id = ntohs(dh->id); |
259 | // cerr<<"got query ID "<<id<<endl; | |
260 | ||
261 | const auto& expected = s_responses.at(id); | |
262 | BOOST_REQUIRE_EQUAL(expected.d_query.size(), query.size()); | |
263 | for (size_t idx = 0; idx < query.size(); idx++) { | |
264 | if (expected.d_query.at(idx) != query.at(idx)) { | |
1226f653 | 265 | cerr << "Mismatch at offset " << idx << ", expected " << std::to_string(query.at(idx)) << " got " << std::to_string(expected.d_query.at(idx)) << endl; |
f05cd66c RG |
266 | BOOST_CHECK(false); |
267 | } | |
268 | } | |
269 | ||
270 | DNSName qname(reinterpret_cast<const char*>(query.data()), query.size(), sizeof(dnsheader), false); | |
271 | if (qname == DNSName("goaway.powerdns.com.")) { | |
272 | conn->submitGoAway(); | |
273 | } | |
274 | else if (qname == DNSName("500.powerdns.com.") && (id % 2) == 0) { | |
275 | /* we return a 500 on the first query only */ | |
276 | conn->submitError(frame->hd.stream_id, 500, "Server failure"); | |
277 | } | |
278 | else if (qname == DNSName("wrong-stream-id.powerdns.com.") && (id % 2) == 0) { | |
279 | /* we return a wrong stremad ID on the first query only */ | |
4caba288 | 280 | BOOST_CHECK_EQUAL(frame->hd.stream_id, 1); |
f05cd66c RG |
281 | conn->d_responses[frame->hd.stream_id] = expected.d_response; |
282 | /* use an invalid stream ID! */ | |
283 | conn->d_idMapping[frame->hd.stream_id] = frame->hd.stream_id + 4; | |
284 | conn->submitResponse(frame->hd.stream_id, conn->d_responses.at(frame->hd.stream_id)); | |
285 | } | |
286 | else { | |
287 | conn->d_responses[frame->hd.stream_id] = expected.d_response; | |
288 | conn->submitResponse(frame->hd.stream_id, conn->d_responses.at(frame->hd.stream_id)); | |
289 | } | |
290 | conn->d_queries.erase(frame->hd.stream_id); | |
291 | } | |
292 | ||
293 | return 0; | |
294 | } | |
295 | ||
296 | 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) | |
297 | { | |
298 | DOHConnection* conn = reinterpret_cast<DOHConnection*>(user_data); | |
f05cd66c RG |
299 | auto& query = conn->d_queries[stream_id]; |
300 | query.insert(query.end(), data, data + len); | |
f05cd66c RG |
301 | return 0; |
302 | } | |
303 | ||
304 | static int on_stream_close_callback(nghttp2_session* session, int32_t stream_id, uint32_t error_code, void* user_data) | |
305 | { | |
f05cd66c RG |
306 | if (error_code == 0) { |
307 | return 0; | |
308 | } | |
309 | ||
310 | return 0; | |
311 | } | |
f05cd66c RG |
312 | }; |
313 | ||
314 | static std::map<int, std::unique_ptr<DOHConnection>> s_connectionBuffers; | |
315 | ||
316 | class MockupTLSConnection : public TLSConnection | |
317 | { | |
318 | public: | |
0e6892c6 | 319 | MockupTLSConnection(int descriptor, bool client = false, bool needProxyProtocol = false) : |
1226f653 | 320 | d_descriptor(descriptor), d_client(client) |
f05cd66c | 321 | { |
0e6892c6 | 322 | s_connectionBuffers[d_descriptor] = std::make_unique<DOHConnection>(needProxyProtocol); |
f05cd66c RG |
323 | } |
324 | ||
1226f653 | 325 | ~MockupTLSConnection() {} |
f05cd66c RG |
326 | |
327 | IOState tryHandshake() override | |
328 | { | |
329 | auto step = getStep(); | |
330 | BOOST_REQUIRE_EQUAL(step.request, ExpectedStep::ExpectedRequest::handshakeClient); | |
331 | ||
332 | return step.nextState; | |
333 | } | |
334 | ||
335 | IOState tryWrite(const PacketBuffer& buffer, size_t& pos, size_t toWrite) override | |
336 | { | |
337 | auto& conn = s_connectionBuffers.at(d_descriptor); | |
338 | auto step = getStep(); | |
339 | BOOST_REQUIRE_EQUAL(step.request, !d_client ? ExpectedStep::ExpectedRequest::writeToClient : ExpectedStep::ExpectedRequest::writeToBackend); | |
340 | ||
341 | if (step.bytes == 0) { | |
342 | if (step.nextState == IOState::NeedWrite) { | |
343 | return step.nextState; | |
344 | } | |
345 | throw std::runtime_error("Remote host closed the connection"); | |
346 | } | |
347 | ||
348 | toWrite -= pos; | |
349 | BOOST_REQUIRE_GE(buffer.size(), pos + toWrite); | |
350 | ||
351 | if (step.bytes < toWrite) { | |
352 | toWrite = step.bytes; | |
353 | } | |
354 | ||
355 | conn->submitIncoming(buffer, pos, toWrite); | |
356 | pos += toWrite; | |
357 | ||
358 | return step.nextState; | |
359 | } | |
360 | ||
1226f653 | 361 | IOState tryRead(PacketBuffer& buffer, size_t& pos, size_t toRead, bool allowIncomplete = false) override |
f05cd66c RG |
362 | { |
363 | auto& conn = s_connectionBuffers.at(d_descriptor); | |
364 | auto step = getStep(); | |
365 | BOOST_REQUIRE_EQUAL(step.request, !d_client ? ExpectedStep::ExpectedRequest::readFromClient : ExpectedStep::ExpectedRequest::readFromBackend); | |
366 | ||
367 | if (step.bytes == 0) { | |
368 | if (step.nextState == IOState::NeedRead) { | |
369 | return step.nextState; | |
370 | } | |
371 | throw std::runtime_error("Remote host closed the connection"); | |
372 | } | |
373 | ||
374 | auto& externalBuffer = conn->d_serverOutBuffer; | |
375 | toRead -= pos; | |
376 | ||
377 | if (step.bytes < toRead) { | |
378 | toRead = step.bytes; | |
379 | } | |
380 | if (allowIncomplete) { | |
381 | if (toRead > externalBuffer.size()) { | |
382 | toRead = externalBuffer.size(); | |
383 | } | |
384 | } | |
385 | else { | |
386 | BOOST_REQUIRE_GE(externalBuffer.size(), toRead); | |
387 | } | |
388 | ||
389 | BOOST_REQUIRE_GE(buffer.size(), toRead); | |
390 | ||
f05cd66c RG |
391 | std::copy(externalBuffer.begin(), externalBuffer.begin() + toRead, buffer.begin() + pos); |
392 | pos += toRead; | |
393 | externalBuffer.erase(externalBuffer.begin(), externalBuffer.begin() + toRead); | |
f05cd66c RG |
394 | |
395 | return step.nextState; | |
396 | } | |
397 | ||
398 | IOState tryConnect(bool fastOpen, const ComboAddress& remote) override | |
399 | { | |
400 | auto step = getStep(); | |
401 | BOOST_REQUIRE_EQUAL(step.request, ExpectedStep::ExpectedRequest::connectToBackend); | |
402 | ||
403 | return step.nextState; | |
404 | } | |
405 | ||
406 | void close() override | |
407 | { | |
408 | auto step = getStep(); | |
409 | BOOST_REQUIRE_EQUAL(step.request, !d_client ? ExpectedStep::ExpectedRequest::closeClient : ExpectedStep::ExpectedRequest::closeBackend); | |
410 | } | |
411 | ||
767f5514 RG |
412 | bool isUsable() const override |
413 | { | |
414 | return true; | |
415 | } | |
416 | ||
f05cd66c RG |
417 | std::string getServerNameIndication() const override |
418 | { | |
419 | return ""; | |
420 | } | |
421 | ||
422 | std::vector<uint8_t> getNextProtocol() const override | |
423 | { | |
424 | return std::vector<uint8_t>(); | |
425 | } | |
426 | ||
427 | LibsslTLSVersion getTLSVersion() const override | |
428 | { | |
429 | return LibsslTLSVersion::TLS13; | |
430 | } | |
431 | ||
432 | bool hasSessionBeenResumed() const override | |
433 | { | |
434 | return false; | |
435 | } | |
436 | ||
437 | std::vector<std::unique_ptr<TLSSession>> getSessions() override | |
438 | { | |
439 | return {}; | |
440 | } | |
441 | ||
442 | void setSession(std::unique_ptr<TLSSession>& session) override | |
443 | { | |
444 | } | |
445 | ||
489caa9f RG |
446 | std::vector<int> getAsyncFDs() override |
447 | { | |
448 | return {}; | |
449 | } | |
450 | ||
f05cd66c RG |
451 | /* unused in that context, don't bother */ |
452 | void doHandshake() override | |
453 | { | |
454 | } | |
455 | ||
456 | void connect(bool fastOpen, const ComboAddress& remote, const struct timeval& timeout) override | |
457 | { | |
458 | } | |
459 | ||
1226f653 | 460 | size_t read(void* buffer, size_t bufferSize, const struct timeval& readTimeout, const struct timeval& totalTimeout = {0, 0}, bool allowIncomplete = false) override |
f05cd66c RG |
461 | { |
462 | return 0; | |
463 | } | |
464 | ||
465 | size_t write(const void* buffer, size_t bufferSize, const struct timeval& writeTimeout) override | |
466 | { | |
467 | return 0; | |
468 | } | |
1226f653 | 469 | |
f05cd66c RG |
470 | private: |
471 | ExpectedStep getStep() const | |
472 | { | |
473 | BOOST_REQUIRE(!s_steps.empty()); | |
474 | auto step = s_steps.front(); | |
475 | s_steps.pop_front(); | |
476 | ||
477 | if (step.cb) { | |
4e82c08c | 478 | step.cb(d_descriptor); |
f05cd66c RG |
479 | } |
480 | ||
481 | return step; | |
482 | } | |
483 | ||
484 | const int d_descriptor; | |
485 | bool d_client{false}; | |
486 | }; | |
487 | ||
c02b7e13 | 488 | #include "test-dnsdistnghttp2_common.hh" |
f05cd66c RG |
489 | |
490 | class MockupQuerySender : public TCPQuerySender | |
491 | { | |
492 | public: | |
493 | bool active() const override | |
494 | { | |
495 | return true; | |
496 | } | |
497 | ||
f05cd66c RG |
498 | void handleResponse(const struct timeval& now, TCPResponse&& response) override |
499 | { | |
500 | if (d_customHandler) { | |
501 | d_customHandler(d_id, now, std::move(response)); | |
502 | return; | |
503 | } | |
504 | ||
505 | BOOST_REQUIRE_GT(response.d_buffer.size(), sizeof(dnsheader)); | |
90686725 | 506 | const dnsheader_aligned dh(response.d_buffer.data()); |
f05cd66c RG |
507 | uint16_t id = ntohs(dh->id); |
508 | ||
509 | BOOST_REQUIRE_EQUAL(id, d_id); | |
510 | const auto& expected = s_responses.at(id); | |
511 | BOOST_REQUIRE_EQUAL(expected.d_response.size(), response.d_buffer.size()); | |
512 | for (size_t idx = 0; idx < response.d_buffer.size(); idx++) { | |
513 | if (expected.d_response.at(idx) != response.d_buffer.at(idx)) { | |
1226f653 | 514 | cerr << "Mismatch at offset " << idx << ", expected " << std::to_string(response.d_buffer.at(idx)) << " got " << std::to_string(expected.d_response.at(idx)) << endl; |
f05cd66c RG |
515 | BOOST_CHECK(false); |
516 | } | |
517 | } | |
518 | ||
519 | if (expected.d_response != response.d_buffer) { | |
520 | BOOST_REQUIRE(false); | |
521 | } | |
522 | d_valid = true; | |
523 | } | |
524 | ||
af1bc11b | 525 | void handleXFRResponse([[maybe_unused]] const struct timeval& now, [[maybe_unused]] TCPResponse&& response) override |
f05cd66c RG |
526 | { |
527 | } | |
528 | ||
af1bc11b | 529 | void notifyIOError([[maybe_unused]] const struct timeval& now, [[maybe_unused]] TCPResponse&& response) override |
f05cd66c RG |
530 | { |
531 | d_error = true; | |
532 | } | |
533 | ||
534 | std::function<void(uint16_t id, const struct timeval& now, TCPResponse&& response)> d_customHandler; | |
535 | uint16_t d_id{0}; | |
536 | bool d_valid{false}; | |
537 | bool d_error{false}; | |
538 | }; | |
539 | ||
f05cd66c RG |
540 | struct TestFixture |
541 | { | |
542 | TestFixture() | |
543 | { | |
544 | s_steps.clear(); | |
545 | s_responses.clear(); | |
2bbc9eb0 | 546 | s_mplexer = std::make_unique<MockupFDMultiplexer>(); |
f05cd66c RG |
547 | } |
548 | ~TestFixture() | |
549 | { | |
550 | clearH2Connections(); | |
551 | s_steps.clear(); | |
552 | s_responses.clear(); | |
553 | s_mplexer.reset(); | |
1226f653 | 554 | } |
f05cd66c RG |
555 | }; |
556 | ||
557 | BOOST_FIXTURE_TEST_CASE(test_SingleQuery, TestFixture) | |
558 | { | |
690a9c40 | 559 | auto local = getBackendAddress("1", 80); |
c114cd18 | 560 | ClientState localCS(local, true, false, 0, "", {}, true); |
f05cd66c RG |
561 | auto tlsCtx = std::make_shared<MockupTLSCtx>(); |
562 | localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx); | |
563 | ||
564 | struct timeval now; | |
565 | gettimeofday(&now, nullptr); | |
566 | ||
567 | size_t counter = 1; | |
568 | DNSName name("powerdns.com."); | |
569 | PacketBuffer query; | |
570 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0); | |
571 | pwQ.getHeader()->rd = 1; | |
572 | pwQ.getHeader()->id = htons(counter); | |
573 | ||
574 | PacketBuffer response; | |
575 | GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0); | |
576 | pwR.getHeader()->qr = 1; | |
577 | pwR.getHeader()->rd = 1; | |
578 | pwR.getHeader()->ra = 1; | |
579 | pwR.getHeader()->id = htons(counter); | |
580 | pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); | |
581 | pwR.xfr32BitInt(0x01020304); | |
582 | pwR.commit(); | |
583 | ||
584 | s_responses[counter] = {query, response}; | |
585 | ||
db5fbb86 | 586 | auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53)); |
f05cd66c | 587 | backend->d_tlsCtx = tlsCtx; |
8cf75ac2 RG |
588 | backend->d_config.d_tlsSubjectName = "backend.powerdns.com"; |
589 | backend->d_config.d_dohPath = "/dns-query"; | |
590 | backend->d_config.d_addXForwardedHeaders = true; | |
f05cd66c RG |
591 | |
592 | auto sender = std::make_shared<MockupQuerySender>(); | |
593 | sender->d_id = counter; | |
592b1d99 | 594 | InternalQuery internalQuery(std::move(query), InternalQueryState()); |
f05cd66c RG |
595 | |
596 | s_steps = { | |
1226f653 | 597 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, |
f05cd66c | 598 | /* opening */ |
1226f653 | 599 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 600 | /* settings */ |
1226f653 | 601 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 602 | /* headers */ |
1226f653 | 603 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 604 | /* data */ |
4e82c08c | 605 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
606 | /* set the outgoing descriptor (backend connection) as ready */ |
607 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
608 | }}, | |
f05cd66c | 609 | /* read settings, headers and response from the server */ |
4e82c08c | 610 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
becad613 RG |
611 | /* set the outgoing descriptor (backend connection) as NOT ready anymore */ |
612 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setNotReady(desc); | |
613 | }}, | |
f05cd66c | 614 | /* acknowledge settings */ |
4e82c08c | 615 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
becad613 RG |
616 | s_connectionBuffers.at(desc)->submitGoAway(); |
617 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
618 | }}, | |
619 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()}, | |
1226f653 | 620 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, |
f05cd66c RG |
621 | }; |
622 | ||
623 | auto sliced = std::shared_ptr<TCPQuerySender>(sender); | |
624 | bool result = sendH2Query(backend, s_mplexer, sliced, std::move(internalQuery), false); | |
625 | BOOST_CHECK_EQUAL(result, true); | |
626 | ||
627 | while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) { | |
628 | s_mplexer->run(&now); | |
629 | } | |
630 | BOOST_CHECK_EQUAL(sender->d_valid, true); | |
631 | } | |
632 | ||
633 | BOOST_FIXTURE_TEST_CASE(test_ConcurrentQueries, TestFixture) | |
634 | { | |
690a9c40 | 635 | auto local = getBackendAddress("1", 80); |
c114cd18 | 636 | ClientState localCS(local, true, false, 0, "", {}, true); |
f05cd66c RG |
637 | auto tlsCtx = std::make_shared<MockupTLSCtx>(); |
638 | localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx); | |
639 | ||
640 | struct timeval now; | |
641 | gettimeofday(&now, nullptr); | |
642 | ||
db5fbb86 | 643 | auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53)); |
f05cd66c | 644 | backend->d_tlsCtx = tlsCtx; |
8cf75ac2 RG |
645 | backend->d_config.d_tlsSubjectName = "backend.powerdns.com"; |
646 | backend->d_config.d_dohPath = "/dns-query"; | |
647 | backend->d_config.d_addXForwardedHeaders = true; | |
f05cd66c RG |
648 | |
649 | size_t numberOfQueries = 2; | |
650 | std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries; | |
651 | for (size_t counter = 0; counter < numberOfQueries; counter++) { | |
652 | DNSName name("powerdns.com."); | |
653 | PacketBuffer query; | |
654 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0); | |
655 | pwQ.getHeader()->rd = 1; | |
656 | pwQ.getHeader()->id = htons(counter); | |
657 | ||
658 | PacketBuffer response; | |
659 | GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0); | |
660 | pwR.getHeader()->qr = 1; | |
661 | pwR.getHeader()->rd = 1; | |
662 | pwR.getHeader()->ra = 1; | |
663 | pwR.getHeader()->id = htons(counter); | |
664 | pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); | |
665 | pwR.xfr32BitInt(0x01020304); | |
666 | pwR.commit(); | |
667 | ||
668 | s_responses[counter] = {query, response}; | |
669 | ||
670 | auto sender = std::make_shared<MockupQuerySender>(); | |
671 | sender->d_id = counter; | |
592b1d99 | 672 | InternalQuery internalQuery(std::move(query), InternalQueryState()); |
f05cd66c RG |
673 | queries.push_back({std::move(sender), std::move(internalQuery)}); |
674 | } | |
675 | ||
676 | s_steps = { | |
1226f653 | 677 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, |
f05cd66c | 678 | /* opening */ |
1226f653 | 679 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 680 | /* settings */ |
1226f653 | 681 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 682 | /* headers */ |
1226f653 | 683 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 684 | /* data */ |
4e82c08c | 685 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
686 | /* set the outgoing descriptor (backend connection) as ready */ |
687 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
688 | }}, | |
f05cd66c | 689 | /* headers */ |
1226f653 | 690 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 691 | /* data */ |
4e82c08c | 692 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
693 | /* set the outgoing descriptor (backend connection) as ready */ |
694 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
695 | }}, | |
f05cd66c | 696 | /* read settings, headers and responses from the server */ |
1226f653 | 697 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 698 | /* acknowledge settings */ |
4e82c08c | 699 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
becad613 RG |
700 | s_connectionBuffers.at(desc)->submitGoAway(); |
701 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
702 | }}, | |
703 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()}, | |
1226f653 | 704 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, |
f05cd66c RG |
705 | }; |
706 | ||
707 | for (auto& query : queries) { | |
708 | auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first); | |
709 | bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false); | |
710 | BOOST_CHECK_EQUAL(result, true); | |
711 | } | |
712 | ||
713 | while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) { | |
714 | s_mplexer->run(&now); | |
715 | } | |
716 | ||
717 | for (auto& query : queries) { | |
718 | BOOST_CHECK_EQUAL(query.first->d_valid, true); | |
719 | } | |
720 | } | |
721 | ||
722 | BOOST_FIXTURE_TEST_CASE(test_ConnectionReuse, TestFixture) | |
723 | { | |
690a9c40 | 724 | auto local = getBackendAddress("1", 80); |
c114cd18 | 725 | ClientState localCS(local, true, false, 0, "", {}, true); |
f05cd66c RG |
726 | auto tlsCtx = std::make_shared<MockupTLSCtx>(); |
727 | localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx); | |
728 | ||
729 | struct timeval now; | |
730 | gettimeofday(&now, nullptr); | |
731 | ||
db5fbb86 | 732 | auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53)); |
f05cd66c | 733 | backend->d_tlsCtx = tlsCtx; |
8cf75ac2 RG |
734 | backend->d_config.d_tlsSubjectName = "backend.powerdns.com"; |
735 | backend->d_config.d_dohPath = "/dns-query"; | |
736 | backend->d_config.d_addXForwardedHeaders = true; | |
f05cd66c RG |
737 | |
738 | size_t numberOfQueries = 2; | |
739 | std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries; | |
740 | for (size_t counter = 0; counter < numberOfQueries; counter++) { | |
741 | DNSName name("powerdns.com."); | |
742 | PacketBuffer query; | |
743 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0); | |
744 | pwQ.getHeader()->rd = 1; | |
745 | pwQ.getHeader()->id = htons(counter); | |
746 | ||
747 | PacketBuffer response; | |
748 | GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0); | |
749 | pwR.getHeader()->qr = 1; | |
750 | pwR.getHeader()->rd = 1; | |
751 | pwR.getHeader()->ra = 1; | |
752 | pwR.getHeader()->id = htons(counter); | |
753 | pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); | |
754 | pwR.xfr32BitInt(0x01020304); | |
755 | pwR.commit(); | |
756 | ||
757 | s_responses[counter] = {query, response}; | |
758 | ||
759 | auto sender = std::make_shared<MockupQuerySender>(); | |
760 | sender->d_id = counter; | |
592b1d99 | 761 | InternalQuery internalQuery(std::move(query), InternalQueryState()); |
f05cd66c RG |
762 | queries.push_back({std::move(sender), std::move(internalQuery)}); |
763 | } | |
764 | ||
becad613 | 765 | bool firstQueryDone = false; |
f05cd66c | 766 | s_steps = { |
1226f653 | 767 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, |
f05cd66c | 768 | /* opening */ |
1226f653 | 769 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 770 | /* settings */ |
1226f653 | 771 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 772 | /* headers */ |
1226f653 | 773 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 774 | /* data */ |
4e82c08c | 775 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
776 | /* set the outgoing descriptor (backend connection) as ready */ |
777 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
778 | }}, | |
f05cd66c | 779 | /* read settings, headers and responses from the server */ |
1226f653 | 780 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 781 | /* acknowledge settings */ |
4e82c08c | 782 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [&firstQueryDone](int desc) { |
ea090af9 RG |
783 | firstQueryDone = true; |
784 | }}, | |
f05cd66c | 785 | /* headers */ |
4e82c08c | 786 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
ea090af9 | 787 | }}, |
f05cd66c | 788 | /* data */ |
4e82c08c | 789 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
790 | /* set the outgoing descriptor (backend connection) as ready */ |
791 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
792 | }}, | |
f05cd66c | 793 | /* read settings, headers and responses from the server */ |
1226f653 | 794 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
becad613 | 795 | /* later the backend sends a go away frame */ |
4e82c08c | 796 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
ea090af9 | 797 | s_connectionBuffers.at(desc)->submitGoAway(); |
becad613 | 798 | }}, |
1226f653 | 799 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, |
f05cd66c RG |
800 | }; |
801 | ||
802 | { | |
803 | auto& query = queries.at(0); | |
804 | auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first); | |
805 | bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false); | |
806 | BOOST_CHECK_EQUAL(result, true); | |
807 | ||
becad613 | 808 | while (!firstQueryDone && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) { |
f05cd66c RG |
809 | s_mplexer->run(&now); |
810 | } | |
811 | ||
812 | BOOST_CHECK_EQUAL(query.first->d_valid, true); | |
becad613 | 813 | BOOST_CHECK_EQUAL(firstQueryDone, true); |
f05cd66c RG |
814 | } |
815 | ||
816 | { | |
817 | auto& query = queries.at(1); | |
818 | auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first); | |
819 | bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false); | |
820 | BOOST_CHECK_EQUAL(result, true); | |
821 | ||
822 | while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) { | |
823 | s_mplexer->run(&now); | |
824 | } | |
825 | ||
826 | BOOST_CHECK_EQUAL(query.first->d_valid, true); | |
827 | } | |
828 | } | |
829 | ||
830 | BOOST_FIXTURE_TEST_CASE(test_InvalidDNSAnswer, TestFixture) | |
831 | { | |
690a9c40 | 832 | auto local = getBackendAddress("1", 80); |
c114cd18 | 833 | ClientState localCS(local, true, false, 0, "", {}, true); |
f05cd66c RG |
834 | auto tlsCtx = std::make_shared<MockupTLSCtx>(); |
835 | localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx); | |
836 | ||
837 | struct timeval now; | |
838 | gettimeofday(&now, nullptr); | |
839 | ||
840 | size_t counter = 1; | |
841 | DNSName name("powerdns.com."); | |
842 | PacketBuffer query; | |
843 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0); | |
844 | pwQ.getHeader()->rd = 1; | |
845 | pwQ.getHeader()->id = htons(counter); | |
846 | ||
847 | PacketBuffer response; | |
848 | GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0); | |
849 | pwR.getHeader()->qr = 1; | |
850 | pwR.getHeader()->rd = 1; | |
851 | pwR.getHeader()->ra = 1; | |
852 | pwR.getHeader()->id = htons(counter); | |
853 | pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); | |
854 | pwR.xfr32BitInt(0x01020304); | |
855 | pwR.commit(); | |
856 | ||
857 | /* TRUNCATE the answer */ | |
858 | response.resize(11); | |
859 | s_responses[counter] = {query, response}; | |
860 | ||
db5fbb86 | 861 | auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53)); |
f05cd66c | 862 | backend->d_tlsCtx = tlsCtx; |
8cf75ac2 RG |
863 | backend->d_config.d_tlsSubjectName = "backend.powerdns.com"; |
864 | backend->d_config.d_dohPath = "/dns-query"; | |
865 | backend->d_config.d_addXForwardedHeaders = true; | |
f05cd66c RG |
866 | |
867 | auto sender = std::make_shared<MockupQuerySender>(); | |
868 | sender->d_id = counter; | |
869 | sender->d_customHandler = [](uint16_t id, const struct timeval&, TCPResponse&& resp) { | |
870 | BOOST_CHECK_EQUAL(resp.d_buffer.size(), 11U); | |
871 | /* simulate an exception, since DoH and UDP frontends will process the query right away, | |
872 | while TCP and DoT will first pass it back to the TCP worker thread */ | |
873 | throw std::runtime_error("Invalid response"); | |
874 | }; | |
592b1d99 | 875 | InternalQuery internalQuery(std::move(query), InternalQueryState()); |
f05cd66c RG |
876 | |
877 | s_steps = { | |
1226f653 | 878 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, |
f05cd66c | 879 | /* opening */ |
1226f653 | 880 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 881 | /* settings */ |
1226f653 | 882 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 883 | /* headers */ |
1226f653 | 884 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 885 | /* data */ |
4e82c08c | 886 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
887 | /* set the outgoing descriptor (backend connection) as ready */ |
888 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
889 | }}, | |
f05cd66c | 890 | /* read settings, headers and response from the server */ |
1226f653 | 891 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 892 | /* acknowledge settings */ |
1226f653 | 893 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
becad613 | 894 | /* try to read, the backend says to go away */ |
4e82c08c | 895 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
ea090af9 | 896 | s_connectionBuffers.at(desc)->submitGoAway(); |
becad613 | 897 | }}, |
1226f653 | 898 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, |
f05cd66c RG |
899 | }; |
900 | ||
901 | auto sliced = std::shared_ptr<TCPQuerySender>(sender); | |
902 | bool result = sendH2Query(backend, s_mplexer, sliced, std::move(internalQuery), false); | |
903 | BOOST_CHECK_EQUAL(result, true); | |
904 | ||
905 | while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) { | |
906 | s_mplexer->run(&now); | |
907 | } | |
908 | BOOST_CHECK_EQUAL(sender->d_valid, false); | |
909 | } | |
910 | ||
911 | BOOST_FIXTURE_TEST_CASE(test_TimeoutWhileWriting, TestFixture) | |
912 | { | |
690a9c40 | 913 | auto local = getBackendAddress("1", 80); |
c114cd18 | 914 | ClientState localCS(local, true, false, 0, "", {}, true); |
f05cd66c RG |
915 | auto tlsCtx = std::make_shared<MockupTLSCtx>(); |
916 | localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx); | |
917 | ||
918 | struct timeval now; | |
919 | gettimeofday(&now, nullptr); | |
920 | ||
db5fbb86 | 921 | auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53)); |
f05cd66c | 922 | backend->d_tlsCtx = tlsCtx; |
8cf75ac2 RG |
923 | backend->d_config.d_tlsSubjectName = "backend.powerdns.com"; |
924 | backend->d_config.d_dohPath = "/dns-query"; | |
925 | backend->d_config.d_addXForwardedHeaders = true; | |
f05cd66c RG |
926 | |
927 | size_t numberOfQueries = 2; | |
928 | std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries; | |
929 | for (size_t counter = 0; counter < numberOfQueries; counter++) { | |
930 | DNSName name("powerdns.com."); | |
931 | PacketBuffer query; | |
932 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0); | |
933 | pwQ.getHeader()->rd = 1; | |
934 | pwQ.getHeader()->id = htons(counter); | |
935 | ||
936 | PacketBuffer response; | |
937 | GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0); | |
938 | pwR.getHeader()->qr = 1; | |
939 | pwR.getHeader()->rd = 1; | |
940 | pwR.getHeader()->ra = 1; | |
941 | pwR.getHeader()->id = htons(counter); | |
942 | pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); | |
943 | pwR.xfr32BitInt(0x01020304); | |
944 | pwR.commit(); | |
945 | ||
946 | s_responses[counter] = {query, response}; | |
947 | ||
948 | auto sender = std::make_shared<MockupQuerySender>(); | |
949 | sender->d_id = counter; | |
592b1d99 | 950 | InternalQuery internalQuery(std::move(query), InternalQueryState()); |
f05cd66c RG |
951 | queries.push_back({std::move(sender), std::move(internalQuery)}); |
952 | } | |
953 | ||
954 | bool timeout = false; | |
955 | s_steps = { | |
1226f653 | 956 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, |
f05cd66c | 957 | /* opening */ |
1226f653 | 958 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 959 | /* settings */ |
1226f653 | 960 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 961 | /* headers */ |
1226f653 | 962 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 963 | /* data */ |
1226f653 | 964 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 965 | /* headers */ |
1226f653 | 966 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 967 | /* data */ |
4e82c08c | 968 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::NeedWrite, std::numeric_limits<size_t>::max(), [&timeout](int desc) { |
1226f653 RG |
969 | timeout = true; |
970 | }}, | |
971 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, | |
f05cd66c RG |
972 | }; |
973 | ||
974 | for (auto& query : queries) { | |
975 | auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first); | |
976 | bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false); | |
977 | BOOST_CHECK_EQUAL(result, true); | |
978 | } | |
979 | ||
980 | while (!timeout && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) { | |
981 | s_mplexer->run(&now); | |
982 | } | |
983 | ||
984 | struct timeval later = now; | |
8cf75ac2 | 985 | later.tv_sec += backend->d_config.tcpSendTimeout + 1; |
f05cd66c RG |
986 | |
987 | auto expiredConns = handleH2Timeouts(*s_mplexer, later); | |
988 | BOOST_CHECK_EQUAL(expiredConns, 1U); | |
989 | ||
990 | for (auto& query : queries) { | |
991 | BOOST_CHECK_EQUAL(query.first->d_valid, false); | |
992 | BOOST_CHECK_EQUAL(query.first->d_error, true); | |
993 | } | |
994 | ||
995 | BOOST_CHECK_EQUAL(clearH2Connections(), 0U); | |
996 | } | |
997 | ||
998 | BOOST_FIXTURE_TEST_CASE(test_TimeoutWhileReading, TestFixture) | |
999 | { | |
690a9c40 | 1000 | auto local = getBackendAddress("1", 80); |
c114cd18 | 1001 | ClientState localCS(local, true, false, 0, "", {}, true); |
f05cd66c RG |
1002 | auto tlsCtx = std::make_shared<MockupTLSCtx>(); |
1003 | localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx); | |
1004 | ||
1005 | struct timeval now; | |
1006 | gettimeofday(&now, nullptr); | |
1007 | ||
db5fbb86 | 1008 | auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53)); |
f05cd66c | 1009 | backend->d_tlsCtx = tlsCtx; |
8cf75ac2 RG |
1010 | backend->d_config.d_tlsSubjectName = "backend.powerdns.com"; |
1011 | backend->d_config.d_dohPath = "/dns-query"; | |
1012 | backend->d_config.d_addXForwardedHeaders = true; | |
f05cd66c RG |
1013 | |
1014 | size_t numberOfQueries = 2; | |
1015 | std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries; | |
1016 | for (size_t counter = 0; counter < numberOfQueries; counter++) { | |
1017 | DNSName name("powerdns.com."); | |
1018 | PacketBuffer query; | |
1019 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0); | |
1020 | pwQ.getHeader()->rd = 1; | |
1021 | pwQ.getHeader()->id = htons(counter); | |
1022 | ||
1023 | PacketBuffer response; | |
1024 | GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0); | |
1025 | pwR.getHeader()->qr = 1; | |
1026 | pwR.getHeader()->rd = 1; | |
1027 | pwR.getHeader()->ra = 1; | |
1028 | pwR.getHeader()->id = htons(counter); | |
1029 | pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); | |
1030 | pwR.xfr32BitInt(0x01020304); | |
1031 | pwR.commit(); | |
1032 | ||
1033 | s_responses[counter] = {query, response}; | |
1034 | ||
1035 | auto sender = std::make_shared<MockupQuerySender>(); | |
1036 | sender->d_id = counter; | |
592b1d99 | 1037 | InternalQuery internalQuery(std::move(query), InternalQueryState()); |
f05cd66c RG |
1038 | queries.push_back({std::move(sender), std::move(internalQuery)}); |
1039 | } | |
1040 | ||
1041 | bool timeout = false; | |
1042 | s_steps = { | |
1226f653 | 1043 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, |
f05cd66c | 1044 | /* opening */ |
1226f653 | 1045 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1046 | /* settings */ |
1226f653 | 1047 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1048 | /* headers */ |
1226f653 | 1049 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1050 | /* data */ |
1226f653 | 1051 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1052 | /* headers */ |
1226f653 | 1053 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1054 | /* data */ |
4e82c08c | 1055 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [&timeout](int desc) { |
1226f653 RG |
1056 | /* set the timeout flag now, since the timeout occurs while waiting for the descriptor to become readable */ |
1057 | timeout = true; | |
1058 | }}, | |
1059 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, | |
1060 | }; | |
f05cd66c RG |
1061 | |
1062 | for (auto& query : queries) { | |
1063 | auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first); | |
1064 | bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false); | |
1065 | BOOST_CHECK_EQUAL(result, true); | |
1066 | } | |
1067 | ||
1068 | while (!timeout && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) { | |
1069 | s_mplexer->run(&now); | |
1070 | } | |
1071 | ||
1072 | struct timeval later = now; | |
8cf75ac2 | 1073 | later.tv_sec += backend->d_config.tcpRecvTimeout + 1; |
f05cd66c RG |
1074 | |
1075 | auto expiredConns = handleH2Timeouts(*s_mplexer, later); | |
1076 | BOOST_CHECK_EQUAL(expiredConns, 1U); | |
1077 | ||
1078 | for (auto& query : queries) { | |
1079 | BOOST_CHECK_EQUAL(query.first->d_valid, false); | |
1080 | BOOST_CHECK_EQUAL(query.first->d_error, true); | |
1081 | } | |
1082 | BOOST_CHECK_EQUAL(clearH2Connections(), 0U); | |
1083 | } | |
1084 | ||
1085 | BOOST_FIXTURE_TEST_CASE(test_ShortWrite, TestFixture) | |
1086 | { | |
690a9c40 | 1087 | auto local = getBackendAddress("1", 80); |
c114cd18 | 1088 | ClientState localCS(local, true, false, 0, "", {}, true); |
f05cd66c RG |
1089 | auto tlsCtx = std::make_shared<MockupTLSCtx>(); |
1090 | localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx); | |
1091 | ||
1092 | struct timeval now; | |
1093 | gettimeofday(&now, nullptr); | |
1094 | ||
db5fbb86 | 1095 | auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53)); |
f05cd66c | 1096 | backend->d_tlsCtx = tlsCtx; |
8cf75ac2 RG |
1097 | backend->d_config.d_tlsSubjectName = "backend.powerdns.com"; |
1098 | backend->d_config.d_dohPath = "/dns-query"; | |
1099 | backend->d_config.d_addXForwardedHeaders = true; | |
f05cd66c RG |
1100 | |
1101 | size_t numberOfQueries = 2; | |
1102 | std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries; | |
1103 | for (size_t counter = 0; counter < numberOfQueries; counter++) { | |
1104 | DNSName name("powerdns.com."); | |
1105 | PacketBuffer query; | |
1106 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0); | |
1107 | pwQ.getHeader()->rd = 1; | |
1108 | pwQ.getHeader()->id = htons(counter); | |
1109 | ||
1110 | PacketBuffer response; | |
1111 | GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0); | |
1112 | pwR.getHeader()->qr = 1; | |
1113 | pwR.getHeader()->rd = 1; | |
1114 | pwR.getHeader()->ra = 1; | |
1115 | pwR.getHeader()->id = htons(counter); | |
1116 | pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); | |
1117 | pwR.xfr32BitInt(0x01020304); | |
1118 | pwR.commit(); | |
1119 | ||
1120 | s_responses[counter] = {query, response}; | |
1121 | ||
1122 | auto sender = std::make_shared<MockupQuerySender>(); | |
1123 | sender->d_id = counter; | |
592b1d99 | 1124 | InternalQuery internalQuery(std::move(query), InternalQueryState()); |
f05cd66c RG |
1125 | queries.push_back({std::move(sender), std::move(internalQuery)}); |
1126 | } | |
1127 | ||
becad613 | 1128 | bool done = false; |
f05cd66c | 1129 | s_steps = { |
1226f653 | 1130 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, |
f05cd66c | 1131 | /* opening */ |
1226f653 | 1132 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1133 | /* settings */ |
4e82c08c | 1134 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::NeedWrite, 2, [](int desc) { |
1226f653 RG |
1135 | /* set the outgoing descriptor (backend connection) as ready */ |
1136 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
1137 | }}, | |
f05cd66c | 1138 | /* settings (second attempt) + headers + data + headers (second query) + data */ |
1226f653 RG |
1139 | { |
1140 | ExpectedStep::ExpectedRequest::writeToBackend, | |
1141 | IOState::Done, | |
1142 | std::numeric_limits<size_t>::max(), | |
1143 | }, | |
f05cd66c | 1144 | /* read settings, headers and responses from the server */ |
1226f653 | 1145 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1146 | /* acknowledge settings */ |
4e82c08c | 1147 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [&done](int desc) { |
ea090af9 RG |
1148 | /* mark backend as not ready */ |
1149 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setNotReady(desc); | |
1150 | done = true; | |
1151 | }}, | |
1226f653 | 1152 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, |
f05cd66c RG |
1153 | }; |
1154 | ||
1155 | for (auto& query : queries) { | |
1156 | auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first); | |
1157 | bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false); | |
1158 | BOOST_CHECK_EQUAL(result, true); | |
1159 | } | |
1160 | ||
becad613 | 1161 | while (!done && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) { |
f05cd66c RG |
1162 | s_mplexer->run(&now); |
1163 | } | |
1164 | ||
1165 | for (auto& query : queries) { | |
1166 | BOOST_CHECK_EQUAL(query.first->d_valid, true); | |
1167 | } | |
1168 | ||
1169 | BOOST_CHECK_EQUAL(clearH2Connections(), 1U); | |
1170 | } | |
1171 | ||
1172 | BOOST_FIXTURE_TEST_CASE(test_ShortRead, TestFixture) | |
1173 | { | |
690a9c40 | 1174 | auto local = getBackendAddress("1", 80); |
c114cd18 | 1175 | ClientState localCS(local, true, false, 0, "", {}, true); |
f05cd66c RG |
1176 | auto tlsCtx = std::make_shared<MockupTLSCtx>(); |
1177 | localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx); | |
1178 | ||
1179 | struct timeval now; | |
1180 | gettimeofday(&now, nullptr); | |
1181 | ||
db5fbb86 | 1182 | auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53)); |
f05cd66c | 1183 | backend->d_tlsCtx = tlsCtx; |
8cf75ac2 RG |
1184 | backend->d_config.d_tlsSubjectName = "backend.powerdns.com"; |
1185 | backend->d_config.d_dohPath = "/dns-query"; | |
1186 | backend->d_config.d_addXForwardedHeaders = true; | |
f05cd66c RG |
1187 | |
1188 | size_t numberOfQueries = 2; | |
1189 | std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries; | |
1190 | for (size_t counter = 0; counter < numberOfQueries; counter++) { | |
1191 | DNSName name("powerdns.com."); | |
1192 | PacketBuffer query; | |
1193 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0); | |
1194 | pwQ.getHeader()->rd = 1; | |
1195 | pwQ.getHeader()->id = htons(counter); | |
1196 | ||
1197 | PacketBuffer response; | |
1198 | GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0); | |
1199 | pwR.getHeader()->qr = 1; | |
1200 | pwR.getHeader()->rd = 1; | |
1201 | pwR.getHeader()->ra = 1; | |
1202 | pwR.getHeader()->id = htons(counter); | |
1203 | pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); | |
1204 | pwR.xfr32BitInt(0x01020304); | |
1205 | pwR.commit(); | |
1206 | ||
1207 | s_responses[counter] = {query, response}; | |
1208 | ||
1209 | auto sender = std::make_shared<MockupQuerySender>(); | |
1210 | sender->d_id = counter; | |
592b1d99 | 1211 | InternalQuery internalQuery(std::move(query), InternalQueryState()); |
f05cd66c RG |
1212 | queries.push_back({std::move(sender), std::move(internalQuery)}); |
1213 | } | |
1214 | ||
becad613 | 1215 | bool done = false; |
f05cd66c | 1216 | s_steps = { |
1226f653 | 1217 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, |
f05cd66c | 1218 | /* opening */ |
1226f653 | 1219 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1220 | /* settings */ |
1226f653 | 1221 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1222 | /* headers */ |
1226f653 | 1223 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1224 | /* data */ |
4e82c08c | 1225 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
1226 | /* set the outgoing descriptor (backend connection) as ready */ |
1227 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
1228 | }}, | |
f05cd66c | 1229 | /* headers */ |
1226f653 | 1230 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1231 | /* data */ |
4e82c08c | 1232 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
1233 | /* set the outgoing descriptor (backend connection) as ready */ |
1234 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
1235 | }}, | |
f05cd66c | 1236 | /* read settings, headers and responses from the server */ |
1226f653 | 1237 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::NeedRead, 4}, |
f05cd66c | 1238 | /* read settings, headers and responses (second attempt) */ |
1226f653 | 1239 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1240 | /* acknowledge settings */ |
4e82c08c | 1241 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [&done](int desc) { |
ea090af9 RG |
1242 | /* mark backend as not ready */ |
1243 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setNotReady(desc); | |
1244 | done = true; | |
1245 | }}, | |
1226f653 | 1246 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, |
f05cd66c RG |
1247 | }; |
1248 | ||
1249 | for (auto& query : queries) { | |
1250 | auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first); | |
1251 | bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false); | |
1252 | BOOST_CHECK_EQUAL(result, true); | |
1253 | } | |
1254 | ||
becad613 | 1255 | while (!done && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) { |
f05cd66c RG |
1256 | s_mplexer->run(&now); |
1257 | } | |
1258 | ||
1259 | for (auto& query : queries) { | |
1260 | BOOST_CHECK_EQUAL(query.first->d_valid, true); | |
1261 | } | |
1262 | ||
1263 | BOOST_CHECK_EQUAL(clearH2Connections(), 1U); | |
1264 | } | |
1265 | ||
1266 | BOOST_FIXTURE_TEST_CASE(test_ConnectionClosedWhileReading, TestFixture) | |
1267 | { | |
690a9c40 | 1268 | auto local = getBackendAddress("1", 80); |
c114cd18 | 1269 | ClientState localCS(local, true, false, 0, "", {}, true); |
f05cd66c RG |
1270 | auto tlsCtx = std::make_shared<MockupTLSCtx>(); |
1271 | localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx); | |
1272 | ||
1273 | struct timeval now; | |
1274 | gettimeofday(&now, nullptr); | |
1275 | ||
db5fbb86 | 1276 | auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53)); |
f05cd66c | 1277 | backend->d_tlsCtx = tlsCtx; |
8cf75ac2 RG |
1278 | backend->d_config.d_tlsSubjectName = "backend.powerdns.com"; |
1279 | backend->d_config.d_dohPath = "/dns-query"; | |
1280 | backend->d_config.d_addXForwardedHeaders = true; | |
f05cd66c RG |
1281 | |
1282 | size_t numberOfQueries = 2; | |
1283 | std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries; | |
1284 | for (size_t counter = 0; counter < numberOfQueries; counter++) { | |
1285 | DNSName name("powerdns.com."); | |
1286 | PacketBuffer query; | |
1287 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0); | |
1288 | pwQ.getHeader()->rd = 1; | |
1289 | pwQ.getHeader()->id = htons(counter); | |
1290 | ||
1291 | PacketBuffer response; | |
1292 | GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0); | |
1293 | pwR.getHeader()->qr = 1; | |
1294 | pwR.getHeader()->rd = 1; | |
1295 | pwR.getHeader()->ra = 1; | |
1296 | pwR.getHeader()->id = htons(counter); | |
1297 | pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); | |
1298 | pwR.xfr32BitInt(0x01020304); | |
1299 | pwR.commit(); | |
1300 | ||
1301 | s_responses[counter] = {query, response}; | |
1302 | ||
1303 | auto sender = std::make_shared<MockupQuerySender>(); | |
1304 | sender->d_id = counter; | |
592b1d99 | 1305 | InternalQuery internalQuery(std::move(query), InternalQueryState()); |
f05cd66c RG |
1306 | queries.push_back({std::move(sender), std::move(internalQuery)}); |
1307 | } | |
1308 | ||
1309 | s_steps = { | |
1226f653 | 1310 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, |
f05cd66c | 1311 | /* opening */ |
1226f653 | 1312 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1313 | /* settings */ |
1226f653 | 1314 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1315 | /* headers */ |
1226f653 | 1316 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1317 | /* data */ |
4e82c08c | 1318 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
1319 | /* set the outgoing descriptor (backend connection) as ready */ |
1320 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
1321 | }}, | |
f05cd66c | 1322 | /* headers */ |
1226f653 | 1323 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1324 | /* data */ |
4e82c08c | 1325 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
1326 | /* set the outgoing descriptor (backend connection) as ready */ |
1327 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
1328 | }}, | |
f05cd66c | 1329 | /* read settings, headers and responses from the server */ |
1226f653 RG |
1330 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, 0}, |
1331 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, | |
f05cd66c RG |
1332 | }; |
1333 | ||
1334 | for (auto& query : queries) { | |
1335 | auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first); | |
1336 | bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false); | |
1337 | BOOST_CHECK_EQUAL(result, true); | |
1338 | } | |
1339 | ||
1340 | while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) { | |
1341 | s_mplexer->run(&now); | |
1342 | } | |
1343 | ||
1344 | for (auto& query : queries) { | |
1345 | BOOST_CHECK_EQUAL(query.first->d_valid, false); | |
1346 | BOOST_CHECK_EQUAL(query.first->d_error, true); | |
1347 | } | |
1348 | ||
1349 | BOOST_CHECK_EQUAL(clearH2Connections(), 0U); | |
1350 | } | |
1351 | ||
1352 | BOOST_FIXTURE_TEST_CASE(test_ConnectionClosedWhileWriting, TestFixture) | |
1353 | { | |
690a9c40 | 1354 | auto local = getBackendAddress("1", 80); |
c114cd18 | 1355 | ClientState localCS(local, true, false, 0, "", {}, true); |
f05cd66c RG |
1356 | auto tlsCtx = std::make_shared<MockupTLSCtx>(); |
1357 | localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx); | |
1358 | ||
1359 | struct timeval now; | |
1360 | gettimeofday(&now, nullptr); | |
1361 | ||
db5fbb86 | 1362 | auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53)); |
f05cd66c | 1363 | backend->d_tlsCtx = tlsCtx; |
8cf75ac2 RG |
1364 | backend->d_config.d_tlsSubjectName = "backend.powerdns.com"; |
1365 | backend->d_config.d_dohPath = "/dns-query"; | |
1366 | backend->d_config.d_addXForwardedHeaders = true; | |
f05cd66c RG |
1367 | |
1368 | size_t numberOfQueries = 2; | |
1369 | std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries; | |
1370 | for (size_t counter = 0; counter < numberOfQueries; counter++) { | |
1371 | DNSName name("powerdns.com."); | |
1372 | PacketBuffer query; | |
1373 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0); | |
1374 | pwQ.getHeader()->rd = 1; | |
1375 | pwQ.getHeader()->id = htons(counter); | |
1376 | ||
1377 | PacketBuffer response; | |
1378 | GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0); | |
1379 | pwR.getHeader()->qr = 1; | |
1380 | pwR.getHeader()->rd = 1; | |
1381 | pwR.getHeader()->ra = 1; | |
1382 | pwR.getHeader()->id = htons(counter); | |
1383 | pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); | |
1384 | pwR.xfr32BitInt(0x01020304); | |
1385 | pwR.commit(); | |
1386 | ||
1387 | s_responses[counter] = {query, response}; | |
1388 | ||
1389 | auto sender = std::make_shared<MockupQuerySender>(); | |
1390 | sender->d_id = counter; | |
592b1d99 | 1391 | InternalQuery internalQuery(std::move(query), InternalQueryState()); |
f05cd66c RG |
1392 | queries.push_back({std::move(sender), std::move(internalQuery)}); |
1393 | } | |
1394 | ||
becad613 | 1395 | bool done = false; |
f05cd66c | 1396 | s_steps = { |
1226f653 | 1397 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, |
f05cd66c | 1398 | /* opening */ |
1226f653 | 1399 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1400 | /* settings */ |
1226f653 | 1401 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1402 | /* headers, connection is closed by the backend */ |
1226f653 RG |
1403 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, 0}, |
1404 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, | |
1405 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, | |
f05cd66c | 1406 | /* opening */ |
1226f653 | 1407 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1408 | /* settings */ |
1226f653 | 1409 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1410 | /* headers */ |
1226f653 | 1411 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1412 | /* data */ |
4e82c08c | 1413 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
1414 | /* set the outgoing descriptor (backend connection) as ready */ |
1415 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
1416 | }}, | |
f05cd66c | 1417 | /* read settings, headers and response from the server */ |
1226f653 | 1418 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1419 | /* acknowledge settings */ |
4e82c08c | 1420 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [&done](int desc) { |
ea090af9 RG |
1421 | /* mark backend as not ready */ |
1422 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setNotReady(desc); | |
1423 | done = true; | |
1424 | }}, | |
1226f653 | 1425 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, |
f05cd66c RG |
1426 | }; |
1427 | ||
1428 | for (auto& query : queries) { | |
1429 | auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first); | |
1430 | bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false); | |
1431 | BOOST_CHECK_EQUAL(result, true); | |
1432 | } | |
1433 | ||
becad613 | 1434 | while (!done && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) { |
f05cd66c RG |
1435 | s_mplexer->run(&now); |
1436 | } | |
1437 | ||
1438 | BOOST_CHECK_EQUAL(queries.at(0).first->d_valid, false); | |
1439 | BOOST_CHECK_EQUAL(queries.at(0).first->d_error, true); | |
1440 | BOOST_CHECK_EQUAL(queries.at(1).first->d_valid, true); | |
1441 | BOOST_CHECK_EQUAL(queries.at(1).first->d_error, false); | |
1442 | ||
1443 | BOOST_CHECK_EQUAL(clearH2Connections(), 1U); | |
1444 | } | |
1445 | ||
1446 | BOOST_FIXTURE_TEST_CASE(test_GoAwayFromServer, TestFixture) | |
1447 | { | |
690a9c40 | 1448 | auto local = getBackendAddress("1", 80); |
c114cd18 | 1449 | ClientState localCS(local, true, false, 0, "", {}, true); |
f05cd66c RG |
1450 | auto tlsCtx = std::make_shared<MockupTLSCtx>(); |
1451 | localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx); | |
1452 | ||
1453 | struct timeval now; | |
1454 | gettimeofday(&now, nullptr); | |
1455 | ||
db5fbb86 | 1456 | auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53)); |
f05cd66c | 1457 | backend->d_tlsCtx = tlsCtx; |
8cf75ac2 RG |
1458 | backend->d_config.d_tlsSubjectName = "backend.powerdns.com"; |
1459 | backend->d_config.d_dohPath = "/dns-query"; | |
1460 | backend->d_config.d_addXForwardedHeaders = true; | |
f05cd66c | 1461 | /* set the number of reconnection attempts to a low value to not waste time */ |
8cf75ac2 | 1462 | backend->d_config.d_retries = 1; |
f05cd66c RG |
1463 | |
1464 | size_t numberOfQueries = 2; | |
1465 | std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries; | |
1466 | for (size_t counter = 0; counter < numberOfQueries; counter++) { | |
1467 | DNSName name("goaway.powerdns.com."); | |
1468 | PacketBuffer query; | |
1469 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0); | |
1470 | pwQ.getHeader()->rd = 1; | |
1471 | pwQ.getHeader()->id = htons(counter); | |
1472 | ||
1473 | PacketBuffer response; | |
1474 | GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0); | |
1475 | pwR.getHeader()->qr = 1; | |
1476 | pwR.getHeader()->rd = 1; | |
1477 | pwR.getHeader()->ra = 1; | |
1478 | pwR.getHeader()->id = htons(counter); | |
1479 | pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); | |
1480 | pwR.xfr32BitInt(0x01020304); | |
1481 | pwR.commit(); | |
1482 | ||
1483 | s_responses[counter] = {query, response}; | |
1484 | ||
1485 | auto sender = std::make_shared<MockupQuerySender>(); | |
1486 | sender->d_id = counter; | |
592b1d99 | 1487 | InternalQuery internalQuery(std::move(query), InternalQueryState()); |
f05cd66c RG |
1488 | queries.push_back({std::move(sender), std::move(internalQuery)}); |
1489 | } | |
1490 | ||
1491 | s_steps = { | |
1226f653 | 1492 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, |
f05cd66c | 1493 | /* opening */ |
1226f653 | 1494 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1495 | /* settings */ |
1226f653 | 1496 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1497 | /* headers */ |
1226f653 | 1498 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1499 | /* data */ |
4e82c08c | 1500 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
1501 | /* set the outgoing descriptor (backend connection) as ready */ |
1502 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
1503 | }}, | |
f05cd66c | 1504 | /* headers */ |
1226f653 | 1505 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1506 | /* data */ |
4e82c08c | 1507 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
1508 | /* set the outgoing descriptor (backend connection) as ready */ |
1509 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
1510 | }}, | |
f05cd66c | 1511 | /* read GO AWAY from the server (1) */ |
1226f653 RG |
1512 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
1513 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, | |
f05cd66c | 1514 | /* opening */ |
1226f653 | 1515 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1516 | /* settings */ |
1226f653 | 1517 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1518 | /* headers */ |
1226f653 | 1519 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1520 | /* data */ |
4e82c08c | 1521 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
1522 | /* set the outgoing descriptor (backend connection) as ready */ |
1523 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
1524 | }}, | |
f05cd66c | 1525 | /* headers */ |
1226f653 | 1526 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1527 | /* data */ |
4e82c08c | 1528 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
1529 | /* set the outgoing descriptor (backend connection) as ready */ |
1530 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
1531 | }}, | |
f05cd66c | 1532 | /* close the first connection. It happens now because the new connection was set up first, then that one destroyed */ |
1226f653 | 1533 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, |
f05cd66c | 1534 | /* read GO AWAY from the server (1) */ |
1226f653 RG |
1535 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
1536 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, | |
f05cd66c RG |
1537 | }; |
1538 | ||
1539 | for (auto& query : queries) { | |
1540 | auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first); | |
1541 | bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false); | |
1542 | BOOST_CHECK_EQUAL(result, true); | |
1543 | } | |
1544 | ||
1545 | while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) { | |
1546 | s_mplexer->run(&now); | |
1547 | } | |
1548 | ||
1549 | for (auto& query : queries) { | |
1550 | BOOST_CHECK_EQUAL(query.first->d_valid, false); | |
1551 | BOOST_CHECK_EQUAL(query.first->d_error, true); | |
1552 | } | |
1553 | ||
1554 | BOOST_CHECK_EQUAL(clearH2Connections(), 0U); | |
1555 | } | |
1556 | ||
1557 | BOOST_FIXTURE_TEST_CASE(test_HTTP500FromServer, TestFixture) | |
1558 | { | |
690a9c40 | 1559 | auto local = getBackendAddress("1", 80); |
c114cd18 | 1560 | ClientState localCS(local, true, false, 0, "", {}, true); |
f05cd66c RG |
1561 | auto tlsCtx = std::make_shared<MockupTLSCtx>(); |
1562 | localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx); | |
1563 | ||
1564 | struct timeval now; | |
1565 | gettimeofday(&now, nullptr); | |
1566 | ||
db5fbb86 | 1567 | auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53)); |
f05cd66c | 1568 | backend->d_tlsCtx = tlsCtx; |
8cf75ac2 RG |
1569 | backend->d_config.d_tlsSubjectName = "backend.powerdns.com"; |
1570 | backend->d_config.d_dohPath = "/dns-query"; | |
1571 | backend->d_config.d_addXForwardedHeaders = true; | |
f05cd66c RG |
1572 | |
1573 | size_t numberOfQueries = 2; | |
1574 | std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries; | |
1575 | for (size_t counter = 0; counter < numberOfQueries; counter++) { | |
1576 | DNSName name("500.powerdns.com."); | |
1577 | PacketBuffer query; | |
1578 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0); | |
1579 | pwQ.getHeader()->rd = 1; | |
1580 | pwQ.getHeader()->id = htons(counter); | |
1581 | ||
1582 | PacketBuffer response; | |
1583 | GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0); | |
1584 | pwR.getHeader()->qr = 1; | |
1585 | pwR.getHeader()->rd = 1; | |
1586 | pwR.getHeader()->ra = 1; | |
1587 | pwR.getHeader()->id = htons(counter); | |
1588 | pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); | |
1589 | pwR.xfr32BitInt(0x01020304); | |
1590 | pwR.commit(); | |
1591 | ||
1592 | s_responses[counter] = {query, response}; | |
1593 | ||
1594 | auto sender = std::make_shared<MockupQuerySender>(); | |
1595 | sender->d_id = counter; | |
592b1d99 | 1596 | InternalQuery internalQuery(std::move(query), InternalQueryState()); |
f05cd66c RG |
1597 | queries.push_back({std::move(sender), std::move(internalQuery)}); |
1598 | } | |
1599 | ||
becad613 | 1600 | bool done = false; |
f05cd66c | 1601 | s_steps = { |
1226f653 | 1602 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, |
f05cd66c | 1603 | /* opening */ |
1226f653 | 1604 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1605 | /* settings */ |
1226f653 | 1606 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1607 | /* headers */ |
1226f653 | 1608 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1609 | /* data */ |
4e82c08c | 1610 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
1611 | /* set the outgoing descriptor (backend connection) as ready */ |
1612 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
1613 | }}, | |
f05cd66c | 1614 | /* headers */ |
1226f653 | 1615 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1616 | /* data */ |
4e82c08c | 1617 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
1618 | /* set the outgoing descriptor (backend connection) as ready */ |
1619 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
1620 | }}, | |
f05cd66c | 1621 | /* read settings, headers and responses from the server */ |
1226f653 | 1622 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1623 | /* acknowledge settings */ |
4e82c08c | 1624 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [&done](int desc) { |
ea090af9 RG |
1625 | /* mark backend as not ready */ |
1626 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setNotReady(desc); | |
1627 | done = true; | |
1628 | }}, | |
1226f653 | 1629 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, |
f05cd66c RG |
1630 | }; |
1631 | ||
1632 | for (auto& query : queries) { | |
1633 | auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first); | |
1634 | bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false); | |
1635 | BOOST_CHECK_EQUAL(result, true); | |
1636 | } | |
1637 | ||
becad613 | 1638 | while (!done && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) { |
f05cd66c RG |
1639 | s_mplexer->run(&now); |
1640 | } | |
1641 | ||
1642 | BOOST_CHECK_EQUAL(queries.at(0).first->d_valid, false); | |
1643 | BOOST_CHECK_EQUAL(queries.at(0).first->d_error, true); | |
1644 | BOOST_CHECK_EQUAL(queries.at(1).first->d_valid, true); | |
1645 | BOOST_CHECK_EQUAL(queries.at(1).first->d_error, false); | |
1646 | ||
1647 | BOOST_CHECK_EQUAL(clearH2Connections(), 1U); | |
1648 | } | |
1649 | ||
1650 | BOOST_FIXTURE_TEST_CASE(test_WrongStreamID, TestFixture) | |
1651 | { | |
690a9c40 | 1652 | auto local = getBackendAddress("1", 80); |
c114cd18 | 1653 | ClientState localCS(local, true, false, 0, "", {}, true); |
f05cd66c RG |
1654 | auto tlsCtx = std::make_shared<MockupTLSCtx>(); |
1655 | localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx); | |
1656 | ||
1657 | struct timeval now; | |
1658 | gettimeofday(&now, nullptr); | |
1659 | ||
db5fbb86 | 1660 | auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53)); |
f05cd66c | 1661 | backend->d_tlsCtx = tlsCtx; |
8cf75ac2 RG |
1662 | backend->d_config.d_tlsSubjectName = "backend.powerdns.com"; |
1663 | backend->d_config.d_dohPath = "/dns-query"; | |
1664 | backend->d_config.d_addXForwardedHeaders = true; | |
f05cd66c RG |
1665 | |
1666 | size_t numberOfQueries = 2; | |
1667 | std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries; | |
1668 | for (size_t counter = 0; counter < numberOfQueries; counter++) { | |
1669 | DNSName name("wrong-stream-id.powerdns.com."); | |
1670 | PacketBuffer query; | |
1671 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0); | |
1672 | pwQ.getHeader()->rd = 1; | |
1673 | pwQ.getHeader()->id = htons(counter); | |
1674 | ||
1675 | PacketBuffer response; | |
1676 | GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0); | |
1677 | pwR.getHeader()->qr = 1; | |
1678 | pwR.getHeader()->rd = 1; | |
1679 | pwR.getHeader()->ra = 1; | |
1680 | pwR.getHeader()->id = htons(counter); | |
1681 | pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); | |
1682 | pwR.xfr32BitInt(0x01020304); | |
1683 | pwR.commit(); | |
1684 | ||
1685 | s_responses[counter] = {query, response}; | |
1686 | ||
1687 | auto sender = std::make_shared<MockupQuerySender>(); | |
1688 | sender->d_id = counter; | |
592b1d99 | 1689 | InternalQuery internalQuery(std::move(query), InternalQueryState()); |
f05cd66c RG |
1690 | queries.push_back({std::move(sender), std::move(internalQuery)}); |
1691 | } | |
1692 | ||
1693 | bool timeout = false; | |
1694 | s_steps = { | |
1226f653 | 1695 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, |
f05cd66c | 1696 | /* opening */ |
1226f653 | 1697 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1698 | /* settings */ |
1226f653 | 1699 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1700 | /* headers */ |
1226f653 | 1701 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1702 | /* data */ |
4e82c08c | 1703 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
1704 | /* set the outgoing descriptor (backend connection) as ready */ |
1705 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
1706 | }}, | |
f05cd66c | 1707 | /* headers */ |
1226f653 | 1708 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1709 | /* data */ |
4e82c08c | 1710 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
1226f653 RG |
1711 | /* set the outgoing descriptor (backend connection) as ready */ |
1712 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
1713 | }}, | |
f05cd66c | 1714 | /* read settings, headers and responses from the server */ |
1226f653 | 1715 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1716 | /* acknowledge settings */ |
1226f653 | 1717 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, |
f05cd66c | 1718 | /* read ends up as a time out since nghttp2 filters the frame with the wrong stream ID */ |
4e82c08c | 1719 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::NeedRead, 0, [&timeout](int desc) { |
1226f653 RG |
1720 | /* set the timeout flag now, since the timeout occurs while waiting for the descriptor to become readable */ |
1721 | timeout = true; | |
1722 | }}, | |
1723 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, | |
f05cd66c RG |
1724 | }; |
1725 | ||
1726 | for (auto& query : queries) { | |
1727 | auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first); | |
1728 | bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false); | |
1729 | BOOST_CHECK_EQUAL(result, true); | |
1730 | } | |
1731 | ||
1732 | while (!timeout && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) { | |
1733 | s_mplexer->run(&now); | |
1734 | } | |
1735 | ||
1736 | struct timeval later = now; | |
8cf75ac2 | 1737 | later.tv_sec += backend->d_config.tcpRecvTimeout + 1; |
f05cd66c RG |
1738 | |
1739 | auto expiredConns = handleH2Timeouts(*s_mplexer, later); | |
1740 | BOOST_CHECK_EQUAL(expiredConns, 1U); | |
1741 | ||
1742 | BOOST_CHECK_EQUAL(queries.at(0).first->d_valid, false); | |
1743 | BOOST_CHECK_EQUAL(queries.at(0).first->d_error, true); | |
1744 | BOOST_CHECK_EQUAL(queries.at(1).first->d_valid, false); | |
1745 | BOOST_CHECK_EQUAL(queries.at(1).first->d_error, true); | |
1746 | ||
1747 | BOOST_CHECK_EQUAL(clearH2Connections(), 0U); | |
1748 | } | |
1749 | ||
0e6892c6 RG |
1750 | BOOST_FIXTURE_TEST_CASE(test_ProxyProtocol, TestFixture) |
1751 | { | |
690a9c40 | 1752 | auto local = getBackendAddress("1", 80); |
c114cd18 | 1753 | ClientState localCS(local, true, false, 0, "", {}, true); |
0e6892c6 RG |
1754 | auto tlsCtx = std::make_shared<MockupTLSCtx>(); |
1755 | tlsCtx->d_needProxyProtocol = true; | |
1756 | localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx); | |
1757 | ||
1758 | struct timeval now; | |
1759 | gettimeofday(&now, nullptr); | |
1760 | ||
db5fbb86 | 1761 | auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53)); |
0e6892c6 | 1762 | backend->d_tlsCtx = tlsCtx; |
8cf75ac2 RG |
1763 | backend->d_config.d_tlsSubjectName = "backend.powerdns.com"; |
1764 | backend->d_config.d_dohPath = "/dns-query"; | |
1765 | backend->d_config.d_addXForwardedHeaders = true; | |
1766 | backend->d_config.useProxyProtocol = true; | |
0e6892c6 RG |
1767 | |
1768 | size_t numberOfQueries = 2; | |
1769 | std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries; | |
1770 | for (size_t counter = 0; counter < numberOfQueries; counter++) { | |
1771 | DNSName name("powerdns.com."); | |
1772 | PacketBuffer query; | |
1773 | GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0); | |
1774 | pwQ.getHeader()->rd = 1; | |
1775 | pwQ.getHeader()->id = htons(counter); | |
1776 | ||
1777 | PacketBuffer response; | |
1778 | GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0); | |
1779 | pwR.getHeader()->qr = 1; | |
1780 | pwR.getHeader()->rd = 1; | |
1781 | pwR.getHeader()->ra = 1; | |
1782 | pwR.getHeader()->id = htons(counter); | |
1783 | pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER); | |
1784 | pwR.xfr32BitInt(0x01020304); | |
1785 | pwR.commit(); | |
1786 | ||
1787 | s_responses[counter] = {query, response}; | |
1788 | ||
1789 | auto sender = std::make_shared<MockupQuerySender>(); | |
1790 | sender->d_id = counter; | |
1791 | std::string payload = makeProxyHeader(counter % 2, local, local, {}); | |
592b1d99 | 1792 | InternalQuery internalQuery(std::move(query), InternalQueryState()); |
0e6892c6 RG |
1793 | internalQuery.d_proxyProtocolPayload = std::move(payload); |
1794 | queries.push_back({std::move(sender), std::move(internalQuery)}); | |
1795 | } | |
1796 | ||
1797 | s_steps = { | |
1798 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, | |
1799 | /* proxy protocol data + opening */ | |
1800 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, | |
1801 | /* settings */ | |
1802 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, | |
1803 | /* headers */ | |
1804 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, | |
1805 | /* data */ | |
4e82c08c | 1806 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
0e6892c6 RG |
1807 | /* set the outgoing descriptor (backend connection) as ready */ |
1808 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
1809 | }}, | |
1810 | {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done}, | |
1811 | /* proxy protocol data + opening */ | |
1812 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, | |
1813 | /* settings */ | |
1814 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, | |
1815 | /* headers */ | |
1816 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, | |
1817 | /* data */ | |
4e82c08c | 1818 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) { |
0e6892c6 RG |
1819 | /* set the outgoing descriptor (backend connection) as ready */ |
1820 | dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc); | |
1821 | }}, | |
1822 | /* read settings, headers and responses from the server */ | |
1823 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()}, | |
1824 | /* acknowledge settings */ | |
1825 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, | |
1826 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, | |
1827 | /* read settings, headers and responses from the server */ | |
1828 | {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()}, | |
1829 | /* acknowledge settings */ | |
1830 | {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()}, | |
1831 | {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done}, | |
1832 | }; | |
1833 | ||
1834 | for (auto& query : queries) { | |
1835 | auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first); | |
1836 | bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false); | |
1837 | BOOST_CHECK_EQUAL(result, true); | |
1838 | } | |
1839 | ||
1840 | while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) { | |
1841 | s_mplexer->run(&now); | |
1842 | } | |
1843 | ||
1844 | for (auto& query : queries) { | |
1845 | BOOST_CHECK_EQUAL(query.first->d_valid, true); | |
1846 | } | |
becad613 RG |
1847 | |
1848 | BOOST_CHECK_EQUAL(clearH2Connections(), 0U); | |
0e6892c6 RG |
1849 | } |
1850 | ||
f05cd66c | 1851 | BOOST_AUTO_TEST_SUITE_END(); |
cf25b82b | 1852 | #endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */ |