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

PowerDNS / pdns / 19733228503

27 Nov 2025 10:30AM UTC coverage: 55.537% (-0.004%) from 55.541%
19733228503

Pull #16560

github

web-flow
Merge dcfe3a1bc into a2edd6e65
Pull Request #16560: dnsdist-2.0.x: Backport 16510 - Better performance when using `recvmmsg`

18598 of 57390 branches covered (32.41%)

Branch coverage included in aggregate %.

15 of 16 new or added lines in 1 file covered. (93.75%)

13 existing lines in 3 files now uncovered.

52308 of 70283 relevant lines covered (74.42%)

2105179.34 hits per line

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

70.6
/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 <pwd.h>
33
#include <set>
34
#include <sys/resource.h>
35
#include <unistd.h>
36

37
#include "dnsdist-systemd.hh"
38
#ifdef HAVE_SYSTEMD
39
#include <systemd/sd-daemon.h>
40
#endif
41

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

74
#include "base64.hh"
75
#include "capabilities.hh"
76
#include "coverage.hh"
77
#include "delaypipe.hh"
78
#include "doh.hh"
79
#include "dolog.hh"
80
#include "dnsname.hh"
81
#include "ednsoptions.hh"
82
#include "gettime.hh"
83
#include "lock.hh"
84
#include "misc.hh"
85
#include "sstuff.hh"
86
#include "threadname.hh"
87
#include "xsk.hh"
88

89
/* Known sins:
90

91
   Receiver is currently single threaded
92
      not *that* bad actually, but now that we are thread safe, might want to scale
93
*/
94

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

101
using std::thread;
102

103
string g_outputBuffer;
104

105
shared_ptr<BPFFilter> g_defaultBPFFilter{nullptr};
106

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

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

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

116
   When an answer comes in on a socket, we look up the offset by the id, and lob it to the
117
   original requestor.
118

119
   IDs are assigned by atomic increments of the socket offset.
120
 */
121

122
Rings g_rings;
123

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

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

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

150
static void truncateTC(PacketBuffer& packet, size_t maximumSize, unsigned int qnameWireLength, bool addEDNSToSelfGeneratedResponses)
151
{
6✔
152
  try {
6✔
153
    bool hadEDNS = false;
6✔
154
    uint16_t payloadSize = 0;
6✔
155
    uint16_t zValue = 0;
6✔
156

157
    if (addEDNSToSelfGeneratedResponses) {
6!
158
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
159
      hadEDNS = getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(packet.data()), packet.size(), &payloadSize, &zValue);
6✔
160
    }
6✔
161

162
    packet.resize(static_cast<uint16_t>(sizeof(dnsheader) + qnameWireLength + DNS_TYPE_SIZE + DNS_CLASS_SIZE));
6✔
163
    dnsdist::PacketMangling::editDNSHeaderFromPacket(packet, [](dnsheader& header) {
6✔
164
      header.ancount = 0;
6✔
165
      header.arcount = 0;
6✔
166
      header.nscount = 0;
6✔
167
      return true;
6✔
168
    });
6✔
169

170
    if (hadEDNS) {
6✔
171
      addEDNS(packet, maximumSize, (zValue & EDNS_HEADER_FLAG_DO) != 0, payloadSize, 0);
2✔
172
    }
2✔
173
  }
6✔
174
  catch (...) {
6✔
175
    ++dnsdist::metrics::g_stats.truncFail;
×
176
  }
×
177
}
6✔
178

179
#ifndef DISABLE_DELAY_PIPE
180
struct DelayedPacket
181
{
182
  int fd{-1};
183
  PacketBuffer packet;
184
  ComboAddress destination;
185
  ComboAddress origDest;
186
  void operator()() const
187
  {
4✔
188
    sendfromto(fd, packet, origDest, destination);
4✔
189
  }
4✔
190
};
191

192
static std::unique_ptr<DelayPipe<DelayedPacket>> g_delay{nullptr};
193
#endif /* DISABLE_DELAY_PIPE */
194

195
static void doLatencyStats(dnsdist::Protocol protocol, double udiff)
196
{
5,880✔
197
  constexpr auto doAvg = [](pdns::stat_double_t& var, double n, double weight) {
23,520✔
198
    var.store((weight - 1) * var.load() / weight + n / weight);
23,520✔
199
  };
23,520✔
200

201
  if (protocol == dnsdist::Protocol::DoUDP || protocol == dnsdist::Protocol::DNSCryptUDP) {
5,880✔
202
    if (udiff < 1000) {
2,808✔
203
      ++dnsdist::metrics::g_stats.latency0_1;
2,657✔
204
    }
2,657✔
205
    else if (udiff < 10000) {
151✔
206
      ++dnsdist::metrics::g_stats.latency1_10;
140✔
207
    }
140✔
208
    else if (udiff < 50000) {
11!
209
      ++dnsdist::metrics::g_stats.latency10_50;
×
210
    }
×
211
    else if (udiff < 100000) {
11!
212
      ++dnsdist::metrics::g_stats.latency50_100;
×
213
    }
×
214
    else if (udiff < 1000000) {
11✔
215
      ++dnsdist::metrics::g_stats.latency100_1000;
8✔
216
    }
8✔
217
    else {
3✔
218
      ++dnsdist::metrics::g_stats.latencySlow;
3✔
219
    }
3✔
220

221
    dnsdist::metrics::g_stats.latencySum += static_cast<unsigned long>(udiff) / 1000;
2,808✔
222
    ++dnsdist::metrics::g_stats.latencyCount;
2,808✔
223

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

261
bool responseContentMatches(const PacketBuffer& response, const DNSName& qname, const uint16_t qtype, const uint16_t qclass, const std::shared_ptr<DownstreamState>& remote, bool allowEmptyResponse)
262
{
5,082✔
263
  if (response.size() < sizeof(dnsheader)) {
5,082!
264
    return false;
×
265
  }
×
266

267
  const dnsheader_aligned dnsHeader(response.data());
5,082✔
268
  if (dnsHeader->qr == 0) {
5,082!
269
    ++dnsdist::metrics::g_stats.nonCompliantResponses;
×
270
    if (remote) {
×
271
      ++remote->nonCompliantResponses;
×
272
    }
×
273
    return false;
×
274
  }
×
275

276
  if (dnsHeader->qdcount == 0) {
5,082✔
277
    if ((dnsHeader->rcode != RCode::NoError && dnsHeader->rcode != RCode::NXDomain) || allowEmptyResponse) {
14✔
278
      return true;
10✔
279
    }
10✔
280

281
    ++dnsdist::metrics::g_stats.nonCompliantResponses;
4✔
282
    if (remote) {
4!
283
      ++remote->nonCompliantResponses;
4✔
284
    }
4✔
285
    return false;
4✔
286
  }
14✔
287

288
  try {
5,068✔
289
    uint16_t rqtype{};
5,068✔
290
    uint16_t rqclass{};
5,068✔
291
    if (response.size() < (sizeof(dnsheader) + qname.wirelength() + sizeof(rqtype) + sizeof(rqclass))) {
5,068!
292
      return false;
×
293
    }
×
294

295
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
296
    const std::string_view packetView(reinterpret_cast<const char*>(response.data() + sizeof(dnsheader)), response.size() - sizeof(dnsheader));
5,068✔
297
    if (qname.matchesUncompressedName(packetView)) {
5,068✔
298
      size_t pos = sizeof(dnsheader) + qname.wirelength();
5,066✔
299
      rqtype = response.at(pos) * 256 + response.at(pos + 1);
5,066✔
300
      rqclass = response.at(pos + 2) * 256 + response.at(pos + 3);
5,066✔
301
      return rqtype == qtype && rqclass == qclass;
5,066!
302
    }
5,066✔
303
    return false;
2✔
304
  }
5,068✔
305
  catch (const std::exception& e) {
5,068✔
306
    if (remote && !response.empty() && static_cast<size_t>(response.size()) > sizeof(dnsheader)) {
×
307
      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());
×
308
    }
×
309
    ++dnsdist::metrics::g_stats.nonCompliantResponses;
×
310
    if (remote) {
×
311
      ++remote->nonCompliantResponses;
×
312
    }
×
313
    return false;
×
314
  }
×
315
}
5,068✔
316

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

331
static bool fixUpQueryTurnedResponse(DNSQuestion& dnsQuestion, const uint16_t origFlags)
332
{
480✔
333
  dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [origFlags](dnsheader& header) {
480✔
334
    restoreFlags(&header, origFlags);
480✔
335
    return true;
480✔
336
  });
480✔
337

338
  if (dnsQuestion.d_selfGeneratedHandledEDNS) {
480✔
339
    return true;
244✔
340
  }
244✔
341
  return addEDNSToQueryTurnedResponse(dnsQuestion);
236✔
342
}
480✔
343

344
static bool fixUpResponse(PacketBuffer& response, const DNSName& qname, uint16_t origFlags, bool ednsAdded, bool ecsAdded, bool* zeroScope)
345
{
4,889✔
346
  if (response.size() < sizeof(dnsheader)) {
4,889!
347
    return false;
×
348
  }
×
349

350
  dnsdist::PacketMangling::editDNSHeaderFromPacket(response, [origFlags](dnsheader& header) {
4,889✔
351
    restoreFlags(&header, origFlags);
4,889✔
352
    return true;
4,889✔
353
  });
4,889✔
354

355
  if (response.size() == sizeof(dnsheader)) {
4,889✔
356
    return true;
10✔
357
  }
10✔
358

359
  if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_fixupCase) {
4,879✔
360
    const auto& realname = qname.getStorage();
2✔
361
    if (response.size() >= (sizeof(dnsheader) + realname.length())) {
2!
362
      memcpy(&response.at(sizeof(dnsheader)), realname.c_str(), realname.length());
2✔
363
    }
2✔
364
  }
2✔
365

366
  if (ednsAdded || ecsAdded) {
4,879✔
367
    uint16_t optStart{};
225✔
368
    size_t optLen = 0;
225✔
369
    bool last = false;
225✔
370

371
    int res = locateEDNSOptRR(response, &optStart, &optLen, &last);
225✔
372

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

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

434
  return true;
4,879✔
435
}
4,889✔
436

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

452
bool applyRulesToResponse(const std::vector<dnsdist::rules::ResponseRuleAction>& respRuleActions, DNSResponse& dnsResponse)
453
{
6,934✔
454
  if (respRuleActions.empty()) {
6,934✔
455
    return true;
5,516✔
456
  }
5,516✔
457

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

509
  return true;
1,120✔
510
}
1,418✔
511

512
bool processResponseAfterRules(PacketBuffer& response, DNSResponse& dnsResponse, [[maybe_unused]] bool muted)
513
{
4,889✔
514
  bool zeroScope = false;
4,889✔
515
  if (!fixUpResponse(response, dnsResponse.ids.qname, dnsResponse.ids.origFlags, dnsResponse.ids.ednsAdded, dnsResponse.ids.ecsAdded, dnsResponse.ids.useZeroScope ? &zeroScope : nullptr)) {
4,889!
516
    return false;
×
517
  }
×
518

519
  if (dnsResponse.ids.packetCache && !dnsResponse.ids.selfGenerated && !dnsResponse.ids.skipCache && (!dnsResponse.ids.forwardedOverUDP || response.size() <= s_maxUDPResponsePacketSize)) {
4,889!
520
    if (!dnsResponse.ids.useZeroScope) {
225✔
521
      /* if the query was not suitable for zero-scope, for
522
         example because it had an existing ECS entry so the hash is
523
         not really 'no ECS', so just insert it for the existing subnet
524
         since:
525
         - we don't have the correct hash for a non-ECS query
526
         - inserting with hash computed before the ECS replacement but with
527
         the subnet extracted _after_ the replacement would not work.
528
      */
529
      zeroScope = false;
214✔
530
    }
214✔
531
    uint32_t cacheKey = dnsResponse.ids.cacheKey;
225✔
532
    if (dnsResponse.ids.protocol == dnsdist::Protocol::DoH && !dnsResponse.ids.forwardedOverUDP) {
225✔
533
      cacheKey = dnsResponse.ids.cacheKeyTCP;
2✔
534
      // disable zeroScope in that case, as we only have the "no-ECS" cache key for UDP
535
      zeroScope = false;
2✔
536
    }
2✔
537
    if (zeroScope) {
225✔
538
      // if zeroScope, pass the pre-ECS hash-key and do not pass the subnet to the cache
539
      cacheKey = dnsResponse.ids.cacheKeyNoECS;
3✔
540
    }
3✔
541
    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);
225!
542

543
    const auto& chains = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ruleChains;
225✔
544
    const auto& cacheInsertedRespRuleActions = dnsdist::rules::getResponseRuleChain(chains, dnsdist::rules::ResponseRuleChain::CacheInsertedResponseRules);
225✔
545
    if (!applyRulesToResponse(cacheInsertedRespRuleActions, dnsResponse)) {
225!
546
      return false;
×
547
    }
×
548
  }
225✔
549

550
  if (dnsResponse.ids.ttlCap > 0) {
4,889!
551
    dnsdist::PacketMangling::restrictDNSPacketTTLs(dnsResponse.getMutableData(), 0, dnsResponse.ids.ttlCap);
×
552
  }
×
553

554
  if (dnsResponse.ids.d_extendedError) {
4,889✔
555
    dnsdist::edns::addExtendedDNSError(dnsResponse.getMutableData(), dnsResponse.getMaximumSize(), dnsResponse.ids.d_extendedError->infoCode, dnsResponse.ids.d_extendedError->extraText);
9✔
556
  }
9✔
557

558
#ifdef HAVE_DNSCRYPT
4,889✔
559
  if (!muted) {
4,889✔
560
    if (!encryptResponse(response, dnsResponse.getMaximumSize(), dnsResponse.overTCP(), dnsResponse.ids.dnsCryptQuery)) {
4,885!
561
      return false;
×
562
    }
×
563
  }
4,885✔
564
#endif /* HAVE_DNSCRYPT */
4,889✔
565

566
  return true;
4,889✔
567
}
4,889✔
568

569
bool processResponse(PacketBuffer& response, DNSResponse& dnsResponse, bool muted)
570
{
5,212✔
571
  const auto& chains = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ruleChains;
5,212✔
572
  const auto& respRuleActions = dnsdist::rules::getResponseRuleChain(chains, dnsdist::rules::ResponseRuleChain::ResponseRules);
5,212✔
573

574
  if (!applyRulesToResponse(respRuleActions, dnsResponse)) {
5,212✔
575
    return false;
7✔
576
  }
7✔
577

578
  if (dnsResponse.isAsynchronous()) {
5,205✔
579
    return true;
458✔
580
  }
458✔
581

582
  return processResponseAfterRules(response, dnsResponse, muted);
4,747✔
583
}
5,205✔
584

585
static size_t getInitialUDPPacketBufferSize(bool expectProxyProtocol)
586
{
767✔
587
  static_assert(dnsdist::configuration::s_udpIncomingBufferSize <= s_initialUDPPacketBufferSize, "The incoming buffer size should not be larger than s_initialUDPPacketBufferSize");
767✔
588

589
  const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
767✔
590
  if (!expectProxyProtocol || runtimeConfig.d_proxyProtocolACL.empty()) {
767✔
591
    return s_initialUDPPacketBufferSize;
762✔
592
  }
762✔
593

594
  return s_initialUDPPacketBufferSize + runtimeConfig.d_proxyProtocolMaximumSize;
5✔
595
}
767✔
596

597
static size_t getMaximumIncomingPacketSize(const ClientState& clientState)
598
{
376✔
599
  if (clientState.dnscryptCtx) {
376✔
600
    return getInitialUDPPacketBufferSize(clientState.d_enableProxyProtocol);
5✔
601
  }
5✔
602

603
  const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
371✔
604
  if (!clientState.d_enableProxyProtocol || runtimeConfig.d_proxyProtocolACL.empty()) {
371✔
605
    return dnsdist::configuration::s_udpIncomingBufferSize;
367✔
606
  }
367✔
607

608
  return dnsdist::configuration::s_udpIncomingBufferSize + runtimeConfig.d_proxyProtocolMaximumSize;
4✔
609
}
371✔
610

611
bool sendUDPResponse(int origFD, const PacketBuffer& response, [[maybe_unused]] const int delayMsec, const ComboAddress& origDest, const ComboAddress& origRemote)
612
{
2,821✔
613
#ifndef DISABLE_DELAY_PIPE
2,821✔
614
  if (delayMsec > 0 && g_delay != nullptr) {
2,821!
615
    DelayedPacket delayed{origFD, response, origRemote, origDest};
4✔
616
    g_delay->submit(delayed, delayMsec);
4✔
617
    return true;
4✔
618
  }
4✔
619
#endif /* DISABLE_DELAY_PIPE */
2,817✔
620
  // NOLINTNEXTLINE(readability-suspicious-call-argument)
621
  sendfromto(origFD, response, origDest, origRemote);
2,817✔
622
  return true;
2,817✔
623
}
2,821✔
624

625
void handleResponseSent(const InternalQueryState& ids, double udiff, const ComboAddress& client, const ComboAddress& backend, unsigned int size, const dnsheader& cleartextDH, dnsdist::Protocol outgoingProtocol, bool fromBackend)
626
{
5,362✔
627
  handleResponseSent(ids.qname, ids.qtype, udiff, client, backend, size, cleartextDH, outgoingProtocol, ids.protocol, fromBackend);
5,362✔
628
}
5,362✔
629

630
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)
631
{
5,880✔
632
  if (g_rings.shouldRecordResponses()) {
5,880!
633
    timespec now{};
5,880✔
634
    gettime(&now);
5,880✔
635
    g_rings.insertResponse(now, client, qname, qtype, static_cast<unsigned int>(udiff), size, cleartextDH, backend, outgoingProtocol);
5,880✔
636
  }
5,880✔
637

638
  switch (cleartextDH.rcode) {
5,880✔
639
  case RCode::NXDomain:
36✔
640
    ++dnsdist::metrics::g_stats.frontendNXDomain;
36✔
641
    break;
36✔
642
  case RCode::ServFail:
277✔
643
    if (fromBackend) {
277✔
644
      ++dnsdist::metrics::g_stats.servfailResponses;
204✔
645
    }
204✔
646
    ++dnsdist::metrics::g_stats.frontendServFail;
277✔
647
    break;
277✔
648
  case RCode::NoError:
5,257✔
649
    ++dnsdist::metrics::g_stats.frontendNoError;
5,257✔
650
    break;
5,257✔
651
  }
5,880✔
652

653
  doLatencyStats(incomingProtocol, udiff);
5,880✔
654
}
5,880✔
655

656
static void handleResponseTC4UDPClient(DNSQuestion& dnsQuestion, uint16_t udpPayloadSize, PacketBuffer& response)
657
{
3,140✔
658
  if (udpPayloadSize != 0 && response.size() > udpPayloadSize) {
3,140✔
659
    vinfolog("Got a response of size %d while the initial UDP payload size was %d, truncating", response.size(), udpPayloadSize);
1!
660
    truncateTC(dnsQuestion.getMutableData(), dnsQuestion.getMaximumSize(), dnsQuestion.ids.qname.wirelength(), dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses);
1✔
661
    dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
1✔
662
      header.tc = true;
1✔
663
      return true;
1✔
664
    });
1✔
665
  }
1✔
666
  else if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_truncateTC && dnsQuestion.getHeader()->tc) {
3,139✔
667
    truncateTC(response, dnsQuestion.getMaximumSize(), dnsQuestion.ids.qname.wirelength(), dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses);
3✔
668
  }
3✔
669
}
3,140✔
670

671
static void handleResponseForUDPClient(InternalQueryState& ids, PacketBuffer& response, const std::shared_ptr<DownstreamState>& backend, bool isAsync, bool selfGenerated)
672
{
2,681✔
673
  DNSResponse dnsResponse(ids, response, backend);
2,681✔
674

675
  handleResponseTC4UDPClient(dnsResponse, ids.udpPayloadSize, response);
2,681✔
676

677
  /* when the answer is encrypted in place, we need to get a copy
678
     of the original header before encryption to fill the ring buffer */
679
  dnsheader cleartextDH{};
2,681✔
680
  memcpy(&cleartextDH, dnsResponse.getHeader().get(), sizeof(cleartextDH));
2,681✔
681

682
  if (!isAsync) {
2,681✔
683
    if (!processResponse(response, dnsResponse, ids.cs != nullptr && ids.cs->muted)) {
2,651!
684
      return;
3✔
685
    }
3✔
686

687
    if (dnsResponse.isAsynchronous()) {
2,648✔
688
      return;
328✔
689
    }
328✔
690
  }
2,648✔
691

692
  ++dnsdist::metrics::g_stats.responses;
2,350✔
693
  if (ids.cs != nullptr) {
2,350✔
694
    ++ids.cs->responses;
2,346✔
695
  }
2,346✔
696

697
  bool muted = true;
2,350✔
698
  if (ids.cs != nullptr && !ids.cs->muted && !ids.isXSK()) {
2,350!
699
    sendUDPResponse(ids.cs->udpFD, response, dnsResponse.ids.delayMsec, ids.hopLocal, ids.hopRemote);
2,346✔
700
    muted = false;
2,346✔
701
  }
2,346✔
702

703
  if (!selfGenerated) {
2,350✔
704
    double udiff = ids.queryRealTime.udiff();
2,340✔
705
    if (!muted) {
2,340!
706
      vinfolog("Got answer from %s, relayed to %s (UDP), took %f us", backend->d_config.remote.toStringWithPort(), ids.origRemote.toStringWithPort(), udiff);
2,340✔
707
    }
2,340✔
708
    else {
×
709
      if (!ids.isXSK()) {
×
710
        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);
×
711
      }
×
712
      else {
×
713
        vinfolog("Got answer from %s, relayed to %s (UDP via XSK), took %f us", backend->d_config.remote.toStringWithPort(), ids.origRemote.toStringWithPort(), udiff);
×
714
      }
×
715
    }
×
716

717
    handleResponseSent(ids, udiff, dnsResponse.ids.origRemote, backend->d_config.remote, response.size(), cleartextDH, backend->getProtocol(), true);
2,340✔
718
  }
