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

PowerDNS / pdns / 18748609987

23 Oct 2025 12:38PM UTC coverage: 73.046% (+4.3%) from 68.781%
18748609987

Pull #16362

github

web-flow
Merge fb6974bbe into dec9583d8
Pull Request #16362: rec: implements new feature to only generate OpenTelemtry Traces on certain conditions

38418 of 63316 branches covered (60.68%)

Branch coverage included in aggregate %.

122 of 136 new or added lines in 11 files covered. (89.71%)

53 existing lines in 11 files now uncovered.

127614 of 163983 relevant lines covered (77.82%)

5916487.93 hits per line

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

60.29
/pdns/tcpiohandler.cc
1

2
#include "config.h"
3
#include "dolog.hh"
4
#include "iputils.hh"
5
#include "lock.hh"
6
#include "tcpiohandler.hh"
7

8
const bool TCPIOHandler::s_disableConnectForUnitTests = false;
9

10
#ifdef HAVE_LIBSODIUM
11
#include <sodium.h>
12
#endif /* HAVE_LIBSODIUM */
13

14
TLSCtx::tickets_key_added_hook TLSCtx::s_ticketsKeyAddedHook{nullptr};
15

16
#if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
17
static std::vector<std::vector<uint8_t>> getALPNVector(TLSFrontend::ALPN alpn, bool client)
18
{
150✔
19
  if (alpn == TLSFrontend::ALPN::DoT) {
150✔
20
    /* we want to set the ALPN to dot (RFC7858), if only to mitigate the ALPACA attack */
21
    return std::vector<std::vector<uint8_t>>{{'d', 'o', 't'}};
60✔
22
  }
60✔
23
  if (alpn == TLSFrontend::ALPN::DoH) {
90✔
24
    if (client) {
67✔
25
      /* we want to set the ALPN to h2, if only to mitigate the ALPACA attack */
26
      return std::vector<std::vector<uint8_t>>{{'h', '2'}};
28✔
27
    }
28✔
28
    /* For server contexts, we want to set the ALPN for DoH (note that h2o sets it own ALPN values):
29
       - HTTP/1.1 so that the OpenSSL callback ALPN accepts it, letting us later return a static response
30
       - HTTP/2
31
    */
32
    return std::vector<std::vector<uint8_t>>{{'h', '2'},{'h', 't', 't', 'p', '/', '1', '.', '1'}};
39✔
33
  }
67✔
34
  return {};
23✔
35
}
90✔
36

37
#ifdef HAVE_LIBSSL
38

39
namespace {
40
bool shouldDoVerboseLogging()
41
{
8✔
42
#ifdef DNSDIST
7✔
43
  return dnsdist::configuration::getCurrentRuntimeConfiguration().d_verbose;
7✔
44
#elif defined(RECURSOR)
45
  return false;
1✔
46
#else
47
  return true;
48
#endif
49
}
8✔
50
}
51

52
#include <openssl/conf.h>
53
#include <openssl/err.h>
54
#include <openssl/rand.h>
55
#include <openssl/ssl.h>
56
#include <openssl/x509v3.h>
57

58
#include "libssl.hh"
59

60
static int sni_server_name_callback(SSL* ssl, int* /* alert */, void* arg);
61

62
class OpenSSLFrontendContext
63
{
64
public:
65
  OpenSSLFrontendContext(const ComboAddress& addr, const TLSConfig& tlsConfig): d_ticketKeys(tlsConfig.d_numberOfTicketsKeys)
73✔
66
  {
73✔
67
    registerOpenSSLUser();
73✔
68

69
    auto [ctx, warnings] = libssl_init_server_context(tlsConfig);
73✔
70
    for (const auto& warning : warnings) {
73✔
71
      warnlog("%s", warning);
2✔
72
    }
2✔
73
    // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer): it cannot be initialized before calling libssl_init_server_context()
74
    d_ocspResponses = std::move(ctx.d_ocspResponses);
73✔
75
    // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer): it cannot be initialized before calling libssl_init_server_context()
76
    d_tlsCtx = std::move(ctx.d_defaultContext);
73✔
77
    // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer): it cannot be initialized before calling libssl_init_server_context()
78
    d_sniMap = std::move(ctx.d_sniMap);
73✔
79
    for (auto& entry : d_sniMap) {
148✔
80
      SSL_CTX_set_tlsext_servername_callback(entry.second.get(), &sni_server_name_callback);
148✔
81
    }
148✔
82

83
    if (!d_tlsCtx) {
73!
84
      ERR_print_errors_fp(stderr);
×
85
      throw std::runtime_error("Error creating TLS context on " + addr.toStringWithPort());
×
86
    }
×
87
  }
73✔
88

89
  void cleanup()
90
  {
×
91
    d_tlsCtx.reset();
×
92

×
93
    unregisterOpenSSLUser();
×
94
  }
×
95

96
  OpenSSLTLSTicketKeysRing d_ticketKeys;
97
  std::map<int, std::string> d_ocspResponses;
98
  pdns::libssl::ServerContext::SNIToContextMap d_sniMap;
99
  std::shared_ptr<SSL_CTX> d_tlsCtx{nullptr};
100
  pdns::UniqueFilePtr d_keyLogFile{nullptr};
101
};
102

103

104
static int sni_server_name_callback(SSL* ssl, int* /* alert */, void* /* arg */)
105
{
551✔
106
  const auto* serverName = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
551✔
107
  if (serverName == nullptr) {
551✔
108
    return SSL_TLSEXT_ERR_NOACK;
3✔
109
  }
3✔
110
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): OpenSSL's API
111
  auto* frontendCtx = reinterpret_cast<OpenSSLFrontendContext*>(libssl_get_ticket_key_callback_data(ssl));
548✔
112
  if (frontendCtx == nullptr) {
548✔
113
    return SSL_TLSEXT_ERR_OK;
4✔
114
  }
4✔
115

116
  auto serverNameView = std::string_view(serverName);
544✔
117

118
  auto mapIt = frontendCtx->d_sniMap.find(serverNameView);
544✔
119
  if (mapIt == frontendCtx->d_sniMap.end()) {
544✔
120
    /* keep the default certificate */
121
    return SSL_TLSEXT_ERR_OK;
1✔
122
  }
1✔
123

124
  /* if it fails there is nothing we can do,
125
     let's hope OpenSSL will fall back to the existing,
126
     default certificate*/
127
  SSL_set_SSL_CTX(ssl, mapIt->second.get());
543✔
128
  return SSL_TLSEXT_ERR_OK;
543✔
129
}
544✔
130

131
class OpenSSLSession : public TLSSession
132
{
133
public:
134
  OpenSSLSession(std::unique_ptr<SSL_SESSION, void(*)(SSL_SESSION*)>&& sess): d_sess(std::move(sess))
131✔
135
  {
153✔
136
  }
153✔
137

138
  std::unique_ptr<SSL_SESSION, void(*)(SSL_SESSION*)> getNative()
139
  {
81✔
140
    return std::move(d_sess);
81✔
141
  }
81✔
142

143
private:
144
  std::unique_ptr<SSL_SESSION, void(*)(SSL_SESSION*)> d_sess;
145
};
146

147
class OpenSSLTLSIOCtx;
148

