• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 13012068652

28 Jan 2025 01:59PM UTC coverage: 64.71% (+0.01%) from 64.699%
13012068652

Pull #14724

github

web-flow
Merge b15562560 into db18c3a17
Pull Request #14724: dnsdist: Add meson support

38328 of 90334 branches covered (42.43%)

Branch coverage included in aggregate %.

361 of 513 new or added lines in 35 files covered. (70.37%)

42 existing lines in 13 files now uncovered.

128150 of 166934 relevant lines covered (76.77%)

4540890.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.25
/pdns/dnsdistdist/test-dnsdistnghttp2_cc.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22
#ifndef BOOST_TEST_DYN_LINK
23
#define BOOST_TEST_DYN_LINK
24
#endif
25

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"
35
#include "sstuff.hh"
36

37
#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
38
#include <nghttp2/nghttp2.h>
39

40
BOOST_AUTO_TEST_SUITE(test_dnsdistnghttp2_cc)
41

42
struct ExpectedStep
43
{
44
public:
45
  enum class ExpectedRequest
46
  {
47
    handshakeClient,
48
    readFromClient,
49
    writeToClient,
50
    closeClient,
51
    connectToBackend,
52
    readFromBackend,
53
    writeToBackend,
54
    closeBackend
55
  };
56

57
  ExpectedStep(ExpectedRequest r, IOState n, size_t b = 0, std::function<void(int descriptor)> fn = nullptr) :
58
    cb(fn), request(r), nextState(n), bytes(b)
59
  {
152✔
60
  }
152✔
61

62
  std::function<void(int descriptor)> cb{nullptr};
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

74
static std::deque<ExpectedStep> s_steps;
75
static std::map<uint16_t, ExpectedData> s_responses;
76
static std::unique_ptr<FDMultiplexer> s_mplexer;
77

78
std::ostream& operator<<(std::ostream& os, const ExpectedStep::ExpectedRequest d);
79

80
std::ostream& operator<<(std::ostream& os, const ExpectedStep::ExpectedRequest d)
81
{
×
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));
×
84
  return os;
×
85
}
×
86

87
class DOHConnection
88
{
89
public:
90
  DOHConnection(bool needProxyProtocol) :
91
    d_session(std::unique_ptr<nghttp2_session, void (*)(nghttp2_session*)>(nullptr, nghttp2_session_del)), d_needProxyProtocol(needProxyProtocol)
92
  {
17✔
93
    nghttp2_session_callbacks* cbs = nullptr;
17✔
94
    nghttp2_session_callbacks_new(&cbs);
17✔
95
    std::unique_ptr<nghttp2_session_callbacks, void (*)(nghttp2_session_callbacks*)> callbacks(cbs, nghttp2_session_callbacks_del);
17✔
96
    cbs = nullptr;
17✔
97
    nghttp2_session_callbacks_set_send_callback(callbacks.get(), send_callback);
17✔
98
    nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks.get(), on_frame_recv_callback);
17✔
99
    nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks.get(), on_data_chunk_recv_callback);
17✔
100
    nghttp2_session_callbacks_set_on_stream_close_callback(callbacks.get(), on_stream_close_callback);
17✔
101
    nghttp2_session* sess = nullptr;
17✔
102
    nghttp2_session_server_new(&sess, callbacks.get(), this);
17✔
103
    d_session = std::unique_ptr<nghttp2_session, void (*)(nghttp2_session*)>(sess, nghttp2_session_del);
17✔
104

105
    nghttp2_settings_entry iv[1] = {
17✔
106
      {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
17✔
107
    nghttp2_submit_settings(d_session.get(), NGHTTP2_FLAG_NONE, iv, sizeof(iv) / sizeof(*iv));
17✔
108
  }
17✔
109

110
  PacketBuffer d_serverOutBuffer;
111
  PacketBuffer d_proxyProtocolBuffer;
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;
115
  /* used to replace the stream ID in outgoing frames. Ugly but the library does not let us
116
     test weird cases without that */
117
  std::map<uint32_t, uint32_t> d_idMapping;
118
  bool d_needProxyProtocol;
119

120
  size_t submitIncoming(const PacketBuffer& data, size_t pos, size_t toWrite)
121
  {
96✔
122
    size_t consumed = 0;
96✔
123
    if (d_needProxyProtocol) {
96✔
124
      do {
4✔
125
        auto bytesRemaining = isProxyHeaderComplete(d_proxyProtocolBuffer);
4✔
126
        if (bytesRemaining < 0) {
4!
127
          size_t toConsume = toWrite > static_cast<size_t>(-bytesRemaining) ? static_cast<size_t>(-bytesRemaining) : toWrite;
4!
128
          d_proxyProtocolBuffer.insert(d_proxyProtocolBuffer.end(), data.begin() + pos, data.begin() + pos + toConsume);
4✔
129
          pos += toConsume;
4✔
130
          toWrite -= toConsume;
4✔
131
          consumed += toConsume;
4✔
132

133
          bytesRemaining = isProxyHeaderComplete(d_proxyProtocolBuffer);
4✔
134
          if (bytesRemaining > 0) {
4✔
135
            d_needProxyProtocol = false;
2✔
136
          }
2✔
137
          else if (bytesRemaining == 0) {
2!
138
            throw("Fatal error while parsing proxy protocol payload");
×
139
          }
×
140
        }
4✔
141
        else if (bytesRemaining == 0) {
×
142
          throw("Fatal error while parsing proxy protocol payload");
×
143
        }
×
144

145
        if (toWrite == 0) {
4!
146
          return consumed;
×
147
        }
×
148
      } while (d_needProxyProtocol && toWrite > 0);
4!
149
    }
2✔
150

151
    ssize_t readlen = nghttp2_session_mem_recv(d_session.get(), &data.at(pos), toWrite);
96✔
152
    if (readlen < 0) {
96!
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());
96✔
158
    if (rv != 0) {
96!
159
      throw("Fatal error while sending: " + std::string(nghttp2_strerror(rv)));
×
160
    }
×
161

162
    return readlen;
96✔
163
  }
96✔
164

165
  void submitResponse(uint32_t streamId, PacketBuffer& data)
166
  {
22✔
167
    const nghttp2_nv hdrs[] = {{(uint8_t*)":status", (uint8_t*)"200", sizeof(":status") - 1, sizeof("200") - 1, NGHTTP2_NV_FLAG_NONE}};
22✔
168
    nghttp2_data_provider dataProvider;
22✔
169
    dataProvider.source.ptr = &data;
22✔
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 {
22✔
171
      (void)session;
22✔
172
      (void)stream_id;
22✔
173
      (void)user_data;
22✔
174
      auto buffer = reinterpret_cast<PacketBuffer*>(source->ptr);
22✔
175
      size_t toCopy = 0;
22✔
176
      if (buffer->size() > 0) {
22!
177
        toCopy = length > buffer->size() ? buffer->size() : length;
22!
178
        memcpy(buf, &buffer->at(0), toCopy);
22✔
179
        buffer->erase(buffer->begin(), buffer->begin() + toCopy);
22✔
180
      }
22✔
181

182
      if (buffer->size() == 0) {
22!
183
        *data_flags |= NGHTTP2_DATA_FLAG_EOF;
22✔
184
      }
22✔
185
      // cerr<<"submitting response data of size "<<toCopy<<" for stream "<<stream_id<<endl;
186
      return toCopy;
22✔
187
    };
22✔
188

189
    int rv = nghttp2_submit_response(d_session.get(), streamId, hdrs, sizeof(hdrs) / sizeof(*hdrs), &dataProvider);
22✔
190
    // cerr<<"Submitting response for stream ID "<<streamId<<": "<<rv<<endl;
191
    BOOST_CHECK_EQUAL(rv, 0);
22✔
192
    /* just in case, see if we have anything to send */
193
    rv = nghttp2_session_send(d_session.get());
22✔
194
    BOOST_CHECK_EQUAL(rv, 0);
22✔
195
  }
22✔
196

197
  void submitError(uint32_t streamId, uint16_t status, const std::string& msg)
198
  {
1✔
199
    (void)msg;
1✔
200
    const std::string statusStr = std::to_string(status);
1✔
201
    const nghttp2_nv hdrs[] = {{(uint8_t*)":status", (uint8_t*)statusStr.c_str(), sizeof(":status") - 1, statusStr.size(), NGHTTP2_NV_FLAG_NONE}};
1✔
202

203
    int rv = nghttp2_submit_response(d_session.get(), streamId, hdrs, sizeof(hdrs) / sizeof(*hdrs), nullptr);
1✔
204
    BOOST_CHECK_EQUAL(rv, 0);
1✔
205
    /* just in case, see if we have anything to send */
206
    rv = nghttp2_session_send(d_session.get());
1✔
207
    BOOST_CHECK_EQUAL(rv, 0);
1✔
208
  }
1✔
209

210
  void submitGoAway()
211
  {
6✔
212
    int rv = nghttp2_submit_goaway(d_session.get(), NGHTTP2_FLAG_NONE, 0, NGHTTP2_INTERNAL_ERROR, nullptr, 0);
6✔
213
    BOOST_CHECK_EQUAL(rv, 0);
6✔
214
    /* just in case, see if we have anything to send */
215
    rv = nghttp2_session_send(d_session.get());
6✔
216
    BOOST_CHECK_EQUAL(rv, 0);
6✔
217
  }
6✔
218

219
private:
220
  static ssize_t send_callback(nghttp2_session* session, const uint8_t* data, size_t length, int flags, void* user_data)
221
  {
85✔
222
    (void)session;
85✔
223
    (void)flags;
85✔
224
    DOHConnection* conn = reinterpret_cast<DOHConnection*>(user_data);
85✔
225
    // cerr<<"inserting "<<length<<" bytes into the server output buffer of size "<<conn->d_serverOutBuffer.size()<<endl;
226
    if (!conn->d_idMapping.empty() && length > 9) {
85!
227
      /* frame type == DATA */
228
      if (data[3] == NGHTTP2_DATA) {
4✔
229
        uint32_t streamId = 0;
2✔
230
        memcpy(&streamId, &data[5], sizeof(streamId));
2✔
231
        const auto it = conn->d_idMapping.find(ntohl(streamId));
2✔
232
        if (it != conn->d_idMapping.end()) {
2✔
233
          streamId = htonl(it->second);
1✔
234
          std::vector<uint8_t> editedData(length);
1✔
235
          std::copy(data, data + length, editedData.begin());
1✔
236
          memcpy(&editedData.at(5), &streamId, sizeof(streamId));
1✔
237
          conn->d_serverOutBuffer.insert(conn->d_serverOutBuffer.end(), editedData.data(), editedData.data() + length);
1✔
238
          return static_cast<ssize_t>(editedData.size());
1✔
239
        }
1✔
240
      }
2✔
241
    }
4✔
242

243
    conn->d_serverOutBuffer.insert(conn->d_serverOutBuffer.end(), data, data + length);
84✔
244
    return static_cast<ssize_t>(length);
84✔
245
  }
85✔
246

247
  static int on_frame_recv_callback(nghttp2_session* session, const nghttp2_frame* frame, void* user_data)
248
  {
76✔
249
    (void)session;
76✔
250
    DOHConnection* conn = reinterpret_cast<DOHConnection*>(user_data);
76✔
251
    // cerr<<"Frame type is "<<std::to_string(frame->hd.type)<<endl;
252
    if ((frame->hd.type == NGHTTP2_HEADERS || frame->hd.type == NGHTTP2_DATA) && frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
76✔
253
#if 0
254
      auto stream_data = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
255
      /* For DATA and HEADERS frame, this callback may be called after on_stream_close_callback. Check that stream still alive. */
256
      if (stream_data == nullptr) {
257
        cerr<<"unable to find stream data!"<<endl;
258
        return 0;
259
      }
260
#endif
261

262
      auto& query = conn->d_queries.at(frame->hd.stream_id);
25✔
263
      BOOST_REQUIRE_GT(query.size(), sizeof(dnsheader));
25✔
264
      const dnsheader_aligned dh(query.data());
25✔
265
      uint16_t id = ntohs(dh->id);
25✔
266
      // cerr<<"got query ID "<<id<<endl;
267

268
      const auto& expected = s_responses.at(id);
25✔
269
      BOOST_REQUIRE_EQUAL(expected.d_query.size(), query.size());
25✔
270
      for (size_t idx = 0; idx < query.size(); idx++) {
829✔
271
        if (expected.d_query.at(idx) != query.at(idx)) {
804!
272
          cerr << "Mismatch at offset " << idx << ", expected " << std::to_string(query.at(idx)) << " got " << std::to_string(expected.d_query.at(idx)) << endl;
×
273
          BOOST_CHECK(false);
×
274
        }
×
275
      }
804✔
276

277
      DNSName qname(reinterpret_cast<const char*>(query.data()), query.size(), sizeof(dnsheader), false);
25✔
278
      if (qname == DNSName("goaway.powerdns.com.")) {
25✔
279
        conn->submitGoAway();
2✔
280
      }
2✔
281
      else if (qname == DNSName("500.powerdns.com.") && (id % 2) == 0) {
23✔
282
        /* we return a 500 on the first query only */
283
        conn->submitError(frame->hd.stream_id, 500, "Server failure");
1✔
284
      }
1✔
285
      else if (qname == DNSName("wrong-stream-id.powerdns.com.") && (id % 2) == 0) {
22✔
286
        /* we return a wrong stremad ID on the first query only */
287
        BOOST_CHECK_EQUAL(frame->hd.stream_id, 1);
1✔
288
        conn->d_responses[frame->hd.stream_id] = expected.d_response;
1✔
289
        /* use an invalid stream ID! */
290
        conn->d_idMapping[frame->hd.stream_id] = frame->hd.stream_id + 4;
1✔
291
        conn->submitResponse(frame->hd.stream_id, conn->d_responses.at(frame->hd.stream_id));
1✔
292
      }
1✔
293
      else {
21✔
294
        conn->d_responses[frame->hd.stream_id] = expected.d_response;
21✔
295
        conn->submitResponse(frame->hd.stream_id, conn->d_responses.at(frame->hd.stream_id));
21✔
296
      }
21✔
297
      conn->d_queries.erase(frame->hd.stream_id);
25✔
298
    }
25✔
299

300
    return 0;
76✔
301
  }
76✔
302

303
  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)