2,340✔
719
  else {
10✔
720
    handleResponseSent(ids, 0., dnsResponse.ids.origRemote, ComboAddress(), response.size(), cleartextDH, dnsdist::Protocol::DoUDP, false);
10✔
721
  }
10✔
722
}
2,350✔
723

724
bool processResponderPacket(std::shared_ptr<DownstreamState>& dss, PacketBuffer& response, InternalQueryState&& ids)
725
{
2,636✔
726

727
  const dnsheader_aligned dnsHeader(response.data());
2,636✔
728
  auto queryId = dnsHeader->id;
2,636✔
729

730
  if (!responseContentMatches(response, ids.qname, ids.qtype, ids.qclass, dss, dnsdist::configuration::getCurrentRuntimeConfiguration().d_allowEmptyResponse)) {
2,636✔
731
    dss->restoreState(queryId, std::move(ids));
3✔
732
    return false;
3✔
733
  }
3✔
734

735
  auto dohUnit = std::move(ids.du);
2,633✔
736
  dnsdist::PacketMangling::editDNSHeaderFromPacket(response, [&ids](dnsheader& header) {
2,633✔
737
    header.id = ids.origID;
2,633✔
738
    return true;
2,633✔
739
  });
2,633✔
740
  ++dss->responses;
2,633✔
741

742
  double udiff = ids.queryRealTime.udiff();
2,633✔
743
  // do that _before_ the processing, otherwise it's not fair to the backend
744
  dss->latencyUsec = (127.0 * dss->latencyUsec / 128.0) + udiff / 128.0;
2,633✔
745
  dss->reportResponse(dnsHeader->rcode);
2,633✔
746

747
  /* don't call processResponse for DOH */
748
  if (dohUnit) {
2,633✔
749
#ifdef HAVE_DNS_OVER_HTTPS
122✔
750
    // DoH query, we cannot touch dohUnit after that
751
    DOHUnitInterface::handleUDPResponse(std::move(dohUnit), std::move(response), std::move(ids), dss);
122✔
752
#endif
122✔
753
    return false;
122✔
754
  }
122✔
755

756
  handleResponseForUDPClient(ids, response, dss, false, false);
2,511✔
757
  return true;
2,511✔
758
}
2,633✔
759

760
// listens on a dedicated socket, lobs answers from downstream servers to original requestors
761
void responderThread(std::shared_ptr<DownstreamState> dss)
762
{
386✔
763
  try {
386✔
764
    setThreadName("dnsdist/respond");
386✔
765
    const size_t initialBufferSize = getInitialUDPPacketBufferSize(false);
386✔
766
    /* allocate one more byte so we can detect truncation */
767
    PacketBuffer response(initialBufferSize + 1);
386✔
768
    uint16_t queryId = 0;
386✔
769
    std::vector<int> sockets;
386✔
770
    sockets.reserve(dss->sockets.size());
386✔
771

772
    for (;;) {
3,410✔
773
      try {
3,410✔
774
        if (dss->isStopped()) {
3,410✔
775
          break;
386✔
776
        }
386✔
777

778
        if (!dss->connected) {
3,024!
779
          /* the sockets are not connected yet, likely because we detected a problem,
780
             tried to reconnect and it failed. We will try to reconnect after the next
781
             successful health-check (unless reconnectOnUp is false), or when trying
782
             to send in the UDP listener thread, but until then we simply need to wait. */
783
          dss->waitUntilConnected();
×
784
          continue;
×
785
        }
×
786

787
        dss->pickSocketsReadyForReceiving(sockets);
3,024✔
788

789
        /* check a second time here because we might have waited quite a bit
790
           since the first check */
791
        if (dss->isStopped()) {
3,024!
792
          break;
×
793
        }
×
794

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

801
          if (got == 0 && dss->isStopped()) {
3,025✔
802
            break;
385✔
803
          }
385✔
804

805
          if (got < 0 || static_cast<size_t>(got) < sizeof(dnsheader) || static_cast<size_t>(got) == (initialBufferSize + 1)) {
2,640!
806
            continue;
3✔
807
          }
3✔
808

809
          response.resize(static_cast<size_t>(got));
2,637✔
810
          const dnsheader_aligned dnsHeader(response.data());
2,637✔
811
          queryId = dnsHeader->id;
2,637✔
812

813
          auto ids = dss->getState(queryId);
2,637✔
814
          if (!ids) {
2,637!
815
            continue;
×
816
          }
×
817

818
          if (!ids->isXSK() && sockDesc != ids->backendFD) {
2,637!
819
            dss->restoreState(queryId, std::move(*ids));
×
820
            continue;
×
821
          }
×
822

823
          dnsdist::configuration::refreshLocalRuntimeConfiguration();
2,637✔
824
          if (processResponderPacket(dss, response, std::move(*ids)) && ids->isXSK() && ids->cs->xskInfoResponder) {
2,637!
825
#ifdef HAVE_XSK
×
826
            auto& xskInfo = ids->cs->xskInfoResponder;
×
827
            auto xskPacket = xskInfo->getEmptyFrame();
×
828
            if (!xskPacket) {
×
829
              continue;
×
830
            }
×
831
            xskPacket->setHeader(ids->xskPacketHeader);
×
832
            if (!xskPacket->setPayload(response)) {
×
833
            }
×
834
            if (ids->delayMsec > 0) {
×
835
              xskPacket->addDelay(ids->delayMsec);
×
836
            }
×
837
            xskPacket->updatePacket();
×
838
            xskInfo->pushToSendQueue(*xskPacket);
×
839
            xskInfo->notifyXskSocket();
×
840
#endif /* HAVE_XSK */
×
841
          }
×
842
        }
2,637✔
843
      }
3,024✔
844
      catch (const std::exception& e) {
3,410✔
845
        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!
846
      }
2✔
847
    }
3,410✔
848
  }
386✔
849
  catch (const std::exception& e) {
386✔
850
    errlog("UDP responder thread died because of exception: %s", e.what());
×
851
  }
×
852
  catch (const PDNSException& e) {
386✔
853
    errlog("UDP responder thread died because of PowerDNS exception: %s", e.reason);
×
854
  }
×
855
  catch (...) {
386✔
856
    errlog("UDP responder thread died because of an exception: %s", "unknown");
×
857
  }
×
858
}
386✔
859

860
RecursiveLockGuarded<LuaContext> g_lua{LuaContext()};
861

862
static void spoofResponseFromString(DNSQuestion& dnsQuestion, const string& spoofContent, bool raw)
863
{
54✔
864
  string result;
54✔
865

866
  if (raw) {
54✔
867
    dnsdist::ResponseConfig config;
6✔
868
    std::vector<std::string> raws;
6✔
869
    stringtok(raws, spoofContent, ",");
6✔
870
    dnsdist::self_answers::generateAnswerFromRDataEntries(dnsQuestion, raws, std::nullopt, config);
6✔
871
  }
6✔
872
  else {
48✔
873
    std::vector<std::string> addrs;
48✔
874
    stringtok(addrs, spoofContent, " ,");
48✔
875

876
    if (addrs.size() == 1) {
48✔
877
      dnsdist::ResponseConfig config;
46✔
878
      try {
46✔
879
        ComboAddress spoofAddr(spoofContent);
46✔
880
        dnsdist::self_answers::generateAnswerFromIPAddresses(dnsQuestion, {spoofAddr}, config);
46✔
881
      }
46✔
882
      catch (const PDNSException& e) {
46✔
883
        DNSName cname(spoofContent);
29✔
884
        dnsdist::self_answers::generateAnswerFromCNAME(dnsQuestion, cname, config);
29✔
885
      }
29✔
886
    }
46✔
887
    else {
2✔
888
      dnsdist::ResponseConfig config;
2✔
889
      std::vector<ComboAddress> cas;
2✔
890
      for (const auto& addr : addrs) {
4✔
891
        try {
4✔
892
          cas.emplace_back(addr);
4✔
893
        }
4✔
894
        catch (...) {
4✔
895
        }
×
896
      }
4✔
897
      dnsdist::self_answers::generateAnswerFromIPAddresses(dnsQuestion, cas, config);
2✔
898
    }
2✔
899
  }
48✔
900
}
54✔
901

902
static void spoofPacketFromString(DNSQuestion& dnsQuestion, const string& spoofContent)
903
{
2✔
904
  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
905
  dnsdist::self_answers::generateAnswerFromRawPacket(dnsQuestion, PacketBuffer(spoofContent.data(), spoofContent.data() + spoofContent.size()));
2✔
906
}
2✔
907

908
bool processRulesResult(const DNSAction::Action& action, DNSQuestion& dnsQuestion, std::string& ruleresult, bool& drop)
909
{
2,623✔
910
  if (dnsQuestion.isAsynchronous()) {
2,623✔
911
    return false;
202✔
912
  }
202✔
913

914
  auto setRCode = [&dnsQuestion](uint8_t rcode) {
2,421✔
915
    dnsdist::self_answers::removeRecordsAndSetRCode(dnsQuestion, rcode);
22✔
916
  };
22✔
917

918
  switch (action) {
2,421!
919
  case DNSAction::Action::Allow:
33✔
920
    return true;
33✔
921
    break;
×
922
  case DNSAction::Action::Drop:
25✔
923
    ++dnsdist::metrics::g_stats.ruleDrop;
25✔
924
    drop = true;
25✔
925
    return true;
25✔
926
    break;
×
927
  case DNSAction::Action::Nxdomain:
12✔
928
    setRCode(RCode::NXDomain);
12✔
929
    return true;
12✔
930
    break;
×
931
  case DNSAction::Action::Refused:
8✔
932
    setRCode(RCode::Refused);
8✔
933
    return true;
8✔
934
    break;
×
935
  case DNSAction::Action::ServFail:
2✔
936
    setRCode(RCode::ServFail);
2✔
937
    return true;
2✔
938
    break;
×
939
  case DNSAction::Action::Spoof:
48✔
940
    spoofResponseFromString(dnsQuestion, ruleresult, false);
48✔
941
    return true;
48✔
942
    break;
×
943
  case DNSAction::Action::SpoofPacket:
2✔
944
    spoofPacketFromString(dnsQuestion, ruleresult);
2✔
945
    return true;
2✔
946
    break;
×
947
  case DNSAction::Action::SpoofRaw:
6✔
948
    spoofResponseFromString(dnsQuestion, ruleresult, true);
6✔
949
    return true;
6✔
950
    break;
×
951
  case DNSAction::Action::Truncate:
8✔
952
    if (!dnsQuestion.overTCP()) {
8✔
953
      dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
7✔
954
        header.tc = true;
7✔
955
        header.qr = true;
7✔
956
        header.ra = header.rd;
7✔
957
        header.aa = false;
7✔
958
        header.ad = false;
7✔
959
        return true;
7✔
960
      });
7✔
961
      ++dnsdist::metrics::g_stats.ruleTruncated;
7✔
962
      return true;
7✔
963
    }
7✔
964
    break;
1✔
965
  case DNSAction::Action::HeaderModify:
323✔
966
    return true;
323✔
967
    break;
×
968
  case DNSAction::Action::Pool:
521✔
969
    /* we need to keep this because a custom Lua action can return
970
       DNSAction.Spoof, 'poolname' */
971
    dnsQuestion.ids.poolName = ruleresult;
521✔
972
    return true;
521✔
973
    break;
×
974
  case DNSAction::Action::NoRecurse:
×
975
    dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
×
976
      header.rd = false;
×
977
      return true;
×
978
    });
×
979
    return true;
×
980
    break;
×
981
    /* non-terminal actions follow */
982
  case DNSAction::Action::Delay:
2✔
983
    pdns::checked_stoi_into(dnsQuestion.ids.delayMsec, ruleresult); // sorry
2✔
984
    break;
2✔
985
  case DNSAction::Action::SetTag:
×
986
    /* unsupported for non-dynamic block */
987
  case DNSAction::Action::None:
1,431✔
988
    /* fall-through */
989
  case DNSAction::Action::NoOp:
1,431!
990
    break;
1,431✔
991
  }
2,421✔
992

993
  /* false means that we don't stop the processing */
994
  return false;
1,434✔
995
}
2,421✔
996

997
static bool applyRulesChainToQuery(const std::vector<dnsdist::rules::RuleAction>& rules, DNSQuestion& dnsQuestion)
998
{
6,276✔
999
  if (rules.empty()) {
6,276✔
1000
    return true;
3,191✔
1001
  }
3,191✔
1002

1003
  DNSAction::Action action = DNSAction::Action::None;
3,085✔
1004
  string ruleresult;
3,085✔
1005
  bool drop = false;
3,085✔
1006

1007
  for (const auto& rule : rules) {
6,660✔
1008
    if (!rule.d_rule->matches(&dnsQuestion)) {
6,660✔
1009
      continue;
4,039✔
1010
    }
4,039✔
1011

1012
    rule.d_rule->d_matches++;
2,621✔
1013
    action = (*rule.d_action)(&dnsQuestion, &ruleresult);
2,621✔
1014
    if (processRulesResult(action, dnsQuestion, ruleresult, drop)) {
2,621✔
1015
      break;
985✔
1016
    }
985✔
1017
  }
2,621✔
1018

1019
  return !drop;
3,085✔
1020
}
6,276✔
1021

1022
static bool applyRulesToQuery(DNSQuestion& dnsQuestion, const timespec& now)
1023
{
6,058✔
1024
  if (g_rings.shouldRecordQueries()) {
6,058!
1025
    g_rings.insertQuery(now, dnsQuestion.ids.origRemote, dnsQuestion.ids.qname, dnsQuestion.ids.qtype, dnsQuestion.getData().size(), *dnsQuestion.getHeader(), dnsQuestion.getProtocol());
6,058✔
1026
  }
6,058✔
1027

1028
  {
6,058✔
1029
    const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
6,058✔
1030
    if (runtimeConfig.d_queryCountConfig.d_enabled) {
6,058!
1031
      string qname = dnsQuestion.ids.qname.toLogString();
×
1032
      bool countQuery{true};
×
1033
      if (runtimeConfig.d_queryCountConfig.d_filter) {
×
1034
        auto lock = g_lua.lock();
×
1035
        std::tie(countQuery, qname) = runtimeConfig.d_queryCountConfig.d_filter(&dnsQuestion);
×
1036
      }
×
1037

1038
      if (countQuery) {
×
1039
        auto records = dnsdist::QueryCount::g_queryCountRecords.write_lock();
×
1040
        if (records->count(qname) == 0) {
×
1041
          (*records)[qname] = 0;
×
1042
        }
×
1043
        (*records)[qname]++;
×
1044
      }
×
1045
    }
×
1046
  }
6,058✔
1047

1048
#ifndef DISABLE_DYNBLOCKS
6,058✔
1049
  const auto defaultDynBlockAction = dnsdist::configuration::getCurrentRuntimeConfiguration().d_dynBlockAction;
6,058✔
1050
  auto setRCode = [&dnsQuestion](uint8_t rcode) {
6,058✔
1051
    dnsdist::self_answers::removeRecordsAndSetRCode(dnsQuestion, rcode);
12✔
1052
  };
12✔
1053

1054
  /* the Dynamic Block mechanism supports address and port ranges, so we need to pass the full address and port */
1055
  if (auto* got = dnsdist::DynamicBlocks::getClientAddressDynamicRules().lookup(AddressAndPortRange(dnsQuestion.ids.origRemote, dnsQuestion.ids.origRemote.isIPv4() ? 32 : 128, 16))) {
6,058✔
1056
    auto updateBlockStats = [&got]() {
510✔
1057
      ++dnsdist::metrics::g_stats.dynBlocked;
38✔
1058
      got->second.blocks++;
38✔
1059
    };
38✔
1060

1061
    if (now < got->second.until) {
510✔
1062
      DNSAction::Action action = got->second.action;
82✔
1063
      if (action == DNSAction::Action::None) {
82✔
1064
        action = defaultDynBlockAction;
22✔
1065
      }
22✔
1066

1067
      switch (action) {
82✔
1068
      case DNSAction::Action::NoOp:
43✔
1069
        /* do nothing */
1070
        break;
43✔
1071

1072
      case DNSAction::Action::Nxdomain:
2✔
1073
        vinfolog("Query from %s turned into NXDomain because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort());
2!
1074
        updateBlockStats();
2✔
1075

1076
        setRCode(RCode::NXDomain);
2✔
1077
        return true;
2✔
1078

1079
      case DNSAction::Action::Refused:
10✔
1080
        vinfolog("Query from %s refused because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort());
10!
1081
        updateBlockStats();
10✔
1082

1083
        setRCode(RCode::Refused);
10✔
1084
        return true;
10✔
1085

1086
      case DNSAction::Action::Truncate:
2✔
1087
        if (!dnsQuestion.overTCP()) {
2✔
1088
          updateBlockStats();
1✔
1089
          vinfolog("Query from %s truncated because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort());
1!
1090
          dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
1✔
1091
            header.tc = true;
1✔
1092
            header.qr = true;
1✔
1093
            header.ra = header.rd;
1✔
1094
            header.aa = false;
1✔
1095
            header.ad = false;
1✔
1096
            return true;
1✔
1097
          });
1✔
1098
          return true;
1✔
1099
        }
1✔
1100
        else {
1✔
1101
          vinfolog("Query from %s for %s over TCP *not* truncated because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), dnsQuestion.ids.qname.toLogString());
1!
1102
        }
1✔
1103
        break;
1✔
1104
      case DNSAction::Action::NoRecurse:
1!
1105
        updateBlockStats();
×
1106
        vinfolog("Query from %s setting rd=0 because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort());
×
1107
        dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
×
1108
          header.rd = false;
×
1109
          return true;
×
1110
        });
×
1111
        return true;
×
1112
      case DNSAction::Action::SetTag: {
3✔
1113
        if (!got->second.tagSettings) {
3!
1114
          vinfolog("Skipping set tag dynamic block for query from %s because of missing options", dnsQuestion.ids.origRemote.toStringWithPort());
×
1115
          break;
×
1116
        }
×
1117
        updateBlockStats();
3✔
1118
        const auto& tagName = got->second.tagSettings->d_name;
3✔
1119
        const auto& tagValue = got->second.tagSettings->d_value;
3✔
1120
        dnsQuestion.setTag(tagName, tagValue);
3✔
1121
        vinfolog("Query from %s setting tag %s to %s because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), tagName, tagValue);
3!
1122
        // do not return, the whole point it to set a Tag to be able to do further processing in rules
1123
        break;
3✔
1124
      }
3✔
1125
      default:
22✔
1126
        updateBlockStats();
22✔
1127
        vinfolog("Query from %s dropped because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort());
22!
1128
        return false;
22✔
1129
      }
82✔
1130
    }
82✔
1131
  }
510✔
1132

1133
  if (auto* got = dnsdist::DynamicBlocks::getSuffixDynamicRules().lookup(dnsQuestion.ids.qname)) {
6,023!
1134
    auto updateBlockStats = [&got]() {
×
1135
      ++dnsdist::metrics::g_stats.dynBlocked;
×
1136
      got->blocks++;
×
1137
    };
×
1138

1139
    if (now < got->until) {
×
1140
      DNSAction::Action action = got->action;
×
1141
      if (action == DNSAction::Action::None) {
×
1142
        action = defaultDynBlockAction;
×
1143
      }
×
1144
      switch (action) {
×
1145
      case DNSAction::Action::NoOp:
×
1146
        /* do nothing */
1147
        break;
×
1148
      case DNSAction::Action::Nxdomain:
×
1149
        vinfolog("Query from %s for %s turned into NXDomain because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), dnsQuestion.ids.qname.toLogString());
×
1150
        updateBlockStats();
×
1151

1152
        setRCode(RCode::NXDomain);
×
1153
        return true;
×
1154
      case DNSAction::Action::Refused:
×
1155
        vinfolog("Query from %s for %s refused because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), dnsQuestion.ids.qname.toLogString());
×
1156
        updateBlockStats();
×
1157

1158
        setRCode(RCode::Refused);
×
1159
        return true;
×
1160
      case DNSAction::Action::Truncate:
×
1161
        if (!dnsQuestion.overTCP()) {
×
1162
          updateBlockStats();
×
1163

1164
          vinfolog("Query from %s for %s truncated because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), dnsQuestion.ids.qname.toLogString());
×
1165
          dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
×
1166
            header.tc = true;
×
1167
            header.qr = true;
×
1168
            header.ra = header.rd;
×
1169
            header.aa = false;
×
1170
            header.ad = false;
×
1171
            return true;
×
1172
          });
×
1173
          return true;
×
1174
        }
×
1175
        else {
×
1176
          vinfolog("Query from %s for %s over TCP *not* truncated because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), dnsQuestion.ids.qname.toLogString());
×
1177
        }
×
1178
        break;
×
1179
      case DNSAction::Action::NoRecurse:
×
1180
        updateBlockStats();
×
1181
        vinfolog("Query from %s setting rd=0 because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort());
×
1182
        dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
×
1183
          header.rd = false;
×
1184
          return true;
×
1185
        });
×
1186
        return true;
×
1187
      case DNSAction::Action::SetTag: {
×
1188
        if (!got->tagSettings) {
×
1189
          vinfolog("Skipping set tag dynamic block for query from %s because of missing options", dnsQuestion.ids.origRemote.toStringWithPort());
×
1190
          break;
×
1191
        }
×
1192
        updateBlockStats();
×
1193
        const auto& tagName = got->tagSettings->d_name;
×
1194
        const auto& tagValue = got->tagSettings->d_value;
×
1195
        dnsQuestion.setTag(tagName, tagValue);
×
1196
        vinfolog("Query from %s setting tag %s to %s because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), tagName, tagValue);
×
1197
        // do not return, the whole point it to set a Tag to be able to do further processing in rules
1198
        break;
×
1199
      }
×
1200
      default:
×
1201
        updateBlockStats();
×
1202
        vinfolog("Query from %s for %s dropped because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), dnsQuestion.ids.qname.toLogString());
×
1203
        return false;
×
1204
      }
×
1205
    }
×
1206
  }
×
1207
#endif /* DISABLE_DYNBLOCKS */
6,023✔
1208

1209
  const auto& chains = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ruleChains;
6,023✔
1210
  const auto& queryRules = dnsdist::rules::getRuleChain(chains, dnsdist::rules::RuleChain::Rules);
6,023✔
1211
  return applyRulesChainToQuery(queryRules, dnsQuestion);
6,023✔
1212
}
6,023✔
1213

1214
ssize_t udpClientSendRequestToBackend(const std::shared_ptr<DownstreamState>& backend, const int socketDesc, const PacketBuffer& request, bool healthCheck)
1215
{
4,254✔
1216
  ssize_t result = 0;
4,254✔
1217

1218
  if (backend->d_config.sourceItf == 0) {
4,254!
1219
    result = send(socketDesc, request.data(), request.size(), 0);
4,254✔
1220
  }
4,254✔
1221
  else {
×
1222
    msghdr msgh{};
×
1223
    iovec iov{};
×
1224
    cmsgbuf_aligned cbuf;
×
1225
    ComboAddress remote(backend->d_config.remote);
×
1226
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-type-const-cast)
1227
    fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), const_cast<char*>(reinterpret_cast<const char*>(request.data())), request.size(), &remote);
×
1228
    addCMsgSrcAddr(&msgh, &cbuf, &backend->d_config.sourceAddr, static_cast<int>(backend->d_config.sourceItf));
×
1229
    result = sendmsg(socketDesc, &msgh, 0);
×
1230
  }
×
1231

1232
  if (result == -1) {
4,254!
1233
    int savederrno = errno;
×
1234
    vinfolog("Error sending request to backend %s: %s", backend->d_config.remote.toStringWithPort(), stringerror(savederrno));
×
1235

1236
    /* This might sound silly, but on Linux send() might fail with EINVAL
1237
       if the interface the socket was bound to doesn't exist anymore.
1238
       We don't want to reconnect the real socket if the healthcheck failed,
1239
       because it's not using the same socket.
1240
    */
1241
    if (!healthCheck) {
×
1242
      if (savederrno == EINVAL || savederrno == ENODEV || savederrno == ENETUNREACH || savederrno == EHOSTUNREACH || savederrno == EBADF) {
×
1243
        backend->reconnect();
×
1244
      }
×
1245
      backend->reportTimeoutOrError();
×
1246
    }
×
1247
  }
×
1248

1249
  return result;
4,254✔
1250
}
4,254✔
1251

1252
static bool isUDPQueryAcceptable(ClientState& clientState, const struct msghdr* msgh, const ComboAddress& remote, ComboAddress& dest, bool& expectProxyProtocol)
1253
{
2,894✔
1254
  if ((msgh->msg_flags & MSG_TRUNC) != 0) {
2,894!
1255
    /* message was too large for our buffer */
1256
    vinfolog("Dropping message too large for our buffer");
×
1257
    ++clientState.nonCompliantQueries;
×
1258
    ++dnsdist::metrics::g_stats.nonCompliantQueries;
×
1259
    return false;
×
1260
  }
×
1261

1262
  expectProxyProtocol = clientState.d_enableProxyProtocol && expectProxyProtocolFrom(remote);
2,894✔
1263
  if (!dnsdist::configuration::getCurrentRuntimeConfiguration().d_ACL.match(remote) && !expectProxyProtocol) {
2,894!
1264
    vinfolog("Query from %s dropped because of ACL", remote.toStringWithPort());
1!
1265
    ++dnsdist::metrics::g_stats.aclDrops;
1✔
1266
    return false;
1✔
1267
  }
1✔
1268

1269
  if (HarvestDestinationAddress(msgh, &dest)) {
2,893✔
1270
    /* so it turns out that sometimes the kernel lies to us:
1271
       the address is set to 0.0.0.0:0 which makes our sendfromto() use
1272
       the wrong address. In that case it's better to let the kernel
1273
       do the work by itself and use sendto() instead.
1274
       This is indicated by setting the family to 0 which is acted upon
1275
       in sendUDPResponse() and DelayedPacket::().
1276
    */
1277
    static const ComboAddress bogusV4("0.0.0.0:0");
5✔
1278
    static const ComboAddress bogusV6("[::]:0");
5✔
1279
    if ((dest.sin4.sin_family == AF_INET && dest == bogusV4) || (dest.sin4.sin_family == AF_INET6 && dest == bogusV6)) {
5!
1280
      dest.sin4.sin_family = 0;
×
1281
    }
×
1282
    else {
5✔
1283
      /* we don't get the port, only the address */
1284
      dest.sin4.sin_port = clientState.local.sin4.sin_port;
5✔
1285
    }
5✔
1286
  }
5✔
1287
  else {
2,888✔
1288
    dest.sin4.sin_family = 0;
2,888✔
1289
  }
2,888✔
1290

1291
  ++clientState.queries;
2,893✔
1292
  ++dnsdist::metrics::g_stats.queries;
2,893✔
1293

1294
  return true;
2,893✔
1295
}
2,894✔
1296

1297
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)
1298
{
5,896✔
1299
  if (clientState.dnscryptCtx) {
5,896✔
1300
#ifdef HAVE_DNSCRYPT
59✔
1301
    PacketBuffer response;
59✔
1302
    dnsCryptQuery = std::make_unique<DNSCryptQuery>(clientState.dnscryptCtx);
59✔
1303

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

1306
    if (!decrypted) {
59✔
1307
      if (!response.empty()) {
23✔
1308
        query = std::move(response);
21✔
1309
        return true;
21✔
1310
      }
21✔
1311
      throw std::runtime_error("Unable to decrypt DNSCrypt query, dropping.");
2✔
1312
    }
23✔
1313
#endif /* HAVE_DNSCRYPT */
59✔
1314
  }
59✔
1315
  return false;
5,873✔
1316
}
5,896✔
1317

1318
bool checkQueryHeaders(const struct dnsheader& dnsHeader, ClientState& clientState)
1319
{
6,081✔
1320
  if (dnsHeader.qr) { // don't respond to responses
6,081✔
1321
    ++dnsdist::metrics::g_stats.nonCompliantQueries;
5✔
1322
    ++clientState.nonCompliantQueries;
5✔
1323
    return false;
5✔
1324
  }
5✔
1325

1326
  if (dnsHeader.qdcount == 0) {
6,076✔
1327
    ++dnsdist::metrics::g_stats.emptyQueries;
6✔
1328
    if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_dropEmptyQueries) {
6✔
1329
      return false;
2✔
1330
    }
2✔
1331
  }
6✔
1332

1333
  if (dnsHeader.rd) {
6,074✔
1334
    ++dnsdist::metrics::g_stats.rdQueries;
5,535✔
1335
  }
5,535✔
1336

1337
  return true;
6,074✔
1338
}
6,076✔
1339

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

1347
  if (dest.sin4.sin_family == 0) {
6!
1348
    outMsg.msg_hdr.msg_control = nullptr;
6✔
1349
  }
6✔
1350
  else {
×
1351
    addCMsgSrcAddr(&outMsg.msg_hdr, cbuf, &dest, 0);
×
1352
  }
×
1353
}
6✔
1354
#elif !defined(HAVE_RECVMMSG)
1355
struct mmsghdr
1356
{
1357
  msghdr msg_hdr;
1358
  unsigned int msg_len{0};
1359
};
1360
#endif
1361

1362
/* self-generated responses or cache hits */
1363
static bool prepareOutgoingResponse([[maybe_unused]] const ClientState& clientState, DNSQuestion& dnsQuestion, bool cacheHit)
1364
{
1,027✔
1365
  std::shared_ptr<DownstreamState> backend{nullptr};
1,027✔
1366
  DNSResponse dnsResponse(dnsQuestion.ids, dnsQuestion.getMutableData(), backend);
1,027✔
1367
  dnsResponse.d_incomingTCPState = dnsQuestion.d_incomingTCPState;
1,027✔
1368
  dnsResponse.ids.selfGenerated = true;
1,027✔
1369
  dnsResponse.ids.cacheHit = cacheHit;
1,027✔
1370

1371
  const auto& chains = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ruleChains;
1,027✔
1372
  const auto& cacheHitRespRules = dnsdist::rules::getResponseRuleChain(chains, dnsdist::rules::ResponseRuleChain::CacheHitResponseRules);
1,027✔
1373
  const auto& selfAnsweredRespRules = dnsdist::rules::getResponseRuleChain(chains, dnsdist::rules::ResponseRuleChain::SelfAnsweredResponseRules);
1,027✔
1374
  if (!applyRulesToResponse(cacheHit ? cacheHitRespRules : selfAnsweredRespRules, dnsResponse)) {
1,027✔
1375
    return false;
5✔
1376
  }
5✔
1377

1378
  if (dnsResponse.ids.ttlCap > 0) {
1,022!
1379
    dnsdist::PacketMangling::restrictDNSPacketTTLs(dnsResponse.getMutableData(), 0, dnsResponse.ids.ttlCap);
×
1380
  }
×
1381

1382
  if (dnsResponse.ids.d_extendedError) {
1,022✔
1383
    dnsdist::edns::addExtendedDNSError(dnsResponse.getMutableData(), dnsResponse.getMaximumSize(), dnsResponse.ids.d_extendedError->infoCode, dnsResponse.ids.d_extendedError->extraText);
8✔
1384
  }
8✔
1385

1386
  if (cacheHit) {
1,022✔
1387
    ++dnsdist::metrics::g_stats.cacheHits;
544✔
1388
  }
544✔
1389

1390
  if (dnsResponse.isAsynchronous()) {
1,022✔
1391
    return false;
12✔
1392
  }
12✔
1393

1394
#ifdef HAVE_DNSCRYPT
1,010✔
1395
  if (!clientState.muted) {
1,010!
1396
    if (!encryptResponse(dnsQuestion.getMutableData(), dnsQuestion.getMaximumSize(), dnsQuestion.overTCP(), dnsQuestion.ids.dnsCryptQuery)) {
1,010!
1397
      return false;
×
1398
    }
×
1399
  }
1,010✔
1400
#endif /* HAVE_DNSCRYPT */
1,010✔
1401

1402
  return true;
1,010✔
1403
}
1,010✔
1404

1405
static ProcessQueryResult handleQueryTurnedIntoSelfAnsweredResponse(DNSQuestion& dnsQuestion)
1406
{
469✔
1407
  fixUpQueryTurnedResponse(dnsQuestion, dnsQuestion.ids.origFlags);
469✔
1408

1409
  if (!prepareOutgoingResponse(*dnsQuestion.ids.cs, dnsQuestion, false)) {
469✔
1410
    return ProcessQueryResult::Drop;
2✔
1411
  }
2✔
1412

1413
  const auto rcode = dnsQuestion.getHeader()->rcode;
467✔
1414
  if (rcode == RCode::NXDomain) {
467✔
1415
    ++dnsdist::metrics::g_stats.ruleNXDomain;
26✔
1416
  }
26✔
1417
  else if (rcode == RCode::Refused) {
441✔
1418
    ++dnsdist::metrics::g_stats.ruleRefused;
123✔
1419
  }
123✔
1420
  else if (rcode == RCode::ServFail) {
318✔
1421
    ++dnsdist::metrics::g_stats.ruleServFail;
6✔
1422
  }
6✔
1423

1424
  ++dnsdist::metrics::g_stats.selfAnswered;
467✔
1425
  ++dnsQuestion.ids.cs->responses;
467✔
1426
  return ProcessQueryResult::SendAnswer;
467✔
1427
}
469✔
1428

1429
static void selectBackendForOutgoingQuery(DNSQuestion& dnsQuestion, const std::shared_ptr<ServerPool>& serverPool, std::shared_ptr<DownstreamState>& selectedBackend)
1430
{
5,857✔
1431
  std::shared_ptr<ServerPolicy> poolPolicy = serverPool->policy;
5,857✔
1432
  const auto& policy = poolPolicy != nullptr ? *poolPolicy : *dnsdist::configuration::getCurrentRuntimeConfiguration().d_lbPolicy;
5,857✔
1433
  const auto servers = serverPool->getServers();
5,857✔
1434
  selectedBackend = policy.getSelectedBackend(*servers, dnsQuestion);
5,857✔
1435
}
5,857✔
1436

1437
ProcessQueryResult processQueryAfterRules(DNSQuestion& dnsQuestion, std::shared_ptr<DownstreamState>& selectedBackend)
1438
{
6,329✔
1439
  const uint16_t queryId = ntohs(dnsQuestion.getHeader()->id);
6,329✔
1440

1441
  try {
6,329✔
1442
    if (dnsQuestion.getHeader()->qr) { // something turned it into a response
6,329✔
1443
      return handleQueryTurnedIntoSelfAnsweredResponse(dnsQuestion);
467✔
1444
    }
467✔
1445
    std::shared_ptr<ServerPool> serverPool = getPool(dnsQuestion.ids.poolName);
5,862✔
1446
    dnsQuestion.ids.packetCache = serverPool->packetCache;
5,862✔
1447
    selectBackendForOutgoingQuery(dnsQuestion, serverPool, selectedBackend);
5,862✔
1448
    bool willBeForwardedOverUDP = !dnsQuestion.overTCP() || dnsQuestion.ids.protocol == dnsdist::Protocol::DoH;
5,862✔
1449
    if (selectedBackend && selectedBackend->isTCPOnly()) {
5,862✔
1450
      willBeForwardedOverUDP = false;
374✔
1451
    }
374✔
1452
    else if (!selectedBackend) {
5,488✔
1453
      willBeForwardedOverUDP = !serverPool->isTCPOnly();
36✔
1454
    }
36✔
1455

1456
    uint32_t allowExpired = selectedBackend ? 0 : dnsdist::configuration::getCurrentRuntimeConfiguration().d_staleCacheEntriesTTL;
5,862✔
1457

1458
    if (dnsQuestion.ids.packetCache && !dnsQuestion.ids.skipCache && !dnsQuestion.ids.dnssecOK) {
5,862✔
1459
      dnsQuestion.ids.dnssecOK = (dnsdist::getEDNSZ(dnsQuestion) & EDNS_HEADER_FLAG_DO) != 0;
349✔
1460
    }
349✔
1461

1462
    if (dnsQuestion.useECS && ((selectedBackend && selectedBackend->d_config.useECS) || (!selectedBackend && serverPool->getECS()))) {
5,862✔
1463
      // we special case our cache in case a downstream explicitly gave us a universally valid response with a 0 scope
1464
      // we need ECS parsing (parseECS) to be true so we can be sure that the initial incoming query did not have an existing
1465
      // ECS option, which would make it unsuitable for the zero-scope feature.
1466
      if (dnsQuestion.ids.packetCache && !dnsQuestion.ids.skipCache && (!selectedBackend || !selectedBackend->d_config.disableZeroScope) && dnsQuestion.ids.packetCache->isECSParsingEnabled()) {
143!
1467
        if (dnsQuestion.ids.packetCache->get(dnsQuestion, dnsQuestion.getHeader()->id, &dnsQuestion.ids.cacheKeyNoECS, dnsQuestion.ids.subnet, *dnsQuestion.ids.dnssecOK, willBeForwardedOverUDP, allowExpired, false, true, false)) {
24✔
1468

1469
          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!
1470

1471
          if (!prepareOutgoingResponse(*dnsQuestion.ids.cs, dnsQuestion, true)) {
7!
1472
            return ProcessQueryResult::Drop;
×
1473
          }
×
1474

1475
          ++dnsdist::metrics::g_stats.responses;
7✔
1476
          ++dnsQuestion.ids.cs->responses;
7✔
1477
          return ProcessQueryResult::SendAnswer;
7✔
1478
        }
7✔
1479

1480
        if (!dnsQuestion.ids.subnet) {
17✔
1481
          /* there was no existing ECS on the query, enable the zero-scope feature */
1482
          dnsQuestion.ids.useZeroScope = true;
15✔
1483
        }
15✔
1484
      }
17✔
1485

1486
      if (!handleEDNSClientSubnet(dnsQuestion, dnsQuestion.ids.ednsAdded, dnsQuestion.ids.ecsAdded)) {
136!
1487
        vinfolog("Dropping query from %s because we couldn't insert the ECS value", dnsQuestion.ids.origRemote.toStringWithPort());
×
1488
        return ProcessQueryResult::Drop;
×
1489
      }
×
1490
    }
136✔
1491

1492
    if (dnsQuestion.ids.packetCache && !dnsQuestion.ids.skipCache) {
5,855✔
1493
      /* First lookup, which takes into account how the protocol over which the query will be forwarded.
1494
         For DoH, this lookup is done with the protocol set to TCP but we will retry over UDP below,
1495
         therefore we do not record a miss for queries received over DoH and forwarded over TCP
1496
         yet, as we will do a second-lookup */
1497
      if (dnsQuestion.ids.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)) {
793!
1498

1499
        dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [flags = dnsQuestion.ids.origFlags](dnsheader& header) {
506✔
1500
          restoreFlags(&header, flags);
506✔
1501
          return true;
506✔
1502
        });
506✔
1503

1504
        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());
506✔
1505

1506
        if (!prepareOutgoingResponse(*dnsQuestion.ids.cs, dnsQuestion, true)) {
506✔
1507
          return ProcessQueryResult::Drop;
11✔
1508
        }
11✔
1509

1510
        ++dnsdist::metrics::g_stats.responses;
495✔
1511
        ++dnsQuestion.ids.cs->responses;
495✔
1512
        return ProcessQueryResult::SendAnswer;
495✔
1513
      }
506✔
1514
      if (dnsQuestion.ids.protocol == dnsdist::Protocol::DoH && willBeForwardedOverUDP) {
287!
1515
        /* do a second-lookup for responses received over UDP, but we do not want TC=1 answers */
1516
        /* we need to be careful to keep the existing cache-key (TCP) */
1517
        if (dnsQuestion.ids.packetCache->get(dnsQuestion, dnsQuestion.getHeader()->id, &dnsQuestion.ids.cacheKey, dnsQuestion.ids.subnet, *dnsQuestion.ids.dnssecOK, true, allowExpired, false, false, true)) {
49✔
1518
          if (!prepareOutgoingResponse(*dnsQuestion.ids.cs, dnsQuestion, true)) {
34✔
1519
            return ProcessQueryResult::Drop;
4✔
1520
          }
4✔
1521

1522
          ++dnsdist::metrics::g_stats.responses;
30✔
1523
          ++dnsQuestion.ids.cs->responses;
30✔
1524
          return ProcessQueryResult::SendAnswer;
30✔
1525
        }
34✔
1526
      }
49✔
1527

1528
      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());
253✔
1529

1530
      ++dnsdist::metrics::g_stats.cacheMisses;
253✔
1531

1532
      // coverity[auto_causes_copy]
1533
      const auto existingPool = dnsQuestion.ids.poolName;
253✔
1534
      const auto& chains = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ruleChains;
253✔
1535
      const auto& cacheMissRuleActions = dnsdist::rules::getRuleChain(chains, dnsdist::rules::RuleChain::CacheMissRules);
253✔
1536

1537
      if (!applyRulesChainToQuery(cacheMissRuleActions, dnsQuestion)) {
253!
1538
        return ProcessQueryResult::Drop;
×
1539
      }
×
1540
      if (dnsQuestion.getHeader()->qr) { // something turned it into a response
253✔
1541
        return handleQueryTurnedIntoSelfAnsweredResponse(dnsQuestion);
2✔
1542
      }
2✔
1543
      /* let's be nice and allow the selection of a different pool,
1544
         but no second cache-lookup for you */
1545
      if (dnsQuestion.ids.poolName != existingPool) {
251✔
1546
        serverPool = getPool(dnsQuestion.ids.poolName);
1✔
1547
        dnsQuestion.ids.packetCache = serverPool->packetCache;
1✔
1548
        selectBackendForOutgoingQuery(dnsQuestion, serverPool, selectedBackend);
1✔
1549
      }
1✔
1550
    }
251✔
1551

1552
    if (!selectedBackend) {
5,313✔
1553
      auto servFailOnNoPolicy = dnsdist::configuration::getCurrentRuntimeConfiguration().d_servFailOnNoPolicy;
27✔
1554
      ++dnsdist::metrics::g_stats.noPolicy;
27✔
1555

1556
      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!
1557
      if (servFailOnNoPolicy) {
27✔
1558
        dnsdist::self_answers::removeRecordsAndSetRCode(dnsQuestion, RCode::ServFail);
11✔
1559

1560
        fixUpQueryTurnedResponse(dnsQuestion, dnsQuestion.ids.origFlags);
11✔
1561

1562
        if (!prepareOutgoingResponse(*dnsQuestion.ids.cs, dnsQuestion, false)) {
11!
1563
          return ProcessQueryResult::Drop;
×
1564
        }
×
1565
        ++dnsdist::metrics::g_stats.responses;
11✔
1566
        ++dnsQuestion.ids.cs->responses;
11✔
1567
        // no response-only statistics counter to update.
1568
        return ProcessQueryResult::SendAnswer;
11✔
1569
      }
11✔
1570

1571
      return ProcessQueryResult::Drop;
16✔
1572
    }
27✔
1573

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

1577
    if (selectedBackend->d_config.useProxyProtocol && dnsQuestion.getProtocol().isEncrypted() && selectedBackend->d_config.d_proxyProtocolAdvertiseTLS) {
5,286✔
1578
      if (!dnsQuestion.proxyProtocolValues) {
24!
1579
        dnsQuestion.proxyProtocolValues = std::make_unique<std::vector<ProxyProtocolValue>>();
×
1580
      }
×
1581
      dnsQuestion.proxyProtocolValues->push_back(ProxyProtocolValue{"", static_cast<uint8_t>(ProxyProtocolValue::Types::PP_TLV_SSL)});
24✔
1582
    }
24✔
1583

