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

PowerDNS / pdns / 17765900968

16 Sep 2025 12:33PM UTC coverage: 65.987% (-0.04%) from 66.029%
17765900968

Pull #16108

github

web-flow
Merge 4059c5fe8 into 2e297650d
Pull Request #16108: dnsdist: implement simple packet shuffle in cache

42424 of 93030 branches covered (45.6%)

Branch coverage included in aggregate %.

9 of 135 new or added lines in 6 files covered. (6.67%)

34 existing lines in 8 files now uncovered.

128910 of 166619 relevant lines covered (77.37%)

5500581.66 hits per line

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

70.45
/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,823✔
131
  const int flags = 0;
2,823✔
132
  if (from.sin4.sin_family == 0) {
2,823✔
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,818✔
135
    if (ret == -1) {
2,818!
136
      int error = errno;
×
137
      vinfolog("Error sending UDP response to %s: %s", dest.toStringWithPort(), stringerror(error));
×
138
    }
×
139
    return;
2,818✔
140
  }
2,818✔
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,885✔
197
  constexpr auto doAvg = [](pdns::stat_double_t& var, double n, double weight) {
23,540✔
198
    var.store((weight - 1) * var.load() / weight + n / weight);
23,540✔
199
  };
23,540✔
200

201
  if (protocol == dnsdist::Protocol::DoUDP || protocol == dnsdist::Protocol::DNSCryptUDP) {
5,885✔
202
    if (udiff < 1000) {
2,810✔
203
      ++dnsdist::metrics::g_stats.latency0_1;
2,688✔
204
    }
2,688✔
205
    else if (udiff < 10000) {
122✔
206
      ++dnsdist::metrics::g_stats.latency1_10;
111✔
207
    }
111✔
208
    else if (udiff < 50000) {
11!
UNCOV
209
      ++dnsdist::metrics::g_stats.latency10_50;
×
UNCOV
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,810✔
222
    ++dnsdist::metrics::g_stats.latencyCount;
2,810✔
223

224
    doAvg(dnsdist::metrics::g_stats.latencyAvg100, udiff, 100);
2,810✔
225
    doAvg(dnsdist::metrics::g_stats.latencyAvg1000, udiff, 1000);
2,810✔
226
    doAvg(dnsdist::metrics::g_stats.latencyAvg10000, udiff, 10000);
2,810✔
227
    doAvg(dnsdist::metrics::g_stats.latencyAvg1000000, udiff, 1000000);
2,810✔
228
  }
2,810✔
229
  else if (protocol == dnsdist::Protocol::DoTCP || protocol == dnsdist::Protocol::DNSCryptTCP) {
3,075✔
230
    doAvg(dnsdist::metrics::g_stats.latencyTCPAvg100, udiff, 100);
2,019✔
231
    doAvg(dnsdist::metrics::g_stats.latencyTCPAvg1000, udiff, 1000);
2,019✔
232
    doAvg(dnsdist::metrics::g_stats.latencyTCPAvg10000, udiff, 10000);
2,019✔
233
    doAvg(dnsdist::metrics::g_stats.latencyTCPAvg1000000, udiff, 1000000);
2,019✔
234
  }
2,019✔
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,885✔
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,084✔
263
  if (response.size() < sizeof(dnsheader)) {
5,084!
264
    return false;
×
265
  }
×
266

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

276
  if (dnsHeader->qdcount == 0) {
5,084✔
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,070✔
289
    uint16_t rqtype{};
5,070✔
290
    uint16_t rqclass{};
5,070✔
291
    if (response.size() < (sizeof(dnsheader) + qname.wirelength() + sizeof(rqtype) + sizeof(rqclass))) {
5,070!
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,070✔
297
    if (qname.matchesUncompressedName(packetView)) {
5,070✔
298
      size_t pos = sizeof(dnsheader) + qname.wirelength();
5,068✔
299
      rqtype = response.at(pos) * 256 + response.at(pos + 1);
5,068✔
300
      rqclass = response.at(pos + 2) * 256 + response.at(pos + 3);
5,068✔
301
      return rqtype == qtype && rqclass == qclass;
5,068!
302
    }
5,068✔
303
    return false;
2✔
304
  }
5,070✔
305
  catch (const std::exception& e) {
5,070✔
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,070✔
316

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

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

338
  if (dnsQuestion.d_selfGeneratedHandledEDNS) {
483✔
339
    return true;
245✔
340
  }
245✔
341
  return addEDNSToQueryTurnedResponse(dnsQuestion);
238✔
342
}
483✔
343

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

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

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

359
  if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_fixupCase) {
4,881✔
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,881✔
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,881✔
435
}
4,891✔
436

437
#ifdef HAVE_DNSCRYPT
438
static bool encryptResponse(PacketBuffer& response, size_t maximumSize, bool tcp, std::unique_ptr<DNSCryptQuery>& dnsCryptQuery)
439
{
5,900✔
440
  if (dnsCryptQuery) {
5,900✔
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,900✔
449
}
5,900✔
450
#endif /* HAVE_DNSCRYPT */
451

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

458
  DNSResponseAction::Action action = DNSResponseAction::Action::None;
1,418✔
459
  std::string ruleresult;
1,418✔
460
  for (const auto& rrule : respRuleActions) {
1,486✔
461
    if (rrule.d_rule->matches(&dnsResponse)) {
1,486✔
462
      ++rrule.d_rule->d_matches;
732✔
463
      action = (*rrule.d_action)(&dnsResponse, &ruleresult);
732✔
464
      switch (action) {
732✔
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:
414✔
504
        break;
414✔
505
      }
732✔
506
    }
732✔
507
  }
1,486✔
508

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

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

519
  if (dnsResponse.ids.packetCache && !dnsResponse.ids.selfGenerated && !dnsResponse.ids.skipCache && (!dnsResponse.ids.forwardedOverUDP || response.size() <= s_maxUDPResponsePacketSize)) {
4,891!
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,891!
551
    dnsdist::PacketMangling::restrictDNSPacketTTLs(dnsResponse.getMutableData(), 0, dnsResponse.ids.ttlCap);
×
552
  }
×
553

554
  if (dnsResponse.ids.d_extendedError) {
4,891✔
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,891✔
559
  if (!muted) {
4,891✔
560
    if (!encryptResponse(response, dnsResponse.getMaximumSize(), dnsResponse.overTCP(), dnsResponse.ids.dnsCryptQuery)) {
4,887!
561
      return false;
×
562
    }
×
563
  }
4,887✔
564
#endif /* HAVE_DNSCRYPT */
4,891✔
565

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

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

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

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

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

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

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

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

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

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

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

611
bool sendUDPResponse(int origFD, const PacketBuffer& response, [[maybe_unused]] const int delayMsec, const ComboAddress& origDest, const ComboAddress& origRemote)
612
{
2,823✔
613
#ifndef DISABLE_DELAY_PIPE
2,823✔
614
  if (delayMsec > 0 && g_delay != nullptr) {
2,823!
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,819✔
620
  // NOLINTNEXTLINE(readability-suspicious-call-argument)
621
  sendfromto(origFD, response, origDest, origRemote);
2,819✔
622
  return true;
2,819✔
623
}
2,823✔
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,366✔
627
  handleResponseSent(ids.qname, ids.qtype, udiff, client, backend, size, cleartextDH, outgoingProtocol, ids.protocol, fromBackend);
5,366✔
628
}
5,366✔
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,885✔
632
  if (g_rings.shouldRecordResponses()) {
5,885!
633
    timespec now{};
5,885✔
634
    gettime(&now);
5,885✔
635
    g_rings.insertResponse(now, client, qname, qtype, static_cast<unsigned int>(udiff), size, cleartextDH, backend, outgoingProtocol);
5,885✔
636
  }
5,885✔
637

638
  switch (cleartextDH.rcode) {
5,885✔
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,260✔
649
    ++dnsdist::metrics::g_stats.frontendNoError;
5,260✔
650
    break;
5,260✔
651
  }
5,885✔
652

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

656
static void handleResponseTC4UDPClient(DNSQuestion& dnsQuestion, uint16_t udpPayloadSize, PacketBuffer& response)
657
{
3,142✔
658
  if (udpPayloadSize != 0 && response.size() > udpPayloadSize) {
3,142✔
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,141✔
667
    truncateTC(response, dnsQuestion.getMaximumSize(), dnsQuestion.ids.qname.wirelength(), dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses);
3✔
668
  }
3✔
669
}
3,142✔
670

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

675
  handleResponseTC4UDPClient(dnsResponse, ids.udpPayloadSize, response);
2,682✔
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,682✔
680
  memcpy(&cleartextDH, dnsResponse.getHeader().get(), sizeof(cleartextDH));
2,682✔
681

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

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

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

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

703
  if (!selfGenerated) {
2,351✔
704
    double udiff = ids.queryRealTime.udiff();
2,341✔
705
    if (!muted) {
2,341!
706
      vinfolog("Got answer from %s, relayed to %s (UDP), took %f us", backend->d_config.remote.toStringWithPort(), ids.origRemote.toStringWithPort(), udiff);
2,341✔
707
    }
2,341✔
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,341✔
718
  }
2,341✔
719
  else {
10✔
720
    handleResponseSent(ids, 0., dnsResponse.ids.origRemote, ComboAddress(), response.size(), cleartextDH, dnsdist::Protocol::DoUDP, false);
10✔
721
  }
10✔
722
}
2,351✔
723

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

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

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

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

742
  double udiff = ids.queryRealTime.udiff();
2,634✔
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,634✔
745
  dss->reportResponse(dnsHeader->rcode);
2,634✔
746

747
  /* don't call processResponse for DOH */
748
  if (dohUnit) {
2,634✔
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,512✔
757
  return true;
2,512✔
758
}
2,634✔
759

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

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

778
        if (!dss->connected) {
3,027!
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,027✔
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,027!
792
          break;
×
793
        }
×
794

795
        for (const auto& sockDesc : sockets) {
3,028✔
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,028✔
799
          ssize_t got = recv(sockDesc, response.data(), response.size(), 0);
3,028✔
800

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

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

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

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

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

823
          dnsdist::configuration::refreshLocalRuntimeConfiguration();
2,638✔
824
          if (processResponderPacket(dss, response, std::move(*ids)) && ids->isXSK() && ids->cs->xskInfoResponder) {
2,638!
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,638✔
843
      }
3,027✔
844
      catch (const std::exception& e) {
3,416✔
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,416✔
848
  }
389✔
849
  catch (const std::exception& e) {
389✔
850
    errlog("UDP responder thread died because of exception: %s", e.what());
×
851
  }
×
852
  catch (const PDNSException& e) {
389✔
853
    errlog("UDP responder thread died because of PowerDNS exception: %s", e.reason);
×
854
  }
×
855
  catch (...) {
389✔
856
    errlog("UDP responder thread died because of an exception: %s", "unknown");
×
857
  }
×
858
}
389✔
859

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

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

866
  if (raw) {
56✔
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 {
50✔
873
    std::vector<std::string> addrs;
50✔
874
    stringtok(addrs, spoofContent, " ,");
50✔
875

876
    if (addrs.size() == 1) {
50✔
877
      dnsdist::ResponseConfig config;
48✔
878
      try {
48✔
879
        ComboAddress spoofAddr(spoofContent);
48✔
880
        dnsdist::self_answers::generateAnswerFromIPAddresses(dnsQuestion, {spoofAddr}, config);
48✔
881
      }
48✔
882
      catch (const PDNSException& e) {
48✔
883
        DNSName cname(spoofContent);
29✔
884
        dnsdist::self_answers::generateAnswerFromCNAME(dnsQuestion, cname, config);
29✔
885
      }
29✔
886
    }
48✔
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
  }
50✔
900
}
56✔
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:
50✔
940
    spoofResponseFromString(dnsQuestion, ruleresult, false);
50✔
941
    return true;
50✔
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:
324✔
966
    return true;
324✔
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,428✔
988
    /* fall-through */
989
  case DNSAction::Action::NoOp:
1,428!
990
    break;
1,428✔
991
  }
2,421✔
992

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

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

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

1007
  for (const auto& rule : rules) {
6,651✔
1008
    if (!rule.d_rule->matches(&dnsQuestion)) {
6,651✔
1009
      continue;
4,030✔
1010
    }
4,030✔
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;
988✔
1016
    }
988✔
1017
  }
2,621✔
1018

1019
  return !drop;
3,063✔
1020
}
6,277✔
1021

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

1028
  {
6,061✔
1029
    const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
6,061✔
1030
    if (runtimeConfig.d_queryCountConfig.d_enabled) {
6,061!
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,061✔
1047

1048
#ifndef DISABLE_DYNBLOCKS
6,061✔
1049
  const auto defaultDynBlockAction = dnsdist::configuration::getCurrentRuntimeConfiguration().d_dynBlockAction;
6,061✔
1050
  auto setRCode = [&dnsQuestion](uint8_t rcode) {
6,061✔
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,061✔
1056
    auto updateBlockStats = [&got]() {
509✔
1057
      ++dnsdist::metrics::g_stats.dynBlocked;
37✔
1058
      got->second.blocks++;
37✔
1059
    };
37✔
1060

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

1067
      switch (action) {
81✔
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: {
2✔
1113
        if (!got->second.tagSettings) {
2!
1114
          vinfolog("Skipping set tag dynamic block for query from %s because of missing options", dnsQuestion.ids.origRemote.toStringWithPort());
×
1115
          break;
×
1116
        }
×
1117
        updateBlockStats();
2✔
1118
        const auto& tagName = got->second.tagSettings->d_name;
2✔
1119
        const auto& tagValue = got->second.tagSettings->d_value;
2✔
1120
        dnsQuestion.setTag(tagName, tagValue);
2✔
1121
        vinfolog("Query from %s setting tag %s to %s because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort(), tagName, tagValue);
2!
1122
        return true;
2✔
1123
      }
2✔
1124
      default:
22✔
1125
        updateBlockStats();
22✔
1126
        vinfolog("Query from %s dropped because of dynamic block", dnsQuestion.ids.origRemote.toStringWithPort());
22!
1127
        return false;
22✔
1128
      }
81✔
1129
    }
81✔
1130
  }
509✔
1131

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

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

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

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

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

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

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

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

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

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

1247
  return result;
4,270✔
1248
}
4,270✔
1249

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

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

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

1289
  ++clientState.queries;
2,894✔
1290
  ++dnsdist::metrics::g_stats.queries;
2,894✔
1291

1292
  return true;
2,894✔
1293
}
2,895✔
1294

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

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

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

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

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

1331
  if (dnsHeader.rd) {
6,077✔
1332
    ++dnsdist::metrics::g_stats.rdQueries;
5,537✔
1333
  }
5,537✔
1334

1335
  return true;
6,077✔
1336
}
6,079✔
1337

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

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

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

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

1376
  if (dnsResponse.ids.ttlCap > 0) {
1,025!
1377
    dnsdist::PacketMangling::restrictDNSPacketTTLs(dnsResponse.getMutableData(), 0, dnsResponse.ids.ttlCap);
×
1378
  }
×
1379

1380
  if (dnsResponse.ids.d_extendedError) {
1,025✔
1381
    dnsdist::edns::addExtendedDNSError(dnsResponse.getMutableData(), dnsResponse.getMaximumSize(), dnsResponse.ids.d_extendedError->infoCode, dnsResponse.ids.d_extendedError->extraText);
10✔
1382
  }
10✔
1383

1384
  if (cacheHit) {
1,025✔
1385
    ++dnsdist::metrics::g_stats.cacheHits;
544✔
1386
  }
544✔
1387

1388
  if (dnsResponse.isAsynchronous()) {
1,025✔
1389
    return false;
12✔
1390
  }
12✔
1391

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

1400
  return true;
1,013✔
1401
}
1,013✔
1402

1403
static ProcessQueryResult handleQueryTurnedIntoSelfAnsweredResponse(DNSQuestion& dnsQuestion)
1404
{
472✔
1405
  fixUpQueryTurnedResponse(dnsQuestion, dnsQuestion.ids.origFlags);
472✔
1406

1407
  if (!prepareOutgoingResponse(*dnsQuestion.ids.cs, dnsQuestion, false)) {
472✔
1408
    return ProcessQueryResult::Drop;
2✔
1409
  }
2✔
1410

1411
  const auto rcode = dnsQuestion.getHeader()->rcode;
470✔
1412
  if (rcode == RCode::NXDomain) {
470✔
1413
    ++dnsdist::metrics::g_stats.ruleNXDomain;
26✔
1414
  }
26✔
1415
  else if (rcode == RCode::Refused) {
444✔
1416
    ++dnsdist::metrics::g_stats.ruleRefused;
125✔
1417
  }
125✔
1418
  else if (rcode == RCode::ServFail) {
319✔
1419
    ++dnsdist::metrics::g_stats.ruleServFail;
6✔
1420
  }
6✔
1421

1422
  ++dnsdist::metrics::g_stats.selfAnswered;
470✔
1423
  ++dnsQuestion.ids.cs->responses;
470✔
1424
  return ProcessQueryResult::SendAnswer;
470✔
1425
}
472✔
1426

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

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

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

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

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

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

1467
          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!
1468

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

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

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

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

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

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

1502
        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✔
1503

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

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

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

1526
      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✔
1527

1528
      ++dnsdist::metrics::g_stats.cacheMisses;
253✔
1529

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

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

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

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

1558
        fixUpQueryTurnedResponse(dnsQuestion, dnsQuestion.ids.origFlags);
11✔
1559

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

1569
      return ProcessQueryResult::Drop;
16✔
1570
    }
27✔
1571

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

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

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

1591
bool handleTimeoutResponseRules(const std::vector<dnsdist::rules::ResponseRuleAction>& rules, InternalQueryState& ids, const std::shared_ptr<DownstreamState>& d_ds, const std::shared_ptr<TCPQuerySender>& sender)
1592
{
31✔
1593
  PacketBuffer empty;
31✔
1594
  DNSResponse dnsResponse(ids, empty, d_ds);
31✔
1595
  auto protocol = dnsResponse.getProtocol();
31✔
1596

1597
  vinfolog("Handling timeout response rules for incoming protocol = %s", protocol.toString());
31✔
1598
  if (protocol == dnsdist::Protocol::DoH) {
31✔
1599
#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
3✔
1600
    dnsResponse.d_incomingTCPState = std::dynamic_pointer_cast<IncomingHTTP2Connection>(sender);
3✔
1601
#endif
3✔
1602
    if (!dnsResponse.d_incomingTCPState || !sender || !sender->active()) {
3!
1603
      return false;
×
1604
    }
×
1605
  }
3✔
1606
  else if (protocol == dnsdist::Protocol::DoTCP || protocol == dnsdist::Protocol::DNSCryptTCP || protocol == dnsdist::Protocol::DoT) {
28!
1607
    dnsResponse.d_incomingTCPState = std::dynamic_pointer_cast<IncomingTCPConnectionState>(sender);
11✔
1608
    if (!dnsResponse.d_incomingTCPState || !sender || !sender->active()) {
11!
1609
      return false;
×
1610
    }
×
1611
  }
11✔
1612
  (void)applyRulesToResponse(rules, dnsResponse);
31✔
1613
  return dnsResponse.isAsynchronous();
31✔
1614
}
31✔
1615

1616
void handleServerStateChange(const string& nameWithAddr, bool newResult)
1617
{
654✔
1618
  try {
654✔
1619
    auto lua = g_lua.lock();
654✔
1620
    dnsdist::lua::hooks::runServerStateChangeHooks(*lua, nameWithAddr, newResult);
654✔
1621
  }
654✔
1622
  catch (const std::exception& exp) {
654✔
1623
    warnlog("Error calling the Lua hook for Server State Change: %s", exp.what());
×
1624
  }
×
1625
}
654✔
1626

1627
class UDPTCPCrossQuerySender : public TCPQuerySender
1628
{
1629
public:
1630
  UDPTCPCrossQuerySender() = default;
766✔
1631
  UDPTCPCrossQuerySender(const UDPTCPCrossQuerySender&) = delete;
1632
  UDPTCPCrossQuerySender& operator=(const UDPTCPCrossQuerySender&) = delete;
1633
  UDPTCPCrossQuerySender(UDPTCPCrossQuerySender&&) = default;
1634
  UDPTCPCrossQuerySender& operator=(UDPTCPCrossQuerySender&&) = default;
1635
  ~UDPTCPCrossQuerySender() override = default;
1636

1637
  [[nodiscard]] bool active() const override
1638
  {
147✔
1639
    return true;
147✔
1640
  }
147✔
1641

1642
  void handleResponse(const struct timeval& now, TCPResponse&& response) override
1643
  {
170✔
1644
    (void)now;
170✔
1645
    if (!response.d_ds && !response.d_idstate.selfGenerated) {
170!
1646
      throw std::runtime_error("Passing a cross-protocol answer originated from UDP without a valid downstream");
×
1647
    }
×
1648

1649
    auto& ids = response.d_idstate;
170✔
1650

1651
    handleResponseForUDPClient(ids, response.d_buffer, response.d_ds, response.isAsync(), response.d_idstate.selfGenerated);
170✔
1652
  }
170✔
1653

1654
  void handleXFRResponse(const struct timeval& now, TCPResponse&& response) override
1655
  {
×
1656
    return handleResponse(now, std::move(response));
×
1657
  }
×
1658

1659
  void notifyIOError([[maybe_unused]] const struct timeval& now, [[maybe_unused]] TCPResponse&& response) override
1660
  {
12✔
1661
    // nothing to do
1662
  }
12✔
1663
};
1664

1665
class UDPCrossProtocolQuery : public CrossProtocolQuery
1666
{
1667
public:
1668
  UDPCrossProtocolQuery(PacketBuffer&& buffer_, InternalQueryState&& ids_, std::shared_ptr<DownstreamState> backend) :
1669
    CrossProtocolQuery(InternalQuery(std::move(buffer_), std::move(ids_)), backend)
503✔
1670
  {
503✔
1671
    auto& ids = query.d_idstate;
503✔
1672
    const auto& buffer = query.d_buffer;
503✔
1673

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

1692
  std::shared_ptr<TCPQuerySender> getTCPQuerySender() override
1693
  {
184✔
1694
    return s_sender;
184✔
1695
  }
184✔
1696

1697
private:
1698
  static std::shared_ptr<UDPTCPCrossQuerySender> s_sender;
1699
};
1700

1701
std::shared_ptr<UDPTCPCrossQuerySender> UDPCrossProtocolQuery::s_sender = std::make_shared<UDPTCPCrossQuerySender>();
1702

1703
std::unique_ptr<CrossProtocolQuery> getUDPCrossProtocolQueryFromDQ(DNSQuestion& dnsQuestion);
1704
std::unique_ptr<CrossProtocolQuery> getUDPCrossProtocolQueryFromDQ(DNSQuestion& dnsQuestion)
1705
{
367✔
1706
  dnsQuestion.ids.origID = dnsQuestion.getHeader()->id;
367✔
1707
  return std::make_unique<UDPCrossProtocolQuery>(std::move(dnsQuestion.getMutableData()), std::move(dnsQuestion.ids), nullptr);
367✔
1708
}
367✔
1709

1710
ProcessQueryResult processQuery(DNSQuestion& dnsQuestion, std::shared_ptr<DownstreamState>& selectedBackend)
1711
{
6,069✔
1712
  const uint16_t queryId = ntohs(dnsQuestion.getHeader()->id);
6,069✔
1713

1714
  try {
6,069✔
1715
    /* we need an accurate ("real") value for the response and
1716
       to store into the IDS, but not for insertion into the
1717
       rings for example */
1718
    timespec now{};
6,069✔
1719
    gettime(&now);
6,069✔
1720

1721
    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,069✔
1722
      dnsdist::self_answers::removeRecordsAndSetRCode(dnsQuestion, RCode::NotImp);
8✔
1723
      return processQueryAfterRules(dnsQuestion, selectedBackend);
8✔
1724
    }
8✔
1725

1726
    if (!applyRulesToQuery(dnsQuestion, now)) {
6,061✔
1727
      return ProcessQueryResult::Drop;
47✔
1728
    }
47✔
1729

1730
    if (dnsQuestion.isAsynchronous()) {
6,014✔
1731
      return ProcessQueryResult::Asynchronous;
202✔
1732
    }
202✔
1733

1734
    return processQueryAfterRules(dnsQuestion, selectedBackend);
5,812✔
1735
  }
6,014✔
1736
  catch (const std::exception& e) {
6,069✔
1737
    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());
×
1738
  }
×
1739
  return ProcessQueryResult::Drop;
×
1740
}
6,069✔
1741

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

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

1761
  if (doh && !dnsQuestion.ids.d_packet) {
2,643✔
1762
    dnsQuestion.ids.d_packet = std::make_unique<PacketBuffer>(query);
121✔
1763
  }
121✔
1764

1765
  try {
2,643✔
1766
    int descriptor = downstream->pickSocketForSending();
2,643✔
1767
    if (actuallySend) {
2,643!
1768
      dnsQuestion.ids.backendFD = descriptor;
2,643✔
1769
    }
2,643✔
1770
    dnsQuestion.ids.origID = queryID;
2,643✔
1771
    dnsQuestion.ids.forwardedOverUDP = true;
2,643✔
1772

1773
    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!
1774

1775
    /* make a copy since we cannot touch dnsQuestion.ids after the move */
1776
    auto proxyProtocolPayloadSize = dnsQuestion.ids.d_proxyProtocolPayloadSize;
2,643✔
1777
    auto idOffset = downstream->saveState(std::move(dnsQuestion.ids));
2,643✔
1778
    /* set the correct ID */
1779
    memcpy(&query.at(proxyProtocolPayloadSize), &idOffset, sizeof(idOffset));
2,643✔
1780

1781
    if (!actuallySend) {
2,643!
1782
      return true;
×
1783
    }
×
1784

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

1789
    if (ret < 0) {
2,643!
1790
      failed = true;
×
1791
    }
×
1792

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

1809
  return true;
2,643✔
1810
}
2,643✔
1811

1812
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)
1813
{
2,895✔
1814
  assert(responsesVect == nullptr || (queuedResponses != nullptr && respIOV != nullptr && respCBuf != nullptr));
2,895!
1815
  uint16_t queryId = 0;
2,895✔
1816
  InternalQueryState ids;
2,895✔
1817
  ids.cs = &clientState;
2,895✔
1818
  ids.origRemote = remote;
2,895✔
1819
  ids.hopRemote = remote;
2,895✔
1820
  ids.protocol = dnsdist::Protocol::DoUDP;
2,895✔
1821

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

1842
    std::vector<ProxyProtocolValue> proxyProtocolValues;
2,894✔
1843
    if (expectProxyProtocol && !handleProxyProtocol(remote, false, dnsdist::configuration::getCurrentRuntimeConfiguration().d_ACL, query, ids.origRemote, ids.origDest, proxyProtocolValues)) {
2,894✔
1844
      return;
1✔
1845
    }
1✔
1846

1847
    ids.queryRealTime.start();
2,893✔
1848

1849
    auto dnsCryptResponse = checkDNSCryptQuery(clientState, query, ids.dnsCryptQuery, ids.queryRealTime.d_start.tv_sec, false);
2,893✔
1850
    if (dnsCryptResponse) {
2,893✔
1851
      sendUDPResponse(clientState.udpFD, query, 0, dest, remote);
21✔
1852
      return;
21✔
1853
    }
21✔
1854

1855
    {
2,872✔
1856
      /* this pointer will be invalidated the second the buffer is resized, don't hold onto it! */
1857
      const dnsheader_aligned dnsHeader(query.data());
2,872✔
1858
      queryId = ntohs(dnsHeader->id);
2,872✔
1859

1860
      if (!checkQueryHeaders(*dnsHeader, clientState)) {
2,872✔
1861
        return;
1✔
1862
      }
1✔
1863

1864
      if (dnsHeader->qdcount == 0) {
2,871✔
1865
        dnsdist::PacketMangling::editDNSHeaderFromPacket(query, [](dnsheader& header) {
1✔
1866
          header.rcode = RCode::NotImp;
1✔
1867
          header.qr = true;
1✔
1868
          return true;
1✔
1869
        });
1✔
1870

1871
        sendUDPResponse(clientState.udpFD, query, 0, dest, remote);
1✔
1872
        return;
1✔
1873
      }
1✔
1874
    }
2,871✔
1875

1876
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
1877
    ids.qname = DNSName(reinterpret_cast<const char*>(query.data()), query.size(), sizeof(dnsheader), false, &ids.qtype, &ids.qclass);
2,870✔
1878
    if (ids.dnsCryptQuery) {
2,870✔
1879
      ids.protocol = dnsdist::Protocol::DNSCryptUDP;
20✔
1880
    }
20✔
1881
    DNSQuestion dnsQuestion(ids, query);
2,870✔
1882
    const uint16_t* flags = getFlagsFromDNSHeader(dnsQuestion.getHeader().get());
2,870✔
1883
    ids.origFlags = *flags;
2,870✔
1884

1885
    if (!proxyProtocolValues.empty()) {
2,870✔
1886
      dnsQuestion.proxyProtocolValues = make_unique<std::vector<ProxyProtocolValue>>(std::move(proxyProtocolValues));
6✔
1887
    }
6✔
1888

1889
    // save UDP payload size from origin query
1890
    uint16_t udpPayloadSize = 0;
2,870✔
1891
    uint16_t zValue = 0;
2,870✔
1892
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
1893
    getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(query.data()), query.size(), &udpPayloadSize, &zValue);
2,870✔
1894
    if (!ids.dnssecOK) {
2,870✔
1895
      ids.dnssecOK = (zValue & EDNS_HEADER_FLAG_DO) != 0;
2,866✔
1896
    }
2,866✔
1897
    if (udpPayloadSize < 512) {
2,870✔
1898
      udpPayloadSize = 512;
2,727✔
1899
    }
2,727✔
1900

1901
    std::shared_ptr<DownstreamState> backend{nullptr};
2,870✔
1902
    auto result = processQuery(dnsQuestion, backend);
2,870✔
1903

1904
    if (result == ProcessQueryResult::Drop || result == ProcessQueryResult::Asynchronous) {
2,870✔
1905
      return;
67✔
1906
    }
67✔
1907

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

1926
      handleResponseSent(dnsQuestion.ids.qname, dnsQuestion.ids.qtype, 0., remote, ComboAddress(), query.size(), *dnsHeader, dnsdist::Protocol::DoUDP, dnsdist::Protocol::DoUDP, false);
454✔
1927
      return;
454✔
1928
    }
460✔
1929

1930
    if (result != ProcessQueryResult::PassToBackend || backend == nullptr) {
2,343!
1931
      return;
×
1932
    }
×
1933

1934
    if (backend->isTCPOnly()) {
2,343✔
1935
      std::string proxyProtocolPayload;
136✔
1936
      /* we need to do this _before_ creating the cross protocol query because
1937
         after that the buffer will have been moved */
1938
      if (backend->d_config.useProxyProtocol) {
136✔
1939
        proxyProtocolPayload = getProxyProtocolPayload(dnsQuestion);
1✔
1940
      }
1✔
1941

1942
      ids.origID = dnsHeader->id;
136✔
1943
      auto cpq = std::make_unique<UDPCrossProtocolQuery>(std::move(query), std::move(ids), backend);
136✔
1944
      cpq->query.d_proxyProtocolPayload = std::move(proxyProtocolPayload);
136✔
1945

1946
      backend->passCrossProtocolQuery(std::move(cpq));
136✔
1947
      return;
136✔
1948
    }
136✔
1949

1950
    assignOutgoingUDPQueryToBackend(backend, dnsHeader->id, dnsQuestion, query);
2,207✔
1951
  }
2,207✔
1952
  catch (const std::exception& e) {
2,895✔
1953
    vinfolog("Got an error in UDP question thread while parsing a query from %s, id %d: %s", ids.origRemote.toStringWithPort(), queryId, e.what());
4!
1954
  }
4✔
1955
}
2,895✔
1956

1957
#ifdef HAVE_XSK
1958
namespace dnsdist::xsk
1959
{
1960
bool XskProcessQuery(ClientState& clientState, XskPacket& packet)
1961
{
×
1962
  uint16_t queryId = 0;
×
1963
  const auto& remote = packet.getFromAddr();
×
1964
  const auto& dest = packet.getToAddr();
×
1965
  InternalQueryState ids;
×
1966
  ids.cs = &clientState;
×
1967
  ids.origRemote = remote;
×
1968
  ids.hopRemote = remote;
×
1969
  ids.origDest = dest;
×
1970
  ids.hopLocal = dest;
×
1971
  ids.protocol = dnsdist::Protocol::DoUDP;
×
1972
  ids.xskPacketHeader = packet.cloneHeaderToPacketBuffer();
×
1973

1974
  try {
×
1975
    bool expectProxyProtocol = false;
×
1976
    if (!XskIsQueryAcceptable(packet, clientState, expectProxyProtocol)) {
×
1977
      return false;
×
1978
    }
×
1979

1980
    auto query = packet.clonePacketBuffer();
×
1981
    std::vector<ProxyProtocolValue> proxyProtocolValues;
×
1982
    if (expectProxyProtocol && !handleProxyProtocol(remote, false, dnsdist::configuration::getCurrentRuntimeConfiguration().d_ACL, query, ids.origRemote, ids.origDest, proxyProtocolValues)) {
×
1983
      return false;
×
1984
    }
×
1985

1986
    ids.queryRealTime.start();
×
1987

1988
    auto dnsCryptResponse = checkDNSCryptQuery(clientState, query, ids.dnsCryptQuery, ids.queryRealTime.d_start.tv_sec, false);
×
1989
    if (dnsCryptResponse) {
×
1990
      packet.setPayload(query);
×
1991
      return true;
×
1992
    }
×
1993

1994
    {
×
1995
      /* this pointer will be invalidated the second the buffer is resized, don't hold onto it! */
1996
      dnsheader_aligned dnsHeader(query.data());
×
1997
      queryId = ntohs(dnsHeader->id);
×
1998

1999
      if (!checkQueryHeaders(*dnsHeader, clientState)) {
×
2000
        return false;
×
2001
      }
×
2002

2003
      if (dnsHeader->qdcount == 0) {
×
2004
        dnsdist::PacketMangling::editDNSHeaderFromPacket(query, [](dnsheader& header) {
×
2005
          header.rcode = RCode::NotImp;
×
2006
          header.qr = true;
×
2007
          return true;
×
2008
        });
×
2009
        packet.setPayload(query);
×
2010
        return true;
×
2011
      }
×
2012
    }
×
2013

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

2029
    if (result == ProcessQueryResult::Drop) {
×
2030
      return false;
×
2031
    }
×
2032

2033
    if (result == ProcessQueryResult::SendAnswer) {
×
2034
      packet.setPayload(query);
×
2035
      if (dnsQuestion.ids.delayMsec > 0) {
×
2036
        packet.addDelay(dnsQuestion.ids.delayMsec);
×
2037
      }
×
2038
      const auto dnsHeader = dnsQuestion.getHeader();
×
2039
      handleResponseSent(ids.qname, ids.qtype, 0., remote, ComboAddress(), query.size(), *dnsHeader, dnsdist::Protocol::DoUDP, dnsdist::Protocol::DoUDP, false);
×
2040
      return true;
×
2041
    }
×
2042

2043
    if (result != ProcessQueryResult::PassToBackend || backend == nullptr) {
×
2044
      return false;
×
2045
    }
×
2046

2047
    // the buffer might have been invalidated by now (resized)
2048
    const auto dnsHeader = dnsQuestion.getHeader();
×
2049
    if (backend->isTCPOnly()) {
×
2050
      std::string proxyProtocolPayload;
×
2051
      /* we need to do this _before_ creating the cross protocol query because
2052
         after that the buffer will have been moved */
2053
      if (backend->d_config.useProxyProtocol) {
×
2054
        proxyProtocolPayload = getProxyProtocolPayload(dnsQuestion);
×
2055
      }
×
2056

2057
      ids.origID = dnsHeader->id;
×
2058
      auto cpq = std::make_unique<UDPCrossProtocolQuery>(std::move(query), std::move(ids), backend);
×
2059
      cpq->query.d_proxyProtocolPayload = std::move(proxyProtocolPayload);
×
2060

2061
      backend->passCrossProtocolQuery(std::move(cpq));
×
2062
      return false;
×
2063
    }
×
2064

2065
    if (backend->d_xskInfos.empty()) {
×
2066
      assignOutgoingUDPQueryToBackend(backend, dnsHeader->id, dnsQuestion, query, true);
×
2067
      return false;
×
2068
    }
×
2069

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

2083
}
2084
#endif /* HAVE_XSK */
2085

2086
#ifndef DISABLE_RECVMMSG
2087
#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
2088
static void MultipleMessagesUDPClientThread(ClientState* clientState)
2089
{
2✔
2090
  struct MMReceiver
2✔
2091
  {
2✔
2092
    PacketBuffer packet;
2✔
2093
    ComboAddress remote;
2✔
2094
    ComboAddress dest;
2✔
2095
    iovec iov{};
2✔
2096
    /* used by HarvestDestinationAddress */
2097
    cmsgbuf_aligned cbuf{};
2✔
2098
  };
2✔
2099
  const size_t vectSize = dnsdist::configuration::getImmutableConfiguration().d_udpVectorSize;
2✔
2100

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

2105
  auto recvData = std::vector<MMReceiver>(vectSize);
2✔
2106
  auto msgVec = std::vector<mmsghdr>(vectSize);
2✔
2107
  auto outMsgVec = std::vector<mmsghdr>(vectSize);
2✔
2108

2109
  /* the actual buffer is larger because:
2110
     - we may have to add EDNS and/or ECS
2111
     - we use it for self-generated responses (from rule or cache)
2112
     but we only accept incoming payloads up to that size
2113
  */
2114
  const size_t initialBufferSize = getInitialUDPPacketBufferSize(clientState->d_enableProxyProtocol);
2✔
2115
  const size_t maxIncomingPacketSize = getMaximumIncomingPacketSize(*clientState);
2✔
2116

2117
  /* initialize the structures needed to receive our messages */
2118
  for (size_t idx = 0; idx < vectSize; idx++) {
22✔
2119
    recvData[idx].remote.sin4.sin_family = clientState->local.sin4.sin_family;
20✔
2120
    recvData[idx].packet.resize(initialBufferSize);
20✔
2121
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
2122
    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✔
2123
  }
20✔
2124

2125
  /* go now */
2126
  for (;;) {
13✔
2127

2128
    /* reset the IO vector, since it's also used to send the vector of responses
2129
       to avoid having to copy the data around */
2130
    for (size_t idx = 0; idx < vectSize; idx++) {
143✔
2131
      recvData[idx].packet.resize(initialBufferSize);
130✔
2132
      recvData[idx].iov.iov_base = &recvData[idx].packet.at(0);
130✔
2133
      recvData[idx].iov.iov_len = recvData[idx].packet.size();
130✔
2134
    }
130✔
2135

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

2140
    if (msgsGot <= 0) {
13!
2141
      vinfolog("Getting UDP messages via recvmmsg() failed with: %s", stringerror());
×
2142
      continue;
×
2143
    }
×
2144

2145
    unsigned int msgsToSend = 0;
13✔
2146

2147
    /* process the received messages */
2148
    for (int msgIdx = 0; msgIdx < msgsGot; msgIdx++) {
24✔
2149
      const struct msghdr* msgh = &msgVec[msgIdx].msg_hdr;
11✔
2150
      unsigned int got = msgVec[msgIdx].msg_len;
11✔
2151
      const ComboAddress& remote = recvData[msgIdx].remote;
11✔
2152

2153
      if (static_cast<size_t>(got) < sizeof(struct dnsheader)) {
11!
2154
        ++dnsdist::metrics::g_stats.nonCompliantQueries;
×
2155
        ++clientState->nonCompliantQueries;
×
2156
        continue;
×
2157
      }
×
2158

2159
      recvData[msgIdx].packet.resize(got);
11✔
2160
      dnsdist::configuration::refreshLocalRuntimeConfiguration();
11✔
2161
      processUDPQuery(*clientState, msgh, remote, recvData[msgIdx].dest, recvData[msgIdx].packet, &outMsgVec, &msgsToSend, &recvData[msgIdx].iov, &recvData[msgIdx].cbuf);
11✔
2162
    }
11✔
2163

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

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

2170
      if (sent < 0 || static_cast<unsigned int>(sent) != msgsToSend) {
6!
2171
        vinfolog("Error sending responses with sendmmsg() (%d on %u): %s", sent, msgsToSend, stringerror());
×
2172
      }
×
2173
    }
6✔
2174
  }
13✔
2175
}
2✔
2176
#endif /* defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE) */
2177
#endif /* DISABLE_RECVMMSG */
2178

2179
// listens to incoming queries, sends out to downstream servers, noting the intended return path
2180
static void udpClientThread(std::vector<ClientState*> states)
2181
{
378✔
2182
  try {
378✔
2183
    setThreadName("dnsdist/udpClie");
378✔
2184
#ifndef DISABLE_RECVMMSG
378✔
2185
#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
378✔
2186
    if (dnsdist::configuration::getImmutableConfiguration().d_udpVectorSize > 1) {
378✔
2187
      MultipleMessagesUDPClientThread(states.at(0));
2✔
2188
    }
2✔
2189
    else
376✔
2190
#endif /* defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE) */
376✔
2191
#endif /* DISABLE_RECVMMSG */
376✔
2192
    {
376✔
2193
      /* the actual buffer is larger because:
2194
         - we may have to add EDNS and/or ECS
2195
         - we use it for self-generated responses (from rule or cache)
2196
         but we only accept incoming payloads up to that size
2197
      */
2198
      struct UDPStateParam
376✔
2199
      {
376✔
2200
        ClientState* cs{nullptr};
376✔
2201
        size_t maxIncomingPacketSize{0};
376✔
2202
        int socket{-1};
376✔
2203
      };
376✔
2204
      const size_t initialBufferSize = getInitialUDPPacketBufferSize(true);
376✔
2205
      PacketBuffer packet(initialBufferSize);
376✔
2206

2207
      msghdr msgh{};
376✔
2208
      iovec iov{};
376✔
2209
      ComboAddress remote;
376✔
2210
      ComboAddress dest;
376✔
2211

2212
      auto handleOnePacket = [&packet, &iov, &msgh, &remote, &dest, initialBufferSize](const UDPStateParam& param) {
3,260✔
2213
        packet.resize(initialBufferSize);
3,260✔
2214
        iov.iov_base = &packet.at(0);
3,260✔
2215
        iov.iov_len = packet.size();
3,260✔
2216

2217
        ssize_t got = recvmsg(param.socket, &msgh, 0);
3,260✔
2218

2219
        if (got < 0 || static_cast<size_t>(got) < sizeof(struct dnsheader)) {
3,260!
2220
          ++dnsdist::metrics::g_stats.nonCompliantQueries;
×
2221
          ++param.cs->nonCompliantQueries;
×
2222
          return;
×
2223
        }
×
2224

2225
        packet.resize(static_cast<size_t>(got));
3,260✔
2226

2227
        dnsdist::configuration::refreshLocalRuntimeConfiguration();
3,260✔
2228
        processUDPQuery(*param.cs, &msgh, remote, dest, packet, nullptr, nullptr, nullptr, nullptr);
3,260✔
2229
      };
3,260✔
2230

2231
      std::vector<UDPStateParam> params;
376✔
2232
      for (auto& state : states) {
376✔
2233
        const size_t maxIncomingPacketSize = getMaximumIncomingPacketSize(*state);
376✔
2234
        params.emplace_back(UDPStateParam{state, maxIncomingPacketSize, state->udpFD});
376✔
2235
      }
376✔
2236

2237
      if (params.size() == 1) {
376!
2238
        const auto& param = params.at(0);
376✔
2239
        remote.sin4.sin_family = param.cs->local.sin4.sin_family;
376✔
2240
        /* used by HarvestDestinationAddress */
2241
        cmsgbuf_aligned cbuf;
376✔
2242
        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
2243
        fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), reinterpret_cast<char*>(&packet.at(0)), param.maxIncomingPacketSize, &remote);
376✔
2244
        while (true) {
3,260✔
2245
          try {
3,260✔
2246
            handleOnePacket(param);
3,260✔
2247
          }
3,260✔
2248
          catch (const std::bad_alloc& e) {
3,260✔
2249
            /* most exceptions are handled by handleOnePacket(), but we might be out of memory (std::bad_alloc)
2250
               in which case we DO NOT want to log (as it would trigger another memory allocation attempt
2251
               that might throw as well) but wait a bit (one millisecond) and then try to recover */
2252
            usleep(1000);
×
2253
          }
×
2254
        }
3,260✔
2255
      }
376✔
2256
      else {
×
2257
        auto callback = [&remote, &msgh, &iov, &packet, &handleOnePacket, initialBufferSize](int socket, FDMultiplexer::funcparam_t& funcparam) {
×
2258
          (void)socket;
×
2259
          const auto* param = boost::any_cast<const UDPStateParam*>(funcparam);
×
2260
          try {
×
2261
            remote.sin4.sin_family = param->cs->local.sin4.sin_family;
×
2262
            packet.resize(initialBufferSize);
×
2263
            /* used by HarvestDestinationAddress */
2264
            cmsgbuf_aligned cbuf;
×
2265
            // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
2266
            fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), reinterpret_cast<char*>(&packet.at(0)), param->maxIncomingPacketSize, &remote);
×
2267
            handleOnePacket(*param);
×
2268
          }
×
2269
          catch (const std::bad_alloc& e) {
×
2270
            /* most exceptions are handled by handleOnePacket(), but we might be out of memory (std::bad_alloc)
2271
               in which case we DO NOT want to log (as it would trigger another memory allocation attempt
2272
               that might throw as well) but wait a bit (one millisecond) and then try to recover */
2273
            usleep(1000);
×
2274
          }
×
2275
        };
×
2276
        auto mplexer = std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent(params.size()));
×
2277
        for (const auto& param : params) {
×
2278
          mplexer->addReadFD(param.socket, callback, &param);
×
2279
        }
×
2280

2281
        timeval now{};
×
2282
        while (true) {
×
2283
          mplexer->run(&now, -1);
×
2284
        }
×
2285
      }
×
2286
    }
376✔
2287
  }
378✔
2288
  catch (const std::exception& e) {
378✔
2289
    errlog("UDP client thread died because of exception: %s", e.what());
×
2290
  }
×
2291
  catch (const PDNSException& e) {
378✔
2292
    errlog("UDP client thread died because of PowerDNS exception: %s", e.reason);
×
2293
  }
×
2294
  catch (...) {
378✔
2295
    errlog("UDP client thread died because of an exception: %s", "unknown");
×
2296
  }
×
2297
}
378✔
2298

2299
static void maintThread()
2300
{
377✔
2301
  setThreadName("dnsdist/main");
377✔
2302
  constexpr int interval = 1;
377✔
2303
  size_t counter = 0;
377✔
2304
  int32_t secondsToWaitLog = 0;
377✔
2305

2306
  for (;;) {
1,563✔
2307
    std::this_thread::sleep_for(std::chrono::seconds(interval));
1,563✔
2308

2309
    dnsdist::configuration::refreshLocalRuntimeConfiguration();
1,563✔
2310
    {
1,563✔
2311
      auto lua = g_lua.lock();
1,563✔
2312
      try {
1,563✔
2313
        auto maintenanceCallback = lua->readVariable<boost::optional<std::function<void()>>>("maintenance");
1,563✔
2314
        if (maintenanceCallback) {
1,563✔
2315
          (*maintenanceCallback)();
330✔
2316
        }
330✔
2317
        dnsdist::lua::hooks::runMaintenanceHooks(*lua);
1,563✔
2318
#if !defined(DISABLE_DYNBLOCKS)
1,563✔
2319
        dnsdist::DynamicBlocks::runRegisteredGroups(*lua);
1,563✔
2320
#endif /* DISABLE_DYNBLOCKS */
1,563✔
2321
        secondsToWaitLog = 0;
1,563✔
2322
      }
1,563✔
2323
      catch (const std::exception& e) {
1,563✔
2324
        if (secondsToWaitLog <= 0) {
×
2325
          warnlog("Error during execution of maintenance function(s): %s", e.what());
×
2326
          secondsToWaitLog = 61;
×
2327
        }
×
2328
        secondsToWaitLog -= interval;
×
2329
      }
×
2330
    }
1,563✔
2331

2332
    counter++;
1,563✔
2333
    if (counter >= dnsdist::configuration::getCurrentRuntimeConfiguration().d_cacheCleaningDelay) {
1,186✔
2334
      /* keep track, for each cache, of whether we should keep
2335
       expired entries */
2336
      std::map<std::shared_ptr<DNSDistPacketCache>, bool> caches;
12✔
2337

2338
      /* gather all caches actually used by at least one pool, and see
2339
         if something prevents us from cleaning the expired entries */
2340
      const auto& pools = dnsdist::configuration::getCurrentRuntimeConfiguration().d_pools;
12✔
2341
      for (const auto& entry : pools) {
12✔
2342
        const auto& pool = entry.second;
11✔
2343

2344
        auto packetCache = pool->packetCache;
11✔
2345
        if (!packetCache) {
11!
2346
          continue;
×
2347
        }
×
2348

2349
        auto pair = caches.insert({packetCache, false});
11✔
2350
        auto& iter = pair.first;
11✔
2351
        /* if we need to keep stale data for this cache (ie, not clear
2352
           expired entries when at least one pool using this cache
2353
           has all its backends down) */
2354
        if (packetCache->keepStaleData() && !iter->second) {
11!
2355
          /* so far all pools had at least one backend up */
2356
          if (pool->countServers(true) == 0) {
4!
2357
            iter->second = true;
4✔
2358
          }
4✔
2359
        }
4✔
2360
      }
11✔
2361

2362
      const time_t now = time(nullptr);
12✔
2363
      for (const auto& pair : caches) {
12✔
2364
        /* shall we keep expired entries ? */
2365
        if (pair.second) {
11✔
2366
          continue;
4✔
2367
        }
4✔
2368
        const auto& packetCache = pair.first;
7✔
2369
        size_t upTo = (packetCache->getMaxEntries() * (100 - dnsdist::configuration::getCurrentRuntimeConfiguration().d_cacheCleaningPercentage)) / 100;
7✔
2370
        packetCache->purgeExpired(upTo, now);
7✔
2371
      }
7✔
2372
      counter = 0;
12✔
2373
    }
12✔
2374
  }
1,186✔
2375
}
377✔
2376

2377
#ifndef DISABLE_DYNBLOCKS
2378
static void dynBlockMaintenanceThread()
2379
{
377✔
2380
  setThreadName("dnsdist/dynBloc");
377✔
2381

2382
  dnsdist::configuration::refreshLocalRuntimeConfiguration();
377✔
2383
  DynBlockMaintenance::run();
377✔
2384
}
377✔
2385
#endif
2386

2387
#ifndef DISABLE_SECPOLL
2388
static void secPollThread()
2389
{
×
2390
  setThreadName("dnsdist/secpoll");
×
2391

2392
  for (;;) {
×
2393
    const auto& runtimeConfig = dnsdist::configuration::refreshLocalRuntimeConfiguration();
×
2394

2395
    try {
×
2396
      dnsdist::secpoll::doSecPoll(runtimeConfig.d_secPollSuffix);
×
2397
    }
×
2398
    catch (...) {
×
2399
    }
×
2400
    // coverity[store_truncates_time_t]
2401
    std::this_thread::sleep_for(std::chrono::seconds(runtimeConfig.d_secPollInterval));
×
2402
  }
×
2403
}
×
2404
#endif /* DISABLE_SECPOLL */
2405

2406
static std::atomic<bool> s_exiting{false};
2407
void doExitNicely(int exitCode = EXIT_SUCCESS);
2408

2409
static void checkExiting()
2410
{
1,938✔
2411
  if (s_exiting) {
1,938✔
2412
    doExitNicely();
377✔
2413
  }
377✔
2414
}
1,938✔
2415

2416
static void healthChecksThread()
2417
{
377✔
2418
  setThreadName("dnsdist/healthC");
377✔
2419

2420
  constexpr int intervalUsec = 1000 * 1000;
377✔
2421
  struct timeval lastRound{
377✔
2422
    .tv_sec = 0,
377✔
2423
    .tv_usec = 0};
377✔
2424

2425
  for (;;) {
1,938✔
2426
    checkExiting();
1,938✔
2427

2428
    timeval now{};
1,938✔
2429
    gettimeofday(&now, nullptr);
1,938✔
2430
    auto elapsedTimeUsec = uSec(now - lastRound);
1,938✔
2431
    if (elapsedTimeUsec < intervalUsec) {
1,938✔
2432
      usleep(intervalUsec - elapsedTimeUsec);
1,181✔
2433
      gettimeofday(&lastRound, nullptr);
1,181✔
2434
    }
1,181✔
2435
    else {
757✔
2436
      lastRound = now;
757✔
2437
    }
757✔
2438

2439
    std::unique_ptr<FDMultiplexer> mplexer{nullptr};
1,938✔
2440
    const auto& runtimeConfig = dnsdist::configuration::refreshLocalRuntimeConfiguration();
1,938✔
2441

2442
    // this points to the actual shared_ptrs!
2443
    // coverity[auto_causes_copy]
2444
    const auto servers = runtimeConfig.d_backends;
1,938✔
2445
    for (const auto& dss : servers) {
1,938✔
2446
      dss->updateStatisticsInfo();
1,935✔
2447

2448
      dss->handleUDPTimeouts();
1,935✔
2449

2450
      if (!dss->healthCheckRequired()) {
1,935✔
2451
        continue;
516✔
2452
      }
516✔
2453

2454
      if (!mplexer) {
1,419✔
2455
        mplexer = std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent(servers.size()));
1,362✔
2456
      }
1,362✔
2457

2458
      if (!queueHealthCheck(mplexer, dss)) {
1,419✔
2459
        dss->submitHealthCheckResult(false, false);
22✔
2460
      }
22✔
2461
    }
1,419✔
2462

2463
    if (mplexer) {
1,938✔
2464
      handleQueuedHealthChecks(*mplexer);
1,362✔
2465
    }
1,362✔
2466
  }
1,938✔
2467
}
377✔
2468

2469
static void bindAny([[maybe_unused]] int addressFamily, [[maybe_unused]] int sock)
2470
{
897✔
2471
  __attribute__((unused)) int one = 1;
897✔
2472

2473
#ifdef IP_FREEBIND
897✔
2474
  if (setsockopt(sock, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0) {
897!
2475
    warnlog("Warning: IP_FREEBIND setsockopt failed: %s", stringerror());
×
2476
  }
×
2477
#endif
897✔
2478

2479
#ifdef IP_BINDANY
2480
  if (addressFamily == AF_INET) {
2481
    if (setsockopt(sock, IPPROTO_IP, IP_BINDANY, &one, sizeof(one)) < 0) {
2482
      warnlog("Warning: IP_BINDANY setsockopt failed: %s", stringerror());
2483
    }
2484
  }
2485
#endif
2486
#ifdef IPV6_BINDANY
2487
  if (addressFamily == AF_INET6) {
2488
    if (setsockopt(sock, IPPROTO_IPV6, IPV6_BINDANY, &one, sizeof(one)) < 0) {
2489
      warnlog("Warning: IPV6_BINDANY setsockopt failed: %s", stringerror());
2490
    }
2491
  }
2492
#endif
2493
#ifdef SO_BINDANY
2494
  if (setsockopt(sock, SOL_SOCKET, SO_BINDANY, &one, sizeof(one)) < 0) {
2495
    warnlog("Warning: SO_BINDANY setsockopt failed: %s", stringerror());
2496
  }
2497
#endif
2498
}
897✔
2499

2500
static void dropGroupPrivs(gid_t gid)
2501
{
×
2502
  if (gid != 0) {
×
2503
    if (setgid(gid) == 0) {
×
2504
      if (setgroups(0, nullptr) < 0) {
×
2505
        warnlog("Warning: Unable to drop supplementary gids: %s", stringerror());
×
2506
      }
×
2507
    }
×
2508
    else {
×
2509
      warnlog("Warning: Unable to set group ID to %d: %s", gid, stringerror());
×
2510
    }
×
2511
  }
×
2512
}
×
2513

2514
static void dropUserPrivs(uid_t uid)
2515
{
×
2516
  if (uid != 0) {
×
2517
    if (setuid(uid) < 0) {
×
2518
      warnlog("Warning: Unable to set user ID to %d: %s", uid, stringerror());
×
2519
    }
×
2520
  }
×
2521
}
×
2522

2523
static void checkFileDescriptorsLimits(size_t udpBindsCount, size_t tcpBindsCount)
2524
{
377✔
2525
  const auto& immutableConfig = dnsdist::configuration::getImmutableConfiguration();
377✔
2526
  /* stdin, stdout, stderr */
2527
  rlim_t requiredFDsCount = 3;
377✔
2528
  const auto& backends = dnsdist::configuration::getCurrentRuntimeConfiguration().d_backends;
377✔
2529
  /* UDP sockets to backends */
2530
  size_t backendUDPSocketsCount = 0;
377✔
2531
  for (const auto& backend : backends) {
422✔
2532
    backendUDPSocketsCount += backend->sockets.size();
422✔
2533
  }
422✔
2534
  requiredFDsCount += backendUDPSocketsCount;
377✔
2535
  /* TCP sockets to backends */
2536
  if (immutableConfig.d_maxTCPClientThreads > 0) {
377!
2537
    requiredFDsCount += (backends.size() * immutableConfig.d_maxTCPClientThreads);
377✔
2538
  }
377✔
2539
  /* listening sockets */
2540
  requiredFDsCount += udpBindsCount;
377✔
2541
  requiredFDsCount += tcpBindsCount;
377✔
2542
  /* number of TCP connections currently served, assuming 1 connection per worker thread which is of course not right */
2543
  if (immutableConfig.d_maxTCPClientThreads > 0) {
377!
2544
    requiredFDsCount += immutableConfig.d_maxTCPClientThreads;
377✔
2545
    /* max pipes for communicating between TCP acceptors and client threads */
2546
    requiredFDsCount += (immutableConfig.d_maxTCPClientThreads * 2);
377✔
2547
  }
377✔
2548
  /* max TCP queued connections */
2549
  requiredFDsCount += immutableConfig.d_maxTCPQueuedConnections;
377✔
2550
  /* DelayPipe pipe */
2551
  requiredFDsCount += 2;
377✔
2552
  /* syslog socket */
2553
  requiredFDsCount++;
377✔
2554
  /* webserver main socket */
2555
  requiredFDsCount++;
377✔
2556
  /* console main socket */
2557
  requiredFDsCount++;
377✔
2558
  /* carbon export */
2559
  requiredFDsCount++;
377✔
2560
  /* history file */
2561
  requiredFDsCount++;
377✔
2562
  rlimit resourceLimits{};
377✔
2563
  getrlimit(RLIMIT_NOFILE, &resourceLimits);
377✔
2564
  if (resourceLimits.rlim_cur <= requiredFDsCount) {
377✔
2565
    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✔
2566
#ifdef HAVE_SYSTEMD
2✔
2567
    warnlog("You can increase this value by using LimitNOFILE= in the systemd unit file or ulimit.");
2✔
2568
#else
2569
    warnlog("You can increase this value by using ulimit.");
2570
#endif
2571
  }
2✔
2572
}
377✔
2573

2574
static void setupLocalSocket(ClientState& clientState, const ComboAddress& addr, int& socket, bool tcp, bool warn)
2575
{
897✔
2576
  const auto& immutableConfig = dnsdist::configuration::getImmutableConfiguration();
897✔
2577
  static bool s_warned_ipv6_recvpktinfo = false;
897✔
2578
  (void)warn;
897✔
2579
  socket = SSocket(addr.sin4.sin_family, !tcp ? SOCK_DGRAM : SOCK_STREAM, 0);
897✔
2580

2581
  if (tcp) {
897✔
2582
    SSetsockopt(socket, SOL_SOCKET, SO_REUSEADDR, 1);
482✔
2583
#ifdef TCP_DEFER_ACCEPT
482✔
2584
    SSetsockopt(socket, IPPROTO_TCP, TCP_DEFER_ACCEPT, 1);
482✔
2585
#endif
482✔
2586
    if (clientState.fastOpenQueueSize > 0) {
482!
2587
#ifdef TCP_FASTOPEN
×
2588
      SSetsockopt(socket, IPPROTO_TCP, TCP_FASTOPEN, clientState.fastOpenQueueSize);
×
2589
#ifdef TCP_FASTOPEN_KEY
×
2590
      if (!immutableConfig.d_tcpFastOpenKey.empty()) {
×
2591
        auto res = setsockopt(socket, IPPROTO_IP, TCP_FASTOPEN_KEY, immutableConfig.d_tcpFastOpenKey.data(), immutableConfig.d_tcpFastOpenKey.size() * sizeof(immutableConfig.d_tcpFastOpenKey[0]));
×
2592
        if (res == -1) {
×
2593
          throw runtime_error("setsockopt for level IPPROTO_TCP and opname TCP_FASTOPEN_KEY failed: " + stringerror());
×
2594
        }
×
2595
      }
×
2596
#endif /* TCP_FASTOPEN_KEY */
×
2597
#else /* TCP_FASTOPEN */
2598
      if (warn) {
2599
        warnlog("TCP Fast Open has been configured on local address '%s' but is not supported", addr.toStringWithPort());
2600
      }
2601
#endif /* TCP_FASTOPEN */
2602
    }
×
2603
  }
482✔
2604

2605
  if (addr.sin4.sin_family == AF_INET6) {
897✔
2606
    SSetsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, 1);
4✔
2607
  }
4✔
2608

2609
  bindAny(addr.sin4.sin_family, socket);
897✔
2610

2611
  if (!tcp && IsAnyAddress(addr)) {
897✔
2612
    int one = 1;
7✔
2613
    (void)setsockopt(socket, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)); // linux supports this, so why not - might fail on other systems
7✔
2614
#ifdef IPV6_RECVPKTINFO
7✔
2615
    if (addr.isIPv6() && setsockopt(socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)) < 0 && !s_warned_ipv6_recvpktinfo) {
7!
2616
      warnlog("Warning: IPV6_RECVPKTINFO setsockopt failed: %s", stringerror());
×
2617
      s_warned_ipv6_recvpktinfo = true;
×
2618
    }
×
2619
#endif
7✔
2620
  }
7✔
2621

2622
  if (clientState.reuseport) {
897✔
2623
    if (!setReusePort(socket)) {
18!
2624
      if (warn) {
×
2625
        /* no need to warn again if configured but support is not available, we already did for UDP */
2626
        warnlog("SO_REUSEPORT has been configured on local address '%s' but is not supported", addr.toStringWithPort());
×
2627
      }
×
2628
    }
×
2629
  }
18✔
2630

2631
  const bool isQUIC = clientState.doqFrontend != nullptr || clientState.doh3Frontend != nullptr;
897✔
2632
  if (isQUIC) {
897✔
2633
    /* disable fragmentation and force PMTU discovery for QUIC-enabled sockets */
2634
    try {
37✔
2635
      setSocketForcePMTU(socket, addr.sin4.sin_family);
37✔
2636
    }
37✔
2637
    catch (const std::exception& e) {
37✔
2638
      warnlog("Failed to set IP_MTU_DISCOVER on QUIC server socket for local address '%s': %s", addr.toStringWithPort(), e.what());
×
2639
    }
×
2640
  }
37✔
2641
  else if (!tcp && !clientState.dnscryptCtx) {
860✔
2642
    /* Only set this on IPv4 UDP sockets.
2643
       Don't set it for DNSCrypt binds. DNSCrypt pads queries for privacy
2644
       purposes, so we do receive large, sometimes fragmented datagrams. */
2645
    try {
373✔
2646
      setSocketIgnorePMTU(socket, addr.sin4.sin_family);
373✔
2647
    }
373✔
2648
    catch (const std::exception& e) {
373✔
2649
      warnlog("Failed to set IP_MTU_DISCOVER on UDP server socket for local address '%s': %s", addr.toStringWithPort(), e.what());
×
2650
    }
×
2651
  }
373✔
2652

2653
  if (!tcp) {
897✔
2654
    if (immutableConfig.d_socketUDPSendBuffer > 0) {
415!
2655
      try {
×
2656
        setSocketSendBuffer(socket, immutableConfig.d_socketUDPSendBuffer);
×
2657
      }
×
2658
      catch (const std::exception& e) {
×
2659
        warnlog(e.what());
×
2660
      }
×
2661
    }
×
2662
    else {
415✔
2663
      try {
415✔
2664
        auto result = raiseSocketSendBufferToMax(socket);
415✔
2665
        if (result > 0) {
415!
2666
          infolog("Raised send buffer to %u for local address '%s'", result, addr.toStringWithPort());
415✔
2667
        }
415✔
2668
      }
415✔
2669
      catch (const std::exception& e) {
415✔
2670
        warnlog(e.what());
×
2671
      }
×
2672
    }
415✔
2673

2674
    if (immutableConfig.d_socketUDPRecvBuffer > 0) {
415!
2675
      try {
×
2676
        setSocketReceiveBuffer(socket, immutableConfig.d_socketUDPRecvBuffer);
×
2677
      }
×
2678
      catch (const std::exception& e) {
×
2679
        warnlog(e.what());
×
2680
      }
×
2681
    }
×
2682
    else {
415✔
2683
      try {
415✔
2684
        auto result = raiseSocketReceiveBufferToMax(socket);
415✔
2685
        if (result > 0) {
415!
2686
          infolog("Raised receive buffer to %u for local address '%s'", result, addr.toStringWithPort());
415✔
2687
        }
415✔
2688
      }
415✔
2689
      catch (const std::exception& e) {
415✔
2690
        warnlog(e.what());
×
2691
      }
×
2692
    }
415✔
2693
  }
415✔
2694

2695
  const std::string& itf = clientState.interface;
897✔
2696
  if (!itf.empty()) {
897✔
2697
#ifdef SO_BINDTODEVICE
2✔
2698
    int res = setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, itf.c_str(), itf.length());
2✔
2699
    if (res != 0) {
2!
2700
      warnlog("Error setting up the interface on local address '%s': %s", addr.toStringWithPort(), stringerror());
×
2701
    }
×
2702
#else
2703
    if (warn) {
2704
      warnlog("An interface has been configured on local address '%s' but SO_BINDTODEVICE is not supported", addr.toStringWithPort());
2705
    }
2706
#endif
2707
  }
2✔
2708

2709
#ifdef HAVE_EBPF
897✔
2710
  /* for now eBPF filtering is not enabled on QUIC sockets because the eBPF code tries
2711
     to parse the QNAME from the payload for all UDP datagrams, which obviously does not
2712
     work well for these. */
2713
  if (!isQUIC && g_defaultBPFFilter && !g_defaultBPFFilter->isExternal()) {
897!
2714
    clientState.attachFilter(g_defaultBPFFilter, socket);
6✔
2715
    vinfolog("Attaching default BPF Filter to %s frontend %s", (!tcp ? std::string("UDP") : std::string("TCP")), addr.toStringWithPort());
6!
2716
  }
6✔
2717
#endif /* HAVE_EBPF */
897✔
2718

2719
  SBind(socket, addr);
897✔
2720

2721
  if (tcp) {
897✔
2722
    SListen(socket, clientState.tcpListenQueueSize);
482✔
2723

2724
    if (clientState.tlsFrontend != nullptr) {
482✔
2725
      infolog("Listening on %s for TLS", addr.toStringWithPort());
43✔
2726
    }
43✔
2727
    else if (clientState.dohFrontend != nullptr) {
439✔
2728
      infolog("Listening on %s for DoH", addr.toStringWithPort());
61✔
2729
    }
61✔
2730
    else if (clientState.dnscryptCtx != nullptr) {
378✔
2731
      infolog("Listening on %s for DNSCrypt", addr.toStringWithPort());
5✔
2732
    }
5✔
2733
    else {
373✔
2734
      infolog("Listening on %s", addr.toStringWithPort());
373✔
2735
    }
373✔
2736
  }
482✔
2737
  else {
415✔
2738
    if (clientState.doqFrontend != nullptr) {
415✔
2739
      infolog("Listening on %s for DoQ", addr.toStringWithPort());
20✔
2740
    }
20✔
2741
    else if (clientState.doh3Frontend != nullptr) {
395✔
2742
      infolog("Listening on %s for DoH3", addr.toStringWithPort());
17✔
2743
    }
17✔
2744
#ifdef HAVE_XSK
378✔
2745
    else if (clientState.xskInfo != nullptr) {
378!
2746
      infolog("Listening on %s (XSK-enabled)", addr.toStringWithPort());
×
2747
    }
×
2748
#endif
415✔
2749
  }
415✔
2750
}
897✔
2751

2752
static void setUpLocalBind(ClientState& cstate)
2753
{
897✔
2754
  /* skip some warnings if there is an identical UDP context */
2755
  bool warn = !cstate.tcp || cstate.tlsFrontend != nullptr || cstate.dohFrontend != nullptr;
897✔
2756
  int& descriptor = !cstate.tcp ? cstate.udpFD : cstate.tcpFD;
897✔
2757
  (void)warn;
897✔
2758

2759
  setupLocalSocket(cstate, cstate.local, descriptor, cstate.tcp, warn);
897✔
2760

2761
  for (auto& [addr, socket] : cstate.d_additionalAddresses) {
897!
2762
    setupLocalSocket(cstate, addr, socket, true, false);
×
2763
  }
×
2764

2765
  if (cstate.tlsFrontend != nullptr) {
897✔
2766
    if (!cstate.tlsFrontend->setupTLS()) {
43!
2767
      errlog("Error while setting up TLS on local address '%s', exiting", cstate.local.toStringWithPort());
×
2768
      _exit(EXIT_FAILURE);
×
2769
    }
×
2770
  }
43✔
2771

2772
  if (cstate.dohFrontend != nullptr) {
897✔
2773
    cstate.dohFrontend->setup();
61✔
2774
  }
61✔
2775
  if (cstate.doqFrontend != nullptr) {
897✔
2776
    cstate.doqFrontend->setup();
20✔
2777
  }
20✔
2778
  if (cstate.doh3Frontend != nullptr) {
897✔
2779
    cstate.doh3Frontend->setup();
17✔
2780
  }
17✔
2781

2782
  cstate.ready = true;
897✔
2783
}
897✔
2784

2785
struct CommandLineParameters
2786
{
2787
  vector<string> locals;
2788
  vector<string> remotes;
2789
  bool checkConfig{false};
2790
  bool beClient{false};
2791
  bool beSupervised{false};
2792
  string command;
2793
  string config;
2794
  string uid;
2795
  string gid;
2796
};
2797

2798
static void usage()
2799
{
×
2800
  cout << endl;
×
2801
  cout << "Syntax: dnsdist [-C,--config file] [-c,--client [IP[:PORT]]]\n";
×
2802
  cout << "[-e,--execute cmd] [-h,--help] [-l,--local addr]\n";
×
2803
  cout << "[-v,--verbose] [--check-config] [--version]\n";
×
2804
  cout << "\n";
×
2805
  cout << "-a,--acl netmask      Add this netmask to the ACL\n";
×
2806
  cout << "-C,--config file      Load configuration from 'file'\n";
×
2807
  cout << "-c,--client           Operate as a client, connect to dnsdist. This reads\n";
×
2808
  cout << "                      controlSocket from your configuration file, but also\n";
×
2809
  cout << "                      accepts an IP:PORT argument\n";
×
2810
#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO)
×
2811
  cout << "-k,--setkey KEY       Use KEY for encrypted communication to dnsdist. This\n";
×
2812
  cout << "                      is similar to setting setKey in the configuration file.\n";
×
2813
  cout << "                      NOTE: this will leak this key in your shell's history\n";
×
2814
  cout << "                      and in the systems running process list.\n";
×
2815
#endif
×
2816
  cout << "--check-config        Validate the configuration file and exit. The exit-code\n";
×
2817
  cout << "                      reflects the validation, 0 is OK, 1 means an error.\n";
×
2818
  cout << "                      Any errors are printed as well.\n";
×
2819
  cout << "-e,--execute cmd      Connect to dnsdist and execute 'cmd'\n";
×
2820
  cout << "-g,--gid gid          Change the process group ID after binding sockets\n";
×
2821
  cout << "-h,--help             Display this helpful message\n";
×
2822
  cout << "-l,--local address    Listen on this local address\n";
×
2823
  cout << "--supervised          Don't open a console, I'm supervised\n";
×
2824
  cout << "                        (use with e.g. systemd and daemontools)\n";
×
2825
  cout << "--disable-syslog      Don't log to syslog, only to stdout\n";
×
2826
  cout << "                        (use with e.g. systemd)\n";
×
2827
  cout << "--log-timestamps      Prepend timestamps to messages logged to stdout.\n";
×
2828
  cout << "-u,--uid uid          Change the process user ID after binding sockets\n";
×
2829
  cout << "-v,--verbose          Enable verbose mode\n";
×
2830
  cout << "-V,--version          Show dnsdist version information and exit\n";
×
2831
}
×
2832

2833
/* g++ defines __SANITIZE_THREAD__
2834
   clang++ supports the nice __has_feature(thread_sanitizer),
2835
   let's merge them */
2836
#if defined(__has_feature)
2837
#if __has_feature(thread_sanitizer)
2838
#define __SANITIZE_THREAD__ 1
2839
#endif
2840
#if __has_feature(address_sanitizer)
2841
#define __SANITIZE_ADDRESS__ 1
2842
#endif
2843
#endif
2844

2845
#if defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE)
2846
#include <sanitizer/lsan_interface.h>
2847
#endif
2848

2849
#if defined(COVERAGE) || (defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE))
2850
static void cleanupLuaObjects(LuaContext& /* luaCtx */)
2851
{
759✔
2852
  dnsdist::lua::hooks::clearExitCallbacks();
759✔
2853
  /* when our coverage mode is enabled, we need to make sure
2854
     that the Lua objects are destroyed before the Lua contexts. */
2855
  dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
759✔
2856
    config.d_ruleChains = dnsdist::rules::RuleChains();
759✔
2857
    config.d_lbPolicy = std::make_shared<ServerPolicy>();
759✔
2858
    config.d_pools.clear();
759✔
2859
    config.d_backends.clear();
759✔
2860
  });
759✔
2861
  dnsdist::webserver::clearWebHandlers();
759✔
2862
  dnsdist::lua::hooks::clearMaintenanceHooks();
759✔
2863
  dnsdist::lua::hooks::clearServerStateChangeCallbacks();
759✔
2864
}
759✔
2865
#endif /* defined(COVERAGE) || (defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE)) */
2866

2867
void doExitNicely(int exitCode)
2868
{
759✔
2869
  if (s_exiting) {
759✔
2870
    if (dnsdist::logging::LoggingConfiguration::getSyslog()) {
377!
2871
      syslog(LOG_INFO, "Exiting on user request");
377✔
2872
    }
377✔
2873
    std::cout << "Exiting on user request" << std::endl;
377✔
2874
  }
377✔
2875

2876
#ifdef HAVE_SYSTEMD
759✔
2877
  sd_notify(0, "STOPPING=1");
759✔
2878
#endif /* HAVE_SYSTEMD */
759✔
2879

2880
#if defined(COVERAGE) || (defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE))
759✔
2881
  if (dnsdist::g_asyncHolder) {
759✔
2882
    dnsdist::g_asyncHolder->stop();
377✔
2883
  }
377✔
2884

2885
  for (auto& backend : dnsdist::configuration::getCurrentRuntimeConfiguration().d_backends) {
848✔
2886
    backend->stop();
848✔
2887
  }
848✔
2888
#endif
759✔
2889

2890
  {
759✔
2891
    auto lock = g_lua.lock();
759✔
2892
    dnsdist::lua::hooks::runExitCallbacks(*lock);
759✔
2893
#if defined(COVERAGE) || (defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE))
759✔
2894
    cleanupLuaObjects(*lock);
759✔
2895
    *lock = LuaContext();
759✔
2896
#endif
759✔
2897
  }
759✔
2898

2899
#if defined(__SANITIZE_ADDRESS__) && defined(HAVE_LEAK_SANITIZER_INTERFACE)
759✔
2900
  __lsan_do_leak_check();
759✔
2901
#endif /* __SANITIZE_ADDRESS__ && HAVE_LEAK_SANITIZER_INTERFACE */
759✔
2902

2903
#ifdef COVERAGE
759✔
2904
  pdns::coverage::dumpCoverageData();
759✔
2905
#endif
759✔
2906

2907
  /* do not call destructors, because we have some
2908
     dependencies between objects that are not trivial
2909
     to solve.
2910
  */
2911
  _exit(exitCode);
759✔
2912
}
759✔
2913

2914
static void sigTermHandler(int /* sig */)
2915
{
377✔
2916
  s_exiting.store(true);
377✔
2917
}
377✔
2918

2919
static void reportFeatures()
2920
{
×
2921
#ifdef LUAJIT_VERSION
×
2922
  cout << "dnsdist " << VERSION << " (" << LUA_RELEASE << " [" << LUAJIT_VERSION << "])" << endl;
×
2923
#else
2924
  cout << "dnsdist " << VERSION << " (" << LUA_RELEASE << ")" << endl;
2925
#endif
2926
  cout << "Enabled features: ";
×
2927
#ifdef HAVE_XSK
×
2928
  cout << "AF_XDP ";
×
2929
#endif
×
2930
#ifdef HAVE_CDB
×
2931
  cout << "cdb ";
×
2932
#endif
×
2933
#ifdef HAVE_DNS_OVER_QUIC
×
2934
  cout << "dns-over-quic ";
×
2935
#endif
×
2936
#ifdef HAVE_DNS_OVER_HTTP3
×
2937
  cout << "dns-over-http3 ";
×
2938
#endif
×
2939
#ifdef HAVE_DNS_OVER_TLS
×
2940
  cout << "dns-over-tls(";
×
2941
#ifdef HAVE_GNUTLS
×
2942
  cout << "gnutls";
×
2943
#ifdef HAVE_LIBSSL
×
2944
  cout << " ";
×
2945
#endif
×
2946
#endif /* HAVE_GNUTLS */
×
2947
#ifdef HAVE_LIBSSL
×
2948
  cout << "openssl";
×
2949
#endif
×
2950
  cout << ") ";
×
2951
#endif /* HAVE_DNS_OVER_TLS */
×
2952
#ifdef HAVE_DNS_OVER_HTTPS
×
2953
  cout << "dns-over-https(";
×
2954
#ifdef HAVE_LIBH2OEVLOOP
×
2955
  cout << "h2o";
×
2956
#endif /* HAVE_LIBH2OEVLOOP */
×
2957
#if defined(HAVE_LIBH2OEVLOOP) && defined(HAVE_NGHTTP2)
×
2958
  cout << " ";
×
2959
#endif /* defined(HAVE_LIBH2OEVLOOP) && defined(HAVE_NGHTTP2) */
×
2960
#ifdef HAVE_NGHTTP2
×
2961
  cout << "nghttp2";
×
2962
#endif /* HAVE_NGHTTP2 */
×
2963
  cout << ") ";
×
2964
#endif /* HAVE_DNS_OVER_HTTPS */
×
2965
#ifdef HAVE_DNSCRYPT
×
2966
  cout << "dnscrypt ";
×
2967
#endif
×
2968
#ifdef HAVE_EBPF
×
2969
  cout << "ebpf ";
×
2970
#endif
×
2971
#ifdef HAVE_FSTRM
×
2972
  cout << "fstrm ";
×
2973
#endif
×
2974
#ifdef HAVE_IPCIPHER
×
2975
  cout << "ipcipher ";
×
2976
#endif
×
2977
#ifdef HAVE_LIBEDIT
×
2978
  cout << "libedit ";
×
2979
#endif
×
2980
#ifdef HAVE_LIBSODIUM
×
2981
  cout << "libsodium ";
×
2982
#endif
×
2983
#ifdef HAVE_LMDB
×
2984
  cout << "lmdb ";
×
2985
#endif
×
2986
#ifndef DISABLE_PROTOBUF
×
2987
  cout << "protobuf ";
×
2988
#endif
×
2989
#ifdef HAVE_RE2
×
2990
  cout << "re2 ";
×
2991
#endif
×
2992
#ifndef DISABLE_RECVMMSG
×
2993
#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
×
2994
  cout << "recvmmsg/sendmmsg ";
×
2995
#endif
×
2996
#endif /* DISABLE_RECVMMSG */
×
2997
#ifdef HAVE_NET_SNMP
×
2998
  cout << "snmp ";
×
2999
#endif
×
3000
#ifdef HAVE_SYSTEMD
×
3001
  cout << "systemd ";
×
3002
#endif
×
3003
#ifdef HAVE_YAML_CONFIGURATION
×
3004
  cout << "yaml ";
×
3005
#endif
×
3006
  cout << endl;
×
3007
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
3008
#ifdef DNSDIST_CONFIG_ARGS
3009
#define double_escape(s) #s
3010
#define escape_quotes(s) double_escape(s)
3011
  // NOLINTEND(cppcoreguidelines-macro-usage)
3012
  cout << "Configured with: " << escape_quotes(DNSDIST_CONFIG_ARGS) << endl;
3013
#undef escape_quotes
3014
#undef double_escape
3015
#endif
3016
}
×
3017

3018
static void parseParameters(int argc, char** argv, CommandLineParameters& cmdLine, ComboAddress& clientAddress)
3019
{
766✔
3020
  const std::array<struct option, 16> longopts{{{"acl", required_argument, nullptr, 'a'},
766✔
3021
                                                {"check-config", no_argument, nullptr, 1},
766✔
3022
                                                {"client", no_argument, nullptr, 'c'},
766✔
3023
                                                {"config", required_argument, nullptr, 'C'},
766✔
3024
                                                {"disable-syslog", no_argument, nullptr, 2},
766✔
3025
                                                {"execute", required_argument, nullptr, 'e'},
766✔
3026
                                                {"gid", required_argument, nullptr, 'g'},
766✔
3027
                                                {"help", no_argument, nullptr, 'h'},
766✔
3028
                                                {"local", required_argument, nullptr, 'l'},
766✔
3029
                                                {"log-timestamps", no_argument, nullptr, 4},
766✔
3030
                                                {"setkey", required_argument, nullptr, 'k'},
766✔
3031
                                                {"supervised", no_argument, nullptr, 3},
766✔
3032
                                                {"uid", required_argument, nullptr, 'u'},
766✔
3033
                                                {"verbose", no_argument, nullptr, 'v'},
766✔
3034
                                                {"version", no_argument, nullptr, 'V'},
766✔
3035
                                                {nullptr, 0, nullptr, 0}}};
766✔
3036
  int longindex = 0;
766✔
3037
  string optstring;
766✔
3038
  dnsdist::configuration::RuntimeConfiguration newConfig;
766✔
3039

3040
  while (true) {
4,263✔
3041
    // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3042
    int gotChar = getopt_long(argc, argv, "a:cC:e:g:hk:l:u:vV", longopts.data(), &longindex);
4,263✔
3043
    if (gotChar == -1) {
4,263✔
3044
      break;
766✔
3045
    }
766✔
3046
    switch (gotChar) {
3,497!
3047
    case 1:
386✔
3048
      cmdLine.checkConfig = true;
386✔
3049
      break;
386✔
3050
    case 2:
×
3051
      dnsdist::logging::LoggingConfiguration::setSyslog(false);
×
3052
      break;
×
3053
    case 3:
754✔
3054
      cmdLine.beSupervised = true;
754✔
3055
      break;
754✔
3056
    case 4:
×
3057
      dnsdist::logging::LoggingConfiguration::setLogTimestamps(true);
×
3058
      break;
×
3059
    case 'C':
766✔
3060
      cmdLine.config = optarg;
766✔
3061
      break;
766✔
3062
    case 'c':
5✔
3063
      cmdLine.beClient = true;
5✔
3064
      break;
5✔
3065
    case 'e':
×
3066
      cmdLine.command = optarg;
×
3067
      break;
×
3068
    case 'g':
×
3069
      cmdLine.gid = optarg;
×
3070
      break;
×
3071
    case 'h':
×
3072
      cout << "dnsdist " << VERSION << endl;
×
3073
      usage();
×
3074
      cout << "\n";
×
3075
      // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3076
      exit(EXIT_SUCCESS);
×
3077
      break;
×
3078
    case 'a':
760✔
3079
      optstring = optarg;
760✔
3080
      newConfig.d_ACL.addMask(optstring);
760✔
3081
      break;
760✔
3082
    case 'k':
×
3083
#if defined HAVE_LIBSODIUM || defined(HAVE_LIBCRYPTO)
×
3084
    {
×
3085
      std::string consoleKey;
×
3086
      if (B64Decode(string(optarg), consoleKey) < 0) {
×
3087
        cerr << "Unable to decode key '" << optarg << "'." << endl;
×
3088
        // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3089
        exit(EXIT_FAILURE);
×
3090
      }
×
3091
      dnsdist::configuration::updateRuntimeConfiguration([&consoleKey](dnsdist::configuration::RuntimeConfiguration& config) {
×
3092
        config.d_consoleKey = std::move(consoleKey);
×
3093
      });
×
3094
    }
×
3095
#else
3096
      cerr << "dnsdist has been built without libsodium or libcrypto, -k/--setkey is unsupported." << endl;
3097
      // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3098
      exit(EXIT_FAILURE);
3099
#endif
3100
    break;
×
3101
    case 'l':
728✔
3102
      cmdLine.locals.push_back(boost::trim_copy(string(optarg)));
728✔
3103
      break;
728✔
3104
    case 'u':
×
3105
      cmdLine.uid = optarg;
×
3106
      break;
×
3107
    case 'v':
98✔
3108
      newConfig.d_verbose = true;
98✔
3109
      break;
98✔
3110
    case 'V':
×
3111
      reportFeatures();
×
3112
      // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3113
      exit(EXIT_SUCCESS);
×
3114
      break;
×
3115
    case '?':
×
3116
      // getopt_long printed an error message.
3117
      usage();
×
3118
      // NOLINTNEXTLINE(concurrency-mt-unsafe): only one thread at this point
3119
      exit(EXIT_FAILURE);
×
3120
      break;
×
3121
    }
3,497✔
3122
  }
3,497✔
3123

3124
  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic): argv
3125
  argv += optind;
766✔
3126

3127
  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic): argv
3128
  for (const auto* ptr = argv; *ptr != nullptr; ++ptr) {
766!
3129
    if (cmdLine.beClient) {
×
3130
      clientAddress = ComboAddress(*ptr, 5199);
×
3131
    }
×
3132
    else {
×
3133
      cmdLine.remotes.emplace_back(*ptr);
×
3134
    }
×
3135
  }
×
3136

3137
  dnsdist::configuration::updateRuntimeConfiguration([&newConfig](dnsdist::configuration::RuntimeConfiguration& config) {
766✔
3138
    config = std::move(newConfig);
766✔
3139
  });
766✔
3140
}
766✔
3141
static void setupPools()
3142
{
377✔
3143
  bool precompute = false;
377✔
3144
  const auto& currentConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
377✔
3145
  if (currentConfig.d_lbPolicy->getName() == "chashed") {
377✔
3146
    precompute = true;
1✔
3147
  }
1✔
3148
  else {
376✔
3149
    for (const auto& entry : currentConfig.d_pools) {
422✔
3150
      if (entry.second->policy != nullptr && entry.second->policy->getName() == "chashed") {
422!
3151
        precompute = true;
×
3152
        break;
×
3153
      }
×
3154
    }
422✔
3155
  }
376✔
3156
  if (precompute) {
377✔
3157
    vinfolog("Pre-computing hashes for consistent hash load-balancing policy");
1!
3158
    // pre compute hashes
3159
    for (const auto& backend : currentConfig.d_backends) {
2✔
3160
      if (backend->d_config.d_weight < 100) {
2!
3161
        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);
×
3162
      }
×
3163

3164
      backend->hash();
2✔
3165
    }
2✔
3166
  }
1✔
3167
}
377✔
3168

3169
static void dropPrivileges(const CommandLineParameters& cmdLine)
3170
{
377✔
3171
  uid_t newgid = getegid();
377✔
3172
  gid_t newuid = geteuid();
377✔
3173

3174
  if (!cmdLine.gid.empty()) {
377!
3175
    newgid = strToGID(cmdLine.gid);
×
3176
  }
×
3177

3178
  if (!cmdLine.uid.empty()) {
377!
3179
    newuid = strToUID(cmdLine.uid);
×
3180
  }
×
3181

3182
  bool retainedCapabilities = true;
377✔
3183
  if (!dnsdist::configuration::getImmutableConfiguration().d_capabilitiesToRetain.empty() && (getegid() != newgid || geteuid() != newuid)) {
377!
3184
    retainedCapabilities = keepCapabilitiesAfterSwitchingIDs();
×
3185
  }
×
3186

3187
  if (getegid() != newgid) {
377!
3188
    if (running_in_service_mgr()) {
×
3189
      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");
×
3190
      _exit(EXIT_FAILURE);
×
3191
    }
×
3192
    dropGroupPrivs(newgid);
×
3193
  }
×
3194

3195
  if (geteuid() != newuid) {
377!
3196
    if (running_in_service_mgr()) {
×
3197
      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");
×
3198
      _exit(EXIT_FAILURE);
×
3199
    }
×
3200
    dropUserPrivs(newuid);
×
3201
  }
×
3202

3203
  if (retainedCapabilities) {
377!
3204
    dropCapabilitiesAfterSwitchingIDs();
377✔
3205
  }
377✔
3206

3207
  try {
377✔
3208
    /* we might still have capabilities remaining,
3209
       for example if we have been started as root
3210
       without --uid or --gid (please don't do that)
3211
       or as an unprivileged user with ambient
3212
       capabilities like CAP_NET_BIND_SERVICE.
3213
    */
3214
    dropCapabilities(dnsdist::configuration::getImmutableConfiguration().d_capabilitiesToRetain);
377✔
3215
  }
377✔
3216
  catch (const std::exception& e) {
377✔
3217
    warnlog("%s", e.what());
×
3218
  }
×
3219
}
377✔
3220

3221
static void initFrontends(const CommandLineParameters& cmdLine)
3222
{
377✔
3223
  auto frontends = dnsdist::configuration::getImmutableConfiguration().d_frontends;
377✔
3224

3225
  if (!cmdLine.locals.empty()) {
377✔
3226
    for (auto it = frontends.begin(); it != frontends.end();) {
532✔
3227
      /* DoH, DoT and DNSCrypt frontends are separate */
3228
      if ((*it)->dohFrontend == nullptr && (*it)->tlsFrontend == nullptr && (*it)->dnscryptCtx == nullptr && (*it)->doqFrontend == nullptr && (*it)->doh3Frontend == nullptr) {
168✔
3229
        it = frontends.erase(it);
24✔
3230
      }
24✔
3231
      else {
144✔
3232
        ++it;
144✔
3233
      }
144✔
3234
    }
168✔
3235

3236
    for (const auto& loc : cmdLine.locals) {
364✔
3237
      /* UDP */
3238
      frontends.emplace_back(std::make_unique<ClientState>(ComboAddress(loc, 53), false, false, 0, "", std::set<int>{}, true));
364✔
3239
      /* TCP */
3240
      frontends.emplace_back(std::make_unique<ClientState>(ComboAddress(loc, 53), true, false, 0, "", std::set<int>{}, true));
364✔
3241
    }
364✔
3242
  }
364✔
3243

3244
  if (frontends.empty()) {
377!
3245
    /* UDP */
3246
    frontends.emplace_back(std::make_unique<ClientState>(ComboAddress("127.0.0.1", 53), false, false, 0, "", std::set<int>{}, true));
×
3247
    /* TCP */
3248
    frontends.emplace_back(std::make_unique<ClientState>(ComboAddress("127.0.0.1", 53), true, false, 0, "", std::set<int>{}, true));
×
3249
  }
×
3250

3251
  dnsdist::configuration::updateImmutableConfiguration([&frontends](dnsdist::configuration::ImmutableConfiguration& config) {
377✔
3252
    config.d_frontends = std::move(frontends);
377✔
3253
  });
377✔
3254
}
377✔
3255

3256
namespace dnsdist
3257
{
3258
static void startFrontends()
3259
{
377✔
3260
#ifdef HAVE_XSK
377✔
3261
  for (auto& xskContext : dnsdist::xsk::g_xsk) {
377!
3262
    std::thread xskThread(dnsdist::xsk::XskRouter, std::move(xskContext));
×
3263
    xskThread.detach();
×
3264
  }
×
3265
#endif /* HAVE_XSK */
377✔
3266

3267
  std::vector<ClientState*> tcpStates;
377✔
3268
  std::vector<ClientState*> udpStates;
377✔
3269
  for (const auto& clientState : dnsdist::getFrontends()) {
897✔
3270
#ifdef HAVE_XSK
897✔
3271
    if (clientState->xskInfo) {
897!
3272
      dnsdist::xsk::addDestinationAddress(clientState->local);
×
3273

3274
      std::thread xskCT(dnsdist::xsk::XskClientThread, clientState.get());
×
3275
      if (!clientState->cpus.empty()) {
×
3276
        mapThreadToCPUList(xskCT.native_handle(), clientState->cpus);
×
3277
      }
×
3278
      xskCT.detach();
×
3279
    }
×
3280
#endif /* HAVE_XSK */
897✔
3281

3282
    if (clientState->dohFrontend != nullptr && clientState->dohFrontend->d_library == "h2o") {
897✔
3283
#ifdef HAVE_DNS_OVER_HTTPS
23✔
3284
#ifdef HAVE_LIBH2OEVLOOP
23✔
3285
      std::thread dohThreadHandle(dohThread, clientState.get());
23✔
3286
      if (!clientState->cpus.empty()) {
23!
3287
        mapThreadToCPUList(dohThreadHandle.native_handle(), clientState->cpus);
×
3288
      }
×
3289
      dohThreadHandle.detach();
23✔
3290
#endif /* HAVE_LIBH2OEVLOOP */
23✔
3291
#endif /* HAVE_DNS_OVER_HTTPS */
23✔
3292
      continue;
23✔
3293
    }
23✔
3294
    if (clientState->doqFrontend != nullptr) {
874✔
3295
#ifdef HAVE_DNS_OVER_QUIC
20✔
3296
      std::thread doqThreadHandle(doqThread, clientState.get());
20✔
3297
      if (!clientState->cpus.empty()) {
20!
3298
        mapThreadToCPUList(doqThreadHandle.native_handle(), clientState->cpus);
×
3299
      }
×
3300
      doqThreadHandle.detach();
20✔
3301
#endif /* HAVE_DNS_OVER_QUIC */
20✔
3302
      continue;
20✔
3303
    }
20✔
3304
    if (clientState->doh3Frontend != nullptr) {
854✔
3305
#ifdef HAVE_DNS_OVER_HTTP3
17✔
3306
      std::thread doh3ThreadHandle(doh3Thread, clientState.get());
17✔
3307
      if (!clientState->cpus.empty()) {
17!
3308
        mapThreadToCPUList(doh3ThreadHandle.native_handle(), clientState->cpus);
×
3309
      }
×
3310
      doh3ThreadHandle.detach();
17✔
3311
#endif /* HAVE_DNS_OVER_HTTP3 */
17✔
3312
      continue;
17✔
3313
    }
17✔
3314
    if (clientState->udpFD >= 0) {
837✔
3315
#ifdef USE_SINGLE_ACCEPTOR_THREAD
3316
      udpStates.push_back(clientState.get());
3317
#else /* USE_SINGLE_ACCEPTOR_THREAD */
3318
      std::thread udpClientThreadHandle(udpClientThread, std::vector<ClientState*>{clientState.get()});
378✔
3319
      if (!clientState->cpus.empty()) {
378!
3320
        mapThreadToCPUList(udpClientThreadHandle.native_handle(), clientState->cpus);
×
3321
      }
×
3322
      udpClientThreadHandle.detach();
378✔
3323
#endif /* USE_SINGLE_ACCEPTOR_THREAD */
378✔
3324
    }
378✔
3325
    else if (clientState->tcpFD >= 0) {
459!
3326
#ifdef USE_SINGLE_ACCEPTOR_THREAD
3327
      tcpStates.push_back(clientState.get());
3328
#else /* USE_SINGLE_ACCEPTOR_THREAD */
3329
      std::thread tcpAcceptorThreadHandle(tcpAcceptorThread, std::vector<ClientState*>{clientState.get()});
459✔
3330
      if (!clientState->cpus.empty()) {
459!
3331
        mapThreadToCPUList(tcpAcceptorThreadHandle.native_handle(), clientState->cpus);
×
3332
      }
×
3333
      tcpAcceptorThreadHandle.detach();
459✔
3334
#endif /* USE_SINGLE_ACCEPTOR_THREAD */
459✔
3335
    }
459✔
3336
  }
837✔
3337
#ifdef USE_SINGLE_ACCEPTOR_THREAD
3338
  if (!udpStates.empty()) {
3339
    std::thread udpThreadHandle(udpClientThread, udpStates);
3340
    udpThreadHandle.detach();
3341
  }
3342
  if (!tcpStates.empty()) {
3343
    g_tcpclientthreads = std::make_unique<TCPClientCollection>(1, tcpStates);
3344
  }
3345
#endif /* USE_SINGLE_ACCEPTOR_THREAD */
3346
}
377✔
3347
}
3348

3349
struct ListeningSockets
3350
{
3351
  Socket d_consoleSocket{-1};
3352
  std::vector<std::pair<ComboAddress, Socket>> d_webServerSockets;
3353
};
3354

3355
static ListeningSockets initListeningSockets()
3356
{
377✔
3357
  ListeningSockets result;
377✔
3358
  const auto& currentConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
377✔
3359

3360
  if (currentConfig.d_consoleEnabled) {
377✔
3361
    const auto& local = currentConfig.d_consoleServerAddress;
74✔
3362
    try {
74✔
3363
      result.d_consoleSocket = Socket(local.sin4.sin_family, SOCK_STREAM, 0);
74✔
3364
      result.d_consoleSocket.bind(local, true);
74✔
3365
      result.d_consoleSocket.listen(5);
74✔
3366
    }
74✔
3367
    catch (const std::exception& exp) {
74✔
3368
      errlog("Unable to bind to control socket on %s: %s", local.toStringWithPort(), exp.what());
×
3369
    }
×
3370
  }
74✔
3371

3372
  for (const auto& local : currentConfig.d_webServerAddresses) {
377✔
3373
    try {
53✔
3374
      auto webServerSocket = Socket(local.sin4.sin_family, SOCK_STREAM, 0);
53✔
3375
      webServerSocket.bind(local, true);
53✔
3376
      webServerSocket.listen(5);
53✔
3377
      result.d_webServerSockets.emplace_back(local, std::move(webServerSocket));
53✔
3378
    }
53✔
3379
    catch (const std::exception& exp) {
53✔
3380
      errlog("Unable to bind to web server socket on %s: %s", local.toStringWithPort(), exp.what());
×
3381
    }
×
3382
  }
53✔
3383

3384
  return result;
377✔
3385
}
377✔
3386

3387
static std::optional<std::string> lookForTentativeConfigurationFileWithExtension(const std::string& configurationFile, const std::string& extension)
3388
{
46✔
3389
  auto dotPos = configurationFile.rfind('.');
46✔
3390
  if (dotPos == std::string::npos) {
46!
3391
    return std::nullopt;
×
3392
  }
×
3393
  auto tentativeFile = configurationFile.substr(0, dotPos + 1) + extension;
46✔
3394
  if (!std::filesystem::exists(tentativeFile)) {
46✔
3395
    return std::nullopt;
40✔
3396
  }
40✔
3397
  return tentativeFile;
6✔
3398
}
46✔
3399

3400
static bool loadConfigurationFromFile(const std::string& configurationFile, bool isClient, bool configCheck)
3401
{
766✔
3402
  if (boost::ends_with(configurationFile, ".yml")) {
766✔
3403
    // the bindings are always needed, for example for inline Lua
3404
    dnsdist::lua::setupLuaBindingsOnly(*(g_lua.lock()), isClient, configCheck);
46✔
3405

3406
    if (auto tentativeLuaConfFile = lookForTentativeConfigurationFileWithExtension(configurationFile, "lua")) {
46✔
3407
      vinfolog("Loading configuration from auto-discovered Lua file %s", *tentativeLuaConfFile);
6✔
3408
      dnsdist::configuration::lua::loadLuaConfigurationFile(*(g_lua.lock()), *tentativeLuaConfFile, configCheck);
6✔
3409
    }
6✔
3410
    vinfolog("Loading configuration from YAML file %s", configurationFile);
46✔
3411
    if (!dnsdist::configuration::yaml::loadConfigurationFromFile(configurationFile, isClient, configCheck)) {
46!
3412
      return false;
×
3413
    }
×
3414
    if (!isClient && !configCheck) {
46!
3415
      dnsdist::lua::setupLuaConfigurationOptions(*(g_lua.lock()), false, false);
23✔
3416
    }
23✔
3417
    return true;
46✔
3418
  }
46✔
3419

3420
  dnsdist::lua::setupLua(*(g_lua.lock()), isClient, configCheck);
720✔
3421
  if (boost::ends_with(configurationFile, ".lua")) {
720!
3422
    vinfolog("Loading configuration from Lua file %s", configurationFile);
×
3423
    dnsdist::configuration::lua::loadLuaConfigurationFile(*(g_lua.lock()), configurationFile, configCheck);
×
3424
    if (auto tentativeYamlConfFile = lookForTentativeConfigurationFileWithExtension(configurationFile, "yml")) {
×
3425
      vinfolog("Loading configuration from auto-discovered YAML file %s", *tentativeYamlConfFile);
×
3426
      return dnsdist::configuration::yaml::loadConfigurationFromFile(*tentativeYamlConfFile, isClient, configCheck);
×
3427
    }
×
3428
  }
×
3429
  else {
720✔
3430
    vinfolog("Loading configuration from Lua file %s", configurationFile);
720✔
3431
    dnsdist::configuration::lua::loadLuaConfigurationFile(*(g_lua.lock()), configurationFile, configCheck);
720✔
3432
  }
720✔
3433
  return true;
720✔
3434
}
720✔
3435

3436
int main(int argc, char** argv)
3437
{
766✔
3438
  try {
766✔
3439
    CommandLineParameters cmdLine{};
766✔
3440
    size_t udpBindsCount = 0;
766✔
3441
    size_t tcpBindsCount = 0;
766✔
3442

3443
    dnsdist::console::completion::setupCompletion();
766✔
3444

3445
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast): SIG_IGN macro
3446
    signal(SIGPIPE, SIG_IGN);
766✔
3447
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast): SIG_IGN macro
3448
    signal(SIGCHLD, SIG_IGN);
766✔
3449
    signal(SIGTERM, sigTermHandler);
766✔
3450

3451
    openlog("dnsdist", LOG_PID | LOG_NDELAY, LOG_DAEMON);
766✔
3452

3453
#ifdef HAVE_LIBSODIUM
766✔
3454
    if (sodium_init() == -1) {
766!
3455
      cerr << "Unable to initialize crypto library" << endl;
×
3456
      // NOLINTNEXTLINE(concurrency-mt-unsafe): only on thread at this point
3457
      exit(EXIT_FAILURE);
×
3458
    }
×
3459
#endif
766✔
3460
    dnsdist::initRandom();
766✔
3461
    dnsdist::configuration::updateImmutableConfiguration([](dnsdist::configuration::ImmutableConfiguration& config) {
766✔
3462
      config.d_hashPerturbation = dnsdist::getRandomValue(0xffffffff);
766✔
3463
    });
766✔
3464

3465
#ifdef HAVE_XSK
766✔
3466
    try {
766✔
3467
      dnsdist::xsk::clearDestinationAddresses();
766✔
3468
    }
766✔
3469
    catch (const std::exception& exp) {
766✔
3470
      /* silently handle failures: at this point we don't even know if XSK is enabled,
3471
         and we might not have the correct map (not the default one). */
3472
    }
766✔
3473
#endif /* HAVE_XSK */
766✔
3474

3475
    ComboAddress clientAddress = ComboAddress();
766✔
3476
    cmdLine.config = SYSCONFDIR "/dnsdist.conf";
766✔
3477

3478
    parseParameters(argc, argv, cmdLine, clientAddress);
766✔
3479

3480
    dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
766✔
3481
      config.d_lbPolicy = std::make_shared<ServerPolicy>("leastOutstanding", leastOutstanding, false);
766✔
3482
    });
766✔
3483

3484
    if (cmdLine.beClient || !cmdLine.command.empty()) {
766!
3485
      if (!loadConfigurationFromFile(cmdLine.config, true, false)) {
5!
3486
#ifdef COVERAGE
×
3487
        exit(EXIT_FAILURE);
×
3488
#else
3489
        _exit(EXIT_FAILURE);
3490
#endif
3491
      }
×
3492
      if (clientAddress != ComboAddress()) {
5!
3493
        dnsdist::configuration::updateRuntimeConfiguration([&clientAddress](dnsdist::configuration::RuntimeConfiguration& config) {
×
3494
          config.d_consoleServerAddress = clientAddress;
×
3495
        });
×
3496
      }
×
3497
      dnsdist::console::doClient(cmdLine.command);
5✔
3498
#ifdef COVERAGE
5✔
3499
      exit(EXIT_SUCCESS);
5✔
3500
#else
3501
      _exit(EXIT_SUCCESS);
3502
#endif
3503
    }
5✔
3504

3505
    dnsdist::configuration::updateRuntimeConfiguration([](dnsdist::configuration::RuntimeConfiguration& config) {
761✔
3506
      auto& acl = config.d_ACL;
761✔
3507
      if (acl.empty()) {
761✔
3508
        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✔
3509
          acl.addMask(addr);
63✔
3510
        }
63✔
3511
      }
7✔
3512
      for (const auto& mask : {"127.0.0.1/8", "::1/128"}) {
1,522✔
3513
        config.d_consoleACL.addMask(mask);
1,522✔
3514
      }
1,522✔
3515
      config.d_webServerACL.toMasks("127.0.0.1, ::1");
761✔
3516
    });
761✔
3517

3518
    dnsdist::webserver::registerBuiltInWebHandlers();
761✔
3519

3520
    if (cmdLine.checkConfig) {
761✔
3521
      if (!loadConfigurationFromFile(cmdLine.config, false, true)) {
384!
3522
#ifdef COVERAGE
×
3523
        exit(EXIT_FAILURE);
×
3524
#else
3525
        _exit(EXIT_FAILURE);
3526
#endif
3527
      }
×
3528
      // No exception was thrown
3529
      infolog("Configuration '%s' OK!", cmdLine.config);
384✔
3530
      doExitNicely();
384✔
3531
    }
384✔
3532

3533
    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);
761✔
3534

3535
    dnsdist::g_asyncHolder = std::make_unique<dnsdist::AsynchronousHolder>();
761✔
3536

3537
    /* create the default pool no matter what */
3538
    createPoolIfNotExists("");
761✔
3539

3540
    if (!loadConfigurationFromFile(cmdLine.config, false, false)) {
761!
3541
#ifdef COVERAGE
×
3542
      exit(EXIT_FAILURE);
×
3543
#else
3544
      _exit(EXIT_FAILURE);
3545
#endif
3546
    }
×
3547

3548
    setupPools();
761✔
3549

3550
    initFrontends(cmdLine);
761✔
3551

3552
    for (const auto& frontend : dnsdist::getFrontends()) {
897✔
3553
      if (!frontend->tcp) {
897✔
3554
        ++udpBindsCount;
415✔
3555
      }
415✔
3556
      else {
482✔
3557
        ++tcpBindsCount;
482✔
3558
      }
482✔
3559
    }
897✔
3560

3561
    dnsdist::configuration::setImmutableConfigurationDone();
761✔
3562

3563
    {
761✔
3564
      const auto& immutableConfig = dnsdist::configuration::getImmutableConfiguration();
761✔
3565
      setTCPDownstreamMaxIdleConnectionsPerBackend(immutableConfig.d_outgoingTCPMaxIdlePerBackend);
761✔
3566
      setTCPDownstreamMaxIdleTime(immutableConfig.d_outgoingTCPMaxIdleTime);
761✔
3567
      setTCPDownstreamCleanupInterval(immutableConfig.d_outgoingTCPCleanupInterval);
761✔
3568
#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
761✔
3569
      setDoHDownstreamMaxIdleConnectionsPerBackend(immutableConfig.d_outgoingDoHMaxIdlePerBackend);
761✔
3570
      setDoHDownstreamMaxIdleTime(immutableConfig.d_outgoingDoHMaxIdleTime);
761✔
3571
      setDoHDownstreamCleanupInterval(immutableConfig.d_outgoingDoHCleanupInterval);
761✔
3572
#endif /* HAVE_DNS_OVER_HTTPS && HAVE_NGHTTP2 */
761✔
3573
    }
761✔
3574

3575
    {
761✔
3576
      const auto& config = dnsdist::configuration::getImmutableConfiguration();
761✔
3577
      g_rings.init(config.d_ringsCapacity, config.d_ringsNumberOfShards, config.d_ringsNbLockTries, config.d_ringsRecordQueries, config.d_ringsRecordResponses);
761✔
3578
    }
761✔
3579

3580
    for (const auto& frontend : dnsdist::getFrontends()) {
897✔
3581
      setUpLocalBind(*frontend);
897✔
3582
    }
897✔
3583

3584
    {
761✔
3585
      std::string acls;
761✔
3586
      auto aclEntries = dnsdist::configuration::getCurrentRuntimeConfiguration().d_ACL.toStringVector();
761✔
3587
      for (const auto& aclEntry : aclEntries) {
761✔
3588
        if (!acls.empty()) {
568✔
3589
          acls += ", ";
191✔
3590
        }
191✔
3591
        acls += aclEntry;
568✔
3592
      }
568✔
3593
      infolog("ACL allowing queries from: %s", acls);
761✔
3594
    }
761✔
3595
    {
761✔
3596
      std::string acls;
761✔
3597
      auto aclEntries = dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleACL.toStringVector();
761✔
3598
      for (const auto& entry : aclEntries) {
761✔
3599
        if (!acls.empty()) {
745✔
3600
          acls += ", ";
368✔
3601
        }
368✔
3602
        acls += entry;
745✔
3603
      }
745✔
3604
      infolog("Console ACL allowing connections from: %s", acls.c_str());
761✔
3605
    }
761✔
3606

3607
    auto listeningSockets = initListeningSockets();
761✔
3608

3609
#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO)
761✔
3610
    if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleEnabled && dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleKey.empty()) {
761✔
3611
      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✔
3612
    }
1✔
3613
#endif
761✔
3614

3615
    dropPrivileges(cmdLine);
761✔
3616

3617
    /* this need to be done _after_ dropping privileges */
3618
#ifndef DISABLE_DELAY_PIPE
761✔
3619
    g_delay = std::make_unique<DelayPipe<DelayedPacket>>();
761✔
3620
#endif /* DISABLE_DELAY_PIPE */
761✔
3621

3622
#if defined(HAVE_NET_SNMP)
761✔
3623
    if (dnsdist::configuration::getImmutableConfiguration().d_snmpEnabled) {
761✔
3624
      g_snmpAgent = std::make_unique<DNSDistSNMPAgent>("dnsdist", dnsdist::configuration::getImmutableConfiguration().d_snmpDaemonSocketPath);
1✔
3625
      g_snmpAgent->run();
1✔
3626
    }
1✔
3627
#endif /* HAVE_NET_SNMP */
761✔
3628

3629
    /* we need to create the TCP worker threads before the
3630
       acceptor ones, otherwise we might crash when processing
3631
       the first TCP query */
3632
#ifndef USE_SINGLE_ACCEPTOR_THREAD
761✔
3633
    const auto maxTCPClientThreads = dnsdist::configuration::getImmutableConfiguration().d_maxTCPClientThreads;
761✔
3634
    /* the limit is completely arbitrary: hopefully high enough not to trigger too many false positives
3635
       but low enough to be useful */
3636
    if (maxTCPClientThreads >= 50U) {
761!
3637
      warnlog("setMaxTCPClientThreads(%d) might create a large number of TCP connections to backends, and is probably not needed, please consider lowering it", maxTCPClientThreads);
×
3638
    }
×
3639
    g_tcpclientthreads = std::make_unique<TCPClientCollection>(maxTCPClientThreads, std::vector<ClientState*>());
761✔
3640
#endif
761✔
3641

3642
#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
761✔
3643
    initDoHWorkers();
761✔
3644
#endif
761✔
3645

3646
    if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_consoleEnabled) {
761✔
3647
      std::thread consoleControlThread(dnsdist::console::controlThread, std::move(listeningSockets.d_consoleSocket));
74✔
3648
      consoleControlThread.detach();
74✔
3649
    }
74✔
3650
    for (auto& [listeningAddress, socket] : listeningSockets.d_webServerSockets) {
761✔
3651
      std::thread webServerThread(dnsdist::webserver::WebserverThread, listeningAddress, std::move(socket));
53✔
3652
      webServerThread.detach();
53✔
3653
    }
53✔
3654

3655
    for (const auto& backend : dnsdist::configuration::getCurrentRuntimeConfiguration().d_backends) {
761✔
3656
      if (backend->connected) {
422✔
3657
        backend->start();
387✔
3658
      }
387✔
3659
    }
422✔
3660

3661
    if (!cmdLine.remotes.empty()) {
761!
3662
      for (const auto& address : cmdLine.remotes) {
×
3663
        DownstreamState::Config config;
×
3664
        config.remote = ComboAddress(address, 53);
×
3665
        auto ret = std::make_shared<DownstreamState>(std::move(config), nullptr, true);
×
3666
        addServerToPool("", ret);
×
3667
        ret->start();
×
3668
        dnsdist::configuration::updateRuntimeConfiguration([&ret](dnsdist::configuration::RuntimeConfiguration& runtimeConfig) {
×
3669
          runtimeConfig.d_backends.push_back(std::move(ret));
×
3670
        });
×
3671
      }
×
3672
    }
×
3673

3674
    if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_backends.empty()) {
761✔
3675
      errlog("No downstream servers defined: all packets will get dropped");
6✔
3676
      // you might define them later, but you need to know
3677
    }
6✔
3678

3679
    checkFileDescriptorsLimits(udpBindsCount, tcpBindsCount);
761✔
3680

3681
    {
761✔
3682
      // coverity[auto_causes_copy]
3683
      const auto states = dnsdist::configuration::getCurrentRuntimeConfiguration().d_backends; // it is a copy, but the internal shared_ptrs are the real deal
761✔
3684
      auto mplexer = std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent(states.size()));
761✔
3685
      for (auto& dss : states) {
761✔
3686

3687
        if (dss->d_config.d_availability == DownstreamState::Availability::Auto) {
422✔
3688
          if (dss->d_config.d_healthCheckMode == DownstreamState::HealthCheckMode::Active) {
341✔
3689
            dss->d_nextCheck = dss->d_config.checkInterval;
338✔
3690
          }
338✔
3691

3692
          if (!queueHealthCheck(mplexer, dss, true)) {
341!
3693
            dss->submitHealthCheckResult(true, false);
×
3694
            dss->setUpStatus(false);
×
3695
            warnlog("Marking downstream %s as 'down'", dss->getNameWithAddr());
×
3696
          }
×
3697
        }
341✔
3698
      }
422✔
3699
      handleQueuedHealthChecks(*mplexer, true);
761✔
3700
    }
761✔
3701

3702
    dnsdist::startFrontends();
761✔
3703

3704
    dnsdist::ServiceDiscovery::run();
761✔
3705

3706
#ifndef DISABLE_CARBON
761✔
3707
    dnsdist::Carbon::run(dnsdist::configuration::getCurrentRuntimeConfiguration().d_carbonEndpoints);
761✔
3708
#endif /* DISABLE_CARBON */
761✔
3709

3710
    thread stattid(maintThread);
761✔
3711
    stattid.detach();
761✔
3712

3713
    thread healththread(healthChecksThread);
761✔
3714

3715
#ifndef DISABLE_DYNBLOCKS
761✔
3716
    thread dynBlockMaintThread(dynBlockMaintenanceThread);
761✔
3717
    dynBlockMaintThread.detach();
761✔
3718
#endif /* DISABLE_DYNBLOCKS */
761✔
3719

3720
#ifndef DISABLE_SECPOLL
761✔
3721
    if (!dnsdist::configuration::getCurrentRuntimeConfiguration().d_secPollSuffix.empty()) {
761!
3722
      thread secpollthread(secPollThread);
×
3723
      secpollthread.detach();
×
3724
    }
×
3725
#endif /* DISABLE_SECPOLL */
761✔
3726

3727
    if (cmdLine.beSupervised) {
761✔
3728
#ifdef HAVE_SYSTEMD
377✔
3729
      sd_notify(0, "READY=1");
377✔
3730
#endif
377✔
3731
      healththread.join();
377✔
3732
    }
377✔
3733
    else {
384✔
3734
      healththread.detach();
384✔
3735
      dnsdist::console::doConsole();
384✔
3736
    }
384✔
3737
    doExitNicely();
761✔
3738
  }
761✔
3739
  catch (const LuaContext::ExecutionErrorException& e) {
766✔
3740
    try {
2✔
3741
      errlog("Fatal Lua error: %s", e.what());
2✔
3742
      std::rethrow_if_nested(e);
2✔
3743
    }
2✔
3744
    catch (const std::exception& ne) {
2✔
3745
      errlog("Details: %s", ne.what());
×
3746
    }
×
3747
    catch (const PDNSException& ae) {
2✔
3748
      errlog("Fatal pdns error: %s", ae.reason);
1✔
3749
    }
1✔
3750
    doExitNicely(EXIT_FAILURE);
2✔
3751
  }
2✔
3752
  catch (const std::exception& e) {
766✔
3753
    errlog("Fatal error: %s", e.what());
1✔
3754
    doExitNicely(EXIT_FAILURE);
1✔
3755
  }
1✔
3756
  catch (const PDNSException& ae) {
766✔
3757
    errlog("Fatal pdns error: %s", ae.reason);
×
3758
    doExitNicely(EXIT_FAILURE);
3759
  }
×
3760
}
766✔
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