304
  {
25✔
305
    (void)session;
25✔
306
    (void)flags;
25✔
307
    DOHConnection* conn = reinterpret_cast<DOHConnection*>(user_data);
25✔
308
    auto& query = conn->d_queries[stream_id];
25✔
309
    query.insert(query.end(), data, data + len);
25✔
310
    return 0;
25✔
311
  }
25✔
312

313
  static int on_stream_close_callback(nghttp2_session* session, int32_t stream_id, uint32_t error_code, void* user_data)
314
  {
25✔
315
    (void)session;
25✔
316
    (void)stream_id;
25✔
317
    (void)user_data;
25✔
318
    if (error_code == 0) {
25✔
319
      return 0;
23✔
320
    }
23✔
321

322
    return 0;
2✔
323
  }
25✔
324
};
325

326
static std::map<int, std::unique_ptr<DOHConnection>> s_connectionBuffers;
327

328
class MockupTLSConnection : public TLSConnection
329
{
330
public:
331
  MockupTLSConnection(int descriptor, bool client = false, bool needProxyProtocol = false) :
332
    d_descriptor(descriptor), d_client(client)
333
  {
17✔
334
    s_connectionBuffers[d_descriptor] = std::make_unique<DOHConnection>(needProxyProtocol);
17✔
335
  }
17✔
336

337
  ~MockupTLSConnection() {}
×
338

339
  IOState tryHandshake() override
340
  {
×
341
    auto step = getStep();
×
342
    BOOST_REQUIRE_EQUAL(step.request, ExpectedStep::ExpectedRequest::handshakeClient);
×
343

344
    return step.nextState;
×
345
  }
×
346

347
  IOState tryWrite(const PacketBuffer& buffer, size_t& pos, size_t toWrite) override
348
  {
97✔
349
    auto& conn = s_connectionBuffers.at(d_descriptor);
97✔
350
    auto step = getStep();
97✔
351
    BOOST_REQUIRE_EQUAL(step.request, !d_client ? ExpectedStep::ExpectedRequest::writeToClient : ExpectedStep::ExpectedRequest::writeToBackend);
97✔
352

353
    if (step.bytes == 0) {
97✔
354
      if (step.nextState == IOState::NeedWrite) {
1!
355
        return step.nextState;
×
356
      }
×
357
      throw std::runtime_error("Remote host closed the connection");
1✔
358
    }
1✔
359

360
    toWrite -= pos;
96✔
361
    BOOST_REQUIRE_GE(buffer.size(), pos + toWrite);
96✔
362

363
    if (step.bytes < toWrite) {
96✔
364
      toWrite = step.bytes;
1✔
365
    }
1✔
366

367
    conn->submitIncoming(buffer, pos, toWrite);
96✔
368
    pos += toWrite;
96✔
369

370
    return step.nextState;
96✔
371
  }
97✔
372

373
  IOState tryRead(PacketBuffer& buffer, size_t& pos, size_t toRead, bool allowIncomplete = false) override
374
  {
21✔
375
    auto& conn = s_connectionBuffers.at(d_descriptor);
21✔
376
    auto step = getStep();
21✔
377
    BOOST_REQUIRE_EQUAL(step.request, !d_client ? ExpectedStep::ExpectedRequest::readFromClient : ExpectedStep::ExpectedRequest::readFromBackend);
21✔
378

379
    if (step.bytes == 0) {
21✔
380
      if (step.nextState == IOState::NeedRead) {
2✔
381
        return step.nextState;
1✔
382
      }
1✔
383
      throw std::runtime_error("Remote host closed the connection");
1✔
384
    }
2✔
385

386
    auto& externalBuffer = conn->d_serverOutBuffer;
19✔
387
    toRead -= pos;
19✔
388

389
    if (step.bytes < toRead) {
19✔
390
      toRead = step.bytes;
1✔
391
    }
1✔
392
    if (allowIncomplete) {
19!
393
      if (toRead > externalBuffer.size()) {
19✔
394
        toRead = externalBuffer.size();
18✔
395
      }
18✔
396
    }
19✔
397
    else {
×
398
      BOOST_REQUIRE_GE(externalBuffer.size(), toRead);
×
399
    }
×
400

401
    BOOST_REQUIRE_GE(buffer.size(), toRead);
19✔
402

403
    std::copy(externalBuffer.begin(), externalBuffer.begin() + toRead, buffer.begin() + pos);
19✔
404
    pos += toRead;
19✔
405
    externalBuffer.erase(externalBuffer.begin(), externalBuffer.begin() + toRead);
19✔
406

407
    return step.nextState;
19✔
408
  }
21✔
409

410
  IOState tryConnect(bool fastOpen, const ComboAddress& remote) override
411
  {
17✔
412
    (void)fastOpen;
17✔
413
    (void)remote;
17✔
414
    auto step = getStep();
17✔
415
    BOOST_REQUIRE_EQUAL(step.request, ExpectedStep::ExpectedRequest::connectToBackend);
17✔
416

417
    return step.nextState;
17✔
418
  }
17✔
419

420
  void close() override
421
  {
17✔
422
    auto step = getStep();
17✔
423
    BOOST_REQUIRE_EQUAL(step.request, !d_client ? ExpectedStep::ExpectedRequest::closeClient : ExpectedStep::ExpectedRequest::closeBackend);
17✔
424
  }
17✔
425

426
  bool isUsable() const override
427
  {
×
428
    return true;
×
429
  }
×
430

431
  std::string getServerNameIndication() const override
432
  {
×
433
    return "";
×
434
  }
×
435

436
  std::vector<uint8_t> getNextProtocol() const override
437
  {
×
438
    return std::vector<uint8_t>();
×
439
  }
×
440

441
  LibsslTLSVersion getTLSVersion() const override
442
  {
×
443
    return LibsslTLSVersion::TLS13;
×
444
  }
×
445

446
  bool hasSessionBeenResumed() const override
447
  {
17✔
448
    return false;
17✔
449
  }
17✔
450

451
  std::vector<std::unique_ptr<TLSSession>> getSessions() override
452
  {
17✔
453
    return {};
17✔
454
  }
17✔
455

456
  void setSession(std::unique_ptr<TLSSession>& session) override
457
  {
×
NEW
458
    (void)session;
×
UNCOV
459
  }
×
460

461
  std::vector<int> getAsyncFDs() override
462
  {
×
463
    return {};
×
464
  }
×
465

466
  /* unused in that context, don't bother */
467
  void doHandshake() override
468
  {
×
469
  }
×
470

471
  void connect(bool fastOpen, const ComboAddress& remote, const struct timeval& timeout) override
472
  {
×
NEW
473
    (void)fastOpen;
×
NEW
474
    (void)remote;
×
NEW
475
    (void)timeout;
×
UNCOV
476
  }
×
477

478
  size_t read(void* buffer, size_t bufferSize, const struct timeval& readTimeout, const struct timeval& totalTimeout = {0, 0}, bool allowIncomplete = false) override
479
  {
×
NEW
480
    (void)buffer;
×
NEW
481
    (void)bufferSize;
×
NEW
482
    (void)readTimeout;
×
NEW
483
    (void)totalTimeout;
×
NEW
484
    (void)allowIncomplete;
×
485
    return 0;
×
486
  }
×
487

488
  size_t write(const void* buffer, size_t bufferSize, const struct timeval& writeTimeout) override
489
  {
×
NEW
490
    (void)buffer;
×
NEW
491
    (void)bufferSize;
×
NEW
492
    (void)writeTimeout;
×
493
    return 0;
×
494
  }
×
495

496
private:
497
  ExpectedStep getStep() const
498
  {
152✔
499
    BOOST_REQUIRE(!s_steps.empty());
152✔
500
    auto step = s_steps.front();
152✔
501
    s_steps.pop_front();
152✔
502

503
    if (step.cb) {
152✔
504
      step.cb(d_descriptor);
36✔
505
    }
36✔
506

507
    return step;
152✔
508
  }
152✔
509

510
  const int d_descriptor;
511
  bool d_client{false};
512
};
513