1584
    selectedBackend->incQueriesCount();
5,286✔
1585
    return ProcessQueryResult::PassToBackend;
5,286✔
1586
  }
5,313✔
1587
  catch (const std::exception& e) {
6,329✔
1588
    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!
1589
  }
6✔
1590
  return ProcessQueryResult::Drop;
6✔
1591
}
6,329✔
1592

1593
bool handleTimeoutResponseRules(const std::vector<dnsdist::rules::ResponseRuleAction>& rules, InternalQueryState& ids, const std::shared_ptr<DownstreamState>& d_ds, const std::shared_ptr<TCPQuerySender>& sender)
1594
{
33✔
1595
  /* let's be nice and restore the original DNS header as well as we can with what we have */
1596
  PacketBuffer payload(sizeof(dnsheader));
33✔
1597
  dnsdist::PacketMangling::editDNSHeaderFromPacket(payload, [&ids](dnsheader& header) {
33✔
1598
    memset(&header, 0, sizeof(header));
33✔
1599
    header.id = ids.origID;
33✔
1600
    restoreFlags(&header, ids.origFlags);
33✔
1601
    // do not set the qdcount, otherwise the protobuf code will choke on it
1602
    // while trying to parse the response RRs
1603
    return true;
33✔
1604
  });
33✔
1605
  DNSResponse dnsResponse(ids, payload, d_ds);
33✔
1606
  auto protocol = dnsResponse.getProtocol();
33✔
1607

1608
  vinfolog("Handling timeout response rules for incoming protocol = %s", protocol.toString());
33✔
1609
  if (protocol == dnsdist::Protocol::DoH) {
33✔
1610
#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
3✔
1611
    dnsResponse.d_incomingTCPState = std::dynamic_pointer_cast<IncomingHTTP2Connection>(sender);
3✔
1612
#endif
3✔
1613
    if (!dnsResponse.d_incomingTCPState || !sender || !sender->active()) {
3!
1614
      return false;
×
1615
    }
×
1616
  }
3✔
1617
  else if (protocol == dnsdist::Protocol::DoTCP || protocol == dnsdist::Protocol::DNSCryptTCP || protocol == dnsdist::Protocol::DoT) {
30!
1618
    dnsResponse.d_incomingTCPState = std::dynamic_pointer_cast<IncomingTCPConnectionState>(sender);
12✔
1619
    if (!dnsResponse.d_incomingTCPState || !sender || !sender->active()) {
12!
1620
      return false;
×
1621
    }
×
1622
  }
12✔
1623

1624
  try {
33✔
1625
    (void)applyRulesToResponse(rules, dnsResponse);
33✔
1626
  }
33✔
1627
  catch (const std::exception& exp) {
33✔
1628
    vinfolog("Exception while processing timeout response rules: %s", exp.what());
×
1629
  }
×
1630

1631
  return dnsResponse.isAsynchronous();
33✔
1632
}
33✔
1633

1634
class UDPTCPCrossQuerySender : public TCPQuerySender
1635
{
1636
public:
1637
  UDPTCPCrossQuerySender() = default;
763✔
1638
  UDPTCPCrossQuerySender(const UDPTCPCrossQuerySender&) = delete;
1639
  UDPTCPCrossQuerySender& operator=(const UDPTCPCrossQuerySender&) = delete;
1640
  UDPTCPCrossQuerySender(UDPTCPCrossQuerySender&&) = default;
1641
  UDPTCPCrossQuerySender& operator=(UDPTCPCrossQuerySender&&) = default;
1642
  ~UDPTCPCrossQuerySender() override = default;
1643

1644
  [[nodiscard]] bool active() const override
1645
  {
147✔
1646
    return true;
147✔
1647
  }
147✔
1648

1649
  void handleResponse(const struct timeval& now, TCPResponse&& response) override
1650
  {
170✔
1651
    (void)now;
170✔
1652
    if (!response.d_ds && !response.d_idstate.selfGenerated) {
170!
1653
      throw std::runtime_error("Passing a cross-protocol answer originated from UDP without a valid downstream");
×
1654
    }
×
1655

1656
    auto& ids = response.d_idstate;
170✔
1657

1658
    handleResponseForUDPClient(ids, response.d_buffer, response.d_ds, response.isAsync(), response.d_idstate.selfGenerated);
170✔
1659
  }
170✔
1660

1661
  void handleXFRResponse(const struct timeval& now, TCPResponse&& response) override
1662
  {
×
1663
    return handleResponse(now, std::move(response));
×
1664
  }
×
1665

1666
  void notifyIOError([[maybe_unused]] const struct timeval& now, [[maybe_unused]] TCPResponse&& response) override
1667
  {
12✔
1668
    // nothing to do
1669
  }
12✔
1670
};
1671

1672
class UDPCrossProtocolQuery : public CrossProtocolQuery
1673
{
1674
public:
1675
  UDPCrossProtocolQuery(PacketBuffer&& buffer_, InternalQueryState&& ids_, std::shared_ptr<DownstreamState> backend) :
1676
    CrossProtocolQuery(InternalQuery(std::move(buffer_), std::move(ids_)), backend)
503✔
1677
  {
503✔
1678
    auto& ids = query.d_idstate;
503✔
1679
    const auto& buffer = query.d_buffer;
503✔
1680

1681
    if (ids.udpPayloadSize == 0) {
503✔
1682
      uint16_t zValue = 0;
373✔
1683
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
1684
      getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(buffer.data()), buffer.size(), &ids.udpPayloadSize, &zValue);
373✔
1685
      if (!ids.dnssecOK) {
373!
1686
        ids.dnssecOK = (zValue & EDNS_HEADER_FLAG_DO) != 0;
×
1687
      }
×
1688
      if (ids.udpPayloadSize < 512) {
373!
1689
        ids.udpPayloadSize = 512;
373✔
1690
      }
373✔
1691
    }
373✔
1692
  }
503✔
1693
  UDPCrossProtocolQuery(const UDPCrossProtocolQuery&) = delete;
1694
  UDPCrossProtocolQuery& operator=(const UDPCrossProtocolQuery&) = delete;
1695
  UDPCrossProtocolQuery(UDPCrossProtocolQuery&&) = delete;
1696
  UDPCrossProtocolQuery& operator=(UDPCrossProtocolQuery&&) = delete;
1697
  ~UDPCrossProtocolQuery() override = default;
1698

1699
  std::shared_ptr<TCPQuerySender> getTCPQuerySender() override
1700
  {
184✔
1701
    return s_sender;
184✔
1702
  }
184✔
1703

1704
private:
1705
  static std::shared_ptr<UDPTCPCrossQuerySender> s_sender;
1706
};
1707

1708
std::shared_ptr<UDPTCPCrossQuerySender> UDPCrossProtocolQuery::s_sender = std::make_shared<UDPTCPCrossQuerySender>();
1709

1710
std::unique_ptr<CrossProtocolQuery> getUDPCrossProtocolQueryFromDQ(DNSQuestion& dnsQuestion);
1711
std::unique_ptr<CrossProtocolQuery> getUDPCrossProtocolQueryFromDQ(DNSQuestion& dnsQuestion)
1712
{
367✔
1713
  dnsQuestion.ids.origID = dnsQuestion.getHeader()->id;
367✔
1714
  return std::make_unique<UDPCrossProtocolQuery>(std::move(dnsQuestion.getMutableData()), std::move(dnsQuestion.ids), nullptr);
367✔
1715
}
367✔
1716

1717
ProcessQueryResult processQuery(DNSQuestion& dnsQuestion, std::shared_ptr<DownstreamState>& selectedBackend)
1718
{
6,066✔
1719
  const uint16_t queryId = ntohs(dnsQuestion.getHeader()->id);
6,066✔
1720

1721
  try {
6,066✔
1722
    /* we need an accurate ("real") value for the response and
1723
       to store into the IDS, but not for insertion into the
1724
       rings for example */
1725
    timespec now{};
6,066✔
1726
    gettime(&now);
6,066✔
1727

1728
    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,066✔
1729
      dnsdist::self_answers::removeRecordsAndSetRCode(dnsQuestion, RCode::NotImp);
8✔
1730
      return processQueryAfterRules(dnsQuestion, selectedBackend);
8✔
1731
    }
8✔
1732

1733
    if (!applyRulesToQuery(dnsQuestion, now)) {
6,058✔
1734
      return ProcessQueryResult::Drop;
47✔
1735
    }
47✔
1736

1737
    if (dnsQuestion.isAsynchronous()) {
6,011✔
1738
      return ProcessQueryResult::Asynchronous;
202✔
1739
    }
202✔
1740

1741
    return processQueryAfterRules(dnsQuestion, selectedBackend);
5,809✔
1742
  }
6,011✔
1743
  catch (const std::exception& e) {
6,066✔
1744
    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());
×
1745
  }
×
1746
  return ProcessQueryResult::Drop;
×
1747
}
6,066✔
1748

1749
bool assignOutgoingUDPQueryToBackend(std::shared_ptr<DownstreamState>& downstream, uint16_t queryID, DNSQuestion& dnsQuestion, PacketBuffer& query, bool actuallySend)
1750
{
2,645✔
1751
  bool doh = dnsQuestion.ids.du != nullptr;
2,645✔
1752

1753
  bool failed = false;
2,645✔
1754
  dnsQuestion.ids.d_proxyProtocolPayloadSize = 0;
2,645✔
1755
  if (downstream->d_config.useProxyProtocol) {
2,645✔
1756
    try {
24✔
1757
      size_t proxyProtocolPayloadSize = 0;
24✔
1758
      if (addProxyProtocol(dnsQuestion, &proxyProtocolPayloadSize)) {
24✔
1759
        dnsQuestion.ids.d_proxyProtocolPayloadSize += proxyProtocolPayloadSize;
22✔
1760
      }
22✔
1761
    }
24✔
1762
    catch (const std::exception& e) {
24✔
1763
      vinfolog("Adding proxy protocol payload to %s query from %s failed: %s", (dnsQuestion.ids.du ? "DoH" : ""), dnsQuestion.ids.origDest.toStringWithPort(), e.what());
2!
1764
      return false;
2✔
1765
    }
2✔
1766
  }
24✔
1767

1768
  if (doh && !dnsQuestion.ids.d_packet) {
2,643✔
1769
    dnsQuestion.ids.d_packet = std::make_unique<PacketBuffer>(query);
121✔
1770
  }
121✔
1771

1772
  try {
2,643✔
1773
    int descriptor = downstream->pickSocketForSending();
2,643✔
1774
    if (actuallySend) {
2,643!
1775
      dnsQuestion.ids.backendFD = descriptor;
2,643✔
1776
    }
2,643✔
1777
    dnsQuestion.ids.origID = queryID;
2,643✔
1778
    dnsQuestion.ids.forwardedOverUDP = true;
2,643✔
1779

1780
    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,643!
1781

1782
    /* make a copy since we cannot touch dnsQuestion.ids after the move */
1783
    auto proxyProtocolPayloadSize = dnsQuestion.ids.d_proxyProtocolPayloadSize;
2,643✔
1784
    auto idOffset = downstream->saveState(std::move(dnsQuestion.ids));
2,643✔
1785
    /* set the correct ID */
1786
    memcpy(&query.at(proxyProtocolPayloadSize), &idOffset, sizeof(idOffset));
2,643✔
1787

1788
    if (!actuallySend) {
2,643!
1789
      return true;
×
1790
    }
×
1791

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

1796
    if (ret < 0) {
2,643!
1797
      failed = true;
×
1798
    }
×
1799

1800
    if (failed) {
2,643!
1801
      /* clear up the state. In the very unlikely event it was reused
1802
         in the meantime, so be it. */
1803
      auto cleared = downstream->getState(idOffset);
×
1804
      if (cleared) {
×
1805
        dnsQuestion.ids.du = std::move(cleared->du);
×
1806
      }
×
1807
      ++dnsdist::metrics::g_stats.downstreamSendErrors;
×
1808
      ++downstream->sendErrors;
×
1809
      return false;
×
1810
    }
×
1811
  }
2,643✔
1812
  catch (const std::exception& e) {
2,643✔
1813
    throw;
×
1814
  }
×
1815

1816
  return true;
2,643✔
1817
}
2,643✔
1818

1819
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)
1820
{
2,894✔
1821
  assert(responsesVect == nullptr || (queuedResponses != nullptr && respIOV != nullptr && respCBuf != nullptr));
2,894!
1822
  uint16_t queryId = 0;
2,894✔
1823
  InternalQueryState ids;
2,894✔
1824
  ids.cs = &clientState;
2,894✔
1825
  ids.origRemote = remote;
2,894✔
1826
  ids.hopRemote = remote;
2,894✔
1827
  ids.protocol = dnsdist::Protocol::DoUDP;
2,894✔
1828

1829
  try {
2,894✔
1830
    bool expectProxyProtocol = false;
2,894✔
1831
    if (!isUDPQueryAcceptable(clientState, msgh, remote, dest, expectProxyProtocol)) {
2,894✔
1832
      return;
1✔
1833
    }
1✔
1834
    /* dest might have been updated, if we managed to harvest the destination address */
1835
    if (dest.sin4.sin_family != 0) {
2,893✔
1836
      ids.origDest = dest;
5✔
1837
      ids.hopLocal = dest;
5✔
1838
    }
5✔
1839
    else {
2,888✔
1840
      /* if we have not been able to harvest the destination address,
1841
         we do NOT want to update dest or hopLocal, to let the kernel
1842
         pick the less terrible option, but we want to update origDest
1843
         which is used by rules and actions to at least the correct
1844
         address family */
1845
      ids.origDest = clientState.local;
2,888✔
1846
      ids.hopLocal.sin4.sin_family = 0;
2,888✔
1847
    }
2,888✔
1848

1849
    std::vector<ProxyProtocolValue> proxyProtocolValues;
2,893✔
1850
    if (expectProxyProtocol && !handleProxyProtocol(remote, false, dnsdist::configuration::getCurrentRuntimeConfiguration().d_ACL, query, ids.origRemote, ids.origDest, proxyProtocolValues)) {
2,893✔
1851
      return;
1✔
1852
    }
1✔
1853

1854
    ids.queryRealTime.start();
2,892✔
1855

1856
    auto dnsCryptResponse = checkDNSCryptQuery(clientState, query, ids.dnsCryptQuery, ids.queryRealTime.d_start.tv_sec, false);
2,892✔
1857
    if (dnsCryptResponse) {
2,892✔
1858
      sendUDPResponse(clientState.udpFD, query, 0, dest, remote);
21✔
1859
      return;
21✔
1860
    }
21✔
1861

1862
    {
2,871✔
1863
      /* this pointer will be invalidated the second the buffer is resized, don't hold onto it! */
1864
      const dnsheader_aligned dnsHeader(query.data());
2,871✔
1865
      queryId = ntohs(dnsHeader->id);
2,871✔
1866

1867
      if (!checkQueryHeaders(*dnsHeader, clientState)) {
2,871✔
1868
        return;
1✔
1869
      }
1✔
1870

1871
      if (dnsHeader->qdcount == 0) {
2,870✔
1872
        dnsdist::PacketMangling::editDNSHeaderFromPacket(query, [](dnsheader& header) {
1✔
1873
          header.rcode = RCode::NotImp;
1✔
1874
          header.qr = true;
1✔
1875
          return true;
1✔
1876
        });
1✔
1877

1878
        sendUDPResponse(clientState.udpFD, query, 0, dest, remote);
1✔
1879
        return;
1✔
1880
      }
1✔
1881
    }
2,870✔
1882

1883
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
1884
    ids.qname = DNSName(reinterpret_cast<const char*>(query.data()), query.size(), sizeof(dnsheader), false, &ids.qtype, &ids.qclass);
2,869✔
1885
    if (ids.dnsCryptQuery) {
2,869✔
1886
      ids.protocol = dnsdist::Protocol::DNSCryptUDP;
20✔
1887
    }
20✔
1888
    DNSQuestion dnsQuestion(ids, query);
2,869✔
1889
    const uint16_t* flags = getFlagsFromDNSHeader(dnsQuestion.getHeader().get());
2,869✔
1890
    ids.origFlags = *flags;
2,869✔
1891

1892
    if (!proxyProtocolValues.empty()) {
2,869✔
1893
      dnsQuestion.proxyProtocolValues = make_unique<std::vector<ProxyProtocolValue>>(std::move(proxyProtocolValues));
6✔
1894
    }
6✔
1895

1896
    // save UDP payload size from origin query
1897
    uint16_t udpPayloadSize = 0;
2,869✔
1898
    uint16_t zValue = 0;
2,869✔
1899
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
1900
    getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(query.data()), query.size(), &udpPayloadSize, &zValue);
2,869✔
1901
    if (!ids.dnssecOK) {
2,869✔
1902
      ids.dnssecOK = (zValue & EDNS_HEADER_FLAG_DO) != 0;
2,865✔
1903
    }
2,865✔
1904
    if (udpPayloadSize < 512) {
2,869✔
1905
      udpPayloadSize = 512;
2,728✔
1906
    }
2,728✔
1907

1908
    std::shared_ptr<DownstreamState> backend{nullptr};
2,869✔
1909
    auto result = processQuery(dnsQuestion, backend);
2,869✔
1910

1911
    if (result == ProcessQueryResult::Drop || result == ProcessQueryResult::Asynchronous) {
2,869✔
1912
      return;
67✔
1913
    }
67✔
1914

1915
    // the buffer might have been invalidated by now (resized)
1916
    const auto dnsHeader = dnsQuestion.getHeader();
2,802✔
1917
    if (result == ProcessQueryResult::SendAnswer) {
2,802✔
1918
      /* ensure payload size is not exceeded */
1919
      handleResponseTC4UDPClient(dnsQuestion, udpPayloadSize, query);
459✔
1920
#ifndef DISABLE_RECVMMSG
459✔
1921
#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
459✔
1922
      if (dnsQuestion.ids.delayMsec == 0 && responsesVect != nullptr) {
459!
1923
        queueResponse(query, dest, remote, (*responsesVect)[*queuedResponses], respIOV, respCBuf);
6✔
1924
        (*queuedResponses)++;
6✔
1925
        handleResponseSent(dnsQuestion.ids.qname, dnsQuestion.ids.qtype, 0., remote, ComboAddress(), query.size(), *dnsHeader, dnsdist::Protocol::DoUDP, dnsdist::Protocol::DoUDP, false);
6✔
1926
        return;
6✔
1927
      }
6✔
1928
#endif /* defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE) */
453✔
1929
#endif /* DISABLE_RECVMMSG */
453✔
1930
      /* 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 */
1931
      sendUDPResponse(clientState.udpFD, query, dnsQuestion.ids.delayMsec, dest, remote);
453✔
1932

1933
      handleResponseSent(dnsQuestion.ids.qname, dnsQuestion.ids.qtype, 0., remote, ComboAddress(), query.size(), *dnsHeader, dnsdist::Protocol::DoUDP, dnsdist::Protocol::DoUDP, false);
453✔
1934
      return;
453✔
1935
    }
459✔
1936

1937
    if (result != ProcessQueryResult::PassToBackend || backend == nullptr) {
2,343!
1938
      return;
×
1939
    }
×
1940

1941
    if (backend->isTCPOnly()) {
2,343✔
1942
      std::string proxyProtocolPayload;
136✔
1943
      /* we need to do this _before_ creating the cross protocol query because
1944
         after that the buffer will have been moved */
1945
      if (backend->d_config.useProxyProtocol) {
136✔
1946
        proxyProtocolPayload = getProxyProtocolPayload(dnsQuestion);
1✔
1947
      }
1✔
1948

1949
      ids.origID = dnsHeader->id;
136✔
1950
      auto cpq = std::make_unique<UDPCrossProtocolQuery>(std::move(query), std::move(ids), backend);
136✔
1951
      cpq->query.d_proxyProtocolPayload = std::move(proxyProtocolPayload);
136✔
1952

1953
      backend->passCrossProtocolQuery(std::move(cpq));
136✔
1954
      return;
136✔
1955
    }
136✔
1956

1957
    assignOutgoingUDPQueryToBackend(backend, dnsHeader->id, dnsQuestion, query);
2,207✔
1958
  }
2,207✔
1959
  catch (const std::exception& e) {
2,894✔
1960
    vinfolog("Got an error in UDP question thread while parsing a query from %s, id %d: %s", ids.origRemote.toStringWithPort(), queryId, e.what());
4!
1961
  }
4✔
1962
}
2,894✔
1963

