• 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

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

23
#include "config.h"
24

25
#include <cstdint>
26
#include <filesystem>
27
#include <fstream>
28
#include <getopt.h>
29
#include <grp.h>
30
#include <limits>
31
#include <netinet/tcp.h>
32
#include <optional>
33
#include <pwd.h>
34
#include <set>
35
#include <sys/resource.h>
36
#include <unistd.h>
37

38
#include "dns.hh"
39
#include "dnsdist-idstate.hh"
40
#include "dnsdist-opentelemetry.hh"
41
#include "dnsdist-systemd.hh"
42
#include "protozero-trace.hh"
43
#ifdef HAVE_SYSTEMD
44
#include <systemd/sd-daemon.h>
45
#endif
46

47
#include "dnsdist.hh"
48
#include "dnsdist-async.hh"
49
#include "dnsdist-cache.hh"
50
#include "dnsdist-carbon.hh"
51
#include "dnsdist-configuration.hh"
52
#include "dnsdist-configuration-yaml.hh"
53
#include "dnsdist-console.hh"
54
#include "dnsdist-console-completion.hh"
55
#include "dnsdist-crypto.hh"
56
#include "dnsdist-discovery.hh"
57
#include "dnsdist-dynblocks.hh"
58
#include "dnsdist-ecs.hh"
59
#include "dnsdist-edns.hh"
60
#include "dnsdist-frontend.hh"
61
#include "dnsdist-healthchecks.hh"
62
#include "dnsdist-lua.hh"
63
#include "dnsdist-lua-hooks.hh"
64
#include "dnsdist-nghttp2.hh"
65
#include "dnsdist-nghttp2-in.hh"
66
#include "dnsdist-proxy-protocol.hh"
67
#include "dnsdist-random.hh"
68
#include "dnsdist-rings.hh"
69
#include "dnsdist-rules.hh"
70
#include "dnsdist-secpoll.hh"
71
#include "dnsdist-self-answers.hh"
72
#include "dnsdist-snmp.hh"
73
#include "dnsdist-tcp.hh"
74
#include "dnsdist-tcp-downstream.hh"
75
#include "dnsdist-tcp-upstream.hh"
76
#include "dnsdist-web.hh"
77
#include "dnsdist-xsk.hh"
78

79
#include "base64.hh"
80
#include "capabilities.hh"
81
#include "coverage.hh"
82
#include "delaypipe.hh"
83
#include "doh.hh"
84
#include "dolog.hh"
85
#include "dnsname.hh"
86
#include "ednsoptions.hh"
87
#include "gettime.hh"
88
#include "lock.hh"
89
#include "misc.hh"
90
#include "sstuff.hh"
91
#include "threadname.hh"
92
#include "xsk.hh"
93

94
/* Known sins:
95

96
   Receiver is currently single threaded
97
      not *that* bad actually, but now that we are thread safe, might want to scale
98
*/
99

100
/* the RuleAction plan
101
   Set of Rules, if one matches, it leads to an Action
102
   Both rules and actions could conceivably be Lua based.
103
   On the C++ side, both could be inherited from a class Rule and a class Action,
104
   on the Lua side we can't do that. */
105

106
using std::thread;
107

108
string g_outputBuffer;
109

110
shared_ptr<BPFFilter> g_defaultBPFFilter{nullptr};
111

112
/* UDP: the grand design. Per socket we listen on for incoming queries there is one thread.
113
   Then we have a bunch of connected sockets for talking to downstream servers.
114
   We send directly to those sockets.
115

116
   For the return path, per downstream server we have a thread that listens to responses.
117

118
   Per socket there is an array of 2^16 states, when we send out a packet downstream, we note
119
   there the original requestor and the original id. The new ID is the offset in the array.
120

121
   When an answer comes in on a socket, we look up the offset by the id, and lob it to the
122
   original requestor.
123

124
   IDs are assigned by atomic increments of the socket offset.
125
 */
126

127
Rings g_rings;
128

129
// we are not willing to receive a bigger UDP response than that, no matter what
130
static constexpr size_t s_maxUDPResponsePacketSize{4096U};
131
static size_t const s_initialUDPPacketBufferSize = s_maxUDPResponsePacketSize + DNSCRYPT_MAX_RESPONSE_PADDING_AND_MAC_SIZE;
132
static_assert(s_initialUDPPacketBufferSize <= UINT16_MAX, "Packet size should fit in a uint16_t");
133

134
static void sendfromto(int sock, const PacketBuffer& buffer, const ComboAddress& from, const ComboAddress& dest)
135
{
2,844✔
136
  const int flags = 0;
2,844✔
137
  if (from.sin4.sin_family == 0) {
2,844✔
138
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
139
    auto ret = sendto(sock, buffer.data(), buffer.size(), flags, reinterpret_cast<const struct sockaddr*>(&dest), dest.getSocklen());
2,839✔
140
    if (ret == -1) {
2,839!
141
      int error = errno;
×
142
      vinfolog("Error sending UDP response to %s: %s", dest.toStringWithPort(), stringerror(error));
×
143
    }
×
144
    return;
2,839✔
145
  }
2,839✔
146

147
  try {
5✔
148
    sendMsgWithOptions(sock, buffer.data(), buffer.size(), &dest, &from, 0, 0);
5✔
149
  }
5✔
150
  catch (const std::exception& exp) {
5✔
151
    vinfolog("Error sending UDP response from %s to %s: %s", from.toStringWithPort(), dest.toStringWithPort(), exp.what());
×
152
  }
×
153
}
5✔
154

155
static void truncateTC(PacketBuffer& packet, size_t maximumSize, unsigned int qnameWireLength, bool addEDNSToSelfGeneratedResponses)
156
{
6✔
157
  try {
6✔
158
    bool hadEDNS = false;
6✔
159
    uint16_t payloadSize = 0;
6✔
160
    uint16_t zValue = 0;
6✔
161

162
    if (addEDNSToSelfGeneratedResponses) {
6!
163
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
164
      hadEDNS = getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(packet.data()), packet.size(), &payloadSize, &zValue);
6✔
165
    }
6✔
166

167
    packet.resize(static_cast<uint16_t>(sizeof(dnsheader) + qnameWireLength + DNS_TYPE_SIZE + DNS_CLASS_SIZE));
6✔
168
    dnsdist::PacketMangling::editDNSHeaderFromPacket(packet, [](dnsheader& header) {
6✔
169
      header.ancount = 0;
6✔
170
      header.arcount = 0;
6✔
171
      header.nscount = 0;
6✔
172
      return true;
6✔
173
    });
6✔
174

175
    if (hadEDNS) {
6✔
176
      addEDNS(packet, maximumSize, (zValue & EDNS_HEADER_FLAG_DO) != 0, payloadSize, 0);
2✔
177
    }
2✔
178
  }
6✔
179
  catch (...) {
6✔
180
    ++dnsdist::metrics::g_stats.truncFail;
×
181
  }
×
182
}
6✔
183

184
#ifndef DISABLE_DELAY_PIPE
185
struct DelayedPacket
186
{
187
  int fd{-1};
188
  PacketBuffer packet;
189
  ComboAddress destination;
190
  ComboAddress origDest;
191
  void operator()() const
192
  {
4✔
193
    sendfromto(fd, packet, origDest, destination);
4✔
194
  }
4✔
195
};
196

197
static std::unique_ptr<DelayPipe<DelayedPacket>> g_delay{nullptr};
198
#endif /* DISABLE_DELAY_PIPE */
199

200
static void doLatencyStats(dnsdist::Protocol protocol, double udiff)
201
{
5,914✔
202
  constexpr auto doAvg = [](pdns::stat_double_t& var, double n, double weight) {
23,656✔
203
    var.store((weight - 1) * var.load() / weight + n / weight);
23,656✔
204
  };
23,656✔
205

206
  if (protocol == dnsdist::Protocol::DoUDP || protocol == dnsdist::Protocol::DNSCryptUDP) {
5,914✔
207
    if (udiff < 1000) {
2,831✔
208
      ++dnsdist::metrics::g_stats.latency0_1;
2,673✔
209
    }
2,673✔
210
    else if (udiff < 10000) {
158✔
211
      ++dnsdist::metrics::g_stats.latency1_10;
147✔
212
    }
147✔
213
    else if (udiff < 50000) {
11!
214
      ++dnsdist::metrics::g_stats.latency10_50;
×
215
    }
×
216
    else if (udiff < 100000) {
11!
217
      ++dnsdist::metrics::g_stats.latency50_100;
×
218
    }
×
219
    else if (udiff < 1000000) {
11✔
220
      ++dnsdist::metrics::g_stats.latency100_1000;
8✔
221
    }
8✔
222
    else {
3✔
223
      ++dnsdist::metrics::g_stats.latencySlow;
3✔
224
    }
3✔
225

226
    dnsdist::metrics::g_stats.latencySum += static_cast<unsigned long>(udiff) / 1000;
2,831✔
227
    ++dnsdist::metrics::g_stats.latencyCount;
2,831✔
228

229
    doAvg(dnsdist::metrics::g_stats.latencyAvg100, udiff, 100);
2,831✔
230
    doAvg(dnsdist::metrics::g_stats.latencyAvg1000, udiff, 1000);
2,831✔
231
    doAvg(dnsdist::metrics::g_stats.latencyAvg10000, udiff, 10000);
2,831✔
232
    doAvg(dnsdist::metrics::g_stats.latencyAvg1000000, udiff, 1000000);
2,831✔
233
  }
2,831✔
234
  else if (protocol == dnsdist::Protocol::DoTCP || protocol == dnsdist::Protocol::DNSCryptTCP) {
3,083✔
235
    doAvg(dnsdist::metrics::g_stats.latencyTCPAvg100, udiff, 100);
2,027✔
236
    doAvg(dnsdist::metrics::g_stats.latencyTCPAvg1000, udiff, 1000);
2,027✔
237
    doAvg(dnsdist::metrics::g_stats.latencyTCPAvg10000, udiff, 10000);
2,027✔
238
    doAvg(dnsdist::metrics::g_stats.latencyTCPAvg1000000, udiff, 1000000);
2,027✔
239
  }
2,027✔
240
  else if (protocol == dnsdist::Protocol::DoT) {
1,056✔
241
    doAvg(dnsdist::metrics::g_stats.latencyDoTAvg100, udiff, 100);
750✔
242
    doAvg(dnsdist::metrics::g_stats.latencyDoTAvg1000, udiff, 1000);
750✔
243
    doAvg(dnsdist::metrics::g_stats.latencyDoTAvg10000, udiff, 10000);
750✔
244
    doAvg(dnsdist::metrics::g_stats.latencyDoTAvg1000000, udiff, 1000000);
750✔
245
  }
750✔
246
  else if (protocol == dnsdist::Protocol::DoH) {
306✔
247
    doAvg(dnsdist::metrics::g_stats.latencyDoHAvg100, udiff, 100);
213✔
248
    doAvg(dnsdist::metrics::g_stats.latencyDoHAvg1000, udiff, 1000);
213✔
249
    doAvg(dnsdist::metrics::g_stats.latencyDoHAvg10000, udiff, 10000);
213✔
250
    doAvg(dnsdist::metrics::g_stats.latencyDoHAvg1000000, udiff, 1000000);
213✔
251
  }
213✔
252
  else if (protocol == dnsdist::Protocol::DoQ) {
93✔
253
    doAvg(dnsdist::metrics::g_stats.latencyDoQAvg100, udiff, 100);
58✔
254
    doAvg(dnsdist::metrics::g_stats.latencyDoQAvg1000, udiff, 1000);
58✔
255
    doAvg(dnsdist::metrics::g_stats.latencyDoQAvg10000, udiff, 10000);
58✔
256
    doAvg(dnsdist::metrics::g_stats.latencyDoQAvg1000000, udiff, 1000000);
58✔
257
  }
58✔
258
  else if (protocol == dnsdist::Protocol::DoH3) {
35!
259
    doAvg(dnsdist::metrics::g_stats.latencyDoH3Avg100, udiff, 100);
35✔
260
    doAvg(dnsdist::metrics::g_stats.latencyDoH3Avg1000, udiff, 1000);
35✔
261
    doAvg(dnsdist::metrics::g_stats.latencyDoH3Avg10000, udiff, 10000);
35✔
262
    doAvg(dnsdist::metrics::g_stats.latencyDoH3Avg1000000, udiff, 1000000);
35✔
263
  }
35✔
264
}
5,914✔
265

266
bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr<DownstreamState>& remote, bool allowEmptyResponse)
267
{
5,107✔
268
  if (response.size() < sizeof(dnsheader)) {
5,107!
269
    return false;
×
270
  }
×
271

272
  const dnsheader_aligned dnsHeader(response.data());
5,107✔
273
  if (dnsHeader->qr == 0) {
5,107!
274
    ++dnsdist::metrics::g_stats.nonCompliantResponses;
×
275
    if (remote) {
×
276
      ++remote->nonCompliantResponses;
×
277
    }
×
278
    return false;
×
279
  }
×
280

281
  if (dnsHeader->qdcount == 0) {
5,107✔
282
    if ((dnsHeader->rcode != RCode::NoError && dnsHeader->rcode != RCode::NXDomain) || allowEmptyResponse) {
14✔
283
      return true;
10✔
284
    }
10✔
285

286
    ++dnsdist::metrics::g_stats.nonCompliantResponses;
4✔
287
    if (remote) {
4!
288
      ++remote->nonCompliantResponses;
4✔
289
    }
4✔
290
    return false;
4✔
291
  }
14✔
292

293
  try {
5,093✔
294
    uint16_t rqtype{};
5,093✔
295
    uint16_t rqclass{};
5,093✔
296
    if (response.size() < (sizeof(dnsheader) + qname.wirelength() + sizeof(rqtype) + sizeof(rqclass))) {
5,093!
297
      return false;
×
298
    }
×
299

300
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
301
    const std::string_view packetView(reinterpret_cast<const char*>(response.data() + sizeof(dnsheader)), response.size() - sizeof(dnsheader));
5,093✔
302
    if (qname.matchesUncompressedName(packetView)) {
5,093✔
303
      size_t pos = sizeof(dnsheader) + qname.wirelength();
5,091✔
304
      rqtype = response.at(pos) * 256 + response.at(pos + 1);
5,091✔
305
      rqclass = response.at(pos + 2) * 256 + response.at(pos + 3);
5,091✔
306
      return rqtype == qtype && rqclass == qclass;
5,091!
307
    }
5,091✔
308
    return false;
2✔
309
  }
5,093✔
310
  catch (const std::exception& e) {
5,093✔
311
    if (remote && !response.empty() && static_cast<size_t>(response.size()) > sizeof(dnsheader)) {
×
312
      infolog("Backend %s sent us a response with id %d that did not parse: %s", remote->d_config.remote.toStringWithPort(), ntohs(dnsHeader->id), e.what());
×
313
    }
×
314
    ++dnsdist::metrics::g_stats.nonCompliantResponses;
×
315
    if (remote) {
×
316
      ++remote->nonCompliantResponses;
×
317
    }
×
318
    return false;
×
319
  }
×
320
}
5,093✔
321

322
static void restoreFlags(struct dnsheader* dnsHeader, uint16_t origFlags)
323
{
5,942✔
324
  static const uint16_t rdMask = 1 << FLAGS_RD_OFFSET;
5,942✔
325
  static const uint16_t cdMask = 1 << FLAGS_CD_OFFSET;
5,942✔
326
  static const uint16_t restoreFlagsMask = UINT16_MAX & ~(rdMask | cdMask);
5,942✔
327
  uint16_t* flags = getFlagsFromDNSHeader(dnsHeader);
5,942✔
328
  /* clear the flags we are about to restore */
329
  *flags &= restoreFlagsMask;
5,942✔
330
  /* only keep the flags we want to restore */
331
  origFlags &= ~restoreFlagsMask;
5,942✔
332
  /* set the saved flags as they were */
333
  *flags |= origFlags;
5,942✔
334
}
5,942✔
335

336
static bool fixUpQueryTurnedResponse(DNSQuestion& dnsQuestion, const uint16_t origFlags)
337
{
486✔
338
  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [origFlags](dnsheader& header) {
486✔
339
    restoreFlags(&header, origFlags);
486✔
340
    return true;
486✔
341
  });
486✔
342

343
  if (dnsQuestion.d_selfGeneratedHandledEDNS) {
486✔
344
    return true;
248✔
345
  }
248✔
346
  return addEDNSToQueryTurnedResponse(dnsQuestion);
238✔
347
}
486✔
348

349
static bool fixUpResponse(PacketBuffer& response, const DNSName& qname, uint16_t origFlags, bool ednsAdded, bool ecsAdded, bool* zeroScope)
350
{
4,914✔
351
  if (response.size() < sizeof(dnsheader)) {
4,914!
352
    return false;
×
353
  }
×
354

355
  dnsdist::PacketMangling::editDNSHeaderFromPacket(response, [origFlags](dnsheader& header) {
4,914✔
356
    restoreFlags(&header, origFlags);
4,914✔
357
    return true;
4,914✔
358
  });
4,914✔
359

360
  if (response.size() == sizeof(dnsheader)) {
4,914✔
361
    return true;
10✔
362
  }
10✔
363

364
  if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_fixupCase) {
4,904✔
365
    const auto& realname = qname.getStorage();
2✔
366
    if (response.size() >= (sizeof(dnsheader) + realname.length())) {
2!
367
      memcpy(&response.at(sizeof(dnsheader)), realname.c_str(), realname.length());
2✔
368
    }
2✔
369
  }
2✔
370

371
  if (ednsAdded || ecsAdded) {
4,904✔
372
    uint16_t optStart{};
230✔
373
    size_t optLen = 0;
230✔
374
    bool last = false;
230✔
375

376
    int res = locateEDNSOptRR(response, &optStart, &optLen, &last);
230✔
377

378
    if (res == 0) {
230✔
379
      if (zeroScope != nullptr) { // this finds if an EDNS Client Subnet scope was set, and if it is 0
62✔
380
        size_t optContentStart = 0;
7✔
381
        uint16_t optContentLen = 0;
7✔
382
        /* we need at least 4 bytes after the option length (family: 2, source prefix-length: 1, scope prefix-length: 1) */
383
        if (isEDNSOptionInOpt(response, optStart, optLen, EDNSOptionCode::ECS, &optContentStart, &optContentLen) && optContentLen >= 4) {
7!
384
          /* see if the EDNS Client Subnet SCOPE PREFIX-LENGTH byte in position 3 is set to 0, which is the only thing
385
             we care about. */
386
          *zeroScope = response.at(optContentStart + 3) == 0;
7✔
387
        }
7✔
388
      }
7✔
389

390
      if (ednsAdded) {
62✔
391
        /* we added the entire OPT RR,
392
           therefore we need to remove it entirely */
393
        if (last) {
44!
394
          /* simply remove the last AR */
395
          response.resize(response.size() - optLen);
44✔
396
          dnsdist::PacketMangling::editDNSHeaderFromPacket(response, [](dnsheader& header) {
44✔
397
            uint16_t arcount = ntohs(header.arcount);
44✔
398
            arcount--;
44✔
399
            header.arcount = htons(arcount);
44✔
400
            return true;
44✔
401
          });
44✔
402
        }
44✔
403
        else {
×
404
          /* Removing an intermediary RR could lead to compression error */
405
          PacketBuffer rewrittenResponse;
×
406
          if (rewriteResponseWithoutEDNS(response, rewrittenResponse) == 0) {
×
407
            response = std::move(rewrittenResponse);
×
408
          }
×
409
          else {
×
410
            warnlog("Error rewriting content");
×
411
          }
×
412
        }
×
413
      }
44✔
414
      else {
18✔
415
        /* the OPT RR was already present, but without ECS,
416
           we need to remove the ECS option if any */
417
        if (last) {
18!
418
          /* nothing after the OPT RR, we can simply remove the
419
             ECS option */
420
          size_t existingOptLen = optLen;
18✔
421
          // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
422
          removeEDNSOptionFromOPT(reinterpret_cast<char*>(&response.at(optStart)), &optLen, EDNSOptionCode::ECS);
18✔
423
          response.resize(response.size() - (existingOptLen - optLen));
18✔
424
        }
18✔
425
        else {
×
426
          PacketBuffer rewrittenResponse;
×
427
          /* Removing an intermediary RR could lead to compression error */
428
          if (rewriteResponseWithoutEDNSOption(response, EDNSOptionCode::ECS, rewrittenResponse) == 0) {
×
429
            response = std::move(rewrittenResponse);
×
430
          }
×
431
          else {
×
432
            warnlog("Error rewriting content");
×
433
          }
×
434
        }
×
435
      }
18✔
436
    }
62✔
437
  }
230✔
438

439
  return true;
4,904✔
440
}
4,914✔
441

442
#ifdef HAVE_DNSCRYPT
443
static bool encryptResponse(PacketBuffer& response, size_t maximumSize, bool tcp, std::unique_ptr<DNSCryptQuery>& dnsCryptQuery)
444
{
5,929✔
445
  if (dnsCryptQuery) {
5,929✔
446
    int res = dnsCryptQuery->encryptResponse(response, maximumSize, tcp);
36✔
447
    if (res != 0) {
36!
448
      /* dropping response */
449
      vinfolog("Error encrypting the response, dropping.");
×
450
      return false;
×
451
    }
×
452
  }
36✔
453
  return true;
5,929✔
454
}
5,929✔
455
#endif /* HAVE_DNSCRYPT */
456

457
bool applyRulesToResponse(const std::vector<dnsdist::rules::ResponseRuleAction>& respRuleActions, DNSResponse& dnsResponse)
458
{
6,971✔
459
  pdns::trace::dnsdist::Tracer::Closer closer;
6,971✔
460
  if (auto tracer = dnsResponse.ids.getTracer(); tracer != nullptr && dnsResponse.ids.tracingEnabled) {
6,971✔
461
    closer = tracer->openSpan("applyRulesToResponse", tracer->getLastSpanID());
8✔
462
  }
8✔
463
  if (respRuleActions.empty()) {
6,971✔
464
    return true;
5,535✔
465
  }
5,535✔
466

467
  DNSResponseAction::Action action = DNSResponseAction::Action::None;
1,436✔
468
  std::string ruleresult;
1,436✔
469
  for (const auto& rrule : respRuleActions) {
1,505✔
470
    if (rrule.d_rule->matches(&dnsResponse)) {
1,505✔
471
      ++rrule.d_rule->d_matches;
751✔
472
      action = (*rrule.d_action)(&dnsResponse, &ruleresult);
751✔
473
      switch (action) {
751✔
474
      case DNSResponseAction::Action::Allow:
168✔
475
        return true;
168✔
476
        break;
×
477
      case DNSResponseAction::Action::Drop:
12✔
478
        return false;
12✔
479
        break;
×
480
      case DNSResponseAction::Action::HeaderModify:
16✔
481
        return true;
16✔
482
        break;
×
483
      case DNSResponseAction::Action::ServFail:
100✔
484
        if (dnsResponse.getData().size() < sizeof(dnsheader)) {
100!
485
          return false;
×
486
        }
×
487
        dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsResponse.getMutableData(), [](dnsheader& header) {
100✔
488
          header.rcode = RCode::ServFail;
100✔
489
          return true;
100✔
490
        });
100✔
491
        return true;
100✔
492
        break;
×
493
      case DNSResponseAction::Action::Truncate:
4✔
494
        if (dnsResponse.getData().size() < sizeof(dnsheader)) {
4!
495
          return false;
×
496
        }
×
497
        if (!dnsResponse.overTCP()) {
4✔
498
          dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsResponse.getMutableData(), [](dnsheader& header) {
2✔
499
            header.tc = true;
2✔
500
            header.qr = true;
2✔
501
            return true;
2✔
502
          });
2✔
503
          truncateTC(dnsResponse.getMutableData(), dnsResponse.getMaximumSize(), dnsResponse.ids.qname.wirelength(), dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses);
2✔
504
          ++dnsdist::metrics::g_stats.ruleTruncated;
2✔
505
          return true;
2✔
506
        }
2✔
507
        break;
2✔
508
        /* non-terminal actions follow */
509
      case DNSResponseAction::Action::Delay:
4✔
510
        pdns::checked_stoi_into(dnsResponse.ids.delayMsec, ruleresult); // sorry
4✔
511
        break;
4✔
512
      case DNSResponseAction::Action::None:
433✔
513
        break;
433✔
514
      }
751✔
515
    }
751✔
516
  }
1,505✔
517

518
  return true;
1,138✔
519
}
1,436✔
520