514
#include "test-dnsdistnghttp2_common.hh"
515

516
class MockupQuerySender : public TCPQuerySender
517
{
518
public:
519
  bool active() const override
520
  {
×
521
    return true;
×
522
  }
×
523

524
  void handleResponse(const struct timeval& now, TCPResponse&& response) override
525
  {
14✔
526
    if (d_customHandler) {
14✔
527
      d_customHandler(d_id, now, std::move(response));
1✔
528
      return;
1✔
529
    }
1✔
530

531
    BOOST_REQUIRE_GT(response.d_buffer.size(), sizeof(dnsheader));
13✔
532
    const dnsheader_aligned dh(response.d_buffer.data());
13✔
533
    uint16_t id = ntohs(dh->id);
13✔
534

535
    BOOST_REQUIRE_EQUAL(id, d_id);
13✔
536
    const auto& expected = s_responses.at(id);
13✔
537
    BOOST_REQUIRE_EQUAL(expected.d_response.size(), response.d_buffer.size());
13✔
538
    for (size_t idx = 0; idx < response.d_buffer.size(); idx++) {
615✔
539
      if (expected.d_response.at(idx) != response.d_buffer.at(idx)) {
602!
540
        cerr << "Mismatch at offset " << idx << ", expected " << std::to_string(response.d_buffer.at(idx)) << " got " << std::to_string(expected.d_response.at(idx)) << endl;
×
541
        BOOST_CHECK(false);
×
542
      }
×
543
    }
602✔
544

545
    if (expected.d_response != response.d_buffer) {
13!
546
      BOOST_REQUIRE(false);
×
547
    }
×
548
    d_valid = true;
13✔
549
  }
13✔
550

551
  void handleXFRResponse([[maybe_unused]] const struct timeval& now, [[maybe_unused]] TCPResponse&& response) override
552
  {
×
553
  }
×
554

555
  void notifyIOError([[maybe_unused]] const struct timeval& now, [[maybe_unused]] TCPResponse&& response) override
556
  {
12✔
557
    d_error = true;
12✔
558
  }
12✔
559

560
  std::function<void(uint16_t id, const struct timeval& now, TCPResponse&& response)> d_customHandler;
561
  uint16_t d_id{0};
562
  bool d_valid{false};
563
  bool d_error{false};
564
};
565

566
struct TestFixture
567
{
568
  TestFixture()
569
  {
14✔
570
    s_steps.clear();
14✔
571
    s_responses.clear();
14✔
572
    s_mplexer = std::make_unique<MockupFDMultiplexer>();
14✔
573
  }
14✔
574
  ~TestFixture()
575
  {
14✔
576
    clearH2Connections();
14✔
577
    s_steps.clear();
14✔
578
    s_responses.clear();
14✔
579
    s_mplexer.reset();
14✔
580
  }
14✔
581
};
582

583
BOOST_FIXTURE_TEST_CASE(test_SingleQuery, TestFixture)
584
{
1✔
585
  auto local = getBackendAddress("1", 80);
1✔
586
  ClientState localCS(local, true, false, 0, "", {}, true);
1✔
587
  auto tlsCtx = std::make_shared<MockupTLSCtx>();
1✔
588
  localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx);
1✔
589

590
  struct timeval now;
1✔
591
  gettimeofday(&now, nullptr);
1✔
592

593
  size_t counter = 1;
1✔
594
  DNSName name("powerdns.com.");
1✔
595
  PacketBuffer query;
1✔
596
  GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
1✔
597
  pwQ.getHeader()->rd = 1;
1✔
598
  pwQ.getHeader()->id = htons(counter);
1✔
599

600
  PacketBuffer response;
1✔
601
  GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0);
1✔
602
  pwR.getHeader()->qr = 1;
1✔
603
  pwR.getHeader()->rd = 1;
1✔
604
  pwR.getHeader()->ra = 1;
1✔
605
  pwR.getHeader()->id = htons(counter);
1✔
606
  pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
1✔
607
  pwR.xfr32BitInt(0x01020304);
1✔
608
  pwR.commit();
1✔
609

610
  s_responses[counter] = {query, response};
1✔
611

612
  auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53));
1✔
613
  backend->d_tlsCtx = tlsCtx;
1✔
614
  backend->d_config.d_tlsSubjectName = "backend.powerdns.com";
1✔
615
  backend->d_config.d_dohPath = "/dns-query";
1✔
616
  backend->d_config.d_addXForwardedHeaders = true;
1✔
617

618
  auto sender = std::make_shared<MockupQuerySender>();
1✔
619
  sender->d_id = counter;
1✔
620
  InternalQuery internalQuery(std::move(query), InternalQueryState());
1✔
621

622
  s_steps = {
1✔
623
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
624
    /* opening */
625
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
626
    /* settings */
627
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
628
    /* headers */
629
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
630
    /* data */
631
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
632
       /* set the outgoing descriptor (backend connection) as ready */
633
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
634
     }},
1✔
635
    /* read settings, headers and response from the server */
636
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
637
       /* set the outgoing descriptor (backend connection) as NOT ready anymore */
638
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setNotReady(desc);
1✔
639
     }},
1✔
640
    /* acknowledge settings */
641
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
642
       s_connectionBuffers.at(desc)->submitGoAway();
1✔
643
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
644
     }},
1✔
645
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
646
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
647
  };
1✔
648

649
  auto sliced = std::shared_ptr<TCPQuerySender>(sender);
1✔
650
  bool result = sendH2Query(backend, s_mplexer, sliced, std::move(internalQuery), false);
1✔
651
  BOOST_CHECK_EQUAL(result, true);
1✔
652

653
  while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) {
3!
654
    s_mplexer->run(&now);
2✔
655
  }
2✔
656
  BOOST_CHECK_EQUAL(sender->d_valid, true);
1✔
657
}
1✔
658

659
BOOST_FIXTURE_TEST_CASE(test_ConcurrentQueries, TestFixture)
660
{
1✔
661
  auto local = getBackendAddress("1", 80);
1✔
662
  ClientState localCS(local, true, false, 0, "", {}, true);
1✔
663
  auto tlsCtx = std::make_shared<MockupTLSCtx>();
1✔
664
  localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx);
1✔
665

666
  struct timeval now;
1✔
667
  gettimeofday(&now, nullptr);
1✔
668

669
  auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53));
1✔
670
  backend->d_tlsCtx = tlsCtx;
1✔
671
  backend->d_config.d_tlsSubjectName = "backend.powerdns.com";
1✔
672
  backend->d_config.d_dohPath = "/dns-query";
1✔
673
  backend->d_config.d_addXForwardedHeaders = true;
1✔
674

675
  size_t numberOfQueries = 2;
1✔
676
  std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries;
1✔
677
  for (size_t counter = 0; counter < numberOfQueries; counter++) {
3✔
678
    DNSName name("powerdns.com.");
2✔
679
    PacketBuffer query;
2✔
680
    GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
2✔
681
    pwQ.getHeader()->rd = 1;
2✔
682
    pwQ.getHeader()->id = htons(counter);
2✔
683

684
    PacketBuffer response;
2✔
685
    GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0);
2✔
686
    pwR.getHeader()->qr = 1;