1964
#ifdef HAVE_XSK
1965
namespace dnsdist::xsk
1966
{
1967
bool XskProcessQuery(ClientState& clientState, XskPacket& packet)
1968
{
×
1969
  uint16_t queryId = 0;
×
1970
  const auto& remote = packet.getFromAddr();
×
1971
  const auto& dest = packet.getToAddr();
×
1972
  InternalQueryState ids;
×
1973
  ids.cs = &clientState;
×
1974
  ids.origRemote = remote;
×
1975
  ids.hopRemote = remote;
×
1976
  ids.origDest = dest;
×
1977
  ids.hopLocal = dest;
×
1978
  ids.protocol = dnsdist::Protocol::DoUDP;
×
1979
  ids.xskPacketHeader = packet.cloneHeaderToPacketBuffer();
×
1980

1981
  try {
×
1982
    bool expectProxyProtocol = false;
×
1983
    if (!XskIsQueryAcceptable(packet, clientState, expectProxyProtocol)) {
×
1984
      return false;
×
1985
    }
×
1986

1987
    auto query = packet.clonePacketBuffer();
×
1988
    std::vector<ProxyProtocolValue> proxyProtocolValues;
×
1989
    if (expectProxyProtocol && !handleProxyProtocol(remote, false, dnsdist::configuration::getCurrentRuntimeConfiguration().d_ACL, query, ids.origRemote, ids.origDest, proxyProtocolValues)) {
×
1990
      return false;
×
1991
    }
×
1992

1993
    ids.queryRealTime.start();
×
1994

1995
    auto dnsCryptResponse = checkDNSCryptQuery(clientState, query, ids.dnsCryptQuery, ids.queryRealTime.d_start.tv_sec, false);
×
1996
    if (dnsCryptResponse) {
×
1997
      packet.setPayload(query);
×
1998
      return true;
×
1999
    }
×
2000

2001
    {
×
2002
      /* this pointer will be invalidated the second the buffer is resized, don't hold onto it! */
2003
      dnsheader_aligned dnsHeader(query.data());
×
2004
      queryId = ntohs(dnsHeader->id);
×
2005

2006
      if (!checkQueryHeaders(*dnsHeader, clientState)) {
×
2007
        return false;
×
2008
      }
×
2009

2010
      if (dnsHeader->qdcount == 0) {
×
2011
        dnsdist::PacketMangling::editDNSHeaderFromPacket(query, [](dnsheader& header) {
×
2012
          header.rcode = RCode::NotImp;
×
2013
          header.qr = true;
×
2014
          return true;
×
2015
        });
×
2016
        packet.setPayload(query);
×
2017
        return true;
×
2018
      }
×
2019
    }
×
2020

2021
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
2022
    ids.qname = DNSName(reinterpret_cast<const char*>(query.data()), query.size(), sizeof(dnsheader), false, &ids.qtype, &ids.qclass);
×
2023
    if (ids.origDest.sin4.sin_family == 0) {
×
2024
      ids.origDest = clientState.local;
×
2025
    }
×
2026
    if (ids.dnsCryptQuery) {
×
2027
      ids.protocol = dnsdist::Protocol::DNSCryptUDP;
×
2028
    }
×
2029
    DNSQuestion dnsQuestion(ids, query);
×
2030
    if (!proxyProtocolValues.empty()) {
×
2031
      dnsQuestion.proxyProtocolValues = make_unique<std::vector<ProxyProtocolValue>>(std::move(proxyProtocolValues));
×
2032
    }
×
2033
    std::shared_ptr<DownstreamState> backend{nullptr};
×
2034
    auto result = processQuery(dnsQuestion, backend);
×
2035

2036
    if (result == ProcessQueryResult::Drop) {
×
2037
      return false;
×
2038
    }
×
2039

2040
    if (result == ProcessQueryResult::SendAnswer) {
×
2041
      packet.setPayload(query);
×
2042
      if (dnsQuestion.ids.delayMsec > 0) {
×
2043
        packet.addDelay(dnsQuestion.ids.delayMsec);
×
2044
      }
×
2045
      const auto dnsHeader = dnsQuestion.getHeader();
×
2046
      handleResponseSent(ids.qname, ids.qtype, 0., remote, ComboAddress(), query.size(), *dnsHeader, dnsdist::Protocol::DoUDP, dnsdist::Protocol::DoUDP, false);
×
2047
      return true;
×
2048
    }
×
2049

2050
    if (result != ProcessQueryResult::PassToBackend || backend == nullptr) {
×
2051
      return false;
×
2052
    }
×
2053

2054
    // the buffer might have been invalidated by now (resized)
2055
    const auto dnsHeader = dnsQuestion.getHeader();
×
2056
    if (backend->isTCPOnly()) {
×
2057
      std::string proxyProtocolPayload;
×
2058
      /* we need to do this _before_ creating the cross protocol query because
2059
         after that the buffer will have been moved */
2060
      if (backend->d_config.useProxyProtocol) {
×
2061
        proxyProtocolPayload = getProxyProtocolPayload(dnsQuestion);
×
2062
      }
×
2063

2064
      ids.origID = dnsHeader->id;
×
2065
      auto cpq = std::make_unique<UDPCrossProtocolQuery>(std::move(query), std::move(ids), backend);
×
2066
      cpq->query.d_proxyProtocolPayload = std::move(proxyProtocolPayload);
×
2067

2068
      backend->passCrossProtocolQuery(std::move(cpq));
×
2069
      return false;
×
2070
    }
×
2071

2072
    if (backend->d_xskInfos.empty()) {
×
2073
      assignOutgoingUDPQueryToBackend(backend, dnsHeader->id, dnsQuestion, query, true);
×
2074
      return false;
×
2075
    }
×
2076

2077
    assignOutgoingUDPQueryToBackend(backend, dnsHeader->id, dnsQuestion, query, false);
×
2078
    auto sourceAddr = backend->pickSourceAddressForSending();
×
2079
    packet.setAddr(sourceAddr, backend->d_config.sourceMACAddr, backend->d_config.remote, backend->d_config.destMACAddr);
×
2080
    packet.setPayload(query);
×
2081
    packet.rewrite();
×
2082
    return true;
×
2083
  }
×
2084
  catch (const std::exception& e) {
×
2085
    vinfolog("Got an error in UDP question thread while parsing a query from %s, id %d: %s", remote.toStringWithPort(), queryId, e.what());
×
2086
  }
×
2087
  return false;
×
2088
}
×
2089

2090
}
2091
#endif /* HAVE_XSK */
2092

2093
#ifndef DISABLE_RECVMMSG
2094
#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
2095
static void MultipleMessagesUDPClientThread(ClientState* clientState)
2096
{
2✔
2097
  struct MMReceiver
2✔
2098
  {
2✔
2099
    PacketBuffer packet;
2✔
2100
    ComboAddress remote;
2✔
2101
    ComboAddress dest;
2✔
2102
    iovec iov{};
2✔
2103
    /* used by HarvestDestinationAddress */
2104
    cmsgbuf_aligned cbuf{};
2✔
2105
  };
2✔
2106
  const size_t vectSize = dnsdist::configuration::getImmutableConfiguration().d_udpVectorSize;
2✔
2107

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

2112
  auto recvData = std::vector<MMReceiver>(vectSize);
2✔
2113
  auto msgVec = std::vector<mmsghdr>(vectSize);
2✔
2114
  auto outMsgVec = std::vector<mmsghdr>(vectSize);
2✔
2115

2116
  /* the actual buffer is larger because:
2117
     - we may have to add EDNS and/or ECS
2118
     - we use it for self-generated responses (from rule or cache)
2119
     but we only accept incoming payloads up to that size
2120
  */
2121
  const size_t initialBufferSize = getInitialUDPPacketBufferSize(clientState->d_enableProxyProtocol);
2✔
2122
  const size_t maxIncomingPacketSize = getMaximumIncomingPacketSize(*clientState);
2✔
2123

2124
  /* initialize the structures needed to receive our messages */
2125
  for (size_t idx = 0; idx < vectSize; idx++) {
22✔
2126
    recvData[idx].remote.sin4.sin_family = clientState->local.sin4.sin_family;
20✔
2127
    recvData[idx].packet.resize(initialBufferSize);
20✔
2128
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
2129
    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✔
2130
  }
20✔
2131

2132
  int msgsGot = static_cast<int>(vectSize);
2✔
2133
  /* go now */
2134
  for (;;) {
13✔
2135

2136
    /* reset the IO vector, since it's also used to send the vector of responses
2137
       to avoid having to copy the data around.
2138
       No need to reset the parts that have not been used, though. */
2139
    for (int idx = 0; idx < msgsGot; idx++) {
44✔
2140
      auto& slot = recvData[idx];
31✔
2141
      /* only resize if the buffer is actually smaller than expected */
2142
      if (slot.packet.size() < initialBufferSize) {
31✔
2143
        slot.packet.resize(initialBufferSize);
11✔
2144
      }
11✔
2145
      /* but we need to set the IOv pointer and size
2146
         anyway, because if we resized it the pointer might
2147
         now be invalid */
2148
      slot.iov.iov_base = &slot.packet.at(0);
31✔
2149
      slot.iov.iov_len = slot.packet.size();
31✔
2150
    }
31✔
2151

2152
    /* block until we have at least one message ready, but return
2153
       as many as possible to save the syscall costs */
2154
    msgsGot = recvmmsg(clientState->udpFD, msgVec.data(), vectSize, MSG_WAITFORONE | MSG_TRUNC, nullptr);
13✔
2155
    if (msgsGot <= 0) {
13!
2156
      vinfolog("Getting UDP messages via recvmmsg() failed with: %s", stringerror());
×
NEW
2157
      msgsGot = 0;
×
2158
      continue;
×
2159
    }
×
2160

2161
    unsigned int msgsToSend = 0;
13✔
2162

2163
    /* process the received messages */
2164
    for (int msgIdx = 0; msgIdx < msgsGot; msgIdx++) {
24✔
2165
      auto& msg = msgVec[msgIdx];
11✔
2166
      const struct msghdr* msgh = &msg.msg_hdr;
11✔
2167
      unsigned int got = msg.msg_len;
11✔
2168
      const ComboAddress& remote = recvData[msgIdx].remote;
11✔
2169

2170
      if (static_cast<size_t>(got) < sizeof(struct dnsheader)) {
11!
2171
        ++dnsdist::metrics::g_stats.nonCompliantQueries;
×
2172
        ++clientState->nonCompliantQueries;
×
2173
        continue;
×
2174
      }
×
2175

2176
      auto& data = recvData[msgIdx];
11✔
2177
      data.packet.resize(got);
11✔
2178
      dnsdist::configuration::refreshLocalRuntimeConfiguration();
11✔
2179
      processUDPQuery(*clientState, msgh, remote, data.dest, data.packet, &outMsgVec, &msgsToSend, &data.iov, &data.cbuf);
11✔
2180
    }
11✔
2181

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

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

2188
      if (sent < 0 || static_cast<unsigned int>(sent) != msgsToSend) {
6!
2189
        vinfolog("Error sending responses with sendmmsg() (%d on %u): %s", sent, msgsToSend, stringerror());
×
2190
      }
×
2191
    }
6✔
2192
  }
13✔
2193
}
2✔
2194
#endif /* defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE) */
2195
#endif /* DISABLE_RECVMMSG */
2196

2197
// listens to incoming queries, sends out to downstream servers, noting the intended return path
2198
static void udpClientThread(std::vector<ClientState*> states)
2199
{
376✔
2200
  try {
376✔
2201
    setThreadName("dnsdist/udpClie");
376✔
2202
#ifndef DISABLE_RECVMMSG
376✔
2203
#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
376✔
2204
    if (dnsdist::configuration::getImmutableConfiguration().d_udpVectorSize > 1) {
376✔
2205
      MultipleMessagesUDPClientThread(states.at(0));
2✔
2206
    }
2✔
2207
    else
374✔
2208
#endif /* defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE) */
374✔
2209
#endif /* DISABLE_RECVMMSG */
374✔
2210
    {
374✔
2211
      /* the actual buffer is larger because:
2212
         - we may have to add EDNS and/or ECS
2213
         - we use it for self-generated responses (from rule or cache)
2214
         but we only accept incoming payloads up to that size
2215
      */
2216
      struct UDPStateParam
374✔
2217
      {
374✔
2218
        ClientState* cs{nullptr};
374✔
2219
        size_t maxIncomingPacketSize{0};
374✔
2220
        int socket{-1};
374✔
2221
      };
374✔
2222
      const size_t initialBufferSize = getInitialUDPPacketBufferSize(true);
374✔
2223
      PacketBuffer packet(initialBufferSize);
374✔
2224

2225
      msghdr msgh{};
374✔
2226
      iovec iov{};
374✔
2227
      ComboAddress remote;
374✔
2228
      ComboAddress dest;
374✔
2229

2230
      auto handleOnePacket = [&packet, &iov, &msgh, &remote, &dest, initialBufferSize](const UDPStateParam& param) {
3,257✔
2231
        packet.resize(initialBufferSize);
3,257✔
2232
        iov.iov_base = &packet.at(0);
3,257✔
2233
        iov.iov_len = packet.size();
3,257✔
2234

2235
        ssize_t got = recvmsg(param.socket, &msgh, 0);
3,257✔
2236

2237
        if (got < 0 || static_cast<size_t>(got) < sizeof(struct dnsheader)) {
3,257!
2238
          ++dnsdist::metrics::g_stats.nonCompliantQueries;
×
2239
          ++param.cs->nonCompliantQueries;
×
2240
          return;
×
2241
        }
×
2242

2243
        packet.resize(static_cast<size_t>(got));
3,257✔
2244

2245
        dnsdist::configuration::refreshLocalRuntimeConfiguration();
3,257✔
2246
        processUDPQuery(*param.cs, &msgh, remote, dest, packet, nullptr, nullptr, nullptr, nullptr);
3,257✔
2247
      };
3,257✔
2248

2249
      std::vector<UDPStateParam> params;
374✔
2250
      for (auto& state : states) {
374✔
2251
        const size_t maxIncomingPacketSize = getMaximumIncomingPacketSize(*state);
374✔
2252
        params.emplace_back(UDPStateParam{state, maxIncomingPacketSize, state->udpFD});
374✔
2253
      }
374✔
2254

2255
      if (params.size() == 1) {
374!
2256
        const auto& param = params.at(0);
374✔
2257
        remote.sin4.sin_family = param.cs->local.sin4.sin_family;
374✔
2258
        /* used by HarvestDestinationAddress */
2259
        cmsgbuf_aligned cbuf;
374✔
2260
        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
2261
        fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), reinterpret_cast<char*>(&packet.at(0)), param.maxIncomingPacketSize, &remote);
374✔
2262
        while (true) {
3,257✔
2263
          try {
3,257✔
2264
            handleOnePacket(param);
3,257✔
2265
          }
3,257✔
2266
          catch (const std::bad_alloc& e) {
3,257✔
2267
            /* most exceptions are handled by handleOnePacket(), but we might be out of memory (std::bad_alloc)
2268
               in which case we DO NOT want to log (as it would trigger another memory allocation attempt
2269
               that might throw as well) but wait a bit (one millisecond) and then try to recover */
2270
            usleep(1000);
×
2271
          }
×
2272
        }
3,257✔
2273
      }
374✔
2274
      else {
×
2275
        auto callback = [&remote, &msgh, &iov, &packet, &handleOnePacket, initialBufferSize](int socket, FDMultiplexer::funcparam_t& funcparam) {
×
2276
          (void)socket;
×
2277
          const auto* param = boost::any_cast<const UDPStateParam*>(funcparam);
×
2278
          try {
×
2279
            remote.sin4.sin_family = param->cs->local.sin4.sin_family;
×
2280
            packet.resize(initialBufferSize);
×
2281
            /* used by HarvestDestinationAddress */
2282
            cmsgbuf_aligned cbuf;
×
2283
            // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
2284
            fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), reinterpret_cast<char*>(&packet.at(0)), param->maxIncomingPacketSize, &remote);
×
2285
            handleOnePacket(*param);
×
2286
          }
×
2287
          catch (const std::bad_alloc& e) {
×
2288
            /* most exceptions are handled by handleOnePacket(), but we might be out of memory (std::bad_alloc)
2289
               in which case we DO NOT want to log (as it would trigger another memory allocation attempt
2290
               that might throw as well) but wait a bit (one millisecond) and then try to recover */
2291
            usleep(1000);
×
2292
          }
×
2293
        };
×
2294
        auto mplexer = std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent(params.size()));
×
2295
        for (const auto& param : params) {
×
2296
          mplexer->addReadFD(param.socket, callback, &param);
×
2297
        }
×
2298

2299
        timeval now{};
×
2300
        while (true) {
×
2301
          mplexer->run(&now, -1);
×
2302
        }
×
2303
      }
×
2304
    }
374✔
2305
  }
376✔
2306
  catch (const std::exception& e) {
376✔
2307
    errlog("UDP client thread died because of exception: %s", e.what());
×
2308
  }
×
2309
  catch (const PDNSException& e) {
376✔
2310
    errlog("UDP client thread died because of PowerDNS exception: %s", e.reason);
×
2311
  }
×
2312
  catch (...) {
376✔
2313
    errlog("UDP client thread died because of an exception: %s", "unknown");
×
2314
  }
×
2315
}
376✔
2316

2317
static void maintThread()
2318
{
375✔
2319
  setThreadName("dnsdist/main");
375✔
2320
  constexpr int interval = 1;
375✔
2321
  size_t counter = 0;
375✔
2322
  int32_t secondsToWaitLog = 0;
375✔
2323

2324
  for (;;) {
1,560✔
2325
    std::this_thread::sleep_for(std::chrono::seconds(interval));
1,560✔
2326

2327
    dnsdist::configuration::refreshLocalRuntimeConfiguration();
1,560✔
2328
    {
1,560✔
2329
      auto lua = g_lua.lock();
1,560✔
2330
      try {
1,560✔
2331
        auto maintenanceCallback = lua->readVariable<boost::optional<std::function<void()>>>("maintenance");
1,560✔
2332
        if (maintenanceCallback) {
1,560✔
2333
          (*maintenanceCallback)();
329✔
2334
        }
329✔
2335
        dnsdist::lua::hooks::runMaintenanceHooks(*lua);
1,560✔
2336
#if !defined(DISABLE_DYNBLOCKS)
1,560✔
2337
        dnsdist::DynamicBlocks::runRegisteredGroups(*lua);
1,560✔
2338
#endif /* DISABLE_DYNBLOCKS */
1,560✔
2339
        secondsToWaitLog = 0;
1,560✔
2340
      }
1,560✔
2341
      catch (const std::exception& e) {
1,560✔
2342
        if (secondsToWaitLog <= 0) {
×
2343
          warnlog("Error during execution of maintenance function(s): %s", e.what());
×
2344
          secondsToWaitLog = 61;
×
2345
        }
×
2346
        secondsToWaitLog -= interval;
×
2347
      }
×
2348
    }
1,560✔
2349

2350
    counter++;
1,560✔
2351
    if (counter >= dnsdist::configuration::getCurrentRuntimeConfiguration().d_cacheCleaningDelay) {
1,185✔
2352
      /* keep track, for each cache, of whether we should keep
2353
       expired entries */
2354
      std::map<std::shared_ptr<DNSDistPacketCache>, bool> caches;
12✔
2355

2356
      /* gather all caches actually used by at least one pool, and see
2357
         if something prevents us from cleaning the expired entries */
2358
      const auto& pools = dnsdist::configuration::getCurrentRuntimeConfiguration().d_pools;
12✔
2359
      for (const auto& entry : pools) {
12✔
2360
        const auto& pool = entry.second;
12✔
2361

2362
        auto packetCache = pool->packetCache;
12✔
2363
        if (!packetCache) {
12!
2364
          continue;
×
2365
        }
×
2366

2367
        auto pair = caches.insert({packetCache, false});
12✔
2368
        auto& iter = pair.first;
12✔
2369
        /* if we need to keep stale data for this cache (ie, not clear
2370
           expired entries when at least one pool using this cache
2371
           has all its backends down) */
2372
        if (packetCache->keepStaleData() && !iter->second) {
12!
2373
          /* so far all pools had at least one backend up */
2374
          if (pool->countServers(true) == 0) {
5!
2375
            iter->second = true;
5✔
2376
          }
5✔
2377
        }
5✔
2378
      }
12✔
2379

2380
      const time_t now = time(nullptr);
12✔
2381
      for (const auto& pair : caches) {
12✔
2382
        /* shall we keep expired entries ? */
2383
        if (pair.second) {
12✔
2384
          continue;
5✔
2385
        }
5✔
2386
        const auto& packetCache = pair.first;
7✔
2387
        size_t upTo = (packetCache->getMaxEntries() * (100 - dnsdist::configuration::getCurrentRuntimeConfiguration().d_cacheCleaningPercentage)) / 100;
7✔
2388
        packetCache->purgeExpired(upTo, now);
7✔
2389
      }
7✔
2390
      counter = 0;
12✔
2391
    }
12✔
2392
  }
1,185✔
2393
}
375✔
2394

2395
#ifndef DISABLE_DYNBLOCKS
2396
static void dynBlockMaintenanceThread()
2397
{
375✔
2398
  setThreadName("dnsdist/dynBloc");
375✔
2399

2400
  dnsdist::configuration::refreshLocalRuntimeConfiguration();
375✔
2401
  DynBlockMaintenance::run();
375✔
2402
}
375✔
2403
#endif
2404

2405
#ifndef DISABLE_SECPOLL
2406
static void secPollThread()
2407
{
×
2408
  setThreadName("dnsdist/secpoll");
×
2409

2410
  for (;;) {
×
2411
    const auto& runtimeConfig = dnsdist::configuration::refreshLocalRuntimeConfiguration();
×
2412

2413
    try {
×
2414
      dnsdist::secpoll::doSecPoll(runtimeConfig.d_secPollSuffix);
×
2415
    }
×
2416
    catch (...) {
×
2417
    }
×
2418
    // coverity[store_truncates_time_t]
2419
    std::this_thread::sleep_for(std::chrono::seconds(runtimeConfig.d_secPollInterval));
×
2420
  }
×
2421
}
×
2422
#endif /* DISABLE_SECPOLL */
2423

2424
static std::atomic<bool> s_exiting{false};
2425
void doExitNicely(int exitCode = EXIT_SUCCESS);
2426

2427
static void checkExiting()
2428
{
1,933✔
2429
  if (s_exiting) {
1,933✔
2430
    doExitNicely();
375✔
2431
  }
375✔
2432
}
1,933✔
2433

2434
static void healthChecksThread()
2435
{
375✔
2436
  setThreadName("dnsdist/healthC");
375✔
2437

2438
  constexpr int intervalUsec = 1000 * 1000;
375✔
2439
  struct timeval lastRound{
375✔
2440
    .tv_sec = 0,
375✔
2441
    .tv_usec = 0};
375✔
2442

2443
  for (;;) {
1,933✔
2444
    try {
1,933✔
2445
      checkExiting();
1,933✔
2446

2447
      timeval now{};
1,933✔
2448
      gettimeofday(&now, nullptr);
1,933✔
2449
      auto elapsedTimeUsec = uSec(now - lastRound);
1,933✔
2450
      if (elapsedTimeUsec < intervalUsec) {
1,933✔
2451
        usleep(intervalUsec - elapsedTimeUsec);
1,182✔
2452
        gettimeofday(&lastRound, nullptr);
1,182✔
2453
      }
1,182✔
2454
      else {
751✔
2455
        lastRound = now;
751✔
2456
      }
751✔
2457

2458
      std::unique_ptr<FDMultiplexer> mplexer{nullptr};
1,933✔
2459
      const auto& runtimeConfig = dnsdist::configuration::refreshLocalRuntimeConfiguration();
1,933✔
2460

2461
      // this points to the actual shared_ptrs!
2462
      // coverity[auto_causes_copy]
2463
      const auto servers = runtimeConfig.d_backends;
1,933✔
2464
      for (const auto& dss : servers) {
1,933✔
2465
        dss->updateStatisticsInfo();
1,930✔
2466

2467
        dss->handleUDPTimeouts();
1,930✔
2468

2469
        if (!dss->healthCheckRequired()) {
1,930✔
2470
          continue;
523✔
2471
        }
523✔
2472

2473
        if (!mplexer) {
1,407✔
2474
          mplexer = std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent(servers.size()));
1,350✔
2475
        }
1,350✔
2476

2477
        if (!queueHealthCheck(mplexer, dss)) {
1,407✔
2478
          dss->submitHealthCheckResult(false, false);
22✔
2479
        }
22✔
2480
      }
1,407✔
2481

2482
      if (mplexer) {
1,933✔
2483
        handleQueuedHealthChecks(*mplexer);
1,350✔
2484
      }
1,350✔
2485
    }
1,933✔
2486
    catch (const std::exception& exp) {
1,933✔
2487
      vinfolog("Exception in the health-check thread: %s", exp.what());
×
2488
    }
×
2489
  }
1,933✔
2490
}
375✔
2491

2492
static void bindAny([[maybe_unused]] int addressFamily, [[maybe_unused]] int sock)
2493
{
893✔
2494
  __attribute__((unused)) int one = 1;
893✔
2495

2496
#ifdef IP_FREEBIND
893✔
2497
  if (setsockopt(sock, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0) {
893!
2498
    warnlog("Warning: IP_FREEBIND setsockopt failed: %s", stringerror());
×
2499
  }
×
2500
#endif
893✔
2501

2502
#ifdef IP_BINDANY
2503
  if (addressFamily == AF_INET) {
2504
    if (setsockopt(sock, IPPROTO_IP, IP_BINDANY, &one, sizeof(one)) < 0) {
2505
      warnlog("Warning: IP_BINDANY setsockopt failed: %s", stringerror());
2506
    }
2507
  }
2508
#endif
2509
#ifdef IPV6_BINDANY
2510
  if (addressFamily == AF_INET6) {
2511
    if (setsockopt(sock, IPPROTO_IPV6, IPV6_BINDANY, &one, sizeof(one)) < 0) {
2512
      warnlog("Warning: IPV6_BINDANY setsockopt failed: %s", stringerror());
2513
    }
2514
  }
2515
#endif
2516
#ifdef SO_BINDANY
2517
  if (setsockopt(sock, SOL_SOCKET, SO_BINDANY, &one, sizeof(one)) < 0) {
2518
    warnlog("Warning: SO_BINDANY setsockopt failed: %s", stringerror());
2519
  }
2520
#endif
2521
}
893✔
2522

2523
static void dropGroupPrivs(gid_t gid)
2524
{
×
2525
  if (gid != 0) {
×
2526
    if (setgid(gid) == 0) {
×
2527
      if (setgroups(0, nullptr) < 0) {
×
2528
        warnlog("Warning: Unable to drop supplementary gids: %s", stringerror());
×
2529
      }
×
2530
    }
×
2531
    else {
×
2532
      warnlog("Warning: Unable to set group ID to %d: %s", gid, stringerror());
×
2533
    }
×
2534
  }
×
2535
}
×
2536

2537
static void dropUserPrivs(uid_t uid)
2538
{
×
2539
  if (uid != 0) {
×
2540
    if (setuid(uid) < 0) {
×
2541
      warnlog("Warning: Unable to set user ID to %d: %s", uid, stringerror());
×
2542
    }
×
2543
  }
×
2544
}
×
2545

2546
static void checkFileDescriptorsLimits(size_t udpBindsCount, size_t tcpBindsCount)
2547
{
375✔
2548
  const auto& immutableConfig = dnsdist::configuration::getImmutableConfiguration();
375✔
2549
  /* stdin, stdout, stderr */
2550
  rlim_t requiredFDsCount = 3;
375✔
2551
  const auto& backends = dnsdist::configuration::getCurrentRuntimeConfiguration().d_backends;
375✔
2552
  /* UDP sockets to backends */
2553
  size_t backendUDPSocketsCount = 0;
375✔
2554
  for (const auto& backend : backends) {
419✔
2555
    backendUDPSocketsCount += backend->sockets.size();
419✔
2556
  }
419✔
2557
  requiredFDsCount += backendUDPSocketsCount;
375✔
2558
  /* TCP sockets to backends */
2559
  if (immutableConfig.d_maxTCPClientThreads > 0) {
375!
2560
    requiredFDsCount += (backends.size() * immutableConfig.d_maxTCPClientThreads);
375✔
2561
  }
375✔
2562
  /* listening sockets */
2563
  requiredFDsCount += udpBindsCount;
375✔
2564
  requiredFDsCount += tcpBindsCount;
375✔
2565
  /* number of TCP connections currently served, assuming 1 connection per worker thread which is of course not right */
2566
  if (immutableConfig.d_maxTCPClientThreads > 0) {
375!
2567
    requiredFDsCount += immutableConfig.d_maxTCPClientThreads;
375✔
2568
    /* max pipes for communicating between TCP acceptors and client threads */
2569
    requiredFDsCount += (immutableConfig.d_maxTCPClientThreads * 2);
375✔
2570
  }
375✔
2571
  /* max TCP queued connections */
2572
  requiredFDsCount += immutableConfig.d_maxTCPQueuedConnections;
375✔
2573
  /* DelayPipe pipe */
2574
  requiredFDsCount += 2;
375✔
2575
  /* syslog socket */
2576
  requiredFDsCount++;
375✔
2577
  /* webserver main socket */
2578
  requiredFDsCount++;
375✔
2579
  /* console main socket */
2580
  requiredFDsCount++;
375✔
2581
  /* carbon export */
2582
  requiredFDsCount++;
375✔
2583
  /* history file */
2584
  requiredFDsCount++;
375✔
2585
  rlimit resourceLimits{};
375✔
2586
  getrlimit(RLIMIT_NOFILE, &resourceLimits);
375✔
2587
  if (resourceLimits.rlim_cur <= requiredFDsCount) {
375✔
2588
    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✔
2589
#ifdef HAVE_SYSTEMD
2✔
2590
    warnlog("You can increase this value by using LimitNOFILE= in the systemd unit file or ulimit.");
2✔
2591
#else
2592
    warnlog("You can increase this value by using ulimit.");
2593
#endif
2594
  }
2✔
2595
}
375✔
2596

2597
static void setupLocalSocket(ClientState& clientState, const ComboAddress& addr, int& socket, bool tcp, bool warn)
2598
{
893✔
2599
  const auto& immutableConfig = dnsdist::configuration::getImmutableConfiguration();
893✔
2600
  static bool s_warned_ipv6_recvpktinfo = false;
893✔
2601
  (void)warn;
893✔
2602
  socket = SSocket(addr.sin4.sin_family, !tcp ? SOCK_DGRAM : SOCK_STREAM, 0);
893✔
2603

2604
  if (tcp) {
893✔
2605
    SSetsockopt(socket, SOL_SOCKET, SO_REUSEADDR, 1);
480✔
2606
#ifdef TCP_DEFER_ACCEPT
480✔
2607
    SSetsockopt(socket, IPPROTO_TCP, TCP_DEFER_ACCEPT, 1);
480✔
2608
#endif
480✔
2609
    if (clientState.fastOpenQueueSize > 0) {
480!
2610
#ifdef TCP_FASTOPEN
×
2611
      SSetsockopt(socket, IPPROTO_TCP, TCP_FASTOPEN, clientState.fastOpenQueueSize);
×
2612
#ifdef TCP_FASTOPEN_KEY
×
2613
      if (!immutableConfig.d_tcpFastOpenKey.empty()) {
×
2614
        auto res = setsockopt(socket, IPPROTO_IP, TCP_FASTOPEN_KEY, immutableConfig.d_tcpFastOpenKey.data(), immutableConfig.d_tcpFastOpenKey.size() * sizeof(immutableConfig.d_tcpFastOpenKey[0]));
×
2615
        if (res == -1) {
×
2616
          throw runtime_error("setsockopt for level IPPROTO_TCP and opname TCP_FASTOPEN_KEY failed: " + stringerror());
×
2617
        }
×
2618
      }
×
2619
#endif /* TCP_FASTOPEN_KEY */
×
2620
#else /* TCP_FASTOPEN */
2621
      if (warn) {
2622
        warnlog("TCP Fast Open has been configured on local address '%s' but is not supported", addr.toStringWithPort());
2623
      }
2624
#endif /* TCP_FASTOPEN */
2625
    }
×
2626
  }
480✔
2627

2628
  if (addr.sin4.sin_family == AF_INET6) {
893✔
2629
    SSetsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, 1);
4✔
2630
  }
4✔
2631

2632
  bindAny(addr.sin4.sin_family, socket);
893✔
2633

2634
  if (!tcp && IsAnyAddress(addr)) {
893✔
2635
    int one = 1;
7✔
2636
    (void)setsockopt(socket, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)); // linux supports this, so why not - might fail on other systems
7✔
2637
#ifdef IPV6_RECVPKTINFO
7✔
2638
    if (addr.isIPv6() && setsockopt(socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)) < 0 && !s_warned_ipv6_recvpktinfo) {
7!
2639
      warnlog("Warning: IPV6_RECVPKTINFO setsockopt failed: %s", stringerror());
×
2640
      s_warned_ipv6_recvpktinfo = true;
×
2641
    }
×
2642
#endif
7✔
2643
  }
7✔
2644

2645
  if (clientState.reuseport) {
893✔
2646
    if (!setReusePort(socket)) {
18!
2647
      if (warn) {
×
2648
        /* no need to warn again if configured but support is not available, we already did for UDP */
2649
        warnlog("SO_REUSEPORT has been configured on local address '%s' but is not supported", addr.toStringWithPort());
×
2650
      }
×
2651
    }
×
2652
  }
18✔
2653

2654
  const bool isQUIC = clientState.doqFrontend != nullptr || clientState.doh3Frontend != nullptr;
893✔
2655
  if (isQUIC) {
893✔
2656
    /* disable fragmentation and force PMTU discovery for QUIC-enabled sockets */
2657
    try {
37✔
2658
      setSocketForcePMTU(socket, addr.sin4.sin_family);
37✔
2659
    }
37✔
2660
    catch (const std::exception& e) {
37✔
2661
      warnlog("Failed to set IP_MTU_DISCOVER on QUIC server socket for local address '%s': %s", addr.toStringWithPort(), e.what());
×
2662
    }
×
2663
  }
37✔
2664
  else if (!tcp && !clientState.dnscryptCtx) {
856✔
2665
    /* Only set this on IPv4 UDP sockets.
2666
       Don't set it for DNSCrypt binds. DNSCrypt pads queries for privacy
2667
       purposes, so we do receive large, sometimes fragmented datagrams. */
2668
    try {
371✔
2669
      setSocketIgnorePMTU(socket, addr.sin4.sin_family);
371✔
2670
    }
371✔
2671
    catch (const std::exception& e) {
371✔
2672
      warnlog("Failed to set IP_MTU_DISCOVER on UDP server socket for local address '%s': %s", addr.toStringWithPort(), e.what());
×
2673
    }
×
2674
  }
371✔
2675

2676
  if (!tcp) {
893✔
2677
    if (immutableConfig.d_socketUDPSendBuffer > 0) {
413!
2678
      try {
×
2679
        setSocketSendBuffer(socket, immutableConfig.d_socketUDPSendBuffer);
×
2680
      }
×
2681
      catch (const std::exception& e) {
×
2682
        warnlog(e.what());
×
2683
      }
×
2684
    }
×
2685
    else {
413✔
2686
      try {
413✔
2687
        auto result = raiseSocketSendBufferToMax(socket);
413✔
2688
        if (result > 0) {
413!
2689
          infolog("Raised send buffer to %u for local address '%s'", result, addr.toStringWithPort());
413✔
2690
        }
413✔
2691
      }
413✔
2692
      catch (const std::exception& e) {
413✔
2693
        warnlog(e.what());
×
2694
      }
×
2695
    }
413✔
2696

2697
    if (immutableConfig.d_socketUDPRecvBuffer > 0) {
413!
2698
      try {
×
2699
        setSocketReceiveBuffer(socket, immutableConfig.d_socketUDPRecvBuffer);
×
2700
      }
×
2701
      catch (const std::exception& e) {
×
2702
        warnlog(e.what());
×
2703
      }
×
2704
    }
×
2705
    else {
413✔
2706
      try {
413✔
2707
        auto result = raiseSocketReceiveBufferToMax(socket);
413✔
2708
        if (result > 0) {
413!
2709
          infolog("Raised receive buffer to %u for local address '%s'", result, addr.toStringWithPort());
413✔
2710
        }
413✔
2711
      }
413✔
2712
      catch (const std::exception& e) {
413✔
2713
        warnlog(e.what());
×
2714
      }
×
2715
    }
413✔
2716
  }
413✔
2717

2718
  const std::string& itf = clientState.interface;
893✔
2719
  if (!itf.empty()) {
893✔
2720
#ifdef SO_BINDTODEVICE
2✔
2721
    int res = setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, itf.c_str(), itf.length());
2✔
2722
    if (res != 0) {
2!
2723
      warnlog("Error setting up the interface on local address '%s': %s", addr.toStringWithPort(), stringerror());
×
2724
    }
×
2725
#else
2726
    if (warn) {
2727
      warnlog("An interface has been configured on local address '%s' but SO_BINDTODEVICE is not supported", addr.toStringWithPort());
2728
    }
2729
#endif
2730
  }
2✔
2731

2732
#ifdef HAVE_EBPF
893✔
2733
  /* for now eBPF filtering is not enabled on QUIC sockets because the eBPF code tries
2734
     to parse the QNAME from the payload for all UDP datagrams, which obviously does not
2735
     work well for these. */
2736
  if (!isQUIC && g_defaultBPFFilter && !g_defaultBPFFilter->isExternal()) {
893!
2737
    clientState.attachFilter(g_defaultBPFFilter, socket);
6✔
2738
    vinfolog("Attaching default BPF Filter to %s frontend %s", (!tcp ? std::string("UDP") : std::string("TCP")), addr.toStringWithPort());
6!
2739
  }
6✔
2740
#endif /* HAVE_EBPF */
893✔
2741

2742
  SBind(socket, addr);
893✔
2743

2744
  if (tcp) {
893✔
2745
    SListen(socket, clientState.tcpListenQueueSize);
480✔
2746

2747
    if (clientState.tlsFrontend != nullptr) {
480✔
2748
      infolog("Listening on %s for TLS", addr.toStringWithPort());
43✔
2749
    }
43✔
2750
    else if (clientState.dohFrontend != nullptr) {
437✔
2751
      infolog("Listening on %s for DoH", addr.toStringWithPort());
61✔
2752
    }
61✔
2753
    else if (clientState.dnscryptCtx != nullptr) {
376✔
2754
      infolog("Listening on %s for DNSCrypt", addr.toStringWithPort());
5✔
2755
    }
5✔
2756
    else {
371✔
2757
      infolog("Listening on %s", addr.toStringWithPort());
371✔
2758
    }
371✔
2759
  }
480✔
2760
  else {
413✔
2761
    if (clientState.doqFrontend != nullptr) {
413✔
2762
      infolog("Listening on %s for DoQ", addr.toStringWithPort());
20✔
2763
    }
20✔
2764
    else if (clientState.doh3Frontend != nullptr) {
393✔
2765
      infolog("Listening on %s for DoH3", addr.toStringWithPort());
17✔
2766
    }
17✔
2767
#ifdef HAVE_XSK
376✔
2768
    else if (clientState.xskInfo != nullptr) {
376!
2769
      infolog("Listening on %s (XSK-enabled)", addr.toStringWithPort());
×
2770
    }
×
2771
#endif
413✔
2772
  }
413✔
2773
}
893✔
2774

2775
static void setUpLocalBind(ClientState& cstate)
2776
{
893✔
2777
  /* skip some warnings if there is an identical UDP context */
2778
  bool warn = !cstate.tcp || cstate.tlsFrontend != nullptr || cstate.dohFrontend != nullptr;
893✔
2779
  int& descriptor = !cstate.tcp ? cstate.udpFD : cstate.tcpFD;
893✔
2780
  (void)warn;
893✔
2781

2782
  setupLocalSocket(cstate, cstate.local, descriptor, cstate.tcp, warn);
893✔
2783

2784
  for (auto& [addr, socket] : cstate.d_additionalAddresses) {
893!
2785
    setupLocalSocket(cstate, addr, socket, true, false);
×
2786
  }
×
2787

2788
  if (cstate.tlsFrontend != nullptr) {
893✔
2789
    if (!cstate.tlsFrontend->setupTLS()) {
43!
2790
      errlog("Error while setting up TLS on local address '%s', exiting", cstate.local.toStringWithPort());
×
2791
      _exit(EXIT_FAILURE);
×
2792
    }
×
2793
  }
43✔
2794

2795
  if (cstate.dohFrontend != nullptr) {
893✔
2796
    cstate.dohFrontend->setup();
61✔
2797
  }
61✔
2798
  if (cstate.doqFrontend != nullptr) {
893✔
2799
    cstate.doqFrontend->setup();
20✔
2800
  }
20✔
2801
  if (cstate.doh3Frontend != nullptr) {
893✔
2802
    cstate.doh3Frontend->setup();
17✔
2803
  }
17✔
2804

2805
  cstate.ready = true;
893✔
2806
}
893✔
2807

2808
struct CommandLineParameters
2809
{
2810
  vector<string> locals;
2811
  vector<string> remotes;
2812
  bool checkConfig{false};
2813
  bool beClient{false};
2814
  bool beSupervised{false};
2815
  string command;
2816
  string config;
2817
  string uid;
2818
  string gid;
2819
};
2820

2821
static void usage()
2822
{
×
2823
  cout << endl;
×
2824
  cout << "Syntax: dnsdist [-C,--config file] [-c,--client [IP[:PORT]]]\n";
×
2825
  cout << "[-e,--execute cmd] [-h,--help] [-l,--local addr]\n";
×
2826
  cout << "[-v,--verbose] [--check-config] [--version]\n";
×
2827
  cout << "\n";
×
2828
  cout << "-a,--acl netmask      Add this netmask to the ACL\n";
×
2829
  cout << "-C,--config file      Load configuration from 'file'\n";
×
2830
  cout << "-c,--client           Operate as a client, connect to dnsdist. This reads\n";
×
2831
  cout << "                      controlSocket from your configuration file, but also\n";
×
2832
  cout << "                      accepts an IP:PORT argument\n";
×
2833
#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO)
×
2834
  cout << "-k,--setkey KEY       Use KEY for encrypted communication to dnsdist. This\n";
×
2835
  cout << "                      is similar to setting setKey in the configuration file.\n";
×
2836
  cout << "                      NOTE: this will leak this key in your shell's history\n";
×
2837
  cout << "                      and in the systems running process list.\n";
×
2838
#endif
×
2839
  cout << "--check-config        Validate the configuration file and exit. The exit-code\n";
×
2840
  cout << "                      reflects the validation, 0 is OK, 1 means an error.\n";
×
2841
  cout << "                      Any errors are printed as well.\n";
×
2842
  cout << "-e,--execute cmd      Connect to dnsdist and execute 'cmd'\n";
×
2843
  cout << "-g,--gid gid          Change the process group ID after binding sockets\n";
×
2844
  cout << "-h,--help             Display this helpful message\n";
×
2845
  cout << "-l,--local address    Listen on this local address\n";
×
2846
  cout << "--supervised          Don't open a console, I'm supervised\n";
×
2847
  cout << "                        (use with e.g. systemd and daemontools)\n";
×
2848
  cout << "--disable-syslog      Don't log to syslog, only to stdout\n";
×
2849
  cout << "                        (use with e.g. systemd)\n";
×
2850
  cout << "--log-timestamps      Prepend timestamps to messages logged to stdout.\n";
×
2851
  cout << "-u,--uid uid          Change the process user ID after binding sockets\n";
×
2852
  cout << "-v,--verbose          Enable verbose mode\n";
×
2853
  cout << "-V,--version          Show dnsdist version information and exit\n";
×
2854
}
×
2855

2856
/* g++ defines __SANITIZE_THREAD__
2857
   clang++ supports the nice __has_feature(thread_sanitizer),
2858
   let's merge them */
2859
#if defined(__has_feature)
2860
#if __has_feature(thread_sanitizer)
2861
#define __SANITIZE_THREAD__ 1
2862
#endif
2863
#if __has_feature(address_sanitizer)
2864
#define __SANITIZE_ADDRESS__ 1
2865
#endif
2866
#endif
2867

2868
#if defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE)
2869
#include <sanitizer/lsan_interface.h>
2870
#endif
2871

2872
#if defined(COVERAGE) || (defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE))
2873
static void cleanupLuaObjects(LuaContext& /* luaCtx */)
2874
{
755✔
2875
  dnsdist::lua::hooks::clearExitCallbacks();
755✔
2876
  /* when our coverage mode is enabled, we need to make sure
2877
     that the Lua objects are destroyed before the Lua contexts. */
2878
  dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
755✔
2879
    config.d_ruleChains = dnsdist::rules::RuleChains();
755✔
2880
    config.d_lbPolicy = std::make_shared<ServerPolicy>();
755✔
2881
    config.d_pools.clear();
755✔
2882
    config.d_backends.clear();
755✔
2883
  });
755✔
2884
  dnsdist::webserver::clearWebHandlers();
755✔
2885
  dnsdist::lua::hooks::clearMaintenanceHooks();
755✔
2886
}
755✔
2887
#endif /* defined(COVERAGE) || (defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE)) */
2888

2889
void doExitNicely(int exitCode)
2890
{
755✔
2891
  if (s_exiting) {
755✔
2892
    if (dnsdist::logging::LoggingConfiguration::getSyslog()) {
375!
2893
      syslog(LOG_INFO, "Exiting on user request");
375✔
2894
    }
375✔
2895
    std::cout << "Exiting on user request" << std::endl;
375✔
2896
  }
375✔
2897

2898
#ifdef HAVE_SYSTEMD
755✔
2899
  sd_notify(0, "STOPPING=1");
755✔
2900
#endif /* HAVE_SYSTEMD */
755✔
2901

2902
#if defined(COVERAGE) || (defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE))
755✔
2903
  if (dnsdist::g_asyncHolder) {
755✔
2904
    dnsdist::g_asyncHolder->stop();
375✔
2905
  }
375✔
2906

2907
  for (auto& backend : dnsdist::configuration::getCurrentRuntimeConfiguration().d_backends) {
842✔
2908
    backend->stop();
842✔
2909
  }
842✔
2910
#endif
755✔
2911

2912
  {
755✔
2913
    auto lock = g_lua.lock();
755✔
2914
    dnsdist::lua::hooks::runExitCallbacks(*lock);
755✔
2915
#if defined(COVERAGE) || (defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE))
755✔
2916
    cleanupLuaObjects(*lock);
755✔
2917
    *lock = LuaContext();
755✔
2918
#endif
755✔
2919
  }
755✔
2920

2921
#if defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE)
755✔
2922
  __lsan_do_leak_check();
755✔
2923
#endif /* __SANITIZE_ADDRESS__ && HAVE_LEAK_SANITIZER_INTERFACE */
755✔
2924

2925
#ifdef COVERAGE
755✔
2926
  pdns::coverage::dumpCoverageData();
755✔
2927
#endif
755✔
2928

2929
  /* do not call destructors, because we have some
2930
     dependencies between objects that are not trivial
2931
     to solve.
2932
  */
2933
  _exit(exitCode);
755✔
2934
}
755✔
2935

2936
static void sigTermHandler(int /* sig */)
2937
{
375✔
2938
  s_exiting.store(true);
375✔
2939
}
375✔
2940

2941
static void reportFeatures()
2942
{
×
2943
#ifdef LUAJIT_VERSION
×
2944
  cout << "dnsdist " << VERSION << " (" << LUA_RELEASE << " [" << LUAJIT_VERSION << "])" << endl;
×
2945
#else
2946
  cout << "dnsdist " << VERSION << " (" << LUA_RELEASE << ")" << endl;
2947
#endif
2948
  cout << "Enabled features: ";
×
2949
#ifdef HAVE_XSK
×
2950
  cout << "AF_XDP ";
×
2951
#endif
×
2952
#ifdef HAVE_CDB
×
2953
  cout << "cdb ";
×
2954
#endif
×
2955
#ifdef HAVE_DNS_OVER_QUIC
×
2956
  cout << "dns-over-quic ";
×
2957
#endif
×
2958
#ifdef HAVE_DNS_OVER_HTTP3
×
2959
  cout << "dns-over-http3 ";
×
2960
#endif
×
2961
#ifdef HAVE_DNS_OVER_TLS
×
2962
  cout << "dns-over-tls(";
×
2963
#ifdef HAVE_GNUTLS
×
2964
  cout << "gnutls";
×
2965
#ifdef HAVE_LIBSSL
×
2966
  cout << " ";
×
2967
#endif
×
2968
#endif /* HAVE_GNUTLS */
×
2969
#ifdef HAVE_LIBSSL
×
2970
  cout << "openssl";
×
2971
#endif
×
2972
  cout << ") ";
×
2973
#endif /* HAVE_DNS_OVER_TLS */
×
2974
#ifdef HAVE_DNS_OVER_HTTPS
×
2975
  cout << "dns-over-https(";
×
2976
#ifdef HAVE_LIBH2OEVLOOP
×
2977
  cout << "h2o";
×
2978
#endif /* HAVE_LIBH2OEVLOOP */
×
2979
#if defined(HAVE_LIBH2OEVLOOP) && defined(HAVE_NGHTTP2)
×
2980
  cout << " ";
×
2981
#endif /* defined(HAVE_LIBH2OEVLOOP) && defined(HAVE_NGHTTP2) */
×
2982
#ifdef HAVE_NGHTTP2
×
2983
  cout << "nghttp2";
×
2984
#endif /* HAVE_NGHTTP2 */
×
2985
  cout << ") ";
×
2986
#endif /* HAVE_DNS_OVER_HTTPS */
×
2987
#ifdef HAVE_DNSCRYPT
×
2988
  cout << "dnscrypt ";
×
2989
#endif
×
2990
#ifdef HAVE_EBPF
×
2991
  cout << "ebpf ";
×
2992
#endif
×
2993
#ifdef HAVE_FSTRM
×
2994
  cout << "fstrm ";
×
2995
#endif
×
2996
#ifdef HAVE_IPCIPHER
×
2997
  cout << "ipcipher ";
×
2998
#endif
×
2999
#ifdef HAVE_LIBEDIT
×
3000
  cout << "libedit ";
×
3001
#endif
×
3002
#ifdef HAVE_LIBSODIUM
×
3003
  cout << "libsodium ";
×
3004
#endif
×
3005
#ifdef HAVE_LMDB
×
3006
  cout << "lmdb ";
×
3007
#endif
×
3008
#ifndef DISABLE_PROTOBUF
×
3009
  cout << "protobuf ";
×
3010
#endif
×
3011
#ifdef HAVE_RE2
×
3012
  cout << "re2 ";
×
3013
#endif
×
3014
#ifndef DISABLE_RECVMMSG
×
3015
#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
×
3016
  cout << "recvmmsg/sendmmsg ";
×
3017
#endif
×
3018
#endif /* DISABLE_RECVMMSG */
×
3019
#ifdef HAVE_NET_SNMP
×
3020
  cout << "snmp ";
×
3021
#endif
×
3022
#ifdef HAVE_SYSTEMD
×
3023
  cout << "systemd ";
×
3024
#endif
×
3025
#ifdef HAVE_YAML_CONFIGURATION
×
3026
  cout << "yaml ";
×
3027
#endif
×
3028
  cout << endl;
×
3029
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
3030
#ifdef DNSDIST_CONFIG_ARGS
3031
#define double_escape(s) #s
3032
#define escape_quotes(s) double_escape(s)
3033
  // NOLINTEND(cppcoreguidelines-macro-usage)
3034
  cout << "Configured with: " << escape_quotes(DNSDIST_CONFIG_ARGS) << endl;
3035
#undef escape_quotes
3036
#undef double_escape
3037
#endif
3038
}
×
3039

3040
static void parseParameters(int argc, char** argv, CommandLineParameters& cmdLine, ComboAddress& clientAddress)
3041
{
763✔
3042
  const std::array<struct option, 16> longopts{{{"acl", required_argument, nullptr, 'a'},
763✔
3043
                                                {"check-config", no_argument, nullptr, 1},
763✔
3044
                                                {"client", no_argument, nullptr, 'c'},
763✔
3045
                                                {"config", required_argument, nullptr, 'C'},
763✔
3046
                                                {"disable-syslog", no_argument, nullptr, 2},
763✔
3047
                                                {"execute", required_argument, nullptr, 'e'},
763✔
3048
                                                {"gid", required_argument, nullptr, 'g'},
763✔
3049
                                                {"help", no_argument, nullptr, 'h'},
763✔
3050
                                                {"local", required_argument, nullptr, 'l'},
763✔
3051
                                                {"log-timestamps", no_argument, nullptr, 4},
763✔
3052
                                                {"setkey", required_argument, nullptr, 'k'},
763✔
3053
                                                {"supervised", no_argument, nullptr, 3},
763✔
3054
                                                {"uid", required_argument, nullptr, 'u'},
763✔
3055
                                                {"verbose", no_argument, nullptr, 'v'},
763✔
3056
                                                {"version", no_argument, nullptr, 'V'},
763✔
3057
                                                {nullptr, 0, nullptr, 0}}};
763✔
3058
  int longindex = 0;
763✔
3059
  string optstring;
763✔
3060
  dnsdist::configuration::RuntimeConfiguration newConfig;
763✔
3061

3062
  while (true) {
4,248✔
3063
    // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3064
    int gotChar = getopt_long(argc, argv, "a:cC:e:g:hk:l:u:vV", longopts.data(), &longindex);
4,248✔
3065
    if (gotChar == -1) {
4,248✔
3066
      break;
763✔
3067
    }
763✔
3068
    switch (gotChar) {
3,485!
3069
    case 1:
386✔
3070
      cmdLine.checkConfig = true;
386✔
3071
      break;
386✔
3072
    case 2:
×
3073
      dnsdist::logging::LoggingConfiguration::setSyslog(false);
×
3074
      break;
×
3075
    case 3:
752✔
3076
      cmdLine.beSupervised = true;
752✔
3077
      break;
752✔
3078
    case 4:
×
3079
      dnsdist::logging::LoggingConfiguration::setLogTimestamps(true);
×
3080
      break;
×
3081
    case 'C':
763✔
3082
      cmdLine.config = optarg;
763✔
3083
      break;
763✔
3084
    case 'c':
4✔
3085
      cmdLine.beClient = true;
4✔
3086
      break;
4✔
3087
    case 'e':
×
3088
      cmdLine.command = optarg;
×
3089
      break;
×
3090
    case 'g':
×
3091
      cmdLine.gid = optarg;
×
3092
      break;
×
3093
    case 'h':
×
3094
      cout << "dnsdist " << VERSION << endl;
×
3095
      usage();
×
3096
      cout << "\n";
×
3097
      // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3098
      exit(EXIT_SUCCESS);
×
3099
      break;
×
3100
    case 'a':
758✔
3101
      optstring = optarg;
758✔
3102
      newConfig.d_ACL.addMask(optstring);
758✔
3103
      break;
758✔
3104
    case 'k':
×
3105
#if defined HAVE_LIBSODIUM || defined(HAVE_LIBCRYPTO)
×
3106
    {
×
3107
      std::string consoleKey;
×
3108
      if (B64Decode(string(optarg), consoleKey) < 0) {
×
3109
        cerr << "Unable to decode key '" << optarg << "'." << endl;
×
3110
        // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3111
        exit(EXIT_FAILURE);
×
3112
      }
×
3113
      dnsdist::configuration::updateRuntimeConfiguration([&consoleKey](dnsdist::configuration::RuntimeConfiguration& config) {
×
3114
        config.d_consoleKey = std::move(consoleKey);
×
3115
      });
×
3116
    }
×
3117
#else
3118
      cerr << "dnsdist has been built without libsodium or libcrypto, -k/--setkey is unsupported." << endl;
3119
      // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3120
      exit(EXIT_FAILURE);
3121
#endif
3122
    break;
×
3123
    case 'l':
726✔
3124
      cmdLine.locals.push_back(boost::trim_copy(string(optarg)));
726✔
3125
      break;
726✔
3126
    case 'u':
×
3127
      cmdLine.uid = optarg;
×
3128
      break;
×
3129
    case 'v':
96✔
3130
      newConfig.d_verbose = true;
96✔
3131
      break;
96✔
3132
    case 'V':
×
3133
      reportFeatures();
×
3134
      // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3135
      exit(EXIT_SUCCESS);
×
3136
      break;
×
3137
    case '?':
×
3138
      // getopt_long printed an error message.
3139
      usage();
×
3140
      // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3141
      exit(EXIT_FAILURE);
×
3142
      break;
×
3143
    }
3,485✔
3144
  }
3,485✔
3145

3146
  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic): argv
3147
  argv += optind;
763✔
3148

3149
  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic): argv
3150
  for (const auto* ptr = argv; *ptr != nullptr; ++ptr) {
763!
3151
    if (cmdLine.beClient) {
×
3152
      clientAddress = ComboAddress(*ptr, 5199);
×
3153
    }
×
3154
    else {
×
3155
      cmdLine.remotes.emplace_back(*ptr);
×
3156
    }
×
3157
  }
×
3158

3159
  dnsdist::configuration::updateRuntimeConfiguration([&newConfig](dnsdist::configuration::RuntimeConfiguration& config) {
763✔
3160
    config = std::move(newConfig);
763✔
3161
  });
763✔
3162
}
763✔
3163
static void setupPools()
3164
{
375✔
3165
  bool precompute = false;
375✔
3166
  const auto& currentConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
375✔
3167
  if (currentConfig.d_lbPolicy->getName() == "chashed") {
375✔
3168
    precompute = true;
1✔
3169
  }
1✔
3170
  else {
374✔
3171
    for (const auto& entry : currentConfig.d_pools) {
420✔
3172
      if (entry.second->policy != nullptr && entry.second->policy->getName() == "chashed") {
420!
3173
        precompute = true;
×
3174
        break;
×
3175
      }
×
3176
    }
420✔
3177
  }
374✔
3178
  if (precompute) {
375✔
3179
    vinfolog("Pre-computing hashes for consistent hash load-balancing policy");
1!
3180
    // pre compute hashes
3181
    for (const auto& backend : currentConfig.d_backends) {
2✔
3182
      if (backend->d_config.d_weight < 100) {
2!
3183
        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);
×
3184
      }
×
3185

3186
      backend->hash();
2✔
3187
    }
2✔
3188
  }
1✔
3189
}
375✔
3190

3191
static void dropPrivileges(const CommandLineParameters& cmdLine)
3192
{
375✔
3193
  uid_t newgid = getegid();
375✔
3194
  gid_t newuid = geteuid();
375✔
3195

3196
  if (!cmdLine.gid.empty()) {
375!
3197
    newgid = strToGID(cmdLine.gid);
×
3198
  }
×
3199

3200
  if (!cmdLine.uid.empty()) {
375!
3201
    newuid = strToUID(cmdLine.uid);
×
3202
  }
×
3203

3204
  bool retainedCapabilities = true;
375✔
3205
  if (!dnsdist::configuration::getImmutableConfiguration().d_capabilitiesToRetain.empty() && (getegid() != newgid || geteuid() != newuid)) {
375!
3206
    retainedCapabilities = keepCapabilitiesAfterSwitchingIDs();
×
3207
  }
×
3208

3209
  if (getegid() != newgid) {
375!
3210
    if (running_in_service_mgr()) {
×
3211
      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");
×
3212
      _exit(EXIT_FAILURE);
×
3213
    }
×
3214
    dropGroupPrivs(newgid);
×
3215
  }
×
3216

3217
  if (geteuid() != newuid) {
375!
3218
    if (running_in_service_mgr()) {
×
3219
      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");
×
3220
      _exit(EXIT_FAILURE);
×
3221
    }
×
3222
    dropUserPrivs(newuid);
×
3223
  }
×
3224

3225
  if (retainedCapabilities) {
375!
3226
    dropCapabilitiesAfterSwitchingIDs();
375✔
3227
  }
375✔
3228

3229
  try {
375✔
3230
    /* we might still have capabilities remaining,
3231
       for example if we have been started as root
3232
       without --uid or --gid (please don't do that)
3233
       or as an unprivileged user with ambient
3234
       capabilities like CAP_NET_BIND_SERVICE.
3235
    */
3236
    dropCapabilities(dnsdist::configuration::getImmutableConfiguration().d_capabilitiesToRetain);
375✔
3237
  }
375✔
3238
  catch (const std::exception& e) {
375✔
3239
    warnlog("%s", e.what());
×
3240
  }
×
3241
}
375✔
3242

3243
static void initFrontends(const CommandLineParameters& cmdLine)
3244
{
375✔
3245
  auto frontends = dnsdist::configuration::getImmutableConfiguration().d_frontends;
375✔
3246

3247
  if (!cmdLine.locals.empty()) {
375✔
3248
    for (auto it = frontends.begin(); it != frontends.end();) {
530✔
3249
      /* DoH, DoT and DNSCrypt frontends are separate */
3250
      if ((*it)->dohFrontend == nullptr && (*it)->tlsFrontend == nullptr && (*it)->dnscryptCtx == nullptr && (*it)->doqFrontend == nullptr && (*it)->doh3Frontend == nullptr) {
168✔
3251
        it = frontends.erase(it);
24✔
3252
      }
24✔
3253
      else {
144✔
3254
        ++it;
144✔
3255
      }
144✔
3256
    }
168✔
3257

3258
    for (const auto& loc : cmdLine.locals) {
362✔
3259
      /* UDP */
3260
      frontends.emplace_back(std::make_unique<ClientState>(ComboAddress(loc, 53), false, false, 0, "", std::set<int>{}, true));
362✔
3261
      /* TCP */
3262
      frontends.emplace_back(std::make_unique<ClientState>(ComboAddress(loc, 53), true, false, 0, "", std::set<int>{}, true));
362✔
3263
    }
362✔
3264
  }
362✔
3265

3266
  if (frontends.empty()) {
375!
3267
    /* UDP */
3268
    frontends.emplace_back(std::make_unique<ClientState>(ComboAddress("127.0.0.1", 53), false, false, 0, "", std::set<int>{}, true));
×
3269
    /* TCP */
3270
    frontends.emplace_back(std::make_unique<ClientState>(ComboAddress("127.0.0.1", 53), true, false, 0, "", std::set<int>{}, true));
×
3271
  }
×
3272

3273
  dnsdist::configuration::updateImmutableConfiguration([&frontends](dnsdist::configuration::ImmutableConfiguration& config) {
375✔
3274
    config.d_frontends = std::move(frontends);
375✔
3275
  });
375✔
3276
}
375✔
3277

3278
namespace dnsdist
3279
{
3280
static void startFrontends()
3281
{
375✔
3282
#ifdef HAVE_XSK
375✔
3283
  for (auto& xskContext : dnsdist::xsk::g_xsk) {
375!
3284
    std::thread xskThread(dnsdist::xsk::XskRouter, std::move(xskContext));
×
3285
    xskThread.detach();
×
3286
  }
×
3287
#endif /* HAVE_XSK */
375✔
3288

3289
  std::vector<ClientState*> tcpStates;
375✔
3290
  std::vector<ClientState*> udpStates;
375✔
3291
  for (const auto& clientState : dnsdist::getFrontends()) {
893✔
3292
#ifdef HAVE_XSK
893✔
3293
    if (clientState->xskInfo) {
893!
3294
      dnsdist::xsk::addDestinationAddress(clientState->local);
×
3295

3296
      std::thread xskCT(dnsdist::xsk::XskClientThread, clientState.get());
×
3297
      if (!clientState->cpus.empty()) {
×
3298
        mapThreadToCPUList(xskCT.native_handle(), clientState->cpus);
×
3299
      }
×
3300
      xskCT.detach();
×
3301
    }
×
3302
#endif /* HAVE_XSK */
893✔
3303

3304
    if (clientState->dohFrontend != nullptr && clientState->dohFrontend->d_library == "h2o") {
893✔
3305
#ifdef HAVE_DNS_OVER_HTTPS
23✔
3306
#ifdef HAVE_LIBH2OEVLOOP
23✔
3307
      std::thread dohThreadHandle(dohThread, clientState.get());
23✔
3308
      if (!clientState->cpus.empty()) {
23!
3309
        mapThreadToCPUList(dohThreadHandle.native_handle(), clientState->cpus);
×
3310
      }
×
3311
      dohThreadHandle.detach();
23✔
3312
#endif /* HAVE_LIBH2OEVLOOP */
23✔
3313
#endif /* HAVE_DNS_OVER_HTTPS */
23✔
3314
      continue;
23✔
3315
    }
23✔
3316
    if (clientState->doqFrontend != nullptr) {
870✔
3317
#ifdef HAVE_DNS_OVER_QUIC
20✔
3318
      std::thread doqThreadHandle(doqThread, clientState.get());
20✔
3319
      if (!clientState->cpus.empty()) {
20!
3320
        mapThreadToCPUList(doqThreadHandle.native_handle(), clientState->cpus);
×
3321
      }
×
3322
      doqThreadHandle.detach();
20✔
3323
#endif /* HAVE_DNS_OVER_QUIC */
20✔
3324
      continue;
20✔
3325
    }
20✔
3326
    if (clientState->doh3Frontend != nullptr) {
850✔
3327
#ifdef HAVE_DNS_OVER_HTTP3
17✔
3328
      std::thread doh3ThreadHandle(doh3Thread, clientState.get());
17✔
3329
      if (!clientState->cpus.empty()) {
17!
3330
        mapThreadToCPUList(doh3ThreadHandle.native_handle(), clientState->cpus);
×
3331
      }
×
3332
      doh3ThreadHandle.detach();
17✔
3333
#endif /* HAVE_DNS_OVER_HTTP3 */
17✔
3334
      continue;
17✔
3335
    }
17✔
3336
    if (clientState->udpFD >= 0) {
833✔
3337
#ifdef USE_SINGLE_ACCEPTOR_THREAD
3338
      udpStates.push_back(clientState.get());
3339
#else /* USE_SINGLE_ACCEPTOR_THREAD */
3340
      std::thread udpClientThreadHandle(udpClientThread, std::vector<ClientState*>{clientState.get()});
376✔
3341
      if (!clientState->cpus.empty()) {
376!
3342
        mapThreadToCPUList(udpClientThreadHandle.native_handle(), clientState->cpus);
×
3343
      }
×
3344
      udpClientThreadHandle.detach();
376✔
3345
#endif /* USE_SINGLE_ACCEPTOR_THREAD */
376✔
3346
    }
376✔
3347
    else if (clientState->tcpFD >= 0) {
457!
3348
#ifdef USE_SINGLE_ACCEPTOR_THREAD
3349
      tcpStates.push_back(clientState.get());
3350
#else /* USE_SINGLE_ACCEPTOR_THREAD */
3351
      std::thread tcpAcceptorThreadHandle(tcpAcceptorThread, std::vector<ClientState*>{clientState.get()});
457✔
3352
      if (!clientState->cpus.empty()) {
457!
3353
        mapThreadToCPUList(tcpAcceptorThreadHandle.native_handle(), clientState->cpus);
×
3354
      }
×
3355
      tcpAcceptorThreadHandle.detach();
457✔
3356
#endif /* USE_SINGLE_ACCEPTOR_THREAD */
457✔
3357
    }
457✔
3358
  }
833✔
3359
#ifdef USE_SINGLE_ACCEPTOR_THREAD
3360
  if (!udpStates.empty()) {
3361
    std::thread udpThreadHandle(udpClientThread, udpStates);
3362
    udpThreadHandle.detach();
3363
  }
3364
  if (!tcpStates.empty()) {
3365
    g_tcpclientthreads = std::make_unique<TCPClientCollection>(1, tcpStates);
3366
  }
3367
#endif /* USE_SINGLE_ACCEPTOR_THREAD */
3368
}
375✔
3369
}
3370