521
bool processResponseAfterRules(PacketBuffer& response, DNSResponse& dnsResponse, [[maybe_unused]] bool muted)
522
{
4,914✔
523
  pdns::trace::dnsdist::Tracer::Closer closer;
4,914✔
524
  if (auto tracer = dnsResponse.ids.getTracer(); tracer != nullptr && dnsResponse.ids.tracingEnabled) {
4,914✔
525
    closer = tracer->openSpan("processResponseAfterRules", tracer->getLastSpanID());
8✔
526
  }
8✔
527
  bool zeroScope = false;
4,914✔
528
  if (!fixUpResponse(response, dnsResponse.ids.qname, dnsResponse.ids.origFlags, dnsResponse.ids.ednsAdded, dnsResponse.ids.ecsAdded, dnsResponse.ids.useZeroScope ? &zeroScope : nullptr)) {
4,914!
529
    if (auto tracer = dnsResponse.ids.getTracer(); tracer != nullptr && dnsResponse.ids.tracingEnabled) {
×
530
      tracer->setSpanAttribute(closer.getSpanID(), "result", AnyValue{"fixUpResponse->false"});
×
531
    }
×
532
    return false;
×
533
  }
×
534

535
  if (dnsResponse.ids.packetCache && !dnsResponse.ids.selfGenerated && !dnsResponse.ids.skipCache && (!dnsResponse.ids.forwardedOverUDP || response.size() <= s_maxUDPResponsePacketSize)) {
4,914!
536
    if (!dnsResponse.ids.useZeroScope) {
228✔
537
      /* if the query was not suitable for zero-scope, for
538
         example because it had an existing ECS entry so the hash is
539
         not really 'no ECS', so just insert it for the existing subnet
540
         since:
541
         - we don't have the correct hash for a non-ECS query
542
         - inserting with hash computed before the ECS replacement but with
543
         the subnet extracted _after_ the replacement would not work.
544
      */
545
      zeroScope = false;
217✔
546
    }
217✔
547
    uint32_t cacheKey = dnsResponse.ids.cacheKey;
228✔
548
    if (dnsResponse.ids.protocol == dnsdist::Protocol::DoH && !dnsResponse.ids.forwardedOverUDP) {
228✔
549
      cacheKey = dnsResponse.ids.cacheKeyTCP;
2✔
550
      // disable zeroScope in that case, as we only have the "no-ECS" cache key for UDP
551
      zeroScope = false;
2✔
552
    }
2✔
553
    if (zeroScope) {
228✔
554
      // if zeroScope, pass the pre-ECS hash-key and do not pass the subnet to the cache
555
      cacheKey = dnsResponse.ids.cacheKeyNoECS;
3✔
556
    }
3✔
557
    {
228✔
558
      pdns::trace::dnsdist::Tracer::Closer cacheInsertCloser;
228✔
559
      if (auto tracer = dnsResponse.ids.getTracer(); tracer != nullptr && dnsResponse.ids.tracingEnabled) {
228!
560
        cacheInsertCloser = tracer->openSpan("packetCacheInsert", closer.getSpanID());
×
561
      }
×
562
      dnsResponse.ids.packetCache->insert(cacheKey, zeroScope ? boost::none : dnsResponse.ids.subnet, dnsResponse.ids.cacheFlags, dnsResponse.ids.dnssecOK ? *dnsResponse.ids.dnssecOK : false, dnsResponse.ids.qname, dnsResponse.ids.qtype, dnsResponse.ids.qclass, response, dnsResponse.ids.forwardedOverUDP, dnsResponse.getHeader()->rcode, dnsResponse.ids.tempFailureTTL);
228!
563
    }
228✔
564
    const auto& chains = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ruleChains;
228✔
565
    const auto& cacheInsertedRespRuleActions = dnsdist::rules::getResponseRuleChain(chains, dnsdist::rules::ResponseRuleChain::CacheInsertedResponseRules);
228✔
566
    if (!applyRulesToResponse(cacheInsertedRespRuleActions, dnsResponse)) {
228!
567
      return false;
×
568
    }
×
569
  }
228✔
570

571
  if (dnsResponse.ids.ttlCap > 0) {
4,914!
572
    dnsdist::PacketMangling::restrictDNSPacketTTLs(dnsResponse.getMutableData(), 0, dnsResponse.ids.ttlCap);
×
573
  }
×
574

575
  if (dnsResponse.ids.d_extendedError) {
4,914✔
576
    dnsdist::edns::addExtendedDNSError(dnsResponse.getMutableData(), dnsResponse.getMaximumSize(), dnsResponse.ids.d_extendedError->infoCode, dnsResponse.ids.d_extendedError->extraText);
9✔
577
  }
9✔
578

579
#ifdef HAVE_DNSCRYPT
4,914✔
580
  if (!muted) {
4,914✔
581
    if (!encryptResponse(response, dnsResponse.getMaximumSize(), dnsResponse.overTCP(), dnsResponse.ids.dnsCryptQuery)) {
4,910!
582
      return false;
×
583
    }
×
584
  }
4,910✔
585
#endif /* HAVE_DNSCRYPT */
4,914✔
586

587
  return true;
4,914✔
588
}
4,914✔
589

590
bool processResponse(PacketBuffer& response, DNSResponse& dnsResponse, bool muted)
591
{
5,237✔
592
  pdns::trace::dnsdist::Tracer::Closer closer;
5,237✔
593
  if (auto tracer = dnsResponse.ids.getTracer(); tracer != nullptr && dnsResponse.ids.tracingEnabled) {
5,237✔
594
    closer = tracer->openSpan("processResponse");
8✔
595
  }
8✔
596

597
  const auto& chains = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ruleChains;
5,237✔
598
  const auto& respRuleActions = dnsdist::rules::getResponseRuleChain(chains, dnsdist::rules::ResponseRuleChain::ResponseRules);
5,237✔
599

600
  if (!applyRulesToResponse(respRuleActions, dnsResponse)) {
5,237✔
601
    return false;
7✔
602
  }
7✔
603

604
  if (dnsResponse.isAsynchronous()) {
5,230✔
605
    return true;
458✔
606
  }
458✔
607

608
  return processResponseAfterRules(response, dnsResponse, muted);
4,772✔
609
}
5,230✔
610

611
static size_t getInitialUDPPacketBufferSize(bool expectProxyProtocol)
612
{
808✔
613
  static_assert(dnsdist::configuration::s_udpIncomingBufferSize <= s_initialUDPPacketBufferSize, "The incoming buffer size should not be larger than s_initialUDPPacketBufferSize");
808✔
614

615
  const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
808✔
616
  if (!expectProxyProtocol || runtimeConfig.d_proxyProtocolACL.empty()) {
808✔
617
    return s_initialUDPPacketBufferSize;
803✔
618
  }
803✔
619

620
  return s_initialUDPPacketBufferSize + runtimeConfig.d_proxyProtocolMaximumSize;
5✔
621
}
808✔
622

623
static size_t getMaximumIncomingPacketSize(const ClientState& clientState)
624
{
395✔
625
  if (clientState.dnscryptCtx) {
395✔
626
    return getInitialUDPPacketBufferSize(clientState.d_enableProxyProtocol);
5✔
627
  }
5✔
628

629
  const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
390✔
630
  if (!clientState.d_enableProxyProtocol || runtimeConfig.d_proxyProtocolACL.empty()) {
390✔
631
    return dnsdist::configuration::s_udpIncomingBufferSize;
386✔
632
  }
386✔
633

634
  return dnsdist::configuration::s_udpIncomingBufferSize + runtimeConfig.d_proxyProtocolMaximumSize;
4✔
635
}
390✔
636

637
bool sendUDPResponse(int origFD, const PacketBuffer& response, [[maybe_unused]] const int delayMsec, const ComboAddress& origDest, const ComboAddress& origRemote)
638
{
2,844✔
639
#ifndef DISABLE_DELAY_PIPE
2,844✔
640
  if (delayMsec > 0 && g_delay != nullptr) {
2,844!
641
    DelayedPacket delayed{origFD, response, origRemote, origDest};
4✔
642
    g_delay->submit(delayed, delayMsec);
4✔
643
    return true;
4✔
644
  }
4✔
645
#endif /* DISABLE_DELAY_PIPE */
2,840✔
646
  // NOLINTNEXTLINE(readability-suspicious-call-argument)
647
  sendfromto(origFD, response, origDest, origRemote);
2,840✔
648
  return true;
2,840✔
649
}
2,844✔
650

651
void handleResponseSent(const InternalQueryState& ids, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol outgoingProtocol, bool fromBackend)
652
{
5,389✔
653
  handleResponseSent(ids.qname, ids.qtype, udiff, client, backend, size, cleartextDH, outgoingProtocol, ids.protocol, fromBackend);
5,389✔
654
  ids.sendDelayedProtobufMessages();
5,389✔
655
}
5,389✔
656

657
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)
658
{
5,914✔
659
  if (g_rings.shouldRecordResponses()) {
5,914!
660
    timespec now{};
5,914✔
661
    gettime(&now);
5,914✔
662
    g_rings.insertResponse(now, client, qname, qtype, static_cast<unsigned int>(udiff), size, cleartextDH, backend, outgoingProtocol);
5,914✔
663
  }
5,914✔
664

665
  switch (cleartextDH.rcode) {
5,914✔
666
  case RCode::NXDomain:
36✔
667
    ++dnsdist::metrics::g_stats.frontendNXDomain;
36✔
668
    break;
36✔
669
  case RCode::ServFail:
277✔
670
    if (fromBackend) {
277✔
671
      ++dnsdist::metrics::g_stats.servfailResponses;
204✔
672
    }
204✔
673
    ++dnsdist::metrics::g_stats.frontendServFail;
277✔
674
    break;
277✔
675
  case RCode::NoError:
5,289✔
676
    ++dnsdist::metrics::g_stats.frontendNoError;
5,289✔
677
    break;
5,289✔
678
  }
5,914✔
679

680
  doLatencyStats(incomingProtocol, udiff);
5,914✔
681
}
5,914✔
682

683
static void handleResponseTC4UDPClient(DNSQuestion& dnsQuestion, uint16_t udpPayloadSize, PacketBuffer& response)
684
{
3,163✔
685
  if (udpPayloadSize != 0 && response.size() > udpPayloadSize) {
3,163✔
686
    vinfolog("Got a response of size %d while the initial UDP payload size was %d, truncating", response.size(), udpPayloadSize);
1!
687
    truncateTC(dnsQuestion.getMutableData(), dnsQuestion.getMaximumSize(), dnsQuestion.ids.qname.wirelength(), dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses);
1✔
688
    dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
1✔
689
      header.tc = true;
1✔
690
      return true;
1✔
691
    });
1✔
692
  }
1✔
693
  else if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_truncateTC && dnsQuestion.getHeader()->tc) {
3,162✔
694
    truncateTC(response, dnsQuestion.getMaximumSize(), dnsQuestion.ids.qname.wirelength(), dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses);
3✔
695
  }
3✔
696
}
3,163✔
697

698
static void handleResponseForUDPClient(InternalQueryState& ids, PacketBuffer& response, const std::shared_ptr<DownstreamState>& backend, bool isAsync, bool selfGenerated)
699
{
2,697✔
700
  DNSResponse dnsResponse(ids, response, backend);
2,697✔
701

702
  handleResponseTC4UDPClient(dnsResponse, ids.udpPayloadSize, response);
2,697✔
703

704
  /* when the answer is encrypted in place, we need to get a copy
705
     of the original header before encryption to fill the ring buffer */
706
  dnsheader cleartextDH{};
2,697✔
707
  memcpy(&cleartextDH, dnsResponse.getHeader().get(), sizeof(cleartextDH));
2,697✔
708

709
  if (!isAsync) {
2,697✔
710
    if (!processResponse(response, dnsResponse, ids.cs != nullptr && ids.cs->muted)) {
2,667!
711
      return;
3✔
712
    }
3✔
713

714
    if (dnsResponse.isAsynchronous()) {
2,664✔
715
      return;
328✔
716
    }
328✔
717
  }
2,664✔
718

719
  ++dnsdist::metrics::g_stats.responses;
2,366✔
720
  if (ids.cs != nullptr) {
2,366✔
721
    ++ids.cs->responses;
2,362✔
722
  }
2,362✔
723

724
  bool muted = true;
2,366✔
725
  if (ids.cs != nullptr && !ids.cs->muted && !ids.isXSK()) {
2,366!
726
    sendUDPResponse(ids.cs->udpFD, response, dnsResponse.ids.delayMsec, ids.hopLocal, ids.hopRemote);
2,362✔
727
    muted = false;
2,362✔
728
  }
2,362✔
729

730
  if (!selfGenerated) {
2,366✔
731
    double udiff = ids.queryRealTime.udiff();
2,356✔
732
    if (!muted) {
2,356!
733
      vinfolog("Got answer from %s, relayed to %s (UDP), took %f us", backend->d_config.remote.toStringWithPort(), ids.origRemote.toStringWithPort(), udiff);
2,356✔
734
    }
2,356✔
735
    else {
×
736
      if (!ids.isXSK()) {
×
737
        vinfolog("Got answer from %s, NOT relayed to %s (UDP) since that frontend is muted, took %f us", backend->d_config.remote.toStringWithPort(), ids.origRemote.toStringWithPort(), udiff);
×
738
      }
×
739
      else {
×
740
        vinfolog("Got answer from %s, relayed to %s (UDP via XSK), took %f us", backend->d_config.remote.toStringWithPort(), ids.origRemote.toStringWithPort(), udiff);
×
741
      }
×
742
    }
×
743

744
    handleResponseSent(ids, udiff, dnsResponse.ids.origRemote, backend->d_config.remote, response.size(), cleartextDH, backend->getProtocol(), true);
2,356✔
745
  }
2,356✔
746
  else {
10✔
747
    handleResponseSent(ids, 0., dnsResponse.ids.origRemote, ComboAddress(), response.size(), cleartextDH, dnsdist::Protocol::DoUDP, false);
10✔
748
  }
10✔
749
}
2,366✔
750

751
bool processResponderPacket(std::shared_ptr<DownstreamState>& dss, PacketBuffer& response, InternalQueryState&& ids)
752
{
2,652✔
753

754
  const dnsheader_aligned dnsHeader(response.data());
2,652✔
755
  auto queryId = dnsHeader->id;
2,652✔
756

757
  if (!responseContentMatches(response, ids.qname, ids.qtype, ids.qclass, dss, dnsdist::configuration::getCurrentRuntimeConfiguration().d_allowEmptyResponse)) {
2,652✔
758
    dss->restoreState(queryId, std::move(ids));
3✔
759
    return false;
3✔
760
  }
3✔
761

762
  auto dohUnit = std::move(ids.du);
2,649✔
763
  dnsdist::PacketMangling::editDNSHeaderFromPacket(response, [&ids](dnsheader& header) {
2,649✔
764
    header.id = ids.origID;
2,649✔
765
    return true;
2,649✔
766
  });
2,649✔
767
  ++dss->responses;
2,649✔
768

769
  double udiff = ids.queryRealTime.udiff();
2,649✔
770
  // do that _before_ the processing, otherwise it's not fair to the backend
771
  dss->latencyUsec = (127.0 * dss->latencyUsec / 128.0) + udiff / 128.0;
2,649✔
772
  dss->reportResponse(dnsHeader->rcode);
2,649✔
773

774
  /* don't call processResponse for DOH */
775
  if (dohUnit) {
2,649✔
776
#ifdef HAVE_DNS_OVER_HTTPS
122✔
777
    // DoH query, we cannot touch dohUnit after that
778
    DOHUnitInterface::handleUDPResponse(std::move(dohUnit), std::move(response), std::move(ids), dss);
122✔
779
#endif
122✔
780
    return false;
122✔
781
  }
122✔
782

783
  handleResponseForUDPClient(ids, response, dss, false, false);
2,527✔
784
  return true;
2,527✔
785
}
2,649✔
786

787
// listens on a dedicated socket, lobs answers from downstream servers to original requestors
788
void responderThread(std::shared_ptr<DownstreamState> dss)
789
{
408✔
790
  try {
408✔
791
    setThreadName("dnsdist/respond");
408✔
792
    const size_t initialBufferSize = getInitialUDPPacketBufferSize(false);
408✔
793
    /* allocate one more byte so we can detect truncation */
794
    PacketBuffer response(initialBufferSize + 1);
408✔
795
    uint16_t queryId = 0;
408✔
796
    std::vector<int> sockets;
408✔
797
    sockets.reserve(dss->sockets.size());
408✔
798

799
    for (;;) {
3,470✔
800
      try {
3,470✔
801
        if (dss->isStopped()) {
3,470✔
802
          break;
407✔
803
        }
407✔
804

805
        if (!dss->connected) {
3,063!
806
          /* the sockets are not connected yet, likely because we detected a problem,
807
             tried to reconnect and it failed. We will try to reconnect after the next
808
             successful health-check (unless reconnectOnUp is false), or when trying
809
             to send in the UDP listener thread, but until then we simply need to wait. */
810
          dss->waitUntilConnected();
×
811
          continue;
×
812
        }
×
813

814
        dss->pickSocketsReadyForReceiving(sockets);
3,063✔
815

816
        /* check a second time here because we might have waited quite a bit
817
           since the first check */
818
        if (dss->isStopped()) {
3,063!
819
          break;
×
820
        }
×
821

822
        for (const auto& sockDesc : sockets) {
3,063✔
823
          /* allocate one more byte so we can detect truncation */
824
          // NOLINTNEXTLINE(bugprone-use-after-move): resizing a vector has no preconditions so it is valid to do so after moving it
825
          response.resize(initialBufferSize + 1);
3,063✔
826
          ssize_t got = recv(sockDesc, response.data(), response.size(), 0);
3,063✔
827

828
          if (got == 0 && dss->isStopped()) {
3,063✔
829
            break;
407✔
830
          }
407✔
831

832
          if (got < 0 || static_cast<size_t>(got) < sizeof(dnsheader) || static_cast<size_t>(got) == (initialBufferSize + 1)) {
2,656!
833
            continue;
3✔
834
          }
3✔
835

836
          response.resize(static_cast<size_t>(got));
2,653✔
837
          const dnsheader_aligned dnsHeader(response.data());
2,653✔
838
          queryId = dnsHeader->id;
2,653✔
839

840
          auto ids = dss->getState(queryId);
2,653✔
841
          if (!ids) {
2,653!
842
            continue;
×
843
          }
×
844

845
          if (!ids->isXSK() && sockDesc != ids->backendFD) {
2,653!
846
            dss->restoreState(queryId, std::move(*ids));
×
847
            continue;
×
848
          }
×
849

850
          dnsdist::configuration::refreshLocalRuntimeConfiguration();
2,653✔
851
          if (processResponderPacket(dss, response, std::move(*ids)) && ids->isXSK() && ids->cs->xskInfoResponder) {
2,653!
852
#ifdef HAVE_XSK
×
853
            auto& xskInfo = ids->cs->xskInfoResponder;
×
854
            auto xskPacket = xskInfo->getEmptyFrame();
×
855
            if (!xskPacket) {
×
856
              continue;
×
857
            }
×
858
            xskPacket->setHeader(ids->xskPacketHeader);
×
859
            if (!xskPacket->setPayload(response)) {
×
860
            }
×
861
            if (ids->delayMsec > 0) {
×
862
              xskPacket->addDelay(ids->delayMsec);
×
863
            }
×
864
            xskPacket->updatePacket();
×
865
            xskInfo->pushToSendQueue(*xskPacket);
×
866
            xskInfo->notifyXskSocket();
×
867
#endif /* HAVE_XSK */
×
868
          }
×
869
        }
2,653✔
870
      }
3,063✔
871
      catch (const std::exception& e) {
3,470✔
872
        vinfolog("Got an error in UDP responder thread while parsing a response from %s, id %d: %s", dss->d_config.remote.toStringWithPort(), queryId, e.what());
2!
873
      }
2✔
874
    }
3,470✔
875
  }
408✔
876
  catch (const std::exception& e) {
408✔
877
    errlog("UDP responder thread died because of exception: %s", e.what());
×
878
  }
×
879
  catch (const PDNSException& e) {
408✔
880
    errlog("UDP responder thread died because of PowerDNS exception: %s", e.reason);
×
881
  }
×
882
  catch (...) {
408✔
883
    errlog("UDP responder thread died because of an exception: %s", "unknown");
×
884
  }
×
885
}
408✔
886

887
RecursiveLockGuarded<LuaContext> g_lua{LuaContext()};
888

889
static void spoofResponseFromString(DNSQuestion& dnsQuestion, const string& spoofContent, bool raw)
890
{
58✔
891
  string result;
58✔
892

893
  if (raw) {
58✔
894
    dnsdist::ResponseConfig config;
6✔
895
    std::vector<std::string> raws;
6✔
896
    stringtok(raws, spoofContent, ",");
6✔
897
    dnsdist::self_answers::generateAnswerFromRDataEntries(dnsQuestion, raws, std::nullopt, config);
6✔
898
  }
6✔
899
  else {
52✔
900
    std::vector<std::string> addrs;
52✔
901
    stringtok(addrs, spoofContent, " ,");
52✔
902

903
    if (addrs.size() == 1) {
52✔
904
      dnsdist::ResponseConfig config;
50✔
905
      try {
50✔
906
        ComboAddress spoofAddr(spoofContent);
50✔
907
        dnsdist::self_answers::generateAnswerFromIPAddresses(dnsQuestion, {spoofAddr}, config);
50✔
908
      }
50✔
909
      catch (const PDNSException& e) {
50✔
910
        DNSName cname(spoofContent);
29✔
911
        dnsdist::self_answers::generateAnswerFromCNAME(dnsQuestion, cname, config);
29✔
912
      }
29✔
913
    }
50✔
914
    else {
2✔
915
      dnsdist::ResponseConfig config;
2✔
916
      std::vector<ComboAddress> cas;
2✔
917
      for (const auto& addr : addrs) {
4✔
918
        try {
4✔
919
          cas.emplace_back(addr);
4✔
920
        }
4✔
921
        catch (...) {
4✔
922
        }
×
923
      }
4✔
924
      dnsdist::self_answers::generateAnswerFromIPAddresses(dnsQuestion, cas, config);
2✔
925
    }
2✔
926
  }
52✔
927
}
58✔
928

929
static void spoofPacketFromString(DNSQuestion& dnsQuestion, const string& spoofContent)
930
{
2✔
931
  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
932
  dnsdist::self_answers::generateAnswerFromRawPacket(dnsQuestion, PacketBuffer(spoofContent.data(), spoofContent.data() + spoofContent.size()));
2✔
933
}
2✔
934

935
bool processRulesResult(const DNSAction::Action& action, DNSQuestion& dnsQuestion, std::string& ruleresult, bool& drop)
936
{
2,643✔
937
  if (dnsQuestion.isAsynchronous()) {
2,643✔
938
    return false;
202✔
939
  }
202✔
940

941
  auto setRCode = [&dnsQuestion](uint8_t rcode) {
2,441✔
942
    dnsdist::self_answers::removeRecordsAndSetRCode(dnsQuestion, rcode);
22✔
943
  };
22✔
944

945
  switch (action) {
2,441!
946
  case DNSAction::Action::Allow:
33✔
947
    return true;
33✔
948
    break;
×
949
  case DNSAction::Action::Drop:
25✔
950
    ++dnsdist::metrics::g_stats.ruleDrop;
25✔
951
    drop = true;
25✔
952
    return true;
25✔
953
    break;
×
954
  case DNSAction::Action::Nxdomain:
12✔
955
    setRCode(RCode::NXDomain);
12✔
956
    return true;
12✔
957
    break;
×
958
  case DNSAction::Action::Refused:
8✔
959
    setRCode(RCode::Refused);
8✔
960
    return true;
8✔
961
    break;
×
962
  case DNSAction::Action::ServFail:
2✔
963
    setRCode(RCode::ServFail);
2✔
964
    return true;
2✔
965
    break;
×
966
  case DNSAction::Action::Spoof:
52✔
967
    spoofResponseFromString(dnsQuestion, ruleresult, false);
52✔
968
    return true;
52✔
969
    break;
×
970
  case DNSAction::Action::SpoofPacket:
2✔
971
    spoofPacketFromString(dnsQuestion, ruleresult);
2✔
972
    return true;
2✔
973
    break;
×
974
  case DNSAction::Action::SpoofRaw:
6✔
975
    spoofResponseFromString(dnsQuestion, ruleresult, true);
6✔
976
    return true;
6✔
977
    break;
×
978
  case DNSAction::Action::Truncate:
8✔
979
    if (!dnsQuestion.overTCP()) {
8✔
980
      dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
7✔
981
        header.tc = true;
7✔
982
        header.qr = true;
7✔
983
        header.ra = header.rd;
7✔
984
        header.aa = false;
7✔
985
        header.ad = false;
7✔
986
        return true;
7✔
987
      });
7✔
988
      ++dnsdist::metrics::g_stats.ruleTruncated;
7✔
989
      return true;
7✔
990
    }
7✔
991
    break;
1✔
992
  case DNSAction::Action::HeaderModify:
325✔
993
    return true;
325✔
994
    break;
×
995
  case DNSAction::Action::Pool:
521✔
996
    /* we need to keep this because a custom Lua action can return
997
       DNSAction.Spoof, 'poolname' */
998
    dnsQuestion.ids.poolName = ruleresult;
521✔
999
    return true;
521✔
1000
    break;
×
1001
  case DNSAction::Action::NoRecurse:
×
1002
    dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
×
1003
      header.rd = false;
×
1004
      return true;
×
1005
    });
×
1006
    return true;
×
1007
    break;
×
1008
    /* non-terminal actions follow */
1009
  case DNSAction::Action::Delay:
2✔
1010
    pdns::checked_stoi_into(dnsQuestion.ids.delayMsec, ruleresult); // sorry
2✔
1011
    break;
2✔
1012
  case DNSAction::Action::SetTag:
×
1013
    /* unsupported for non-dynamic block */
1014
  case DNSAction::Action::None:
1,445✔
1015
    /* fall-through */
1016
  case DNSAction::Action::NoOp:
1,445!
1017
    break;