2✔
687
    pwR.getHeader()->rd = 1;
2✔
688
    pwR.getHeader()->ra = 1;
2✔
689
    pwR.getHeader()->id = htons(counter);
2✔
690
    pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
2✔
691
    pwR.xfr32BitInt(0x01020304);
2✔
692
    pwR.commit();
2✔
693

694
    s_responses[counter] = {query, response};
2✔
695

696
    auto sender = std::make_shared<MockupQuerySender>();
2✔
697
    sender->d_id = counter;
2✔
698
    InternalQuery internalQuery(std::move(query), InternalQueryState());
2✔
699
    queries.push_back({std::move(sender), std::move(internalQuery)});
2✔
700
  }
2✔
701

702
  s_steps = {
1✔
703
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
704
    /* opening */
705
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
706
    /* settings */
707
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
708
    /* headers */
709
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
710
    /* data */
711
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
712
       /* set the outgoing descriptor (backend connection) as ready */
713
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
714
     }},
1✔
715
    /* headers */
716
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
717
    /* data */
718
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
719
       /* set the outgoing descriptor (backend connection) as ready */
720
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
721
     }},
1✔
722
    /* read settings, headers and responses from the server */
723
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
724
    /* acknowledge settings */
725
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
726
       s_connectionBuffers.at(desc)->submitGoAway();
1✔
727
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
728
     }},
1✔
729
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
730
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
731
  };
1✔
732

733
  for (auto& query : queries) {
2✔
734
    auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first);
2✔
735
    bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false);
2✔
736
    BOOST_CHECK_EQUAL(result, true);
2✔
737
  }
2✔
738

739
  while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) {
3!
740
    s_mplexer->run(&now);
2✔
741
  }
2✔
742

743
  for (auto& query : queries) {
2✔
744
    BOOST_CHECK_EQUAL(query.first->d_valid, true);
2✔
745
  }
2✔
746
}
1✔
747

748
BOOST_FIXTURE_TEST_CASE(test_ConnectionReuse, TestFixture)
749
{
1✔
750
  auto local = getBackendAddress("1", 80);
1✔
751
  ClientState localCS(local, true, false, 0, "", {}, true);
1✔
752
  auto tlsCtx = std::make_shared<MockupTLSCtx>();
1✔
753
  localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx);
1✔
754

755
  struct timeval now;
1✔
756
  gettimeofday(&now, nullptr);
1✔
757

758
  auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53));
1✔
759
  backend->d_tlsCtx = tlsCtx;
1✔
760
  backend->d_config.d_tlsSubjectName = "backend.powerdns.com";
1✔
761
  backend->d_config.d_dohPath = "/dns-query";
1✔
762
  backend->d_config.d_addXForwardedHeaders = true;
1✔
763

764
  size_t numberOfQueries = 2;
1✔
765
  std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries;
1✔
766
  for (size_t counter = 0; counter < numberOfQueries; counter++) {
3✔
767
    DNSName name("powerdns.com.");
2✔
768
    PacketBuffer query;
2✔
769
    GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
2✔
770
    pwQ.getHeader()->rd = 1;
2✔
771
    pwQ.getHeader()->id = htons(counter);
2✔
772

773
    PacketBuffer response;
2✔
774
    GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0);
2✔
775
    pwR.getHeader()->qr = 1;
2✔
776
    pwR.getHeader()->rd = 1;
2✔
777
    pwR.getHeader()->ra = 1;
2✔
778
    pwR.getHeader()->id = htons(counter);
2✔
779
    pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
2✔
780
    pwR.xfr32BitInt(0x01020304);
2✔
781
    pwR.commit();
2✔
782

783
    s_responses[counter] = {query, response};
2✔
784

785
    auto sender = std::make_shared<MockupQuerySender>();
2✔
786
    sender->d_id = counter;
2✔
787
    InternalQuery internalQuery(std::move(query), InternalQueryState());
2✔
788
    queries.push_back({std::move(sender), std::move(internalQuery)});
2✔
789
  }
2✔
790

791
  bool firstQueryDone = false;
1✔
792
  s_steps = {
1✔
793
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
794
    /* opening */
795
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
796
    /* settings */
797
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
798
    /* headers */
799
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
800
    /* data */
801
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
802
       /* set the outgoing descriptor (backend connection) as ready */
803
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
804
     }},
1✔
805
    /* read settings, headers and responses from the server */
806
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
807
    /* acknowledge settings */
808
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [&firstQueryDone](int desc) {
1✔
809
       (void)desc;
1✔
810
       firstQueryDone = true;
1✔
811
     }},
1✔
812
    /* headers */
813
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
814
       (void)desc;
1✔
815
     }},
1✔
816
    /* data */
817
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
818
       /* set the outgoing descriptor (backend connection) as ready */
819
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
820
     }},
1✔
821
    /* read settings, headers and responses from the server */
822
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
823
    /* later the backend sends a go away frame */
824
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
825
       (void)desc;
1✔
826
       s_connectionBuffers.at(desc)->submitGoAway();
1✔
827
     }},
1✔
828
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
829
  };
1✔
830

831
  {
1✔
832
    auto& query = queries.at(0);
1✔
833
    auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first);
1✔
834
    bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false);
1✔
835
    BOOST_CHECK_EQUAL(result, true);
1✔
836

837
    while (!firstQueryDone && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) {
2!
838
      s_mplexer->run(&now);
1✔
839
    }
1✔
840

841
    BOOST_CHECK_EQUAL(query.first->d_valid, true);
1✔
842
    BOOST_CHECK_EQUAL(firstQueryDone, true);
1✔
843
  }
1✔
844

845
  {
1✔
846
    auto& query = queries.at(1);
1✔
847
    auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first);
1✔
848
    bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false);
1✔
849
    BOOST_CHECK_EQUAL(result, true);
1✔
850

851
    while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) {
3!
852
      s_mplexer->run(&now);
2✔
853
    }
2✔
854

855
    BOOST_CHECK_EQUAL(query.first->d_valid, true);
1✔
856
  }
1✔
857
}
1✔
858

859
BOOST_FIXTURE_TEST_CASE(test_InvalidDNSAnswer, TestFixture)
860
{
1✔
861
  auto local = getBackendAddress("1", 80);
1✔
862
  ClientState localCS(local, true, false, 0, "", {}, true);
1✔
863
  auto tlsCtx = std::make_shared<MockupTLSCtx>();
1✔
864
  localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx);
1✔
865

866
  struct timeval now;
1✔
867
  gettimeofday(&now, nullptr);
1✔
868

869
  size_t counter = 1;
1✔
870
  DNSName name("powerdns.com.");
1✔
871
  PacketBuffer query;
1✔
872
  GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
1✔
873
  pwQ.getHeader()->rd = 1;
1✔
874
  pwQ.getHeader()->id = htons(counter);
1✔
875

876
  PacketBuffer response;
1✔
877
  GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0);
1✔
878
  pwR.getHeader()->qr = 1;
1✔
879
  pwR.getHeader()->rd = 1;
1✔
880
  pwR.getHeader()->ra = 1;
1✔
881
  pwR.getHeader()->id = htons(counter);
1✔
882
  pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
1✔
883
  pwR.xfr32BitInt(0x01020304);
1✔
884
  pwR.commit();
1✔
885

886
  /* TRUNCATE the answer */
887
  response.resize(11);
1✔
888
  s_responses[counter] = {query, response};
1✔
889

890
  auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53));
1✔
891
  backend->d_tlsCtx = tlsCtx;
1✔
892
  backend->d_config.d_tlsSubjectName = "backend.powerdns.com";
1✔
893
  backend->d_config.d_dohPath = "/dns-query";
1✔
894
  backend->d_config.d_addXForwardedHeaders = true;
1✔
895

896
  auto sender = std::make_shared<MockupQuerySender>();
1✔
897
  sender->d_id = counter;
1✔
898
  sender->d_customHandler = [](uint16_t id, const struct timeval&, TCPResponse&& resp) {
1✔
899
    (void)id;
1✔
900
    BOOST_CHECK_EQUAL(resp.d_buffer.size(), 11U);
1✔
901
    /* simulate an exception, since DoH and UDP frontends will process the query right away,
902
       while TCP and DoT will first pass it back to the TCP worker thread */
903
    throw std::runtime_error("Invalid response");
1✔
904
  };
1✔
905
  InternalQuery internalQuery(std::move(query), InternalQueryState());
1✔
906

907
  s_steps = {
1✔
908
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
909
    /* opening */
910
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
911
    /* settings */
912
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
913
    /* headers */
914
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
915
    /* data */
916
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
917
       /* set the outgoing descriptor (backend connection) as ready */
918
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
919
     }},
1✔
920
    /* read settings, headers and response from the server */
921
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
922
    /* acknowledge settings */
923
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
924
    /* try to read, the backend says to go away */
925
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
926
       s_connectionBuffers.at(desc)->submitGoAway();
1✔
927
     }},
1✔
928
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
929
  };
1✔
930

931
  auto sliced = std::shared_ptr<TCPQuerySender>(sender);
1✔
932
  bool result = sendH2Query(backend, s_mplexer, sliced, std::move(internalQuery), false);
1✔
933
  BOOST_CHECK_EQUAL(result, true);
1✔
934

935
  while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) {
3!
936
    s_mplexer->run(&now);
2✔
937
  }
2✔
938
  BOOST_CHECK_EQUAL(sender->d_valid, false);
1✔
939
}
1✔
940

941
BOOST_FIXTURE_TEST_CASE(test_TimeoutWhileWriting, TestFixture)
942
{
1✔
943
  auto local = getBackendAddress("1", 80);
1✔
944
  ClientState localCS(local, true, false, 0, "", {}, true);
1✔
945
  auto tlsCtx = std::make_shared<MockupTLSCtx>();
1✔
946
  localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx);
1✔
947

948
  struct timeval now;
1✔
949
  gettimeofday(&now, nullptr);