149
class OpenSSLTLSConnection: public TLSConnection
150
{
151
public:
152
  /* server side connection */
153
  OpenSSLTLSConnection(int socket, const struct timeval& timeout, std::shared_ptr<const OpenSSLTLSIOCtx> tlsCtx, std::unique_ptr<SSL, void(*)(SSL*)>&& conn): d_tlsCtx(std::move(tlsCtx)), d_conn(std::move(conn)), d_timeout(timeout)
553✔
154
  {
553✔
155
    d_socket = socket;
553✔
156

157
    if (!d_conn) {
553!
158
      vinfolog("Error creating TLS object");
×
159
      if (shouldDoVerboseLogging()) {
×
160
        ERR_print_errors_fp(stderr);
×
161
      }
×
162
      throw std::runtime_error("Error creating TLS object");
×
163
    }
×
164

165
    if (!SSL_set_fd(d_conn.get(), d_socket)) {
553!
166
      throw std::runtime_error("Error assigning socket");
×
167
    }
×
168

169
    SSL_set_ex_data(d_conn.get(), getConnectionIndex(), this);
553✔
170
  }
553✔
171

172
  /* client-side connection */
173
  OpenSSLTLSConnection(std::string hostname, bool hostIsAddr, int socket, const struct timeval& timeout, std::shared_ptr<const OpenSSLTLSIOCtx> tlsCtx, std::unique_ptr<SSL, void(*)(SSL*)>&& conn): d_tlsCtx(std::move(tlsCtx)), d_conn(std::move(conn)), d_hostname(std::move(hostname)), d_timeout(timeout), d_isClient(true)
134✔
174
  {
146✔
175
    d_socket = socket;
146✔
176

177
    if (!d_conn) {
146!
178
      vinfolog("Error creating TLS object");
×
179
      if (shouldDoVerboseLogging()) {
×
180
        ERR_print_errors_fp(stderr);
×
181
      }
×
182
      throw std::runtime_error("Error creating TLS object");
×
183
    }
×
184

185
    if (!SSL_set_fd(d_conn.get(), d_socket)) {
146!
186
      throw std::runtime_error("Error assigning socket");
×
187
    }
×
188

189
    /* set outgoing Server Name Indication */
190
    if (!d_hostname.empty() && SSL_set_tlsext_host_name(d_conn.get(), d_hostname.c_str()) != 1) {
146!
191
      throw std::runtime_error("Error setting TLS SNI to " + d_hostname);
×
192
    }
×
193

194
    if (hostIsAddr) {
146✔
195
#if (OPENSSL_VERSION_NUMBER >= 0x10002000L)
24✔
196
      X509_VERIFY_PARAM *param = SSL_get0_param(d_conn.get());
24✔
197
      /* Enable automatic IP checks */
198
      X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
24✔
199
      if (X509_VERIFY_PARAM_set1_ip_asc(param, d_hostname.c_str()) != 1) {
24!
200
        throw std::runtime_error("Error setting TLS IP for certificate validation");
×
201
      }
×
202
#else
203
      /* no validation for you, see https://wiki.openssl.org/index.php/Hostname_validation */
204
#endif
205
    }
24✔
206
    else {
122✔
207
#if (OPENSSL_VERSION_NUMBER >= 0x1010000fL) && defined(HAVE_SSL_SET_HOSTFLAGS) // grrr libressl
122✔
208
      SSL_set_hostflags(d_conn.get(), X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
122✔
209
      if (SSL_set1_host(d_conn.get(), d_hostname.c_str()) != 1) {
122!
210
        throw std::runtime_error("Error setting TLS hostname for certificate validation");
×
211
      }
×
212
#elif (OPENSSL_VERSION_NUMBER >= 0x10002000L)
213
      X509_VERIFY_PARAM *param = SSL_get0_param(d_conn.get());
214
      /* Enable automatic hostname checks */
215
      X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
216
      if (X509_VERIFY_PARAM_set1_host(param, d_hostname.c_str(), d_hostname.size()) != 1) {
217
        throw std::runtime_error("Error setting TLS hostname for certificate validation");
218
      }
219
#else
220
      /* no hostname validation for you, see https://wiki.openssl.org/index.php/Hostname_validation */
221
#endif
222
    }
122✔
223

224
    SSL_set_ex_data(d_conn.get(), getConnectionIndex(), this);
146✔
225
  }
146✔
226

227
  std::vector<int> getAsyncFDs() override
228
  {
352✔
229
    std::vector<int> results;
352✔
230
#ifdef SSL_MODE_ASYNC
352✔
231
    if (SSL_waiting_for_async(d_conn.get()) != 1) {
352!
232
      return results;
352✔
233
    }
352✔
234

235
    OSSL_ASYNC_FD fds[32];
352✔
UNCOV
236
    size_t numfds = sizeof(fds)/sizeof(*fds);
×
UNCOV
237
    SSL_get_all_async_fds(d_conn.get(), nullptr, &numfds);
×
UNCOV
238
    if (numfds == 0) {
×
239
      return results;
×
240
    }
×
241

UNCOV
242
    SSL_get_all_async_fds(d_conn.get(), fds, &numfds);
×
UNCOV
243
    results.reserve(numfds);
×
UNCOV
244
    for (size_t idx = 0; idx < numfds; idx++) {
×
245
      results.push_back(fds[idx]);
×
246
    }
×
UNCOV
247
#endif
×
UNCOV
248
    return results;
×
UNCOV
249
  }
×
250

251
  IOState convertIORequestToIOState(int res) const
252
  {
2,430✔
253
    int error = SSL_get_error(d_conn.get(), res);
2,430✔
254
    if (error == SSL_ERROR_WANT_READ) {
2,430✔
255
      return IOState::NeedRead;
1,831✔
256
    }
1,831✔
257
    else if (error == SSL_ERROR_WANT_WRITE) {
599✔
258
      return IOState::NeedWrite;
22✔
259
    }
22✔
260
    else if (error == SSL_ERROR_SYSCALL) {
577✔
261
      if (errno == 0) {
24!
262
        throw std::runtime_error("TLS connection closed by remote end");
×
263
      }
×
264
      else {
24✔
265
        throw std::runtime_error("Syscall error while processing TLS connection: " + std::string(strerror(errno)));
24✔
266
      }
24✔
267
    }
24✔
268
    else if (error == SSL_ERROR_ZERO_RETURN) {
553✔
269
      throw std::runtime_error("TLS connection closed by remote end");
545✔
270
    }
545✔
271
#ifdef SSL_MODE_ASYNC
8✔
272
    else if (error == SSL_ERROR_WANT_ASYNC) {
8!
273
      return IOState::Async;
×
274
    }
×
275
#endif
8✔
276
    else {
8✔
277
      if (shouldDoVerboseLogging()) {
8✔
278
        throw std::runtime_error("Error while processing TLS connection: (" + std::to_string(error) + ") " + libssl_get_error_string());
1✔
279
      } else {
7✔
280
        throw std::runtime_error("Error while processing TLS connection: " + std::to_string(error));
7✔
281
      }
7✔
282
    }
8✔
283
  }
2,430✔
284

285
  void handleIORequest(int res, const struct timeval& timeout)
286
  {
5✔
287
    auto state = convertIORequestToIOState(res);
5✔
288
    if (state == IOState::NeedRead) {
5✔
289
      res = waitForData(d_socket, timeout);
4✔
290
      if (res == 0) {
4!
291
        throw std::runtime_error("Timeout while reading from TLS connection");
×
292
      }
×
293
      else if (res < 0) {
4!
294
        throw std::runtime_error("Error waiting to read from TLS connection");
×
295
      }
×
296
    }
4✔
297
    else if (state == IOState::NeedWrite) {
1!
298
      res = waitForRWData(d_socket, false, timeout);
×
299
      if (res == 0) {
×
300
        throw std::runtime_error("Timeout while writing to TLS connection");
×
301
      }
×
302
      else if (res < 0) {
×
303
        throw std::runtime_error("Error waiting to write to TLS connection");
×
304
      }
×
305
    }
×
306
  }
5✔
307

308
  IOState tryConnect(bool fastOpen, const ComboAddress& remote) override
309
  {
139✔
310
    /* sorry */
311
    (void) fastOpen;
139✔
312
    (void) remote;
139✔
313

314
    int res = SSL_connect(d_conn.get());
139✔
315
    if (res == 1) {
139!
316
      return IOState::Done;
×
317
    }
×
318
    else if (res < 0) {
139!
319
      return convertIORequestToIOState(res);
139✔
320
    }
139✔
321

322
    throw std::runtime_error("Error establishing a TLS connection");
×
323
  }
139✔
324

325
  void connect(bool fastOpen, const ComboAddress& remote, const struct timeval &timeout) override
326
  {
4✔
327
    /* sorry */
328
    (void) fastOpen;
4✔
329
    (void) remote;
4✔
330

331
    struct timeval start{0,0};
4✔
332
    struct timeval remainingTime = timeout;
4✔
333
    if (timeout.tv_sec != 0 || timeout.tv_usec != 0) {
4!
334
      gettimeofday(&start, nullptr);
4✔
335
    }
4✔
336

337
    int res = 0;
4✔
338
    do {
8✔
339
      res = SSL_connect(d_conn.get());
8✔
340
      if (res < 0) {
8✔
341
        handleIORequest(res, remainingTime);
5✔
342
      }
5✔
343

344
      if (timeout.tv_sec != 0 || timeout.tv_usec != 0) {
8!
345
        struct timeval now;
7✔
346
        gettimeofday(&now, nullptr);
7✔
347
        struct timeval elapsed = now - start;
7✔
348
        if (now < start || remainingTime < elapsed) {
7!
349
          throw runtime_error("Timeout while establishing TLS connection");
×
350
        }
×
351
        start = now;
7✔
352
        remainingTime = remainingTime - elapsed;
7✔
353
      }
7✔
354
    }
8✔
355
    while (res != 1);
8✔
356
  }
4✔
357

358
  IOState tryHandshake() override
359
  {
1,105✔
360
    if (isClient()) {
1,105!
361
      /* In client mode, the handshake is initiated by the call to SSL_connect()
362
         done from connect()/tryConnect().
363
         In blocking mode it does not return before the handshake has been finished,
364
         and in non-blocking mode calling SSL_connect() once is enough for SSL_write()
365
         and SSL_read() to transparently continue to negotiate the connection after that
366
         (equivalent to doing SSL_set_connect_state() plus trying to write).
367
      */
368
      return IOState::Done;
×
369
    }
×
370

371
    /* As explained above in the client-mode block, we only need to call SSL_accept() once
372
       for SSL_write() and SSL_read() to transparently continue to negotiate the connection after that.
373
       It is equivalent to calling SSL_set_accept_state() plus trying to read.
374
    */
375
    int res = SSL_accept(d_conn.get());
1,105✔
376
    if (res == 1) {
1,105✔
377
      return IOState::Done;
551✔
378
    }
551✔
379
    else if (res < 0) {
554!
380
      return convertIORequestToIOState(res);
554✔
381
    }
554✔
382

383
    throw std::runtime_error("Error accepting TLS connection");
×
384
  }
1,105✔
385

386
  void doHandshake() override
387
  {
×
388
    if (isClient()) {
×
389
      /* we are a client, nothing to do, see the non-blocking version */
390
      return;
×
391
    }
×
392

393
    int res = 0;
×
394
    do {
×
395
      res = SSL_accept(d_conn.get());
×
396
      if (res < 0) {
×
397
        handleIORequest(res, d_timeout);
×
398
      }
×
399
    }
×
400
    while (res < 0);
×
401

402
    if (res != 1) {
×
403
      throw std::runtime_error("Error accepting TLS connection");
×
404
    }
×
405
  }
×
406

407
  IOState tryWrite(const PacketBuffer& buffer, size_t& pos, size_t toWrite) override
408
  {
1,635✔
409
    if (isClient() && !d_connected) {
1,635✔
410
      if (d_ktls) {
248!
411
        /* work-around to get kTLS to be started, as we cannot do that until after the socket has been connected */
412
        SSL_set_fd(d_conn.get(), SSL_get_fd(d_conn.get()));
×
413
      }
×
414
    }
248✔
415

416
    do {
1,635✔
417
      int res = SSL_write(d_conn.get(), reinterpret_cast<const char *>(&buffer.at(pos)), static_cast<int>(toWrite - pos));
1,635✔
418
      if (res <= 0) {
1,635✔
419
        return convertIORequestToIOState(res);
142✔
420
      }
142✔
421
      else {
1,493✔
422
        pos += static_cast<size_t>(res);
1,493✔
423
      }
1,493✔
424
    }
1,635✔
425
    while (pos < toWrite);
1,635!
426

427
    if (!d_connected) {
1,493✔
428
      d_connected = true;
626✔
429
    }
626✔
430

431
    return IOState::Done;
1,493✔
432
  }
1,635✔
433

434
  IOState tryRead(PacketBuffer& buffer, size_t& pos, size_t toRead, bool allowIncomplete) override
435
  {
3,841✔
436
    do {
3,843✔
437
      int res = SSL_read(d_conn.get(), reinterpret_cast<char *>(&buffer.at(pos)), static_cast<int>(toRead - pos));
3,843✔
438
      if (res <= 0) {
3,843✔
439
        return convertIORequestToIOState(res);
1,587✔
440
      }
1,587✔
441
      else {
2,256✔
442
        pos += static_cast<size_t>(res);
2,256✔
443
        if (allowIncomplete) {
2,256✔
444
          break;
786✔
445
        }
786✔
446
      }
2,256✔
447
    }
3,843✔
448
    while (pos < toRead);
3,841✔
449
    return IOState::Done;
2,254✔
450
  }
3,841✔
451

452
  size_t read(void* buffer, size_t bufferSize, const struct timeval& readTimeout, const struct timeval& totalTimeout, bool allowIncomplete) override
453
  {
×
454
    size_t got = 0;
×
455
    struct timeval start = {0, 0};
×
456
    struct timeval remainingTime = totalTimeout;
×
457
    if (totalTimeout.tv_sec != 0 || totalTimeout.tv_usec != 0) {
×
458
      gettimeofday(&start, nullptr);
×
459
    }
×
460

461
    do {
×
462
      int res = SSL_read(d_conn.get(), (reinterpret_cast<char *>(buffer) + got), static_cast<int>(bufferSize - got));
×
463
      if (res <= 0) {
×
464
        handleIORequest(res, readTimeout);
×
465
      }
×
466
      else {
×
467
        got += static_cast<size_t>(res);
×
468
        if (allowIncomplete) {
×
469
          break;
×
470
        }
×
471
      }
×
472

473
      if (totalTimeout.tv_sec != 0 || totalTimeout.tv_usec != 0) {
×
474
        struct timeval now;
×
475
        gettimeofday(&now, nullptr);
×
476
        struct timeval elapsed = now - start;
×
477
        if (now < start || remainingTime < elapsed) {
×
478
          throw runtime_error("Timeout while reading data");
×
479
        }
×
480
        start = now;
×
481
        remainingTime = remainingTime - elapsed;
×
482
      }
×
483
    }
×
484
    while (got < bufferSize);
×
485

486
    return got;
×
487
  }
×
488

489
  size_t write(const void* buffer, size_t bufferSize, const struct timeval& writeTimeout) override
490
  {
×
491
    size_t got = 0;
×
492
    do {
×
493
      int res = SSL_write(d_conn.get(), (reinterpret_cast<const char *>(buffer) + got), static_cast<int>(bufferSize - got));
×
494
      if (res <= 0) {
×
495
        handleIORequest(res, writeTimeout);
×
496
      }
×
497
      else {
×
498
        got += static_cast<size_t>(res);
×
499
      }
×
500
    }
×
501
    while (got < bufferSize);
×
502

503
    return got;
×
504
  }
×
505

506
  bool isUsable() const override
507
  {
3✔
508
    if (!d_conn) {
3!
509
      return false;
×
510
    }
×
511

512
    char buf;
3✔
513
    int res = SSL_peek(d_conn.get(), &buf, sizeof(buf));
3✔
514
    if (res > 0) {
3!
515
      return true;
×
516
    }
×
517
    try {
3✔
518
      convertIORequestToIOState(res);
3✔
519
      return true;
3✔
520
    }
3✔
521
    catch (...) {
3✔
522
      return false;
1✔
523
    }
1✔
524

525
    return false;
×
526
  }
3✔
527

528
  void close() override
529
  {
675✔
530
    if (d_conn) {
675!
531
      SSL_shutdown(d_conn.get());
675✔
532
    }
675✔
533
  }
675✔
534

535
  std::string getServerNameIndication() const override
536
  {
793✔
537
    if (d_conn) {
793!
538
      const char* value = SSL_get_servername(d_conn.get(), TLSEXT_NAMETYPE_host_name);
793✔
539
      if (value) {
793✔
540
        return std::string(value);
790✔
541
      }
790✔
542
    }
793✔
543
    return std::string();
3✔
544
  }
793✔
545

546
  std::vector<uint8_t> getNextProtocol() const override
547
  {
198✔
548
    std::vector<uint8_t> result;
198✔
549
    if (!d_conn) {
198!
550
      return result;
×
551
    }
×
552

553
    const unsigned char* alpn = nullptr;
198✔
554
    unsigned int alpnLen  = 0;
198✔
555
#ifdef HAVE_SSL_GET0_ALPN_SELECTED
198✔
556
    if (alpn == nullptr) {
198!
557
      SSL_get0_alpn_selected(d_conn.get(), &alpn, &alpnLen);
198✔
558
    }
198✔
559
#endif /* HAVE_SSL_GET0_ALPN_SELECTED */
198✔
560
    if (alpn != nullptr && alpnLen > 0) {
198!
561
      result.insert(result.end(), alpn, alpn + alpnLen);
175✔
562
    }
175✔
563
    return result;
198✔
564
  }
198✔
565

566
  [[nodiscard]] std::pair<long, std::string> getVerifyResult() const override
567
  {
1✔
568
    if (d_conn) {
1!
569
      auto errorCode = SSL_get_verify_result(d_conn.get());
1✔
570
      auto certPresented = errorCode != X509_V_OK;
1✔
571
      if (!certPresented) {
1!
572
        auto* cert = SSL_get_peer_certificate(d_conn.get());
×
573
        if (cert != nullptr) {
×
574
          certPresented = true;
×
575
          X509_free(cert);
×
576
        }
×
577
      }
×
578
      if (!certPresented) {
1!
579
        return {-1, "No certificate presented by peer"};
×
580
      }
×
581
      const auto* errorMsg = X509_verify_cert_error_string(errorCode);
1✔
582
      return {errorCode, errorMsg != nullptr ? errorMsg : "No details available"};
1!
583
    }
1✔
584
    return {0, ""};
×
585
  }
1✔
586

587
  LibsslTLSVersion getTLSVersion() const override
588
  {
799✔
589
    auto proto = SSL_version(d_conn.get());
799✔
590
    switch (proto) {
799✔
591
    case TLS1_VERSION:
×
592
      return LibsslTLSVersion::TLS10;
×
593
    case TLS1_1_VERSION:
×
594
      return LibsslTLSVersion::TLS11;
×
595
    case TLS1_2_VERSION:
11✔
596
      return LibsslTLSVersion::TLS12;
11✔
597
#ifdef TLS1_3_VERSION
×
598
    case TLS1_3_VERSION:
788✔
599
      return LibsslTLSVersion::TLS13;
788✔
600
#endif /* TLS1_3_VERSION */
×
601
    default:
×
602
      return LibsslTLSVersion::Unknown;
×
603
    }
799✔
604
  }
799✔
605

606
  bool hasSessionBeenResumed() const override
607
  {
613✔
608
    if (d_conn) {
613!
609
      return SSL_session_reused(d_conn.get()) != 0;
613✔
610
    }
613✔
611
    return false;
×
612
  }
613✔
613

614
  std::vector<std::unique_ptr<TLSSession>> getSessions() override
615
  {
79✔
616
    return std::move(d_tlsSessions);
79✔
617
  }
79✔
618

619
  void setSession(std::unique_ptr<TLSSession>& session) override
620
  {
81✔
621
    auto sess = dynamic_cast<OpenSSLSession*>(session.get());
81✔
622
    if (!sess) {
81!
623
      throw std::runtime_error("Unable to convert OpenSSL session");
×
624
    }
×
625

626
    auto native = sess->getNative();
81✔
627
    auto ret = SSL_set_session(d_conn.get(), native.get());
81✔
628
    if (ret != 1) {
81!
629
      throw std::runtime_error("Error setting up session: " + libssl_get_error_string());
×
630
    }
×
631
    session.reset();
81✔
632
  }
81✔
633

634
  void addNewTicket(SSL_SESSION* session)
635
  {
153✔
636
    d_tlsSessions.push_back(std::make_unique<OpenSSLSession>(std::unique_ptr<SSL_SESSION, void (*)(SSL_SESSION*)>(session, SSL_SESSION_free)));
153✔
637
  }
153✔
638

639
  void enableKTLS()
640
  {
×
641
    d_ktls = true;
×
642
  }
×
643

644
  [[nodiscard]] bool isClient() const
645
  {
2,740✔
646
    return d_isClient;
2,740✔
647
  }
2,740✔
648

649
  static void generateConnectionIndexIfNeeded()
650
  {
122✔
651
    auto init = s_initTLSConnIndex.lock();
122✔
652
    if (*init == true) {
122✔
653
      return;
42✔
654
    }
42✔
655

656
    /* not initialized yet */
657
    s_tlsConnIndex = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);
80✔
658
    if (s_tlsConnIndex == -1) {
80!
659
      throw std::runtime_error("Error getting an index for TLS connection data");
×
660
    }
×
661

662
    *init = true;
80✔
663
  }
80✔
664

665
  static int getConnectionIndex()
666
  {
866✔
667
    return s_tlsConnIndex;
866✔
668
  }
866✔
669

670
private:
671
  static LockGuarded<bool> s_initTLSConnIndex;
672
  static int s_tlsConnIndex;
673
  std::vector<std::unique_ptr<TLSSession>> d_tlsSessions;
674
  const std::shared_ptr<const OpenSSLTLSIOCtx> d_tlsCtx; // we need to hold a reference to this to make sure that the context exists for as long as the connection, even if a reload happens in the meantime
675
  std::unique_ptr<SSL, void(*)(SSL*)> d_conn;
676
  const std::string d_hostname;
677
  const timeval d_timeout;
678
  bool d_connected{false};
679
  bool d_ktls{false};
680
  const bool d_isClient{false};
681
};
682

683
LockGuarded<bool> OpenSSLTLSConnection::s_initTLSConnIndex{false};
684
int OpenSSLTLSConnection::s_tlsConnIndex{-1};
685

686
class OpenSSLTLSIOCtx: public TLSCtx, public std::enable_shared_from_this<OpenSSLTLSIOCtx>
687
{
688
  struct Private
689
  {
690
    explicit Private() = default;
691
  };
692

693
public:
694
  static std::shared_ptr<OpenSSLTLSIOCtx> createServerSideContext(TLSFrontend& frontend)
695
  {
73✔
696
    return std::make_shared<OpenSSLTLSIOCtx>(frontend, Private());
73✔
697
  }
73✔
698

699
  static std::shared_ptr<OpenSSLTLSIOCtx> createClientSideContext(const TLSContextParameters& params)
700
  {
49✔
701
    return std::make_shared<OpenSSLTLSIOCtx>(params, Private());
49✔
702
  }
49✔
703

704
  /* server side context */
705
  OpenSSLTLSIOCtx(TLSFrontend& frontend, [[maybe_unused]] Private priv): d_alpnProtos(getALPNVector(frontend.d_alpn, false)), d_feContext(std::make_unique<OpenSSLFrontendContext>(frontend.d_addr, frontend.d_tlsConfig))
73✔
706
  {
73✔
707
    OpenSSLTLSConnection::generateConnectionIndexIfNeeded();
73✔
708

709
    d_ticketsKeyRotationDelay = frontend.d_tlsConfig.d_ticketsKeyRotationDelay;
73✔
710

711
    for (auto& entry : d_feContext->d_sniMap) {
148✔
712
      auto* ctx = entry.second.get();
148✔
713
      if (frontend.d_tlsConfig.d_enableTickets && frontend.d_tlsConfig.d_numberOfTicketsKeys > 0) {
148!
714
        /* use our own ticket keys handler so we can rotate them */
715
#if OPENSSL_VERSION_MAJOR >= 3
144✔
716
        SSL_CTX_set_tlsext_ticket_key_evp_cb(ctx, &OpenSSLTLSIOCtx::ticketKeyCb);
144✔
717
#else
718
        SSL_CTX_set_tlsext_ticket_key_cb(ctx, &OpenSSLTLSIOCtx::ticketKeyCb);
719
#endif
720
        libssl_set_ticket_key_callback_data(ctx, d_feContext.get());
144✔
721
      }
144✔
722

723
#ifndef DISABLE_OCSP_STAPLING
148✔
724
      if (!d_feContext->d_ocspResponses.empty()) {
148✔
725
        SSL_CTX_set_tlsext_status_cb(ctx, &OpenSSLTLSIOCtx::ocspStaplingCb);
10✔
726
        SSL_CTX_set_tlsext_status_arg(ctx, &d_feContext->d_ocspResponses);
10✔
727
      }
10✔
728
#endif /* DISABLE_OCSP_STAPLING */
148✔
729

730
      if (frontend.d_tlsConfig.d_readAhead) {
148!
731
        SSL_CTX_set_read_ahead(ctx, 1);
148✔
732
      }
148✔
733

734
      libssl_set_error_counters_callback(*ctx, &frontend.d_tlsCounters);
148✔
735

736
      libssl_set_alpn_select_callback(ctx, alpnServerSelectCallback, this);
148✔
737

738
      if (!frontend.d_tlsConfig.d_keyLogFile.empty()) {
148!
739
        d_feContext->d_keyLogFile = libssl_set_key_log_file(ctx, frontend.d_tlsConfig.d_keyLogFile);
×
740
      }
×
741
    }
148✔
742

743
    try {
73✔
744
      if (frontend.d_tlsConfig.d_ticketKeyFile.empty()) {
73!
745
        handleTicketsKeyRotation(time(nullptr));
73✔
746
      }
73✔
747
      else {
×
748
        OpenSSLTLSIOCtx::loadTicketsKeys(frontend.d_tlsConfig.d_ticketKeyFile);
×
749
      }
×
750
    }
73✔
751
    catch (const std::exception& e) {
73✔
752
      throw;
×
753
    }
×
754
  }
73✔
755

756
  /* client side context */
757
  OpenSSLTLSIOCtx(const TLSContextParameters& params, [[maybe_unused]] Private priv)
758
  {
49✔
759
    int sslOptions =
49✔
760
      SSL_OP_NO_SSLv2 |
49✔
761
      SSL_OP_NO_SSLv3 |
49✔
762
      SSL_OP_NO_COMPRESSION |
49✔
763
      SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
49✔
764
      SSL_OP_SINGLE_DH_USE |
49✔
765
      SSL_OP_SINGLE_ECDH_USE |
49✔
766
#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF
49✔
767
      SSL_OP_IGNORE_UNEXPECTED_EOF |
49✔
768
#endif
49✔
769
      SSL_OP_CIPHER_SERVER_PREFERENCE;
49✔
770
    if (!params.d_enableRenegotiation) {
49!
771
#ifdef SSL_OP_NO_RENEGOTIATION
49✔
772
      sslOptions |= SSL_OP_NO_RENEGOTIATION;
49✔
773
#elif defined(SSL_OP_NO_CLIENT_RENEGOTIATION)
774
      sslOptions |= SSL_OP_NO_CLIENT_RENEGOTIATION;
775
#endif
776
    }
49✔
777

778
    if (params.d_ktls) {
49!
779
#ifdef SSL_OP_ENABLE_KTLS
×
780
      sslOptions |= SSL_OP_ENABLE_KTLS;
×
781
      d_ktls = true;
×
782
#endif /* SSL_OP_ENABLE_KTLS */
×
783
    }
×
784

785
    registerOpenSSLUser();
49✔
786

787
    OpenSSLTLSConnection::generateConnectionIndexIfNeeded();
49✔
788

789
#ifdef HAVE_TLS_CLIENT_METHOD
790
    d_tlsCtx = std::shared_ptr<SSL_CTX>(SSL_CTX_new(TLS_client_method()), SSL_CTX_free);
791
#else
792
    d_tlsCtx = std::shared_ptr<SSL_CTX>(SSL_CTX_new(SSLv23_client_method()), SSL_CTX_free);
49✔
793
#endif
49✔
794
    if (!d_tlsCtx) {
49!
795
      ERR_print_errors_fp(stderr);
×
796
      throw std::runtime_error("Error creating TLS context");
×
797
    }
×
798

799
    SSL_CTX_set_options(d_tlsCtx.get(), sslOptions);
49✔
800
#if defined(SSL_CTX_set_ecdh_auto)
49✔
801
    SSL_CTX_set_ecdh_auto(d_tlsCtx.get(), 1);
49✔
802
#endif
49✔
803

804
    if (!params.d_ciphers.empty()) {
49!
805
      if (SSL_CTX_set_cipher_list(d_tlsCtx.get(), params.d_ciphers.c_str()) != 1) {
×
806
        ERR_print_errors_fp(stderr);
×
807
        throw std::runtime_error("Error setting the cipher list to '" + params.d_ciphers + "' for the TLS context");
×
808
      }
×
809
    }
×
810
#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
49✔
811
    if (!params.d_ciphers13.empty()) {
49!
812
      if (SSL_CTX_set_ciphersuites(d_tlsCtx.get(), params.d_ciphers13.c_str()) != 1) {
×
813
        ERR_print_errors_fp(stderr);
×
814
        throw std::runtime_error("Error setting the TLS 1.3 cipher list to '" + params.d_ciphers13 + "' for the TLS context");
×
815
      }
×
816
    }
×
817
#endif /* HAVE_SSL_CTX_SET_CIPHERSUITES */
49✔
818

819
    if (params.d_validateCertificates) {
49✔
820
      if (params.d_caStore.empty())  {
40✔
821
        if (SSL_CTX_set_default_verify_paths(d_tlsCtx.get()) != 1) {
6!
822
          throw std::runtime_error("Error adding the system's default trusted CAs");
×
823
        }
×
824
      } else {
39✔
825
        if (SSL_CTX_load_verify_locations(d_tlsCtx.get(), params.d_caStore.c_str(), nullptr) != 1) {
34!
826
          throw std::runtime_error("Error adding the trusted CAs file " + params.d_caStore);
×
827
        }
×
828
      }
34✔
829

830
      SSL_CTX_set_verify(d_tlsCtx.get(), SSL_VERIFY_PEER, nullptr);
40✔
831
#if (OPENSSL_VERSION_NUMBER < 0x10002000L)
832
      warnlog("TLS hostname validation requested but not supported for OpenSSL < 1.0.2");
833
#endif
834
    }
40✔
835

836
    /* we need to set SSL_SESS_CACHE_CLIENT for the "new ticket" callback (below) to be called,
837
       but we don't want OpenSSL to cache the session itself so we set SSL_SESS_CACHE_NO_INTERNAL_STORE as well */
838
    SSL_CTX_set_session_cache_mode(d_tlsCtx.get(), SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL_STORE);
49✔
839
    SSL_CTX_sess_set_new_cb(d_tlsCtx.get(), &OpenSSLTLSIOCtx::newTicketFromServerCb);
49✔
840

841
    if (!params.d_keyLogFile.empty()) {
49✔
842
      d_keyLogFile = libssl_set_key_log_file(d_tlsCtx.get(), params.d_keyLogFile);
4✔
843
    }
4✔
844

845
    libssl_set_alpn_protos(d_tlsCtx.get(), getALPNVector(params.d_alpn, true));
49✔
846

847
#ifdef SSL_MODE_RELEASE_BUFFERS
49✔
848
    if (params.d_releaseBuffers) {
49!
849
      SSL_CTX_set_mode(d_tlsCtx.get(), SSL_MODE_RELEASE_BUFFERS);
49✔
850
    }
49✔
851
#endif
49✔
852
  }
49✔
853

854
  OpenSSLTLSIOCtx(const OpenSSLTLSIOCtx&) = delete;
855
  OpenSSLTLSIOCtx(OpenSSLTLSIOCtx&&) = delete;
856
  OpenSSLTLSIOCtx& operator=(const OpenSSLTLSIOCtx&) = delete;
857
  OpenSSLTLSIOCtx& operator=(OpenSSLTLSIOCtx&&) = delete;
858

859
  ~OpenSSLTLSIOCtx() override
860
  {
37✔
861
    d_tlsCtx.reset();
37✔
862
    unregisterOpenSSLUser();
37✔
863
  }
37✔
864

865
#if OPENSSL_VERSION_MAJOR >= 3
866
  static int ticketKeyCb(SSL* s, unsigned char keyName[TLS_TICKETS_KEY_NAME_SIZE], unsigned char* iv, EVP_CIPHER_CTX* ectx, EVP_MAC_CTX* hctx, int enc)
867
#else
868
  static int ticketKeyCb(SSL* s, unsigned char keyName[TLS_TICKETS_KEY_NAME_SIZE], unsigned char* iv, EVP_CIPHER_CTX* ectx, HMAC_CTX* hctx, int enc)
869
#endif
870
  {
865✔
871
    auto* ctx = reinterpret_cast<OpenSSLFrontendContext*>(libssl_get_ticket_key_callback_data(s));
865✔
872
    if (ctx == nullptr) {
865!
873
      return -1;
×
874
    }
×
875

876
    int ret = libssl_ticket_key_callback(s, ctx->d_ticketKeys, keyName, iv, ectx, hctx, enc);
865✔
877
    if (enc == 0) {
865✔
878
      if (ret == 0 || ret == 2) {
241✔
879
        auto* conn = reinterpret_cast<OpenSSLTLSConnection*>(SSL_get_ex_data(s, OpenSSLTLSConnection::getConnectionIndex()));
14✔
880
        if (conn != nullptr) {
14!
881
          if (ret == 0) {
14✔
882
            conn->setUnknownTicketKey();
6✔
883
          }
6✔
884
          else if (ret == 2) {
8!
885
            conn->setResumedFromInactiveTicketKey();
8✔
886
          }
8✔
887
        }
14✔
888
      }
14✔
889
    }
241✔
890

891
    return ret;
865✔
892
  }
865✔
893

894
#ifndef DISABLE_OCSP_STAPLING
895
  static int ocspStaplingCb(SSL* ssl, void* arg)
896
  {
4✔
897
    if (ssl == nullptr || arg == nullptr) {
4!
898
      return SSL_TLSEXT_ERR_NOACK;
×
899
    }
×
900
    const auto ocspMap = reinterpret_cast<std::map<int, std::string>*>(arg);
4✔
901
    return libssl_ocsp_stapling_callback(ssl, *ocspMap);
4✔
902
  }
4✔
903
#endif /* DISABLE_OCSP_STAPLING */
904

905
  static int newTicketFromServerCb(SSL* ssl, SSL_SESSION* session)
906
  {
153✔
907
    OpenSSLTLSConnection* conn = reinterpret_cast<OpenSSLTLSConnection*>(SSL_get_ex_data(ssl, OpenSSLTLSConnection::getConnectionIndex()));
153✔
908
    if (session == nullptr || conn == nullptr) {
153!
909
      return 0;
×
910
    }
×
911

912
    conn->addNewTicket(session);
153✔
913
    return 1;
153✔
914
  }
153✔
915

916
  SSL_CTX* getOpenSSLContext() const
917
  {
699✔
918
    if (d_feContext) {
699✔
919
      return d_feContext->d_tlsCtx.get();
553✔
920
    }
553✔
921
    return d_tlsCtx.get();
146✔
922
  }
699✔
923

924
  std::unique_ptr<TLSConnection> getConnection(int socket, const struct timeval& timeout, time_t now) override
925
  {
553✔
926
    handleTicketsKeyRotation(now);
553✔
927

928
    return std::make_unique<OpenSSLTLSConnection>(socket, timeout, shared_from_this(), std::unique_ptr<SSL, void(*)(SSL*)>(SSL_new(getOpenSSLContext()), SSL_free));
553✔
929
  }
553✔
930

931
  std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, bool hostIsAddr, int socket, const struct timeval& timeout) override
932
  {
146✔
933
    auto conn = std::make_unique<OpenSSLTLSConnection>(host, hostIsAddr, socket, timeout, shared_from_this(), std::unique_ptr<SSL, void(*)(SSL*)>(SSL_new(getOpenSSLContext()), SSL_free));
146✔
934
    if (d_ktls) {
146!
935
      conn->enableKTLS();
×
936
    }
×
937
    return conn;
146✔
938
  }
146✔
939

940
  void rotateTicketsKey(time_t now) override
941
  {
117✔
942
    d_feContext->d_ticketKeys.rotateTicketsKey(now);
117✔
943

944
    if (d_ticketsKeyRotationDelay > 0) {
117!
945
      d_ticketsKeyNextRotation = now + d_ticketsKeyRotationDelay;
117✔
946
    }
117✔
947
  }
117✔
948

949
  void loadTicketsKeys(const std::string& keyFile) final
950
  {
12✔
951
    d_feContext->d_ticketKeys.loadTicketsKeys(keyFile);
12✔
952

953
    if (d_ticketsKeyRotationDelay > 0) {
12!
954
      d_ticketsKeyNextRotation = time(nullptr) + d_ticketsKeyRotationDelay;
12✔
955
    }
12✔
956
  }
12✔
957

958
  void loadTicketsKey(const std::string& key) final
959
  {
1✔
960
    d_feContext->d_ticketKeys.loadTicketsKey(key);
1✔
961

962
    if (d_ticketsKeyRotationDelay > 0) {
1!
963
      d_ticketsKeyNextRotation = time(nullptr) + d_ticketsKeyRotationDelay;
1✔
964
    }
1✔
965
  }
1✔
966

967
  size_t getTicketsKeysCount() override
968
  {
12✔
969
    return d_feContext->d_ticketKeys.getKeysCount();
12✔
970
  }
12✔
971

972
  std::string getName() const override
973
  {
4✔
974
    return "openssl";
4✔
975
  }
4✔
976

977
  bool isServerContext() const
978
  {
×
979
    return d_feContext != nullptr;
×
980
  }
×
981

982
private:
983
  /* called in a client context, if the client advertised more than one ALPN value and the server returned more than one as well, to select the one to use. */
984
  static int alpnServerSelectCallback(SSL*, const unsigned char** out, unsigned char* outlen, const unsigned char* in, unsigned int inlen, void* arg)
985
  {
181✔
986
    if (!arg) {
181!
987
      return SSL_TLSEXT_ERR_ALERT_WARNING;
×
988
    }
×
989
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): OpenSSL's API
990
    OpenSSLTLSIOCtx* obj = reinterpret_cast<OpenSSLTLSIOCtx*>(arg);
181✔
991

992
    const pdns::views::UnsignedCharView inView(in, inlen);
181✔
993
    // Server preference algorithm as per RFC 7301 section 3.2
994
    for (const auto& tentative : obj->d_alpnProtos) {
185✔
995
      size_t pos = 0;
185✔
996
      while (pos < inView.size()) {
261✔
997
        size_t protoLen = inView.at(pos);
251✔
998
        pos++;
251✔
999
        if (protoLen > (inlen - pos)) {
251!
1000
          /* something is very wrong */
1001
          return SSL_TLSEXT_ERR_ALERT_WARNING;
×
1002
        }
×
1003

1004
        if (tentative.size() == protoLen && memcmp(&inView.at(pos), tentative.data(), tentative.size()) == 0) {
251!
1005
          *out = &inView.at(pos);
175✔
1006
          *outlen = protoLen;
175✔
1007
          return SSL_TLSEXT_ERR_OK;
175✔
1008
        }
175✔
1009
        pos += protoLen;
76✔
1010
      }
76✔
1011
    }
185✔
1012

1013
    return SSL_TLSEXT_ERR_NOACK;
6✔
1014
  }
181✔
1015

1016
  const std::vector<std::vector<uint8_t>> d_alpnProtos; // store the supported ALPN protocols, so that the server can select based on what the client sent
1017
  std::shared_ptr<SSL_CTX> d_tlsCtx{nullptr}; // client context, on a server-side the context is stored in d_feContext->d_tlsCtx
1018
  std::unique_ptr<OpenSSLFrontendContext> d_feContext{nullptr};
1019
  pdns::UniqueFilePtr d_keyLogFile{nullptr};
1020
  bool d_ktls{false};
1021
};
1022

1023
#endif /* HAVE_LIBSSL */
1024

1025
#ifdef HAVE_GNUTLS
1026
#include <gnutls/gnutls.h>
1027
#include <gnutls/x509.h>
1028

1029
static void safe_memory_lock([[maybe_unused]] void* data, [[maybe_unused]] size_t size)
1030
{
9✔
1031
#ifdef HAVE_LIBSODIUM
9✔
1032
  sodium_mlock(data, size);
9✔
1033
#endif
9✔
1034
}
9✔
1035

1036
static void safe_memory_release(void* data, size_t size)
1037
{
49✔
1038
#ifdef HAVE_LIBSODIUM
49✔
1039
  sodium_munlock(data, size);
49✔
1040
#elif defined(HAVE_EXPLICIT_BZERO)
1041
  explicit_bzero(data, size);
1042
#elif defined(HAVE_EXPLICIT_MEMSET)
1043
  explicit_memset(data, 0, size);
1044
#elif defined(HAVE_GNUTLS_MEMSET)
1045
  gnutls_memset(data, 0, size);
1046
#else
1047
  /* shamelessly taken from Dovecot's src/lib/safe-memset.c */
1048
  volatile unsigned int volatile_zero_idx = 0;
1049
  volatile unsigned char *p = reinterpret_cast<volatile unsigned char *>(data);
1050

1051
  if (size == 0)
1052
    return;
1053

1054
  do {
1055
    memset(data, 0, size);
1056
  } while (p[volatile_zero_idx] != 0);
1057
#endif
1058
}
49✔
1059

1060
class GnuTLSTicketsKey
1061
{
1062
public:
1063
  GnuTLSTicketsKey()
1064
  {
6✔
1065
    if (gnutls_session_ticket_key_generate(&d_key) != GNUTLS_E_SUCCESS) {
6!
1066
      throw std::runtime_error("Error generating tickets key for TLS context");
×
1067
    }
×
1068

1069
    safe_memory_lock(d_key.data, d_key.size);
6✔
1070
  }
6✔
1071

1072
  GnuTLSTicketsKey(const std::string& key)
1073
  {
1✔
1074
    /* to be sure we are loading the correct amount of data, which
1075
       may change between versions, let's generate a correct key first */
1076
    if (gnutls_session_ticket_key_generate(&d_key) != GNUTLS_E_SUCCESS) {
1!
1077
      throw std::runtime_error("Error generating tickets key (before parsing key file) for TLS context");
×
1078
    }
×
1079

1080
    safe_memory_lock(d_key.data, d_key.size);
1✔
1081
    if (key.size() != d_key.size) {
1!
1082
      safe_memory_release(d_key.data, d_key.size);
×
1083
      gnutls_free(d_key.data);
×
1084
      d_key.data = nullptr;
×
1085
      throw std::runtime_error("Invalid GnuTLS ticket key size");
×
1086
    }
×
1087
    memcpy(d_key.data, key.data(), key.size());
1✔
1088
  }
1✔
1089
  GnuTLSTicketsKey(std::ifstream& file)
1090
  {
×
1091
    /* to be sure we are loading the correct amount of data, which
1092
       may change between versions, let's generate a correct key first */
1093
    if (gnutls_session_ticket_key_generate(&d_key) != GNUTLS_E_SUCCESS) {
×
1094
      throw std::runtime_error("Error generating tickets key (before parsing key file) for TLS context");
×
1095
    }
×
1096

1097
    safe_memory_lock(d_key.data, d_key.size);
×
1098

1099
    try {
×
1100
      file.read(reinterpret_cast<char*>(d_key.data), d_key.size);
×
1101

1102
      if (file.fail()) {
×
1103
        throw std::runtime_error("Invalid GnuTLS tickets key file");
×
1104
      }
×
1105

1106
    }
×
1107
    catch (const std::exception& e) {
×
1108
      safe_memory_release(d_key.data, d_key.size);
×
1109
      gnutls_free(d_key.data);
×
1110
      d_key.data = nullptr;
×
1111
      throw;
×
1112
    }
×
1113
  }
×
1114
  [[nodiscard]] std::string content() const
1115
  {
2✔
1116
    std::string result{};
2✔
1117
    if (d_key.data != nullptr && d_key.size > 0) {
2!
1118
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
1119
      result.append(reinterpret_cast<const char*>(d_key.data), d_key.size);
2✔
1120
      safe_memory_lock(result.data(), result.size());
2✔
1121
    }
2✔
1122
    return result;
2✔
1123
  }
2✔
1124

1125
  ~GnuTLSTicketsKey()
1126
  {
3✔
1127
    if (d_key.data != nullptr && d_key.size > 0) {
3!
1128
      safe_memory_release(d_key.data, d_key.size);
3✔
1129
    }
3✔
1130
    gnutls_free(d_key.data);
3✔
1131
    d_key.data = nullptr;
3✔
1132
  }
3✔
1133
  const gnutls_datum_t& getKey() const
1134
  {
14✔
1135
    return d_key;
14✔
1136
  }
14✔
1137

1138
private:
1139
  gnutls_datum_t d_key{nullptr, 0};
1140
};
1141

1142
class GnuTLSSession : public TLSSession
1143
{
1144
public:
1145
  GnuTLSSession(gnutls_datum_t& sess): d_sess(sess)
49✔
1146
  {
55✔
1147
    sess.data = nullptr;
55✔
1148
    sess.size = 0;
55✔
1149
  }
55✔
1150

1151
  ~GnuTLSSession() override
1152
  {
44✔
1153
    if (d_sess.data != nullptr && d_sess.size > 0) {
44!
1154
      safe_memory_release(d_sess.data, d_sess.size);
44✔
1155
    }
44✔
1156
    gnutls_free(d_sess.data);
44✔
1157
    d_sess.data = nullptr;
44✔
1158
  }
44✔
1159

1160
  const gnutls_datum_t& getNative()
1161
  {
38✔
1162
    return d_sess;
38✔
1163
  }
38✔
1164

1165
private:
1166
  gnutls_datum_t d_sess{nullptr, 0};
1167
};
1168

1169
class GnuTLSConnection: public TLSConnection
1170
{
1171
public:
1172
  /* server side connection */
1173
  GnuTLSConnection(int socket, const struct timeval& timeout, std::shared_ptr<gnutls_certificate_credentials_st>& creds, const gnutls_priority_t priorityCache, std::shared_ptr<GnuTLSTicketsKey>& ticketsKey, bool enableTickets): d_creds(creds), d_ticketsKey(ticketsKey), d_conn(std::unique_ptr<gnutls_session_int, void(*)(gnutls_session_t)>(nullptr, gnutls_deinit))
14✔
1174
  {
14✔
1175
    unsigned int sslOptions = GNUTLS_SERVER | GNUTLS_NONBLOCK;
14✔
1176
#ifdef GNUTLS_NO_SIGNAL
14✔
1177
    sslOptions |= GNUTLS_NO_SIGNAL;
14✔
1178
#endif
14✔
1179

1180
    d_socket = socket;
14✔
1181

1182
    gnutls_session_t conn;
14✔
1183
    if (gnutls_init(&conn, sslOptions) != GNUTLS_E_SUCCESS) {
14!
1184
      throw std::runtime_error("Error creating TLS connection");
×
1185
    }
×
1186

1187
    d_conn = std::unique_ptr<gnutls_session_int, void(*)(gnutls_session_t)>(conn, gnutls_deinit);
14✔
1188
    conn = nullptr;
14✔
1189

1190
    if (gnutls_credentials_set(d_conn.get(), GNUTLS_CRD_CERTIFICATE, d_creds.get()) != GNUTLS_E_SUCCESS) {
14!
1191
      throw std::runtime_error("Error setting certificate and key to TLS connection");
×
1192
    }
×
1193

1194
    if (gnutls_priority_set(d_conn.get(), priorityCache) != GNUTLS_E_SUCCESS) {
14!
1195
      throw std::runtime_error("Error setting ciphers to TLS connection");
×
1196
    }
×
1197

1198
    if (enableTickets && d_ticketsKey) {
14!
1199
      const gnutls_datum_t& key = d_ticketsKey->getKey();
14✔
1200
      if (gnutls_session_ticket_enable_server(d_conn.get(), &key) != GNUTLS_E_SUCCESS) {
14!
1201
        throw std::runtime_error("Error setting the tickets key to TLS connection");
×
1202
      }
×
1203
    }
14✔
1204

1205
    gnutls_transport_set_int(d_conn.get(), d_socket);
14✔
1206

1207
    /* timeouts are in milliseconds */
1208
    gnutls_handshake_set_timeout(d_conn.get(), timeout.tv_sec * 1000 + timeout.tv_usec / 1000);
14✔
1209
    gnutls_record_set_timeout(d_conn.get(), timeout.tv_sec * 1000 + timeout.tv_usec / 1000);
14✔
1210
  }
14✔
1211

1212
  /* client-side connection */
1213
  GnuTLSConnection(const std::string& host, int socket, const struct timeval& timeout, std::shared_ptr<gnutls_certificate_credentials_st>& creds, const gnutls_priority_t priorityCache, bool validateCerts): d_creds(creds), d_conn(std::unique_ptr<gnutls_session_int, void(*)(gnutls_session_t)>(nullptr, gnutls_deinit)), d_host(host), d_client(true)
64✔
1214
  {
68✔
1215
    unsigned int sslOptions = GNUTLS_CLIENT | GNUTLS_NONBLOCK;
68✔
1216
#ifdef GNUTLS_NO_SIGNAL
68✔
1217
    sslOptions |= GNUTLS_NO_SIGNAL;
68✔
1218
#endif
68✔
1219

1220
    d_socket = socket;
68✔
1221

1222
    gnutls_session_t conn;
68✔
1223
    if (gnutls_init(&conn, sslOptions) != GNUTLS_E_SUCCESS) {
68!
1224
      throw std::runtime_error("Error creating TLS connection");
×
1225
    }
×
1226

1227
    d_conn = std::unique_ptr<gnutls_session_int, void(*)(gnutls_session_t)>(conn, gnutls_deinit);
68✔
1228
    conn = nullptr;
68✔
1229

1230
    int rc = gnutls_credentials_set(d_conn.get(), GNUTLS_CRD_CERTIFICATE, d_creds.get());
68✔
1231
    if (rc != GNUTLS_E_SUCCESS) {
68!
1232
      throw std::runtime_error("Error setting certificate and key to TLS connection: " + std::string(gnutls_strerror(rc)));
×
1233
    }
×
1234

1235
    rc = gnutls_priority_set(d_conn.get(), priorityCache);
68✔
1236
    if (rc != GNUTLS_E_SUCCESS) {
68!
1237
      throw std::runtime_error("Error setting ciphers to TLS connection: " + std::string(gnutls_strerror(rc)));
×
1238
    }
×
1239

1240
    gnutls_transport_set_int(d_conn.get(), d_socket);
68✔
1241

1242
    /* timeouts are in milliseconds */
1243
    gnutls_handshake_set_timeout(d_conn.get(),  timeout.tv_sec * 1000 + timeout.tv_usec / 1000);
68✔
1244
    gnutls_record_set_timeout(d_conn.get(),  timeout.tv_sec * 1000 + timeout.tv_usec / 1000);
68✔
1245

1246
#ifdef HAVE_GNUTLS_SESSION_SET_VERIFY_CERT
68✔
1247
    if (validateCerts && !d_host.empty()) {
68!
1248
      gnutls_session_set_verify_cert(d_conn.get(), d_host.c_str(), GNUTLS_VERIFY_ALLOW_UNSORTED_CHAIN);
46✔
1249
      rc = gnutls_server_name_set(d_conn.get(), GNUTLS_NAME_DNS, d_host.c_str(), d_host.size());
46✔
1250
      if (rc != GNUTLS_E_SUCCESS) {
46!
1251
        throw std::runtime_error("Error setting the SNI value to '" + d_host + "' on TLS connection: " + std::string(gnutls_strerror(rc)));
×
1252
      }
×
1253
    }
46✔
1254
#else
1255
    /* no hostname validation for you */
1256
#endif
1257

1258
    /* allow access to our data in the callbacks */
1259
    gnutls_session_set_ptr(d_conn.get(), this);
68✔
1260
    gnutls_handshake_set_hook_function(d_conn.get(), GNUTLS_HANDSHAKE_NEW_SESSION_TICKET, GNUTLS_HOOK_POST, newTicketFromServerCb);
68✔
1261
  }
68✔
1262

1263
  /* The callback prototype changed in 3.4.0. */
1264
#if GNUTLS_VERSION_NUMBER >= 0x030400
1265
  static int newTicketFromServerCb(gnutls_session_t session, unsigned int htype, unsigned post, unsigned int /* incoming */, const gnutls_datum_t* /* msg */)
1266
#else
1267
  static int newTicketFromServerCb(gnutls_session_t session, unsigned int htype, unsigned post, unsigned int /* incoming */)
1268
#endif /* GNUTLS_VERSION_NUMBER >= 0x030400 */
1269
  {
55✔
1270
    if (htype != GNUTLS_HANDSHAKE_NEW_SESSION_TICKET || post != GNUTLS_HOOK_POST || session == nullptr) {
55!
1271
      return 0;
×
1272
    }
×
1273

1274
    GnuTLSConnection* conn = reinterpret_cast<GnuTLSConnection*>(gnutls_session_get_ptr(session));
55✔
1275
    if (conn == nullptr) {
55!
1276
      return 0;
×
1277
    }
×
1278

1279
    gnutls_datum_t sess{nullptr, 0};
55✔
1280
    auto ret = gnutls_session_get_data2(session, &sess);
55✔
1281
    /* GnuTLS returns a 'fake' ticket of 4 bytes set to zero when there is no ticket available */
1282
    if (ret != GNUTLS_E_SUCCESS || sess.size <= 4) {
55!
1283
      throw std::runtime_error("Error getting GnuTLSSession: " + std::string(gnutls_strerror(ret)));
×
1284
    }
×
1285
    conn->d_tlsSessions.push_back(std::make_unique<GnuTLSSession>(sess));
55✔
1286
    return 0;
55✔
1287
  }
55✔
1288

1289
  IOState tryConnect(bool fastOpen, [[maybe_unused]] const ComboAddress& remote) override
1290
  {
68✔
1291
    int ret = 0;
68✔
1292

1293
    if (fastOpen) {
68!
1294
#ifdef HAVE_GNUTLS_TRANSPORT_SET_FASTOPEN
1295
      gnutls_transport_set_fastopen(d_conn.get(), d_socket, const_cast<struct sockaddr*>(reinterpret_cast<const struct sockaddr*>(&remote)), remote.getSocklen(), 0);
1296
#endif
1297
    }
×
1298

1299
    do {
68✔
1300
      ret = gnutls_handshake(d_conn.get());
68✔
1301
      if (ret == GNUTLS_E_SUCCESS) {
68!
1302
        d_handshakeDone = true;
×
1303
        return IOState::Done;
×
1304
      }
×
1305
      else if (ret == GNUTLS_E_AGAIN) {
68✔
1306
        int direction = gnutls_record_get_direction(d_conn.get());
50✔
1307
        return direction == 0 ? IOState::NeedRead : IOState::NeedWrite;
50✔
1308
      }
50✔
1309
      else if (gnutls_error_is_fatal(ret) || ret == GNUTLS_E_WARNING_ALERT_RECEIVED) {
18!
1310
        throw std::runtime_error("Error establishing a new connection: " + std::string(gnutls_strerror(ret)));
18✔
1311
      }
18✔
1312
    } while (ret == GNUTLS_E_INTERRUPTED);
68!
1313

1314
    throw std::runtime_error("Error establishing a new connection: " + std::string(gnutls_strerror(ret)));
×
1315
  }
68✔
1316

1317
  void connect(bool fastOpen, const ComboAddress& remote, const struct timeval& timeout) override
1318
  {
×
1319
    struct timeval start = {0, 0};
×
1320
    struct timeval remainingTime = timeout;
×
1321
    if (timeout.tv_sec != 0 || timeout.tv_usec != 0) {
×
1322
      gettimeofday(&start, nullptr);
×
1323
    }
×
1324

1325
    IOState state;
×
1326
    do {
×
1327
      state = tryConnect(fastOpen, remote);
×
1328
      if (state == IOState::Done) {
×
1329
        return;
×
1330
      }
×
1331
      else if (state == IOState::NeedRead) {
×
1332
        int result = waitForData(d_socket, remainingTime);
×
1333
        if (result <= 0) {
×
1334
          throw std::runtime_error("Error reading from TLS connection: " + std::to_string(result));
×
1335
        }
×
1336
      }
×
1337
      else if (state == IOState::NeedWrite) {
×
1338
        int result = waitForRWData(d_socket, false, remainingTime);
×
1339
        if (result <= 0) {
×
1340
          throw std::runtime_error("Error reading from TLS connection: " + std::to_string(result));
×
1341
        }
×
1342
      }
×
1343

1344
      if (timeout.tv_sec != 0 || timeout.tv_usec != 0) {
×
1345
        struct timeval now;
×
1346
        gettimeofday(&now, nullptr);
×
1347
        struct timeval elapsed = now - start;
×
1348
        if (now < start || remainingTime < elapsed) {
×
1349
          throw runtime_error("Timeout while establishing TLS connection");
×
1350
        }
×
1351
        start = now;
×
1352
        remainingTime = remainingTime - elapsed;
×
1353
      }
×
1354
    }
×
1355
    while (state != IOState::Done);
×
1356
  }
×
1357

1358
  void doHandshake() override
1359
  {
×
1360
    int ret = 0;
×
1361
    do {
×
1362
      ret = gnutls_handshake(d_conn.get());
×
1363
      if (gnutls_error_is_fatal(ret) || ret == GNUTLS_E_WARNING_ALERT_RECEIVED) {
×
1364
        if (d_client) {
×
1365
          throw std::runtime_error("Error establishing a new connection: " + std::string(gnutls_strerror(ret)));
×
1366
        }
×
1367
        else {
×
1368
          throw std::runtime_error("Error accepting a new connection: " + std::string(gnutls_strerror(ret)));
×
1369
        }
×
1370
      }
×
1371
    }
×
1372
    while (ret != GNUTLS_E_SUCCESS && ret == GNUTLS_E_INTERRUPTED);
×
1373

1374
    d_handshakeDone = true;
×
1375
  }
×
1376

1377
  IOState tryHandshake() override
1378
  {
128✔
1379
    int ret = 0;
128✔
1380

1381
    do {
128✔
1382
      ret = gnutls_handshake(d_conn.get());
128✔
1383
      if (ret == GNUTLS_E_SUCCESS) {
128✔
1384
        d_handshakeDone = true;
57✔
1385
        return IOState::Done;
57✔
1386
      }
57✔
1387
      else if (ret == GNUTLS_E_AGAIN) {
71✔
1388
        int direction = gnutls_record_get_direction(d_conn.get());
64✔
1389
        return direction == 0 ? IOState::NeedRead : IOState::NeedWrite;
64✔
1390
      }
64✔
1391
      else if (gnutls_error_is_fatal(ret) || ret == GNUTLS_E_WARNING_ALERT_RECEIVED) {
7!
1392
        if (d_client) {
7!
1393
          std::string error;
7✔
1394
#ifdef HAVE_GNUTLS_SESSION_GET_VERIFY_CERT_STATUS
7✔
1395
          if (ret == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR) {
7!
1396
            gnutls_datum_t out;
7✔
1397
            if (gnutls_certificate_verification_status_print(gnutls_session_get_verify_cert_status(d_conn.get()), gnutls_certificate_type_get(d_conn.get()), &out, 0) == 0) {
7!
1398
              error = " (" + std::string(reinterpret_cast<const char*>(out.data)) + ")";
7✔
1399
              gnutls_free(out.data);
7✔
1400
            }
7✔
1401
          }
7✔
1402
#endif /* HAVE_GNUTLS_SESSION_GET_VERIFY_CERT_STATUS */
7✔
1403
          throw std::runtime_error("Error accepting a new connection: " + std::string(gnutls_strerror(ret)) + error);
7✔
1404
        }
7✔
1405
        else {
×
1406
          throw std::runtime_error("Error establishing a new connection: " + std::string(gnutls_strerror(ret)));
×
1407
        }
×
1408
      }
7✔
1409
    } while (ret == GNUTLS_E_INTERRUPTED);
128!
1410

1411
    if (d_client) {
×
1412
      throw std::runtime_error("Error establishinging a new connection: " + std::string(gnutls_strerror(ret)));
×
1413
    }
×
1414
    else {
×
1415
      throw std::runtime_error("Error accepting a new connection: " + std::string(gnutls_strerror(ret)));
×
1416
    }
×
1417
  }
×
1418

1419
  IOState tryWrite(const PacketBuffer& buffer, size_t& pos, size_t toWrite) override
1420
  {
307✔
1421
    if (!d_handshakeDone) {
307✔
1422
      /* As opposed to OpenSSL, GnuTLS will not transparently finish the handshake for us,
1423
         we need to keep calling gnutls_handshake() until the handshake has been finished. */
1424
      auto state = tryHandshake();
100✔
1425
      if (state != IOState::Done) {
100✔
1426
        return state;
50✔
1427
      }
50✔
1428
    }
100✔
1429

1430
    do {
257✔
1431
      ssize_t res = gnutls_record_send(d_conn.get(), reinterpret_cast<const char *>(&buffer.at(pos)), toWrite - pos);
257✔
1432
      if (res == 0) {
257!
1433
        throw std::runtime_error("Error writing to TLS connection");
×
1434
      }
×
1435
      else if (res > 0) {
257✔
1436
        pos += static_cast<size_t>(res);
250✔
1437
      }
250✔
1438
      else if (res < 0) {
7!
1439
        if (gnutls_error_is_fatal(res)) {
×
1440
          throw std::runtime_error("Fatal error writing to TLS connection: " + std::string(gnutls_strerror(res)));
×
1441
        }
×
1442
        else if (res == GNUTLS_E_AGAIN) {
×
1443
          return IOState::NeedWrite;
×
1444
        }
×
1445
        vinfolog("Warning, non-fatal error while writing to TLS connection: %s", gnutls_strerror(res));
×
1446
      }
×
1447
    }
257✔
1448
    while (pos < toWrite);
257!
1449
    return IOState::Done;
257✔
1450
  }
257✔
1451

1452
  IOState tryRead(PacketBuffer& buffer, size_t& pos, size_t toRead, bool allowIncomplete) override
1453
  {
521✔
1454
    if (!d_handshakeDone) {
521!
1455
      /* As opposed to OpenSSL, GnuTLS will not transparently finish the handshake for us,
1456
         we need to keep calling gnutls_handshake() until the handshake has been finished. */
1457
      auto state = tryHandshake();
×
1458
      if (state != IOState::Done) {
×
1459
        return state;
×
1460
      }
×
1461
    }
×
1462

1463
    do {
521✔
1464
      ssize_t res = gnutls_record_recv(d_conn.get(), reinterpret_cast<char *>(&buffer.at(pos)), toRead - pos);
521✔
1465
      if (res == 0) {
521✔
1466
        throw std::runtime_error("EOF while reading from TLS connection");
3✔
1467
      }
3✔
1468
      else if (res > 0) {
518✔
1469
        pos += static_cast<size_t>(res);
354✔
1470
        if (allowIncomplete) {
354✔
1471
          break;
70✔
1472
        }
70✔
1473
      }
354✔
1474
      else if (res < 0) {
164!
1475
        if (gnutls_error_is_fatal(res)) {
164✔
1476
          throw std::runtime_error("Fatal error reading from TLS connection: " + std::string(gnutls_strerror(res)));
33✔
1477
        }
33✔
1478
        else if (res == GNUTLS_E_AGAIN) {
131!
1479
          return IOState::NeedRead;
131✔
1480
        }
131✔
1481
        vinfolog("Warning, non-fatal error while writing to TLS connection: %s", gnutls_strerror(res));
×
1482
      }
×
1483
    }
521✔
1484
    while (pos < toRead);
521!
1485
    return IOState::Done;
354✔
1486
  }
521✔
1487

1488
  size_t read(void* buffer, size_t bufferSize, const struct timeval& readTimeout, const struct timeval& totalTimeout, bool allowIncomplete) override
1489
  {
×
1490
    size_t got = 0;
×
1491
    struct timeval start{0,0};
×
1492
    struct timeval  remainingTime = totalTimeout;
×
1493
    if (totalTimeout.tv_sec != 0 || totalTimeout.tv_usec != 0) {
×
1494
      gettimeofday(&start, nullptr);
×
1495
    }
×
1496

1497
    do {
×
1498
      ssize_t res = gnutls_record_recv(d_conn.get(), (reinterpret_cast<char *>(buffer) + got), bufferSize - got);
×
1499
      if (res == 0) {
×
1500
        throw std::runtime_error("EOF while reading from TLS connection");
×
1501
      }
×
1502
      else if (res > 0) {
×
1503
        got += static_cast<size_t>(res);
×
1504
        if (allowIncomplete) {
×
1505
          break;
×
1506
        }
×
1507
      }
×
1508
      else if (res < 0) {
×
1509
        if (gnutls_error_is_fatal(res)) {
×
1510
          throw std::runtime_error("Fatal error reading from TLS connection: " + std::string(gnutls_strerror(res)));
×
1511
        }
×
1512
        else if (res == GNUTLS_E_AGAIN) {
×
1513
          int result = waitForData(d_socket, readTimeout);
×
1514
          if (result <= 0) {
×
1515
            throw std::runtime_error("Error while waiting to read from TLS connection: " + std::to_string(result));
×
1516
          }
×
1517
        }
×
1518
        else {
×
1519
          vinfolog("Non-fatal error while reading from TLS connection: %s", gnutls_strerror(res));
×
1520
        }
×
1521
      }
×
1522

1523
      if (totalTimeout.tv_sec != 0 || totalTimeout.tv_usec != 0) {
×
1524
        struct timeval now;
×
1525
        gettimeofday(&now, nullptr);
×
1526
        struct timeval elapsed = now - start;
×
1527
        if (now < start || remainingTime < elapsed) {
×
1528
          throw runtime_error("Timeout while reading data");
×
1529
        }
×
1530
        start = now;
×
1531
        remainingTime = remainingTime - elapsed;
×
1532
      }
×
1533
    }
×
1534
    while (got < bufferSize);
×
1535

1536
    return got;
×
1537
  }
×
1538

1539
  size_t write(const void* buffer, size_t bufferSize, const struct timeval& writeTimeout) override
1540
  {
×
1541
    size_t got = 0;
×
1542

1543
    do {
×
1544
      ssize_t res = gnutls_record_send(d_conn.get(), (reinterpret_cast<const char *>(buffer) + got), bufferSize - got);
×
1545
      if (res == 0) {
×
1546
        throw std::runtime_error("Error writing to TLS connection");
×
1547
      }
×
1548
      else if (res > 0) {
×
1549
        got += static_cast<size_t>(res);
×
1550
      }
×
1551
      else if (res < 0) {
×
1552
        if (gnutls_error_is_fatal(res)) {
×
1553
          throw std::runtime_error("Fatal error writing to TLS connection: " + std::string(gnutls_strerror(res)));
×
1554
        }
×
1555
        else if (res == GNUTLS_E_AGAIN) {
×
1556
          int result = waitForRWData(d_socket, false, writeTimeout);
×
1557
          if (result <= 0) {
×
1558
            throw std::runtime_error("Error waiting to write to TLS connection: " + std::to_string(result));
×
1559
          }
×
1560
        }
×
1561
        else {
×
1562
          vinfolog("Non-fatal error while writing to TLS connection: %s", gnutls_strerror(res));
×
1563
        }
×
1564
      }
×
1565
    }
×
1566
    while (got < bufferSize);
×
1567

1568
    return got;
×
1569
  }
×
1570

1571
  bool isUsable() const override
1572
  {
2✔
1573
    if (!d_conn) {
2!
1574
      return false;
×
1575
    }
×
1576

1577
    /* as far as I can tell we can't peek so we cannot do better */
1578
    return isTCPSocketUsable(d_socket);
2✔
1579
  }
2✔
1580

1581
  std::string getServerNameIndication() const override
1582
  {
112✔
1583
    if (d_conn) {
112!
1584
      unsigned int type;
112✔
1585
      size_t name_len = 256;
112✔
1586
      std::string sni;
112✔
1587
      sni.resize(name_len);
112✔
1588

1589
      int res = gnutls_server_name_get(d_conn.get(), const_cast<char*>(sni.c_str()), &name_len, &type, 0);
112✔
1590
      if (res == GNUTLS_E_SUCCESS) {
112!
1591
        sni.resize(name_len);
112✔
1592
        return sni;
112✔
1593
      }
112✔
1594
    }
112✔
1595
    return std::string();
×
1596
  }
112✔
1597

1598
  std::vector<uint8_t> getNextProtocol() const override
1599
  {
×
1600
    std::vector<uint8_t> result;
×
1601
    if (!d_conn) {
×
1602
      return result;
×
1603
    }
×
1604
    gnutls_datum_t next;
×
1605
    if (gnutls_alpn_get_selected_protocol(d_conn.get(), &next) != GNUTLS_E_SUCCESS) {
×
1606
      return result;
×
1607
    }
×
1608
    result.insert(result.end(), next.data, next.data + next.size);
×
1609
    return result;
×
1610
  }
×
1611

1612
  LibsslTLSVersion getTLSVersion() const override
1613
  {
112✔
1614
    auto proto = gnutls_protocol_get_version(d_conn.get());
112✔
1615
    switch (proto) {
112✔
1616
    case GNUTLS_TLS1_0:
×
1617
      return LibsslTLSVersion::TLS10;
×
1618
    case GNUTLS_TLS1_1:
×
1619
      return LibsslTLSVersion::TLS11;
×
1620
    case GNUTLS_TLS1_2:
3✔
1621
      return LibsslTLSVersion::TLS12;
3✔
1622
#if GNUTLS_VERSION_NUMBER >= 0x030603
×
1623
    case GNUTLS_TLS1_3:
109✔
1624
      return LibsslTLSVersion::TLS13;
109✔
1625
#endif /* GNUTLS_VERSION_NUMBER >= 0x030603 */
×
1626
    default:
×
1627
      return LibsslTLSVersion::Unknown;
×
1628
    }
112✔
1629
  }
112✔
1630

1631
  [[nodiscard]] std::pair<long, std::string> getVerifyResult() const override
1632
  {
1✔
1633
    if (d_conn) {
1!
1634
      auto status = gnutls_session_get_verify_cert_status(d_conn.get());
1✔
1635
      gnutls_datum_t out{};
1✔
1636
      if (gnutls_certificate_verification_status_print(status, GNUTLS_CRT_X509, &out, 0) == 0) {
1!
1637
        auto errString = std::string(reinterpret_cast<const char*>(out.data), out.size); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) It's the API
1✔
1638
        gnutls_free(out.data);
1✔
1639
        return {status, errString};
1✔
1640
      }
1✔
1641
      return {status, ""};
×
1642
    }
1✔
1643
    return {0, ""};
×
1644
  }
1✔
1645

1646
  bool hasSessionBeenResumed() const override
1647
  {
46✔
1648
    if (d_conn) {
46!
1649
      return gnutls_session_is_resumed(d_conn.get()) != 0;
46✔
1650
    }
46✔
1651
    return false;
×
1652
  }
46✔
1653

1654
  std::vector<std::unique_ptr<TLSSession>> getSessions() override
1655
  {
36✔
1656
    return std::move(d_tlsSessions);
36✔
1657
  }
36✔
1658

1659
  void setSession(std::unique_ptr<TLSSession>& session) override
1660
  {
38✔
1661
    auto sess = dynamic_cast<GnuTLSSession*>(session.get());
38✔
1662
    if (!sess) {
38!
1663
      throw std::runtime_error("Unable to convert GnuTLS session");
×
1664
    }
×
1665

1666
    auto native = sess->getNative();
38✔
1667
    auto ret = gnutls_session_set_data(d_conn.get(), native.data, native.size);
38✔
1668
    if (ret != GNUTLS_E_SUCCESS) {
38!
1669
      throw std::runtime_error("Error setting up GnuTLS session: " + std::string(gnutls_strerror(ret)));
×
1670
    }
×
1671
    session.reset();
38✔
1672
  }
38✔
1673

1674
  void close() override
1675
  {
76✔
1676
    if (d_conn) {
76!
1677
      gnutls_bye(d_conn.get(), GNUTLS_SHUT_RDWR);
76✔
1678
    }
76✔
1679
  }
76✔
1680

1681
  bool setALPNProtos(const std::vector<std::vector<uint8_t>>& protos)
1682
  {
78✔
1683
    std::vector<gnutls_datum_t> values;
78✔
1684
    values.reserve(protos.size());
78✔
1685
    for (const auto& proto : protos) {
78✔
1686
      gnutls_datum_t value;
78✔
1687
      value.data = const_cast<uint8_t*>(proto.data());
78✔
1688
      value.size = proto.size();
78✔
1689
      values.push_back(value);
78✔
1690
    }
78✔
1691
    unsigned int flags = 0;
78✔
1692
#if GNUTLS_VERSION_NUMBER >= 0x030500
78✔
1693
    flags |= GNUTLS_ALPN_MANDATORY;
78✔
1694
#elif defined(GNUTLS_ALPN_MAND)
1695
    flags |= GNUTLS_ALPN_MAND;
1696
#endif
1697
    return gnutls_alpn_set_protocols(d_conn.get(), values.data(), values.size(), flags);
78✔
1698
  }
78✔
1699

1700
  std::vector<int> getAsyncFDs() override
1701
  {
14✔
1702
    return {};
14✔
1703
  }
14✔
1704

1705
private:
1706
  std::shared_ptr<gnutls_certificate_credentials_st> d_creds;
1707
  std::shared_ptr<GnuTLSTicketsKey> d_ticketsKey;
1708
  std::unique_ptr<gnutls_session_int, void(*)(gnutls_session_t)> d_conn;
1709
  std::vector<std::unique_ptr<TLSSession>> d_tlsSessions;
1710
  std::string d_host;
1711
  const bool d_client{false};
1712
  bool d_handshakeDone{false};
1713
};
1714

1715
class GnuTLSIOCtx: public TLSCtx
1716
{
1717
public:
1718
  /* server side context */
1719
  GnuTLSIOCtx(TLSFrontend& frontend): d_protos(getALPNVector(frontend.d_alpn, false)), d_enableTickets(frontend.d_tlsConfig.d_enableTickets)
6✔
1720
  {
6✔
1721
    int rc = 0;
6✔
1722
    d_ticketsKeyRotationDelay = frontend.d_tlsConfig.d_ticketsKeyRotationDelay;
6✔
1723

1724
    gnutls_certificate_credentials_t creds;
6✔
1725
    rc = gnutls_certificate_allocate_credentials(&creds);
6✔
1726
    if (rc != GNUTLS_E_SUCCESS) {
6!
1727
      throw std::runtime_error("Error allocating credentials for TLS context on " + frontend.d_addr.toStringWithPort() + ": " + gnutls_strerror(rc));
×
1728
    }
×
1729

1730
    d_creds = std::shared_ptr<gnutls_certificate_credentials_st>(creds, gnutls_certificate_free_credentials);
6✔
1731
    creds = nullptr;
6✔
1732

1733
    for (const auto& pair : frontend.d_tlsConfig.d_certKeyPairs) {
6✔
1734
      rc = gnutls_certificate_set_x509_key_file(d_creds.get(), pair.d_cert.c_str(), pair.d_key->c_str(), GNUTLS_X509_FMT_PEM);
6✔
1735
      if (rc != GNUTLS_E_SUCCESS) {
6!
1736
        throw std::runtime_error("Error loading certificate ('" + pair.d_cert + "') and key ('" + pair.d_key.value() + "') for TLS context on " + frontend.d_addr.toStringWithPort() + ": " + gnutls_strerror(rc));
×
1737
      }
×
1738
    }
6✔
1739

1740
#ifndef DISABLE_OCSP_STAPLING
6✔
1741
    size_t count = 0;
6✔
1742
    for (const auto& file : frontend.d_tlsConfig.d_ocspFiles) {
6✔
1743
      rc = gnutls_certificate_set_ocsp_status_request_file(d_creds.get(), file.c_str(), count);
3✔
1744
      if (rc != GNUTLS_E_SUCCESS) {
3✔
1745
        warnlog("Error loading OCSP response from file '%s' for certificate ('%s') and key ('%s') for TLS context on %s: %s", file, frontend.d_tlsConfig.d_certKeyPairs.at(count).d_cert, frontend.d_tlsConfig.d_certKeyPairs.at(count).d_key.value(), frontend.d_addr.toStringWithPort(), gnutls_strerror(rc));
1✔
1746
      }
1✔
1747
      ++count;
3✔
1748
    }
3✔
1749
#endif /* DISABLE_OCSP_STAPLING */
6✔
1750

1751
#if GNUTLS_VERSION_NUMBER >= 0x030600
6✔
1752
    rc = gnutls_certificate_set_known_dh_params(d_creds.get(), GNUTLS_SEC_PARAM_HIGH);
6✔
1753
    if (rc != GNUTLS_E_SUCCESS) {
6!
1754
      throw std::runtime_error("Error setting DH params for TLS context on " + frontend.d_addr.toStringWithPort() + ": " + gnutls_strerror(rc));
×
1755
    }
×
1756
#endif
6✔
1757

1758
    rc = gnutls_priority_init(&d_priorityCache, frontend.d_tlsConfig.d_ciphers.empty() ? "NORMAL" : frontend.d_tlsConfig.d_ciphers.c_str(), nullptr);
6!
1759
    if (rc != GNUTLS_E_SUCCESS) {
6!
1760
      throw std::runtime_error("Error setting up TLS cipher preferences to '" + frontend.d_tlsConfig.d_ciphers + "' (" + gnutls_strerror(rc) + ") on " + frontend.d_addr.toStringWithPort());
×
1761
    }
×
1762

1763
    try {
6✔
1764
      if (frontend.d_tlsConfig.d_ticketKeyFile.empty()) {
6!
1765
        handleTicketsKeyRotation(time(nullptr));
6✔
1766
      }
6✔
1767
      else {
×
1768
        GnuTLSIOCtx::loadTicketsKeys(frontend.d_tlsConfig.d_ticketKeyFile);
×
1769
      }
×
1770
    }
6✔
1771
    catch(const std::runtime_error& e) {
6✔
1772
      throw std::runtime_error("Error generating tickets key for TLS context on " + frontend.d_addr.toStringWithPort() + ": " + e.what());
×
1773
    }
×
1774
  }
6✔
1775

1776
  /* client side context */
1777
  GnuTLSIOCtx(const TLSContextParameters& params): d_protos(getALPNVector(params.d_alpn, true)), d_contextParameters(std::make_unique<TLSContextParameters>(params)), d_validateCerts(params.d_validateCertificates)
18✔
1778
  {
22✔
1779
    int rc = 0;
22✔
1780

1781
    gnutls_certificate_credentials_t creds;
22✔
1782
    rc = gnutls_certificate_allocate_credentials(&creds);
22✔
1783
    if (rc != GNUTLS_E_SUCCESS) {
22!
1784
      throw std::runtime_error("Error allocating credentials for TLS context: " + std::string(gnutls_strerror(rc)));
×
1785
    }
×
1786

1787
    d_creds = std::shared_ptr<gnutls_certificate_credentials_st>(creds, gnutls_certificate_free_credentials);
22✔
1788
    creds = nullptr;
22✔
1789

1790
    if (params.d_validateCertificates) {
22✔
1791
      if (params.d_caStore.empty()) {
18✔
1792
#if GNUTLS_VERSION_NUMBER >= 0x030700 && GNUTLS_VERSION_NUMBER < 0x030703
1793
        /* see https://gitlab.com/gnutls/gnutls/-/issues/1277 */
1794
        std::cerr<<"Warning: GnuTLS 3.7.0 - 3.7.2 have a memory leak when validating server certificates in some configurations (PKCS11 support enabled, and a default PKCS11 trust store), please consider upgrading GnuTLS, using the OpenSSL provider for outgoing connections, or explicitly setting a CA store"<<std::endl;
1795
#endif /* GNUTLS_VERSION_NUMBER >= 0x030700 && GNUTLS_VERSION_NUMBER < 0x030703 */
1796
        rc = gnutls_certificate_set_x509_system_trust(d_creds.get());
3✔
1797
        if (rc < 0) {
3!
1798
          throw std::runtime_error("Error adding the system's default trusted CAs: " + std::string(gnutls_strerror(rc)));
×
1799
        }
×
1800
      }
3✔
1801
      else {
15✔
1802
        rc = gnutls_certificate_set_x509_trust_file(d_creds.get(), params.d_caStore.c_str(), GNUTLS_X509_FMT_PEM);
15✔
1803
        if (rc < 0) {
15!
1804
          throw std::runtime_error("Error adding '" + params.d_caStore + "' to the trusted CAs: " + std::string(gnutls_strerror(rc)));
×
1805
        }
×
1806
      }
15✔
1807
    }
18✔
1808

1809
    rc = gnutls_priority_init(&d_priorityCache, params.d_ciphers.empty() ? "NORMAL" : params.d_ciphers.c_str(), nullptr);
22!
1810
    if (rc != GNUTLS_E_SUCCESS) {
22!
1811
      throw std::runtime_error("Error setting up TLS cipher preferences to 'NORMAL' (" + std::string(gnutls_strerror(rc)) + ")");
×
1812
    }
×
1813
  }
22✔
1814

1815
  ~GnuTLSIOCtx() override
1816
  {
15✔
1817
    d_creds.reset();
15✔
1818

1819
    if (d_priorityCache) {
15!
1820
      gnutls_priority_deinit(d_priorityCache);
15✔
1821
    }
15✔
1822
  }
15✔
1823

1824
  std::unique_ptr<TLSConnection> getConnection(int socket, const struct timeval& timeout, time_t now) override
1825
  {
14✔
1826
    handleTicketsKeyRotation(now);
14✔
1827

1828
    std::shared_ptr<GnuTLSTicketsKey> ticketsKey;
14✔
1829
    {
14✔
1830
      ticketsKey = *(d_ticketsKey.read_lock());
14✔
1831
    }
14✔
1832

1833
    auto connection = std::make_unique<GnuTLSConnection>(socket, timeout, d_creds, d_priorityCache, ticketsKey, d_enableTickets);
14✔
1834
    if (!d_protos.empty()) {
14!
1835
      connection->setALPNProtos(d_protos);
14✔
1836
    }
14✔
1837
    return connection;
14✔
1838
  }
14✔
1839

1840
  static std::shared_ptr<gnutls_certificate_credentials_st> getPerThreadCredentials(bool validate, const std::string& caStore)
1841
  {
68✔
1842
    static thread_local std::map<std::pair<bool, std::string>, std::shared_ptr<gnutls_certificate_credentials_st>> t_credentials;
68✔
1843
    auto& entry = t_credentials[{validate, caStore}];
68✔
1844
    if (!entry) {
68✔
1845
      gnutls_certificate_credentials_t creds;
21✔
1846
      int rc = gnutls_certificate_allocate_credentials(&creds);
21✔
1847
      if (rc != GNUTLS_E_SUCCESS) {
21!
1848
        throw std::runtime_error("Error allocating credentials for TLS context: " + std::string(gnutls_strerror(rc)));
×
1849
      }
×
1850

1851
      entry = std::shared_ptr<gnutls_certificate_credentials_st>(creds, gnutls_certificate_free_credentials);
21✔
1852
      creds = nullptr;
21✔
1853

1854
      if (validate) {
21✔
1855
        if (caStore.empty()) {
16✔
1856
          rc = gnutls_certificate_set_x509_system_trust(entry.get());
2✔
1857
          if (rc < 0) {
2!
1858
            throw std::runtime_error("Error adding the system's default trusted CAs: " + std::string(gnutls_strerror(rc)));
×
1859
          }
×
1860
        }
2✔
1861
        else {
14✔
1862
          rc = gnutls_certificate_set_x509_trust_file(entry.get(), caStore.c_str(), GNUTLS_X509_FMT_PEM);
14✔
1863
          if (rc < 0) {
14!
1864
            throw std::runtime_error("Error adding '" + caStore + "' to the trusted CAs: " + std::string(gnutls_strerror(rc)));
×
1865
          }
×
1866
        }
14✔
1867
      }
16✔
1868
    }
21✔
1869
    return entry;
68✔
1870
  }
68✔
1871

1872
  std::unique_ptr<TLSConnection> getClientConnection(const std::string& host, bool, int socket, const struct timeval& timeout) override
1873
  {
68✔
1874
    auto creds = getPerThreadCredentials(d_contextParameters->d_validateCertificates, d_contextParameters->d_caStore);
68✔
1875
    auto connection = std::make_unique<GnuTLSConnection>(host, socket, timeout, creds, d_priorityCache, d_validateCerts);
68✔
1876
    if (!d_protos.empty()) {
68✔
1877
      connection->setALPNProtos(d_protos);
64✔
1878
    }
64✔
1879
    return connection;
68✔
1880
  }
68✔
1881

1882
  void addTicketsKey(time_t now, std::shared_ptr<GnuTLSTicketsKey>&& newKey)
1883
  {
7✔
1884
    if (!d_enableTickets) {
7!
1885
      return;
×
1886
    }
×
1887

1888
    {
7✔
1889
      *(d_ticketsKey.write_lock()) = std::move(newKey);
7✔
1890
    }
7✔
1891

1892
    if (d_ticketsKeyRotationDelay > 0) {
7!
1893
      d_ticketsKeyNextRotation = now + d_ticketsKeyRotationDelay;
7✔
1894
    }
7✔
1895

1896
    if (TLSCtx::hasTicketsKeyAddedHook()) {
7✔
1897
      auto ticketsKey = *(d_ticketsKey.read_lock());
2✔
1898
      auto content = ticketsKey->content();
2✔
1899
      TLSCtx::getTicketsKeyAddedHook()(content);
2✔
1900
      safe_memory_release(content.data(), content.size());
2✔
1901
    }
2✔
1902
  }
7✔
1903
  void rotateTicketsKey(time_t now) override
1904
  {
6✔
1905
    if (!d_enableTickets) {
6!
1906
      return;
×
1907
    }
×
1908

1909
    auto newKey = std::make_shared<GnuTLSTicketsKey>();
6✔
1910
    addTicketsKey(now, std::move(newKey));
6✔
1911
  }
6✔
1912
  void loadTicketsKey(const std::string& key) final
1913
  {
1✔
1914
    if (!d_enableTickets) {
1!
1915
      return;
×
1916
    }
×
1917

1918
    auto newKey = std::make_shared<GnuTLSTicketsKey>(key);
1✔
1919
    addTicketsKey(time(nullptr), std::move(newKey));
1✔
1920
  }
1✔
1921

1922
  void loadTicketsKeys(const std::string& keyFile) final
1923
  {
×
1924
    if (!d_enableTickets) {
×
1925
      return;
×
1926
    }
×
1927

1928
    std::ifstream file(keyFile);
×
1929
    auto newKey = std::make_shared<GnuTLSTicketsKey>(file);
×
1930
    addTicketsKey(time(nullptr), std::move(newKey));
×
1931
    file.close();
×
1932
  }
×
1933

1934
  size_t getTicketsKeysCount() override
1935
  {
×
1936
    return *(d_ticketsKey.read_lock()) != nullptr ? 1 : 0;
×
1937
  }
×
1938

1939
  std::string getName() const override
1940
  {
3✔
1941
    return "gnutls";
3✔
1942
  }
3✔
1943

1944
private:
1945
  /* client context parameters */
1946
  std::shared_ptr<gnutls_certificate_credentials_st> d_creds;
1947
  const std::vector<std::vector<uint8_t>> d_protos;
1948
  std::unique_ptr<TLSContextParameters> d_contextParameters{nullptr};
1949
  gnutls_priority_t d_priorityCache{nullptr};
1950
  SharedLockGuarded<std::shared_ptr<GnuTLSTicketsKey>> d_ticketsKey{nullptr};
1951
  bool d_enableTickets{true};
1952
  bool d_validateCerts{true};
1953
};
1954

1955
#endif /* HAVE_GNUTLS */
1956

1957
#endif /* HAVE_DNS_OVER_TLS || HAVE_DNS_OVER_HTTPS */
1958

1959
bool TLSFrontend::setupTLS()
1960
{
88✔
1961
#if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
88✔
1962
  std::shared_ptr<TLSCtx> newCtx{nullptr};
88✔
1963
  if (d_parentFrontend) {
88✔
1964
    newCtx = d_parentFrontend->getContext();
9✔
1965
    if (newCtx) {
9!
1966
      std::atomic_store_explicit(&d_ctx, std::move(newCtx), std::memory_order_release);
9✔
1967
      return true;
9✔
1968
    }
9✔
1969
  }
9✔
1970

1971
  /* get the "best" available provider */
1972
#if defined(HAVE_GNUTLS)
79✔
1973
  if (d_provider == "gnutls") {
79✔
1974
    newCtx = std::make_shared<GnuTLSIOCtx>(*this);
6✔
1975
  }
6✔
1976
#endif /* HAVE_GNUTLS */
79✔
1977
#if defined(HAVE_LIBSSL)
79✔
1978
  if (d_provider == "openssl") {
79✔
1979
    newCtx = OpenSSLTLSIOCtx::createServerSideContext(*this);
27✔
1980
  }
27✔
1981
#endif /* HAVE_LIBSSL */
79✔
1982

1983
  if (!newCtx) {
79✔
1984
#if defined(HAVE_LIBSSL)
46✔
1985
    newCtx = OpenSSLTLSIOCtx::createServerSideContext(*this);
46✔
1986
#elif defined(HAVE_GNUTLS)
1987
    newCtx = std::make_shared<GnuTLSIOCtx>(*this);
1988
#else
1989
#error "TLS support needed but neither libssl nor GnuTLS were selected"
1990
#endif
1991
  }
46✔
1992

1993
  std::atomic_store_explicit(&d_ctx, std::move(newCtx), std::memory_order_release);
79✔
1994
#endif /* HAVE_DNS_OVER_TLS || HAVE_DNS_OVER_HTTPS */
79✔
1995
  return true;
79✔
1996
}
88✔
1997

1998
std::shared_ptr<TLSCtx> getTLSContext([[maybe_unused]] const TLSContextParameters& params)
1999
{
71✔
2000
#ifdef HAVE_DNS_OVER_TLS
71✔
2001
  /* get the "best" available provider */
2002
  if (!params.d_provider.empty()) {
71✔
2003
#if defined(HAVE_GNUTLS)
57✔
2004
    if (params.d_provider == "gnutls") {
57✔
2005
      return std::make_shared<GnuTLSIOCtx>(params);
22✔
2006
    }
22✔
2007
#endif /* HAVE_GNUTLS */
35✔
2008
#if defined(HAVE_LIBSSL)
35✔
2009
    if (params.d_provider == "openssl") {
35!
2010
      return OpenSSLTLSIOCtx::createClientSideContext(params);
35✔
2011
    }
35✔
2012
#endif /* HAVE_LIBSSL */
35✔
2013
  }
35✔
2014

2015
#if defined(HAVE_LIBSSL)
14✔
2016
  return OpenSSLTLSIOCtx::createClientSideContext(params);
14✔
2017
#elif defined(HAVE_GNUTLS)
2018
  return std::make_shared<GnuTLSIOCtx>(params);
2019
#else
2020
#error "DNS over TLS support needed but neither libssl nor GnuTLS were selected"
2021
#endif
2022

2023
#endif /* HAVE_DNS_OVER_TLS */
×
2024
  return nullptr;
×
2025
}
71✔
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