1,445✔
1018
  }
2,441✔
1019

1020
  /* false means that we don't stop the processing */
1021
  return false;
1,448✔
1022
}
2,441✔
1023

1024
static bool applyRulesChainToQuery(const std::vector<dnsdist::rules::RuleAction>& rules, DNSQuestion& dnsQuestion)
1025
{
6,313✔
1026
  if (rules.empty()) {
6,313✔
1027
    return true;
3,208✔
1028
  }
3,208✔
1029

1030
  DNSAction::Action action = DNSAction::Action::None;
3,105✔
1031
  string ruleresult;
3,105✔
1032
  bool drop = false;
3,105✔
1033

1034
  for (const auto& rule : rules) {
6,696✔
1035
    if (!rule.d_rule->matches(&dnsQuestion)) {
6,696✔
1036
      continue;
4,055✔
1037
    }
4,055✔
1038

1039
    rule.d_rule->d_matches++;
2,641✔
1040
    action = (*rule.d_action)(&dnsQuestion, &ruleresult);
2,641✔
1041
    if (processRulesResult(action, dnsQuestion, ruleresult, drop)) {
2,641✔
1042
      break;
991✔
1043
    }
991✔
1044
  }
2,641✔
1045

1046
  return !drop;
3,105✔
1047
}
6,313✔
1048

1049
static bool applyRulesToQuery(DNSQuestion& dnsQuestion, const timespec& now)
1050
{
6,092✔
1051
  pdns::trace::dnsdist::Tracer::Closer closer;
6,092✔
1052
  if (auto tracer = dnsQuestion.ids.getTracer(); tracer != nullptr) {
6,092✔
1053
    closer = tracer->openSpan("applyRulesToQuery", tracer->getLastSpanID());
12✔
1054
  }
12✔
1055
  if (g_rings.shouldRecordQueries()) {
6,092!
1056
    g_rings.insertQuery(now, dnsQuestion.ids.origRemote, dnsQuestion.ids.qname, dnsQuestion.ids.qtype, dnsQuestion.getData().size(), *dnsQuestion.getHeader(), dnsQuestion.getProtocol());
6,092✔
1057
  }
6,092✔
1058

1059
  {
6,092✔
1060
    const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
6,092✔
1061
    if (runtimeConfig.d_queryCountConfig.d_enabled) {
6,092!
1062
      string qname = dnsQuestion.ids.qname.toLogString();
×
1063
      bool countQuery{true};
×
1064
      if (runtimeConfig.d_queryCountConfig.d_filter) {
×
1065
        auto lock = g_lua.lock();
×
1066
        std::tie(countQuery, qname) = runtimeConfig.d_queryCountConfig.d_filter(&dnsQuestion);
×
1067
      }
×
1068

1069
      if (countQuery) {
×
1070
        auto records = dnsdist::QueryCount::g_queryCountRecords.write_lock();
×
1071
        if (records->count(qname) == 0) {
×
1072
          (*records)[qname] = 0;
×
1073
        }
×
1074
        (*records)[qname]++;
×
1075
      }
×
1076
    }
×
1077
  }
6,092✔
1078

1079
#ifndef DISABLE_DYNBLOCKS
6,092✔
1080
  const auto defaultDynBlockAction = dnsdist::configuration::getCurrentRuntimeConfiguration().d_dynBlockAction;
6,092✔
1081
  auto setRCode = [&dnsQuestion](uint8_t rcode) {
6,092✔
1082
    dnsdist::self_answers::removeRecordsAndSetRCode(dnsQuestion, rcode);
12✔
1083
  };
12✔
1084

1085
  /* the Dynamic Block mechanism supports address and port ranges, so we need to pass the full address and port */
1086
  if (auto* got = dnsdist::DynamicBlocks::getClientAddressDynamicRules().lookup(AddressAndPortRange(dnsQuestion.ids.origRemote, dnsQuestion.ids.origRemote.isIPv4() ? 32 : 128, 16))) {
6,092✔
1087
    auto updateBlockStats = [&got]() {
510✔
1088
      ++dnsdist::metrics::g_stats.dynBlocked;
38✔
1089
      got->second.blocks++;
38✔
1090
    };
38✔
1091

1092
    if (now < got->second.until) {
510✔
1093
      DNSAction::Action action = got->second.action;
82✔
1094
      if (action == DNSAction::Action::None) {
82✔
1095
        action = defaultDynBlockAction;
22✔
1096
      }
22✔
1097

1098
      switch (action) {
82✔
1099
      case DNSAction::Action::NoOp:
43✔
1100
        /* do nothing */
1101
        break;
43✔
1102

1103
      case DNSAction::Action::Nxdomain:
2✔
1104
        vinfolog("Query from %s turned into NXDomain because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort());
2!
1105
        updateBlockStats();
2✔
1106

1107
        setRCode(RCode::NXDomain);
2✔
1108
        return true;
2✔
1109

1110
      case DNSAction::Action::Refused:
10✔
1111
        vinfolog("Query from %s refused because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort());
10!
1112
        updateBlockStats();
10✔
1113

1114
        setRCode(RCode::Refused);
10✔
1115
        return true;
10✔
1116

1117
      case DNSAction::Action::Truncate:
2✔
1118
        if (!dnsQuestion.overTCP()) {
2✔
1119
          updateBlockStats();
1✔
1120
          vinfolog("Query from %s truncated because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort());
1!
1121
          dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
1✔
1122
            header.tc = true;
1✔
1123
            header.qr = true;
1✔
1124
            header.ra = header.rd;
1✔
1125
            header.aa = false;
1✔
1126
            header.ad = false;
1✔
1127
            return true;
1✔
1128
          });
1✔
1129
          return true;
1✔
1130
        }
1✔
1131
        else {
1✔
1132
          vinfolog("Query from %s for %s over TCP *not* truncated because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), dnsQuestion.ids.qname.toLogString());
1!
1133
        }
1✔
1134
        break;
1✔
1135
      case DNSAction::Action::NoRecurse:
1!
1136
        updateBlockStats();
×
1137
        vinfolog("Query from %s setting rd=0 because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort());
×
1138
        dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
×
1139
          header.rd = false;
×
1140
          return true;
×
1141
        });
×
1142
        return true;
×
1143
      case DNSAction::Action::SetTag: {
3✔
1144
        if (!got->second.tagSettings) {
3!
1145
          vinfolog("Skipping set tag dynamic block for query from %s because of missing options", dnsQuestion.ids.origRemote.toStringWithPort());
×
1146
          break;
×
1147
        }
×
1148
        updateBlockStats();
3✔
1149
        const auto& tagName = got->second.tagSettings->d_name;
3✔
1150
        const auto& tagValue = got->second.tagSettings->d_value;
3✔
1151
        dnsQuestion.setTag(tagName, tagValue);
3✔
1152
        vinfolog("Query from %s setting tag %s to %s because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), tagName, tagValue);
3!
1153
        // do not return, the whole point it to set a Tag to be able to do further processing in rules
1154
        break;
3✔
1155
      }
3✔
1156
      default:
22✔
1157
        updateBlockStats();
22✔
1158
        vinfolog("Query from %s dropped because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort());
22!
1159
        return false;
22✔
1160
      }
82✔
1161
    }
82✔
1162
  }
510✔
1163

1164
  if (auto* got = dnsdist::DynamicBlocks::getSuffixDynamicRules().lookup(dnsQuestion.ids.qname)) {
6,057!
1165
    auto updateBlockStats = [&got]() {
×
1166
      ++dnsdist::metrics::g_stats.dynBlocked;
×
1167
      got->blocks++;
×
1168
    };
×
1169

1170
    if (now < got->until) {
×
1171
      DNSAction::Action action = got->action;
×
1172
      if (action == DNSAction::Action::None) {
×
1173
        action = defaultDynBlockAction;
×
1174
      }
×
1175
      switch (action) {
×
1176
      case DNSAction::Action::NoOp:
×
1177
        /* do nothing */
1178
        break;
×
1179
      case DNSAction::Action::Nxdomain:
×
1180
        vinfolog("Query from %s for %s turned into NXDomain because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), dnsQuestion.ids.qname.toLogString());
×
1181
        updateBlockStats();
×
1182

1183
        setRCode(RCode::NXDomain);
×
1184
        return true;
×
1185
      case DNSAction::Action::Refused:
×
1186
        vinfolog("Query from %s for %s refused because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), dnsQuestion.ids.qname.toLogString());
×
1187
        updateBlockStats();
×
1188

1189
        setRCode(RCode::Refused);
×
1190
        return true;
×
1191
      case DNSAction::Action::Truncate:
×
1192
        if (!dnsQuestion.overTCP()) {
×
1193
          updateBlockStats();
×
1194

1195
          vinfolog("Query from %s for %s truncated because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), dnsQuestion.ids.qname.toLogString());
×
1196
          dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
×
1197
            header.tc = true;
×
1198
            header.qr = true;
×
1199
            header.ra = header.rd;
×
1200
            header.aa = false;
×
1201
            header.ad = false;
×
1202
            return true;
×
1203
          });
×
1204
          return true;
×
1205
        }
×
1206
        else {
×
1207
          vinfolog("Query from %s for %s over TCP *not* truncated because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), dnsQuestion.ids.qname.toLogString());
×
1208
        }
×
1209
        break;
×
1210
      case DNSAction::Action::NoRecurse:
×
1211
        updateBlockStats();
×
1212
        vinfolog("Query from %s setting rd=0 because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort());
×
1213
        dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
×
1214
          header.rd = false;
×
1215
          return true;
×
1216
        });
×
1217
        return true;
×
1218
      case DNSAction::Action::SetTag: {
×
1219
        if (!got->tagSettings) {
×
1220
          vinfolog("Skipping set tag dynamic block for query from %s because of missing options", dnsQuestion.ids.origRemote.toStringWithPort());
×
1221
          break;
×
1222
        }
×
1223
        updateBlockStats();
×
1224
        const auto& tagName = got->tagSettings->d_name;
×
1225
        const auto& tagValue = got->tagSettings->d_value;
×
1226
        dnsQuestion.setTag(tagName, tagValue);
×
1227
        vinfolog("Query from %s setting tag %s to %s because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), tagName, tagValue);
×
1228
        // do not return, the whole point it to set a Tag to be able to do further processing in rules
1229
        break;
×
1230
      }
×
1231
      default:
×
1232
        updateBlockStats();
×
1233
        vinfolog("Query from %s for %s dropped because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), dnsQuestion.ids.qname.toLogString());
×
1234
        return false;
×
1235
      }
×
1236
    }
×
1237
  }
×
1238
#endif /* DISABLE_DYNBLOCKS */
6,057✔
1239

1240
  const auto& chains = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ruleChains;
6,057✔
1241
  const auto& queryRules = dnsdist::rules::getRuleChain(chains, dnsdist::rules::RuleChain::Rules);
6,057✔
1242
  return applyRulesChainToQuery(queryRules, dnsQuestion);
6,057✔
1243
}
6,057✔
1244

1245
ssize_t udpClientSendRequestToBackend(const std::shared_ptr<DownstreamState>& backend, const int socketDesc, const PacketBuffer& request, bool healthCheck)
1246
{
4,354✔
1247
  ssize_t result = 0;
4,354✔
1248

1249
  if (backend->d_config.sourceItf == 0) {
4,354!
1250
    result = send(socketDesc, request.data(), request.size(), 0);
4,354✔
1251
  }
4,354✔
1252
  else {
×
1253
    msghdr msgh{};
×
1254
    iovec iov{};
×
1255
    cmsgbuf_aligned cbuf;
×
1256
    ComboAddress remote(backend->d_config.remote);
×
1257
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-type-const-cast)
1258
    fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), const_cast<char*>(reinterpret_cast<const char*>(request.data())), request.size(), &remote);
×
1259
    addCMsgSrcAddr(&msgh, &cbuf, &backend->d_config.sourceAddr, static_cast<int>(backend->d_config.sourceItf));
×
1260
    result = sendmsg(socketDesc, &msgh, 0);
×
1261
  }
×
1262

1263
  if (result == -1) {
4,354!
1264
    int savederrno = errno;
×
1265
    vinfolog("Error sending request to backend %s: %s", backend->d_config.remote.toStringWithPort(), stringerror(savederrno));
×
1266

1267
    /* This might sound silly, but on Linux send() might fail with EINVAL
1268
       if the interface the socket was bound to doesn't exist anymore.
1269
       We don't want to reconnect the real socket if the healthcheck failed,
1270
       because it's not using the same socket.
1271
    */
1272
    if (!healthCheck) {
×
1273
      if (savederrno == EINVAL || savederrno == ENODEV || savederrno == ENETUNREACH || savederrno == EHOSTUNREACH || savederrno == EBADF) {
×
1274
        backend->reconnect();
×
1275
      }
×
1276
      backend->reportTimeoutOrError();
×
1277
    }
×
1278
  }
×
1279

1280
  return result;
4,354✔
1281
}
4,354✔
1282

1283
static bool isUDPQueryAcceptable(ClientState& clientState, const struct msghdr* msgh, const ComboAddress& remote, ComboAddress& dest, bool& expectProxyProtocol)
1284
{
2,917✔
1285
  if ((msgh->msg_flags & MSG_TRUNC) != 0) {
2,917!
1286
    /* message was too large for our buffer */
1287
    vinfolog("Dropping message too large for our buffer");
×
1288
    ++clientState.nonCompliantQueries;
×
1289
    ++dnsdist::metrics::g_stats.nonCompliantQueries;
×
1290
    return false;
×
1291
  }
×
1292

1293
  expectProxyProtocol = clientState.d_enableProxyProtocol && expectProxyProtocolFrom(remote);
2,917✔
1294
  if (!dnsdist::configuration::getCurrentRuntimeConfiguration().d_ACL.match(remote) && !expectProxyProtocol) {
2,917!
1295
    vinfolog("Query from %s dropped because of ACL", remote.toStringWithPort());
1!
1296
    ++dnsdist::metrics::g_stats.aclDrops;
1✔
1297
    return false;
1✔
1298
  }
1✔
1299

1300
  if (HarvestDestinationAddress(msgh, &dest)) {
2,916✔
1301
    /* so it turns out that sometimes the kernel lies to us:
1302
       the address is set to 0.0.0.0:0 which makes our sendfromto() use
1303
       the wrong address. In that case it's better to let the kernel
1304
       do the work by itself and use sendto() instead.
1305
       This is indicated by setting the family to 0 which is acted upon
1306
       in sendUDPResponse() and DelayedPacket::().
1307
    */
1308
    const ComboAddress bogusV4("0.0.0.0:0");
5✔
1309
    const ComboAddress bogusV6("[::]:0");
5✔
1310
    if ((dest.sin4.sin_family == AF_INET && dest == bogusV4) || (dest.sin4.sin_family == AF_INET6 && dest == bogusV6)) {
5!
1311
      dest.sin4.sin_family = 0;
×
1312
    }
×
1313
    else {
5✔
1314
      /* we don't get the port, only the address */
1315
      dest.sin4.sin_port = clientState.local.sin4.sin_port;
5✔
1316
    }
5✔
1317
  }
5✔
1318
  else {
2,911✔
1319
    dest.sin4.sin_family = 0;
2,911✔
1320
  }
2,911✔
1321

1322
  ++clientState.queries;
2,916✔
1323
  ++dnsdist::metrics::g_stats.queries;
2,916✔
1324

1325
  return true;
2,916✔
1326
}
2,917✔
1327

1328
bool checkDNSCryptQuery(const ClientState& clientState, [[maybe_unused]] PacketBuffer& query, [[maybe_unused]] std::unique_ptr<DNSCryptQuery>& dnsCryptQuery, [[maybe_unused]] time_t now, [[maybe_unused]] bool tcp)
1329
{
5,930✔
1330
  if (clientState.dnscryptCtx) {
5,930✔
1331
#ifdef HAVE_DNSCRYPT
59✔
1332
    PacketBuffer response;
59✔
1333
    dnsCryptQuery = std::make_unique<DNSCryptQuery>(clientState.dnscryptCtx);
59✔
1334

1335
    bool decrypted = handleDNSCryptQuery(query, *dnsCryptQuery, tcp, now, response);
59✔
1336

1337
    if (!decrypted) {
59✔
1338
      if (!response.empty()) {
23✔
1339
        query = std::move(response);
21✔
1340
        return true;
21✔
1341
      }
21✔
1342
      throw std::runtime_error("Unable to decrypt DNSCrypt query, dropping.");
2✔
1343
    }
23✔
1344
#endif /* HAVE_DNSCRYPT */
59✔
1345
  }
59✔
1346
  return false;
5,907✔
1347
}
5,930✔
1348

1349
bool checkQueryHeaders(const struct dnsheader& dnsHeader, ClientState& clientState)
1350
{
6,115✔
1351
  if (dnsHeader.qr) { // don't respond to responses
6,115✔
1352
    ++dnsdist::metrics::g_stats.nonCompliantQueries;
5✔
1353
    ++clientState.nonCompliantQueries;
5✔
1354
    return false;
5✔
1355
  }
5✔
1356

1357
  if (dnsHeader.qdcount == 0) {
6,110✔
1358
    ++dnsdist::metrics::g_stats.emptyQueries;
6✔
1359
    if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_dropEmptyQueries) {
6✔
1360
      return false;
2✔
1361
    }
2✔
1362
  }
6✔
1363

1364
  if (dnsHeader.rd) {
6,108✔
1365
    ++dnsdist::metrics::g_stats.rdQueries;
5,561✔
1366
  }
5,561✔
1367

1368
  return true;
6,108✔
1369
}
6,110✔
1370

1371
#if !defined(DISABLE_RECVMMSG) && defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
1372
static void queueResponse(const PacketBuffer& response, const ComboAddress& dest, const ComboAddress& remote, struct mmsghdr& outMsg, struct iovec* iov, cmsgbuf_aligned* cbuf)
1373
{
6✔
1374
  outMsg.msg_len = 0;
6✔
1375
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast,cppcoreguidelines-pro-type-reinterpret-cast): API
1376
  fillMSGHdr(&outMsg.msg_hdr, iov, nullptr, 0, const_cast<char*>(reinterpret_cast<const char*>(&response.at(0))), response.size(), const_cast<ComboAddress*>(&remote));
6✔
1377

1378
  if (dest.sin4.sin_family == 0) {
6!
1379
    outMsg.msg_hdr.msg_control = nullptr;
6✔
1380
  }
6✔
1381
  else {
×
1382
    addCMsgSrcAddr(&outMsg.msg_hdr, cbuf, &dest, 0);
×
1383
  }
×
1384
}
6✔
1385
#elif !defined(HAVE_RECVMMSG)
1386
struct mmsghdr
1387
{
1388
  msghdr msg_hdr;
1389
  unsigned int msg_len{0};
1390
};
1391
#endif
1392

1393
/* self-generated responses or cache hits */
1394
static bool prepareOutgoingResponse([[maybe_unused]] const ClientState& clientState, DNSQuestion& dnsQuestion, bool cacheHit)
1395
{
1,036✔
1396
  std::shared_ptr<DownstreamState> backend{nullptr};
1,036✔
1397
  DNSResponse dnsResponse(dnsQuestion.ids, dnsQuestion.getMutableData(), backend);
1,036✔
1398
  dnsResponse.d_incomingTCPState = dnsQuestion.d_incomingTCPState;
1,036✔
1399
  dnsResponse.ids.selfGenerated = true;
1,036✔
1400
  dnsResponse.ids.cacheHit = cacheHit;
1,036✔
1401

1402
  const auto& chains = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ruleChains;
1,036✔
1403
  const auto& cacheHitRespRules = dnsdist::rules::getResponseRuleChain(chains, dnsdist::rules::ResponseRuleChain::CacheHitResponseRules);
1,036✔
1404
  const auto& selfAnsweredRespRules = dnsdist::rules::getResponseRuleChain(chains, dnsdist::rules::ResponseRuleChain::SelfAnsweredResponseRules);
1,036✔
1405
  if (!applyRulesToResponse(cacheHit ? cacheHitRespRules : selfAnsweredRespRules, dnsResponse)) {
1,036✔
1406
    return false;
5✔
1407
  }
5✔
1408

1409
  if (dnsResponse.ids.ttlCap > 0) {
1,031!
1410
    dnsdist::PacketMangling::restrictDNSPacketTTLs(dnsResponse.getMutableData(), 0, dnsResponse.ids.ttlCap);
×
1411
  }
×
1412

1413
  if (dnsResponse.ids.d_extendedError) {
1,031✔
1414
    dnsdist::edns::addExtendedDNSError(dnsResponse.getMutableData(), dnsResponse.getMaximumSize(), dnsResponse.ids.d_extendedError->infoCode, dnsResponse.ids.d_extendedError->extraText);
10✔
1415
  }
10✔
1416

1417
  if (cacheHit) {
1,031✔
1418
    ++dnsdist::metrics::g_stats.cacheHits;
547✔
1419
  }
547✔
1420

1421
  if (dnsResponse.isAsynchronous()) {
1,031✔
1422
    return false;
12✔
1423
  }
12✔
1424

1425
#ifdef HAVE_DNSCRYPT
1,019✔
1426
  if (!clientState.muted) {
1,019!
1427
    if (!encryptResponse(dnsQuestion.getMutableData(), dnsQuestion.getMaximumSize(), dnsQuestion.overTCP(), dnsQuestion.ids.dnsCryptQuery)) {
1,019!
1428
      return false;
×
1429
    }
×
1430
  }
1,019✔
1431
#endif /* HAVE_DNSCRYPT */
1,019✔
1432

1433
  return true;
1,019✔
1434
}
1,019✔
1435

1436
static ProcessQueryResult handleQueryTurnedIntoSelfAnsweredResponse(DNSQuestion& dnsQuestion)
1437
{
475✔
1438
  fixUpQueryTurnedResponse(dnsQuestion, dnsQuestion.ids.origFlags);
475✔
1439

1440
  if (!prepareOutgoingResponse(*dnsQuestion.ids.cs, dnsQuestion, false)) {
475✔
1441
    return ProcessQueryResult::Drop;
2✔
1442
  }
2✔
1443

1444
  const auto rcode = dnsQuestion.getHeader()->rcode;
473✔
1445
  if (rcode == RCode::NXDomain) {
473✔
1446
    ++dnsdist::metrics::g_stats.ruleNXDomain;
26✔
1447
  }
26✔
1448
  else if (rcode == RCode::Refused) {
447✔
1449
    ++dnsdist::metrics::g_stats.ruleRefused;
125✔
1450
  }
125✔
1451
  else if (rcode == RCode::ServFail) {
322✔
1452
    ++dnsdist::metrics::g_stats.ruleServFail;
6✔
1453
  }
6✔
1454

1455
  ++dnsdist::metrics::g_stats.selfAnswered;
473✔
1456
  ++dnsQuestion.ids.cs->responses;
473✔
1457
  return ProcessQueryResult::SendAnswer;
473✔
1458
}
475✔
1459

1460
static ServerPolicy::SelectedBackend selectBackendForOutgoingQuery(DNSQuestion& dnsQuestion, const ServerPool& serverPool)
1461
{
5,334✔
1462
  pdns::trace::dnsdist::Tracer::Closer closer;
5,334✔
1463
  if (auto tracer = dnsQuestion.ids.getTracer(); tracer != nullptr && dnsQuestion.ids.tracingEnabled) {
5,334✔
1464
    closer = tracer->openSpan("selectBackendForOutgoingQuery", tracer->getLastSpanID());
8✔
1465
  }
8✔
1466

1467
  const auto& policy = serverPool.policy != nullptr ? *serverPool.policy : *dnsdist::configuration::getCurrentRuntimeConfiguration().d_lbPolicy;
5,334✔
1468
  const auto& servers = serverPool.getServers();
5,334✔
1469
  auto selectedBackend = policy.getSelectedBackend(servers, dnsQuestion);
5,334✔
1470

1471
  if (auto tracer = dnsQuestion.ids.getTracer(); tracer != nullptr && selectedBackend && dnsQuestion.ids.tracingEnabled) {
5,334!
1472
    tracer->setSpanAttribute(closer.getSpanID(), "backend.name", AnyValue{selectedBackend->getNameWithAddr()});
8✔
1473
    tracer->setSpanAttribute(closer.getSpanID(), "backend.id", AnyValue{boost::uuids::to_string(selectedBackend->getID())});
8✔
1474
  }
8✔
1475

1476
  return selectedBackend;
5,334✔
1477
}
5,334✔
1478

1479
// NOLINTNEXTLINE(readability-function-cognitive-complexity): refactoring will be done in https://github.com/PowerDNS/pdns/pull/16124
1480
ProcessQueryResult processQueryAfterRules(DNSQuestion& dnsQuestion, std::shared_ptr<DownstreamState>& outgoingBackend)
1481
{
6,363✔
1482
  const auto sendAnswer = [](DNSQuestion& dnsQ) -> ProcessQueryResult {
6,363✔
1483
    ++dnsdist::metrics::g_stats.responses;
546✔
1484
    ++dnsQ.ids.cs->responses;
546✔
1485
    return ProcessQueryResult::SendAnswer;
546✔
1486
  };
546✔
1487
  const uint16_t queryId = ntohs(dnsQuestion.getHeader()->id);
6,363✔
1488

1489
  try {
6,363✔
1490
    if (dnsQuestion.getHeader()->qr) { // something turned it into a response
6,363✔
1491
      return handleQueryTurnedIntoSelfAnsweredResponse(dnsQuestion);
473✔
1492
    }
473✔
1493
    bool backendLookupDone = false;
5,890✔
1494
    const auto& serverPool = getPool(dnsQuestion.ids.poolName);
5,890✔
1495
    ServerPolicy::SelectedBackend selectedBackend(serverPool.getServers());
5,890✔
1496
    if (!serverPool.packetCache || !serverPool.isConsistent()) {
5,890✔
1497
      selectedBackend = selectBackendForOutgoingQuery(dnsQuestion, serverPool);
4,834✔
1498
      backendLookupDone = true;
4,834✔
1499
    }
4,834✔
1500

1501
    bool willBeForwardedOverUDP = !dnsQuestion.overTCP() || dnsQuestion.ids.protocol == dnsdist::Protocol::DoH;
5,890✔
1502
    if (selectedBackend) {
5,890✔
1503
      if (selectedBackend->isTCPOnly()) {
4,809✔
1504
        willBeForwardedOverUDP = false;
262✔
1505
      }
262✔
1506
    }
4,809✔
1507
    else if (serverPool.isTCPOnly()) {
1,081✔
1508
      willBeForwardedOverUDP = false;
117✔
1509
    }
117✔
1510

1511
    uint32_t allowExpired = 0;
5,890✔
1512
    if (!selectedBackend && dnsdist::configuration::getCurrentRuntimeConfiguration().d_staleCacheEntriesTTL > 0 && (backendLookupDone || !serverPool.hasAtLeastOneServerAvailable())) {
5,890!
1513
      allowExpired = dnsdist::configuration::getCurrentRuntimeConfiguration().d_staleCacheEntriesTTL;
6✔
1514
    }
6✔
1515

1516
    if (serverPool.packetCache && !dnsQuestion.ids.skipCache && !dnsQuestion.ids.dnssecOK) {
5,890✔
1517
      dnsQuestion.ids.dnssecOK = (dnsdist::getEDNSZ(dnsQuestion) & EDNS_HEADER_FLAG_DO) != 0;
349✔
1518
    }
349✔
1519

1520
    const bool useECS = dnsQuestion.useECS && ((selectedBackend && selectedBackend->d_config.useECS) || (!selectedBackend && serverPool.getECS()));
5,890✔
1521
    if (useECS) {
5,890✔
1522
      const bool useZeroScope = (selectedBackend && !selectedBackend->d_config.disableZeroScope) || (!selectedBackend && serverPool.getZeroScope());
148!
1523
      // we special case our cache in case a downstream explicitly gave us a universally valid response with a 0 scope
1524
      // we need ECS parsing (parseECS) to be true so we can be sure that the initial incoming query did not have an existing
1525
      // ECS option, which would make it unsuitable for the zero-scope feature.
1526
      if (serverPool.packetCache && !dnsQuestion.ids.skipCache && useZeroScope && serverPool.packetCache->isECSParsingEnabled()) {
148!
1527
        if (serverPool.packetCache->get(dnsQuestion, dnsQuestion.getHeader()->id, &dnsQuestion.ids.cacheKeyNoECS, dnsQuestion.ids.subnet, *dnsQuestion.ids.dnssecOK, willBeForwardedOverUDP, allowExpired, false, true, false)) {
24✔
1528

1529
          vinfolog("Packet cache hit for query for %s|%s from %s (%s, %d bytes)", dnsQuestion.ids.qname.toLogString(), QType(dnsQuestion.ids.qtype).toString(), dnsQuestion.ids.origRemote.toStringWithPort(), dnsQuestion.ids.protocol.toString(), dnsQuestion.getData().size());
7!
1530

1531
          if (!prepareOutgoingResponse(*dnsQuestion.ids.cs, dnsQuestion, true)) {
7!
1532
            return ProcessQueryResult::Drop;
×
1533
          }
×
1534

1535
          return sendAnswer(dnsQuestion);
7✔
1536
        }
7✔
1537

1538
        if (!dnsQuestion.ids.subnet) {
17✔
1539
          /* there was no existing ECS on the query, enable the zero-scope feature */
1540
          dnsQuestion.ids.useZeroScope = true;
15✔
1541
        }
15✔
1542
      }
17✔
1543

1544
      if (!handleEDNSClientSubnet(dnsQuestion, dnsQuestion.ids.ednsAdded, dnsQuestion.ids.ecsAdded)) {
141!
1545
        vinfolog("Dropping query from %s because we couldn't insert the ECS value", dnsQuestion.ids.origRemote.toStringWithPort());
×
1546
        return ProcessQueryResult::Drop;
×
1547
      }
×
1548
    }
141✔
1549

1550
    if (serverPool.packetCache && !dnsQuestion.ids.skipCache) {
5,883✔
1551
      /* First lookup, which takes into account how the protocol over which the query will be forwarded.
1552
         For DoH, this lookup is done with the protocol set to TCP but we will retry over UDP below,
1553
         therefore we do not record a miss for queries received over DoH and forwarded over TCP
1554
         yet, as we will do a second-lookup */
1555
      if (serverPool.packetCache->get(dnsQuestion, dnsQuestion.getHeader()->id, dnsQuestion.ids.protocol == dnsdist::Protocol::DoH ? &dnsQuestion.ids.cacheKeyTCP : &dnsQuestion.ids.cacheKey, dnsQuestion.ids.subnet, *dnsQuestion.ids.dnssecOK, dnsQuestion.ids.protocol != dnsdist::Protocol::DoH && willBeForwardedOverUDP, allowExpired, false, true, dnsQuestion.ids.protocol != dnsdist::Protocol::DoH || !willBeForwardedOverUDP)) {
799!
1556

1557
        dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [flags = dnsQuestion.ids.origFlags](dnsheader& header) {
509✔
1558
          restoreFlags(&header, flags);
509✔
1559
          return true;
509✔
1560
        });
509✔
1561

1562
        vinfolog("Packet cache hit for query for %s|%s from %s (%s, %d bytes)", dnsQuestion.ids.qname.toLogString(), QType(dnsQuestion.ids.qtype).toString(), dnsQuestion.ids.origRemote.toStringWithPort(), dnsQuestion.ids.protocol.toString(), dnsQuestion.getData().size());
509✔
1563

1564
        if (!prepareOutgoingResponse(*dnsQuestion.ids.cs, dnsQuestion, true)) {
509✔
1565
          return ProcessQueryResult::Drop;
11✔
1566
        }
11✔
1567

1568
        return sendAnswer(dnsQuestion);
498✔
1569
      }
509✔
1570
      if (dnsQuestion.ids.protocol == dnsdist::Protocol::DoH && willBeForwardedOverUDP) {
290!
1571
        /* do a second-lookup for responses received over UDP, but we do not want TC=1 answers */
1572
        /* we need to be careful to keep the existing cache-key (TCP) */
1573
        if (serverPool.packetCache->get(dnsQuestion, dnsQuestion.getHeader()->id, &dnsQuestion.ids.cacheKey, dnsQuestion.ids.subnet, *dnsQuestion.ids.dnssecOK, true, allowExpired, false, false, true)) {
49✔
1574
          if (!prepareOutgoingResponse(*dnsQuestion.ids.cs, dnsQuestion, true)) {
34✔
1575
            return ProcessQueryResult::Drop;
4✔
1576
          }
4✔
1577

1578
          return sendAnswer(dnsQuestion);
30✔
1579
        }
34✔
1580
      }
49✔
1581

1582
      vinfolog("Packet cache miss for query for %s|%s from %s (%s, %d bytes)", dnsQuestion.ids.qname.toLogString(), QType(dnsQuestion.ids.qtype).toString(), dnsQuestion.ids.origRemote.toStringWithPort(), dnsQuestion.ids.protocol.toString(), dnsQuestion.getData().size());
256✔
1583

1584
      ++dnsdist::metrics::g_stats.cacheMisses;
256✔
1585

1586
      // coverity[auto_causes_copy]
1587
      const auto existingPool = dnsQuestion.ids.poolName;
256✔
1588
      const auto& chains = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ruleChains;
256✔
1589
      const auto& cacheMissRuleActions = dnsdist::rules::getRuleChain(chains, dnsdist::rules::RuleChain::CacheMissRules);
256✔
1590

1591
      if (!applyRulesChainToQuery(cacheMissRuleActions, dnsQuestion)) {
256!
1592
        return ProcessQueryResult::Drop;
×
1593
      }
×
1594
      if (dnsQuestion.getHeader()->qr) { // something turned it into a response
256✔
1595
        return handleQueryTurnedIntoSelfAnsweredResponse(dnsQuestion);
2✔
1596
      }
2✔
1597
      /* let's be nice and allow the selection of a different pool,
1598
         but no second cache-lookup for you */
1599
      if (dnsQuestion.ids.poolName != existingPool) {
254✔
1600
        const auto& newServerPool = getPool(dnsQuestion.ids.poolName);
1✔
1601
        dnsQuestion.ids.packetCache = newServerPool.packetCache;
1✔
1602
        selectedBackend = selectBackendForOutgoingQuery(dnsQuestion, newServerPool);
1✔
1603
        backendLookupDone = true;
1✔
1604
      }
1✔
1605
      else {
253✔
1606
        dnsQuestion.ids.packetCache = serverPool.packetCache;
253✔
1607
      }
253✔
1608
    }
254✔
1609

1610
    if (!backendLookupDone) {
5,338✔
1611
      selectedBackend = selectBackendForOutgoingQuery(dnsQuestion, serverPool);
499✔
1612
    }
499✔
1613

1614
    if (!selectedBackend) {
5,338✔
1615
      auto servFailOnNoPolicy = dnsdist::configuration::getCurrentRuntimeConfiguration().d_servFailOnNoPolicy;
27✔
1616
      ++dnsdist::metrics::g_stats.noPolicy;
27✔
1617

1618
      vinfolog("%s query for %s|%s from %s, no downstream server available", servFailOnNoPolicy ? "ServFailed" : "Dropped", dnsQuestion.ids.qname.toLogString(), QType(dnsQuestion.ids.qtype).toString(), dnsQuestion.ids.origRemote.toStringWithPort());
27!
1619
      if (servFailOnNoPolicy) {
27✔
1620
        dnsdist::self_answers::removeRecordsAndSetRCode(dnsQuestion, RCode::ServFail);
11✔
1621

1622
        fixUpQueryTurnedResponse(dnsQuestion, dnsQuestion.ids.origFlags);
11✔
1623

1624
        if (!prepareOutgoingResponse(*dnsQuestion.ids.cs, dnsQuestion, false)) {
11!
1625
          return ProcessQueryResult::Drop;
×
1626
        }
×
1627
        return sendAnswer(dnsQuestion);
11✔
1628
      }
11✔
1629

1630
      return ProcessQueryResult::Drop;
16✔
1631
    }
27✔
1632

1633
    /* save the DNS flags as sent to the backend so we can cache the answer with the right flags later */
1634
    dnsQuestion.ids.cacheFlags = *getFlagsFromDNSHeader(dnsQuestion.getHeader().get());
5,311✔
1635

1636
    if (selectedBackend->d_config.useProxyProtocol && dnsQuestion.getProtocol().isEncrypted() && selectedBackend->d_config.d_proxyProtocolAdvertiseTLS) {
5,311✔
1637
      if (!dnsQuestion.proxyProtocolValues) {
24!
1638
        dnsQuestion.proxyProtocolValues = std::make_unique<std::vector<ProxyProtocolValue>>();
×
1639
      }
×
1640
      dnsQuestion.proxyProtocolValues->push_back(ProxyProtocolValue{"", static_cast<uint8_t>(ProxyProtocolValue::Types::PP_TLV_SSL)});
24✔
1641
    }
24✔
1642

1643
    selectedBackend->incQueriesCount();
5,311✔
1644
    outgoingBackend = selectedBackend.get();
5,311✔
1645
    return ProcessQueryResult::PassToBackend;
5,311✔
1646
  }
5,338✔
1647
  catch (const std::exception& e) {
6,363✔
1648
    vinfolog("Got an error while parsing a %s query (after applying rules)  from %s, id %d: %s", (dnsQuestion.overTCP() ? "TCP" : "UDP"), dnsQuestion.ids.origRemote.toStringWithPort(), queryId, e.what());
6!
1649
  }
6✔
1650
  return ProcessQueryResult::Drop;
6✔
1651
}
6,363✔
1652