1✔
950

951
  auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53));
1✔
952
  backend->d_tlsCtx = tlsCtx;
1✔
953
  backend->d_config.d_tlsSubjectName = "backend.powerdns.com";
1✔
954
  backend->d_config.d_dohPath = "/dns-query";
1✔
955
  backend->d_config.d_addXForwardedHeaders = true;
1✔
956

957
  size_t numberOfQueries = 2;
1✔
958
  std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries;
1✔
959
  for (size_t counter = 0; counter < numberOfQueries; counter++) {
3✔
960
    DNSName name("powerdns.com.");
2✔
961
    PacketBuffer query;
2✔
962
    GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
2✔
963
    pwQ.getHeader()->rd = 1;
2✔
964
    pwQ.getHeader()->id = htons(counter);
2✔
965

966
    PacketBuffer response;
2✔
967
    GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0);
2✔
968
    pwR.getHeader()->qr = 1;
2✔
969
    pwR.getHeader()->rd = 1;
2✔
970
    pwR.getHeader()->ra = 1;
2✔
971
    pwR.getHeader()->id = htons(counter);
2✔
972
    pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
2✔
973
    pwR.xfr32BitInt(0x01020304);
2✔
974
    pwR.commit();
2✔
975

976
    s_responses[counter] = {query, response};
2✔
977

978
    auto sender = std::make_shared<MockupQuerySender>();
2✔
979
    sender->d_id = counter;
2✔
980
    InternalQuery internalQuery(std::move(query), InternalQueryState());
2✔
981
    queries.push_back({std::move(sender), std::move(internalQuery)});
2✔
982
  }
2✔
983

984
  bool timeout = false;
1✔
985
  s_steps = {
1✔
986
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
987
    /* opening */
988
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
989
    /* settings */
990
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
991
    /* headers */
992
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
993
    /* data */
994
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
995
    /* headers */
996
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
997
    /* data */
998
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::NeedWrite, std::numeric_limits<size_t>::max(), [&timeout](int desc) {
1✔
999
       (void)desc;
1✔
1000
       timeout = true;
1✔
1001
     }},
1✔
1002
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
1003
  };
1✔
1004

1005
  for (auto& query : queries) {
2✔
1006
    auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first);
2✔
1007
    bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false);
2✔
1008
    BOOST_CHECK_EQUAL(result, true);
2✔
1009
  }
2✔
1010

1011
  while (!timeout && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) {
1!
1012
    s_mplexer->run(&now);
×
1013
  }
×
1014

1015
  struct timeval later = now;
1✔
1016
  later.tv_sec += backend->d_config.tcpSendTimeout + 1;
1✔
1017

1018
  auto expiredConns = handleH2Timeouts(*s_mplexer, later);
1✔
1019
  BOOST_CHECK_EQUAL(expiredConns, 1U);
1✔
1020

1021
  for (auto& query : queries) {
2✔
1022
    BOOST_CHECK_EQUAL(query.first->d_valid, false);
2✔
1023
    BOOST_CHECK_EQUAL(query.first->d_error, true);
2✔
1024
  }
2✔
1025

1026
  BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
1✔
1027
}
1✔
1028

1029
BOOST_FIXTURE_TEST_CASE(test_TimeoutWhileReading, TestFixture)
1030
{
1✔
1031
  auto local = getBackendAddress("1", 80);
1✔
1032
  ClientState localCS(local, true, false, 0, "", {}, true);
1✔
1033
  auto tlsCtx = std::make_shared<MockupTLSCtx>();
1✔
1034
  localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx);
1✔
1035

1036
  struct timeval now;
1✔
1037
  gettimeofday(&now, nullptr);
1✔
1038

1039
  auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53));
1✔
1040
  backend->d_tlsCtx = tlsCtx;
1✔
1041
  backend->d_config.d_tlsSubjectName = "backend.powerdns.com";
1✔
1042
  backend->d_config.d_dohPath = "/dns-query";
1✔
1043
  backend->d_config.d_addXForwardedHeaders = true;
1✔
1044

1045
  size_t numberOfQueries = 2;
1✔
1046
  std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries;
1✔
1047
  for (size_t counter = 0; counter < numberOfQueries; counter++) {
3✔
1048
    DNSName name("powerdns.com.");
2✔
1049
    PacketBuffer query;
2✔
1050
    GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
2✔
1051
    pwQ.getHeader()->rd = 1;
2✔
1052
    pwQ.getHeader()->id = htons(counter);
2✔
1053

1054
    PacketBuffer response;
2✔
1055
    GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0);
2✔
1056
    pwR.getHeader()->qr = 1;
2✔
1057
    pwR.getHeader()->rd = 1;
2✔
1058
    pwR.getHeader()->ra = 1;
2✔
1059
    pwR.getHeader()->id = htons(counter);
2✔
1060
    pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
2✔
1061
    pwR.xfr32BitInt(0x01020304);
2✔
1062
    pwR.commit();
2✔
1063

1064
    s_responses[counter] = {query, response};
2✔
1065

1066
    auto sender = std::make_shared<MockupQuerySender>();
2✔
1067
    sender->d_id = counter;
2✔
1068
    InternalQuery internalQuery(std::move(query), InternalQueryState());
2✔
1069
    queries.push_back({std::move(sender), std::move(internalQuery)});
2✔
1070
  }
2✔
1071

1072
  bool timeout = false;
1✔
1073
  s_steps = {
1✔
1074
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
1075
    /* opening */
1076
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1077
    /* settings */
1078
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1079
    /* headers */
1080
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1081
    /* data */
1082
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1083
    /* headers */
1084
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1085
    /* data */
1086
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [&timeout](int desc) {
1✔
1087
       (void)desc;
1✔
1088
       /* set the timeout flag now, since the timeout occurs while waiting for the descriptor to become readable */
1089
       timeout = true;
1✔
1090
     }},
1✔
1091
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
1092
  };
1✔
1093

1094
  for (auto& query : queries) {
2✔
1095
    auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first);
2✔
1096
    bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false);
2✔
1097
    BOOST_CHECK_EQUAL(result, true);
2✔
1098
  }
2✔
1099

1100
  while (!timeout && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) {
1!
1101
    s_mplexer->run(&now);
×
1102
  }
×
1103

1104
  struct timeval later = now;
1✔
1105
  later.tv_sec += backend->d_config.tcpRecvTimeout + 1;
1✔
1106

1107
  auto expiredConns = handleH2Timeouts(*s_mplexer, later);
1✔
1108
  BOOST_CHECK_EQUAL(expiredConns, 1U);
1✔
1109

1110
  for (auto& query : queries) {
2✔
1111
    BOOST_CHECK_EQUAL(query.first->d_valid, false);
2✔
1112
    BOOST_CHECK_EQUAL(query.first->d_error, true);
2✔
1113
  }
2✔
1114
  BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
1✔
1115
}
1✔
1116

1117
BOOST_FIXTURE_TEST_CASE(test_ShortWrite, TestFixture)
1118
{
1✔
1119
  auto local = getBackendAddress("1", 80);
1✔
1120
  ClientState localCS(local, true, false, 0, "", {}, true);
1✔
1121
  auto tlsCtx = std::make_shared<MockupTLSCtx>();
1✔
1122
  localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx);
1✔
1123

1124
  struct timeval now;
1✔
1125
  gettimeofday(&now, nullptr);
1✔
1126

1127
  auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53));
1✔
1128
  backend->d_tlsCtx = tlsCtx;
1✔
1129
  backend->d_config.d_tlsSubjectName = "backend.powerdns.com";
1✔
1130
  backend->d_config.d_dohPath = "/dns-query";
1✔
1131
  backend->d_config.d_addXForwardedHeaders = true;
1✔
1132

1133
  size_t numberOfQueries = 2;
1✔
1134
  std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries;
1✔
1135
  for (size_t counter = 0; counter < numberOfQueries; counter++) {
3✔
1136
    DNSName name("powerdns.com.");
2✔
1137
    PacketBuffer query;
2✔
1138
    GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
2✔
1139
    pwQ.getHeader()->rd = 1;
2✔
1140
    pwQ.getHeader()->id = htons(counter);
2✔
1141

1142
    PacketBuffer response;
2✔
1143
    GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0);
2✔
1144
    pwR.getHeader()->qr = 1;
2✔
1145
    pwR.getHeader()->rd = 1;
2✔
1146
    pwR.getHeader()->ra = 1;
2✔
1147
    pwR.getHeader()->id = htons(counter);
2✔
1148
    pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
2✔
1149
    pwR.xfr32BitInt(0x01020304);
2✔
1150
    pwR.commit();
2✔
1151

1152
    s_responses[counter] = {query, response};
2✔
1153

1154
    auto sender = std::make_shared<MockupQuerySender>();
2✔
1155
    sender->d_id = counter;
2✔
1156
    InternalQuery internalQuery(std::move(query), InternalQueryState());
2✔
1157
    queries.push_back({std::move(sender), std::move(internalQuery)});
2✔
1158
  }
2✔
1159

1160
  bool done = false;
1✔
1161
  s_steps = {
1✔
1162
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
1163
    /* opening */
1164
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1165
    /* settings */
1166
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::NeedWrite, 2, [](int desc) {
1✔
1167
       /* set the outgoing descriptor (backend connection) as ready */
1168
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
1169
     }},
1✔
1170
    /* settings (second attempt) + headers + data + headers (second query) + data */
1171
    {
1✔
1172
      ExpectedStep::ExpectedRequest::writeToBackend,
1✔
1173
      IOState::Done,
1✔
1174
      std::numeric_limits<size_t>::max(),
1✔
1175
    },
1✔
1176
    /* read settings, headers and responses from the server */
1177
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1178
    /* acknowledge settings */
1179
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [&done](int desc) {
1✔
1180
       /* mark backend as not ready */
1181
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setNotReady(desc);
1✔
1182
       done = true;
1✔
1183
     }},