3371
struct ListeningSockets
3372
{
3373
  Socket d_consoleSocket{-1};
3374
  std::vector<std::pair<ComboAddress, Socket>> d_webServerSockets;
3375
};
3376

3377
static ListeningSockets initListeningSockets()
3378
{
375✔
3379
  ListeningSockets result;
375✔
3380
  const auto& currentConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
375✔
3381

3382
  if (currentConfig.d_consoleEnabled) {
375✔
3383
    const auto& local = currentConfig.d_consoleServerAddress;
72✔
3384
    try {
72✔
3385
      result.d_consoleSocket = Socket(local.sin4.sin_family, SOCK_STREAM, 0);
72✔
3386
      result.d_consoleSocket.bind(local, true);
72✔
3387
      result.d_consoleSocket.listen(5);
72✔
3388
    }
72✔
3389
    catch (const std::exception& exp) {
72✔
3390
      errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), exp.what());
×
3391
    }
×
3392
  }
72✔
3393

3394
  for (const auto& local : currentConfig.d_webServerAddresses) {
375✔
3395
    try {
52✔
3396
      auto webServerSocket = Socket(local.sin4.sin_family, SOCK_STREAM, 0);
52✔
3397
      webServerSocket.bind(local, true);
52✔
3398
      webServerSocket.listen(5);
52✔
3399
      result.d_webServerSockets.emplace_back(local, std::move(webServerSocket));
52✔
3400
    }
52✔
3401
    catch (const std::exception& exp) {
52✔
3402
      errlog("Unable to bind to web server socket on %s: %s", local.toStringWithPort(), exp.what());
×
3403
    }
×
3404
  }
52✔
3405

3406
  return result;
375✔
3407
}
375✔
3408

3409
static std::optional<std::string> lookForTentativeConfigurationFileWithExtension(const std::string& configurationFile, const std::string& extension)
3410
{
48✔
3411
  auto dotPos = configurationFile.rfind('.');
48✔
3412
  if (dotPos == std::string::npos) {
48!
3413
    return std::nullopt;
×
3414
  }
×
3415
  auto tentativeFile = configurationFile.substr(0, dotPos + 1) + extension;
48✔
3416
  if (!std::filesystem::exists(tentativeFile)) {
48✔
3417
    return std::nullopt;
42✔
3418
  }
42✔
3419
  return tentativeFile;
6✔
3420
}
48✔
3421

3422
static bool loadConfigurationFromFile(const std::string& configurationFile, bool isClient, bool configCheck)
3423
{
763✔
3424
  if (boost::ends_with(configurationFile, ".yml")) {
763✔
3425
    // the bindings are always needed, for example for inline Lua
3426
    dnsdist::lua::setupLuaBindingsOnly(*(g_lua.lock()), isClient, configCheck);
48✔
3427

3428
    if (auto tentativeLuaConfFile = lookForTentativeConfigurationFileWithExtension(configurationFile, "lua")) {
48✔
3429
      vinfolog("Loading configuration from auto-discovered Lua file %s", *tentativeLuaConfFile);
6✔
3430
      dnsdist::configuration::lua::loadLuaConfigurationFile(*(g_lua.lock()), *tentativeLuaConfFile, configCheck);
6✔
3431
    }
6✔
3432
    vinfolog("Loading configuration from YAML file %s", configurationFile);
48✔
3433
    if (!dnsdist::configuration::yaml::loadConfigurationFromFile(configurationFile, isClient, configCheck)) {
48✔
3434
      return false;
2✔
3435
    }
2✔
3436
    if (!isClient && !configCheck) {
46!
3437
      dnsdist::lua::setupLuaConfigurationOptions(*(g_lua.lock()), false, false);
23✔
3438
    }
23✔
3439
    return true;
46✔
3440
  }
48✔
3441

3442
  dnsdist::lua::setupLua(*(g_lua.lock()), isClient, configCheck);
715✔
3443
  if (boost::ends_with(configurationFile, ".lua")) {
715!
3444
    vinfolog("Loading configuration from Lua file %s", configurationFile);
×
3445
    dnsdist::configuration::lua::loadLuaConfigurationFile(*(g_lua.lock()), configurationFile, configCheck);
×
3446
    if (auto tentativeYamlConfFile = lookForTentativeConfigurationFileWithExtension(configurationFile, "yml")) {
×
3447
      vinfolog("Loading configuration from auto-discovered YAML file %s", *tentativeYamlConfFile);
×
3448
      return dnsdist::configuration::yaml::loadConfigurationFromFile(*tentativeYamlConfFile, isClient, configCheck);
×
3449
    }
×
3450
  }
×
3451
  else {
715✔
3452
    vinfolog("Loading configuration from Lua file %s", configurationFile);
715✔
3453
    dnsdist::configuration::lua::loadLuaConfigurationFile(*(g_lua.lock()), configurationFile, configCheck);
715✔
3454
  }
715✔
3455
  return true;
715✔
3456
}
715✔
3457

3458
int main(int argc, char** argv)
3459
{
763✔
3460
  try {
763✔
3461
    CommandLineParameters cmdLine{};
763✔
3462
    size_t udpBindsCount = 0;
763✔
3463
    size_t tcpBindsCount = 0;
763✔
3464

3465
    dnsdist::console::completion::setupCompletion();
763✔
3466

3467
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast): SIG_IGN macro
3468
    signal(SIGPIPE, SIG_IGN);
763✔
3469
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast): SIG_IGN macro
3470
    signal(SIGCHLD, SIG_IGN);
763✔
3471
    signal(SIGTERM, sigTermHandler);
763✔
3472

3473
    openlog("dnsdist", LOG_PID | LOG_NDELAY, LOG_DAEMON);
763✔
3474

3475
#ifdef HAVE_LIBSODIUM
763✔
3476
    if (sodium_init() == -1) {
763!
3477
      cerr << "Unable to initialize crypto library" << endl;
×
3478
      // NOLINTNEXTLINE(concurrency-mt-unsafe): only on thread at this point
3479
      exit(EXIT_FAILURE);
×
3480
    }
×
3481
#endif
763✔
3482
    dnsdist::initRandom();
763✔
3483

3484
#ifdef HAVE_XSK
763✔
3485
    try {
763✔
3486
      dnsdist::xsk::clearDestinationAddresses();
763✔
3487
    }
763✔
3488
    catch (const std::exception& exp) {
763✔
3489
      /* silently handle failures: at this point we don't even know if XSK is enabled,
3490
         and we might not have the correct map (not the default one). */
3491
    }
763✔
3492
#endif /* HAVE_XSK */
763✔
3493

3494
    ComboAddress clientAddress = ComboAddress();
763✔
3495
    cmdLine.config = SYSCONFDIR "/dnsdist.conf";
763✔
3496

3497
    parseParameters(argc, argv, cmdLine, clientAddress);
763✔
3498

3499
    dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
763✔
3500
      config.d_lbPolicy = std::make_shared<ServerPolicy>("leastOutstanding", leastOutstanding, false);
763✔
3501
    });
763✔
3502

3503
    if (cmdLine.beClient || !cmdLine.command.empty()) {
763!
3504
      if (!loadConfigurationFromFile(cmdLine.config, true, false)) {
4!
3505
#ifdef COVERAGE
×
3506
        exit(EXIT_FAILURE);
×
3507
#else
3508
        _exit(EXIT_FAILURE);
3509
#endif
3510
      }
×
3511
      if (clientAddress != ComboAddress()) {
4!
3512
        dnsdist::configuration::updateRuntimeConfiguration([&clientAddress](dnsdist::configuration::RuntimeConfiguration& config) {
×
3513
          config.d_consoleServerAddress = clientAddress;
×
3514
        });
×
3515
      }
×
3516
      dnsdist::console::doClient(cmdLine.command);
4✔
3517
#ifdef COVERAGE
4✔
3518
      exit(EXIT_SUCCESS);
4✔
3519
#else
3520
      _exit(EXIT_SUCCESS);
3521
#endif
3522
    }
4✔
3523

3524
    dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
759✔
3525
      auto& acl = config.d_ACL;
759✔
3526
      if (acl.empty()) {
759✔
3527
        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✔
3528
          acl.addMask(addr);
63✔
3529
        }
63✔
3530
      }
7✔
3531
      for (const auto& mask : {"127.0.0.1/8", "::1/128"}) {
1,518✔
3532
        config.d_consoleACL.addMask(mask);
1,518✔
3533
      }
1,518✔
3534
      config.d_webServerACL.toMasks("127.0.0.1, ::1");
759✔
3535
    });
759✔
3536

3537
    dnsdist::webserver::registerBuiltInWebHandlers();
759✔
3538

3539
    if (cmdLine.checkConfig) {
759✔
3540
      if (!loadConfigurationFromFile(cmdLine.config, false, true)) {
384✔
3541
#ifdef COVERAGE
2✔
3542
        exit(EXIT_FAILURE);
2✔
3543
#else
3544
        _exit(EXIT_FAILURE);
3545
#endif
3546
      }
2✔
3547
      // No exception was thrown
3548
      infolog("Configuration '%s' OK!", cmdLine.config);
382✔
3549
      doExitNicely();
382✔
3550
    }
382✔
3551

3552
    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);
757✔
3553

3554
    dnsdist::g_asyncHolder = std::make_unique<dnsdist::AsynchronousHolder>();
757✔
3555

3556
    /* create the default pool no matter what */
3557
    createPoolIfNotExists("");
757✔
3558

3559
    if (!loadConfigurationFromFile(cmdLine.config, false, false)) {
757!
3560
#ifdef COVERAGE
×
3561
      exit(EXIT_FAILURE);
×
3562
#else
3563
      _exit(EXIT_FAILURE);
3564
#endif
3565
    }
×
3566

3567
    // we only want to update this value if it has not been set by either the Lua or YAML configuration,
3568
    // and we need to stop touching this value once the backends' hashes have been computed, in setupPools()
3569
    dnsdist::configuration::updateImmutableConfiguration([](dnsdist::configuration::ImmutableConfiguration& config) {
757✔
3570
      if (config.d_hashPerturbation == 0) {
375!
3571
        config.d_hashPerturbation = dnsdist::getRandomValue(0xffffffff);
375✔
3572
      }
375✔
3573
    });
375✔
3574

3575
    setupPools();
757✔
3576

3577
    initFrontends(cmdLine);
757✔
3578

3579
    for (const auto& frontend : dnsdist::getFrontends()) {
893✔
3580
      if (!frontend->tcp) {
893✔
3581
        ++udpBindsCount;
413✔
3582
      }
413✔
3583
      else {
480✔
3584
        ++tcpBindsCount;
480✔
3585
      }
480✔
3586
    }
893✔
3587

3588
    dnsdist::configuration::setImmutableConfigurationDone();
757✔
3589

3590
    {
757✔
3591
      const auto& immutableConfig = dnsdist::configuration::getImmutableConfiguration();
757✔
3592
      setTCPDownstreamMaxIdleConnectionsPerBackend(immutableConfig.d_outgoingTCPMaxIdlePerBackend);
757✔
3593
      setTCPDownstreamMaxIdleTime(immutableConfig.d_outgoingTCPMaxIdleTime);
757✔
3594
      setTCPDownstreamCleanupInterval(immutableConfig.d_outgoingTCPCleanupInterval);
757✔
3595
#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
757✔
3596
      setDoHDownstreamMaxIdleConnectionsPerBackend(immutableConfig.d_outgoingDoHMaxIdlePerBackend);
757✔
3597
      setDoHDownstreamMaxIdleTime(immutableConfig.d_outgoingDoHMaxIdleTime);
757✔
3598
      setDoHDownstreamCleanupInterval(immutableConfig.d_outgoingDoHCleanupInterval);
757✔
3599
#endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
757✔
3600
    }
757✔
3601

3602
    {
757✔
3603
      const auto& config = dnsdist::configuration::getImmutableConfiguration();
757✔
3604
      g_rings.init(config.d_ringsCapacity, config.d_ringsNumberOfShards, config.d_ringsNbLockTries, config.d_ringsRecordQueries, config.d_ringsRecordResponses);
757✔
3605
    }
757✔
3606

3607
    for (const auto& frontend : dnsdist::getFrontends()) {
893✔
3608
      setUpLocalBind(*frontend);
893✔
3609
    }
893✔
3610

3611
    {
757✔
3612
      std::string acls;
757✔
3613
      auto aclEntries = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ACL.toStringVector();
757✔
3614
      for (const auto& aclEntry : aclEntries) {
757✔
3615
        if (!acls.empty()) {
566✔
3616
          acls += ", ";
191✔
3617
        }
191✔
3618
        acls += aclEntry;
566✔
3619
      }
566✔
3620
      infolog("ACL allowing queries from: %s", acls);
757✔
3621
    }
757✔
3622
    {
757✔
3623
      std::string acls;
757✔
3624
      auto aclEntries = dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleACL.toStringVector();
757✔
3625
      for (const auto& entry : aclEntries) {
757✔
3626
        if (!acls.empty()) {
741✔
3627
          acls += ", ";
366✔
3628
        }
366✔
3629
        acls += entry;
741✔
3630
      }
741✔
3631
      infolog("Console ACL allowing connections from: %s", acls.c_str());
757✔
3632
    }
757✔
3633

3634
    auto listeningSockets = initListeningSockets();
757✔
3635

3636
#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO)
757✔
3637
    if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleEnabled && dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleKey.empty()) {
757✔
3638
      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✔
3639
    }
1✔
3640
#endif
757✔
3641

3642
    dropPrivileges(cmdLine);
757✔
3643

3644
    /* this need to be done _after_ dropping privileges */
3645
#ifndef DISABLE_DELAY_PIPE
757✔
3646
    g_delay = std::make_unique<DelayPipe<DelayedPacket>>();
757✔
3647
#endif /* DISABLE_DELAY_PIPE */
757✔
3648

3649
#if defined(HAVE_NET_SNMP)
757✔
3650
    if (dnsdist::configuration::getImmutableConfiguration().d_snmpEnabled) {
757✔
3651
      g_snmpAgent = std::make_unique<DNSDistSNMPAgent>("dnsdist", dnsdist::configuration::getImmutableConfiguration().d_snmpDaemonSocketPath);
1✔
3652
      g_snmpAgent->run();
1✔
3653
    }
1✔
3654
#endif /* HAVE_NET_SNMP */
757✔
3655

3656
    /* we need to create the TCP worker threads before the
3657
       acceptor ones, otherwise we might crash when processing
3658
       the first TCP query */
3659
#ifndef USE_SINGLE_ACCEPTOR_THREAD
757✔
3660
    const auto maxTCPClientThreads = dnsdist::configuration::getImmutableConfiguration().d_maxTCPClientThreads;
757✔
3661
    /* the limit is completely arbitrary: hopefully high enough not to trigger too many false positives
3662
       but low enough to be useful */
3663
    if (maxTCPClientThreads >= 50U) {
757!
3664
      warnlog("setMaxTCPClientThreads(%d) might create a large number of TCP connections to backends, and is probably not needed, please consider lowering it", maxTCPClientThreads);
×
3665
    }
×
3666
    g_tcpclientthreads = std::make_unique<TCPClientCollection>(maxTCPClientThreads, std::vector<ClientState*>());
757✔
3667
#endif
757✔
3668

3669
#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
757✔
3670
    initDoHWorkers();
757✔
3671
#endif
757✔
3672

3673
    if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleEnabled) {
757✔
3674
      std::thread consoleControlThread(dnsdist::console::controlThread, std::move(listeningSockets.d_consoleSocket));
72✔
3675
      consoleControlThread.detach();
72✔
3676
    }
72✔
3677
    for (auto& [listeningAddress, socket] : listeningSockets.d_webServerSockets) {
757✔
3678
      std::thread webServerThread(dnsdist::webserver::WebserverThread, listeningAddress, std::move(socket));
52✔
3679
      webServerThread.detach();
52✔
3680
    }
52✔
3681

3682
    for (const auto& backend : dnsdist::configuration::getCurrentRuntimeConfiguration().d_backends) {
757✔
3683
      if (backend->connected) {
419✔
3684
        backend->start();
384✔
3685
      }
384✔
3686
    }
419✔
3687

3688
    if (!cmdLine.remotes.empty()) {
757!
3689
      for (const auto& address : cmdLine.remotes) {
×
3690
        DownstreamState::Config config;
×
3691
        config.remote = ComboAddress(address, 53);
×
3692
        auto ret = std::make_shared<DownstreamState>(std::move(config), nullptr, true);
×
3693
        addServerToPool("", ret);
×
3694
        ret->start();
×
3695
        dnsdist::configuration::updateRuntimeConfiguration([&ret](dnsdist::configuration::RuntimeConfiguration& runtimeConfig) {
×
3696
          runtimeConfig.d_backends.push_back(std::move(ret));
×
3697
        });
×
3698
      }
×
3699
    }
×
3700

3701
    if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_backends.empty()) {
757✔
3702
      errlog("No downstream servers defined: all packets will get dropped");
7✔
3703
      // you might define them later, but you need to know
3704
    }
7✔
3705

3706
    checkFileDescriptorsLimits(udpBindsCount, tcpBindsCount);
757✔
3707

3708
    {
757✔
3709
      // coverity[auto_causes_copy]
3710
      const auto states = dnsdist::configuration::getCurrentRuntimeConfiguration().d_backends; // it is a copy, but the internal shared_ptrs are the real deal
757✔
3711
      auto mplexer = std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent(states.size()));
757✔
3712
      for (auto& dss : states) {
757✔
3713

3714
        if (dss->d_config.d_availability == DownstreamState::Availability::Auto) {
419✔
3715
          if (dss->d_config.d_healthCheckMode == DownstreamState::HealthCheckMode::Active) {
337✔
3716
            dss->d_nextCheck = dss->d_config.checkInterval;
334✔
3717
          }
334✔
3718

3719
          if (!queueHealthCheck(mplexer, dss, true)) {
337!
3720
            dss->submitHealthCheckResult(true, false);
×
3721
            dss->setUpStatus(false);
×
3722
            warnlog("Marking downstream %s as 'down'", dss->getNameWithAddr());
×
3723
          }
×
3724
        }
337✔
3725
      }
419✔
3726
      handleQueuedHealthChecks(*mplexer, true);
757✔
3727
    }
757✔
3728

3729
    dnsdist::startFrontends();
757✔
3730

3731
    dnsdist::ServiceDiscovery::run();
757✔
3732

3733
#ifndef DISABLE_CARBON
757✔
3734
    dnsdist::Carbon::run(dnsdist::configuration::getCurrentRuntimeConfiguration().d_carbonEndpoints);
757✔
3735
#endif /* DISABLE_CARBON */
757✔
3736

3737
    thread stattid(maintThread);
757✔
3738
    stattid.detach();
757✔
3739

3740
    thread healththread(healthChecksThread);
757✔
3741

3742
#ifndef DISABLE_DYNBLOCKS
757✔
3743
    thread dynBlockMaintThread(dynBlockMaintenanceThread);
757✔
3744
    dynBlockMaintThread.detach();
757✔
3745
#endif /* DISABLE_DYNBLOCKS */
757✔
3746

3747
#ifndef DISABLE_SECPOLL
757✔
3748
    if (!dnsdist::configuration::getCurrentRuntimeConfiguration().d_secPollSuffix.empty()) {
757!
3749
      thread secpollthread(secPollThread);
×
3750
      secpollthread.detach();
×
3751
    }
×
3752
#endif /* DISABLE_SECPOLL */
757✔
3753

3754
    if (cmdLine.beSupervised) {
757✔
3755
#ifdef HAVE_SYSTEMD
375✔
3756
      sd_notify(0, "READY=1");
375✔
3757
#endif
375✔
3758
      healththread.join();
375✔
3759
    }
375✔
3760
    else {
382✔
3761
      healththread.detach();
382✔
3762
      dnsdist::console::doConsole();
382✔
3763
    }
382✔
3764
    doExitNicely();
757✔
3765
  }
757✔
3766
  catch (const LuaContext::ExecutionErrorException& e) {
763✔
3767
    try {
2✔
3768
      errlog("Fatal Lua error: %s", e.what());
2✔
3769
      std::rethrow_if_nested(e);
2✔
3770
    }
2✔
3771
    catch (const std::exception& ne) {
2✔
3772
      errlog("Details: %s", ne.what());
×
3773
    }
×
3774
    catch (const PDNSException& ae) {
2✔
3775
      errlog("Fatal pdns error: %s", ae.reason);
1✔
3776
    }
1✔
3777
    doExitNicely(EXIT_FAILURE);
2✔
3778
  }
2✔
3779
  catch (const std::exception& e) {
763✔
3780
    errlog("Fatal error: %s", e.what());
1✔
3781
    doExitNicely(EXIT_FAILURE);
1✔
3782
  }
1✔
3783
  catch (const PDNSException& ae) {
763✔
3784
    errlog("Fatal pdns error: %s", ae.reason);
×
3785
    doExitNicely(EXIT_FAILURE);
3786
  }
×
3787
}
763✔
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