1653
bool handleTimeoutResponseRules(const std::vector<dnsdist::rules::ResponseRuleAction>& rules, InternalQueryState& ids, const std::shared_ptr<DownstreamState>& d_ds, const std::shared_ptr<TCPQuerySender>& sender)
1654
{
33✔
1655
  /* let's be nice and restore the original DNS header as well as we can with what we have */
1656
  PacketBuffer payload(sizeof(dnsheader));
33✔
1657
  dnsdist::PacketMangling::editDNSHeaderFromPacket(payload, [&ids](dnsheader& header) {
33✔
1658
    memset(&header, 0, sizeof(header));
33✔
1659
    header.id = ids.origID;
33✔
1660
    restoreFlags(&header, ids.origFlags);
33✔
1661
    // do not set the qdcount, otherwise the protobuf code will choke on it
1662
    // while trying to parse the response RRs
1663
    return true;
33✔
1664
  });
33✔
1665
  DNSResponse dnsResponse(ids, payload, d_ds);
33✔
1666
  auto protocol = dnsResponse.getProtocol();
33✔
1667

1668
  vinfolog("Handling timeout response rules for incoming protocol = %s", protocol.toString());
33✔
1669
  if (protocol == dnsdist::Protocol::DoH) {
33✔
1670
#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
3✔
1671
    dnsResponse.d_incomingTCPState = std::dynamic_pointer_cast<IncomingHTTP2Connection>(sender);
3✔
1672
#endif
3✔
1673
    if (!dnsResponse.d_incomingTCPState || !sender || !sender->active()) {
3!
1674
      return false;
×
1675
    }
×
1676
  }
3✔
1677
  else if (protocol == dnsdist::Protocol::DoTCP || protocol == dnsdist::Protocol::DNSCryptTCP || protocol == dnsdist::Protocol::DoT) {
30!
1678
    dnsResponse.d_incomingTCPState = std::dynamic_pointer_cast<IncomingTCPConnectionState>(sender);
12✔
1679
    if (!dnsResponse.d_incomingTCPState || !sender || !sender->active()) {
12!
1680
      return false;
×
1681
    }
×
1682
  }
12✔
1683

1684
  try {
33✔
1685
    (void)applyRulesToResponse(rules, dnsResponse);
33✔
1686
  }
33✔
1687
  catch (const std::exception& exp) {
33✔
1688
    vinfolog("Exception while processing timeout response rules: %s", exp.what());
×
1689
  }
×
1690

1691
  return dnsResponse.isAsynchronous();
33✔
1692
}
33✔
1693

1694
void handleServerStateChange(const string& nameWithAddr, bool newResult)
1695
{
689✔
1696
  try {
689✔
1697
    auto lua = g_lua.lock();
689✔
1698
    dnsdist::lua::hooks::runServerStateChangeHooks(*lua, nameWithAddr, newResult);
689✔
1699
  }
689✔
1700
  catch (const std::exception& exp) {
689✔
1701
    warnlog("Error calling the Lua hook for Server State Change: %s", exp.what());
×
1702
  }
×
1703
}
689✔
1704

1705
class UDPTCPCrossQuerySender : public TCPQuerySender
1706
{
1707
public:
1708
  UDPTCPCrossQuerySender() = default;
800✔
1709
  UDPTCPCrossQuerySender(const UDPTCPCrossQuerySender&) = delete;
1710
  UDPTCPCrossQuerySender& operator=(const UDPTCPCrossQuerySender&) = delete;
1711
  UDPTCPCrossQuerySender(UDPTCPCrossQuerySender&&) = default;
1712
  UDPTCPCrossQuerySender& operator=(UDPTCPCrossQuerySender&&) = default;
1713
  ~UDPTCPCrossQuerySender() override = default;
1714

1715
  [[nodiscard]] bool active() const override
1716
  {
147✔
1717
    return true;
147✔
1718
  }
147✔
1719

1720
  void handleResponse(const struct timeval& now, TCPResponse&& response) override
1721
  {
170✔
1722
    (void)now;
170✔
1723
    if (!response.d_ds && !response.d_idstate.selfGenerated) {
170!
1724
      throw std::runtime_error("Passing a cross-protocol answer originated from UDP without a valid downstream");
×
1725
    }
×
1726

1727
    auto& ids = response.d_idstate;
170✔
1728

1729
    handleResponseForUDPClient(ids, response.d_buffer, response.d_ds, response.isAsync(), response.d_idstate.selfGenerated);
170✔
1730
  }
170✔
1731

1732
  void handleXFRResponse(const struct timeval& now, TCPResponse&& response) override
1733
  {
×
1734
    return handleResponse(now, std::move(response));
×
1735
  }
×
1736

1737
  void notifyIOError([[maybe_unused]] const struct timeval& now, [[maybe_unused]] TCPResponse&& response) override
1738
  {
12✔
1739
    // nothing to do
1740
  }
12✔
1741
};
1742

1743
class UDPCrossProtocolQuery : public CrossProtocolQuery
1744
{
1745
public:
1746
  UDPCrossProtocolQuery(PacketBuffer&& buffer_, InternalQueryState&& ids_, std::shared_ptr<DownstreamState> backend) :
1747
    CrossProtocolQuery(InternalQuery(std::move(buffer_), std::move(ids_)), backend)
503✔
1748
  {
503✔
1749
    auto& ids = query.d_idstate;
503✔
1750
    const auto& buffer = query.d_buffer;
503✔
1751

1752
    if (ids.udpPayloadSize == 0) {
503✔
1753
      uint16_t zValue = 0;
373✔
1754
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
1755
      getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(buffer.data()), buffer.size(), &ids.udpPayloadSize, &zValue);
373✔
1756
      if (!ids.dnssecOK) {
373!
1757
        ids.dnssecOK = (zValue & EDNS_HEADER_FLAG_DO) != 0;
×
1758
      }
×
1759
      if (ids.udpPayloadSize < 512) {
373!
1760
        ids.udpPayloadSize = 512;
373✔
1761
      }
373✔
1762
    }
373✔
1763
  }
503✔
1764
  UDPCrossProtocolQuery(const UDPCrossProtocolQuery&) = delete;
1765
  UDPCrossProtocolQuery& operator=(const UDPCrossProtocolQuery&) = delete;
1766
  UDPCrossProtocolQuery(UDPCrossProtocolQuery&&) = delete;
1767
  UDPCrossProtocolQuery& operator=(UDPCrossProtocolQuery&&) = delete;
1768
  ~UDPCrossProtocolQuery() override = default;
1769

1770
  std::shared_ptr<TCPQuerySender> getTCPQuerySender() override
1771
  {
184✔
1772
    return s_sender;
184✔
1773
  }
184✔
1774

1775
private:
1776
  static std::shared_ptr<UDPTCPCrossQuerySender> s_sender;
1777
};
1778

1779
std::shared_ptr<UDPTCPCrossQuerySender> UDPCrossProtocolQuery::s_sender = std::make_shared<UDPTCPCrossQuerySender>();
1780

1781
std::unique_ptr<CrossProtocolQuery> getUDPCrossProtocolQueryFromDQ(DNSQuestion& dnsQuestion);
1782
std::unique_ptr<CrossProtocolQuery> getUDPCrossProtocolQueryFromDQ(DNSQuestion& dnsQuestion)
1783
{
367✔
1784
  dnsQuestion.ids.origID = dnsQuestion.getHeader()->id;
367✔
1785
  return std::make_unique<UDPCrossProtocolQuery>(std::move(dnsQuestion.getMutableData()), std::move(dnsQuestion.ids), nullptr);
367✔
1786
}
367✔
1787

1788
ProcessQueryResult processQuery(DNSQuestion& dnsQuestion, std::shared_ptr<DownstreamState>& selectedBackend)
1789
{
6,100✔
1790

1791
  pdns::trace::dnsdist::Tracer::Closer closer;
6,100✔
1792
  if (auto tracer = dnsQuestion.ids.getTracer(); tracer != nullptr) {
6,100✔
1793
    closer = tracer->openSpan("processQuery", tracer->getLastSpanID());
12✔
1794
  }
12✔
1795
  const uint16_t queryId = ntohs(dnsQuestion.getHeader()->id);
6,100✔
1796
  try {
6,100✔
1797
    /* we need an accurate ("real") value for the response and
1798
       to store into the IDS, but not for insertion into the
1799
       rings for example */
1800
    timespec now{};
6,100✔
1801
    gettime(&now);
6,100✔
1802

1803
    if ((dnsQuestion.ids.qtype == QType::AXFR || dnsQuestion.ids.qtype == QType::IXFR) && (dnsQuestion.getProtocol() == dnsdist::Protocol::DoH || dnsQuestion.getProtocol() == dnsdist::Protocol::DoQ || dnsQuestion.getProtocol() == dnsdist::Protocol::DoH3)) {
6,100✔
1804
      dnsdist::self_answers::removeRecordsAndSetRCode(dnsQuestion, RCode::NotImp);
8✔
1805
      return processQueryAfterRules(dnsQuestion, selectedBackend);
8✔
1806
    }
8✔
1807

1808
    if (!applyRulesToQuery(dnsQuestion, now)) {
6,092✔
1809
      return ProcessQueryResult::Drop;
47✔
1810
    }
47✔
1811

1812
    if (dnsQuestion.isAsynchronous()) {
6,045✔
1813
      return ProcessQueryResult::Asynchronous;
202✔
1814
    }
202✔
1815

1816
    return processQueryAfterRules(dnsQuestion, selectedBackend);
5,843✔
1817
  }
6,045✔
1818
  catch (const std::exception& e) {
6,100✔
1819
    vinfolog("Got an error while parsing a %s query from %s, id %d: %s", (dnsQuestion.overTCP() ? "TCP" : "UDP"), dnsQuestion.ids.origRemote.toStringWithPort(), queryId, e.what());
×
1820
  }
×
1821
  return ProcessQueryResult::Drop;
×
1822
}
6,100✔
1823

1824
bool assignOutgoingUDPQueryToBackend(std::shared_ptr<DownstreamState>& downstream, uint16_t queryID, DNSQuestion& dnsQuestion, PacketBuffer& query, bool actuallySend)
1825
{
2,661✔
1826
  pdns::trace::dnsdist::Tracer::Closer closer;
2,661✔
1827
  if (auto tracer = dnsQuestion.ids.getTracer(); tracer != nullptr && dnsQuestion.ids.tracingEnabled) {
2,661✔
1828
    closer = tracer->openSpan("assignOutgoingUDPQueryToBackend", tracer->getLastSpanIDForName("processUDPQuery"));
4✔
1829
  }
4✔
1830

1831
  bool doh = dnsQuestion.ids.du != nullptr;
2,661✔
1832

1833
  bool failed = false;
2,661✔
1834
  dnsQuestion.ids.d_proxyProtocolPayloadSize = 0;
2,661✔
1835
  if (downstream->d_config.useProxyProtocol) {
2,661✔
1836
    try {
24✔
1837
      size_t proxyProtocolPayloadSize = 0;
24✔
1838
      if (addProxyProtocol(dnsQuestion, &proxyProtocolPayloadSize)) {
24✔
1839
        dnsQuestion.ids.d_proxyProtocolPayloadSize += proxyProtocolPayloadSize;
22✔
1840
      }
22✔
1841
    }
24✔
1842
    catch (const std::exception& e) {
24✔
1843
      vinfolog("Adding proxy protocol payload to %s query from %s failed: %s", (dnsQuestion.ids.du ? "DoH" : ""), dnsQuestion.ids.origDest.toStringWithPort(), e.what());
2!
1844
      return false;
2✔
1845
    }
2✔
1846
  }
24✔
1847

1848
  if (doh && !dnsQuestion.ids.d_packet) {
2,659✔
1849
    dnsQuestion.ids.d_packet = std::make_unique<PacketBuffer>(query);
121✔
1850
  }
121✔
1851

1852
  try {
2,659✔
1853
    int descriptor = downstream->pickSocketForSending();
2,659✔
1854
    if (actuallySend) {
2,659!
1855
      dnsQuestion.ids.backendFD = descriptor;
2,659✔
1856
    }
2,659✔
1857
    dnsQuestion.ids.origID = queryID;
2,659✔
1858
    dnsQuestion.ids.forwardedOverUDP = true;
2,659✔
1859

1860
    vinfolog("Got query for %s|%s from %s%s, relayed to %s%s", dnsQuestion.ids.qname.toLogString(), QType(dnsQuestion.ids.qtype).toString(), dnsQuestion.ids.origRemote.toStringWithPort(), (doh ? " (https)" : ""), downstream->getNameWithAddr(), actuallySend ? "" : " (xsk)");
2,659!
1861

1862
    /* make a copy since we cannot touch dnsQuestion.ids after the move */
1863
    auto proxyProtocolPayloadSize = dnsQuestion.ids.d_proxyProtocolPayloadSize;
2,659✔
1864
    auto idOffset = downstream->saveState(std::move(dnsQuestion.ids));
2,659✔
1865
    /* set the correct ID */
1866
    memcpy(&query.at(proxyProtocolPayloadSize), &idOffset, sizeof(idOffset));
2,659✔
1867

1868
    if (!actuallySend) {
2,659!
1869
      return true;
×
1870
    }
×
1871

1872
    /* you can't touch ids or du after this line, unless the call returned a non-negative value,
1873
       because it might already have been freed */
1874
    ssize_t ret = udpClientSendRequestToBackend(downstream, descriptor, query);
2,659✔
1875

1876
    if (ret < 0) {
2,659!
1877
      failed = true;
×
1878
    }
×
1879

1880
    if (failed) {
2,659!
1881
      /* clear up the state. In the very unlikely event it was reused
1882
         in the meantime, so be it. */
1883
      auto cleared = downstream->getState(idOffset);
×
1884
      if (cleared) {
×
1885
        dnsQuestion.ids.du = std::move(cleared->du);
×
1886
      }
×
1887
      ++dnsdist::metrics::g_stats.downstreamSendErrors;
×
1888
      ++downstream->sendErrors;
×
1889
      return false;
×
1890
    }
×
1891
  }
2,659✔
1892
  catch (const std::exception& e) {
2,659✔
1893
    throw;
×
1894
  }
×
1895

1896
  return true;
2,659✔
1897
}
2,659✔
1898

1899
static void processUDPQuery(ClientState& clientState, const struct msghdr* msgh, const ComboAddress& remote, ComboAddress& dest, PacketBuffer& query, std::vector<mmsghdr>* responsesVect, unsigned int* queuedResponses, struct iovec* respIOV, cmsgbuf_aligned* respCBuf)
1900
{
2,917✔
1901
  assert(responsesVect == nullptr || (queuedResponses != nullptr && respIOV != nullptr && respCBuf != nullptr));
2,917!
1902
  uint16_t queryId = 0;
2,917✔
1903
  InternalQueryState ids;
2,917✔
1904

1905
  pdns::trace::dnsdist::Tracer::Closer closer;
2,917✔
1906
  if (auto tracer = ids.getTracer(); tracer != nullptr) {
2,917✔
1907
    closer = tracer->openSpan("processUDPQuery");
8✔
1908
  }
8✔
1909

1910
  ids.cs = &clientState;
2,917✔
1911
  ids.origRemote = remote;
2,917✔
1912
  ids.hopRemote = remote;
2,917✔
1913
  ids.protocol = dnsdist::Protocol::DoUDP;
2,917✔
1914

1915
  try {
2,917✔
1916
    bool expectProxyProtocol = false;
2,917✔
1917
    if (!isUDPQueryAcceptable(clientState, msgh, remote, dest, expectProxyProtocol)) {
2,917✔
1918
      return;
1✔
1919
    }
1✔
1920
    /* dest might have been updated, if we managed to harvest the destination address */
1921
    if (dest.sin4.sin_family != 0) {
2,916✔
1922
      ids.origDest = dest;
5✔
1923
      ids.hopLocal = dest;
5✔
1924
    }
5✔
1925
    else {
2,911✔
1926
      /* if we have not been able to harvest the destination address,
1927
         we do NOT want to update dest or hopLocal, to let the kernel
1928
         pick the less terrible option, but we want to update origDest
1929
         which is used by rules and actions to at least the correct
1930
         address family */
1931
      ids.origDest = clientState.local;
2,911✔
1932
      ids.hopLocal.sin4.sin_family = 0;
2,911✔
1933
    }
2,911✔
1934

1935
    std::vector<ProxyProtocolValue> proxyProtocolValues;
2,916✔
1936
    if (expectProxyProtocol && !handleProxyProtocol(remote, false, dnsdist::configuration::getCurrentRuntimeConfiguration().d_ACL, query, ids.origRemote, ids.origDest, proxyProtocolValues)) {
2,916✔
1937
      return;
1✔
1938
    }
1✔
1939

1940
    ids.queryRealTime.start();
2,915✔
1941

1942
    auto dnsCryptResponse = checkDNSCryptQuery(clientState, query, ids.dnsCryptQuery, ids.queryRealTime.d_start.tv_sec, false);
2,915✔
1943
    if (dnsCryptResponse) {
2,915✔
1944
      sendUDPResponse(clientState.udpFD, query, 0, dest, remote);
21✔
1945
      return;
21✔
1946
    }
21✔
1947

1948
    {
2,894✔
1949
      /* this pointer will be invalidated the second the buffer is resized, don't hold onto it! */
1950
      const dnsheader_aligned dnsHeader(query.data());
2,894✔
1951
      queryId = ntohs(dnsHeader->id);
2,894✔
1952

1953
      if (!checkQueryHeaders(*dnsHeader, clientState)) {
2,894✔
1954
        return;
1✔
1955
      }
1✔
1956

1957
      if (dnsHeader->qdcount == 0) {
2,893✔
1958
        dnsdist::PacketMangling::editDNSHeaderFromPacket(query, [](dnsheader& header) {
1✔
1959
          header.rcode = RCode::NotImp;
1✔
1960
          header.qr = true;
1✔
1961
          return true;
1✔
1962
        });
1✔
1963

1964
        sendUDPResponse(clientState.udpFD, query, 0, dest, remote);
1✔
1965
        return;
1✔
1966
      }
1✔
1967
    }
2,893✔
1968

1969
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
1970
    ids.qname = DNSName(reinterpret_cast<const char*>(query.data()), query.size(), sizeof(dnsheader), false, &ids.qtype, &ids.qclass);
2,892✔
1971
    if (ids.dnsCryptQuery) {
2,892✔
1972
      ids.protocol = dnsdist::Protocol::DNSCryptUDP;
20✔
1973
    }
20✔
1974
    DNSQuestion dnsQuestion(ids, query);
2,892✔
1975
    const uint16_t* flags = getFlagsFromDNSHeader(dnsQuestion.getHeader().get());
2,892✔
1976
    ids.origFlags = *flags;
2,892✔
1977

1978
    if (!proxyProtocolValues.empty()) {
2,892✔
1979
      dnsQuestion.proxyProtocolValues = make_unique<std::vector<ProxyProtocolValue>>(std::move(proxyProtocolValues));
6✔
1980
    }
6✔
1981

1982
    // save UDP payload size from origin query
1983
    uint16_t udpPayloadSize = 0;
2,892✔
1984
    uint16_t zValue = 0;
2,892✔
1985
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
1986
    getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(query.data()), query.size(), &udpPayloadSize, &zValue);
2,892✔
1987
    if (!ids.dnssecOK) {
2,892✔
1988
      ids.dnssecOK = (zValue & EDNS_HEADER_FLAG_DO) != 0;
2,888✔
1989
    }
2,888✔
1990
    if (udpPayloadSize < 512) {
2,892✔
1991
      udpPayloadSize = 512;
2,743✔
1992
    }
2,743✔
1993

1994
    std::shared_ptr<DownstreamState> backend{nullptr};
2,892✔
1995
    auto result = processQuery(dnsQuestion, backend);
2,892✔
1996

1997
    if (result == ProcessQueryResult::Drop || result == ProcessQueryResult::Asynchronous) {
2,892✔
1998
      return;
67✔
1999
    }
67✔
2000

2001
    // the buffer might have been invalidated by now (resized)
2002
    const auto dnsHeader = dnsQuestion.getHeader();
2,825✔
2003
    if (result == ProcessQueryResult::SendAnswer) {
2,825✔
2004
      /* ensure payload size is not exceeded */
2005
      handleResponseTC4UDPClient(dnsQuestion, udpPayloadSize, query);
466✔
2006
#ifndef DISABLE_RECVMMSG
466✔
2007
#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
466✔
2008
      if (dnsQuestion.ids.delayMsec == 0 && responsesVect != nullptr) {
466!
2009
        queueResponse(query, dest, remote, (*responsesVect)[*queuedResponses], respIOV, respCBuf);
6✔
2010
        (*queuedResponses)++;
6✔
2011
        handleResponseSent(dnsQuestion.ids.qname, dnsQuestion.ids.qtype, 0., remote, ComboAddress(), query.size(), *dnsHeader, dnsdist::Protocol::DoUDP, dnsdist::Protocol::DoUDP, false);
6✔
2012
        return;
6✔
2013
      }
6✔
2014
#endif /* defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE) */
460✔
2015
#endif /* DISABLE_RECVMMSG */
460✔
2016
      /* we use dest, always, because we don't want to use the listening address to send a response since it could be 0.0.0.0 */
2017
      sendUDPResponse(clientState.udpFD, query, dnsQuestion.ids.delayMsec, dest, remote);
460✔
2018

2019
      handleResponseSent(dnsQuestion.ids.qname, dnsQuestion.ids.qtype, 0., remote, ComboAddress(), query.size(), *dnsHeader, dnsdist::Protocol::DoUDP, dnsdist::Protocol::DoUDP, false);
460✔
2020
      return;
460✔
2021
    }
466✔
2022

2023
    if (result != ProcessQueryResult::PassToBackend || backend == nullptr) {
2,359!
2024
      return;
×
2025
    }
×
2026

2027
    if (backend->isTCPOnly()) {
2,359✔
2028
      std::string proxyProtocolPayload;
136✔
2029
      /* we need to do this _before_ creating the cross protocol query because
2030
         after that the buffer will have been moved */
2031
      if (backend->d_config.useProxyProtocol) {
136✔
2032
        proxyProtocolPayload = getProxyProtocolPayload(dnsQuestion);
1✔
2033
      }
1✔
2034

2035
      ids.origID = dnsHeader->id;
136✔
2036
      auto cpq = std::make_unique<UDPCrossProtocolQuery>(std::move(query), std::move(ids), backend);
136✔
2037
      cpq->query.d_proxyProtocolPayload = std::move(proxyProtocolPayload);
136✔
2038

2039
      backend->passCrossProtocolQuery(std::move(cpq));
136✔
2040
      return;
136✔
2041
    }
136✔
2042

2043
    assignOutgoingUDPQueryToBackend(backend, dnsHeader->id, dnsQuestion, query);
2,223✔
2044
  }
2,223✔
2045
  catch (const std::exception& e) {
2,917✔
2046
    vinfolog("Got an error in UDP question thread while parsing a query from %s, id %d: %s", ids.origRemote.toStringWithPort(), queryId, e.what());
4!
2047
  }
4✔
2048
}
2,917✔
2049

2050
#ifdef HAVE_XSK
2051
namespace dnsdist::xsk
2052
{
2053
bool XskProcessQuery(ClientState& clientState, XskPacket& packet)
2054
{
×
2055
  uint16_t queryId = 0;
×
2056
  const auto& remote = packet.getFromAddr();
×
2057
  const auto& dest = packet.getToAddr();
×
2058
  InternalQueryState ids;
×
2059
  ids.cs = &clientState;
×
2060
  ids.origRemote = remote;
×
2061
  ids.hopRemote = remote;
×
2062
  ids.origDest = dest;
×
2063
  ids.hopLocal = dest;
×
2064
  ids.protocol = dnsdist::Protocol::DoUDP;
×
2065
  ids.xskPacketHeader = packet.cloneHeaderToPacketBuffer();
×
2066

2067
  try {
×
2068
    bool expectProxyProtocol = false;
×
2069
    if (!XskIsQueryAcceptable(packet, clientState, expectProxyProtocol)) {
×
2070
      return false;
×
2071
    }
×
2072

2073
    auto query = packet.clonePacketBuffer();
×
2074
    std::vector<ProxyProtocolValue> proxyProtocolValues;
×
2075
    if (expectProxyProtocol && !handleProxyProtocol(remote, false, dnsdist::configuration::getCurrentRuntimeConfiguration().d_ACL, query, ids.origRemote, ids.origDest, proxyProtocolValues)) {
×
2076
      return false;
×
2077
    }
×
2078

2079
    ids.queryRealTime.start();
×
2080

2081
    auto dnsCryptResponse = checkDNSCryptQuery(clientState, query, ids.dnsCryptQuery, ids.queryRealTime.d_start.tv_sec, false);
×
2082
    if (dnsCryptResponse) {
×
2083
      packet.setPayload(query);
×
2084
      return true;
×
2085
    }
×
2086

2087
    {
×
2088
      /* this pointer will be invalidated the second the buffer is resized, don't hold onto it! */
2089
      dnsheader_aligned dnsHeader(query.data());
×
2090
      queryId = ntohs(dnsHeader->id);
×
2091

2092
      if (!checkQueryHeaders(*dnsHeader, clientState)) {
×
2093
        return false;
×
2094
      }
×
2095

2096
      if (dnsHeader->qdcount == 0) {
×
2097
        dnsdist::PacketMangling::editDNSHeaderFromPacket(query, [](dnsheader& header) {
×
2098
          header.rcode = RCode::NotImp;
×
2099
          header.qr = true;
×
2100
          return true;
×
2101
        });
×
2102
        packet.setPayload(query);
×
2103
        return true;
×
2104
      }
×
2105
    }
×
2106

2107
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
2108
    ids.qname = DNSName(reinterpret_cast<const char*>(query.data()), query.size(), sizeof(dnsheader), false, &ids.qtype, &ids.qclass);
×
2109
    if (ids.origDest.sin4.sin_family == 0) {
×
2110
      ids.origDest = clientState.local;
×
2111
    }
×
2112
    if (ids.dnsCryptQuery) {
×
2113
      ids.protocol = dnsdist::Protocol::DNSCryptUDP;
×
2114
    }
×
2115
    DNSQuestion dnsQuestion(ids, query);
×
2116
    if (!proxyProtocolValues.empty()) {
×
2117
      dnsQuestion.proxyProtocolValues = make_unique<std::vector<ProxyProtocolValue>>(std::move(proxyProtocolValues));
×
2118
    }
×
2119
    std::shared_ptr<DownstreamState> backend{nullptr};
×
2120
    auto result = processQuery(dnsQuestion, backend);
×
2121

2122
    if (result == ProcessQueryResult::Drop) {
×
2123
      return false;
×
2124
    }
×
2125

2126
    if (result == ProcessQueryResult::SendAnswer) {
×
2127
      packet.setPayload(query);
×
2128
      if (dnsQuestion.ids.delayMsec > 0) {
×
2129
        packet.addDelay(dnsQuestion.ids.delayMsec);
×
2130
      }
×
2131
      const auto dnsHeader = dnsQuestion.getHeader();
×
2132
      handleResponseSent(ids.qname, ids.qtype, 0., remote, ComboAddress(), query.size(), *dnsHeader, dnsdist::Protocol::DoUDP, dnsdist::Protocol::DoUDP, false);
×
2133
      return true;
×
2134
    }
×
2135

2136
    if (result != ProcessQueryResult::PassToBackend || backend == nullptr) {
×
2137
      return false;
×
2138
    }
×
2139

2140
    // the buffer might have been invalidated by now (resized)
2141
    const auto dnsHeader = dnsQuestion.getHeader();
×
2142
    if (backend->isTCPOnly()) {
×
2143
      std::string proxyProtocolPayload;
×
2144
      /* we need to do this _before_ creating the cross protocol query because
2145
         after that the buffer will have been moved */
2146
      if (backend->d_config.useProxyProtocol) {
×
2147
        proxyProtocolPayload = getProxyProtocolPayload(dnsQuestion);
×
2148
      }
×
2149

2150
      ids.origID = dnsHeader->id;
×
2151
      auto cpq = std::make_unique<UDPCrossProtocolQuery>(std::move(query), std::move(ids), backend);
×
2152
      cpq->query.d_proxyProtocolPayload = std::move(proxyProtocolPayload);
×
2153

2154
      backend->passCrossProtocolQuery(std::move(cpq));
×
2155
      return false;
×
2156
    }
×
2157

2158
    if (backend->d_xskInfos.empty()) {
×
2159
      assignOutgoingUDPQueryToBackend(backend, dnsHeader->id, dnsQuestion, query, true);
×
2160
      return false;
×
2161
    }
×
2162

2163
    assignOutgoingUDPQueryToBackend(backend, dnsHeader->id, dnsQuestion, query, false);
×
2164
    auto sourceAddr = backend->pickSourceAddressForSending();
×
2165
    packet.setAddr(sourceAddr, backend->d_config.sourceMACAddr, backend->d_config.remote, backend->d_config.destMACAddr);
×
2166
    packet.setPayload(query);
×
2167
    packet.rewrite();
×
2168
    return true;
×
2169
  }
×
2170
  catch (const std::exception& e) {
×
2171
    vinfolog("Got an error in UDP question thread while parsing a query from %s, id %d: %s", remote.toStringWithPort(), queryId, e.what());
×
2172
  }
×
2173
  return false;
×
2174
}
×
2175

2176
}
2177
#endif /* HAVE_XSK */
2178

2179
#ifndef DISABLE_RECVMMSG
2180
#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
2181
static void MultipleMessagesUDPClientThread(ClientState* clientState)
2182
{
2✔
2183
  struct MMReceiver
2✔
2184
  {
2✔
2185
    PacketBuffer packet;
2✔
2186
    ComboAddress remote;
2✔
2187
    ComboAddress dest;
2✔
2188
    iovec iov{};
2✔
2189
    /* used by HarvestDestinationAddress */
2190
    cmsgbuf_aligned cbuf{};
2✔
2191
  };
2✔
2192
  const size_t vectSize = dnsdist::configuration::getImmutableConfiguration().d_udpVectorSize;
2✔
2193

2194
  if (vectSize > std::numeric_limits<uint16_t>::max()) {
2!
2195
    throw std::runtime_error("The value of setUDPMultipleMessagesVectorSize is too high, the maximum value is " + std::to_string(std::numeric_limits<uint16_t>::max()));
×
2196
  }
×
2197

2198
  auto recvData = std::vector<MMReceiver>(vectSize);
2✔
2199
  auto msgVec = std::vector<mmsghdr>(vectSize);
2✔
2200
  auto outMsgVec = std::vector<mmsghdr>(vectSize);
2✔
2201

2202
  /* the actual buffer is larger because:
2203
     - we may have to add EDNS and/or ECS
2204
     - we use it for self-generated responses (from rule or cache)
2205
     but we only accept incoming payloads up to that size
2206
  */
2207
  const size_t initialBufferSize = getInitialUDPPacketBufferSize(clientState->d_enableProxyProtocol);
2✔
2208
  const size_t maxIncomingPacketSize = getMaximumIncomingPacketSize(*clientState);
2✔
2209

2210
  /* initialize the structures needed to receive our messages */
2211
  for (size_t idx = 0; idx < vectSize; idx++) {
22✔
2212
    recvData[idx].remote.sin4.sin_family = clientState->local.sin4.sin_family;
20✔
2213
    recvData[idx].packet.resize(initialBufferSize);
20✔
2214
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
2215
    fillMSGHdr(&msgVec[idx].msg_hdr, &recvData[idx].iov, &recvData[idx].cbuf, sizeof(recvData[idx].cbuf), reinterpret_cast<char*>(recvData[idx].packet.data()), maxIncomingPacketSize, &recvData[idx].remote);
20✔
2216
  }
20✔
2217

2218
  /* go now */
2219
  for (;;) {
13✔
2220

2221
    /* reset the IO vector, since it's also used to send the vector of responses
2222
       to avoid having to copy the data around */
2223
    for (size_t idx = 0; idx < vectSize; idx++) {
143✔
2224
      recvData[idx].packet.resize(initialBufferSize);
130✔
2225
      recvData[idx].iov.iov_base = &recvData[idx].packet.at(0);
130✔
2226
      recvData[idx].iov.iov_len = recvData[idx].packet.size();
130✔
2227
    }
130✔
2228

2229
    /* block until we have at least one message ready, but return
2230
       as many as possible to save the syscall costs */
2231
    int msgsGot = recvmmsg(clientState->udpFD, msgVec.data(), vectSize, MSG_WAITFORONE | MSG_TRUNC, nullptr);
13✔
2232

2233
    if (msgsGot <= 0) {
13!
2234
      vinfolog("Getting UDP messages via recvmmsg() failed with: %s", stringerror());
×
2235
      continue;
×
2236
    }
×
2237

2238
    unsigned int msgsToSend = 0;
13✔
2239

2240
    /* process the received messages */
2241
    for (int msgIdx = 0; msgIdx < msgsGot; msgIdx++) {
24✔
2242
      const struct msghdr* msgh = &msgVec[msgIdx].msg_hdr;
11✔
2243
      unsigned int got = msgVec[msgIdx].msg_len;
11✔
2244
      const ComboAddress& remote = recvData[msgIdx].remote;
11✔
2245

2246
      if (static_cast<size_t>(got) < sizeof(struct dnsheader)) {
11!
2247
        ++dnsdist::metrics::g_stats.nonCompliantQueries;
×
2248
        ++clientState->nonCompliantQueries;
×
2249
        continue;
×
2250
      }
×
2251

2252
      recvData[msgIdx].packet.resize(got);
11✔
2253
      dnsdist::configuration::refreshLocalRuntimeConfiguration();
11✔
2254
      processUDPQuery(*clientState, msgh, remote, recvData[msgIdx].dest, recvData[msgIdx].packet, &outMsgVec, &msgsToSend, &recvData[msgIdx].iov, &recvData[msgIdx].cbuf);
11✔
2255
    }
11✔
2256

2257
    /* immediate (not delayed or sent to a backend) responses (mostly from a rule, dynamic block
2258
       or the cache) can be sent in batch too */
2259

2260
    if (msgsToSend > 0 && msgsToSend <= static_cast<unsigned int>(msgsGot)) {
13!
2261
      int sent = sendmmsg(clientState->udpFD, outMsgVec.data(), msgsToSend, 0);
6✔
2262

2263
      if (sent < 0 || static_cast<unsigned int>(sent) != msgsToSend) {
6!
2264
        vinfolog("Error sending responses with sendmmsg() (%d on %u): %s", sent, msgsToSend, stringerror());
×
2265
      }
×
2266
    }
6✔
2267
  }
13✔
2268
}
2✔
2269
#endif /* defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE) */
2270
#endif /* DISABLE_RECVMMSG */
2271

2272
// listens to incoming queries, sends out to downstream servers, noting the intended return path
2273
static void udpClientThread(std::vector<ClientState*> states)
2274
{
395✔
2275
  try {
395✔
2276
    setThreadName("dnsdist/udpClie");
395✔
2277
#ifndef DISABLE_RECVMMSG
395✔
2278
#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
395✔
2279
    if (dnsdist::configuration::getImmutableConfiguration().d_udpVectorSize > 1) {
395✔
2280
      MultipleMessagesUDPClientThread(states.at(0));
2✔
2281
    }
2✔
2282
    else
393✔
2283
#endif /* defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE) */
393✔
2284
#endif /* DISABLE_RECVMMSG */
393✔
2285
    {
393✔
2286
      /* the actual buffer is larger because:
2287
         - we may have to add EDNS and/or ECS
2288
         - we use it for self-generated responses (from rule or cache)
2289
         but we only accept incoming payloads up to that size
2290
      */
2291
      struct UDPStateParam
393✔
2292
      {
393✔
2293
        ClientState* cs{nullptr};
393✔
2294
        size_t maxIncomingPacketSize{0};
393✔
2295
        int socket{-1};
393✔
2296
      };
393✔
2297
      const size_t initialBufferSize = getInitialUDPPacketBufferSize(true);
393✔
2298
      PacketBuffer packet(initialBufferSize);
393✔
2299

2300
      msghdr msgh{};
393✔
2301
      iovec iov{};
393✔
2302
      ComboAddress remote;
393✔
2303
      ComboAddress dest;
393✔
2304

2305
      auto handleOnePacket = [&packet, &iov, &msgh, &remote, &dest, initialBufferSize](const UDPStateParam& param) {
3,299✔
2306
        packet.resize(initialBufferSize);
3,299✔
2307
        iov.iov_base = &packet.at(0);
3,299✔
2308
        iov.iov_len = packet.size();
3,299✔
2309

2310
        ssize_t got = recvmsg(param.socket, &msgh, 0);
3,299✔
2311

2312
        if (got < 0 || static_cast<size_t>(got) < sizeof(struct dnsheader)) {
3,299!
2313
          ++dnsdist::metrics::g_stats.nonCompliantQueries;
×
2314
          ++param.cs->nonCompliantQueries;
×
2315
          return;
×
2316
        }
×
2317

2318
        packet.resize(static_cast<size_t>(got));
3,299✔
2319

2320
        dnsdist::configuration::refreshLocalRuntimeConfiguration();
3,299✔
2321
        processUDPQuery(*param.cs, &msgh, remote, dest, packet, nullptr, nullptr, nullptr, nullptr);
3,299✔
2322
      };
3,299✔
2323

2324
      std::vector<UDPStateParam> params;
393✔
2325
      for (auto& state : states) {
393✔
2326
        const size_t maxIncomingPacketSize = getMaximumIncomingPacketSize(*state);
393✔
2327
        params.emplace_back(UDPStateParam{state, maxIncomingPacketSize, state->udpFD});
393✔
2328
      }
393✔
2329

2330
      if (params.size() == 1) {
393!
2331
        const auto& param = params.at(0);
393✔
2332
        remote.sin4.sin_family = param.cs->local.sin4.sin_family;
393✔
2333
        /* used by HarvestDestinationAddress */
2334
        cmsgbuf_aligned cbuf;
393✔
2335
        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
2336
        fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), reinterpret_cast<char*>(&packet.at(0)), param.maxIncomingPacketSize, &remote);
393✔
2337
        while (true) {
3,299✔
2338
          try {
3,299✔
2339
            handleOnePacket(param);
3,299✔
2340
          }
3,299✔
2341
          catch (const std::bad_alloc& e) {
3,299✔
2342
            /* most exceptions are handled by handleOnePacket(), but we might be out of memory (std::bad_alloc)
2343
               in which case we DO NOT want to log (as it would trigger another memory allocation attempt
2344
               that might throw as well) but wait a bit (one millisecond) and then try to recover */
2345
            usleep(1000);
×
2346
          }
×
2347
        }
3,299✔
2348
      }
393✔
UNCOV
2349
      else {
×
UNCOV
2350
        auto callback = [&remote, &msgh, &iov, &packet, &handleOnePacket, initialBufferSize](int socket, FDMultiplexer::funcparam_t& funcparam) {
×
2351
          (void)socket;
×
2352
          const auto* param = boost::any_cast<const UDPStateParam*>(funcparam);
×
2353
          try {
×
2354
            remote.sin4.sin_family = param->cs->local.sin4.sin_family;
×
2355
            packet.resize(initialBufferSize);
×
2356
            /* used by HarvestDestinationAddress */
2357
            cmsgbuf_aligned cbuf;
×
2358
            // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
2359
            fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), reinterpret_cast<char*>(&packet.at(0)), param->maxIncomingPacketSize, &remote);
×
2360
            handleOnePacket(*param);
×
2361
          }
×
2362
          catch (const std::bad_alloc& e) {
×
2363
            /* most exceptions are handled by handleOnePacket(), but we might be out of memory (std::bad_alloc)
2364
               in which case we DO NOT want to log (as it would trigger another memory allocation attempt
2365
               that might throw as well) but wait a bit (one millisecond) and then try to recover */
2366
            usleep(1000);
×
2367
          }
×
2368
        };
×
UNCOV
2369
        auto mplexer = std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent(params.size()));
×
UNCOV
2370
        for (const auto& param : params) {
×
2371
          mplexer->addReadFD(param.socket, callback, &param);
×
2372
        }
×
2373

UNCOV
2374
        timeval now{};
×
UNCOV
2375
        while (true) {
×
2376
          mplexer->run(&now, -1);
×
2377
        }
×
UNCOV
2378
      }
×
2379
    }
393✔
2380
  }
395✔
2381
  catch (const std::exception& e) {
395✔
2382
    errlog("UDP client thread died because of exception: %s", e.what());
×
2383
  }
×
2384
  catch (const PDNSException& e) {
395✔
2385
    errlog("UDP client thread died because of PowerDNS exception: %s", e.reason);
×
2386
  }
×
2387
  catch (...) {
395✔
2388
    errlog("UDP client thread died because of an exception: %s", "unknown");
×
2389
  }
×
2390
}
395✔
2391

2392
static void maintThread()
2393
{
394✔
2394
  setThreadName("dnsdist/main");
394✔
2395
  constexpr int interval = 1;
394✔
2396
  size_t counter = 0;
394✔
2397
  int32_t secondsToWaitLog = 0;
394✔
2398

2399
  for (;;) {
1,619✔
2400
    std::this_thread::sleep_for(std::chrono::seconds(interval));
1,619✔
2401

2402
    dnsdist::configuration::refreshLocalRuntimeConfiguration();
1,619✔
2403
    {
1,619✔
2404
      auto lua = g_lua.lock();
1,619✔
2405
      try {
1,619✔
2406
        auto maintenanceCallback = lua->readVariable<boost::optional<std::function<void()>>>("maintenance");
1,619✔
2407
        if (maintenanceCallback) {
1,619✔
2408
          (*maintenanceCallback)();
328✔
2409
        }
328✔
2410
        dnsdist::lua::hooks::runMaintenanceHooks(*lua);
1,619✔
2411
#if !defined(DISABLE_DYNBLOCKS)
1,619✔
2412
        dnsdist::DynamicBlocks::runRegisteredGroups(*lua);
1,619✔
2413
#endif /* DISABLE_DYNBLOCKS */
1,619✔
2414
        secondsToWaitLog = 0;
1,619✔
2415
      }
1,619✔
2416
      catch (const std::exception& e) {
1,619✔
2417
        if (secondsToWaitLog <= 0) {
×
2418
          warnlog("Error during execution of maintenance function(s): %s", e.what());
×
2419
          secondsToWaitLog = 61;
×
2420
        }
×
2421
        secondsToWaitLog -= interval;
×
2422
      }
×
2423
    }
1,619✔
2424

2425
    counter++;
1,619✔
2426
    if (counter >= dnsdist::configuration::getCurrentRuntimeConfiguration().d_cacheCleaningDelay) {
1,225✔
2427
      /* keep track, for each cache, of whether we should keep
2428
       expired entries */
2429
      std::map<std::shared_ptr<DNSDistPacketCache>, bool> caches;
12✔
2430

2431
      /* gather all caches actually used by at least one pool, and see
2432
         if something prevents us from cleaning the expired entries */
2433
      const auto& pools = dnsdist::configuration::getCurrentRuntimeConfiguration().d_pools;
12✔
2434
      for (const auto& entry : pools) {
12✔
2435
        const auto& pool = entry.second;
11✔
2436

2437
        const auto& packetCache = pool.packetCache;
11✔
2438
        if (!packetCache) {
11!
2439
          continue;
×
2440
        }
×
2441

2442
        auto pair = caches.insert({packetCache, false});
11✔
2443
        auto& iter = pair.first;
11✔
2444
        /* if we need to keep stale data for this cache (ie, not clear
2445
           expired entries when at least one pool using this cache
2446
           has all its backends down) */
2447
        if (packetCache->keepStaleData() && !iter->second) {
11!
2448
          /* so far all pools had at least one backend up */
2449
          if (pool.countServers(true) == 0) {
5!
2450
            iter->second = true;
5✔
2451
          }
5✔
2452
        }
5✔
2453
      }
11✔
2454

2455
      const time_t now = time(nullptr);
12✔
2456
      for (const auto& pair : caches) {
12✔
2457
        /* shall we keep expired entries ? */
2458
        if (pair.second) {
11✔
2459
          continue;
5✔
2460
        }
5✔
2461
        const auto& packetCache = pair.first;
6✔
2462
        size_t upTo = (packetCache->getMaxEntries() * (100 - dnsdist::configuration::getCurrentRuntimeConfiguration().d_cacheCleaningPercentage)) / 100;
6✔
2463
        packetCache->purgeExpired(upTo, now);
6✔
2464
      }
6✔
2465
      counter = 0;
12✔
2466
    }
12✔
2467
  }
1,225✔
2468
}
394✔
2469

2470
#ifndef DISABLE_DYNBLOCKS
2471
static void dynBlockMaintenanceThread()
2472
{
394✔
2473
  setThreadName("dnsdist/dynBloc");
394✔
2474

2475
  dnsdist::configuration::refreshLocalRuntimeConfiguration();
394✔
2476
  DynBlockMaintenance::run();
394✔
2477
}
394✔
2478
#endif
2479

2480
#ifndef DISABLE_SECPOLL
2481
static void secPollThread()
2482
{
×
2483
  setThreadName("dnsdist/secpoll");
×
2484

2485
  for (;;) {
×
2486
    const auto& runtimeConfig = dnsdist::configuration::refreshLocalRuntimeConfiguration();
×
2487

2488
    try {
×
2489
      dnsdist::secpoll::doSecPoll(runtimeConfig.d_secPollSuffix);
×
2490
    }
×
2491
    catch (...) {
×
2492
    }
×
2493
    // coverity[store_truncates_time_t]
2494
    std::this_thread::sleep_for(std::chrono::seconds(runtimeConfig.d_secPollInterval));
×
2495
  }
×
2496
}
×
2497
#endif /* DISABLE_SECPOLL */
2498

2499
static std::atomic<bool> s_exiting{false};
2500
void doExitNicely(int exitCode = EXIT_SUCCESS);
2501

2502
static void checkExiting()
2503
{
2,011✔
2504
  if (s_exiting) {
2,011✔
2505
    doExitNicely();
394✔
2506
  }
394✔
2507
}
2,011✔
2508

2509
static void healthChecksThread()
2510
{
394✔
2511
  setThreadName("dnsdist/healthC");
394✔
2512

2513
  constexpr int intervalUsec = 1000 * 1000;
394✔
2514
  struct timeval lastRound{
394✔
2515
    .tv_sec = 0,
394✔
2516
    .tv_usec = 0};
394✔
2517

2518
  for (;;) {
2,011✔
2519
    try {
2,011✔
2520
      checkExiting();
2,011✔
2521

2522
      timeval now{};
2,011✔
2523
      gettimeofday(&now, nullptr);
2,011✔
2524
      auto elapsedTimeUsec = uSec(now - lastRound);
2,011✔
2525
      if (elapsedTimeUsec < intervalUsec) {
2,011✔
2526
        usleep(intervalUsec - elapsedTimeUsec);
1,220✔
2527
        gettimeofday(&lastRound, nullptr);
1,220✔
2528
      }
1,220✔
2529
      else {
791✔
2530
        lastRound = now;
791✔
2531
      }
791✔
2532

2533
      std::unique_ptr<FDMultiplexer> mplexer{nullptr};
2,011✔
2534
      const auto& runtimeConfig = dnsdist::configuration::refreshLocalRuntimeConfiguration();
2,011✔
2535

2536
      // this points to the actual shared_ptrs!
2537
      // coverity[auto_causes_copy]
2538
      const auto servers = runtimeConfig.d_backends;
2,011✔
2539
      for (const auto& dss : servers) {
2,011✔
2540
        dss->updateStatisticsInfo();
1,997✔
2541

2542
        dss->handleUDPTimeouts();
1,997✔
2543

2544
        if (!dss->healthCheckRequired()) {
1,997✔
2545
          continue;
527✔
2546
        }
527✔
2547

2548
        if (!mplexer) {
1,470✔
2549
          mplexer = std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent(servers.size()));
1,411✔
2550
        }
1,411✔
2551

2552
        if (!queueHealthCheck(mplexer, dss)) {
1,470✔
2553
          dss->submitHealthCheckResult(false, false);
22✔
2554
        }
22✔
2555
      }
1,470✔
2556

2557
      if (mplexer) {
2,011✔
2558
        handleQueuedHealthChecks(*mplexer);
1,411✔
2559
      }
1,411✔
2560
    }
2,011✔
2561
    catch (const std::exception& exp) {
2,011✔
2562
      vinfolog("Exception in the health-check thread: %s", exp.what());
×
2563
    }
×
2564
  }
2,011✔
2565
}
394✔
2566

2567
static void bindAny([[maybe_unused]] int addressFamily, [[maybe_unused]] int sock)
2568
{
931✔
2569
  __attribute__((unused)) int one = 1;
931✔
2570

2571
#ifdef IP_FREEBIND
931✔
2572
  if (setsockopt(sock, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0) {
931!
2573
    warnlog("Warning: IP_FREEBIND setsockopt failed: %s", stringerror());
×
2574
  }
×
2575
#endif
931✔
2576

2577
#ifdef IP_BINDANY
2578
  if (addressFamily == AF_INET) {
2579
    if (setsockopt(sock, IPPROTO_IP, IP_BINDANY, &one, sizeof(one)) < 0) {
2580
      warnlog("Warning: IP_BINDANY setsockopt failed: %s", stringerror());
2581
    }
2582
  }
2583
#endif
2584
#ifdef IPV6_BINDANY
2585
  if (addressFamily == AF_INET6) {
2586
    if (setsockopt(sock, IPPROTO_IPV6, IPV6_BINDANY, &one, sizeof(one)) < 0) {
2587
      warnlog("Warning: IPV6_BINDANY setsockopt failed: %s", stringerror());
2588
    }
2589
  }
2590
#endif
2591
#ifdef SO_BINDANY
2592
  if (setsockopt(sock, SOL_SOCKET, SO_BINDANY, &one, sizeof(one)) < 0) {
2593
    warnlog("Warning: SO_BINDANY setsockopt failed: %s", stringerror());
2594
  }
2595
#endif
2596
}
931✔
2597

2598
static void dropGroupPrivs(gid_t gid)
2599
{
×
2600
  if (gid != 0) {
×
2601
    if (setgid(gid) == 0) {
×
2602
      if (setgroups(0, nullptr) < 0) {
×
2603
        warnlog("Warning: Unable to drop supplementary gids: %s", stringerror());
×
2604
      }
×
2605
    }
×
2606
    else {
×
2607
      warnlog("Warning: Unable to set group ID to %d: %s", gid, stringerror());
×
2608
    }
×
2609
  }
×
2610
}
×
2611

2612
static void dropUserPrivs(uid_t uid)
2613
{
×
2614
  if (uid != 0) {
×
2615
    if (setuid(uid) < 0) {
×
2616
      warnlog("Warning: Unable to set user ID to %d: %s", uid, stringerror());
×
2617
    }
×
2618
  }
×
2619
}
×
2620

2621
static void checkFileDescriptorsLimits(size_t udpBindsCount, size_t tcpBindsCount)
2622
{
394✔
2623
  const auto& immutableConfig = dnsdist::configuration::getImmutableConfiguration();
394✔
2624
  /* stdin, stdout, stderr */
2625
  rlim_t requiredFDsCount = 3;
394✔
2626
  const auto& backends = dnsdist::configuration::getCurrentRuntimeConfiguration().d_backends;
394✔
2627
  /* UDP sockets to backends */
2628
  size_t backendUDPSocketsCount = 0;
394✔
2629
  for (const auto& backend : backends) {
441✔
2630
    backendUDPSocketsCount += backend->sockets.size();
441✔
2631
  }
441✔
2632
  requiredFDsCount += backendUDPSocketsCount;
394✔
2633
  /* TCP sockets to backends */
2634
  if (immutableConfig.d_maxTCPClientThreads > 0) {
394!
2635
    requiredFDsCount += (backends.size() * immutableConfig.d_maxTCPClientThreads);
394✔
2636
  }
394✔
2637
  /* listening sockets */
2638
  requiredFDsCount += udpBindsCount;
394✔
2639
  requiredFDsCount += tcpBindsCount;
394✔
2640
  /* number of TCP connections currently served, assuming 1 connection per worker thread which is of course not right */
2641
  if (immutableConfig.d_maxTCPClientThreads > 0) {
394!
2642
    requiredFDsCount += immutableConfig.d_maxTCPClientThreads;
394✔
2643
    /* max pipes for communicating between TCP acceptors and client threads */
2644
    requiredFDsCount += (immutableConfig.d_maxTCPClientThreads * 2);
394✔
2645
  }
394✔
2646
  /* max TCP queued connections */
2647
  requiredFDsCount += immutableConfig.d_maxTCPQueuedConnections;
394✔
2648
  /* DelayPipe pipe */
2649
  requiredFDsCount += 2;
394✔
2650
  /* syslog socket */
2651
  requiredFDsCount++;
394✔
2652
  /* webserver main socket */
2653
  requiredFDsCount++;
394✔
2654
  /* console main socket */
2655
  requiredFDsCount++;
394✔
2656
  /* carbon export */
2657
  requiredFDsCount++;
394✔
2658
  /* history file */
2659
  requiredFDsCount++;
394✔
2660
  rlimit resourceLimits{};
394✔
2661
  getrlimit(RLIMIT_NOFILE, &resourceLimits);
394✔
2662
  if (resourceLimits.rlim_cur <= requiredFDsCount) {
394✔
2663
    warnlog("Warning, this configuration can use more than %d file descriptors, web server and console connections not included, and the current limit is %d.", std::to_string(requiredFDsCount), std::to_string(resourceLimits.rlim_cur));
2✔
2664
#ifdef HAVE_SYSTEMD
2✔
2665
    warnlog("You can increase this value by using LimitNOFILE= in the systemd unit file or ulimit.");
2✔
2666
#else
2667
    warnlog("You can increase this value by using ulimit.");
2668
#endif
2669
  }
2✔
2670
}
394✔
2671

2672
static void setupLocalSocket(ClientState& clientState, const ComboAddress& addr, int& socket, bool tcp, bool warn)
2673
{
931✔
2674
  const auto& immutableConfig = dnsdist::configuration::getImmutableConfiguration();
931✔
2675
  static bool s_warned_ipv6_recvpktinfo = false;
931✔
2676
  (void)warn;
931✔
2677
  socket = SSocket(addr.sin4.sin_family, !tcp ? SOCK_DGRAM : SOCK_STREAM, 0);
931✔
2678

2679
  if (tcp) {
931✔
2680
    SSetsockopt(socket, SOL_SOCKET, SO_REUSEADDR, 1);
499✔
2681
#ifdef TCP_DEFER_ACCEPT
499✔
2682
    SSetsockopt(socket, IPPROTO_TCP, TCP_DEFER_ACCEPT, 1);
499✔
2683
#endif
499✔
2684
    if (clientState.fastOpenQueueSize > 0) {
499!
2685
#ifdef TCP_FASTOPEN
×
2686
      SSetsockopt(socket, IPPROTO_TCP, TCP_FASTOPEN, clientState.fastOpenQueueSize);
×
2687
#ifdef TCP_FASTOPEN_KEY
×
2688
      if (!immutableConfig.d_tcpFastOpenKey.empty()) {
×
2689
        auto res = setsockopt(socket, IPPROTO_IP, TCP_FASTOPEN_KEY, immutableConfig.d_tcpFastOpenKey.data(), immutableConfig.d_tcpFastOpenKey.size() * sizeof(immutableConfig.d_tcpFastOpenKey[0]));
×
2690
        if (res == -1) {
×
2691
          throw runtime_error("setsockopt for level IPPROTO_TCP and opname TCP_FASTOPEN_KEY failed: " + stringerror());
×
2692
        }
×
2693
      }
×
2694
#endif /* TCP_FASTOPEN_KEY */
×
2695
#else /* TCP_FASTOPEN */
2696
      if (warn) {
2697
        warnlog("TCP Fast Open has been configured on local address '%s' but is not supported", addr.toStringWithPort());
2698
      }
2699
#endif /* TCP_FASTOPEN */
2700
    }
×
2701
  }
499✔
2702

2703
  if (addr.sin4.sin_family == AF_INET6) {
931✔
2704
    SSetsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, 1);
4✔
2705
  }
4✔
2706

2707
  bindAny(addr.sin4.sin_family, socket);
931✔
2708

2709
  if (!tcp && IsAnyAddress(addr)) {
931✔
2710
    int one = 1;
7✔
2711
    (void)setsockopt(socket, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)); // linux supports this, so why not - might fail on other systems
7✔
2712
#ifdef IPV6_RECVPKTINFO
7✔
2713
    if (addr.isIPv6() && setsockopt(socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)) < 0 && !s_warned_ipv6_recvpktinfo) {
7!
2714
      warnlog("Warning: IPV6_RECVPKTINFO setsockopt failed: %s", stringerror());
×
2715
      s_warned_ipv6_recvpktinfo = true;
×
2716
    }
×
2717
#endif
7✔
2718
  }
7✔
2719

2720
  if (clientState.reuseport) {
931✔
2721
    if (!setReusePort(socket)) {
18!
2722
      if (warn) {
×
2723
        /* no need to warn again if configured but support is not available, we already did for UDP */
2724
        warnlog("SO_REUSEPORT has been configured on local address '%s' but is not supported", addr.toStringWithPort());
×
2725
      }
×
2726
    }
×
2727
  }
18✔
2728

2729
  const bool isQUIC = clientState.doqFrontend != nullptr || clientState.doh3Frontend != nullptr;
931✔
2730
  if (isQUIC) {
931✔
2731
    /* disable fragmentation and force PMTU discovery for QUIC-enabled sockets */
2732
    try {
37✔
2733
      setSocketForcePMTU(socket, addr.sin4.sin_family);
37✔
2734
    }
37✔
2735
    catch (const std::exception& e) {
37✔
2736
      warnlog("Failed to set IP_MTU_DISCOVER on QUIC server socket for local address '%s': %s", addr.toStringWithPort(), e.what());
×
2737
    }
×
2738
  }
37✔
2739
  else if (!tcp && !clientState.dnscryptCtx) {
894✔
2740
    /* Only set this on IPv4 UDP sockets.
2741
       Don't set it for DNSCrypt binds. DNSCrypt pads queries for privacy
2742
       purposes, so we do receive large, sometimes fragmented datagrams. */
2743
    try {
390✔
2744
      setSocketIgnorePMTU(socket, addr.sin4.sin_family);
390✔
2745
    }
390✔
2746
    catch (const std::exception& e) {
390✔
2747
      warnlog("Failed to set IP_MTU_DISCOVER on UDP server socket for local address '%s': %s", addr.toStringWithPort(), e.what());
×
2748
    }
×
2749
  }
390✔
2750

2751
  if (!tcp) {
931✔
2752
    if (immutableConfig.d_socketUDPSendBuffer > 0) {
432!
2753
      try {
×
2754
        setSocketSendBuffer(socket, immutableConfig.d_socketUDPSendBuffer);
×
2755
      }
×
2756
      catch (const std::exception& e) {
×
2757
        warnlog(e.what());
×
2758
      }
×
2759
    }
×
2760
    else {
432✔
2761
      try {
432✔
2762
        auto result = raiseSocketSendBufferToMax(socket);
432✔
2763
        if (result > 0) {
432!
2764
          infolog("Raised send buffer to %u for local address '%s'", result, addr.toStringWithPort());
432✔
2765
        }
432✔
2766
      }
432✔
2767
      catch (const std::exception& e) {
432✔
2768
        warnlog(e.what());
×
2769
      }
×
2770
    }
432✔
2771

2772
    if (immutableConfig.d_socketUDPRecvBuffer > 0) {
432!
2773
      try {
×
2774
        setSocketReceiveBuffer(socket, immutableConfig.d_socketUDPRecvBuffer);
×
2775
      }
×
2776
      catch (const std::exception& e) {
×
2777
        warnlog(e.what());
×
2778
      }
×
2779
    }
×
2780
    else {
432✔
2781
      try {
432✔
2782
        auto result = raiseSocketReceiveBufferToMax(socket);
432✔
2783
        if (result > 0) {
432!
2784
          infolog("Raised receive buffer to %u for local address '%s'", result, addr.toStringWithPort());
432✔
2785
        }
432✔
2786
      }
432✔
2787
      catch (const std::exception& e) {
432✔
2788
        warnlog(e.what());
×
2789
      }
×
2790
    }
432✔
2791
  }
432✔
2792

2793
  const std::string& itf = clientState.interface;
931✔
2794
  if (!itf.empty()) {
931✔
2795
#ifdef SO_BINDTODEVICE
2✔
2796
    int res = setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, itf.c_str(), itf.length());
2✔
2797
    if (res != 0) {
2!
2798
      warnlog("Error setting up the interface on local address '%s': %s", addr.toStringWithPort(), stringerror());
×
2799
    }
×
2800
#else
2801
    if (warn) {
2802
      warnlog("An interface has been configured on local address '%s' but SO_BINDTODEVICE is not supported", addr.toStringWithPort());
2803
    }
2804
#endif
2805
  }
2✔
2806

2807
#ifdef HAVE_EBPF
931✔
2808
  /* for now eBPF filtering is not enabled on QUIC sockets because the eBPF code tries
2809
     to parse the QNAME from the payload for all UDP datagrams, which obviously does not
2810
     work well for these. */
2811
  if (!isQUIC && g_defaultBPFFilter && !g_defaultBPFFilter->isExternal()) {
931!
2812
    clientState.attachFilter(g_defaultBPFFilter, socket);
6✔
2813
    vinfolog("Attaching default BPF Filter to %s frontend %s", (!tcp ? std::string("UDP") : std::string("TCP")), addr.toStringWithPort());
6!
2814
  }
6✔
2815
#endif /* HAVE_EBPF */
931✔
2816

2817
  SBind(socket, addr);
931✔
2818

2819
  if (tcp) {
931✔
2820
    SListen(socket, clientState.tcpListenQueueSize);
499✔
2821

2822
    if (clientState.tlsFrontend != nullptr) {
499✔
2823
      infolog("Listening on %s for TLS", addr.toStringWithPort());
43✔
2824
    }
43✔
2825
    else if (clientState.dohFrontend != nullptr) {
456✔
2826
      infolog("Listening on %s for DoH", addr.toStringWithPort());
61✔
2827
    }
61✔
2828
    else if (clientState.dnscryptCtx != nullptr) {
395✔
2829
      infolog("Listening on %s for DNSCrypt", addr.toStringWithPort());
5✔
2830
    }
5✔
2831
    else {
390✔
2832
      infolog("Listening on %s", addr.toStringWithPort());
390✔
2833
    }
390✔
2834
  }
499✔
2835
  else {
432✔
2836
    if (clientState.doqFrontend != nullptr) {
432✔
2837
      infolog("Listening on %s for DoQ", addr.toStringWithPort());
20✔
2838
    }
20✔
2839
    else if (clientState.doh3Frontend != nullptr) {
412✔
2840
      infolog("Listening on %s for DoH3", addr.toStringWithPort());
17✔
2841
    }
17✔
2842
#ifdef HAVE_XSK
395✔
2843
    else if (clientState.xskInfo != nullptr) {
395!
2844
      infolog("Listening on %s (XSK-enabled)", addr.toStringWithPort());
×
2845
    }
×
2846
#endif
432✔
2847
  }
432✔
2848
}
931✔
2849

2850
static void setUpLocalBind(ClientState& cstate)
2851
{
931✔
2852
  /* skip some warnings if there is an identical UDP context */
2853
  bool warn = !cstate.tcp || cstate.tlsFrontend != nullptr || cstate.dohFrontend != nullptr;
931✔
2854
  int& descriptor = !cstate.tcp ? cstate.udpFD : cstate.tcpFD;
931✔
2855
  (void)warn;
931✔
2856

2857
  setupLocalSocket(cstate, cstate.local, descriptor, cstate.tcp, warn);
931✔
2858

2859
  for (auto& [addr, socket] : cstate.d_additionalAddresses) {
931!
2860
    setupLocalSocket(cstate, addr, socket, true, false);
×
2861
  }
×
2862

2863
  if (cstate.tlsFrontend != nullptr) {
931✔
2864
    if (!cstate.tlsFrontend->setupTLS()) {
43!
2865
      errlog("Error while setting up TLS on local address '%s', exiting", cstate.local.toStringWithPort());
×
2866
      _exit(EXIT_FAILURE);
×
2867
    }
×
2868
  }
43✔
2869

2870
  if (cstate.dohFrontend != nullptr) {
931✔
2871
    cstate.dohFrontend->setup();
61✔
2872
  }
61✔
2873
  if (cstate.doqFrontend != nullptr) {
931✔
2874
    cstate.doqFrontend->setup();
20✔
2875
  }
20✔
2876
  if (cstate.doh3Frontend != nullptr) {
931✔
2877
    cstate.doh3Frontend->setup();
17✔
2878
  }
17✔
2879

2880
  cstate.ready = true;
931✔
2881
}
931✔
2882

2883
struct CommandLineParameters
2884
{
2885
  vector<string> locals;
2886
  vector<string> remotes;
2887
  bool checkConfig{false};
2888
  bool beClient{false};
2889
  bool beSupervised{false};
2890
  string command;
2891
  string config;
2892
  string uid;
2893
  string gid;
2894
};
2895

2896
static void usage()
2897
{
×
2898
  cout << endl;
×
2899
  cout << "Syntax: dnsdist [-C,--config file] [-c,--client [IP[:PORT]]]\n";
×
2900
  cout << "[-e,--execute cmd] [-h,--help] [-l,--local addr]\n";
×
2901
  cout << "[-v,--verbose] [--check-config] [--version]\n";
×
2902
  cout << "\n";
×
2903
  cout << "-a,--acl netmask      Add this netmask to the ACL\n";
×
2904
  cout << "-C,--config file      Load configuration from 'file'\n";
×
2905
  cout << "-c,--client           Operate as a client, connect to dnsdist. This reads\n";
×
2906
  cout << "                      controlSocket from your configuration file, but also\n";
×
2907
  cout << "                      accepts an IP:PORT argument\n";
×
2908
#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO)
×
2909
  cout << "-k,--setkey KEY       Use KEY for encrypted communication to dnsdist. This\n";
×
2910
  cout << "                      is similar to setting setKey in the configuration file.\n";
×
2911
  cout << "                      NOTE: this will leak this key in your shell's history\n";
×
2912
  cout << "                      and in the systems running process list.\n";
×
2913
#endif
×
2914
  cout << "--check-config        Validate the configuration file and exit. The exit-code\n";
×
2915
  cout << "                      reflects the validation, 0 is OK, 1 means an error.\n";
×
2916
  cout << "                      Any errors are printed as well.\n";
×
2917
  cout << "-e,--execute cmd      Connect to dnsdist and execute 'cmd'\n";
×
2918
  cout << "-g,--gid gid          Change the process group ID after binding sockets\n";
×
2919
  cout << "-h,--help             Display this helpful message\n";
×
2920
  cout << "-l,--local address    Listen on this local address\n";
×
2921
  cout << "--supervised          Don't open a console, I'm supervised\n";
×
2922
  cout << "                        (use with e.g. systemd and daemontools)\n";
×
2923
  cout << "--disable-syslog      Don't log to syslog, only to stdout\n";
×
2924
  cout << "                        (use with e.g. systemd)\n";
×
2925
  cout << "--log-timestamps      Prepend timestamps to messages logged to stdout.\n";
×
2926
  cout << "-u,--uid uid          Change the process user ID after binding sockets\n";
×
2927
  cout << "-v,--verbose          Enable verbose mode\n";
×
2928
  cout << "-V,--version          Show dnsdist version information and exit\n";
×
2929
}
×
2930

2931
/* g++ defines __SANITIZE_THREAD__
2932
   clang++ supports the nice __has_feature(thread_sanitizer),
2933
   let's merge them */
2934
#if defined(__has_feature)
2935
#if __has_feature(thread_sanitizer)
2936
#define __SANITIZE_THREAD__ 1
2937
#endif
2938
#if __has_feature(address_sanitizer)
2939
#define __SANITIZE_ADDRESS__ 1
2940
#endif
2941
#endif
2942

2943
#if defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE)
2944
#include <sanitizer/lsan_interface.h>
2945
#endif
2946

2947
#if defined(COVERAGE) || (defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE))
2948
static void cleanupLuaObjects(LuaContext& /* luaCtx */)
2949
{
793✔
2950
  dnsdist::lua::hooks::clearExitCallbacks();
793✔
2951
  /* when our coverage mode is enabled, we need to make sure
2952
     that the Lua objects are destroyed before the Lua contexts. */
2953
  dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
793✔
2954
    config.d_ruleChains = dnsdist::rules::RuleChains();
793✔
2955
    config.d_lbPolicy = std::make_shared<ServerPolicy>();
793✔
2956
    config.d_pools.clear();
793✔
2957
    config.d_backends.clear();
793✔
2958
  });
793✔
2959
  dnsdist::webserver::clearWebHandlers();
793✔
2960
  dnsdist::lua::hooks::clearMaintenanceHooks();
793✔
2961
  dnsdist::lua::hooks::clearServerStateChangeCallbacks();
793✔
2962
}
793✔
2963
#endif /* defined(COVERAGE) || (defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE)) */
2964

2965
void doExitNicely(int exitCode)
2966
{
793✔
2967
  if (s_exiting) {
793✔
2968
    if (dnsdist::logging::LoggingConfiguration::getSyslog()) {
394!
2969
      syslog(LOG_INFO, "Exiting on user request");
394✔
2970
    }
394✔
2971
    std::cout << "Exiting on user request" << std::endl;
394✔
2972
  }
394✔
2973

2974
#ifdef HAVE_SYSTEMD
793✔
2975
  sd_notify(0, "STOPPING=1");
793✔
2976
#endif /* HAVE_SYSTEMD */
793✔
2977

2978
#if defined(COVERAGE) || (defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE))
793✔
2979
  if (dnsdist::g_asyncHolder) {
793✔
2980
    dnsdist::g_asyncHolder->stop();
394✔
2981
  }
394✔
2982

2983
  for (auto& backend : dnsdist::configuration::getCurrentRuntimeConfiguration().d_backends) {
886✔
2984
    backend->stop();
886✔
2985
  }
886✔
2986
#endif
793✔
2987

2988
  {
793✔
2989
    auto lock = g_lua.lock();
793✔
2990
    dnsdist::lua::hooks::runExitCallbacks(*lock);
793✔
2991
#if defined(COVERAGE) || (defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE))
793✔
2992
    cleanupLuaObjects(*lock);
793✔
2993
    *lock = LuaContext();
793✔
2994
#endif
793✔
2995
  }
793✔
2996

2997
#if defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE)
793✔
2998
  __lsan_do_leak_check();
793✔
2999
#endif /* __SANITIZE_ADDRESS__ && HAVE_LEAK_SANITIZER_INTERFACE */
793✔
3000

3001
#ifdef COVERAGE
793✔
3002
  pdns::coverage::dumpCoverageData();
793✔
3003
#endif
793✔
3004

3005
  /* do not call destructors, because we have some
3006
     dependencies between objects that are not trivial
3007
     to solve.
3008
  */
3009
  _exit(exitCode);
793✔
3010
}
793✔
3011

3012
static void sigTermHandler(int /* sig */)
3013
{
394✔
3014
  s_exiting.store(true);
394✔
3015
}
394✔
3016

3017
static void reportFeatures()
3018
{
×
3019
#ifdef LUAJIT_VERSION
×
3020
  cout << "dnsdist " << VERSION << " (" << LUA_RELEASE << " [" << LUAJIT_VERSION << "])" << endl;
×
3021
#else
3022
  cout << "dnsdist " << VERSION << " (" << LUA_RELEASE << ")" << endl;
3023
#endif
3024
  cout << "Enabled features: ";
×
3025
#ifdef HAVE_XSK
×
3026
  cout << "AF_XDP ";
×
3027
#endif
×
3028
#ifdef HAVE_CDB
×
3029
  cout << "cdb ";
×
3030
#endif
×
3031
#ifdef HAVE_DNS_OVER_QUIC
×
3032
  cout << "dns-over-quic ";
×
3033
#endif
×
3034
#ifdef HAVE_DNS_OVER_HTTP3
×
3035
  cout << "dns-over-http3 ";
×
3036
#endif
×
3037
#ifdef HAVE_DNS_OVER_TLS
×
3038
  cout << "dns-over-tls(";
×
3039
#ifdef HAVE_GNUTLS
×
3040
  cout << "gnutls";
×
3041
#ifdef HAVE_LIBSSL
×
3042
  cout << " ";
×
3043
#endif
×
3044
#endif /* HAVE_GNUTLS */
×
3045
#ifdef HAVE_LIBSSL
×
3046
  cout << "openssl";
×
3047
#endif
×
3048
  cout << ") ";
×
3049
#endif /* HAVE_DNS_OVER_TLS */
×
3050
#ifdef HAVE_DNS_OVER_HTTPS
×
3051
  cout << "dns-over-https(";
×
3052
#ifdef HAVE_LIBH2OEVLOOP
×
3053
  cout << "h2o";
×
3054
#endif /* HAVE_LIBH2OEVLOOP */
×
3055
#if defined(HAVE_LIBH2OEVLOOP) && defined(HAVE_NGHTTP2)
×
3056
  cout << " ";
×
3057
#endif /* defined(HAVE_LIBH2OEVLOOP) && defined(HAVE_NGHTTP2) */
×
3058
#ifdef HAVE_NGHTTP2
×
3059
  cout << "nghttp2";
×
3060
#endif /* HAVE_NGHTTP2 */
×
3061
  cout << ") ";
×
3062
#endif /* HAVE_DNS_OVER_HTTPS */
×
3063
#ifdef HAVE_DNSCRYPT
×
3064
  cout << "dnscrypt ";
×
3065
#endif
×
3066
#ifdef HAVE_EBPF
×
3067
  cout << "ebpf ";
×
3068
#endif
×
3069
#ifdef HAVE_FSTRM
×
3070
  cout << "fstrm ";
×
3071
#endif
×
3072
#ifdef HAVE_IPCIPHER
×
3073
  cout << "ipcipher ";
×
3074
#endif
×
3075
#ifdef HAVE_IPCRYPT2
×
3076
  cout << "ipcrypt2 ";
×
3077
#endif
×
3078
#ifdef HAVE_LIBEDIT
×
3079
  cout << "libedit ";
×
3080
#endif
×
3081
#ifdef HAVE_LIBSODIUM
×
3082
  cout << "libsodium ";
×
3083
#endif
×
3084
#ifdef HAVE_LMDB
×
3085
  cout << "lmdb ";
×
3086
#endif
×
3087
#ifndef DISABLE_PROTOBUF
×
3088
  cout << "protobuf ";
×
3089
#endif
×
3090
#ifdef HAVE_RE2
×
3091
  cout << "re2 ";
×
3092
#endif
×
3093
#ifndef DISABLE_RECVMMSG
×
3094
#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
×
3095
  cout << "recvmmsg/sendmmsg ";
×
3096
#endif
×
3097
#endif /* DISABLE_RECVMMSG */
×
3098
#ifdef HAVE_NET_SNMP
×
3099
  cout << "snmp ";
×
3100
#endif
×
3101
#ifdef HAVE_SYSTEMD
×
3102
  cout << "systemd ";
×
3103
#endif
×
3104
#ifdef HAVE_YAML_CONFIGURATION
×
3105
  cout << "yaml ";
×
3106
#endif
×
3107
  cout << endl;
×
3108
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
3109
#ifdef DNSDIST_CONFIG_ARGS
3110
#define double_escape(s) #s
3111
#define escape_quotes(s) double_escape(s)
3112
  // NOLINTEND(cppcoreguidelines-macro-usage)
3113
  cout << "Configured with: " << escape_quotes(DNSDIST_CONFIG_ARGS) << endl;
3114
#undef escape_quotes
3115
#undef double_escape
3116
#endif
3117
}
×
3118

3119
static void parseParameters(int argc, char** argv, CommandLineParameters& cmdLine, ComboAddress& clientAddress)
3120
{
800✔
3121
  const std::array<struct option, 16> longopts{{{"acl", required_argument, nullptr, 'a'},
800✔
3122
                                                {"check-config", no_argument, nullptr, 1},
800✔
3123
                                                {"client", no_argument, nullptr, 'c'},
800✔
3124
                                                {"config", required_argument, nullptr, 'C'},
800✔
3125
                                                {"disable-syslog", no_argument, nullptr, 2},
800✔
3126
                                                {"execute", required_argument, nullptr, 'e'},
800✔
3127
                                                {"gid", required_argument, nullptr, 'g'},
800✔
3128
                                                {"help", no_argument, nullptr, 'h'},
800✔
3129
                                                {"local", required_argument, nullptr, 'l'},
800✔
3130
                                                {"log-timestamps", no_argument, nullptr, 4},
800✔
3131
                                                {"setkey", required_argument, nullptr, 'k'},
800✔
3132
                                                {"supervised", no_argument, nullptr, 3},
800✔
3133
                                                {"uid", required_argument, nullptr, 'u'},
800✔
3134
                                                {"verbose", no_argument, nullptr, 'v'},
800✔
3135
                                                {"version", no_argument, nullptr, 'V'},
800✔
3136
                                                {nullptr, 0, nullptr, 0}}};
800✔
3137
  int longindex = 0;
800✔
3138
  string optstring;
800✔
3139
  dnsdist::configuration::RuntimeConfiguration newConfig;
800✔
3140

3141
  while (true) {
4,450✔
3142
    // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3143
    int gotChar = getopt_long(argc, argv, "a:cC:e:g:hk:l:u:vV", longopts.data(), &longindex);
4,450✔
3144
    if (gotChar == -1) {
4,450✔
3145
      break;
800✔
3146
    }
800✔
3147
    switch (gotChar) {
3,650!
3148
    case 1:
403✔
3149
      cmdLine.checkConfig = true;
403✔
3150
      break;
403✔
3151
    case 2:
×
3152
      dnsdist::logging::LoggingConfiguration::setSyslog(false);
×
3153
      break;
×
3154
    case 3:
788✔
3155
      cmdLine.beSupervised = true;
788✔
3156
      break;
788✔
3157
    case 4:
×
3158
      dnsdist::logging::LoggingConfiguration::setLogTimestamps(true);
×
3159
      break;
×
3160
    case 'C':
800✔
3161
      cmdLine.config = optarg;
800✔
3162
      break;
800✔
3163
    case 'c':
5✔
3164
      cmdLine.beClient = true;
5✔
3165
      break;
5✔
3166
    case 'e':
×
3167
      cmdLine.command = optarg;
×
3168
      break;
×
3169
    case 'g':
×
3170
      cmdLine.gid = optarg;
×
3171
      break;
×
3172
    case 'h':
×
3173
      cout << "dnsdist " << VERSION << endl;
×
3174
      usage();
×
3175
      cout << "\n";
×
3176
      // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3177
      exit(EXIT_SUCCESS);
×
3178
      break;
×
3179
    case 'a':
794✔
3180
      optstring = optarg;
794✔
3181
      newConfig.d_ACL.addMask(optstring);
794✔
3182
      break;
794✔
3183
    case 'k':
×
3184
#if defined HAVE_LIBSODIUM || defined(HAVE_LIBCRYPTO)
×
3185
    {
×
3186
      std::string consoleKey;
×
3187
      if (B64Decode(string(optarg), consoleKey) < 0) {
×
3188
        cerr << "Unable to decode key '" << optarg << "'." << endl;
×
3189
        // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3190
        exit(EXIT_FAILURE);
×
3191
      }
×
3192
      dnsdist::configuration::updateRuntimeConfiguration([&consoleKey](dnsdist::configuration::RuntimeConfiguration& config) {
×
3193
        config.d_consoleKey = std::move(consoleKey);
×
3194
      });
×
3195
    }
×
3196
#else
3197
      cerr << "dnsdist has been built without libsodium or libcrypto, -k/--setkey is unsupported." << endl;
3198
      // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3199
      exit(EXIT_FAILURE);
3200
#endif
3201
    break;
×
3202
    case 'l':
762✔
3203
      cmdLine.locals.push_back(boost::trim_copy(string(optarg)));
762✔
3204
      break;
762✔
3205
    case 'u':
×
3206
      cmdLine.uid = optarg;
×
3207
      break;
×
3208
    case 'v':
98✔
3209
      newConfig.d_verbose = true;
98✔
3210
      break;
98✔
3211
    case 'V':
×
3212
      reportFeatures();
×
3213
      // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3214
      exit(EXIT_SUCCESS);
×
3215
      break;
×
3216
    case '?':
×
3217
      // getopt_long printed an error message.
3218
      usage();
×
3219
      // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3220
      exit(EXIT_FAILURE);
×
3221
      break;
×
3222
    }
3,650✔
3223
  }
3,650✔
3224

3225
  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic): argv
3226
  argv += optind;
800✔
3227

3228
  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic): argv
3229
  for (const auto* ptr = argv; *ptr != nullptr; ++ptr) {
800!
3230
    if (cmdLine.beClient) {
×
3231
      clientAddress = ComboAddress(*ptr, 5199);
×
3232
    }
×
3233
    else {
×
3234
      cmdLine.remotes.emplace_back(*ptr);
×
3235
    }
×
3236
  }
×
3237

3238
  dnsdist::configuration::updateRuntimeConfiguration([&newConfig](dnsdist::configuration::RuntimeConfiguration& config) {
800✔
3239
    config = std::move(newConfig);
800✔
3240
  });
800✔
3241
}
800✔
3242
static void setupPools()
3243
{
394✔
3244
  bool precompute = false;
394✔
3245
  const auto& currentConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
394✔
3246
  if (currentConfig.d_lbPolicy->getName() == "chashed") {
394✔
3247
    precompute = true;
1✔
3248
  }
1✔
3249
  else {
393✔
3250
    for (const auto& entry : currentConfig.d_pools) {
439✔
3251
      if (entry.second.policy != nullptr && entry.second.policy->getName() == "chashed") {
439!
3252
        precompute = true;
×
3253
        break;
×
3254
      }
×
3255
    }
439✔
3256
  }
393✔
3257
  if (precompute) {
394✔
3258
    vinfolog("Pre-computing hashes for consistent hash load-balancing policy");
1!
3259
    // pre compute hashes
3260
    for (const auto& backend : currentConfig.d_backends) {
2✔
3261
      if (backend->d_config.d_weight < 100) {
2!
3262
        vinfolog("Warning, the backend '%s' has a very low weight (%d), which will not yield a good distribution of queries with the 'chashed' policy. Please consider raising it to at least '100'.", backend->getName(), backend->d_config.d_weight);
×
3263
      }
×
3264

3265
      backend->hash();
2✔
3266
    }
2✔
3267
  }
1✔
3268
}
394✔
3269

3270
static void dropPrivileges(const CommandLineParameters& cmdLine)
3271
{
394✔
3272
  uid_t newgid = getegid();
394✔
3273
  gid_t newuid = geteuid();
394✔
3274

3275
  if (!cmdLine.gid.empty()) {
394!
3276
    newgid = strToGID(cmdLine.gid);
×
3277
  }
×
3278

3279
  if (!cmdLine.uid.empty()) {
394!
3280
    newuid = strToUID(cmdLine.uid);
×
3281
  }
×
3282

3283
  bool retainedCapabilities = true;
394✔
3284
  if (!dnsdist::configuration::getImmutableConfiguration().d_capabilitiesToRetain.empty() && (getegid() != newgid || geteuid() != newuid)) {
394!
3285
    retainedCapabilities = keepCapabilitiesAfterSwitchingIDs();
×
3286
  }
×
3287

3288
  if (getegid() != newgid) {
394!
3289
    if (running_in_service_mgr()) {
×
3290
      errlog("--gid/-g set on command-line, but dnsdist was started as a systemd service. Use the 'Group' setting in the systemd unit file to set the group to run as");
×
3291
      _exit(EXIT_FAILURE);
×
3292
    }
×
3293
    dropGroupPrivs(newgid);
×
3294
  }
×
3295

3296
  if (geteuid() != newuid) {
394!
3297
    if (running_in_service_mgr()) {
×
3298
      errlog("--uid/-u set on command-line, but dnsdist was started as a systemd service. Use the 'User' setting in the systemd unit file to set the user to run as");
×
3299
      _exit(EXIT_FAILURE);
×
3300
    }
×
3301
    dropUserPrivs(newuid);
×
3302
  }
×
3303

3304
  if (retainedCapabilities) {
394!
3305
    dropCapabilitiesAfterSwitchingIDs();
394✔
3306
  }
394✔
3307

3308
  try {
394✔
3309
    /* we might still have capabilities remaining,
3310
       for example if we have been started as root
3311
       without --uid or --gid (please don't do that)
3312
       or as an unprivileged user with ambient
3313
       capabilities like CAP_NET_BIND_SERVICE.
3314
    */
3315
    dropCapabilities(dnsdist::configuration::getImmutableConfiguration().d_capabilitiesToRetain);
394✔
3316
  }
394✔
3317
  catch (const std::exception& e) {
394✔
3318
    warnlog("%s", e.what());
×
3319
  }
×
3320
}
394✔
3321

3322
static void initFrontends(const CommandLineParameters& cmdLine)
3323
{
394✔
3324
  auto frontends = dnsdist::configuration::getImmutableConfiguration().d_frontends;
394✔
3325

3326
  if (!cmdLine.locals.empty()) {
394✔
3327
    for (auto it = frontends.begin(); it != frontends.end();) {
553✔
3328
      /* DoH, DoT and DNSCrypt frontends are separate */
3329
      if ((*it)->dohFrontend == nullptr && (*it)->tlsFrontend == nullptr && (*it)->dnscryptCtx == nullptr && (*it)->doqFrontend == nullptr && (*it)->doh3Frontend == nullptr) {
172✔
3330
        it = frontends.erase(it);
28✔
3331
      }
28✔
3332
      else {
144✔
3333
        ++it;
144✔
3334
      }
144✔
3335
    }
172✔
3336

3337
    for (const auto& loc : cmdLine.locals) {
381✔
3338
      /* UDP */
3339
      frontends.emplace_back(std::make_unique<ClientState>(ComboAddress(loc, 53), false, false, 0, "", std::set<int>{}, true));
381✔
3340
      /* TCP */
3341
      frontends.emplace_back(std::make_unique<ClientState>(ComboAddress(loc, 53), true, false, 0, "", std::set<int>{}, true));
381✔
3342
    }
381✔
3343
  }
381✔
3344

3345
  if (frontends.empty()) {
394!
3346
    /* UDP */
3347
    frontends.emplace_back(std::make_unique<ClientState>(ComboAddress("127.0.0.1", 53), false, false, 0, "", std::set<int>{}, true));
×
3348
    /* TCP */
3349
    frontends.emplace_back(std::make_unique<ClientState>(ComboAddress("127.0.0.1", 53), true, false, 0, "", std::set<int>{}, true));
×
3350
  }
×
3351

3352
  dnsdist::configuration::updateImmutableConfiguration([&frontends](dnsdist::configuration::ImmutableConfiguration& config) {
394✔
3353
    config.d_frontends = std::move(frontends);
394✔
3354
  });
394✔
3355
}
394✔
3356