1✔
1184
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
1185
  };
1✔
1186

1187
  for (auto& query : queries) {
2✔
1188
    auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first);
2✔
1189
    bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false);
2✔
1190
    BOOST_CHECK_EQUAL(result, true);
2✔
1191
  }
2✔
1192

1193
  while (!done && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) {
3!
1194
    s_mplexer->run(&now);
2✔
1195
  }
2✔
1196

1197
  for (auto& query : queries) {
2✔
1198
    BOOST_CHECK_EQUAL(query.first->d_valid, true);
2✔
1199
  }
2✔
1200

1201
  BOOST_CHECK_EQUAL(clearH2Connections(), 1U);
1✔
1202
}
1✔
1203

1204
BOOST_FIXTURE_TEST_CASE(test_ShortRead, TestFixture)
1205
{
1✔
1206
  auto local = getBackendAddress("1", 80);
1✔
1207
  ClientState localCS(local, true, false, 0, "", {}, true);
1✔
1208
  auto tlsCtx = std::make_shared<MockupTLSCtx>();
1✔
1209
  localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx);
1✔
1210

1211
  struct timeval now;
1✔
1212
  gettimeofday(&now, nullptr);
1✔
1213

1214
  auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53));
1✔
1215
  backend->d_tlsCtx = tlsCtx;
1✔
1216
  backend->d_config.d_tlsSubjectName = "backend.powerdns.com";
1✔
1217
  backend->d_config.d_dohPath = "/dns-query";
1✔
1218
  backend->d_config.d_addXForwardedHeaders = true;
1✔
1219

1220
  size_t numberOfQueries = 2;
1✔
1221
  std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries;
1✔
1222
  for (size_t counter = 0; counter < numberOfQueries; counter++) {
3✔
1223
    DNSName name("powerdns.com.");
2✔
1224
    PacketBuffer query;
2✔
1225
    GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
2✔
1226
    pwQ.getHeader()->rd = 1;
2✔
1227
    pwQ.getHeader()->id = htons(counter);
2✔
1228

1229
    PacketBuffer response;
2✔
1230
    GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0);
2✔
1231
    pwR.getHeader()->qr = 1;
2✔
1232
    pwR.getHeader()->rd = 1;
2✔
1233
    pwR.getHeader()->ra = 1;
2✔
1234
    pwR.getHeader()->id = htons(counter);
2✔
1235
    pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
2✔
1236
    pwR.xfr32BitInt(0x01020304);
2✔
1237
    pwR.commit();
2✔
1238

1239
    s_responses[counter] = {query, response};
2✔
1240

1241
    auto sender = std::make_shared<MockupQuerySender>();
2✔
1242
    sender->d_id = counter;
2✔
1243
    InternalQuery internalQuery(std::move(query), InternalQueryState());
2✔
1244
    queries.push_back({std::move(sender), std::move(internalQuery)});
2✔
1245
  }
2✔
1246

1247
  bool done = false;
1✔
1248
  s_steps = {
1✔
1249
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
1250
    /* opening */
1251
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1252
    /* settings */
1253
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1254
    /* headers */
1255
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1256
    /* data */
1257
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
1258
       /* set the outgoing descriptor (backend connection) as ready */
1259
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
1260
     }},
1✔
1261
    /* headers */
1262
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1263
    /* data */
1264
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
1265
       /* set the outgoing descriptor (backend connection) as ready */
1266
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
1267
     }},
1✔
1268
    /* read settings, headers and responses from the server */
1269
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::NeedRead, 4},
1✔
1270
    /* read settings, headers and responses (second attempt) */
1271
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1272
    /* acknowledge settings */
1273
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [&done](int desc) {
1✔
1274
       /* mark backend as not ready */
1275
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setNotReady(desc);
1✔
1276
       done = true;
1✔
1277
     }},
1✔
1278
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
1279
  };
1✔
1280

1281
  for (auto& query : queries) {
2✔
1282
    auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first);
2✔
1283
    bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false);
2✔
1284
    BOOST_CHECK_EQUAL(result, true);
2✔
1285
  }
2✔
1286

1287
  while (!done && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) {
3!
1288
    s_mplexer->run(&now);
2✔
1289
  }
2✔
1290

1291
  for (auto& query : queries) {
2✔
1292
    BOOST_CHECK_EQUAL(query.first->d_valid, true);
2✔
1293
  }
2✔
1294

1295
  BOOST_CHECK_EQUAL(clearH2Connections(), 1U);
1✔
1296
}
1✔
1297

1298
BOOST_FIXTURE_TEST_CASE(test_ConnectionClosedWhileReading, TestFixture)
1299
{
1✔
1300
  auto local = getBackendAddress("1", 80);
1✔
1301
  ClientState localCS(local, true, false, 0, "", {}, true);
1✔
1302
  auto tlsCtx = std::make_shared<MockupTLSCtx>();
1✔
1303
  localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx);
1✔
1304

1305
  struct timeval now;
1✔
1306
  gettimeofday(&now, nullptr);
1✔
1307

1308
  auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53));
1✔
1309
  backend->d_tlsCtx = tlsCtx;
1✔
1310
  backend->d_config.d_tlsSubjectName = "backend.powerdns.com";
1✔
1311
  backend->d_config.d_dohPath = "/dns-query";
1✔
1312
  backend->d_config.d_addXForwardedHeaders = true;
1✔
1313

1314
  size_t numberOfQueries = 2;
1✔
1315
  std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries;
1✔
1316
  for (size_t counter = 0; counter < numberOfQueries; counter++) {
3✔
1317
    DNSName name("powerdns.com.");
2✔
1318
    PacketBuffer query;
2✔
1319
    GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
2✔
1320
    pwQ.getHeader()->rd = 1;
2✔
1321
    pwQ.getHeader()->id = htons(counter);
2✔
1322

1323
    PacketBuffer response;
2✔
1324
    GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0);
2✔
1325
    pwR.getHeader()->qr = 1;
2✔
1326
    pwR.getHeader()->rd = 1;
2✔
1327
    pwR.getHeader()->ra = 1;
2✔
1328
    pwR.getHeader()->id = htons(counter);
2✔
1329
    pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
2✔
1330
    pwR.xfr32BitInt(0x01020304);
2✔
1331
    pwR.commit();
2✔
1332

1333
    s_responses[counter] = {query, response};
2✔
1334

1335
    auto sender = std::make_shared<MockupQuerySender>();
2✔
1336
    sender->d_id = counter;
2✔
1337
    InternalQuery internalQuery(std::move(query), InternalQueryState());
2✔
1338
    queries.push_back({std::move(sender), std::move(internalQuery)});
2✔
1339
  }
2✔
1340

1341
  s_steps = {
1✔
1342
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
1343
    /* opening */
1344
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1345
    /* settings */
1346
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1347
    /* headers */
1348
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1349
    /* data */
1350
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
1351
       /* set the outgoing descriptor (backend connection) as ready */
1352
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
1353
     }},
1✔
1354
    /* headers */
1355
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1356
    /* data */
1357
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
1358
       /* set the outgoing descriptor (backend connection) as ready */
1359
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
1360
     }},
1✔
1361
    /* read settings, headers and responses from the server */
1362
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, 0},
1✔
1363
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
1364
  };
1✔
1365

1366
  for (auto& query : queries) {
2✔
1367
    auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first);
2✔
1368
    bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false);
2✔
1369
    BOOST_CHECK_EQUAL(result, true);
2✔
1370
  }
2✔
1371

1372
  while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) {
2!
1373
    s_mplexer->run(&now);
1✔
1374
  }
1✔
1375

1376
  for (auto& query : queries) {
2✔
1377
    BOOST_CHECK_EQUAL(query.first->d_valid, false);
2✔
1378
    BOOST_CHECK_EQUAL(query.first->d_error, true);
2✔
1379
  }
2✔
1380

1381
  BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
1✔
1382
}
1✔
1383

1384
BOOST_FIXTURE_TEST_CASE(test_ConnectionClosedWhileWriting, TestFixture)
1385
{
1✔
1386
  auto local = getBackendAddress("1", 80);
1✔
1387
  ClientState localCS(local, true, false, 0, "", {}, true);
1✔
1388
  auto tlsCtx = std::make_shared<MockupTLSCtx>();
1✔
1389
  localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx);
1✔
1390

1391
  struct timeval now;
1✔
1392
  gettimeofday(&now, nullptr);
1✔
1393

1394
  auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53));
1✔
1395
  backend->d_tlsCtx = tlsCtx;
1✔
1396
  backend->d_config.d_tlsSubjectName = "backend.powerdns.com";
1✔
1397
  backend->d_config.d_dohPath = "/dns-query";
1✔
1398
  backend->d_config.d_addXForwardedHeaders = true;
1✔
1399

1400
  size_t numberOfQueries = 2;
1✔
1401
  std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries;
1✔
1402
  for (size_t counter = 0; counter < numberOfQueries; counter++) {
3✔
1403
    DNSName name("powerdns.com.");
2✔
1404
    PacketBuffer query;
2✔
1405
    GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
2✔
1406
    pwQ.getHeader()->rd = 1;
2✔
1407
    pwQ.getHeader()->id = htons(counter);
2✔
1408

1409
    PacketBuffer response;
2✔
1410
    GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0);
2✔
1411
    pwR.getHeader()->qr = 1;
2✔
1412
    pwR.getHeader()->rd = 1;
2✔
1413
    pwR.getHeader()->ra = 1;
2✔
1414
    pwR.getHeader()->id = htons(counter);
2✔
1415
    pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
2✔
1416
    pwR.xfr32BitInt(0x01020304);
2✔
1417
    pwR.commit();
2✔
1418

1419
    s_responses[counter] = {query, response};
2✔
1420

1421
    auto sender = std::make_shared<MockupQuerySender>();
