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

PowerDNS / pdns / 12268421405

11 Dec 2024 02:20AM UTC coverage: 64.728% (+3.2%) from 61.567%
12268421405

Pull #14954

github

web-flow
Merge d6414658d into 5089e2c08
Pull Request #14954: clang-tidy: use std::min/max

37522 of 88810 branches covered (42.25%)

Branch coverage included in aggregate %.

3 of 10 new or added lines in 4 files covered. (30.0%)

169 existing lines in 9 files now uncovered.

125906 of 163674 relevant lines covered (76.92%)

4802085.67 hits per line

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

84.24
/pdns/dnsdistdist/dnsdist.hh
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
#pragma once
23

24
#include "config.h"
25

26
#include <condition_variable>
27
#include <memory>
28
#include <mutex>
29
#include <string>
30
#include <thread>
31
#include <time.h>
32
#include <unistd.h>
33
#include <unordered_map>
34

35
#include "bpf-filter.hh"
36
#include "circular_buffer.hh"
37
#include "dnsdist-idstate.hh"
38
#include "dnsdist-lbpolicies.hh"
39
#include "dnsdist-protocols.hh"
40
#include "dnsname.hh"
41
#include "dnsdist-doh-common.hh"
42
#include "doq.hh"
43
#include "doh3.hh"
44
#include "ednsoptions.hh"
45
#include "iputils.hh"
46
#include "misc.hh"
47
#include "mplexer.hh"
48
#include "noinitvector.hh"
49
#include "uuid-utils.hh"
50
#include "proxy-protocol.hh"
51
#include "stat_t.hh"
52

53
uint64_t uptimeOfProcess(const std::string& str);
54

55
using QTag = std::unordered_map<string, string>;
56

57
class IncomingTCPConnectionState;
58

59
struct ClientState;
60

61
struct DNSQuestion
62
{
63
  DNSQuestion(InternalQueryState& ids_, PacketBuffer& data_);
64
  DNSQuestion(const DNSQuestion&) = delete;
65
  DNSQuestion& operator=(const DNSQuestion&) = delete;
66
  DNSQuestion(DNSQuestion&&) = default;
140✔
67
  virtual ~DNSQuestion() = default;
26,664,066✔
68

69
  std::string getTrailingData() const;
70
  bool setTrailingData(const std::string&);
71
  const PacketBuffer& getData() const
72
  {
29,842,877✔
73
    return data;
29,842,877✔
74
  }
29,842,877✔
75
  PacketBuffer& getMutableData()
76
  {
22,510,990✔
77
    return data;
22,510,990✔
78
  }
22,510,990✔
79

80
  bool editHeader(const std::function<bool(dnsheader&)>& editFunction);
81

82
  const dnsheader_aligned getHeader() const
83
  {
22,566,769✔
84
    if (data.size() < sizeof(dnsheader)) {
22,566,769✔
85
      throw std::runtime_error("Trying to access the dnsheader of a too small (" + std::to_string(data.size()) + ") DNSQuestion buffer");
30✔
86
    }
30✔
87
    dnsheader_aligned dh(data.data());
22,566,739✔
88
    return dh;
22,566,739✔
89
  }
22,566,769✔
90

91
  /* this function is not safe against unaligned access, you should
92
     use editHeader() instead, but we need it for the Lua bindings */
93
  dnsheader* getMutableHeader() const
94
  {
779✔
95
    if (data.size() < sizeof(dnsheader)) {
779!
96
      throw std::runtime_error("Trying to access the dnsheader of a too small (" + std::to_string(data.size()) + ") DNSQuestion buffer");
×
97
    }
×
98
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
99
    return reinterpret_cast<dnsheader*>(data.data());
779✔
100
  }
779✔
101

102
  bool hasRoomFor(size_t more) const
103
  {
66✔
104
    return data.size() <= getMaximumSize() && (getMaximumSize() - data.size()) >= more;
66!
105
  }
66✔
106

107
  size_t getMaximumSize() const
108
  {
36,586✔
109
    if (overTCP()) {
36,586✔
110
      return std::numeric_limits<uint16_t>::max();
20,264✔
111
    }
20,264✔
112
    return 4096;
16,322✔
113
  }
36,586✔
114

115
  dnsdist::Protocol getProtocol() const
116
  {
38,640✔
117
    return ids.protocol;
38,640✔
118
  }
38,640✔
119

120
  bool overTCP() const
121
  {
118,361✔
122
    return !(ids.protocol == dnsdist::Protocol::DoUDP || ids.protocol == dnsdist::Protocol::DNSCryptUDP);
118,361✔
123
  }
118,361✔
124

125
  void setTag(std::string&& key, std::string&& value)
126
  {
16✔
127
    if (!ids.qTag) {
16✔
128
      ids.qTag = std::make_unique<QTag>();
10✔
129
    }
10✔
130
    ids.qTag->insert_or_assign(std::move(key), std::move(value));
16✔
131
  }
16✔
132

133
  void setTag(const std::string& key, const std::string& value)
134
  {
447✔
135
    if (!ids.qTag) {
447✔
136
      ids.qTag = std::make_unique<QTag>();
342✔
137
    }
342✔
138
    ids.qTag->insert_or_assign(key, value);
447✔
139
  }
447✔
140

141
  void setTag(const std::string& key, std::string&& value)
142
  {
78✔
143
    if (!ids.qTag) {
78✔
144
      ids.qTag = std::make_unique<QTag>();
18✔
145
    }
18✔
146
    ids.qTag->insert_or_assign(key, std::move(value));
78✔
147
  }
78✔
148

149
  const struct timespec& getQueryRealTime() const
150
  {
131,318✔
151
    return ids.queryRealTime.d_start;
131,318✔
152
  }
131,318✔
153

154
  bool isAsynchronous() const
155
  {
77,246✔
156
    return asynchronous;
77,246✔
157
  }
77,246✔
158

159
  std::shared_ptr<IncomingTCPConnectionState> getIncomingTCPState() const
160
  {
187✔
161
    return d_incomingTCPState;
187✔
162
  }
187✔
163

164
  ClientState* getFrontend() const
165
  {
×
166
    return ids.cs;
×
167
  }
×
168

169
protected:
170
  PacketBuffer& data;
171

172
public:
173
  InternalQueryState& ids;
174
  std::unique_ptr<Netmask> ecs{nullptr};
175
  std::string sni; /* Server Name Indication, if any (DoT or DoH) */
176
  mutable std::unique_ptr<EDNSOptionViewMap> ednsOptions; /* this needs to be mutable because it is parsed just in time, when DNSQuestion is read-only */
177
  std::shared_ptr<IncomingTCPConnectionState> d_incomingTCPState{nullptr};
178
  std::unique_ptr<std::vector<ProxyProtocolValue>> proxyProtocolValues{nullptr};
179
  uint16_t ecsPrefixLength;
180
  uint8_t ednsRCode{0};
181
  bool ecsOverride;
182
  bool useECS{true};
183
  bool asynchronous{false};
184
};
185

186
struct DownstreamState;
187

188
struct DNSResponse : DNSQuestion
189
{
190
  DNSResponse(InternalQueryState& ids_, PacketBuffer& data_, const std::shared_ptr<DownstreamState>& downstream) :
191
    DNSQuestion(ids_, data_), d_downstream(downstream) {}
27,586✔
192
  DNSResponse(const DNSResponse&) = delete;
193
  DNSResponse& operator=(const DNSResponse&) = delete;
194
  DNSResponse(DNSResponse&&) = default;
195

196
  const std::shared_ptr<DownstreamState>& d_downstream;
197
};
198

199
using pdns::stat_t;
200

201
class BasicQPSLimiter
202
{
203
public:
204
  BasicQPSLimiter()
205
  {
1,564✔
206
  }
1,564✔
207

208
  BasicQPSLimiter(unsigned int burst) :
209
    d_tokens(burst)
210
  {
327,738✔
211
    d_prev.start();
327,738✔
212
  }
327,738✔
213

214
  virtual ~BasicQPSLimiter()
215
  {
983,296✔
216
  }
983,296✔
217

218
  bool check(unsigned int rate, unsigned int burst) const // this is not quite fair
219
  {
196,794✔
220
    if (checkOnly(rate, burst)) {
196,794✔
221
      addHit();
196,773✔
222
      return true;
196,773✔
223
    }
196,773✔
224

225
    return false;
21✔
226
  }
196,794✔
227

228
  bool checkOnly(unsigned int rate, unsigned int burst) const // this is not quite fair
229
  {
328,436✔
230
    auto delta = d_prev.udiffAndSet();
328,436✔
231

232
    if (delta > 0.0) { // time, frequently, does go backwards..
328,436!
233
      d_tokens += 1.0 * rate * (delta / 1000000.0);
328,436✔
234
    }
328,436✔
235

236
    if (d_tokens > burst) {
328,436✔
237
      d_tokens = burst;
327,722✔
238
    }
327,722✔
239

240
    bool ret = false;
328,436✔
241
    if (d_tokens >= 1.0) { // we need this because burst=1 is weird otherwise
328,436✔
242
      ret = true;
328,407✔
243
    }
328,407✔
244

245
    return ret;
328,436✔
246
  }
328,436✔
247

248
  virtual void addHit() const
249
  {
327,735✔
250
    --d_tokens;
327,735✔
251
  }
327,735✔
252

253
  bool seenSince(const struct timespec& cutOff) const
254
  {
13,150✔
255
    return cutOff < d_prev.d_start;
13,150✔
256
  }
13,150✔
257

258
protected:
259
  mutable StopWatch d_prev;
260
  mutable double d_tokens{0.0};
261
};
262

263
class QPSLimiter : public BasicQPSLimiter
264
{
265
public:
266
  QPSLimiter() :
267
    BasicQPSLimiter()
268
  {
1,564✔
269
  }
1,564✔
270

271
  QPSLimiter(unsigned int rate, unsigned int burst) :
272
    BasicQPSLimiter(burst), d_rate(rate), d_burst(burst), d_passthrough(false)
273
  {
327,738✔
274
    d_prev.start();
327,738✔
275
  }
327,738✔
276

277
  unsigned int getRate() const
278
  {
1,270✔
279
    return d_passthrough ? 0 : d_rate;
1,270!
280
  }
1,270✔
281

282
  bool check() const // this is not quite fair
283
  {
150✔
284
    if (d_passthrough) {
150!
285
      return true;
×
286
    }
×
287

288
    return BasicQPSLimiter::check(d_rate, d_burst);
150✔
289
  }
150✔
290

291
  bool checkOnly() const
292
  {
224✔
293
    if (d_passthrough) {
224✔
294
      return true;
100✔
295
    }
100✔
296

297
    return BasicQPSLimiter::checkOnly(d_rate, d_burst);
124✔
298
  }
224✔
299

300
  void addHit() const override
301
  {
19,210✔
302
    if (!d_passthrough) {
19,210✔
303
      --d_tokens;
240✔
304
    }
240✔
305
  }
19,210✔
306

307
private:
308
  unsigned int d_rate{0};
309
  unsigned int d_burst{0};
310
  bool d_passthrough{true};
311
};
312

313
class XskPacket;
314
class XskSocket;
315
class XskWorker;
316

317
class DNSCryptContext;
318

319
struct ClientState
320
{
321
  ClientState(const ComboAddress& local_, bool isTCP_, bool doReusePort, int fastOpenQueue, const std::string& itfName, const std::set<int>& cpus_, bool enableProxyProtocol) :
322
    cpus(cpus_), interface(itfName), local(local_), fastOpenQueueSize(fastOpenQueue), tcp(isTCP_), reuseport(doReusePort), d_enableProxyProtocol(enableProxyProtocol)
323
  {
1,763✔
324
  }
1,763✔
325

326
  stat_t queries{0};
327
  stat_t nonCompliantQueries{0};
328
  mutable stat_t responses{0};
329
  mutable stat_t tcpDiedReadingQuery{0};
330
  mutable stat_t tcpDiedSendingResponse{0};
331
  mutable stat_t tcpGaveUp{0};
332
  mutable stat_t tcpClientTimeouts{0};
333
  mutable stat_t tcpDownstreamTimeouts{0};
334
  /* current number of connections to this frontend */
335
  mutable stat_t tcpCurrentConnections{0};
336
  /* maximum number of concurrent connections to this frontend reached */
337
  mutable stat_t tcpMaxConcurrentConnections{0};
338
  stat_t tlsNewSessions{0}; // A new TLS session has been negotiated, no resumption
339
  stat_t tlsResumptions{0}; // A TLS session has been resumed, either via session id or via a TLS ticket
340
  stat_t tlsUnknownTicketKey{0}; // A TLS ticket has been presented but we don't have the associated key (might have expired)
341
  stat_t tlsInactiveTicketKey{0}; // A TLS ticket has been successfully resumed but the key is no longer active, we should issue a new one
342
  stat_t tls10queries{0}; // valid DNS queries received via TLSv1.0
343
  stat_t tls11queries{0}; // valid DNS queries received via TLSv1.1
344
  stat_t tls12queries{0}; // valid DNS queries received via TLSv1.2
345
  stat_t tls13queries{0}; // valid DNS queries received via TLSv1.3
346
  stat_t tlsUnknownqueries{0}; // valid DNS queries received via unknown TLS version
347
  pdns::stat_double_t tcpAvgQueriesPerConnection{0.0};
348
  /* in ms */
349
  pdns::stat_double_t tcpAvgConnectionDuration{0.0};
350
  std::set<int> cpus;
351
  std::string interface;
352
  ComboAddress local;
353
  std::vector<std::pair<ComboAddress, int>> d_additionalAddresses;
354
  std::shared_ptr<DNSCryptContext> dnscryptCtx{nullptr};
355
  std::shared_ptr<TLSFrontend> tlsFrontend{nullptr};
356
  std::shared_ptr<DOHFrontend> dohFrontend{nullptr};
357
  std::shared_ptr<DOQFrontend> doqFrontend{nullptr};
358
  std::shared_ptr<DOH3Frontend> doh3Frontend{nullptr};
359
  std::shared_ptr<BPFFilter> d_filter{nullptr};
360
  std::shared_ptr<XskWorker> xskInfo{nullptr};
361
  std::shared_ptr<XskWorker> xskInfoResponder{nullptr};
362
  size_t d_maxInFlightQueriesPerConn{1};
363
  size_t d_tcpConcurrentConnectionsLimit{0};
364
  int udpFD{-1};
365
  int tcpFD{-1};
366
  int tcpListenQueueSize{SOMAXCONN};
367
  int fastOpenQueueSize{0};
368
  bool muted{false};
369
  bool tcp;
370
  bool reuseport;
371
  bool d_enableProxyProtocol{true}; // the global proxy protocol ACL still applies
372
  bool ready{false};
373

374
  int getSocket() const
375
  {
×
376
    return udpFD != -1 ? udpFD : tcpFD;
×
377
  }
×
378

379
  bool isUDP() const
380
  {
2✔
381
    return udpFD != -1;
2✔
382
  }
2✔
383

384
  bool isTCP() const
385
  {
4✔
386
    return udpFD == -1;
4✔
387
  }
4✔
388

389
  bool isDoH() const
390
  {
2,066✔
391
    return dohFrontend != nullptr;
2,066✔
392
  }
2,066✔
393

394
  bool hasTLS() const
395
  {
31,586✔
396
    return tlsFrontend != nullptr || (dohFrontend != nullptr && dohFrontend->isHTTPS());
31,586✔
397
  }
31,586✔
398

399
  const TLSFrontend& getTLSFrontend() const
400
  {
1,360✔
401
    if (tlsFrontend != nullptr) {
1,360✔
402
      return *tlsFrontend;
792✔
403
    }
792✔
404
    if (dohFrontend) {
568!
405
      return dohFrontend->d_tlsContext;
568✔
406
    }
568✔
UNCOV
407
    throw std::runtime_error("Trying to get a TLS frontend from a non-TLS ClientState");
×
408
  }
568✔
409

410
  dnsdist::Protocol getProtocol() const
411
  {
1,074✔
412
    if (dnscryptCtx) {
1,074✔
413
      if (udpFD != -1) {
42✔
414
        return dnsdist::Protocol::DNSCryptUDP;
14✔
415
      }
14✔
416
      return dnsdist::Protocol::DNSCryptTCP;
28✔
417
    }
42✔
418
    if (isDoH()) {
1,032✔
419
      return dnsdist::Protocol::DoH;
242✔
420
    }
242✔
421
    else if (hasTLS()) {
790✔
422
      return dnsdist::Protocol::DoT;
150✔
423
    }
150✔
424
    else if (doqFrontend != nullptr) {
640!
425
      return dnsdist::Protocol::DoQ;
×
426
    }
×
427
    else if (doh3Frontend != nullptr) {
640!
428
      return dnsdist::Protocol::DoH3;
×
429
    }
×
430
    else if (udpFD != -1) {
640✔
431
      return dnsdist::Protocol::DoUDP;
320✔
432
    }
320✔
433
    else {
320✔
434
      return dnsdist::Protocol::DoTCP;
320✔
435
    }
320✔
436
  }
1,032✔
437

438
  std::string getType() const
439
  {
1,884✔
440
    std::string result = udpFD != -1 ? "UDP" : "TCP";
1,884✔
441

442
    if (doqFrontend) {
1,884!
443
      result += " (DNS over QUIC)";
×
444
    }
×
445
    else if (doh3Frontend) {
1,884!
446
      result += " (DNS over HTTP/3)";
×
447
    }
×
448
    else if (dohFrontend) {
1,884✔
449
      if (dohFrontend->isHTTPS()) {
360!
450
        result += " (DNS over HTTPS)";
360✔
451
      }
360✔
452
      else {
×
453
        result += " (DNS over HTTP)";
×
454
      }
×
455
    }
360✔
456
    else if (tlsFrontend) {
1,524✔
457
      result += " (DNS over TLS)";
360✔
458
    }
360✔
459
    else if (dnscryptCtx) {
1,164!
460
      result += " (DNSCrypt)";
×
461
    }
×
462

463
    return result;
1,884✔
464
  }
1,884✔
465

466
  void detachFilter(int socket)
467
  {
12✔
468
    if (d_filter) {
12!
469
      d_filter->removeSocket(socket);
×
470
      d_filter = nullptr;
×
471
    }
×
472
  }
12✔
473

474
  void attachFilter(shared_ptr<BPFFilter>& bpf, int socket)
475
  {
12✔
476
    detachFilter(socket);
12✔
477

478
    bpf->addSocket(socket);
12✔
479
    d_filter = bpf;
12✔
480
  }
12✔
481

482
  void detachFilter()
483
  {
×
484
    if (d_filter) {
×
485
      detachFilter(getSocket());
×
486
      for (const auto& [addr, socket] : d_additionalAddresses) {
×
487
        (void)addr;
×
488
        if (socket != -1) {
×
489
          detachFilter(socket);
×
490
        }
×
491
      }
×
492

×
493
      d_filter = nullptr;
×
494
    }
×
495
  }
×
496

497
  void attachFilter(shared_ptr<BPFFilter>& bpf)
498
  {
×
499
    detachFilter();
×
500

×
501
    bpf->addSocket(getSocket());
×
502
    for (const auto& [addr, socket] : d_additionalAddresses) {
×
503
      (void)addr;
×
504
      if (socket != -1) {
×
505
        bpf->addSocket(socket);
×
506
      }
×
507
    }
×
508
    d_filter = bpf;
×
509
  }
×
510

511
  void updateTCPMetrics(size_t nbQueries, uint64_t durationMs)
512
  {
4,703✔
513
    tcpAvgQueriesPerConnection = (99.0 * tcpAvgQueriesPerConnection / 100.0) + (nbQueries / 100.0);
4,703✔
514
    tcpAvgConnectionDuration = (99.0 * tcpAvgConnectionDuration / 100.0) + (durationMs / 100.0);
4,703✔
515
  }
4,703✔
516
};
517

518
struct CrossProtocolQuery;
519
class FDMultiplexer;
520

521
struct DownstreamState : public std::enable_shared_from_this<DownstreamState>
522
{
523
  DownstreamState(const DownstreamState&) = delete;
524
  DownstreamState(DownstreamState&&) = delete;
525
  DownstreamState& operator=(const DownstreamState&) = delete;
526
  DownstreamState& operator=(DownstreamState&&) = delete;
527

528
  typedef std::function<std::tuple<DNSName, uint16_t, uint16_t>(const DNSName&, uint16_t, uint16_t, dnsheader*)> checkfunc_t;
529
  enum class Availability : uint8_t
530
  {
531
    Up,
532
    Down,
533
    Auto,
534
    Lazy
535
  };
536
  enum class LazyHealthCheckMode : uint8_t
537
  {
538
    TimeoutOnly,
539
    TimeoutOrServFail
540
  };
541

542
  struct Config
543
  {
544
    Config()
545
    {
1,444✔
546
    }
1,444✔
547
    Config(const ComboAddress& remote_) :
548
      remote(remote_)
549
    {
388✔
550
    }
388✔
551

552
    TLSContextParameters d_tlsParams;
553
    set<string> pools;
554
    std::set<int> d_cpus;
555
    checkfunc_t checkFunction;
556
    std::optional<boost::uuids::uuid> id;
557
    DNSName checkName{"a.root-servers.net."};
558
    ComboAddress remote;
559
    ComboAddress sourceAddr;
560
    std::string sourceItfName;
561
    std::string d_tlsSubjectName;
562
    std::string d_dohPath;
563
    std::string name;
564
    std::string nameWithAddr;
565
#ifdef HAVE_XSK
566
    std::array<uint8_t, 6> sourceMACAddr;
567
    std::array<uint8_t, 6> destMACAddr;
568
#endif /* HAVE_XSK */
569
    size_t d_numberOfSockets{1};
570
    size_t d_maxInFlightQueriesPerConn{1};
571
    size_t d_tcpConcurrentConnectionsLimit{0};
572
    int order{1};
573
    int d_weight{1};
574
    int tcpConnectTimeout{5};
575
    int tcpRecvTimeout{30};
576
    int tcpSendTimeout{30};
577
    int d_qpsLimit{0};
578
    unsigned int checkInterval{1};
579
    unsigned int sourceItf{0};
580
    QType checkType{QType::A};
581
    uint16_t checkClass{QClass::IN};
582
    uint16_t d_retries{5};
583
    uint16_t checkTimeout{1000}; /* in milliseconds */
584
    uint16_t d_lazyHealthCheckSampleSize{100};
585
    uint16_t d_lazyHealthCheckMinSampleCount{1};
586
    uint16_t d_lazyHealthCheckFailedInterval{30};
587
    uint16_t d_lazyHealthCheckMaxBackOff{3600};
588
    uint8_t d_lazyHealthCheckThreshold{20};
589
    LazyHealthCheckMode d_lazyHealthCheckMode{LazyHealthCheckMode::TimeoutOrServFail};
590
    uint8_t maxCheckFailures{1};
591
    uint8_t minRiseSuccesses{1};
592
    Availability availability{Availability::Auto};
593
    bool d_tlsSubjectIsAddr{false};
594
    bool mustResolve{false};
595
    bool useECS{false};
596
    bool useProxyProtocol{false};
597
    bool d_proxyProtocolAdvertiseTLS{false};
598
    bool setCD{false};
599
    bool disableZeroScope{false};
600
    bool tcpFastOpen{false};
601
    bool ipBindAddrNoPort{true};
602
    bool reconnectOnUp{false};
603
    bool d_tcpCheck{false};
604
    bool d_tcpOnly{false};
605
    bool d_addXForwardedHeaders{false}; // for DoH backends
606
    bool d_lazyHealthCheckUseExponentialBackOff{false};
607
    bool d_upgradeToLazyHealthChecks{false};
608
  };
609

610
  struct HealthCheckMetrics
611
  {
612
    stat_t d_failures{0};
613
    stat_t d_timeOuts{0};
614
    stat_t d_parseErrors{0};
615
    stat_t d_networkErrors{0};
616
    stat_t d_mismatchErrors{0};
617
    stat_t d_invalidResponseErrors{0};
618
  };
619

620
  DownstreamState(DownstreamState::Config&& config, std::shared_ptr<TLSCtx> tlsCtx, bool connect);
621
  DownstreamState(const ComboAddress& remote) :
622
    DownstreamState(DownstreamState::Config(remote), nullptr, false)
623
  {
388✔
624
  }
388✔
625

626
  ~DownstreamState();
627

628
  Config d_config;
629
  HealthCheckMetrics d_healthCheckMetrics;
630
  stat_t sendErrors{0};
631
  stat_t outstanding{0};
632
  stat_t reuseds{0};
633
  stat_t queries{0};
634
  stat_t responses{0};
635
  stat_t nonCompliantResponses{0};
636
  struct
637
  {
638
    stat_t sendErrors{0};
639
    stat_t reuseds{0};
640
    stat_t queries{0};
641
  } prev;
642
  stat_t tcpDiedSendingQuery{0};
643
  stat_t tcpDiedReadingResponse{0};
644
  stat_t tcpGaveUp{0};
645
  stat_t tcpReadTimeouts{0};
646
  stat_t tcpWriteTimeouts{0};
647
  stat_t tcpConnectTimeouts{0};
648
  /* current number of connections to this backend */
649
  stat_t tcpCurrentConnections{0};
650
  /* maximum number of concurrent connections to this backend reached */
651
  stat_t tcpMaxConcurrentConnections{0};
652
  /* number of times we had to enforce the maximum concurrent connections limit */
653
  stat_t tcpTooManyConcurrentConnections{0};
654
  stat_t tcpReusedConnections{0};
655
  stat_t tcpNewConnections{0};
656
  stat_t tlsResumptions{0};
657
  pdns::stat_double_t tcpAvgQueriesPerConnection{0.0};
658
  /* in ms */
659
  pdns::stat_double_t tcpAvgConnectionDuration{0.0};
660
  pdns::stat_double_t queryLoad{0.0};
661
  pdns::stat_double_t dropRate{0.0};
662

663
  SharedLockGuarded<std::vector<unsigned int>> hashes;
664
  LockGuarded<std::unique_ptr<FDMultiplexer>> mplexer{nullptr};
665

666
private:
667
  LockGuarded<std::map<uint16_t, IDState>> d_idStatesMap;
668
  vector<IDState> idStates;
669

670
  struct LazyHealthCheckStats
671
  {
672
    boost::circular_buffer<bool> d_lastResults;
673
    time_t d_nextCheck{0};
674
    enum class LazyStatus : uint8_t
675
    {
676
      Healthy = 0,
677
      PotentialFailure,
678
      Failed
679
    };
680
    LazyStatus d_status{LazyStatus::Healthy};
681
  };
682
  LockGuarded<LazyHealthCheckStats> d_lazyHealthCheckStats;
683

684
public:
685
  std::shared_ptr<TLSCtx> d_tlsCtx{nullptr};
686
  std::vector<int> sockets;
687
  StopWatch sw;
688
  QPSLimiter qps;
689
#ifdef HAVE_XSK
690
  std::vector<std::shared_ptr<XskWorker>> d_xskInfos;
691
  std::vector<std::shared_ptr<XskSocket>> d_xskSockets;
692
#endif
693
  std::atomic<uint64_t> idOffset{0};
694
  size_t socketsOffset{0};
695
  double latencyUsec{0.0};
696
  double latencyUsecTCP{0.0};
697
  unsigned int d_nextCheck{0};
698
  uint16_t currentCheckFailures{0};
699
  std::atomic<bool> hashesComputed{false};
700
  std::atomic<bool> connected{false};
701
  std::atomic<bool> upStatus{false};
702

703
private:
704
  void handleUDPTimeout(IDState& ids);
705
  void updateNextLazyHealthCheck(LazyHealthCheckStats& stats, bool checkScheduled, std::optional<time_t> currentTime = std::nullopt);
706
  void connectUDPSockets();
707
#ifdef HAVE_XSK
708
  void addXSKDestination(int fd);
709
  void removeXSKDestination(int fd);
710
#endif /* HAVE_XSK */
711

712
  std::mutex connectLock;
713
  std::condition_variable d_connectedWait;
714
#ifdef HAVE_XSK
715
  SharedLockGuarded<std::vector<ComboAddress>> d_socketSourceAddresses;
716
#endif
717
  std::atomic_flag threadStarted;
718
  uint8_t consecutiveSuccessfulChecks{0};
719
  bool d_stopped{false};
720

721
public:
722
  void updateStatisticsInfo()
723
  {
1,287✔
724
    auto delta = sw.udiffAndSet() / 1000000.0;
1,287✔
725
    queryLoad.store(1.0 * (queries.load() - prev.queries.load()) / delta);
1,287✔
726
    dropRate.store(1.0 * (reuseds.load() - prev.reuseds.load()) / delta);
1,287✔
727
    prev.queries.store(queries.load());
1,287✔
728
    prev.reuseds.store(reuseds.load());
1,287✔
729
  }
1,287✔
730
  void start();
731

732
  bool isUp() const
733
  {
26,305✔
734
    if (d_config.availability == Availability::Down) {
26,305✔
735
      return false;
269✔
736
    }
269✔
737
    else if (d_config.availability == Availability::Up) {
26,036✔
738
      return true;
11,554✔
739
    }
11,554✔
740
    return upStatus.load(std::memory_order_relaxed);
14,482✔
741
  }
26,305✔
742

743
  void setUp()
744
  {
174✔
745
    d_config.availability = Availability::Up;
174✔
746
  }
174✔
747

748
  void setUpStatus(bool newStatus)
749
  {
969✔
750
    upStatus.store(newStatus);
969✔
751
    if (!newStatus) {
969✔
752
      latencyUsec = 0.0;
35✔
753
      latencyUsecTCP = 0.0;
35✔
754
    }
35✔
755
  }
969✔
756
  void setDown()
757
  {
25✔
758
    d_config.availability = Availability::Down;
25✔
759
    latencyUsec = 0.0;
25✔
760
    latencyUsecTCP = 0.0;
25✔
761
  }
25✔
762
  void setAuto()
763
  {
9✔
764
    d_config.availability = Availability::Auto;
9✔
765
  }
9✔
766
  void setLazyAuto()
767
  {
×
768
    d_config.availability = Availability::Lazy;
×
769
    d_lazyHealthCheckStats.lock()->d_lastResults.set_capacity(d_config.d_lazyHealthCheckSampleSize);
×
770
  }
×
771
  bool healthCheckRequired(std::optional<time_t> currentTime = std::nullopt);
772

773
  const string& getName() const
774
  {
3,728✔
775
    return d_config.name;
3,728✔
776
  }
3,728✔
777
  const string& getNameWithAddr() const
778
  {
8,176✔
779
    return d_config.nameWithAddr;
8,176✔
780
  }
8,176✔
781
  void setName(const std::string& newName)
782
  {
1,564✔
783
    d_config.name = newName;
1,564✔
784
    d_config.nameWithAddr = newName.empty() ? d_config.remote.toStringWithPort() : (d_config.name + " (" + d_config.remote.toStringWithPort() + ")");
1,564✔
785
  }
1,564✔
786

787
  string getStatus() const
788
  {
116✔
789
    string status;
116✔
790
    if (d_config.availability == DownstreamState::Availability::Up) {
116✔
791
      status = "UP";
38✔
792
    }
38✔
793
    else if (d_config.availability == DownstreamState::Availability::Down) {
78✔
794
      status = "DOWN";
2✔
795
    }
2✔
796
    else {
76✔
797
      status = (upStatus.load(std::memory_order_relaxed) ? "up" : "down");
76✔
798
    }
76✔
799
    return status;
116✔
800
  }
116✔
801

802
  bool reconnect(bool initialAttempt = false);
803
  void waitUntilConnected();
804
  void hash();
805
  void setId(const boost::uuids::uuid& newId);
806
  void setWeight(int newWeight);
807
  void stop();
808
  bool isStopped() const
809
  {
12,856✔
810
    return d_stopped;
12,856✔
811
  }
12,856✔
812
  const boost::uuids::uuid& getID() const
813
  {
28,104✔
814
    return *d_config.id;
28,104✔
815
  }
28,104✔
816

817
  void updateTCPMetrics(size_t nbQueries, uint64_t durationMs)
818
  {
213✔
819
    tcpAvgQueriesPerConnection = (99.0 * tcpAvgQueriesPerConnection / 100.0) + (nbQueries / 100.0);
213✔
820
    tcpAvgConnectionDuration = (99.0 * tcpAvgConnectionDuration / 100.0) + (durationMs / 100.0);
213✔
821
  }
213✔
822

823
  void updateTCPLatency(double udiff)
824
  {
4,719✔
825
    latencyUsecTCP = (127.0 * latencyUsecTCP / 128.0) + udiff / 128.0;
4,719✔
826
  }
4,719✔
827

828
  void incQueriesCount()
829
  {
3,798✔
830
    ++queries;
3,798✔
831
    qps.addHit();
3,798✔
832
  }
3,798✔
833

834
  void incCurrentConnectionsCount();
835

836
  bool doHealthcheckOverTCP() const
837
  {
2,473✔
838
    return d_config.d_tcpOnly || d_config.d_tcpCheck || d_tlsCtx != nullptr;
2,473!
839
  }
2,473✔
840

841
  bool isTCPOnly() const
842
  {
130,522✔
843
    return d_config.d_tcpOnly || d_tlsCtx != nullptr;
130,522✔
844
  }
130,522✔
845

846
  bool isDoH() const
847
  {
67,605✔
848
    return !d_config.d_dohPath.empty();
67,605✔
849
  }
67,605✔
850

851
  bool passCrossProtocolQuery(std::unique_ptr<CrossProtocolQuery>&& cpq);
852
  int pickSocketForSending();
853
  void pickSocketsReadyForReceiving(std::vector<int>& ready);
854
  void handleUDPTimeouts();
855
  void reportTimeoutOrError();
856
  void reportResponse(uint8_t rcode);
857
  void submitHealthCheckResult(bool initial, bool newResult);
858
  time_t getNextLazyHealthCheck();
859
  uint16_t saveState(InternalQueryState&&);
860
  void restoreState(uint16_t id, InternalQueryState&&);
861
  std::optional<InternalQueryState> getState(uint16_t id);
862

863
#ifdef HAVE_XSK
864
  void registerXsk(std::vector<std::shared_ptr<XskSocket>>& xsks);
865
  [[nodiscard]] ComboAddress pickSourceAddressForSending();
866
#endif /* HAVE_XSK */
867

868
  dnsdist::Protocol getProtocol() const
869
  {
42,972✔
870
    if (isDoH()) {
42,972✔
871
      return dnsdist::Protocol::DoH;
1,248✔
872
    }
1,248✔
873
    if (d_tlsCtx != nullptr) {
41,724✔
874
      return dnsdist::Protocol::DoT;
1,532✔
875
    }
1,532✔
876
    if (isTCPOnly()) {
40,192✔
877
      return dnsdist::Protocol::DoTCP;
896✔
878
    }
896✔
879
    return dnsdist::Protocol::DoUDP;
39,296✔
880
  }
40,192✔
881

882
  double getRelevantLatencyUsec() const
883
  {
21✔
884
    if (isTCPOnly()) {
21!
885
      return latencyUsecTCP;
×
886
    }
×
887
    return latencyUsec;
21✔
888
  }
21✔
889
};
890

891
void responderThread(std::shared_ptr<DownstreamState> dss);
892

893
class DNSDistPacketCache;
894

895
class DNSRule
896
{
897
public:
898
  virtual ~DNSRule()
899
  {
336✔
900
  }
336✔
901
  virtual bool matches(const DNSQuestion* dq) const = 0;
902
  virtual string toString() const = 0;
903
  mutable stat_t d_matches{0};
904
};
905

906
struct ServerPool
907
{
908
  ServerPool() :
909
    d_servers(std::make_shared<const ServerPolicy::NumberedServerVector>())
910
  {
1,414✔
911
  }
1,414✔
912

913
  ~ServerPool()
914
  {
574✔
915
  }
574✔
916

917
  const std::shared_ptr<DNSDistPacketCache> getCache() const { return packetCache; };
46✔
918

919
  bool getECS() const
920
  {
64✔
921
    return d_useECS;
64✔
922
  }
64✔
923

924
  void setECS(bool useECS)
925
  {
2✔
926
    d_useECS = useECS;
2✔
927
  }
2✔
928

929
  std::shared_ptr<DNSDistPacketCache> packetCache{nullptr};
930
  std::shared_ptr<ServerPolicy> policy{nullptr};
931

932
  size_t poolLoad();
933
  size_t countServers(bool upOnly);
934
  const std::shared_ptr<const ServerPolicy::NumberedServerVector> getServers();
935
  void addServer(shared_ptr<DownstreamState>& server);
936
  void removeServer(shared_ptr<DownstreamState>& server);
937

938
private:
939
  SharedLockGuarded<std::shared_ptr<const ServerPolicy::NumberedServerVector>> d_servers;
940
  bool d_useECS{false};
941
};
942

943
enum ednsHeaderFlags
944
{
945
  EDNS_HEADER_FLAG_NONE = 0,
946
  EDNS_HEADER_FLAG_DO = 32768
947
};
948

949
extern shared_ptr<BPFFilter> g_defaultBPFFilter;
950

951
void tcpAcceptorThread(const std::vector<ClientState*>& states);
952

953
void setLuaNoSideEffect(); // if nothing has been declared, set that there are no side effects
954
void setLuaSideEffect(); // set to report a side effect, cancelling all _no_ side effect calls
955
bool getLuaNoSideEffect(); // set if there were only explicit declarations of _no_ side effect
956
void resetLuaSideEffect(); // reset to indeterminate state
957

958
bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr<DownstreamState>& remote, bool allowEmptyResponse);
959

960
bool checkQueryHeaders(const struct dnsheader& dnsHeader, ClientState& clientState);
961

962
class DNSCryptQuery;
963

964
bool handleDNSCryptQuery(PacketBuffer& packet, DNSCryptQuery& query, bool tcp, time_t now, PacketBuffer& response);
965
bool checkDNSCryptQuery(const ClientState& clientState, PacketBuffer& query, std::unique_ptr<DNSCryptQuery>& dnsCryptQuery, time_t now, bool tcp);
966

967
enum class ProcessQueryResult : uint8_t
968
{
969
  Drop,
970
  SendAnswer,
971
  PassToBackend,
972
  Asynchronous
973
};
974

975
#include "dnsdist-actions.hh"
976
#include "dnsdist-rule-chains.hh"
977

978
ProcessQueryResult processQuery(DNSQuestion& dnsQuestion, std::shared_ptr<DownstreamState>& selectedBackend);
979
ProcessQueryResult processQueryAfterRules(DNSQuestion& dnsQuestion, std::shared_ptr<DownstreamState>& selectedBackend);
980
bool processResponse(PacketBuffer& response, DNSResponse& dnsResponse, bool muted);
981
bool processRulesResult(const DNSAction::Action& action, DNSQuestion& dnsQuestion, std::string& ruleresult, bool& drop);
982
bool processResponseAfterRules(PacketBuffer& response, DNSResponse& dnsResponse, bool muted);
983
bool processResponderPacket(std::shared_ptr<DownstreamState>& dss, PacketBuffer& response, InternalQueryState&& ids);
984
bool applyRulesToResponse(const std::vector<dnsdist::rules::ResponseRuleAction>& respRuleActions, DNSResponse& dnsResponse);
985

986
bool assignOutgoingUDPQueryToBackend(std::shared_ptr<DownstreamState>& downstream, uint16_t queryID, DNSQuestion& dnsQuestion, PacketBuffer& query, bool actuallySend = true);
987

988
ssize_t udpClientSendRequestToBackend(const std::shared_ptr<DownstreamState>& backend, const int socketDesc, const PacketBuffer& request, bool healthCheck = false);
989
bool sendUDPResponse(int origFD, const PacketBuffer& response, const int delayMsec, const ComboAddress& origDest, const ComboAddress& origRemote);
990
void handleResponseSent(const DNSName& qname, const QType& qtype, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol outgoingProtocol, dnsdist::Protocol incomingProtocol, bool fromBackend);
991
void handleResponseSent(const InternalQueryState& ids, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol outgoingProtocol, bool fromBackend);
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