3357
namespace dnsdist
3358
{
3359
static void startFrontends()
3360
{
394✔
3361
#ifdef HAVE_XSK
394✔
3362
  for (auto& xskContext : dnsdist::xsk::g_xsk) {
394!
3363
    std::thread xskThread(dnsdist::xsk::XskRouter, std::move(xskContext));
×
3364
    xskThread.detach();
×
3365
  }
×
3366
#endif /* HAVE_XSK */
394✔
3367

3368
  std::vector<ClientState*> tcpStates;
394✔
3369
  std::vector<ClientState*> udpStates;
394✔
3370
  for (const auto& clientState : dnsdist::getFrontends()) {
931✔
3371
#ifdef HAVE_XSK
931✔
3372
    if (clientState->xskInfo) {
931!
3373
      dnsdist::xsk::addDestinationAddress(clientState->local);
×
3374

3375
      std::thread xskCT(dnsdist::xsk::XskClientThread, clientState.get());
×
3376
      if (!clientState->cpus.empty()) {
×
3377
        mapThreadToCPUList(xskCT.native_handle(), clientState->cpus);
×
3378
      }
×
3379
      xskCT.detach();
×
3380
    }
×
3381
#endif /* HAVE_XSK */
931✔
3382

3383
    if (clientState->dohFrontend != nullptr && clientState->dohFrontend->d_library == "h2o") {
931✔
3384
#ifdef HAVE_DNS_OVER_HTTPS
23✔
3385
#ifdef HAVE_LIBH2OEVLOOP
23✔
3386
      std::thread dohThreadHandle(dohThread, clientState.get());
23✔
3387
      if (!clientState->cpus.empty()) {
23!
3388
        mapThreadToCPUList(dohThreadHandle.native_handle(), clientState->cpus);
×
3389
      }
×
3390
      dohThreadHandle.detach();
23✔
3391
#endif /* HAVE_LIBH2OEVLOOP */
23✔
3392
#endif /* HAVE_DNS_OVER_HTTPS */
23✔
3393
      continue;
23✔
3394
    }
23✔
3395
    if (clientState->doqFrontend != nullptr) {
908✔
3396
#ifdef HAVE_DNS_OVER_QUIC
20✔
3397
      std::thread doqThreadHandle(doqThread, clientState.get());
20✔
3398
      if (!clientState->cpus.empty()) {
20!
3399
        mapThreadToCPUList(doqThreadHandle.native_handle(), clientState->cpus);
×
3400
      }
×
3401
      doqThreadHandle.detach();
20✔
3402
#endif /* HAVE_DNS_OVER_QUIC */
20✔
3403
      continue;
20✔
3404
    }
20✔
3405
    if (clientState->doh3Frontend != nullptr) {
888✔
3406
#ifdef HAVE_DNS_OVER_HTTP3
17✔
3407
      std::thread doh3ThreadHandle(doh3Thread, clientState.get());
17✔
3408
      if (!clientState->cpus.empty()) {
17!
3409
        mapThreadToCPUList(doh3ThreadHandle.native_handle(), clientState->cpus);
×
3410
      }
×
3411
      doh3ThreadHandle.detach();
17✔
3412
#endif /* HAVE_DNS_OVER_HTTP3 */
17✔
3413
      continue;
17✔
3414
    }
17✔
3415
    if (clientState->udpFD >= 0) {
871✔
3416
#ifdef USE_SINGLE_ACCEPTOR_THREAD
3417
      udpStates.push_back(clientState.get());
3418
#else /* USE_SINGLE_ACCEPTOR_THREAD */
3419
      std::thread udpClientThreadHandle(udpClientThread, std::vector<ClientState*>{clientState.get()});
395✔
3420
      if (!clientState->cpus.empty()) {
395!
3421
        mapThreadToCPUList(udpClientThreadHandle.native_handle(), clientState->cpus);
×
3422
      }
×
3423
      udpClientThreadHandle.detach();
395✔
3424
#endif /* USE_SINGLE_ACCEPTOR_THREAD */
395✔
3425
    }
395✔
3426
    else if (clientState->tcpFD >= 0) {
476!
3427
#ifdef USE_SINGLE_ACCEPTOR_THREAD
3428
      tcpStates.push_back(clientState.get());
3429
#else /* USE_SINGLE_ACCEPTOR_THREAD */
3430
      std::thread tcpAcceptorThreadHandle(tcpAcceptorThread, std::vector<ClientState*>{clientState.get()});
476✔
3431
      if (!clientState->cpus.empty()) {
476!
3432
        mapThreadToCPUList(tcpAcceptorThreadHandle.native_handle(), clientState->cpus);
×
3433
      }
×
3434
      tcpAcceptorThreadHandle.detach();
476✔
3435
#endif /* USE_SINGLE_ACCEPTOR_THREAD */
476✔
3436
    }
476✔
3437
  }
871✔
3438
#ifdef USE_SINGLE_ACCEPTOR_THREAD
3439
  if (!udpStates.empty()) {
3440
    std::thread udpThreadHandle(udpClientThread, udpStates);
3441
    udpThreadHandle.detach();
3442
  }
3443
  if (!tcpStates.empty()) {
3444
    g_tcpclientthreads = std::make_unique<TCPClientCollection>(1, tcpStates);
3445
  }
3446
#endif /* USE_SINGLE_ACCEPTOR_THREAD */
3447
}
394✔
3448
}
3449

3450
struct ListeningSockets
3451
{
3452
  Socket d_consoleSocket{-1};
3453
  std::vector<std::pair<ComboAddress, Socket>> d_webServerSockets;
3454
};
3455

3456
static ListeningSockets initListeningSockets()
3457
{
394✔
3458
  ListeningSockets result;
394✔
3459
  const auto& currentConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
394✔
3460

3461
  if (currentConfig.d_consoleEnabled) {
394✔
3462
    const auto& local = currentConfig.d_consoleServerAddress;
75✔
3463
    try {
75✔
3464
      result.d_consoleSocket = Socket(local.sin4.sin_family, SOCK_STREAM, 0);
75✔
3465
      result.d_consoleSocket.bind(local, true);
75✔
3466
      result.d_consoleSocket.listen(5);
75✔
3467
    }
75✔
3468
    catch (const std::exception& exp) {
75✔
3469
      errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), exp.what());
×
3470
    }
×
3471
  }
75✔
3472

3473
  for (const auto& local : currentConfig.d_webServerAddresses) {
394✔
3474
    try {
54✔
3475
      auto webServerSocket = Socket(local.sin4.sin_family, SOCK_STREAM, 0);
54✔
3476
      webServerSocket.bind(local, true);
54✔
3477
      webServerSocket.listen(5);
54✔
3478
      result.d_webServerSockets.emplace_back(local, std::move(webServerSocket));
54✔
3479
    }
54✔
3480
    catch (const std::exception& exp) {
54✔
3481
      errlog("Unable to bind to web server socket on %s: %s", local.toStringWithPort(), exp.what());
×
3482
    }
×
3483
  }
54✔
3484

3485
  return result;
394✔
3486
}
394✔
3487

3488
static std::optional<std::string> lookForTentativeConfigurationFileWithExtension(const std::string& configurationFile, const std::string& extension)
3489
{
60✔
3490
  auto dotPos = configurationFile.rfind('.');
60✔
3491
  if (dotPos == std::string::npos) {
60!
3492
    return std::nullopt;
×
3493
  }
×
3494
  auto tentativeFile = configurationFile.substr(0, dotPos + 1) + extension;
60✔
3495
  if (!std::filesystem::exists(tentativeFile)) {
60✔
3496
    return std::nullopt;
54✔
3497
  }
54✔
3498
  return tentativeFile;
6✔
3499
}
60✔
3500

3501
static bool loadConfigurationFromFile(const std::string& configurationFile, bool isClient, bool configCheck)
3502
{
800✔
3503
  if (boost::ends_with(configurationFile, ".yml")) {
800✔
3504
    // the bindings are always needed, for example for inline Lua
3505
    dnsdist::lua::setupLuaBindingsOnly(*(g_lua.lock()), isClient, configCheck);
60✔
3506

3507
    if (auto tentativeLuaConfFile = lookForTentativeConfigurationFileWithExtension(configurationFile, "lua")) {
60✔
3508
      vinfolog("Loading configuration from auto-discovered Lua file %s", *tentativeLuaConfFile);
6✔
3509
      dnsdist::configuration::lua::loadLuaConfigurationFile(*(g_lua.lock()), *tentativeLuaConfFile, configCheck);
6✔
3510
    }
6✔
3511
    vinfolog("Loading configuration from YAML file %s", configurationFile);
60✔
3512
    if (!dnsdist::configuration::yaml::loadConfigurationFromFile(configurationFile, isClient, configCheck)) {
60!
3513
      return false;
×
3514
    }
×
3515
    if (!isClient && !configCheck) {
60!
3516
      dnsdist::lua::setupLuaConfigurationOptions(*(g_lua.lock()), false, false);
30✔
3517
    }
30✔
3518
    return true;
60✔
3519
  }
60✔
3520

3521
  dnsdist::lua::setupLua(*(g_lua.lock()), isClient, configCheck);
740✔
3522
  if (boost::ends_with(configurationFile, ".lua")) {
740!
3523
    vinfolog("Loading configuration from Lua file %s", configurationFile);
×
3524
    dnsdist::configuration::lua::loadLuaConfigurationFile(*(g_lua.lock()), configurationFile, configCheck);
×
3525
    if (auto tentativeYamlConfFile = lookForTentativeConfigurationFileWithExtension(configurationFile, "yml")) {
×
3526
      vinfolog("Loading configuration from auto-discovered YAML file %s", *tentativeYamlConfFile);
×
3527
      return dnsdist::configuration::yaml::loadConfigurationFromFile(*tentativeYamlConfFile, isClient, configCheck);
×
3528
    }
×
3529
  }
×
3530
  else {
740✔
3531
    vinfolog("Loading configuration from Lua file %s", configurationFile);
740✔
3532
    dnsdist::configuration::lua::loadLuaConfigurationFile(*(g_lua.lock()), configurationFile, configCheck);
740✔
3533
  }
740✔
3534
  return true;
740✔
3535
}
740✔
3536

3537
int main(int argc, char** argv)
3538
{
800✔
3539
  try {
800✔
3540
    CommandLineParameters cmdLine{};
800✔
3541
    size_t udpBindsCount = 0;
800✔
3542
    size_t tcpBindsCount = 0;
800✔
3543

3544
    dnsdist::console::completion::setupCompletion();
800✔
3545

3546
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast): SIG_IGN macro
3547
    signal(SIGPIPE, SIG_IGN);
800✔
3548
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast): SIG_IGN macro
3549
    signal(SIGCHLD, SIG_IGN);
800✔
3550
    signal(SIGTERM, sigTermHandler);
800✔
3551

3552
    openlog("dnsdist", LOG_PID | LOG_NDELAY, LOG_DAEMON);
800✔
3553

3554
#ifdef HAVE_LIBSODIUM
800✔
3555
    if (sodium_init() == -1) {
800!
3556
      cerr << "Unable to initialize crypto library" << endl;
×
3557
      // NOLINTNEXTLINE(concurrency-mt-unsafe): only on thread at this point
3558
      exit(EXIT_FAILURE);
×
3559
    }
×
3560
#endif
800✔
3561
    dnsdist::initRandom();
800✔
3562

3563
#ifdef HAVE_XSK
800✔
3564
    try {
800✔
3565
      dnsdist::xsk::clearDestinationAddresses();
800✔
3566
    }
800✔
3567
    catch (const std::exception& exp) {
800✔
3568
      /* silently handle failures: at this point we don't even know if XSK is enabled,
3569
         and we might not have the correct map (not the default one). */
3570
    }
800✔
3571
#endif /* HAVE_XSK */
800✔
3572

3573
    ComboAddress clientAddress = ComboAddress();
800✔
3574
    cmdLine.config = SYSCONFDIR "/dnsdist.conf";
800✔
3575

3576
    parseParameters(argc, argv, cmdLine, clientAddress);
800✔
3577

3578
    dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
800✔
3579
      config.d_lbPolicy = std::make_shared<ServerPolicy>("leastOutstanding", leastOutstanding, false);
800✔
3580
    });
800✔
3581

3582
    if (cmdLine.beClient || !cmdLine.command.empty()) {
800!
3583
      if (!loadConfigurationFromFile(cmdLine.config, true, false)) {
5!
3584
#ifdef COVERAGE
×
3585
        exit(EXIT_FAILURE);
×
3586
#else
3587
        _exit(EXIT_FAILURE);
3588
#endif
3589
      }
×
3590
      if (clientAddress != ComboAddress()) {
5!
3591
        dnsdist::configuration::updateRuntimeConfiguration([&clientAddress](dnsdist::configuration::RuntimeConfiguration& config) {
×
3592
          config.d_consoleServerAddress = clientAddress;
×
3593
        });
×
3594
      }
×
3595
      dnsdist::console::doClient(cmdLine.command);
5✔
3596
#ifdef COVERAGE
5✔
3597
      exit(EXIT_SUCCESS);
5✔
3598
#else
3599
      _exit(EXIT_SUCCESS);
3600
#endif
3601
    }
5✔
3602

3603
    dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
795✔
3604
      auto& acl = config.d_ACL;
795✔
3605
      if (acl.empty()) {
795✔
3606
        for (const auto& addr : {"127.0.0.0/8", "10.0.0.0/8", "100.64.0.0/10", "169.254.0.0/16", "192.168.0.0/16", "172.16.0.0/12", "::1/128", "fc00::/7", "fe80::/10"}) {
63✔
3607
          acl.addMask(addr);
63✔
3608
        }
63✔
3609
      }
7✔
3610
      for (const auto& mask : {"127.0.0.1/8", "::1/128"}) {
1,590✔
3611
        config.d_consoleACL.addMask(mask);
1,590✔
3612
      }
1,590✔
3613
      config.d_webServerACL.toMasks("127.0.0.1, ::1");
795✔
3614
    });
795✔
3615

3616
    dnsdist::webserver::registerBuiltInWebHandlers();
795✔
3617

3618
    if (cmdLine.checkConfig) {
795✔
3619
      if (!loadConfigurationFromFile(cmdLine.config, false, true)) {
401!
3620
#ifdef COVERAGE
×
3621
        exit(EXIT_FAILURE);
×
3622
#else
3623
        _exit(EXIT_FAILURE);
3624
#endif
3625
      }
×
3626
      // No exception was thrown
3627
      infolog("Configuration '%s' OK!", cmdLine.config);
401✔
3628
      doExitNicely();
401✔
3629
    }
401✔
3630

3631
    infolog("dnsdist %s comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it according to the terms of the GPL version 2", VERSION);
795✔
3632

3633
    dnsdist::g_asyncHolder = std::make_unique<dnsdist::AsynchronousHolder>();
795✔
3634

3635
    /* create the default pool no matter what */
3636
    createPoolIfNotExists("");
795✔
3637

3638
    if (!loadConfigurationFromFile(cmdLine.config, false, false)) {
795!
3639
#ifdef COVERAGE
×
3640
      exit(EXIT_FAILURE);
×
3641
#else
3642
      _exit(EXIT_FAILURE);
3643
#endif
3644
    }
×
3645

3646
    // we only want to update this value if it has not been set by either the Lua or YAML configuration,
3647
    // and we need to stop touching this value once the backends' hashes have been computed, in setupPools()
3648
    dnsdist::configuration::updateImmutableConfiguration([](dnsdist::configuration::ImmutableConfiguration& config) {
795✔
3649
      if (config.d_hashPerturbation == 0) {
394!
3650
        config.d_hashPerturbation = dnsdist::getRandomValue(0xffffffff);
394✔
3651
      }
394✔
3652
    });
394✔
3653

3654
    setupPools();
795✔
3655

3656
    initFrontends(cmdLine);
795✔
3657

3658
    for (const auto& frontend : dnsdist::getFrontends()) {
931✔
3659
      if (!frontend->tcp) {
931✔
3660
        ++udpBindsCount;
432✔
3661
      }
432✔
3662
      else {
499✔
3663
        ++tcpBindsCount;
499✔
3664
      }
499✔
3665
    }
931✔
3666

3667
    dnsdist::configuration::setImmutableConfigurationDone();
795✔
3668

3669
    {
795✔
3670
      const auto& immutableConfig = dnsdist::configuration::getImmutableConfiguration();
795✔
3671
      setTCPDownstreamMaxIdleConnectionsPerBackend(immutableConfig.d_outgoingTCPMaxIdlePerBackend);
795✔
3672
      setTCPDownstreamMaxIdleTime(immutableConfig.d_outgoingTCPMaxIdleTime);
795✔
3673
      setTCPDownstreamCleanupInterval(immutableConfig.d_outgoingTCPCleanupInterval);
795✔
3674
#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
795✔
3675
      setDoHDownstreamMaxIdleConnectionsPerBackend(immutableConfig.d_outgoingDoHMaxIdlePerBackend);
795✔
3676
      setDoHDownstreamMaxIdleTime(immutableConfig.d_outgoingDoHMaxIdleTime);
795✔
3677
      setDoHDownstreamCleanupInterval(immutableConfig.d_outgoingDoHCleanupInterval);
795✔
3678
#endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
795✔
3679
    }
795✔
3680

3681
    {
795✔
3682
      const auto& config = dnsdist::configuration::getImmutableConfiguration();
795✔
3683
      g_rings.init(config.d_ringsCapacity, config.d_ringsNumberOfShards, config.d_ringsNbLockTries, config.d_ringsRecordQueries, config.d_ringsRecordResponses);
795✔
3684
    }
795✔
3685

3686
    for (const auto& frontend : dnsdist::getFrontends()) {
931✔
3687
      setUpLocalBind(*frontend);
931✔
3688
    }
931✔
3689

3690
    {
795✔
3691
      std::string acls;
795✔
3692
      auto aclEntries = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ACL.toStringVector();
795✔
3693
      for (const auto& aclEntry : aclEntries) {
795✔
3694
        if (!acls.empty()) {
641✔
3695
          acls += ", ";
247✔
3696
        }
247✔
3697
        acls += aclEntry;
641✔
3698
      }
641✔
3699
      infolog("ACL allowing queries from: %s", acls);
795✔
3700
    }
795✔
3701
    {
795✔
3702
      std::string acls;
795✔
3703
      auto aclEntries = dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleACL.toStringVector();
795✔
3704
      for (const auto& entry : aclEntries) {
795✔
3705
        if (!acls.empty()) {
779✔
3706
          acls += ", ";
385✔
3707
        }
385✔
3708
        acls += entry;
779✔
3709
      }
779✔
3710
      infolog("Console ACL allowing connections from: %s", acls.c_str());
795✔
3711
    }
795✔
3712

3713
    auto listeningSockets = initListeningSockets();
795✔
3714

3715
#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO)
795✔
3716
    if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleEnabled && dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleKey.empty()) {
795✔
3717
      warnlog("Warning, the console has been enabled via 'controlSocket()' but no key has been set with 'setKey()' so all connections will fail until a key has been set");
1✔
3718
    }
1✔
3719
#endif
795✔
3720

3721
    dropPrivileges(cmdLine);
795✔
3722

3723
    /* this need to be done _after_ dropping privileges */
3724
#ifndef DISABLE_DELAY_PIPE
795✔
3725
    g_delay = std::make_unique<DelayPipe<DelayedPacket>>();
795✔
3726
#endif /* DISABLE_DELAY_PIPE */
795✔
3727

3728
#if defined(HAVE_NET_SNMP)
795✔
3729
    if (dnsdist::configuration::getImmutableConfiguration().d_snmpEnabled) {
795✔
3730
      g_snmpAgent = std::make_unique<DNSDistSNMPAgent>("dnsdist", dnsdist::configuration::getImmutableConfiguration().d_snmpDaemonSocketPath);
1✔
3731
      g_snmpAgent->run();
1✔
3732
    }
1✔
3733
#endif /* HAVE_NET_SNMP */
795✔
3734

3735
    /* we need to create the TCP worker threads before the
3736
       acceptor ones, otherwise we might crash when processing
3737
       the first TCP query */
3738
#ifndef USE_SINGLE_ACCEPTOR_THREAD
795✔
3739
    const auto maxTCPClientThreads = dnsdist::configuration::getImmutableConfiguration().d_maxTCPClientThreads;
795✔
3740
    /* the limit is completely arbitrary: hopefully high enough not to trigger too many false positives
3741
       but low enough to be useful */
3742
    if (maxTCPClientThreads >= 50U) {
795!
3743
      warnlog("setMaxTCPClientThreads(%d) might create a large number of TCP connections to backends, and is probably not needed, please consider lowering it", maxTCPClientThreads);
×
3744
    }
×
3745
    g_tcpclientthreads = std::make_unique<TCPClientCollection>(maxTCPClientThreads, std::vector<ClientState*>());
795✔
3746
#endif
795✔
3747

3748
#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
795✔
3749
    initDoHWorkers();
795✔
3750
#endif
795✔
3751

3752
    if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleEnabled) {
795✔
3753
      std::thread consoleControlThread(dnsdist::console::controlThread, std::move(listeningSockets.d_consoleSocket));
75✔
3754
      consoleControlThread.detach();
75✔
3755
    }
75✔
3756
    for (auto& [listeningAddress, socket] : listeningSockets.d_webServerSockets) {
795✔
3757
      std::thread webServerThread(dnsdist::webserver::WebserverThread, listeningAddress, std::move(socket));
54✔
3758
      webServerThread.detach();
54✔
3759
    }
54✔
3760

3761
    for (const auto& backend : dnsdist::configuration::getCurrentRuntimeConfiguration().d_backends) {
795✔
3762
      if (backend->connected) {
441✔
3763
        backend->start();
406✔
3764
      }
406✔
3765
    }
441✔
3766

3767
    if (!cmdLine.remotes.empty()) {
795!
3768
      for (const auto& address : cmdLine.remotes) {
×
3769
        DownstreamState::Config config;
×
3770
        config.remote = ComboAddress(address, 53);
×
3771
        auto ret = std::make_shared<DownstreamState>(std::move(config), nullptr, true);
×
3772
        addServerToPool("", ret);
×
3773
        ret->start();
×
3774
        dnsdist::configuration::updateRuntimeConfiguration([&ret](dnsdist::configuration::RuntimeConfiguration& runtimeConfig) {
×
3775
          runtimeConfig.d_backends.push_back(std::move(ret));
×
3776
        });
×
3777
      }
×
3778
    }
×
3779

3780
    if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_backends.empty()) {
795✔
3781
      errlog("No downstream servers defined: all packets will get dropped");
6✔
3782
      // you might define them later, but you need to know
3783
    }
6✔
3784

3785
    checkFileDescriptorsLimits(udpBindsCount, tcpBindsCount);
795✔
3786

3787
    {
795✔
3788
      // coverity[auto_causes_copy]
3789
      const auto states = dnsdist::configuration::getCurrentRuntimeConfiguration().d_backends; // it is a copy, but the internal shared_ptrs are the real deal
795✔
3790
      auto mplexer = std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent(states.size()));
795✔
3791
      for (auto& dss : states) {
795✔
3792

3793
        if (dss->d_config.d_availability == DownstreamState::Availability::Auto) {
441✔
3794
          if (dss->d_config.d_healthCheckMode == DownstreamState::HealthCheckMode::Active) {
358✔
3795
            dss->d_nextCheck = dss->d_config.checkInterval;
355✔
3796
          }
355✔
3797

3798
          if (!queueHealthCheck(mplexer, dss, true)) {
358!
3799
            dss->submitHealthCheckResult(true, false);
×
3800
            dss->setUpStatus(false);
×
3801
            warnlog("Marking downstream %s as 'down'", dss->getNameWithAddr());
×
3802
          }
×
3803
        }
358✔
3804
      }
441✔
3805
      handleQueuedHealthChecks(*mplexer, true);
795✔
3806
    }
795✔
3807

3808
    dnsdist::startFrontends();
795✔
3809

3810
    dnsdist::ServiceDiscovery::run();
795✔
3811

3812
#ifndef DISABLE_CARBON
795✔
3813
    dnsdist::Carbon::run(dnsdist::configuration::getCurrentRuntimeConfiguration().d_carbonEndpoints);
795✔
3814
#endif /* DISABLE_CARBON */
795✔
3815

3816
    thread stattid(maintThread);
795✔
3817
    stattid.detach();
795✔
3818

3819
    thread healththread(healthChecksThread);
795✔
3820

3821
#ifndef DISABLE_DYNBLOCKS
795✔
3822
    thread dynBlockMaintThread(dynBlockMaintenanceThread);
795✔
3823
    dynBlockMaintThread.detach();
795✔
3824
#endif /* DISABLE_DYNBLOCKS */
795✔
3825

3826
#ifndef DISABLE_SECPOLL
795✔
3827
    if (!dnsdist::configuration::getCurrentRuntimeConfiguration().d_secPollSuffix.empty()) {
795!
3828
      thread secpollthread(secPollThread);
×
3829
      secpollthread.detach();
×
3830
    }
×
3831
#endif /* DISABLE_SECPOLL */
795✔
3832

3833
    if (cmdLine.beSupervised) {
795✔
3834
#ifdef HAVE_SYSTEMD
394✔
3835
      sd_notify(0, "READY=1");
394✔
3836
#endif
394✔
3837
      healththread.join();
394✔
3838
    }
394✔
3839
    else {
401✔
3840
      healththread.detach();
401✔
3841
      dnsdist::console::doConsole();
401✔
3842
    }
401✔
3843
    doExitNicely();
795✔
3844
  }
795✔
3845
  catch (const LuaContext::ExecutionErrorException& e) {
800✔
3846
    try {
2✔
3847
      errlog("Fatal Lua error: %s", e.what());
2✔
3848
      std::rethrow_if_nested(e);
2✔
3849
    }
2✔
3850
    catch (const std::exception& ne) {
2✔
3851
      errlog("Details: %s", ne.what());
×
3852
    }
×
3853
    catch (const PDNSException& ae) {
2✔
3854
      errlog("Fatal pdns error: %s", ae.reason);
1✔
3855
    }
1✔
3856
    doExitNicely(EXIT_FAILURE);
2✔
3857
  }
2✔
3858
  catch (const std::exception& e) {
800✔
3859
    errlog("Fatal error: %s", e.what());
1✔
3860
    doExitNicely(EXIT_FAILURE);
1✔
3861
  }
1✔
3862
  catch (const PDNSException& ae) {
800✔
3863
    errlog("Fatal pdns error: %s", ae.reason);
×
3864
    doExitNicely(EXIT_FAILURE);
3865
  }
×
3866
}
800✔
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