2✔
1422
    sender->d_id = counter;
2✔
1423
    InternalQuery internalQuery(std::move(query), InternalQueryState());
2✔
1424
    queries.push_back({std::move(sender), std::move(internalQuery)});
2✔
1425
  }
2✔
1426

1427
  bool done = false;
1✔
1428
  s_steps = {
1✔
1429
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
1430
    /* opening */
1431
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1432
    /* settings */
1433
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1434
    /* headers, connection is closed by the backend */
1435
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, 0},
1✔
1436
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
1437
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
1438
    /* opening */
1439
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1440
    /* settings */
1441
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1442
    /* headers */
1443
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1444
    /* data */
1445
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
1446
       /* set the outgoing descriptor (backend connection) as ready */
1447
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
1448
     }},
1✔
1449
    /* read settings, headers and response from the server */
1450
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1451
    /* acknowledge settings */
1452
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [&done](int desc) {
1✔
1453
       /* mark backend as not ready */
1454
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setNotReady(desc);
1✔
1455
       done = true;
1✔
1456
     }},
1✔
1457
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
1458
  };
1✔
1459

1460
  for (auto& query : queries) {
2✔
1461
    auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first);
2✔
1462
    bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false);
2✔
1463
    BOOST_CHECK_EQUAL(result, true);
2✔
1464
  }
2✔
1465

1466
  while (!done && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) {
2!
1467
    s_mplexer->run(&now);
1✔
1468
  }
1✔
1469

1470
  BOOST_CHECK_EQUAL(queries.at(0).first->d_valid, false);
1✔
1471
  BOOST_CHECK_EQUAL(queries.at(0).first->d_error, true);
1✔
1472
  BOOST_CHECK_EQUAL(queries.at(1).first->d_valid, true);
1✔
1473
  BOOST_CHECK_EQUAL(queries.at(1).first->d_error, false);
1✔
1474

1475
  BOOST_CHECK_EQUAL(clearH2Connections(), 1U);
1✔
1476
}
1✔
1477

1478
BOOST_FIXTURE_TEST_CASE(test_GoAwayFromServer, TestFixture)
1479
{
1✔
1480
  auto local = getBackendAddress("1", 80);
1✔
1481
  ClientState localCS(local, true, false, 0, "", {}, true);
1✔
1482
  auto tlsCtx = std::make_shared<MockupTLSCtx>();
1✔
1483
  localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx);
1✔
1484

1485
  struct timeval now;
1✔
1486
  gettimeofday(&now, nullptr);
1✔
1487

1488
  auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53));
1✔
1489
  backend->d_tlsCtx = tlsCtx;
1✔
1490
  backend->d_config.d_tlsSubjectName = "backend.powerdns.com";
1✔
1491
  backend->d_config.d_dohPath = "/dns-query";
1✔
1492
  backend->d_config.d_addXForwardedHeaders = true;
1✔
1493
  /* set the number of reconnection attempts to a low value to not waste time */
1494
  backend->d_config.d_retries = 1;
1✔
1495

1496
  size_t numberOfQueries = 2;
1✔
1497
  std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries;
1✔
1498
  for (size_t counter = 0; counter < numberOfQueries; counter++) {
3✔
1499
    DNSName name("goaway.powerdns.com.");
2✔
1500
    PacketBuffer query;
2✔
1501
    GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
2✔
1502
    pwQ.getHeader()->rd = 1;
2✔
1503
    pwQ.getHeader()->id = htons(counter);
2✔
1504

1505
    PacketBuffer response;
2✔
1506
    GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0);
2✔
1507
    pwR.getHeader()->qr = 1;
2✔
1508
    pwR.getHeader()->rd = 1;
2✔
1509
    pwR.getHeader()->ra = 1;
2✔
1510
    pwR.getHeader()->id = htons(counter);
2✔
1511
    pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
2✔
1512
    pwR.xfr32BitInt(0x01020304);
2✔
1513
    pwR.commit();
2✔
1514

1515
    s_responses[counter] = {query, response};
2✔
1516

1517
    auto sender = std::make_shared<MockupQuerySender>();
2✔
1518
    sender->d_id = counter;
2✔
1519
    InternalQuery internalQuery(std::move(query), InternalQueryState());
2✔
1520
    queries.push_back({std::move(sender), std::move(internalQuery)});
2✔
1521
  }
2✔
1522

1523
  s_steps = {
1✔
1524
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
1525
    /* opening */
1526
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1527
    /* settings */
1528
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1529
    /* headers */
1530
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1531
    /* data */
1532
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
1533
       /* set the outgoing descriptor (backend connection) as ready */
1534
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
1535
     }},
1✔
1536
    /* headers */
1537
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1538
    /* data */
1539
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
1540
       /* set the outgoing descriptor (backend connection) as ready */
1541
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
1542
     }},
1✔
1543
    /* read GO AWAY from the server (1) */
1544
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1545
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
1546
    /* opening */
1547
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1548
    /* settings */
1549
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1550
    /* headers */
1551
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1552
    /* data */
1553
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
1554
       /* set the outgoing descriptor (backend connection) as ready */
1555
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
1556
     }},
1✔
1557
    /* headers */
1558
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1559
    /* data */
1560
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
1561
       /* set the outgoing descriptor (backend connection) as ready */
1562
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
1563
     }},
1✔
1564
    /* close the first connection. It happens now because the new connection was set up first, then that one destroyed */
1565
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
1566
    /* read GO AWAY from the server (1) */
1567
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1568
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
1569
  };
1✔
1570

1571
  for (auto& query : queries) {
2✔
1572
    auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first);
2✔
1573
    bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false);
2✔
1574
    BOOST_CHECK_EQUAL(result, true);
2✔
1575
  }
2✔
1576

1577
  while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) {
3!
1578
    s_mplexer->run(&now);
2✔
1579
  }
2✔
1580

1581
  for (auto& query : queries) {
2✔
1582
    BOOST_CHECK_EQUAL(query.first->d_valid, false);
2✔
1583
    BOOST_CHECK_EQUAL(query.first->d_error, true);
2✔
1584
  }
2✔
1585

1586
  BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
1✔
1587
}
1✔
1588

1589
BOOST_FIXTURE_TEST_CASE(test_HTTP500FromServer, TestFixture)
1590
{
1✔
1591
  auto local = getBackendAddress("1", 80);
1✔
1592
  ClientState localCS(local, true, false, 0, "", {}, true);
1✔
1593
  auto tlsCtx = std::make_shared<MockupTLSCtx>();
1✔
1594
  localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx);
1✔
1595

1596
  struct timeval now;
1✔
1597
  gettimeofday(&now, nullptr);
1✔
1598

1599
  auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53));
1✔
1600
  backend->d_tlsCtx = tlsCtx;
1✔
1601
  backend->d_config.d_tlsSubjectName = "backend.powerdns.com";
1✔
1602
  backend->d_config.d_dohPath = "/dns-query";
1✔
1603
  backend->d_config.d_addXForwardedHeaders = true;
1✔
1604

1605
  size_t numberOfQueries = 2;
1✔
1606
  std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries;
1✔
1607
  for (size_t counter = 0; counter < numberOfQueries; counter++) {
3✔
1608
    DNSName name("500.powerdns.com.");
2✔
1609
    PacketBuffer query;
2✔
1610
    GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
2✔
1611
    pwQ.getHeader()->rd = 1;
2✔
1612
    pwQ.getHeader()->id = htons(counter);
2✔
1613

1614
    PacketBuffer response;
2✔
1615
    GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0);
2✔
1616
    pwR.getHeader()->qr = 1;
2✔
1617
    pwR.getHeader()->rd = 1;
2✔
1618
    pwR.getHeader()->ra = 1;
2✔
1619
    pwR.getHeader()->id = htons(counter);
2✔
1620
    pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
2✔
1621
    pwR.xfr32BitInt(0x01020304);
2✔
1622
    pwR.commit();
2✔
1623

1624
    s_responses[counter] = {query, response};
2✔
1625

1626
    auto sender = std::make_shared<MockupQuerySender>();
2✔
1627
    sender->d_id = counter;
2✔
1628
    InternalQuery internalQuery(std::move(query), InternalQueryState());
2✔
1629
    queries.push_back({std::move(sender), std::move(internalQuery)});
2✔
1630
  }
2✔
1631

1632
  bool done = false;
1✔
1633
  s_steps = {
1✔
1634
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
1635
    /* opening */
1636
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1637
    /* settings */
1638
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1639
    /* headers */
1640
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1641
    /* data */
1642
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
1643
       /* set the outgoing descriptor (backend connection) as ready */
1644
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
1645
     }},
1✔
1646
    /* headers */
1647
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1648
    /* data */
1649
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
1650
       /* set the outgoing descriptor (backend connection) as ready */
1651
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
1652
     }},
1✔
1653
    /* read settings, headers and responses from the server */
1654
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1655
    /* acknowledge settings */
1656
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [&done](int desc) {
1✔
1657
       /* mark backend as not ready */
1658
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setNotReady(desc);
1✔
1659
       done = true;
1✔
1660
     }},
1✔
1661
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
1662
  };
1✔
1663

1664
  for (auto& query : queries) {
2✔
1665
    auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first);
2✔
1666
    bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false);
2✔
1667
    BOOST_CHECK_EQUAL(result, true);
2✔
1668
  }
2✔
1669

1670
  while (!done && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) {
2!
1671
    s_mplexer->run(&now);
1✔
1672
  }
1✔
1673

1674
  BOOST_CHECK_EQUAL(queries.at(0).first->d_valid, false);
1✔
1675
  BOOST_CHECK_EQUAL(queries.at(0).first->d_error, true);
1✔
1676
  BOOST_CHECK_EQUAL(queries.at(1).first->d_valid, true);
1✔
1677
  BOOST_CHECK_EQUAL(queries.at(1).first->d_error, false);
1✔
1678

1679
  BOOST_CHECK_EQUAL(clearH2Connections(), 1U);
1✔
1680
}
1✔
1681

1682
BOOST_FIXTURE_TEST_CASE(test_WrongStreamID, TestFixture)
1683
{
1✔
1684
  auto local = getBackendAddress("1", 80);
1✔
1685
  ClientState localCS(local, true, false, 0, "", {}, true);
1✔
1686
  auto tlsCtx = std::make_shared<MockupTLSCtx>();
1✔
1687
  localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx);
1✔
1688

1689
  struct timeval now;
1✔
1690
  gettimeofday(&now, nullptr);
1✔
1691

1692
  auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53));
1✔
1693
  backend->d_tlsCtx = tlsCtx;
1✔
1694
  backend->d_config.d_tlsSubjectName = "backend.powerdns.com";
1✔
1695
  backend->d_config.d_dohPath = "/dns-query";
1✔
1696
  backend->d_config.d_addXForwardedHeaders = true;
1✔
1697

1698
  size_t numberOfQueries = 2;
1✔
1699
  std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries;
1✔
1700
  for (size_t counter = 0; counter < numberOfQueries; counter++) {
3✔
1701
    DNSName name("wrong-stream-id.powerdns.com.");
2✔
1702
    PacketBuffer query;
2✔
1703
    GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
2✔
1704
    pwQ.getHeader()->rd = 1;
2✔
1705
    pwQ.getHeader()->id = htons(counter);
2✔
1706

1707
    PacketBuffer response;
2✔
1708
    GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0);
2✔
1709
    pwR.getHeader()->qr = 1;
2✔
1710
    pwR.getHeader()->rd = 1;
2✔
1711
    pwR.getHeader()->ra = 1;
2✔
1712
    pwR.getHeader()->id = htons(counter);
2✔
1713
    pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
2✔
1714
    pwR.xfr32BitInt(0x01020304);
2✔
1715
    pwR.commit();
2✔
1716

1717
    s_responses[counter] = {query, response};
2✔
1718

1719
    auto sender = std::make_shared<MockupQuerySender>();
2✔
1720
    sender->d_id = counter;
2✔
1721
    InternalQuery internalQuery(std::move(query), InternalQueryState());
2✔
1722
    queries.push_back({std::move(sender), std::move(internalQuery)});
2✔
1723
  }
2✔
1724

1725
  bool timeout = false;
1✔
1726
  s_steps = {
1✔
1727
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
1728
    /* opening */
1729
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1730
    /* settings */
1731
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1732
    /* headers */
1733
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1734
    /* data */
1735
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
1736
       /* set the outgoing descriptor (backend connection) as ready */
1737
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
1738
     }},
1✔
1739
    /* headers */
1740
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1741
    /* data */
1742
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
1743
       /* set the outgoing descriptor (backend connection) as ready */
1744
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
1745
     }},
1✔
1746
    /* read settings, headers and responses from the server */
1747
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1748
    /* acknowledge settings */
1749
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1750
    /* read ends up as a time out since nghttp2 filters the frame with the wrong stream ID */
1751
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::NeedRead, 0, [&timeout](int desc) {
1✔
1752
       (void)desc;
1✔
1753
       /* set the timeout flag now, since the timeout occurs while waiting for the descriptor to become readable */
1754
       timeout = true;
1✔
1755
     }},
1✔
1756
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
1757
  };
1✔
1758

1759
  for (auto& query : queries) {
2✔
1760
    auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first);
2✔
1761
    bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false);
2✔
1762
    BOOST_CHECK_EQUAL(result, true);
2✔
1763
  }
2✔
1764

1765
  while (!timeout && (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0)) {
2!
1766
    s_mplexer->run(&now);
1✔
1767
  }
1✔
1768

1769
  struct timeval later = now;
1✔
1770
  later.tv_sec += backend->d_config.tcpRecvTimeout + 1;
1✔
1771

1772
  auto expiredConns = handleH2Timeouts(*s_mplexer, later);
1✔
1773
  BOOST_CHECK_EQUAL(expiredConns, 1U);
1✔
1774

1775
  BOOST_CHECK_EQUAL(queries.at(0).first->d_valid, false);
1✔
1776
  BOOST_CHECK_EQUAL(queries.at(0).first->d_error, true);
1✔
1777
  BOOST_CHECK_EQUAL(queries.at(1).first->d_valid, false);
1✔
1778
  BOOST_CHECK_EQUAL(queries.at(1).first->d_error, true);
1✔
1779

1780
  BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
1✔
1781
}
1✔
1782

1783
BOOST_FIXTURE_TEST_CASE(test_ProxyProtocol, TestFixture)
1784
{
1✔
1785
  auto local = getBackendAddress("1", 80);
1✔
1786
  ClientState localCS(local, true, false, 0, "", {}, true);
1✔
1787
  auto tlsCtx = std::make_shared<MockupTLSCtx>();
1✔
1788
  tlsCtx->d_needProxyProtocol = true;
1✔
1789
  localCS.tlsFrontend = std::make_shared<TLSFrontend>(tlsCtx);
1✔
1790

1791
  struct timeval now;
1✔
1792
  gettimeofday(&now, nullptr);
1✔
1793

1794
  auto backend = std::make_shared<DownstreamState>(getBackendAddress("42", 53));
1✔
1795
  backend->d_tlsCtx = tlsCtx;
1✔
1796
  backend->d_config.d_tlsSubjectName = "backend.powerdns.com";
1✔
1797
  backend->d_config.d_dohPath = "/dns-query";
1✔
1798
  backend->d_config.d_addXForwardedHeaders = true;
1✔
1799
  backend->d_config.useProxyProtocol = true;
1✔
1800

1801
  size_t numberOfQueries = 2;
1✔
1802
  std::vector<std::pair<std::shared_ptr<MockupQuerySender>, InternalQuery>> queries;
1✔
1803
  for (size_t counter = 0; counter < numberOfQueries; counter++) {
3✔
1804
    DNSName name("powerdns.com.");
2✔
1805
    PacketBuffer query;
2✔
1806
    GenericDNSPacketWriter<PacketBuffer> pwQ(query, name, QType::A, QClass::IN, 0);
2✔
1807
    pwQ.getHeader()->rd = 1;
2✔
1808
    pwQ.getHeader()->id = htons(counter);
2✔
1809

1810
    PacketBuffer response;
2✔
1811
    GenericDNSPacketWriter<PacketBuffer> pwR(response, name, QType::A, QClass::IN, 0);
2✔
1812
    pwR.getHeader()->qr = 1;
2✔
1813
    pwR.getHeader()->rd = 1;
2✔
1814
    pwR.getHeader()->ra = 1;
2✔
1815
    pwR.getHeader()->id = htons(counter);
2✔
1816
    pwR.startRecord(name, QType::A, 7200, QClass::IN, DNSResourceRecord::ANSWER);
2✔
1817
    pwR.xfr32BitInt(0x01020304);
2✔
1818
    pwR.commit();
2✔
1819

1820
    s_responses[counter] = {query, response};
2✔
1821

1822
    auto sender = std::make_shared<MockupQuerySender>();
2✔
1823
    sender->d_id = counter;
2✔
1824
    std::string payload = makeProxyHeader(counter % 2, local, local, {});
2✔
1825
    InternalQuery internalQuery(std::move(query), InternalQueryState());
2✔
1826
    internalQuery.d_proxyProtocolPayload = std::move(payload);
2✔
1827
    queries.push_back({std::move(sender), std::move(internalQuery)});
2✔
1828
  }
2✔
1829

1830
  s_steps = {
1✔
1831
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
1832
    /* proxy protocol data + opening */
1833
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1834
    /* settings */
1835
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1836
    /* headers */
1837
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1838
    /* data */
1839
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
1840
       /* set the outgoing descriptor (backend connection) as ready */
1841
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
1842
     }},
1✔
1843
    {ExpectedStep::ExpectedRequest::connectToBackend, IOState::Done},
1✔
1844
    /* proxy protocol data + opening */
1845
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1846
    /* settings */
1847
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1848
    /* headers */
1849
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1850
    /* data */
1851
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max(), [](int desc) {
1✔
1852
       /* set the outgoing descriptor (backend connection) as ready */
1853
       dynamic_cast<MockupFDMultiplexer*>(s_mplexer.get())->setReady(desc);
1✔
1854
     }},
1✔
1855
    /* read settings, headers and responses from the server */
1856
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1857
    /* acknowledge settings */
1858
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1859
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
1860
    /* read settings, headers and responses from the server */
1861
    {ExpectedStep::ExpectedRequest::readFromBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1862
    /* acknowledge settings */
1863
    {ExpectedStep::ExpectedRequest::writeToBackend, IOState::Done, std::numeric_limits<size_t>::max()},
1✔
1864
    {ExpectedStep::ExpectedRequest::closeBackend, IOState::Done},
1✔
1865
  };
1✔
1866

1867
  for (auto& query : queries) {
2✔
1868
    auto sliced = std::static_pointer_cast<TCPQuerySender>(query.first);
2✔
1869
    bool result = sendH2Query(backend, s_mplexer, sliced, std::move(query.second), false);
2✔
1870
    BOOST_CHECK_EQUAL(result, true);
2✔
1871
  }
2✔
1872

1873
  while (s_mplexer->getWatchedFDCount(false) != 0 || s_mplexer->getWatchedFDCount(true) != 0) {
2!
1874
    s_mplexer->run(&now);
1✔
1875
  }
1✔
1876

1877
  for (auto& query : queries) {
2✔
1878
    BOOST_CHECK_EQUAL(query.first->d_valid, true);
2✔
1879
  }
2✔
1880

1881
  BOOST_CHECK_EQUAL(clearH2Connections(), 0U);
1✔
1882
}
1✔
1883

1884
BOOST_AUTO_TEST_SUITE_END();
1885
#endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc