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

PowerDNS / pdns / 18283365864

06 Oct 2025 01:58PM UTC coverage: 66.134% (+3.8%) from 62.31%
18283365864

Pull #16214

github

web-flow
Merge a30f69413 into cf120217d
Pull Request #16214: dnsdist: Refactor the FFI "alternate name" interface

42524 of 93024 branches covered (45.71%)

Branch coverage included in aggregate %.

74 of 103 new or added lines in 3 files covered. (71.84%)

32 existing lines in 11 files now uncovered.

129365 of 166886 relevant lines covered (77.52%)

6522221.1 hits per line

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

74.48
/pdns/recursordist/pdns_recursor.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 "rec-main.hh"
24

25
#include "arguments.hh"
26
#include "dns_random.hh"
27
#include "ednsextendederror.hh"
28
#include "ednspadding.hh"
29
#include "query-local-address.hh"
30
#include "rec-taskqueue.hh"
31
#include "shuffle.hh"
32
#include "validate-recursor.hh"
33
#include "ratelimitedlog.hh"
34
#include "ednsoptions.hh"
35

36
#ifdef HAVE_SYSTEMD
37
#include <systemd/sd-daemon.h>
38
#endif
39

40
#ifdef NOD_ENABLED
41
#include "nod.hh"
42
#include "logging.hh"
43
#endif /* NOD_ENABLED */
44

45
thread_local std::shared_ptr<RecursorLua4> t_pdl;
46
thread_local std::shared_ptr<Regex> t_traceRegex;
47
thread_local FDWrapper t_tracefd = -1;
48
thread_local ProtobufServersInfo t_protobufServers;
49
thread_local ProtobufServersInfo t_outgoingProtobufServers;
50

51
thread_local std::unique_ptr<MT_t> g_multiTasker; // the big MTasker
52
std::unique_ptr<MemRecursorCache> g_recCache;
53
std::unique_ptr<NegCache> g_negCache;
54
std::unique_ptr<RecursorPacketCache> g_packetCache;
55

56
thread_local std::unique_ptr<FDMultiplexer> t_fdm;
57
thread_local std::unique_ptr<addrringbuf_t> t_remotes, t_servfailremotes, t_largeanswerremotes, t_bogusremotes;
58
thread_local std::unique_ptr<boost::circular_buffer<pair<DNSName, uint16_t>>> t_queryring, t_servfailqueryring, t_bogusqueryring;
59
thread_local std::shared_ptr<NetmaskGroup> t_allowFrom;
60
thread_local std::shared_ptr<NetmaskGroup> t_allowNotifyFrom;
61
thread_local std::shared_ptr<notifyset_t> t_allowNotifyFor;
62
thread_local std::shared_ptr<NetmaskGroup> t_proxyProtocolACL;
63
thread_local std::shared_ptr<std::set<ComboAddress>> t_proxyProtocolExceptions;
64

65
__thread struct timeval g_now; // timestamp, updated (too) frequently
66

67
using listenSocketsAddresses_t = map<int, ComboAddress>; // is shared across all threads right now
68

69
static listenSocketsAddresses_t g_listenSocketsAddresses; // is shared across all threads right now
70
static set<int> g_fromtosockets; // listen sockets that use 'sendfromto()' mechanism (without actually using sendfromto())
71
NetmaskGroup g_paddingFrom;
72
size_t g_proxyProtocolMaximumSize;
73
size_t g_maxUDPQueriesPerRound;
74
unsigned int g_maxMThreads;
75
unsigned int g_paddingTag;
76
PaddingMode g_paddingMode;
77
uint16_t g_udpTruncationThreshold;
78
std::atomic<bool> g_quiet;
79
bool g_allowNoRD;
80
bool g_logCommonErrors;
81
bool g_reusePort{false};
82
bool g_gettagNeedsEDNSOptions{false};
83
bool g_useKernelTimestamp;
84
std::atomic<uint32_t> g_maxCacheEntries, g_maxPacketCacheEntries;
85
boost::container::flat_set<uint16_t> g_avoidUdpSourcePorts;
86
uint16_t g_minUdpSourcePort;
87
uint16_t g_maxUdpSourcePort;
88
double g_balancingFactor;
89

90
bool g_lowercaseOutgoing;
91
unsigned int g_networkTimeoutMsec;
92
uint16_t g_outgoingEDNSBufsize;
93

94
// Used in Syncres to counts DNSSEC stats for names in a different "universe"
95
GlobalStateHolder<SuffixMatchNode> g_xdnssec;
96
// Used in the Syncres to not throttle certain servers
97
GlobalStateHolder<SuffixMatchNode> g_dontThrottleNames;
98
GlobalStateHolder<NetmaskGroup> g_dontThrottleNetmasks;
99
GlobalStateHolder<SuffixMatchNode> g_DoTToAuthNames;
100
uint64_t g_latencyStatSize;
101

102
LWResult::Result UDPClientSocks::getSocket(const ComboAddress& toaddr, const std::optional<ComboAddress>& localAddress, int* fileDesc)
103
{
10,043✔
104
  *fileDesc = makeClientSocket(toaddr.sin4.sin_family, localAddress);
10,043✔
105
  if (*fileDesc < 0) { // temporary error - receive exception otherwise
10,043!
106
    return LWResult::Result::OSLimitError;
×
107
  }
×
108

109
  if (connect(*fileDesc, reinterpret_cast<const struct sockaddr*>(&toaddr), toaddr.getSocklen()) < 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast))
10,043!
110
    int err = errno;
×
111
    try {
×
112
      closesocket(*fileDesc);
×
113
    }
×
114
    catch (const PDNSException& e) {
×
115
      g_slogout->error(Logr::Error, e.reason, "Error closing UDP socket after connect() failed", "exception", Logging::Loggable("PDNSException"));
×
116
    }
×
117

118
    if (err == ENETUNREACH) { // Seth "My Interfaces Are Like A Yo Yo" Arnold special
×
119
      return LWResult::Result::OSLimitError;
×
120
    }
×
121

122
    return LWResult::Result::PermanentError;
×
123
  }
×
124

125
  d_numsocks++;
10,043✔
126
  return LWResult::Result::Success;
10,043✔
127
}
10,043✔
128

129
// return a socket to the pool, or simply erase it
130
void UDPClientSocks::returnSocket(int fileDesc)
131
{
10,043✔
132
  try {
10,043✔
133
    t_fdm->removeReadFD(fileDesc);
10,043✔
134
  }
10,043✔
135
  catch (const FDMultiplexerException& e) {
10,043✔
136
    // we sometimes return a socket that has not yet been assigned to t_fdm
137
  }
×
138

139
  try {
10,043✔
140
    closesocket(fileDesc);
10,041✔
141
  }
10,041✔
142
  catch (const PDNSException& e) {
10,041✔
143
    g_slogout->error(Logr::Error, e.reason, "Error closing returned UDP socket", "exception", Logging::Loggable("PDNSException"));
×
144
  }
×
145

146
  --d_numsocks;
10,042✔
147
}
10,040✔
148

149
// returns -1 for errors which might go away, throws for ones that won't
150
int UDPClientSocks::makeClientSocket(int family, const std::optional<ComboAddress>& localAddress)
151
{
10,042✔
152
  int ret = socket(family, SOCK_DGRAM, 0); // turns out that setting CLO_EXEC and NONBLOCK from here is not a performance win on Linux (oddly enough)
10,042✔
153

154
  if (ret < 0 && errno == EMFILE) { // this is not a catastrophic error
10,042!
155
    return ret;
×
156
  }
×
157
  if (ret < 0) {
10,042!
158
    int err = errno;
×
159
    throw PDNSException("Making a socket for resolver (family = " + std::to_string(family) + "): " + stringerror(err));
×
160
  }
×
161

162
  // The loop below runs the body with [tries-1 tries-2 ... 1]. Last iteration with tries == 1 is special: it uses a kernel
163
  // allocated UDP port.
164
#if !defined(__OpenBSD__)
10,042✔
165
  int tries = 10;
10,042✔
166
#else
167
  int tries = 2; // hit the reliable kernel random case for OpenBSD immediately (because it will match tries==1 below), using sysctl net.inet.udp.baddynamic to exclude ports
168
#endif
169
  ComboAddress sin;
10,042✔
170
  while (--tries != 0) {
10,057✔
171
    in_port_t port = 0;
10,051✔
172

173
    if (tries == 1) { // last iteration: fall back to kernel 'random'
10,051!
174
      port = 0;
×
175
    }
×
176
    else {
10,051✔
177
      do {
10,051✔
178
        port = g_minUdpSourcePort + dns_random(g_maxUdpSourcePort - g_minUdpSourcePort + 1);
10,051✔
179
      } while (g_avoidUdpSourcePorts.count(port) != 0);
10,051!
180
    }
10,051✔
181

182
    // localAddress is set if a cookie was involved, bind to the same address the cookie is
183
    // associated with (RFC 9018 section 3 last paragraph)
184
    if (localAddress) {
10,051✔
185
      sin = *localAddress;
20✔
186
      sin.setPort(port);
20✔
187
    }
20✔
188
    else {
10,031✔
189
      sin = pdns::getQueryLocalAddress(family, port); // does htons for us
10,031✔
190
    }
10,031✔
191
    if (::bind(ret, reinterpret_cast<struct sockaddr*>(&sin), sin.getSocklen()) >= 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
10,051✔
192
      break;
10,036✔
193
    }
10,036✔
194
  }
10,051✔
195

196
  int err = errno;
10,042✔
197

198
  if (tries == 0) {
10,042!
199
    closesocket(ret);
×
200
    throw PDNSException("Resolver binding to local query client socket on " + sin.toString() + ": " + stringerror(err));
×
201
  }
×
202

203
  try {
10,042✔
204
    setReceiveSocketErrors(ret, family);
10,042✔
205
    setNonBlocking(ret);
10,042✔
206
  }
10,042✔
207
  catch (...) {
10,042✔
208
    closesocket(ret);
×
209
    throw;
×
210
  }
×
211
  return ret;
10,040✔
212
}
10,042✔
213

214
static void handleGenUDPQueryResponse(int fileDesc, FDMultiplexer::funcparam_t& var)
215
{
4✔
216
  auto pident = boost::any_cast<std::shared_ptr<PacketID>>(var);
4✔
217
  PacketBuffer resp;
4✔
218
  resp.resize(512);
4✔
219
  ComboAddress fromaddr;
4✔
220
  socklen_t addrlen = sizeof(fromaddr);
4✔
221

222
  ssize_t ret = recvfrom(fileDesc, resp.data(), resp.size(), 0, reinterpret_cast<sockaddr*>(&fromaddr), &addrlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
4✔
223
  if (fromaddr != pident->remote) {
4!
224
    g_slog->withName("lua")->info(Logr::Notice, "Response received from the wrong remote host. discarding", "method", Logging::Loggable("GenUDPQueryResponse"), "fromaddr", Logging::Loggable(fromaddr), "expected", Logging::Loggable(pident->remote));
×
225
  }
×
226

227
  t_fdm->removeReadFD(fileDesc);
4✔
228
  if (ret >= 0) {
4!
229
    resp.resize(ret);
4✔
230
    g_multiTasker->sendEvent(pident, &resp);
4✔
231
  }
4✔
232
  else {
×
233
    PacketBuffer empty;
×
234
    g_multiTasker->sendEvent(pident, &empty);
×
235
  }
×
236
}
4✔
237

238
PacketBuffer GenUDPQueryResponse(const ComboAddress& dest, const string& query)
239
{
4✔
240
  Socket socket(dest.sin4.sin_family, SOCK_DGRAM);
4✔
241
  socket.setNonBlocking();
4✔
242
  ComboAddress local = pdns::getQueryLocalAddress(dest.sin4.sin_family, 0);
4✔
243

244
  socket.bind(local);
4✔
245
  socket.connect(dest);
4✔
246
  socket.send(query);
4✔
247

248
  std::shared_ptr<PacketID> pident = std::make_shared<PacketID>();
4✔
249
  pident->fd = socket.getHandle();
4✔
250
  pident->remote = dest;
4✔
251
  pident->type = 0;
4✔
252
  t_fdm->addReadFD(socket.getHandle(), handleGenUDPQueryResponse, pident);
4✔
253

254
  PacketBuffer data;
4✔
255
  int ret = g_multiTasker->waitEvent(pident, &data, authWaitTimeMSec(g_multiTasker));
4✔
256

257
  if (ret == 0 || ret == -1) { // timeout
4!
258
    t_fdm->removeReadFD(socket.getHandle());
×
259
  }
×
260
  else if (data.empty()) { // error, EOF or other
4!
261
    // we could special case this
262
    return data;
×
263
  }
×
264
  return data;
4✔
265
}
4✔
266

267
static void handleUDPServerResponse(int fileDesc, FDMultiplexer::funcparam_t& var);
268

269
thread_local std::unique_ptr<UDPClientSocks> t_udpclientsocks;
270

271
// If we have plenty of mthreads slot left, use default timeout.
272
// Otherwise reduce the timeout to be between g_networkTimeoutMsec/10 and g_networkTimeoutMsec
273
unsigned int authWaitTimeMSec(const std::unique_ptr<MT_t>& mtasker)
274
{
13,290✔
275
  const auto max = g_maxMThreads;
13,290✔
276
  const auto current = mtasker->numProcesses();
13,290✔
277
  const unsigned int cutoff = max / 10; // if we have less than 10% used,  do not reduce auth timeout
13,290✔
278
  if (current < cutoff) {
13,294✔
279
    return g_networkTimeoutMsec;
13,293✔
280
  }
13,293✔
281
  const auto avail = max - current;
2,147,483,647✔
282
  return std::max(g_networkTimeoutMsec / 10, g_networkTimeoutMsec * avail / (max - cutoff));
2,147,483,647✔
283
}
13,290✔
284

285
/* these two functions are used by LWRes */
286
LWResult::Result asendto(const void* data, size_t len, int /* flags */,
287
                         const ComboAddress& toAddress, std::optional<ComboAddress>& localAddress, uint16_t qid, const DNSName& domain, uint16_t qtype, const std::optional<EDNSSubnetOpts>& ecs, int* fileDesc, timeval& now)
288
{
11,640✔
289

290
  auto pident = std::make_shared<PacketID>();
11,640✔
291
  pident->domain = domain;
11,640✔
292
  pident->remote = toAddress;
11,640✔
293
  pident->type = qtype;
11,640✔
294
  if (ecs) {
11,640✔
295
    pident->ecsSubnet = ecs->getSource();
575✔
296
  }
575✔
297

298
  // See if there is an existing outstanding request we can chain on to, using partial equivalence
299
  // function looking for the same query (qname, qtype and ecs if applicable) to the same host, but
300
  // with a different message ID.
301
  auto chain = g_multiTasker->getWaiters().equal_range(pident, PacketIDBirthdayCompare());
11,640✔
302

303
  for (; chain.first != chain.second; chain.first++) {
98,333✔
304
    // Line below detected an issue with the two ways of ordering PacketIDs (birthday and non-birthday)
305
    assert(chain.first->key->domain == pident->domain); // NOLINT
88,294✔
306
    // don't chain onto existing chained waiter or a chain already processed
307
    if (chain.first->key->fd > -1 && !chain.first->key->closed && pident->ecsSubnet == chain.first->key->ecsSubnet) {
88,294✔
308
      auto currentChainSize = chain.first->key->authReqChain.size();
1,601✔
309
      *fileDesc = -static_cast<int>(currentChainSize + 1); // value <= -1, gets used in waitEvent / sendEvent later on
1,601✔
310
      if (g_maxChainLength > 0 && currentChainSize >= g_maxChainLength) {
1,601!
311
        return LWResult::Result::ChainLimitError;
×
312
      }
×
313
      assert(uSec(chain.first->key->creationTime) != 0); // NOLINT
1,601✔
314
      auto age = now - chain.first->key->creationTime;
×
315
      if (uSec(age) > static_cast<uint64_t>(1000) * authWaitTimeMSec(g_multiTasker) * 2 / 3) {
1,601!
316
        return LWResult::Result::ChainLimitError;
×
317
      }
×
318
      chain.first->key->authReqChain.emplace(*fileDesc, qid); // we can chain
1,601✔
319
      auto maxLength = t_Counters.at(rec::Counter::maxChainLength);
1,601✔
320
      if (currentChainSize + 1 > maxLength) {
1,601✔
321
        t_Counters.at(rec::Counter::maxChainLength) = currentChainSize + 1;
467✔
322
      }
467✔
323
      return LWResult::Result::Success;
1,601✔
324
    }
1,601✔
325
  }
88,294✔
326

327
  auto ret = t_udpclientsocks->getSocket(toAddress, localAddress, fileDesc);
10,039✔
328
  if (ret != LWResult::Result::Success) {
10,039!
329
    return ret;
×
330
  }
×
331

332
  pident->fd = *fileDesc;
10,039✔
333
  pident->id = qid;
10,039✔
334

335
  t_fdm->addReadFD(*fileDesc, handleUDPServerResponse, pident);
10,039✔
336
  ssize_t sent = send(*fileDesc, data, len, 0);
10,039✔
337

338
  int tmp = errno;
10,039✔
339

340
  if (sent < 0) {
10,039!
341
    t_udpclientsocks->returnSocket(*fileDesc);
×
342
    errno = tmp; // this is for logging purposes only
×
343
    return LWResult::Result::PermanentError;
×
344
  }
×
345

346
  return LWResult::Result::Success;
10,039✔
347
}
10,039✔
348

349
static bool checkIncomingECSSource(const PacketBuffer& packet, const Netmask& subnet);
350

351
LWResult::Result arecvfrom(PacketBuffer& packet, int /* flags */, const ComboAddress& fromAddr, size_t& len,
352
                           uint16_t qid, const DNSName& domain, uint16_t qtype, int fileDesc, const std::optional<EDNSSubnetOpts>& ecs, const struct timeval& now)
353
{
11,641✔
354
  static const unsigned int nearMissLimit = ::arg().asNum("spoof-nearmiss-max");
11,641✔
355

356
  auto pident = std::make_shared<PacketID>();
11,641✔
357
  pident->fd = fileDesc;
11,641✔
358
  pident->id = qid;
11,641✔
359
  pident->domain = domain;
11,641✔
360
  pident->type = qtype;
11,641✔
361
  pident->remote = fromAddr;
11,641✔
362
  pident->creationTime = now;
11,641✔
363
  if (ecs) {
11,641✔
364
    // We sent out the query using ecs
365
    // We expect incoming source ECS to match, see https://www.rfc-editor.org/rfc/rfc7871#section-7.3
366
    // But there's also section 11-2, which says we should treat absent incoming ecs as scope zero
367
    // We fill in the search key with the ecs we sent out, so both cases are covered and accepted here.
368
    pident->ecsSubnet = ecs->getSource();
575✔
369
  }
575✔
370
  int ret = g_multiTasker->waitEvent(pident, &packet, authWaitTimeMSec(g_multiTasker), &now);
11,641✔
371
  len = 0;
11,641✔
372

373
  /* -1 means error, 0 means timeout, 1 means a result from handleUDPServerResponse() which might still be an error */
374
  if (ret > 0) {
11,644✔
375
    /* handleUDPServerResponse() will close the socket for us no matter what */
376
    if (packet.empty()) { // means "error"
11,638✔
377
      return LWResult::Result::PermanentError;
64✔
378
    }
64✔
379

380
    len = packet.size();
11,574✔
381

382
    // In ecs hardening mode, we consider a missing or a mismatched ECS in the reply as a case for
383
    // retrying without ECS. The actual logic to do that is in Syncres::doResolveAtThisIP()
384
    if (g_ECSHardening && pident->ecsSubnet && !checkIncomingECSSource(packet, *pident->ecsSubnet)) {
11,574✔
385
      t_Counters.at(rec::Counter::ecsMissingCount)++;
277✔
386
      return LWResult::Result::ECSMissing;
277✔
387
    }
277✔
388
    if (nearMissLimit > 0 && pident->nearMisses > nearMissLimit) {
11,297!
389
      /* we have received more than nearMissLimit answers on the right IP and port, from the right source (we are using connected sockets),
390
         for the correct qname and qtype, but with an unexpected message ID. That looks like a spoofing attempt. */
391
      g_slogudpin->info(Logr::Error, "Too many answers with a wrong message ID, assuming spoofing attempt",
×
392
                        "nearmisses", Logging::Loggable(pident->nearMisses),
×
393
                        "nearmisslimit", Logging::Loggable(nearMissLimit),
×
394
                        "qname", Logging::Loggable(domain),
×
395
                        "from", Logging::Loggable(fromAddr));
×
396
      t_Counters.at(rec::Counter::spoofCount)++;
×
397
      return LWResult::Result::Spoofed;
×
398
    }
×
399

400
    return LWResult::Result::Success;
11,297✔
401
  }
11,297✔
402
  /* getting there means error or timeout, it's up to us to close the socket */
403
  if (fileDesc >= 0) {
2,147,483,650✔
404
    t_udpclientsocks->returnSocket(fileDesc);
5✔
405
  }
5✔
406

407
  return ret == 0 ? LWResult::Result::Timeout : LWResult::Result::PermanentError;
2,147,483,650✔
408
}
11,641✔
409

410
// the idea is, only do things that depend on the *response* here. Incoming accounting is on incoming.
411
static void updateResponseStats(int res, const ComboAddress& remote, unsigned int packetsize, const DNSName* query, uint16_t qtype)
412
{
6,195✔
413
  if (packetsize > 1000 && t_largeanswerremotes) {
6,195!
414
    t_largeanswerremotes->push_back(remote);
33✔
415
  }
33✔
416
  switch (res) {
6,195✔
417
  case RCode::ServFail:
14✔
418
    if (t_servfailremotes) {
14!
419
      t_servfailremotes->push_back(remote);
14✔
420
      if (query != nullptr && t_servfailqueryring) { // packet cache
14!
421
        t_servfailqueryring->push_back({*query, qtype});
14✔
422
      }
14✔
423
    }
14✔
424
    ++t_Counters.at(rec::Counter::servFails);
14✔
425
    break;
14✔
426
  case RCode::NXDomain:
116✔
427
    ++t_Counters.at(rec::Counter::nxDomains);
116✔
428
    break;
116✔
429
  case RCode::NoError:
6,061✔
430
    t_Counters.at(rec::Counter::noErrors)++;
6,061✔
431
    break;
6,061✔
432
  }
6,195✔
433
}
6,195✔
434

435
/**
436
 * Chases the CNAME provided by the PolicyCustom RPZ policy.
437
 *
438
 * @param spoofed: The DNSRecord that was created by the policy, should already be added to ret
439
 * @param qtype: The QType of the original query
440
 * @param sr: A SyncRes
441
 * @param res: An integer that will contain the RCODE of the lookup we do
442
 * @param ret: A vector of DNSRecords where the result of the CNAME chase should be appended to
443
 */
444
static void handleRPZCustom(const DNSRecord& spoofed, const QType& qtype, SyncRes& resolver, int& res, vector<DNSRecord>& ret)
445
{
2✔
446
  if (spoofed.d_type == QType::CNAME) {
2!
447
    bool oldWantsRPZ = resolver.getWantsRPZ();
×
448
    resolver.setWantsRPZ(false);
×
449
    vector<DNSRecord> ans;
×
450
    res = resolver.beginResolve(DNSName(spoofed.getContent()->getZoneRepresentation()), qtype, QClass::IN, ans);
×
451
    for (const auto& rec : ans) {
×
452
      if (rec.d_place == DNSResourceRecord::ANSWER) {
×
453
        ret.push_back(rec);
×
454
      }
×
455
    }
×
456
    // Reset the RPZ state of the SyncRes
457
    resolver.setWantsRPZ(oldWantsRPZ);
×
458
  }
×
459
}
2✔
460

461
static bool addRecordToPacket(DNSPacketWriter& packetWritewr, const DNSRecord& rec, uint32_t& minTTL, uint32_t ttlCap, const uint16_t maxAnswerSize, bool& seenAuthSOA)
462
{
9,920✔
463
  packetWritewr.startRecord(rec.d_name, rec.d_type, (rec.d_ttl > ttlCap ? ttlCap : rec.d_ttl), rec.d_class, rec.d_place);
9,920!
464

465
  if (rec.d_type == QType::SOA && rec.d_place == DNSResourceRecord::AUTHORITY) {
9,920✔
466
    seenAuthSOA = true;
385✔
467
  }
385✔
468

469
  if (rec.d_type != QType::OPT) { // their TTL ain't real
9,920!
470
    minTTL = min(minTTL, rec.d_ttl);
9,920✔
471
  }
9,920✔
472

473
  rec.getContent()->toPacket(packetWritewr);
9,920✔
474
  if (packetWritewr.size() > static_cast<size_t>(maxAnswerSize)) {
9,920✔
475
    packetWritewr.rollback();
4✔
476
    if (rec.d_place != DNSResourceRecord::ADDITIONAL) {
4!
477
      packetWritewr.getHeader()->tc = 1;
4✔
478
      packetWritewr.truncate();
4✔
479
    }
4✔
480
    return false;
4✔
481
  }
4✔
482

483
  return true;
9,916✔
484
}
9,920✔
485

486
/**
487
 * A helper class that handles the TCP in-flight bookkeeping on
488
 * destruct. This class ise used by startDoResolve() to not forget
489
 * that. You can also signal that the TCP connection must be closed
490
 * once the in-flight connections drop to zero.
491
 **/
492
class RunningResolveGuard
493
{
494
public:
495
  RunningResolveGuard(const RunningResolveGuard&) = default;
496
  RunningResolveGuard(RunningResolveGuard&&) = delete;
497
  RunningResolveGuard& operator=(const RunningResolveGuard&) = delete;
498
  RunningResolveGuard& operator=(RunningResolveGuard&&) = delete;
499
  RunningResolveGuard(std::unique_ptr<DNSComboWriter>& comboWriter) :
500
    d_dc(comboWriter)
501
  {
3,196✔
502
    if (d_dc->d_tcp && !d_dc->d_tcpConnection) {
3,196!
503
      throw std::runtime_error("incoming TCP case without TCP connection");
×
504
    }
×
505
  }
3,196✔
506
  ~RunningResolveGuard()
507
  {
3,196✔
508
    if (!d_handled && d_dc->d_tcp) {
3,196✔
509
      try {
6✔
510
        finishTCPReply(d_dc, false, true);
6✔
511
      }
6✔
512
      catch (const FDMultiplexerException&) {
6✔
513
      }
×
514
    }
6✔
515
  }
3,196✔
516
  void setHandled()
517
  {
785✔
518
    d_handled = true;
785✔
519
  }
785✔
520
  void setDropOnIdle()
521
  {
7✔
522
    if (d_dc->d_tcp) {
7✔
523
      d_dc->d_tcpConnection->setDropOnIdle();
3✔
524
    }
3✔
525
  }
7✔
526

527
private:
528
  std::unique_ptr<DNSComboWriter>& d_dc; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
529
  bool d_handled{false};
530
};
531

532
enum class PolicyResult : uint8_t
533
{
534
  NoAction,
535
  HaveAnswer,
536
  Drop
537
};
538

539
static PolicyResult handlePolicyHit(const DNSFilterEngine::Policy& appliedPolicy, const std::unique_ptr<DNSComboWriter>& comboWriter, SyncRes& resolver, int& res, vector<DNSRecord>& ret, DNSPacketWriter& packetWriter, RunningResolveGuard& tcpGuard)
540
{
91✔
541
  /* don't account truncate actions for TCP queries, since they are not applied */
542
  if (appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::Truncate || !comboWriter->d_tcp) {
91!
543
    ++t_Counters.at(rec::PolicyHistogram::policy).at(appliedPolicy.d_kind);
91✔
544
    ++t_Counters.at(rec::PolicyNameHits::policyName).counts[appliedPolicy.getName()];
91✔
545
  }
91✔
546

547
  if (resolver.doLog() && appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
91!
548
    appliedPolicy.info(Logr::Warning, resolver.d_slog);
6✔
549
  }
6✔
550

551
  if (appliedPolicy.d_zoneData && appliedPolicy.d_zoneData->d_extendedErrorCode) {
91!
552
    comboWriter->d_extendedErrorCode = *appliedPolicy.d_zoneData->d_extendedErrorCode;
×
553
    comboWriter->d_extendedErrorExtra = appliedPolicy.d_zoneData->d_extendedErrorExtra;
×
554
  }
×
555

556
  switch (appliedPolicy.d_kind) {
91!
557

558
  case DNSFilterEngine::PolicyKind::NoAction:
77✔
559
    return PolicyResult::NoAction;
77✔
560

561
  case DNSFilterEngine::PolicyKind::Drop:
7✔
562
    tcpGuard.setDropOnIdle();
7✔
563
    ++t_Counters.at(rec::Counter::policyDrops);
7✔
564
    return PolicyResult::Drop;
7✔
565

566
  case DNSFilterEngine::PolicyKind::NXDOMAIN:
1✔
567
    ret.clear();
1✔
568
    appliedPolicy.addSOAtoRPZResult(ret);
1✔
569
    res = RCode::NXDomain;
1✔
570
    return PolicyResult::HaveAnswer;
1✔
571

572
  case DNSFilterEngine::PolicyKind::NODATA:
1✔
573
    ret.clear();
1✔
574
    appliedPolicy.addSOAtoRPZResult(ret);
1✔
575
    res = RCode::NoError;
1✔
576
    return PolicyResult::HaveAnswer;
1✔
577

578
  case DNSFilterEngine::PolicyKind::Truncate:
1✔
579
    if (!comboWriter->d_tcp) {
1!
580
      ret.clear();
1✔
581
      appliedPolicy.addSOAtoRPZResult(ret);
1✔
582
      res = RCode::NoError;
1✔
583
      packetWriter.getHeader()->tc = 1;
1✔
584
      return PolicyResult::HaveAnswer;
1✔
585
    }
1✔
586
    return PolicyResult::NoAction;
×
587

588
  case DNSFilterEngine::PolicyKind::Custom:
4✔
589
    res = RCode::NoError;
4✔
590
    {
4✔
591
      auto spoofed = appliedPolicy.getCustomRecords(comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype);
4✔
592
      for (auto& record : spoofed) {
4✔
593
        ret.push_back(record);
2✔
594
        try {
2✔
595
          handleRPZCustom(record, QType(comboWriter->d_mdp.d_qtype), resolver, res, ret);
2✔
596
        }
2✔
597
        catch (const ImmediateServFailException& e) {
2✔
598
          if (g_logCommonErrors) {
×
599
            resolver.d_slog->error(Logr::Notice, e.reason, "Sending SERVFAIL during resolve of the custom filter policy",
×
600
                                   "policyName", Logging::Loggable(appliedPolicy.getName()), "exception", Logging::Loggable("ImmediateServFailException"));
×
601
          }
×
602
          res = RCode::ServFail;
×
603
          break;
×
604
        }
×
605
        catch (const pdns::validation::TooManySEC3IterationsException& e) {
2✔
606
          if (g_logCommonErrors || (g_dnssecLogBogus && resolver.getDNSSECLimitHit())) {
×
607
            resolver.d_slog->error(Logr::Notice, e.what(), "Sending SERVFAIL during resolve of the custom filter policy",
×
608
                                   "policyName", Logging::Loggable(appliedPolicy.getName()), "exception", Logging::Loggable("TooManySEC3IterationsException"), "dnsseclimithit", Logging::Loggable(resolver.getDNSSECLimitHit()));
×
609
          }
×
610
          res = RCode::ServFail;
×
611
          break;
×
612
        }
×
613
        catch (const PolicyHitException& e) {
2✔
614
          if (g_logCommonErrors) {
×
615
            resolver.d_slog->info(Logr::Notice, "Sending SERVFAIL during resolve of the custom filter policy because another RPZ policy was hit",
×
616
                                  "policyName", Logging::Loggable(appliedPolicy.getName()), "exception", Logging::Loggable("PolicyHitException"));
×
617
          }
×
618
          res = RCode::ServFail;
×
619
          break;
×
620
        }
×
621
      }
2✔
622

623
      appliedPolicy.addSOAtoRPZResult(ret);
4✔
624
      return PolicyResult::HaveAnswer;
4✔
625
    }
4✔
626
  }
91✔
627

628
  return PolicyResult::NoAction;
×
629
}
91✔
630

631
#ifdef NOD_ENABLED
632
static bool nodCheckNewDomain(Logr::log_t nodlogger, const DNSName& dname)
633
{
3✔
634
  bool ret = false;
3✔
635
  // First check the (sub)domain isn't ignored for NOD purposes
636
  if (g_nodDomainWL.check(dname)) {
3!
637
    return ret;
×
638
  }
×
639
  // Now check the NODDB (note this is probabilistic so can have FNs/FPs)
640
  if (g_nodDBp && g_nodDBp->isNewDomain(dname)) {
3!
641
    if (g_nodLog) {
3!
642
      // This should probably log to a dedicated log file
643
      nodlogger->info(Logr::Notice, "New domain observed");
3✔
644
    }
3✔
645
    t_Counters.at(rec::Counter::nodCount)++;
3✔
646
    ret = true;
3✔
647
  }
3✔
648
  return ret;
3✔
649
}
3✔
650

651
static void sendNODLookup(Logr::log_t nodlogger, const DNSName& dname)
652
{
3✔
653
  if (!(g_nodLookupDomain.isRoot())) {
3!
654
    // Send a DNS A query to <domain>.g_nodLookupDomain
655
    DNSName qname;
×
656
    try {
×
657
      qname = dname + g_nodLookupDomain;
×
658
    }
×
659
    catch (const std::range_error& e) {
×
660
      if (g_logCommonErrors) {
×
661
        nodlogger->v(10)->error(Logr::Error, "DNSName too long", "Unable to send NOD lookup");
×
662
      }
×
663
      ++t_Counters.at(rec::Counter::nodLookupsDroppedOversize);
×
664
      return;
×
665
    }
×
666
    nodlogger->v(10)->info(Logr::Debug, "Sending NOD lookup", "nodqname", Logging::Loggable(qname));
×
667
    vector<DNSRecord> dummy;
×
668
    directResolve(qname, QType::A, QClass::IN, dummy, nullptr, false, nodlogger);
×
669
  }
×
670
}
3✔
671

672
static bool udrCheckUniqueDNSRecord(Logr::log_t nodlogger, const DNSName& dname, uint16_t qtype, const DNSRecord& record)
673
{
3✔
674
  bool ret = false;
3✔
675
  // First check the (sub)domain isn't ignored for UDR purposes
676
  if (g_udrDomainWL.check(dname)) {
3!
677
    return ret;
×
678
  }
×
679
  if (record.d_place == DNSResourceRecord::ANSWER || record.d_place == DNSResourceRecord::ADDITIONAL) {
3!
680
    // Create a string that represent a triplet of (qname, qtype and RR[type, name, content])
681
    std::stringstream strStream;
3✔
682
    strStream << dname.toDNSStringLC() << ":" << qtype << ":" << qtype << ":" << record.d_type << ":" << record.d_name.toDNSStringLC() << ":" << record.getContent()->getZoneRepresentation();
3✔
683
    if (g_udrDBp && g_udrDBp->isUniqueResponse(strStream.str())) {
3!
684
      if (g_udrLog) {
3!
685
        // This should also probably log to a dedicated file.
686
        nodlogger->info(Logr::Notice, "New response observed",
3✔
687
                        "qtype", Logging::Loggable(QType(qtype)),
3✔
688
                        "rrtype", Logging::Loggable(QType(record.d_type)),
3✔
689
                        "rrname", Logging::Loggable(record.d_name),
3✔
690
                        "rrcontent", Logging::Loggable(record.getContent()->getZoneRepresentation()));
3✔
691
      }
3✔
692
      t_Counters.at(rec::Counter::udrCount)++;
3✔
693
      ret = true;
3✔
694
    }
3✔
695
  }
3✔
696
  return ret;
3✔
697
}
3✔
698
#endif /* NOD_ENABLED */
699

700
static bool dns64Candidate(uint16_t requestedType, int rcode, const std::vector<DNSRecord>& records);
701

702
int followCNAMERecords(vector<DNSRecord>& ret, const QType qtype, int rcode)
703
{
4✔
704
  vector<DNSRecord> resolved;
4✔
705
  DNSName target;
4✔
706
  for (const DNSRecord& record : ret) {
4!
707
    if (record.d_type == QType::CNAME) {
4!
708
      auto rec = getRR<CNAMERecordContent>(record);
4✔
709
      if (rec) {
4!
710
        target = rec->getTarget();
4✔
711
        break;
4✔
712
      }
4✔
713
    }
4✔
714
  }
4✔
715

716
  if (target.empty()) {
4!
717
    return rcode;
×
718
  }
×
719

720
  auto log = g_slog->withName("lua")->withValues("method", Logging::Loggable("followCNAMERecords"));
4✔
721
  rcode = directResolve(target, qtype, QClass::IN, resolved, t_pdl, log);
4✔
722

723
  if (g_dns64Prefix && qtype == QType::AAAA && dns64Candidate(qtype, rcode, resolved)) {
4!
724
    rcode = getFakeAAAARecords(target, *g_dns64Prefix, resolved);
2✔
725
  }
2✔
726

727
  for (DNSRecord& record : resolved) {
9✔
728
    if (record.d_place == DNSResourceRecord::ANSWER) {
9!
729
      ret.push_back(std::move(record));
9✔
730
    }
9✔
731
  }
9✔
732
  return rcode;
4✔
733
}
4✔
734

735
int getFakeAAAARecords(const DNSName& qname, ComboAddress prefix, vector<DNSRecord>& ret)
736
{
18✔
737
  auto log = g_slog->withName("dns64")->withValues("method", Logging::Loggable("getAAAA"));
18✔
738
  /* we pass a separate vector of records because we will be resolving the initial qname
739
     again, possibly encountering the same CNAME(s), and we don't want to trigger the CNAME
740
     loop detection. */
741
  vector<DNSRecord> newRecords;
18✔
742
  int rcode = directResolve(qname, QType::A, QClass::IN, newRecords, t_pdl, log);
18✔
743

744
  ret.reserve(ret.size() + newRecords.size());
18✔
745
  for (auto& record : newRecords) {
31✔
746
    ret.push_back(std::move(record));
31✔
747
  }
31✔
748

749
  // Remove double CNAME records
750
  std::set<DNSName> seenCNAMEs;
18✔
751
  ret.erase(std::remove_if(
18✔
752
              ret.begin(),
18✔
753
              ret.end(),
18✔
754
              [&seenCNAMEs](DNSRecord& record) {
72✔
755
                if (record.d_type == QType::CNAME) {
72✔
756
                  auto target = getRR<CNAMERecordContent>(record);
20✔
757
                  if (target == nullptr) {
20!
758
                    return false;
×
759
                  }
×
760
                  if (seenCNAMEs.count(target->getTarget()) > 0) {
20✔
761
                    // We've had this CNAME before, remove it
762
                    return true;
10✔
763
                  }
10✔
764
                  seenCNAMEs.insert(target->getTarget());
10✔
765
                }
10✔
766
                return false;
62✔
767
              }),
72✔
768
            ret.end());
18✔
769

770
  bool seenA = false;
18✔
771
  for (DNSRecord& record : ret) {
62✔
772
    if (record.d_type == QType::A && record.d_place == DNSResourceRecord::ANSWER) {
62!
773
      if (auto rec = getRR<ARecordContent>(record)) {
14!
774
        ComboAddress ipv4(rec->getCA());
14✔
775
        memcpy(&prefix.sin6.sin6_addr.s6_addr[12], &ipv4.sin4.sin_addr.s_addr, sizeof(ipv4.sin4.sin_addr.s_addr));
14✔
776
        record.setContent(std::make_shared<AAAARecordContent>(prefix));
14✔
777
        record.d_type = QType::AAAA;
14✔
778
      }
14✔
779
      seenA = true;
14✔
780
    }
14✔
781
  }
62✔
782

783
  if (seenA) {
18✔
784
    // We've seen an A in the ANSWER section, so there is no need to keep any
785
    // SOA in the AUTHORITY section as this is not a NODATA response.
786
    ret.erase(std::remove_if(
14✔
787
                ret.begin(),
14✔
788
                ret.end(),
14✔
789
                [](DNSRecord& record) {
52✔
790
                  return (record.d_type == QType::SOA && record.d_place == DNSResourceRecord::AUTHORITY);
52!
791
                }),
52✔
792
              ret.end());
14✔
793
  }
14✔
794
  else {
4✔
795
    pdns::dedupRecords(ret);
4✔
796
  }
4✔
797
  t_Counters.at(rec::Counter::dns64prefixanswers)++;
18✔
798
  return rcode;
18✔
799
}
18✔
800

801
int getFakePTRRecords(const DNSName& qname, vector<DNSRecord>& ret)
802
{
2✔
803
  /* qname has a reverse ordered IPv6 address, need to extract the underlying IPv4 address from it
804
     and turn it into an IPv4 in-addr.arpa query */
805
  ret.clear();
2✔
806
  vector<string> parts = qname.getRawLabels();
2✔
807

808
  if (parts.size() < 8) {
2!
809
    return -1;
×
810
  }
×
811

812
  string newquery;
2✔
813
  for (size_t octet = 0; octet < 4; ++octet) {
10✔
814
    newquery += std::to_string(stoll(parts[octet * 2], nullptr, 16) + 16 * stoll(parts[octet * 2 + 1], nullptr, 16));
8✔
815
    newquery.append(1, '.');
8✔
816
  }
8✔
817
  newquery += "in-addr.arpa.";
2✔
818

819
  auto log = g_slog->withName("dns64")->withValues("method", Logging::Loggable("getPTR"));
2✔
820
  vector<DNSRecord> answers;
2✔
821
  int rcode = directResolve(DNSName(newquery), QType::PTR, QClass::IN, answers, t_pdl, log);
2✔
822

823
  DNSRecord record;
2✔
824
  record.d_name = qname;
2✔
825
  record.d_type = QType::CNAME;
2✔
826
  record.setContent(std::make_shared<CNAMERecordContent>(newquery));
2✔
827
  // Copy the TTL of the synthesized CNAME from the actual answer
828
  record.d_ttl = (rcode == RCode::NoError && !answers.empty()) ? answers.at(0).d_ttl : SyncRes::s_minimumTTL;
2!
829
  ret.push_back(std::move(record));
2✔
830

831
  ret.insert(ret.end(), answers.begin(), answers.end());
2✔
832

833
  t_Counters.at(rec::Counter::dns64prefixanswers)++;
2✔
834
  return rcode;
2✔
835
}
2✔
836

837
// RFC 6147 section 5.1 all rcodes except NXDomain should be candidate for dns64
838
// for NoError, check if it is NoData
839
static bool dns64Candidate(uint16_t requestedType, int rcode, const std::vector<DNSRecord>& records)
840
{
18✔
841
  if (rcode == RCode::NoError) {
18✔
842
    return SyncRes::answerIsNOData(requestedType, rcode, records);
16✔
843
  }
16✔
844
  return rcode != RCode::NXDomain;
2✔
845
}
18✔
846

847
bool isAllowNotifyForZone(DNSName qname)
848
{
24✔
849
  if (t_allowNotifyFor->empty()) {
24!
850
    return false;
×
851
  }
×
852

853
  do {
24✔
854
    auto ret = t_allowNotifyFor->find(qname);
24✔
855
    if (ret != t_allowNotifyFor->end()) {
24!
856
      return true;
24✔
857
    }
24✔
858
  } while (qname.chopOff());
24!
859
  return false;
×
860
}
24✔
861

862
#if defined(HAVE_FSTRM) && defined(NOD_ENABLED)
863
#include "dnstap.hh"
864
#include "fstrm_logger.hh"
865

866
static bool isEnabledForNODs(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
867
{
3✔
868
  if (fstreamLoggers == nullptr) {
3!
869
    return false;
×
870
  }
×
871
  for (auto& logger : *fstreamLoggers) {
3✔
872
    if (logger->logNODs()) {
3✔
873
      return true;
2✔
874
    }
2✔
875
  }
3✔
876
  return false;
1✔
877
}
3✔
878
static bool isEnabledForUDRs(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
879
{
3✔
880
  if (fstreamLoggers == nullptr) {
3!
881
    return false;
×
882
  }
×
883
  for (auto& logger : *fstreamLoggers) {
3✔
884
    if (logger->logUDRs()) {
3✔
885
      return true;
2✔
886
    }
2✔
887
  }
3✔
888
  return false;
1✔
889
}
3✔
890
#endif // HAVE_FSTRM
891

892
static void dumpTrace(const string& trace, const timeval& timev)
893
{
16✔
894
  if (trace.empty()) {
16✔
895
    return;
15✔
896
  }
15✔
897
  if (t_tracefd < 0) {
1!
898
    std::istringstream buf(trace);
1✔
899
    g_log << Logger::Warning << "=== START OF FAIL TRACE ====" << endl;
1✔
900
    for (string line; std::getline(buf, line);) {
11✔
901
      g_log << Logger::Warning << line << endl;
10✔
902
    }
10✔
903
    g_log << Logger::Warning << "=== END OF FAIL TRACE ====" << endl;
1✔
904
    return;
1✔
905
  }
1✔
906
  timeval now{};
×
907
  Utility::gettimeofday(&now);
×
908
  int traceFd = dup(t_tracefd);
×
909
  if (traceFd == -1) {
×
910
    int err = errno;
×
911
    g_slog->withName("trace")->error(Logr::Error, err, "Could not dup trace file");
×
912
    return;
×
913
  }
×
914
  setNonBlocking(traceFd);
×
915
  auto filep = pdns::UniqueFilePtr(fdopen(traceFd, "a"));
×
916
  if (!filep) {
×
917
    int err = errno;
×
918
    g_slog->withName("trace")->error(Logr::Error, err, "Could not write to trace file");
×
919
    close(traceFd);
×
920
    return;
×
921
  }
×
922
  timebuf_t timebuf;
×
923
  isoDateTimeMillis(timev, timebuf);
×
924
  fprintf(filep.get(), " us === START OF TRACE %s ===\n", timebuf.data());
×
925
  fprintf(filep.get(), "%s", trace.c_str());
×
926
  isoDateTimeMillis(now, timebuf);
×
927
  if (ferror(filep.get()) != 0) {
×
928
    int err = errno;
×
929
    g_slog->withName("trace")->error(Logr::Error, err, "Problems writing to trace file");
×
930
    // There's no guarantee the message below will end up in the stream, but we try our best
931
    clearerr(filep.get());
×
932
    fprintf(filep.get(), "=== TRACE %s TRUNCATED; USE FILE ARGUMENT INSTEAD OF `-' ===\n", timebuf.data());
×
933
  }
×
934
  else {
×
935
    fprintf(filep.get(), "=== END OF TRACE %s ===\n", timebuf.data());
×
936
  }
×
937
  // fclose by unique_ptr does implicit flush
938
}
×
939

940
static uint32_t capPacketCacheTTL(const struct dnsheader& hdr, uint32_t ttl, bool seenAuthSOA)
941
{
2,863✔
942
  if (hdr.rcode == RCode::NXDomain || (hdr.rcode == RCode::NoError && hdr.ancount == 0 && seenAuthSOA)) {
2,863✔
943
    ttl = std::min(ttl, SyncRes::s_packetcachenegativettl);
357✔
944
  }
357✔
945
  else if ((hdr.rcode != RCode::NoError && hdr.rcode != RCode::NXDomain) || (hdr.ancount == 0 && hdr.nscount == 0)) {
2,506!
946
    ttl = min(ttl, SyncRes::s_packetcacheservfailttl);
84✔
947
  }
84✔
948
  else {
2,422✔
949
    ttl = std::min(ttl, SyncRes::s_packetcachettl);
2,422✔
950
  }
2,422✔
951
  return ttl;
2,863✔
952
}
2,863✔
953

954
static void addPolicyTagsToPBMessageIfNeeded(DNSComboWriter& comboWriter, pdns::ProtoZero::RecMessage& pbMessage)
955
{
28✔
956
  /* we do _not_ want to store policy tags set by the gettag hook into the packet cache,
957
     since the call to gettag for subsequent queries could yield the same PC tag but different policy tags */
958
  if (!comboWriter.d_gettagPolicyTags.empty()) {
28✔
959
    for (const auto& tag : comboWriter.d_gettagPolicyTags) {
8✔
960
      comboWriter.d_policyTags.erase(tag);
8✔
961
    }
8✔
962
  }
7✔
963
  if (!comboWriter.d_policyTags.empty()) {
28✔
964
    pbMessage.addPolicyTags(comboWriter.d_policyTags);
3✔
965
  }
3✔
966
}
28✔
967

968
void startDoResolve(void* arg) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
969
{
3,196✔
970
  auto comboWriter = std::unique_ptr<DNSComboWriter>(static_cast<DNSComboWriter*>(arg));
3,196✔
971
  SyncRes resolver(comboWriter->d_now);
3,196✔
972
  try {
3,196✔
973
    if (t_queryring) {
3,196✔
974
      t_queryring->push_back({comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype});
3,193✔
975
    }
3,193✔
976

977
    uint16_t maxanswersize = comboWriter->d_tcp ? 65535 : min(static_cast<uint16_t>(512), g_udpTruncationThreshold);
3,196✔
978
    EDNSOpts edo;
3,196✔
979
    std::vector<pair<uint16_t, string>> ednsOpts;
3,196✔
980
    bool variableAnswer = comboWriter->d_variable;
3,196✔
981
    bool haveEDNS = false;
3,196✔
982
    bool paddingAllowed = false;
3,196✔
983
    bool addPaddingToResponse = false;
3,196✔
984
#ifdef NOD_ENABLED
3,196✔
985
    bool hasUDR = false;
3,196✔
986
    std::shared_ptr<Logr::Logger> nodlogger{nullptr};
3,196✔
987
    if (g_udrEnabled || g_nodEnabled) {
3,196✔
988
      nodlogger = g_slog->withName("nod")->v(1)->withValues("qname", Logging::Loggable(comboWriter->d_mdp.d_qname));
3✔
989
    }
3✔
990
#endif /* NOD_ENABLED */
3,196✔
991
    DNSPacketWriter::optvect_t returnedEdnsOptions; // Here we stuff all the options for the return packet
3,196✔
992
    uint8_t ednsExtRCode = 0;
3,196✔
993
    if (getEDNSOpts(comboWriter->d_mdp, &edo)) {
3,196✔
994
      haveEDNS = true;
1,353✔
995
      if (edo.d_version != 0) {
1,353!
996
        ednsExtRCode = ERCode::BADVERS;
×
997
      }
×
998

999
      if (!comboWriter->d_tcp) {
1,353✔
1000
        /* rfc6891 6.2.3:
1001
           "Values lower than 512 MUST be treated as equal to 512."
1002
        */
1003
        maxanswersize = min(static_cast<uint16_t>(edo.d_packetsize >= 512 ? edo.d_packetsize : 512), g_udpTruncationThreshold);
586✔
1004
      }
586✔
1005
      ednsOpts = edo.d_options;
1,353✔
1006
      maxanswersize -= 11; // EDNS header size
1,353✔
1007

1008
      if (!comboWriter->d_responsePaddingDisabled && g_paddingFrom.match(comboWriter->d_remote)) {
1,353✔
1009
        paddingAllowed = true;
7✔
1010
        if (g_paddingMode == PaddingMode::Always) {
7✔
1011
          addPaddingToResponse = true;
5✔
1012
        }
5✔
1013
      }
7✔
1014

1015
      for (const auto& option : edo.d_options) {
1,353✔
1016
        if (option.first == EDNSOptionCode::ECS && g_useIncomingECS && !comboWriter->d_ecsParsed) {
521✔
1017
          comboWriter->d_ecsFound = EDNSSubnetOpts::getFromString(option.second, &comboWriter->d_ednssubnet);
437✔
1018
        }
437✔
1019
        else if (option.first == EDNSOptionCode::NSID) {
84✔
1020
          const static string mode_server_id = ::arg()["server-id"];
2✔
1021
          if (mode_server_id != "disabled" && !mode_server_id.empty() && maxanswersize > (EDNSOptionCodeSize + EDNSOptionLengthSize + mode_server_id.size())) {
2!
1022
            returnedEdnsOptions.emplace_back(EDNSOptionCode::NSID, mode_server_id);
2✔
1023
            variableAnswer = true; // Can't packetcache an answer with NSID
2✔
1024
            maxanswersize -= EDNSOptionCodeSize + EDNSOptionLengthSize + mode_server_id.size();
2✔
1025
          }
2✔
1026
        }
2✔
1027
        else if (paddingAllowed && !addPaddingToResponse && g_paddingMode == PaddingMode::PaddedQueries && option.first == EDNSOptionCode::PADDING) {
82!
1028
          addPaddingToResponse = true;
1✔
1029
        }
1✔
1030
      }
521✔
1031
    }
1,353✔
1032

1033
    /* the lookup will be done _before_ knowing whether the query actually
1034
       has a padding option, so we need to use the separate tag even when the
1035
       query does not have padding, as long as it is from an allowed source */
1036
    if (paddingAllowed && comboWriter->d_tag == 0) {
3,196✔
1037
      comboWriter->d_tag = g_paddingTag;
2✔
1038
    }
2✔
1039

1040
    /* perhaps there was no EDNS or no ECS but by now we looked */
1041
    comboWriter->d_ecsParsed = true;
3,196✔
1042
    vector<DNSRecord> ret;
3,196✔
1043
    vector<uint8_t> packet;
3,196✔
1044

1045
    auto luaconfsLocal = g_luaconfs.getLocal();
3,196✔
1046
    // Used to tell syncres later on if we should apply NSDNAME and NSIP RPZ triggers for this query
1047
    bool wantsRPZ(true);
3,196✔
1048
    RecursorPacketCache::OptPBData pbDataForCache;
3,196✔
1049
    pdns::ProtoZero::RecMessage pbMessage;
3,196✔
1050
    if (checkProtobufExport(luaconfsLocal)) {
3,196✔
1051
      pbMessage.reserve(128, 128); // It's a bit of a guess...
30✔
1052
      pbMessage.setResponse(comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype, comboWriter->d_mdp.d_qclass);
30✔
1053
      pbMessage.setServerIdentity(SyncRes::s_serverID);
30✔
1054

1055
      // RRSets added below
1056
    }
30✔
1057
    checkOutgoingProtobufExport(luaconfsLocal); // to pick up changed configs
3,196✔
1058
#ifdef HAVE_FSTRM
3,196✔
1059
    checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
3,196✔
1060
    checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
3,196✔
1061
#endif
3,196✔
1062

1063
    DNSPacketWriter packetWriter(packet, comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype, comboWriter->d_mdp.d_qclass, comboWriter->d_mdp.d_header.opcode);
3,196✔
1064

1065
    packetWriter.getHeader()->aa = 0;
3,196✔
1066
    packetWriter.getHeader()->ra = 1;
3,196✔
1067
    packetWriter.getHeader()->qr = 1;
3,196✔
1068
    packetWriter.getHeader()->tc = 0;
3,196✔
1069
    packetWriter.getHeader()->id = comboWriter->d_mdp.d_header.id;
3,196✔
1070
    packetWriter.getHeader()->rd = comboWriter->d_mdp.d_header.rd;
3,196✔
1071
    packetWriter.getHeader()->cd = comboWriter->d_mdp.d_header.cd;
3,196✔
1072

1073
    /* This is the lowest TTL seen in the records of the response,
1074
       so we can't cache it for longer than this value.
1075
       If we have a TTL cap, this value can't be larger than the
1076
       cap no matter what. */
1077
    uint32_t minTTL = comboWriter->d_ttlCap;
3,196✔
1078
    bool seenAuthSOA = false;
3,196✔
1079

1080
    resolver.d_eventTrace = std::move(comboWriter->d_eventTrace);
3,196✔
1081
    resolver.d_otTrace = std::move(comboWriter->d_otTrace);
3,196✔
1082
    resolver.setId(g_multiTasker->getTid());
3,196✔
1083

1084
    bool DNSSECOK = false;
3,196✔
1085
    if (comboWriter->d_luaContext) {
3,196✔
1086
      resolver.setLuaEngine(comboWriter->d_luaContext);
414✔
1087
    }
414✔
1088
    if (g_dnssecmode != DNSSECMode::Off) {
3,196✔
1089
      resolver.setDoDNSSEC(true);
3,173✔
1090

1091
      // Does the requestor want DNSSEC records?
1092
      if ((edo.d_extFlags & EDNSOpts::DNSSECOK) != 0) {
3,173✔
1093
        DNSSECOK = true;
1,169✔
1094
        t_Counters.at(rec::Counter::dnssecQueries)++;
1,169✔
1095
      }
1,169✔
1096
      if (comboWriter->d_mdp.d_header.cd) {
3,173✔
1097
        /* Per rfc6840 section 5.9, "When processing a request with
1098
           the Checking Disabled (CD) bit set, a resolver SHOULD attempt
1099
           to return all response data, even data that has failed DNSSEC
1100
           validation. */
1101
        ++t_Counters.at(rec::Counter::dnssecCheckDisabledQueries);
313✔
1102
      }
313✔
1103
      if (comboWriter->d_mdp.d_header.ad) {
3,173✔
1104
        /* Per rfc6840 section 5.7, "the AD bit in a query as a signal
1105
           indicating that the requester understands and is interested in the
1106
           value of the AD bit in the response.  This allows a requester to
1107
           indicate that it understands the AD bit without also requesting
1108
           DNSSEC data via the DO bit. */
1109
        ++t_Counters.at(rec::Counter::dnssecAuthenticDataQueries);
769✔
1110
      }
769✔
1111
    }
3,173✔
1112
    else {
23✔
1113
      // Ignore the client-set CD flag
1114
      packetWriter.getHeader()->cd = 0;
23✔
1115
    }
23✔
1116
    resolver.setDNSSECValidationRequested(g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode == DNSSECMode::ValidateForLog || ((comboWriter->d_mdp.d_header.ad || DNSSECOK) && g_dnssecmode == DNSSECMode::Process));
3,196!
1117

1118
    resolver.setInitialRequestId(comboWriter->d_uuid);
3,196✔
1119
    resolver.setOutgoingProtobufServers(t_outgoingProtobufServers.servers);
3,196✔
1120
#ifdef HAVE_FSTRM
3,196✔
1121
    resolver.setFrameStreamServers(t_frameStreamServersInfo.servers);
3,196✔
1122
#endif
3,196✔
1123

1124
    bool useMapped = true;
3,196✔
1125
    // If proxy by table is active and had a match, we only want to use the mapped address if it also has a domain match
1126
    // (if a domain suffix match table is present in the config)
1127
    if (t_proxyMapping && comboWriter->d_source != comboWriter->d_mappedSource) {
3,196!
1128
      if (const auto* iter = t_proxyMapping->lookup(comboWriter->d_source)) {
9!
1129
        if (iter->second.suffixMatchNode) {
9✔
1130
          if (!iter->second.suffixMatchNode->check(comboWriter->d_mdp.d_qname)) {
3✔
1131
            // No match in domains, use original source
1132
            useMapped = false;
2✔
1133
          }
2✔
1134
          else {
1✔
1135
            ++iter->second.stats.suffixMatches;
1✔
1136
          }
1✔
1137
        }
3✔
1138
        // No suffix match node defined, use mapped address
1139
      }
9✔
1140
      // lookup failing cannot happen as dc->d_source != dc->d_mappedSource
1141
    }
9✔
1142
    resolver.setQuerySource(useMapped ? comboWriter->d_mappedSource : comboWriter->d_source, g_useIncomingECS && !comboWriter->d_ednssubnet.getSource().empty() ? boost::optional<const EDNSSubnetOpts&>(comboWriter->d_ednssubnet) : boost::none);
3,196✔
1143

1144
    resolver.setQueryReceivedOverTCP(comboWriter->d_tcp);
3,196✔
1145

1146
    bool tracedQuery = false; // we could consider letting Lua know about this too
3,196✔
1147
    bool shouldNotValidate = false;
3,196✔
1148

1149
    /* preresolve expects res (dq.rcode) to be set to RCode::NoError by default */
1150
    int res = RCode::NoError;
3,196✔
1151

1152
    DNSFilterEngine::Policy appliedPolicy;
3,196✔
1153
    RecursorLua4::DNSQuestion dnsQuestion(comboWriter->d_remote, comboWriter->d_local, comboWriter->d_source, comboWriter->d_destination, comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype, comboWriter->d_tcp, variableAnswer, wantsRPZ, comboWriter->d_logResponse, addPaddingToResponse, (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) ? comboWriter->d_kernelTimestamp : comboWriter->d_now);
3,196!
1154
    dnsQuestion.ednsFlags = &edo.d_extFlags;
3,196✔
1155
    dnsQuestion.ednsOptions = &ednsOpts;
3,196✔
1156
    dnsQuestion.tag = comboWriter->d_tag;
3,196✔
1157
    dnsQuestion.discardedPolicies = &resolver.d_discardedPolicies;
3,196✔
1158
    dnsQuestion.policyTags = &comboWriter->d_policyTags;
3,196✔
1159
    dnsQuestion.appliedPolicy = &appliedPolicy;
3,196✔
1160
    dnsQuestion.currentRecords = &ret;
3,196✔
1161
    dnsQuestion.dh = &comboWriter->d_mdp.d_header;
3,196✔
1162
    dnsQuestion.data = comboWriter->d_data;
3,196✔
1163
    dnsQuestion.requestorId = comboWriter->d_requestorId;
3,196✔
1164
    dnsQuestion.deviceId = comboWriter->d_deviceId;
3,196✔
1165
    dnsQuestion.deviceName = comboWriter->d_deviceName;
3,196✔
1166
    dnsQuestion.proxyProtocolValues = &comboWriter->d_proxyProtocolValues;
3,196✔
1167
    dnsQuestion.extendedErrorCode = &comboWriter->d_extendedErrorCode;
3,196✔
1168
    dnsQuestion.extendedErrorExtra = &comboWriter->d_extendedErrorExtra;
3,196✔
1169
    dnsQuestion.meta = std::move(comboWriter->d_meta);
3,196✔
1170
    dnsQuestion.fromAuthIP = &resolver.d_fromAuthIP;
3,196✔
1171

1172
    resolver.d_slog = resolver.d_slog->withValues("qname", Logging::Loggable(comboWriter->d_mdp.d_qname),
3,196✔
1173
                                                  "qtype", Logging::Loggable(QType(comboWriter->d_mdp.d_qtype)),
3,196✔
1174
                                                  "remote", Logging::Loggable(comboWriter->getRemote()),
3,196✔
1175
                                                  "proto", Logging::Loggable(comboWriter->d_tcp ? "tcp" : "udp"),
3,196✔
1176
                                                  "ecs", Logging::Loggable(comboWriter->d_ednssubnet.getSource().empty() ? "" : comboWriter->d_ednssubnet.getSource().toString()),
3,196✔
1177
                                                  "mtid", Logging::Loggable(g_multiTasker->getTid()));
3,196✔
1178
    RunningResolveGuard tcpGuard(comboWriter);
3,196✔
1179

1180
    if (ednsExtRCode != 0 || comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Notify)) {
3,196✔
1181
      goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
24✔
1182
    }
24✔
1183

1184
    if (comboWriter->d_mdp.d_qtype == QType::ANY && !comboWriter->d_tcp && g_anyToTcp) {
3,172!
1185
      packetWriter.getHeader()->tc = 1;
×
1186
      res = 0;
×
1187
      variableAnswer = true;
×
1188
      goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
×
1189
    }
×
1190

1191
    if (t_traceRegex && t_traceRegex->match(comboWriter->d_mdp.d_qname.toString())) {
3,172!
1192
      resolver.setLogMode(SyncRes::Store);
×
1193
      tracedQuery = true;
×
1194
    }
×
1195

1196
    if (!g_quiet || tracedQuery) {
3,172✔
1197
      resolver.d_slog->info(Logr::Info, "Question");
939✔
1198
    }
939✔
1199

1200
    if (!comboWriter->d_mdp.d_header.rd) {
3,172✔
1201
      if (g_allowNoRD) {
3✔
1202
        resolver.setCacheOnly();
2✔
1203
      }
2✔
1204
      else {
1✔
1205
        ret.clear();
1✔
1206
        res = RCode::Refused;
1✔
1207
        goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
1✔
1208
      }
1✔
1209
    }
3✔
1210

1211
    if (comboWriter->d_luaContext) {
3,171✔
1212
      comboWriter->d_luaContext->prerpz(dnsQuestion, res, resolver.d_eventTrace);
403✔
1213
    }
403✔
1214

1215
    // Check if the client has a policy attached to it
1216
    if (wantsRPZ && !appliedPolicy.wasHit()) {
3,171✔
1217

1218
      if (luaconfsLocal->dfe.getClientPolicy(comboWriter->d_source, resolver.d_discardedPolicies, appliedPolicy)) {
3,170!
1219
        mergePolicyTags(comboWriter->d_policyTags, appliedPolicy.getTags());
×
1220
      }
×
1221
    }
3,170✔
1222

1223
    /* If we already have an answer generated from gettag_ffi, let's see if the filtering policies
1224
       should be applied to it */
1225
    if (comboWriter->d_rcode != boost::none) {
3,171✔
1226

1227
      bool policyOverride = false;
5✔
1228
      /* Unless we already matched on the client IP, time to check the qname.
1229
         We normally check it in beginResolve() but it will be bypassed since we already have an answer */
1230
      if (wantsRPZ && appliedPolicy.policyOverridesGettag()) {
5!
1231
        if (appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
5!
1232
          // Client IP already matched
1233
        }
×
1234
        else {
5✔
1235
          // no match on the client IP, check the qname
1236
          if (luaconfsLocal->dfe.getQueryPolicy(comboWriter->d_mdp.d_qname, resolver.d_discardedPolicies, appliedPolicy)) {
5✔
1237
            // got a match
1238
            mergePolicyTags(comboWriter->d_policyTags, appliedPolicy.getTags());
2✔
1239
          }
2✔
1240
        }
5✔
1241

1242
        if (appliedPolicy.wasHit()) {
5✔
1243
          policyOverride = true;
2✔
1244
        }
2✔
1245
      }
5✔
1246

1247
      if (!policyOverride) {
5✔
1248
        /* No RPZ or gettag overrides it anyway */
1249
        ret = std::move(comboWriter->d_records);
3✔
1250
        res = *comboWriter->d_rcode;
3✔
1251
        if (res == RCode::NoError && comboWriter->d_followCNAMERecords) {
3!
1252
          res = followCNAMERecords(ret, QType(comboWriter->d_mdp.d_qtype), res);
×
1253
        }
×
1254
        goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
3✔
1255
      }
3✔
1256
    }
5✔
1257

1258
    // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve
1259
    if (!comboWriter->d_luaContext || !comboWriter->d_luaContext->preresolve(dnsQuestion, res, resolver.d_eventTrace)) {
3,168✔
1260

1261
      if (!g_dns64PrefixReverse.empty() && dnsQuestion.qtype == QType::PTR && dnsQuestion.qname.isPartOf(g_dns64PrefixReverse)) {
3,107✔
1262
        res = getFakePTRRecords(dnsQuestion.qname, ret);
2✔
1263
        goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
2✔
1264
      }
2✔
1265

1266
      resolver.setWantsRPZ(wantsRPZ);
3,105✔
1267

1268
      if (wantsRPZ && appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
3,106✔
1269

1270
        if (comboWriter->d_luaContext && comboWriter->d_luaContext->policyHitEventFilter(comboWriter->d_source, comboWriter->d_mdp.d_qname, QType(comboWriter->d_mdp.d_qtype), comboWriter->d_tcp, appliedPolicy, comboWriter->d_policyTags, resolver.d_discardedPolicies)) {
2!
1271
          /* reset to no match */
1272
          appliedPolicy = DNSFilterEngine::Policy();
×
1273
        }
×
1274
        else {
2✔
1275
          auto policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
2✔
1276
          if (policyResult == PolicyResult::HaveAnswer) {
2!
1277
            if (g_dns64Prefix && dnsQuestion.qtype == QType::AAAA && dns64Candidate(comboWriter->d_mdp.d_qtype, res, ret)) {
2!
1278
              res = getFakeAAAARecords(dnsQuestion.qname, *g_dns64Prefix, ret);
2✔
1279
              shouldNotValidate = true;
2✔
1280
            }
2✔
1281
            goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
2✔
1282
          }
2✔
1283
          else if (policyResult == PolicyResult::Drop) {
×
1284
            return;
×
1285
          }
×
1286
        }
2✔
1287
      }
2✔
1288

1289
      // Query did not get handled for Client IP or QNAME Policy reasons, now actually go out to find an answer
1290
      try {
3,103✔
1291
        resolver.d_appliedPolicy = appliedPolicy;
3,103✔
1292
        resolver.d_policyTags = std::move(comboWriter->d_policyTags);
3,103✔
1293

1294
        if (!comboWriter->d_routingTag.empty()) {
3,103✔
1295
          resolver.d_routingTag = comboWriter->d_routingTag;
16✔
1296
        }
16✔
1297

1298
        ret.clear(); // policy might have filled it with custom records but we decided not to use them
3,103✔
1299
        res = resolver.beginResolve(comboWriter->d_mdp.d_qname, QType(comboWriter->d_mdp.d_qtype), comboWriter->d_mdp.d_qclass, ret);
3,103✔
1300
        shouldNotValidate = resolver.wasOutOfBand();
3,103✔
1301
      }
3,103✔
1302
      catch (const ImmediateQueryDropException& e) {
3,103✔
1303
        // XXX We need to export a protobuf message (and do a NOD lookup) if requested!
1304
        t_Counters.at(rec::Counter::policyDrops)++;
6✔
1305
        resolver.d_slog->info(Logr::Debug, "Dropping query because of a filtering policy");
6✔
1306
        return;
6✔
1307
      }
6✔
1308
      catch (const ImmediateServFailException& e) {
3,103✔
1309
        if (g_logCommonErrors) {
4!
1310
          resolver.d_slog->error(Logr::Notice, e.reason, "Sending SERVFAIL during resolve");
4✔
1311
        }
4✔
1312
        res = RCode::ServFail;
4✔
1313
      }
4✔
1314
      catch (const pdns::validation::TooManySEC3IterationsException& e) {
3,103✔
1315
        if (g_logCommonErrors) {
×
1316
          resolver.d_slog->error(Logr::Notice, e.what(), "Sending SERVFAIL during resolve", "dnsseclimithit", Logging::Loggable(true));
×
1317
        }
×
1318
        res = RCode::ServFail;
×
1319
      }
×
1320
      catch (const SendTruncatedAnswerException& e) {
3,103✔
1321
        ret.clear();
3✔
1322
        resolver.d_appliedPolicy.addSOAtoRPZResult(ret);
3✔
1323
        res = RCode::NoError;
3✔
1324
        packetWriter.getHeader()->tc = 1;
3✔
1325
      }
3✔
1326
      catch (const PolicyHitException& e) {
3,103✔
1327
        res = -2;
2✔
1328
      }
2✔
1329
      dnsQuestion.validationState = resolver.getValidationState();
3,099✔
1330
      appliedPolicy = resolver.d_appliedPolicy;
3,099✔
1331
      comboWriter->d_policyTags = std::move(resolver.d_policyTags);
3,099✔
1332

1333
      if (appliedPolicy.d_type != DNSFilterEngine::PolicyType::None && appliedPolicy.d_zoneData && appliedPolicy.d_zoneData->d_extendedErrorCode) {
3,099!
1334
        comboWriter->d_extendedErrorCode = *appliedPolicy.d_zoneData->d_extendedErrorCode;
4✔
1335
        comboWriter->d_extendedErrorExtra = appliedPolicy.d_zoneData->d_extendedErrorExtra;
4✔
1336
      }
4✔
1337

1338
      // During lookup, an NSDNAME or NSIP trigger was hit in RPZ
1339
      if (res == -2) { // XXX This block should be macro'd, it is repeated post-resolve.
3,099✔
1340
        if (appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction) {
2!
1341
          throw PDNSException("NoAction policy returned while a NSDNAME or NSIP trigger was hit");
×
1342
        }
×
1343
        auto policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
2✔
1344
        if (policyResult == PolicyResult::HaveAnswer) {
2!
1345
          goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
2✔
1346
        }
2✔
1347
        else if (policyResult == PolicyResult::Drop) {
×
1348
          return;
×
1349
        }
×
1350
      }
2✔
1351

1352
      bool luaHookHandled = false;
3,097✔
1353
      if (comboWriter->d_luaContext) {
3,097✔
1354
        PolicyResult policyResult = PolicyResult::NoAction;
329✔
1355
        if (SyncRes::answerIsNOData(comboWriter->d_mdp.d_qtype, res, ret)) {
329✔
1356
          if (comboWriter->d_luaContext->nodata(dnsQuestion, res, resolver.d_eventTrace)) {
41✔
1357
            luaHookHandled = true;
12✔
1358
            shouldNotValidate = true;
12✔
1359
            policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
12✔
1360
          }
12✔
1361
        }
41✔
1362
        else if (res == RCode::NXDomain && comboWriter->d_luaContext->nxdomain(dnsQuestion, res, resolver.d_eventTrace)) {
288✔
1363
          luaHookHandled = true;
6✔
1364
          shouldNotValidate = true;
6✔
1365
          policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
6✔
1366
        }
6✔
1367
        if (policyResult == PolicyResult::HaveAnswer) {
329!
1368
          goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
×
1369
        }
×
1370
        else if (policyResult == PolicyResult::Drop) {
329✔
1371
          return;
4✔
1372
        }
4✔
1373
      } // dc->d_luaContext
329✔
1374

1375
      if (!luaHookHandled && g_dns64Prefix && comboWriter->d_mdp.d_qtype == QType::AAAA && (shouldNotValidate || !resolver.isDNSSECValidationRequested() || !vStateIsBogus(dnsQuestion.validationState)) && dns64Candidate(comboWriter->d_mdp.d_qtype, res, ret)) {
3,093!
1376
        res = getFakeAAAARecords(dnsQuestion.qname, *g_dns64Prefix, ret);
10✔
1377
        shouldNotValidate = true;
10✔
1378
      }
10✔
1379

1380
      if (comboWriter->d_luaContext) {
3,093✔
1381
        PolicyResult policyResult = PolicyResult::NoAction;
325✔
1382
        if (comboWriter->d_luaContext->hasPostResolveFFIfunc()) {
325✔
1383
          RecursorLua4::PostResolveFFIHandle handle(dnsQuestion);
7✔
1384
          auto match = resolver.d_eventTrace.add(RecEventTrace::LuaPostResolveFFI);
7✔
1385
          bool prResult = comboWriter->d_luaContext->postresolve_ffi(handle);
7✔
1386
          resolver.d_eventTrace.add(RecEventTrace::LuaPostResolveFFI, prResult, false, match);
7✔
1387
          if (prResult) {
7✔
1388
            shouldNotValidate = true;
6✔
1389
            policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
6✔
1390
          }
6✔
1391
        }
7✔
1392
        else if (comboWriter->d_luaContext->postresolve(dnsQuestion, res, resolver.d_eventTrace)) {
318✔
1393
          shouldNotValidate = true;
4✔
1394
          policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
4✔
1395
        }
4✔
1396
        if (policyResult == PolicyResult::HaveAnswer) {
325✔
1397
          goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
3✔
1398
        }
3✔
1399
        else if (policyResult == PolicyResult::Drop) {
322✔
1400
          return;
1✔
1401
        }
1✔
1402
      } // dc->d_luaContext
325✔
1403
    }
3,093✔
1404
    else if (comboWriter->d_luaContext) {
61✔
1405
      // preresolve returned true
1406
      shouldNotValidate = true;
59✔
1407
      auto policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
59✔
1408
      // haveAnswer case redundant
1409
      if (policyResult == PolicyResult::Drop) {
59✔
1410
        return;
2✔
1411
      }
2✔
1412
    }
59✔
1413

1414
  haveAnswer:;
3,161✔
1415
    if (tracedQuery || res == -1 || res == RCode::ServFail || packetWriter.getHeader()->rcode == static_cast<unsigned>(RCode::ServFail)) {
3,159!
1416
      dumpTrace(resolver.getTrace(), resolver.d_fixednow);
16✔
1417
    }
16✔
1418

1419
    if (res == -1) {
3,159✔
1420
      packetWriter.getHeader()->rcode = RCode::ServFail;
2✔
1421
      // no commit here, because no record
1422
      ++t_Counters.at(rec::Counter::servFails);
2✔
1423
    }
2✔
1424
    else {
3,157✔
1425
      packetWriter.getHeader()->rcode = res;
3,157✔
1426

1427
      // Does the validation mode or query demand validation?
1428
      if (!shouldNotValidate && resolver.isDNSSECValidationRequested()) {
3,157✔
1429
        try {
2,549✔
1430
          auto state = resolver.getValidationState();
2,549✔
1431

1432
          string x_marker;
2,549✔
1433
          std::shared_ptr<Logr::Logger> log;
2,549✔
1434
          if (resolver.doLog() || vStateIsBogus(state)) {
2,549✔
1435
            // Only create logging object if needed below, beware if you change the logging logic!
1436
            log = resolver.d_slog->withValues("vstate", Logging::Loggable(state));
317✔
1437
            if (resolver.getDNSSECLimitHit()) {
317!
1438
              log = log->withValues("dnsseclimithit", Logging::Loggable(true));
×
1439
            }
×
1440
            auto xdnssec = g_xdnssec.getLocal();
317✔
1441
            if (xdnssec->check(comboWriter->d_mdp.d_qname)) {
317!
1442
              log = log->withValues("in-x-dnssec-names", Logging::Loggable(1));
×
1443
              x_marker = " [in x-dnssec-names]";
×
1444
            }
×
1445
          }
317✔
1446
          if (state == vState::Secure) {
2,549✔
1447
            if (resolver.doLog()) {
761✔
1448
              log->info(Logr::Info, "Validates Correctly");
150✔
1449
            }
150✔
1450

1451
            // Is the query source interested in the value of the ad-bit?
1452
            if (comboWriter->d_mdp.d_header.ad || DNSSECOK) {
761✔
1453
              packetWriter.getHeader()->ad = 1;
726✔
1454
            }
726✔
1455
          }
761✔
1456
          else if (state == vState::Insecure) {
1,788✔
1457
            if (resolver.doLog()) {
1,671✔
1458
              log->info(Logr::Info, "Validates as Insecure");
50✔
1459
            }
50✔
1460

1461
            packetWriter.getHeader()->ad = 0;
1,671✔
1462
          }
1,671✔
1463
          else if (vStateIsBogus(state)) {
117✔
1464
            if (t_bogusremotes) {
55!
1465
              t_bogusremotes->push_back(comboWriter->d_source);
55✔
1466
            }
55✔
1467
            if (t_bogusqueryring) {
55!
1468
              t_bogusqueryring->push_back({comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype});
55✔
1469
            }
55✔
1470
            if (g_dnssecLogBogus || resolver.doLog() || g_dnssecmode == DNSSECMode::ValidateForLog) {
55!
1471
              log->info(Logr::Notice, "Validates as Bogus");
55✔
1472
            }
55✔
1473

1474
            // Does the query or validation mode sending out a SERVFAIL on validation errors?
1475
            if (!packetWriter.getHeader()->cd && (g_dnssecmode == DNSSECMode::ValidateAll || comboWriter->d_mdp.d_header.ad || DNSSECOK)) {
55!
1476
              if (resolver.doLog()) {
46!
1477
                log->info(Logr::Notice, "Sending out SERVFAIL because recursor or query demands it for Bogus results");
46✔
1478
              }
46✔
1479

1480
              packetWriter.getHeader()->rcode = RCode::ServFail;
46✔
1481
              goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
46✔
1482
            }
46✔
1483
            else {
9✔
1484
              if (resolver.doLog()) {
9!
1485
                log->info(Logr::Notice, "Sending out SERVFAIL because recursor or query demands it for Bogus results");
9✔
1486
              }
9✔
1487
            }
9✔
1488
          }
55✔
1489
        }
2,549✔
1490
        catch (const ImmediateServFailException& e) {
2,549✔
1491
          if (g_logCommonErrors) {
×
1492
            resolver.d_slog->error(Logr::Notice, e.reason, "Sending SERVFAIL during validation", "exception", Logging::Loggable("ImmediateServFailException"));
×
1493
          }
×
1494
          goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
×
1495
        }
×
1496
        catch (const pdns::validation::TooManySEC3IterationsException& e) {
2,549✔
1497
          if (g_logCommonErrors || (g_dnssecLogBogus && resolver.getDNSSECLimitHit())) {
×
1498
            resolver.d_slog->error(Logr::Notice, e.what(), "Sending SERVFAIL during validation", "exception", Logging::Loggable("TooManySEC3IterationsException"), "dnsseclimithit", Logging::Loggable(resolver.getDNSSECLimitHit()));
×
1499
          }
×
1500
          goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
×
1501
        }
×
1502
      }
2,549✔
1503

1504
      if (!ret.empty()) {
3,111✔
1505
#ifdef notyet
1506
        // As dedupping is relatively expensive do not dedup in general. We do have a few cases
1507
        // where we call dedup explicitly, e.g. when doing NAT64 or when adding NSEC records in
1508
        // doCNAMECacheCheck
1509
        pdns::dedupRecords(ret);
1510
#endif
1511
        pdns::orderAndShuffle(ret, false);
3,076✔
1512
        if (auto listToSort = luaconfsLocal->sortlist.getOrderCmp(comboWriter->d_source)) {
3,076✔
1513
          stable_sort(ret.begin(), ret.end(), *listToSort);
1✔
1514
          variableAnswer = true;
1✔
1515
        }
1✔
1516
      }
3,076✔
1517

1518
      bool needCommit = false;
3,111✔
1519
      for (const auto& record : ret) {
10,038✔
1520
        if (!DNSSECOK && (record.d_type == QType::NSEC3 || ((record.d_type == QType::RRSIG || record.d_type == QType::NSEC) && ((comboWriter->d_mdp.d_qtype != record.d_type && comboWriter->d_mdp.d_qtype != QType::ANY) || (record.d_place != DNSResourceRecord::ANSWER && record.d_place != DNSResourceRecord::ADDITIONAL))))) {
10,038!
1521
          continue;
118✔
1522
        }
118✔
1523

1524
        if (!addRecordToPacket(packetWriter, record, minTTL, comboWriter->d_ttlCap, maxanswersize, seenAuthSOA)) {
9,920✔
1525
          needCommit = false;
4✔
1526
          break;
4✔
1527
        }
4✔
1528
        needCommit = true;
9,916✔
1529

1530
        bool udr = false;
9,916✔
1531
#ifdef NOD_ENABLED
9,916✔
1532
        if (g_udrEnabled) {
9,916✔
1533
          udr = udrCheckUniqueDNSRecord(nodlogger, comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype, record);
3✔
1534
          if (!hasUDR && udr) {
3!
1535
            hasUDR = true;
3✔
1536
          }
3✔
1537
        }
3✔
1538
#endif /* NOD ENABLED */
9,916✔
1539

1540
        if (t_protobufServers.servers) {
9,916✔
1541
          // Max size is 64k, but we're conservative here, as other fields are added after the answers have been added
1542
          // If a single answer causes a too big protobuf message, it will be dropped by queueData()
1543
          // But note addRR has code to prevent that
1544
          if (pbMessage.size() < std::numeric_limits<uint16_t>::max() / 2) {
36!
1545
            pbMessage.addRR(record, luaconfsLocal->protobufExportConfig.exportTypes, udr);
36✔
1546
          }
36✔
1547
        }
36✔
1548
      }
9,916✔
1549
      if (needCommit) {
3,111✔
1550
        packetWriter.commit();
3,072✔
1551
      }
3,072✔
1552
#ifdef NOD_ENABLED
3,111✔
1553
#ifdef HAVE_FSTRM
3,111✔
1554
      if (hasUDR) {
3,111✔
1555
        if (isEnabledForUDRs(t_nodFrameStreamServersInfo.servers)) {
3✔
1556
          struct timespec timeSpec{};
2✔
1557
          std::string str;
2✔
1558
          if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
2!
1559
            TIMEVAL_TO_TIMESPEC(&comboWriter->d_kernelTimestamp, &timeSpec); // NOLINT
×
1560
          }
×
1561
          else {
2✔
1562
            TIMEVAL_TO_TIMESPEC(&comboWriter->d_now, &timeSpec); // NOLINT
2✔
1563
          }
2✔
1564
          DnstapMessage message(std::move(str), DnstapMessage::MessageType::resolver_response, SyncRes::s_serverID, &comboWriter->d_source, &comboWriter->d_destination, comboWriter->d_tcp ? DnstapMessage::ProtocolType::DoTCP : DnstapMessage::ProtocolType::DoUDP, reinterpret_cast<const char*>(&*packet.begin()), packet.size(), &timeSpec, nullptr, comboWriter->d_mdp.d_qname); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2!
1565
          str = message.getBuffer();
2✔
1566
          for (auto& logger : *(t_nodFrameStreamServersInfo.servers)) {
2✔
1567
            if (logger->logUDRs()) {
2!
1568
              remoteLoggerQueueData(*logger, str);
2✔
1569
            }
2✔
1570
          }
2✔
1571
        }
2✔
1572
      }
3✔
1573
#endif // HAVE_FSTRM
3,111✔
1574
#endif // NOD_ENABLED
3,111✔
1575
    }
3,111✔
1576
  sendit:;
3,183✔
1577

1578
    if (g_useIncomingECS && comboWriter->d_ecsFound && !resolver.wasVariable() && !variableAnswer) {
3,183!
1579
      EDNSSubnetOpts ednsOptions;
387✔
1580
      ednsOptions.setSource(comboWriter->d_ednssubnet.getSource());
387✔
1581
      ComboAddress sourceAddr;
387✔
1582
      sourceAddr.reset();
387✔
1583
      sourceAddr.sin4.sin_family = ednsOptions.getFamily();
387✔
1584
      ednsOptions.setScopePrefixLength(0);
387✔
1585
      auto ecsPayload = ednsOptions.makeOptString();
387✔
1586

1587
      // if we don't have enough space available let's just not set that scope of zero,
1588
      // it will prevent some caching, mostly from dnsdist, but that's fine
1589
      if (packetWriter.size() < maxanswersize && (maxanswersize - packetWriter.size()) >= (EDNSOptionCodeSize + EDNSOptionLengthSize + ecsPayload.size())) {
387!
1590

1591
        maxanswersize -= EDNSOptionCodeSize + EDNSOptionLengthSize + ecsPayload.size();
386✔
1592

1593
        returnedEdnsOptions.emplace_back(EDNSOptionCode::ECS, std::move(ecsPayload));
386✔
1594
      }
386✔
1595
    }
387✔
1596

1597
    if (haveEDNS && addPaddingToResponse) {
3,183✔
1598
      size_t currentSize = packetWriter.getSizeWithOpts(returnedEdnsOptions);
5✔
1599
      /* we don't use maxawnswersize because it accounts for some EDNS options, but
1600
         not all of them (for example ECS) */
1601
      size_t maxSize = min(static_cast<uint16_t>(edo.d_packetsize >= 512 ? edo.d_packetsize : 512), g_udpTruncationThreshold);
5!
1602

1603
      if (currentSize < (maxSize - 4)) {
5!
1604
        size_t remaining = maxSize - (currentSize + 4);
5✔
1605
        /* from rfc8647, "4.1.  Recommended Strategy: Block-Length Padding":
1606
           If a server receives a query that includes the EDNS(0) "Padding"
1607
           option, it MUST pad the corresponding response (see Section 4 of
1608
           RFC 7830) and SHOULD pad the corresponding response to a
1609
           multiple of 468 octets (see below).
1610
        */
1611
        const size_t blockSize = 468;
5✔
1612
        size_t modulo = (currentSize + 4) % blockSize;
5✔
1613
        size_t padSize = 0;
5✔
1614
        if (modulo > 0) {
5!
1615
          padSize = std::min(blockSize - modulo, remaining);
5✔
1616
        }
5✔
1617
        returnedEdnsOptions.emplace_back(EDNSOptionCode::PADDING, makeEDNSPaddingOptString(padSize));
5✔
1618
      }
5✔
1619
    }
5✔
1620

1621
    std::optional<EDNSExtendedError> eee;
3,183✔
1622
    if (haveEDNS) {
3,183✔
1623
      auto state = resolver.getValidationState();
1,341✔
1624
      if (comboWriter->d_extendedErrorCode || resolver.d_extendedError || (SyncRes::s_addExtendedResolutionDNSErrors && vStateIsBogus(state))) {
1,341✔
1625
        EDNSExtendedError::code code = EDNSExtendedError::code::Other;
94✔
1626
        std::string extra;
94✔
1627

1628
        if (comboWriter->d_extendedErrorCode) {
94✔
1629
          code = static_cast<EDNSExtendedError::code>(*comboWriter->d_extendedErrorCode);
10✔
1630
          extra = std::move(comboWriter->d_extendedErrorExtra);
10✔
1631
        }
10✔
1632
        else if (resolver.d_extendedError) {
84✔
1633
          code = static_cast<EDNSExtendedError::code>(resolver.d_extendedError->infoCode);
40✔
1634
          extra = std::move(resolver.d_extendedError->extraText);
40✔
1635
        }
40✔
1636
        else {
44✔
1637
          switch (state) {
44✔
1638
          case vState::BogusNoValidDNSKEY:
31✔
1639
            code = EDNSExtendedError::code::DNSKEYMissing;
31✔
1640
            break;
31✔
1641
          case vState::BogusInvalidDenial:
×
1642
            code = EDNSExtendedError::code::NSECMissing;
×
1643
            break;
×
1644
          case vState::BogusUnableToGetDSs:
×
1645
            code = EDNSExtendedError::code::DNSSECBogus;
×
1646
            break;
×
1647
          case vState::BogusUnableToGetDNSKEYs:
×
1648
            code = EDNSExtendedError::code::DNSKEYMissing;
×
1649
            break;
×
1650
          case vState::BogusSelfSignedDS:
×
1651
            code = EDNSExtendedError::code::DNSSECBogus;
×
1652
            break;
×
1653
          case vState::BogusNoRRSIG:
2✔
1654
            code = EDNSExtendedError::code::RRSIGsMissing;
2✔
1655
            break;
2✔
1656
          case vState::BogusNoValidRRSIG:
4✔
1657
            code = EDNSExtendedError::code::DNSSECBogus;
4✔
1658
            break;
4✔
1659
          case vState::BogusMissingNegativeIndication:
1✔
1660
            code = EDNSExtendedError::code::NSECMissing;
1✔
1661
            break;
1✔
1662
          case vState::BogusSignatureNotYetValid:
2✔
1663
            code = EDNSExtendedError::code::SignatureNotYetValid;
2✔
1664
            break;
2✔
1665
          case vState::BogusSignatureExpired:
4✔
1666
            code = EDNSExtendedError::code::SignatureExpired;
4✔
1667
            break;
4✔
1668
          case vState::BogusUnsupportedDNSKEYAlgo:
×
1669
            code = EDNSExtendedError::code::UnsupportedDNSKEYAlgorithm;
×
1670
            break;
×
1671
          case vState::BogusUnsupportedDSDigestType:
×
1672
            code = EDNSExtendedError::code::UnsupportedDSDigestType;
×
1673
            break;
×
1674
          case vState::BogusNoZoneKeyBitSet:
×
1675
            code = EDNSExtendedError::code::NoZoneKeyBitSet;
×
1676
            break;
×
1677
          case vState::BogusRevokedDNSKEY:
×
1678
          case vState::BogusInvalidDNSKEYProtocol:
×
1679
            code = EDNSExtendedError::code::DNSSECBogus;
×
1680
            break;
×
1681
          default:
×
1682
            throw std::runtime_error("Bogus validation state not handled: " + vStateToString(state));
×
1683
          }
44✔
1684
        }
44✔
1685
        eee.emplace(EDNSExtendedError{static_cast<uint16_t>(code), std::move(extra)});
94✔
1686

1687
        if (packetWriter.size() < maxanswersize && (maxanswersize - packetWriter.size()) >= (EDNSOptionCodeSize + EDNSOptionLengthSize + sizeof(EDNSExtendedError::code) + eee->extraText.size())) {
94!
1688
          returnedEdnsOptions.emplace_back(EDNSOptionCode::EXTENDEDERROR, makeEDNSExtendedErrorOptString(*eee));
93✔
1689
        }
93✔
1690
      }
94✔
1691

1692
      /* we try to add the EDNS OPT RR even for truncated answers,
1693
         as rfc6891 states:
1694
         "The minimal response MUST be the DNS header, question section, and an
1695
         OPT record.  This MUST also occur when a truncated response (using
1696
         the DNS header's TC bit) is returned."
1697
      */
1698
      packetWriter.addOpt(512, ednsExtRCode, DNSSECOK ? EDNSOpts::DNSSECOK : 0, returnedEdnsOptions);
1,341✔
1699
      packetWriter.commit();
1,341✔
1700
    }
1,341✔
1701

1702
    t_Counters.at(rec::ResponseStats::responseStats).submitResponse(comboWriter->d_mdp.d_qtype, packet.size(), packetWriter.getHeader()->rcode);
3,183✔
1703
    updateResponseStats(res, comboWriter->d_source, packet.size(), &comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype);
3,183✔
1704
#ifdef NOD_ENABLED
3,183✔
1705
    bool nod = false;
3,183✔
1706
    if (g_nodEnabled) {
3,183✔
1707
      if (nodCheckNewDomain(nodlogger, comboWriter->d_mdp.d_qname)) {
3!
1708
        nod = true;
3✔
1709
#ifdef HAVE_FSTRM
3✔
1710
        if (isEnabledForNODs(t_nodFrameStreamServersInfo.servers)) {
3✔
1711
          struct timespec timeSpec{};
2✔
1712
          std::string str;
2✔
1713
          if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
2!
1714
            TIMEVAL_TO_TIMESPEC(&comboWriter->d_kernelTimestamp, &timeSpec); // NOLINT
×
1715
          }
×
1716
          else {
2✔
1717
            TIMEVAL_TO_TIMESPEC(&comboWriter->d_now, &timeSpec); // NOLINT
2✔
1718
          }
2✔
1719
          DnstapMessage message(std::move(str), DnstapMessage::MessageType::client_query, SyncRes::s_serverID, &comboWriter->d_source, &comboWriter->d_destination, comboWriter->d_tcp ? DnstapMessage::ProtocolType::DoTCP : DnstapMessage::ProtocolType::DoUDP, nullptr, 0, &timeSpec, nullptr, comboWriter->d_mdp.d_qname);
2!
1720
          str = message.getBuffer();
2✔
1721

1722
          for (auto& logger : *(t_nodFrameStreamServersInfo.servers)) {
2✔
1723
            if (logger->logNODs()) {
2!
1724
              remoteLoggerQueueData(*logger, str);
2✔
1725
            }
2✔
1726
          }
2✔
1727
        }
2✔
1728
#endif // HAVE_FSTRM
3✔
1729
      }
3✔
1730
    }
3✔
1731
#endif /* NOD_ENABLED */
3,183✔
1732

1733
    if (variableAnswer || resolver.wasVariable()) {
3,183✔
1734
      t_Counters.at(rec::Counter::variableResponses)++;
178✔
1735
    }
178✔
1736

1737
    if (t_protobufServers.servers && !(luaconfsLocal->protobufExportConfig.taggedOnly && appliedPolicy.getName().empty() && comboWriter->d_policyTags.empty())) {
3,183!
1738
      // Start constructing embedded DNSResponse object
1739
      pbMessage.setResponseCode(packetWriter.getHeader()->rcode);
28✔
1740
      if (!appliedPolicy.getName().empty()) {
28✔
1741
        pbMessage.setAppliedPolicy(appliedPolicy.getName());
3✔
1742
        pbMessage.setAppliedPolicyType(appliedPolicy.d_type);
3✔
1743
        pbMessage.setAppliedPolicyTrigger(appliedPolicy.getTrigger());
3✔
1744
        pbMessage.setAppliedPolicyHit(appliedPolicy.getHit());
3✔
1745
        pbMessage.setAppliedPolicyKind(appliedPolicy.d_kind);
3✔
1746
      }
3✔
1747
      pbMessage.setInBytes(packet.size());
28✔
1748
      pbMessage.setValidationState(resolver.getValidationState());
28✔
1749
      // See if we want to store the policyTags into the PC
1750
      addPolicyTagsToPBMessageIfNeeded(*comboWriter, pbMessage);
28✔
1751
      if (eee) {
28✔
1752
        pbMessage.setEDE(eee->infoCode);
2✔
1753
        pbMessage.setEDEText(eee->extraText);
2✔
1754
      }
2✔
1755

1756
      // Take s snap of the current protobuf buffer state to store in the PC
1757
      pbDataForCache = boost::make_optional(RecursorPacketCache::PBData{
28✔
1758
        pbMessage.getMessageBuf(),
28✔
1759
        pbMessage.getResponseBuf(),
28✔
1760
        !appliedPolicy.getName().empty() || !comboWriter->d_policyTags.empty()});
28✔
1761
#ifdef NOD_ENABLED
28✔
1762
      // if (g_udrEnabled) ??
1763
      pbMessage.clearUDR(pbDataForCache->d_response);
28✔
1764
#endif
28✔
1765
    }
28✔
1766

1767
    const bool intoPC = g_packetCache && !variableAnswer && !resolver.wasVariable();
3,183✔
1768
    if (intoPC) {
3,183✔
1769
      minTTL = capPacketCacheTTL(*packetWriter.getHeader(), minTTL, seenAuthSOA);
2,863✔
1770
      g_packetCache->insertResponsePacket(comboWriter->d_tag, comboWriter->d_qhash, std::move(comboWriter->d_query), comboWriter->d_mdp.d_qname,
2,863✔
1771
                                          comboWriter->d_mdp.d_qtype, comboWriter->d_mdp.d_qclass,
2,863✔
1772
                                          string(reinterpret_cast<const char*>(&*packet.begin()), packet.size()), // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2,863✔
1773
                                          g_now.tv_sec,
2,863✔
1774
                                          minTTL,
2,863✔
1775
                                          dnsQuestion.validationState,
2,863✔
1776
                                          std::move(pbDataForCache), comboWriter->d_tcp);
2,863✔
1777
    }
2,863✔
1778

1779
    if (g_regressionTestMode) {
3,183✔
1780
      t_Counters.updateSnap(g_regressionTestMode);
623✔
1781
    }
623✔
1782

1783
    if (!comboWriter->d_tcp) {
3,183✔
1784
      struct msghdr msgh{};
2,398✔
1785
      struct iovec iov{};
2,398✔
1786
      cmsgbuf_aligned cbuf{};
2,398✔
1787
      fillMSGHdr(&msgh, &iov, &cbuf, 0, reinterpret_cast<char*>(&*packet.begin()), packet.size(), &comboWriter->d_remote); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2,398✔
1788
      msgh.msg_control = nullptr;
2,398✔
1789

1790
      if (g_fromtosockets.count(comboWriter->d_socket) > 0) {
2,398✔
1791
        addCMsgSrcAddr(&msgh, &cbuf, &comboWriter->d_local, 0);
1✔
1792
      }
1✔
1793
      int sendErr = sendOnNBSocket(comboWriter->d_socket, &msgh);
2,398✔
1794
      if (sendErr != 0 && g_logCommonErrors) {
2,398!
1795
        g_slogudpin->error(Logr::Warning, sendErr, "Sending UDP reply to client failed");
×
1796
      }
×
1797
    }
2,398✔
1798
    else {
785✔
1799
      bool hadError = sendResponseOverTCP(comboWriter, packet);
785✔
1800
      finishTCPReply(comboWriter, hadError, true);
785✔
1801
      tcpGuard.setHandled();
785✔
1802
    }
785✔
1803

1804
    resolver.d_eventTrace.add(RecEventTrace::AnswerSent);
3,183✔
1805

1806
    // Now do the per query changing part of the protobuf message
1807
    if (t_protobufServers.servers && !(luaconfsLocal->protobufExportConfig.taggedOnly && appliedPolicy.getName().empty() && comboWriter->d_policyTags.empty())) {
3,183!
1808
      // Below are the fields that are not stored in the packet cache and will be appended here and on a cache hit
1809
      if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
28!
1810
        pbMessage.setQueryTime(comboWriter->d_kernelTimestamp.tv_sec, comboWriter->d_kernelTimestamp.tv_usec);
×
1811
      }
×
1812
      else {
28✔
1813
        pbMessage.setQueryTime(comboWriter->d_now.tv_sec, comboWriter->d_now.tv_usec);
28✔
1814
      }
28✔
1815
      pbMessage.setMessageIdentity(comboWriter->d_uuid);
28✔
1816
      pbMessage.setSocketProtocol(comboWriter->d_tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP);
28✔
1817

1818
      if (!luaconfsLocal->protobufExportConfig.logMappedFrom) {
28✔
1819
        pbMessage.setSocketFamily(comboWriter->d_source.sin4.sin_family);
26✔
1820
        Netmask requestorNM(comboWriter->d_source, comboWriter->d_source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
26!
1821
        ComboAddress requestor = requestorNM.getMaskedNetwork();
26✔
1822
        pbMessage.setFrom(requestor);
26✔
1823
        pbMessage.setFromPort(comboWriter->d_source.getPort());
26✔
1824
      }
26✔
1825
      else {
2✔
1826
        pbMessage.setSocketFamily(comboWriter->d_mappedSource.sin4.sin_family);
2✔
1827
        Netmask requestorNM(comboWriter->d_mappedSource, comboWriter->d_mappedSource.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
2!
1828
        ComboAddress requestor = requestorNM.getMaskedNetwork();
2✔
1829
        pbMessage.setFrom(requestor);
2✔
1830
        pbMessage.setFromPort(comboWriter->d_mappedSource.getPort());
2✔
1831
      }
2✔
1832

1833
      pbMessage.setTo(comboWriter->d_destination);
28✔
1834
      pbMessage.setId(comboWriter->d_mdp.d_header.id);
28✔
1835

1836
      pbMessage.setTime();
28✔
1837
      pbMessage.setEDNSSubnet(comboWriter->d_ednssubnet.getSource(), comboWriter->d_ednssubnet.getSource().isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
28!
1838
      pbMessage.setRequestorId(dnsQuestion.requestorId);
28✔
1839
      pbMessage.setDeviceId(dnsQuestion.deviceId);
28✔
1840
      pbMessage.setDeviceName(dnsQuestion.deviceName);
28✔
1841
      pbMessage.setToPort(comboWriter->d_destination.getPort());
28✔
1842
      pbMessage.addPolicyTags(comboWriter->d_gettagPolicyTags);
28✔
1843
      pbMessage.setWorkerId(RecThreadInfo::thread_local_id());
28✔
1844
      pbMessage.setPacketCacheHit(false);
28✔
1845
      pbMessage.setOutgoingQueries(resolver.d_outqueries);
28✔
1846
      for (const auto& metaValue : dnsQuestion.meta) {
28✔
1847
        pbMessage.setMeta(metaValue.first, metaValue.second.stringVal, metaValue.second.intVal);
2✔
1848
      }
2✔
1849
#ifdef NOD_ENABLED
28✔
1850
      if (g_nodEnabled) {
28!
1851
        if (nod) {
×
1852
          pbMessage.setNewlyObservedDomain(true);
×
1853
          pbMessage.addPolicyTag(g_nod_pbtag);
×
1854
        }
×
1855
        if (hasUDR) {
×
1856
          pbMessage.addPolicyTag(g_udr_pbtag);
×
1857
        }
×
1858
      }
×
1859
#endif /* NOD_ENABLED */
28✔
1860
      if (resolver.d_eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_pb)) {
28!
1861
        pbMessage.addEvents(resolver.d_eventTrace);
×
1862
      }
×
1863
      if (resolver.d_eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
28!
1864
        auto otTrace = pdns::trace::TracesData::boilerPlate("rec", comboWriter->d_mdp.d_qname.toLogString() + '/' + QType(comboWriter->d_mdp.d_qtype).toString(), resolver.d_eventTrace.convertToOT(resolver.d_otTrace));
2✔
1865
        string otData = otTrace.encode();
2✔
1866
        pbMessage.setOpenTelemetryData(otData);
2✔
1867
      }
2✔
1868
      // Currently only set if an OT trace is generated
1869
      if (resolver.d_otTrace.trace_id != pdns::trace::s_emptyTraceID) {
28✔
1870
        pbMessage.setOpenTelemtryTraceID(resolver.d_otTrace.trace_id);
2✔
1871
      }
2✔
1872
      if (comboWriter->d_logResponse) {
28✔
1873
        protobufLogResponse(pbMessage);
25✔
1874
      }
25✔
1875
    }
28✔
1876

1877
    if (resolver.d_eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_log)) {
3,183!
1878
      resolver.d_slog->info(Logr::Info, resolver.d_eventTrace.toString()); // Maybe we want it to be more fancy?
×
1879
    }
×
1880

1881
    // Originally this code used a mix of floats, doubles, uint64_t with different units.
1882
    // Now it always uses an integral number of microseconds, except for averages, which use doubles
1883
    uint64_t spentUsec = uSec(resolver.getNow() - comboWriter->d_now);
3,183✔
1884
    if (!g_quiet) {
3,183✔
1885
      resolver.d_slog->info(Logr::Info, "Answer", "rd", Logging::Loggable(comboWriter->d_mdp.d_header.rd),
950✔
1886
                            "tc", Logging::Loggable(packetWriter.getHeader()->tc),
950✔
1887
                            "answers", Logging::Loggable(ntohs(packetWriter.getHeader()->ancount)),
950✔
1888
                            "additional", Logging::Loggable(ntohs(packetWriter.getHeader()->arcount)),
950✔
1889
                            "outqueries", Logging::Loggable(resolver.d_outqueries),
950✔
1890
                            "netms", Logging::Loggable(resolver.d_totUsec / 1000.0),
950✔
1891
                            "totms", Logging::Loggable(static_cast<double>(spentUsec) / 1000.0),
950✔
1892
                            "throttled", Logging::Loggable(resolver.d_throttledqueries),
950✔
1893
                            "timeouts", Logging::Loggable(resolver.d_timeouts),
950✔
1894
                            "tcpout", Logging::Loggable(resolver.d_tcpoutqueries),
950✔
1895
                            "dotout", Logging::Loggable(resolver.d_dotoutqueries),
950✔
1896
                            "rcode", Logging::Loggable(res),
950✔
1897
                            "validationState", Logging::Loggable(resolver.getValidationState()),
950✔
1898
                            "answer-is-variable", Logging::Loggable(resolver.wasVariable()),
950✔
1899
                            "into-packetcache", Logging::Loggable(intoPC),
950✔
1900
                            "maxdepth", Logging::Loggable(resolver.d_maxdepth));
950✔
1901
    }
950✔
1902

1903
    if (comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Query)) {
3,183✔
1904
      if (resolver.d_outqueries != 0 || resolver.d_throttledqueries != 0 || resolver.d_authzonequeries != 0) {
3,159✔
1905
        g_recCache->incCacheMisses();
2,669✔
1906
      }
2,669✔
1907
      else {
490✔
1908
        g_recCache->incCacheHits();
490✔
1909
      }
490✔
1910
    }
3,159✔
1911

1912
    t_Counters.at(rec::Histogram::answers)(spentUsec);
3,183✔
1913
    t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
3,183✔
1914

1915
    auto newLat = static_cast<double>(spentUsec);
3,183✔
1916
    newLat = min(newLat, g_networkTimeoutMsec * 1000.0); // outliers of several minutes exist..
3,183✔
1917
    t_Counters.at(rec::DoubleWAvgCounter::avgLatencyUsec).addToRollingAvg(newLat, g_latencyStatSize);
3,183✔
1918
    // no worries, we do this for packet cache hits elsewhere
1919

1920
    if (spentUsec >= resolver.d_totUsec) {
3,183✔
1921
      uint64_t ourtime = spentUsec - resolver.d_totUsec;
3,182✔
1922
      t_Counters.at(rec::Histogram::ourtime)(ourtime);
3,182✔
1923
      newLat = static_cast<double>(ourtime); // usec
3,182✔
1924
      t_Counters.at(rec::DoubleWAvgCounter::avgLatencyOursUsec).addToRollingAvg(newLat, g_latencyStatSize);
3,182✔
1925
    }
3,182✔
1926

1927
#ifdef NOD_ENABLED
3,183✔
1928
    if (nod) {
3,183✔
1929
      sendNODLookup(nodlogger, comboWriter->d_mdp.d_qname);
3✔
1930
    }
3✔
1931
#endif /* NOD_ENABLED */
3,183✔
1932

1933
    //    cout<<dc->d_mdp.d_qname<<"\t"<<MT->getUsec()<<"\t"<<sr.d_outqueries<<endl;
1934
  }
3,183✔
1935
  catch (const PDNSException& ae) {
3,196✔
1936
    resolver.d_slog->error(Logr::Error, ae.reason, "startDoResolve problem", "exception", Logging::Loggable("PDNSException"));
×
1937
  }
×
1938
  catch (const MOADNSException& mde) {
3,196✔
1939
    resolver.d_slog->error(Logr::Error, mde.what(), "DNS parser error");
×
1940
  }
×
1941
  catch (const std::exception& e) {
3,196✔
1942
    resolver.d_slog->error(Logr::Error, e.what(), "Exception in resolver context", "exception", Logging::Loggable("std::exception"));
×
1943

1944
    // Luawrapper nests the exception from Lua, so we unnest it here
1945
    try {
×
1946
      std::rethrow_if_nested(e);
×
1947
    }
×
1948
    catch (const std::exception& ne) {
×
1949
      resolver.d_slog->error(Logr::Error, ne.what(), "Nested exception in resolver context", Logging::Loggable("std::exception"));
×
1950
    }
×
1951
    catch (...) {
×
1952
      ;
×
1953
    }
×
1954
  }
×
1955
  catch (...) {
3,196✔
1956
    resolver.d_slog->info(Logr::Error, "Any other exception in a resolver context");
×
1957
  }
×
1958

1959
  runTaskOnce(g_logCommonErrors);
3,183✔
1960

1961
  static const size_t stackSizeThreshold = 9 * ::arg().asNum("stack-size") / 10;
3,183✔
1962
  if (g_multiTasker->getMaxStackUsage() >= stackSizeThreshold) {
3,183!
1963
    resolver.d_slog->info(Logr::Error, "Reached mthread stack usage of 90%",
×
1964
                          "stackUsage", Logging::Loggable(g_multiTasker->getMaxStackUsage()),
×
1965
                          "outqueries", Logging::Loggable(resolver.d_outqueries),
×
1966
                          "netms", Logging::Loggable(resolver.d_totUsec / 1000.0),
×
1967
                          "throttled", Logging::Loggable(resolver.d_throttledqueries),
×
1968
                          "timeouts", Logging::Loggable(resolver.d_timeouts),
×
1969
                          "tcpout", Logging::Loggable(resolver.d_tcpoutqueries),
×
1970
                          "dotout", Logging::Loggable(resolver.d_dotoutqueries),
×
1971
                          "validationState", Logging::Loggable(resolver.getValidationState()));
×
1972
  }
×
1973
  t_Counters.at(rec::Counter::maxMThreadStackUsage) = max(g_multiTasker->getMaxStackUsage(), t_Counters.at(rec::Counter::maxMThreadStackUsage));
3,183✔
1974
  t_Counters.updateSnap(g_regressionTestMode);
3,183✔
1975
}
3,183✔
1976

1977
void getQNameAndSubnet(const std::string& question, DNSName* dnsname, uint16_t* qtype, uint16_t* qclass,
1978
                       bool& foundECS, EDNSSubnetOpts* ednssubnet, EDNSOptionViewMap* options, boost::optional<uint32_t>& ednsVersion)
1979
{
157✔
1980
  const dnsheader_aligned dnshead(question.data());
157✔
1981
  const dnsheader* dhPointer = dnshead.get();
157✔
1982
  size_t questionLen = question.length();
157✔
1983
  unsigned int consumed = 0;
157✔
1984
  *dnsname = DNSName(question.c_str(), static_cast<int>(questionLen), sizeof(dnsheader), false, qtype, qclass, &consumed);
157✔
1985

1986
  size_t pos = sizeof(dnsheader) + consumed + 4;
157✔
1987
  const size_t headerSize = /* root */ 1 + sizeof(dnsrecordheader);
157✔
1988
  const uint16_t arcount = ntohs(dhPointer->arcount);
157✔
1989

1990
  for (uint16_t arpos = 0; arpos < arcount && questionLen >= (pos + headerSize) && !foundECS; arpos++) {
174!
1991
    if (question.at(pos) != 0) {
115!
1992
      /* not an OPT, bye. */
1993
      return;
×
1994
    }
×
1995

1996
    pos += 1;
115✔
1997
    const auto* drh = reinterpret_cast<const dnsrecordheader*>(&question.at(pos)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
115✔
1998
    if (ntohs(drh->d_type) == QType::OPT) {
115!
1999
      uint32_t edns{};
115✔
2000
      memcpy(&edns, &drh->d_ttl, sizeof(edns)); // drh is not neccesarily aligned, so no uint32 assignment can be done
115✔
2001
      ednsVersion = edns;
115✔
2002
    }
115✔
2003
    pos += sizeof(dnsrecordheader);
115✔
2004

2005
    if (pos >= questionLen) {
115✔
2006
      return;
98✔
2007
    }
98✔
2008

2009
    /* OPT root label (1) followed by type (2) */
2010
    if (ntohs(drh->d_type) == QType::OPT) {
17!
2011
      if (options == nullptr) {
17✔
2012
        size_t ecsStartPosition = 0;
13✔
2013
        size_t ecsLen = 0;
13✔
2014
        /* we need to pass the record len */
2015
        int res = getEDNSOption(reinterpret_cast<const char*>(&question.at(pos - sizeof(drh->d_clen))), questionLen - pos + sizeof(drh->d_clen), EDNSOptionCode::ECS, &ecsStartPosition, &ecsLen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
13✔
2016
        if (res == 0 && ecsLen > 4) {
13!
2017
          EDNSSubnetOpts eso;
10✔
2018
          if (EDNSSubnetOpts::getFromString(&question.at(pos - sizeof(drh->d_clen) + ecsStartPosition + 4), ecsLen - 4, &eso)) {
10!
2019
            *ednssubnet = eso;
10✔
2020
            foundECS = true;
10✔
2021
          }
10✔
2022
        }
10✔
2023
      }
13✔
2024
      else {
4✔
2025
        /* we need to pass the record len */
2026
        int res = getEDNSOptions(reinterpret_cast<const char*>(&question.at(pos - sizeof(drh->d_clen))), questionLen - pos + (sizeof(drh->d_clen)), *options); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
4✔
2027
        if (res == 0) {
4!
2028
          const auto iter = options->find(EDNSOptionCode::ECS);
4✔
2029
          if (iter != options->end() && !iter->second.values.empty() && iter->second.values.at(0).content != nullptr && iter->second.values.at(0).size > 0) {
4!
2030
            EDNSSubnetOpts eso;
4✔
2031
            if (EDNSSubnetOpts::getFromString(iter->second.values.at(0).content, iter->second.values.at(0).size, &eso)) {
4!
2032
              *ednssubnet = eso;
4✔
2033
              foundECS = true;
4✔
2034
            }
4✔
2035
          }
4✔
2036
        }
4✔
2037
      }
4✔
2038
    }
17✔
2039

2040
    pos += ntohs(drh->d_clen);
17✔
2041
  }
17✔
2042
}
157✔
2043

2044
bool checkForCacheHit(bool qnameParsed, unsigned int tag, const string& data,
2045
                      DNSName& qname, uint16_t& qtype, uint16_t& qclass,
2046
                      const struct timeval& now,
2047
                      string& response, uint32_t& qhash,
2048
                      RecursorPacketCache::OptPBData& pbData, bool tcp, const ComboAddress& source, const ComboAddress& mappedSource)
2049
{
6,182✔
2050
  if (!g_packetCache) {
6,182✔
2051
    return false;
144✔
2052
  }
144✔
2053
  bool cacheHit = false;
6,038✔
2054
  uint32_t age = 0;
6,038✔
2055
  vState valState = vState::Indeterminate;
6,038✔
2056

2057
  if (qnameParsed) {
6,038✔
2058
    cacheHit = g_packetCache->getResponsePacket(tag, data, qname, qtype, qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
133✔
2059
  }
133✔
2060
  else {
5,905✔
2061
    cacheHit = g_packetCache->getResponsePacket(tag, data, qname, &qtype, &qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
5,905✔
2062
  }
5,905✔
2063

2064
  if (cacheHit) {
6,038✔
2065
    if (vStateIsBogus(valState)) {
3,011!
2066
      if (t_bogusremotes) {
×
2067
        t_bogusremotes->push_back(source);
×
2068
      }
×
2069
      if (t_bogusqueryring) {
×
2070
        t_bogusqueryring->push_back({qname, qtype});
×
2071
      }
×
2072
    }
×
2073

2074
    // This is only to get the proxyMapping suffixMatch stats right i the case of a PC hit
2075
    if (t_proxyMapping && source != mappedSource) {
3,011!
2076
      if (const auto* found = t_proxyMapping->lookup(source)) {
×
2077
        if (found->second.suffixMatchNode) {
×
2078
          if (found->second.suffixMatchNode->check(qname)) {
×
2079
            ++found->second.stats.suffixMatches;
×
2080
          }
×
2081
        }
×
2082
      }
×
2083
    }
×
2084

2085
    if (response.length() >= sizeof(struct dnsheader)) {
3,013✔
2086
      dnsheader_aligned dh_aligned(response.data());
3,003✔
2087
      ageDNSPacket(response, age, dh_aligned);
3,003✔
2088
      const auto* dhp = dh_aligned.get();
3,003✔
2089
      updateResponseStats(dhp->rcode, source, response.length(), nullptr, 0);
3,003✔
2090
      t_Counters.at(rec::ResponseStats::responseStats).submitResponse(qtype, response.length(), dhp->rcode);
3,003✔
2091
    }
3,003✔
2092

2093
    // we assume 0 usec
2094
    t_Counters.at(rec::DoubleWAvgCounter::avgLatencyUsec).addToRollingAvg(0.0, g_latencyStatSize);
3,011✔
2095
    t_Counters.at(rec::DoubleWAvgCounter::avgLatencyOursUsec).addToRollingAvg(0.0, g_latencyStatSize);
3,011✔
2096
#if 0
2097
    // XXX changes behaviour compared to old code!
2098
    t_Counters.at(rec::Counter::answers)(0);
2099
    t_Counters.at(rec::Counter::ourtime)(0);
2100
#endif
2101
  }
3,011✔
2102

2103
  return cacheHit;
6,038✔
2104
}
6,182✔
2105

2106
static void* pleaseWipeCaches(const DNSName& canon, bool subtree, uint16_t qtype)
2107
{
2✔
2108
  auto res = wipeCaches(canon, subtree, qtype);
2✔
2109
  g_slog->withName("runtime")->info(Logr::Info, "Wiped cache", "qname", Logging::Loggable(canon), "records", Logging::Loggable(res.record_count), "negrecords", Logging::Loggable(res.negative_record_count), "packets", Logging::Loggable(res.packet_count));
2✔
2110
  return nullptr;
2✔
2111
}
2✔
2112

2113
void requestWipeCaches(const DNSName& canon)
2114
{
2✔
2115
  // send a message to the handler thread asking it
2116
  // to wipe all of the caches
2117
  ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: pointer owner
2✔
2118
  tmsg->func = [=] { return pleaseWipeCaches(canon, true, 0xffff); };
2✔
2119
  tmsg->wantAnswer = false;
2✔
2120
  if (write(RecThreadInfo::info(0).getPipes().writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // NOLINT: correct sizeof
2!
2121
    delete tmsg; // NOLINT: pointer owner
×
2122

2123
    unixDie("write to thread pipe returned wrong size or error");
×
2124
  }
×
2125
  // coverity[leaked_storage]
2126
}
2✔
2127

2128
bool expectProxyProtocol(const ComboAddress& from, const ComboAddress& listenAddress)
2129
{
5,801✔
2130
  if (!t_proxyProtocolACL) {
5,801✔
2131
    return false;
5,728✔
2132
  }
5,728✔
2133
  if (t_proxyProtocolACL->match(from)) {
73✔
2134
    if (!t_proxyProtocolExceptions) {
65✔
2135
      return true;
60✔
2136
    }
60✔
2137
    return t_proxyProtocolExceptions->count(listenAddress) == 0;
5✔
2138
  }
65✔
2139
  return false;
8✔
2140
}
73✔
2141

2142
// fromaddr: the address the query is coming from
2143
// destaddr: the address the query was received on
2144
// source: the address we assume the query is coming from, might be set by proxy protocol
2145
// destination: the address we assume the query was sent to, might be set by proxy protocol
2146
// mappedSource: the address we assume the query is coming from. Differs from source if table based mapping has been applied
2147
static string* doProcessUDPQuestion(const std::string& question, const ComboAddress& fromaddr, const ComboAddress& destaddr, ComboAddress source, ComboAddress destination, const ComboAddress& mappedSource, struct timeval tval, int fileDesc, std::vector<ProxyProtocolValue>& proxyProtocolValues, RecEventTrace& eventTrace, pdns::trace::InitialSpanInfo& otTrace) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
2148
{
5,362✔
2149
  RecThreadInfo::self().incNumberOfDistributedQueries();
5,362✔
2150
  gettimeofday(&g_now, nullptr);
5,362✔
2151
  if (tval.tv_sec != 0) {
5,365✔
2152
    struct timeval diff = g_now - tval;
5,344✔
2153
    double delta = (static_cast<double>(diff.tv_sec) * 1000 + static_cast<double>(diff.tv_usec) / 1000.0);
5,344✔
2154

2155
    if (delta > 1000.0) {
5,344✔
2156
      t_Counters.at(rec::Counter::tooOldDrops)++;
17✔
2157
      return nullptr;
17✔
2158
    }
17✔
2159
  }
5,344✔
2160

2161
  ++t_Counters.at(rec::Counter::qcounter);
5,345✔
2162

2163
  if (fromaddr.sin4.sin_family == AF_INET6) {
5,345✔
2164
    t_Counters.at(rec::Counter::ipv6qcounter)++;
2✔
2165
  }
2✔
2166

2167
  string response;
5,345✔
2168
  const dnsheader_aligned headerdata(question.data());
5,345✔
2169
  const dnsheader* dnsheader = headerdata.get();
5,345✔
2170
  unsigned int ctag = 0;
5,345✔
2171
  uint32_t qhash = 0;
5,345✔
2172
  bool needEDNSParse = false;
5,345✔
2173
  std::unordered_set<std::string> policyTags;
5,345✔
2174
  std::map<std::string, RecursorLua4::MetaValue> meta;
5,345✔
2175
  LuaContext::LuaObject data;
5,345✔
2176
  string requestorId;
5,345✔
2177
  string deviceId;
5,345✔
2178
  string deviceName;
5,345✔
2179
  string routingTag;
5,345✔
2180
  bool logQuery = false;
5,345✔
2181
  bool logResponse = false;
5,345✔
2182
  boost::uuids::uuid uniqueId{};
5,345✔
2183
  auto luaconfsLocal = g_luaconfs.getLocal();
5,345✔
2184
  const auto pbExport = checkProtobufExport(luaconfsLocal);
5,345✔
2185
  const auto outgoingbExport = checkOutgoingProtobufExport(luaconfsLocal);
5,345✔
2186
  if (pbExport || outgoingbExport) {
5,364✔
2187
    if (pbExport) {
40✔
2188
      needEDNSParse = true;
33✔
2189
    }
33✔
2190
    uniqueId = getUniqueID();
40✔
2191
  }
40✔
2192
  logQuery = t_protobufServers.servers && luaconfsLocal->protobufExportConfig.logQueries;
5,345✔
2193
  logResponse = t_protobufServers.servers && luaconfsLocal->protobufExportConfig.logResponses;
5,345✔
2194
#ifdef HAVE_FSTRM
5,345✔
2195
  checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
5,345✔
2196
#endif
5,345✔
2197
  EDNSSubnetOpts ednssubnet;
5,345✔
2198
  bool ecsFound = false;
5,345✔
2199
  bool ecsParsed = false;
5,345✔
2200
  std::vector<DNSRecord> records;
5,345✔
2201
  std::string extendedErrorExtra;
5,345✔
2202
  boost::optional<int> rcode = boost::none;
5,345✔
2203
  boost::optional<uint16_t> extendedErrorCode{boost::none};
5,345✔
2204
  boost::optional<uint32_t> ednsVersion{boost::none};
5,345✔
2205
  uint32_t ttlCap = std::numeric_limits<uint32_t>::max();
5,345✔
2206
  bool variable = false;
5,345✔
2207
  bool followCNAMEs = false;
5,345✔
2208
  bool responsePaddingDisabled = false;
5,345✔
2209
  DNSName qname;
5,345✔
2210
  try {
5,345✔
2211
    uint16_t qtype = 0;
5,345✔
2212
    uint16_t qclass = 0;
5,345✔
2213
    bool qnameParsed = false;
5,345✔
2214
#ifdef MALLOC_TRACE
2215
    /*
2216
    static uint64_t last=0;
2217
    if(!last)
2218
      g_mtracer->clearAllocators();
2219
    cout<<g_mtracer->getAllocs()-last<<" "<<g_mtracer->getNumOut()<<" -- BEGIN TRACE"<<endl;
2220
    last=g_mtracer->getAllocs();
2221
    cout<<g_mtracer->topAllocatorsString()<<endl;
2222
    g_mtracer->clearAllocators();
2223
    */
2224
#endif
2225

2226
    // We do not have a SyncRes specific Lua context at this point yet, so ok to use t_pdl
2227
    if (needEDNSParse || (t_pdl && (t_pdl->hasGettagFunc() || t_pdl->hasGettagFFIFunc())) || dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
5,363✔
2228
      try {
112✔
2229
        EDNSOptionViewMap ednsOptions;
112✔
2230

2231
        ecsFound = false;
112✔
2232

2233
        getQNameAndSubnet(question, &qname, &qtype, &qclass,
112✔
2234
                          ecsFound, &ednssubnet,
112✔
2235
                          (g_gettagNeedsEDNSOptions || SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) ? &ednsOptions : nullptr,
112✔
2236
                          ednsVersion);
112✔
2237

2238
        qnameParsed = true;
112✔
2239
        ecsParsed = true;
112✔
2240

2241
        if (SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
112✔
2242
          pdns::trace::extractOTraceIDs(ednsOptions, otTrace);
3✔
2243
        }
3✔
2244
        if (t_pdl) {
112✔
2245
          try {
86✔
2246
            if (t_pdl->hasGettagFFIFunc()) {
86✔
2247
              RecursorLua4::FFIParams params(qname, qtype, destaddr, fromaddr, destination, source, ednssubnet.getSource(), data, policyTags, records, ednsOptions, proxyProtocolValues, requestorId, deviceId, deviceName, routingTag, rcode, ttlCap, variable, false, logQuery, logResponse, followCNAMEs, extendedErrorCode, extendedErrorExtra, responsePaddingDisabled, meta);
42✔
2248

2249
              auto match = eventTrace.add(RecEventTrace::LuaGetTagFFI);
42✔
2250
              ctag = t_pdl->gettag_ffi(params);
42✔
2251
              eventTrace.add(RecEventTrace::LuaGetTagFFI, ctag, false, match);
42✔
2252
            }
42✔
2253
            else if (t_pdl->hasGettagFunc()) {
44✔
2254
              auto match = eventTrace.add(RecEventTrace::LuaGetTag);
33✔
2255
              ctag = t_pdl->gettag(source, ednssubnet.getSource(), destination, qname, qtype, &policyTags, data, ednsOptions, false, requestorId, deviceId, deviceName, routingTag, proxyProtocolValues);
33✔
2256
              eventTrace.add(RecEventTrace::LuaGetTag, ctag, false, match);
33✔
2257
            }
33✔
2258
          }
86✔
2259
          catch (const MOADNSException& moadnsexception) {
86✔
2260
            if (g_logCommonErrors) {
×
2261
              g_slogudpin->error(moadnsexception.what(), "Error parsing a query packet for tag determination", "qname", Logging::Loggable(qname), "excepion", Logging::Loggable("MOADNSException"));
×
2262
            }
×
2263
          }
×
2264
          catch (const std::exception& stdException) {
86✔
2265
            g_rateLimitedLogger.log(g_slogudpin, "Error parsing a query packet for tag determination", stdException, "qname", Logging::Loggable(qname), "remote", Logging::Loggable(fromaddr));
×
2266
          }
×
2267
        }
86✔
2268
      }
112✔
2269
      catch (const std::exception& stdException) {
112✔
2270
        g_rateLimitedLogger.log(g_slogudpin, "Error parsing a query packet for tag determination, setting tag=0", stdException);
×
2271
      }
×
2272
    }
112✔
2273

2274
    RecursorPacketCache::OptPBData pbData{boost::none};
5,345✔
2275
    if (t_protobufServers.servers) {
5,345✔
2276
      if (logQuery && !(luaconfsLocal->protobufExportConfig.taggedOnly && policyTags.empty())) {
33✔
2277
        protobufLogQuery(luaconfsLocal, uniqueId, source, destination, mappedSource, ednssubnet.getSource(), false, question.size(), qname, qtype, qclass, policyTags, requestorId, deviceId, deviceName, meta, ednsVersion, *dnsheader, otTrace.trace_id);
24✔
2278
      }
24✔
2279
    }
33✔
2280

2281
    if (ctag == 0 && !responsePaddingDisabled && g_paddingFrom.match(fromaddr)) {
5,361✔
2282
      ctag = g_paddingTag;
10✔
2283
    }
10✔
2284

2285
    if (dnsheader->opcode == static_cast<unsigned>(Opcode::Query)) {
5,359✔
2286
      /* It might seem like a good idea to skip the packet cache lookup if we know that the answer is not cacheable,
2287
         but it means that the hash would not be computed. If some script decides at a later time to mark back the answer
2288
         as cacheable we would cache it with a wrong tag, so better safe than sorry. */
2289
      auto match = eventTrace.add(RecEventTrace::PCacheCheck);
5,334✔
2290
      bool cacheHit = checkForCacheHit(qnameParsed, ctag, question, qname, qtype, qclass, g_now, response, qhash, pbData, false, source, mappedSource);
5,334✔
2291
      eventTrace.add(RecEventTrace::PCacheCheck, cacheHit, false, match);
5,334✔
2292
      if (cacheHit) {
5,334✔
2293
        if (!g_quiet) {
2,947✔
2294
          g_slogudpin->info(Logr::Notice, "Question answered from packet cache", "tag", Logging::Loggable(ctag),
125✔
2295
                            "qname", Logging::Loggable(qname), "qtype", Logging::Loggable(QType(qtype)),
125✔
2296
                            "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
125✔
2297
        }
125✔
2298
        struct msghdr msgh{};
2,947✔
2299
        struct iovec iov{};
2,947✔
2300
        cmsgbuf_aligned cbuf{};
2,947✔
2301
        fillMSGHdr(&msgh, &iov, &cbuf, 0, reinterpret_cast<char*>(response.data()), response.length(), const_cast<ComboAddress*>(&fromaddr)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-type-const-cast)
2,947✔
2302
        msgh.msg_control = nullptr;
2,947✔
2303

2304
        if (g_fromtosockets.count(fileDesc) != 0) {
2,947!
2305
          addCMsgSrcAddr(&msgh, &cbuf, &destaddr, 0);
×
2306
        }
×
2307
        int sendErr = sendOnNBSocket(fileDesc, &msgh);
2,947✔
2308
        eventTrace.add(RecEventTrace::AnswerSent);
2,947✔
2309

2310
        if (t_protobufServers.servers && logResponse && (!luaconfsLocal->protobufExportConfig.taggedOnly || (pbData && pbData->d_tagged))) {
2,947!
2311
          protobufLogResponse(qname, qtype, dnsheader, luaconfsLocal, pbData, tval, false, source, destination, mappedSource, ednssubnet, uniqueId, requestorId, deviceId, deviceName, meta, eventTrace, otTrace, policyTags);
6✔
2312
        }
6✔
2313

2314
        if (eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_log)) {
2,947!
2315
          g_slogudpin->info(Logr::Info, eventTrace.toString()); // Do we want more fancy logging here?
×
2316
        }
×
2317
        if (sendErr != 0 && g_logCommonErrors) {
2,947!
2318
          g_slogudpin->error(Logr::Error, sendErr, "Sending UDP reply to client failed", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
×
2319
        }
×
2320
        struct timeval now{};
2,947✔
2321
        Utility::gettimeofday(&now, nullptr);
2,947✔
2322
        uint64_t spentUsec = uSec(now - tval);
2,947✔
2323
        t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
2,947✔
2324
        t_Counters.updateSnap(g_regressionTestMode);
2,947✔
2325
        return nullptr;
2,947✔
2326
      }
2,947✔
2327
    }
5,334✔
2328
  }
5,345✔
2329
  catch (const std::exception& e) {
5,345✔
2330
    if (g_logCommonErrors) {
×
2331
      g_slogudpin->error(Logr::Error, e.what(), "Error processing or aging answer packet", "exception", Logging::Loggable("std::exception"));
×
2332
    }
×
2333
    return nullptr;
×
2334
  }
×
2335

2336
  if (t_pdl) {
2,406✔
2337
    bool ipf = t_pdl->ipfilter(source, destination, *dnsheader, eventTrace);
239✔
2338
    if (ipf) {
239✔
2339
      if (!g_quiet) {
2!
2340
        g_slogudpin->info(Logr::Notice, "Dropped question based on policy", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
2✔
2341
      }
2✔
2342
      t_Counters.at(rec::Counter::policyDrops)++;
2✔
2343
      return nullptr;
2✔
2344
    }
2✔
2345
  }
239✔
2346

2347
  if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
2,404✔
2348
    if (!isAllowNotifyForZone(qname)) {
23!
2349
      if (!g_quiet) {
×
2350
        g_slogudpin->info(Logr::Notice, "Dropping UDP NOTIFY, zone not matched by allow-notify-for", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
×
2351
      }
×
2352

2353
      t_Counters.at(rec::Counter::zoneDisallowedNotify)++;
×
2354
      return nullptr;
×
2355
    }
×
2356

2357
    if (!g_quiet) {
23!
2358
      g_slogudpin->info(Logr::Notice, "Got NOTIFY", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr), "qname", Logging::Loggable(qname));
23✔
2359
    }
23✔
2360
    if (!ZoneXFR::notifyZoneTracker(qname)) {
23✔
2361
      // It wasn't an RPZ
2362
      requestWipeCaches(qname);
1✔
2363
    }
1✔
2364

2365
    // the operation will now be treated as a Query, generating
2366
    // a normal response, as the rest of the code does not
2367
    // check dh->opcode, but we need to ensure that the response
2368
    // to this request does not get put into the packet cache
2369
    variable = true;
23✔
2370
  }
23✔
2371

2372
  if (g_multiTasker->numProcesses() >= g_maxMThreads) {
2,404!
2373
    if (!g_quiet) {
×
2374
      g_slogudpin->info(Logr::Notice, "Dropped question, over capacity", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
×
2375
    }
×
2376
    t_Counters.at(rec::Counter::overCapacityDrops)++;
×
2377
    return nullptr;
×
2378
  }
×
2379

2380
  auto comboWriter = std::make_unique<DNSComboWriter>(question, g_now, std::move(policyTags), t_pdl, std::move(data), std::move(records));
2,404✔
2381

2382
  comboWriter->setSocket(fileDesc);
2,404✔
2383
  comboWriter->d_tag = ctag;
2,404✔
2384
  comboWriter->d_qhash = qhash;
2,404✔
2385
  comboWriter->setRemote(fromaddr); // the address the query is coming from
2,404✔
2386
  comboWriter->setSource(source); // the address we assume the query is coming from, might be set by proxy protocol
2,404✔
2387
  comboWriter->setLocal(destaddr); // the address the query was received on
2,404✔
2388
  comboWriter->setDestination(destination); // the address we assume the query is sent to, might be set by proxy protocol
2,404✔
2389
  comboWriter->setMappedSource(mappedSource); // the address we assume the query is coming from. Differs from source if table-based mapping has been applied
2,404✔
2390
  comboWriter->d_tcp = false;
2,404✔
2391
  comboWriter->d_ecsFound = ecsFound;
2,404✔
2392
  comboWriter->d_ecsParsed = ecsParsed;
2,404✔
2393
  comboWriter->d_ednssubnet = ednssubnet;
2,404✔
2394
  comboWriter->d_ttlCap = ttlCap;
2,404✔
2395
  comboWriter->d_variable = variable;
2,404✔
2396
  comboWriter->d_followCNAMERecords = followCNAMEs;
2,404✔
2397
  comboWriter->d_rcode = rcode;
2,404✔
2398
  comboWriter->d_logResponse = logResponse;
2,404✔
2399
  if (t_protobufServers.servers || t_outgoingProtobufServers.servers) {
2,405✔
2400
    comboWriter->d_uuid = uniqueId;
33✔
2401
  }
33✔
2402
  comboWriter->d_requestorId = std::move(requestorId);
2,404✔
2403
  comboWriter->d_deviceId = std::move(deviceId);
2,404✔
2404
  comboWriter->d_deviceName = std::move(deviceName);
2,404✔
2405
  comboWriter->d_kernelTimestamp = tval;
2,404✔
2406
  comboWriter->d_proxyProtocolValues = std::move(proxyProtocolValues);
2,404✔
2407
  comboWriter->d_routingTag = std::move(routingTag);
2,404✔
2408
  comboWriter->d_extendedErrorCode = extendedErrorCode;
2,404✔
2409
  comboWriter->d_extendedErrorExtra = std::move(extendedErrorExtra);
2,404✔
2410
  comboWriter->d_responsePaddingDisabled = responsePaddingDisabled;
2,404✔
2411
  comboWriter->d_meta = std::move(meta);
2,404✔
2412

2413
  comboWriter->d_eventTrace = std::move(eventTrace);
2,404✔
2414
  comboWriter->d_otTrace = std::move(otTrace);
2,404✔
2415

2416
  g_multiTasker->makeThread(startDoResolve, (void*)comboWriter.release()); // deletes dc
2,404✔
2417

2418
  return nullptr;
2,404✔
2419
}
2,404✔
2420

2421
static void handleNewUDPQuestion(int fileDesc, FDMultiplexer::funcparam_t& /* var */) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
2422
{
938✔
2423
  const bool proxyActive = t_proxyProtocolACL && !t_proxyProtocolACL->empty();
938!
2424
  static const size_t maxIncomingQuerySize = !proxyActive ? 512 : (512 + g_proxyProtocolMaximumSize);
938✔
2425
  static thread_local std::string data;
938✔
2426
  ComboAddress fromaddr; // the address the query is coming from
938✔
2427
  ComboAddress source; // the address we assume the query is coming from, might be set by proxy protocol
938✔
2428
  ComboAddress destination; // the address we assume the query was sent to, might be set by proxy protocol
938✔
2429
  struct msghdr msgh{};
938✔
2430
  struct iovec iov{};
938✔
2431
  cmsgbuf_aligned cbuf;
938✔
2432
  bool firstQuery = true;
938✔
2433
  std::vector<ProxyProtocolValue> proxyProtocolValues;
938✔
2434
  RecEventTrace eventTrace;
938✔
2435
  pdns::trace::InitialSpanInfo otTrace;
938✔
2436

2437
  for (size_t queriesCounter = 0; queriesCounter < g_maxUDPQueriesPerRound; queriesCounter++) {
6,322!
2438
    bool proxyProto = false;
6,322✔
2439
    proxyProtocolValues.clear();
6,322✔
2440
    data.resize(maxIncomingQuerySize);
6,322✔
2441
    fromaddr.sin6.sin6_family = AF_INET6; // this makes sure fromaddr is big enough
6,322✔
2442
    fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), data.data(), data.size(), &fromaddr);
6,322✔
2443

2444
    if (ssize_t len = recvmsg(fileDesc, &msgh, 0); len >= 0) {
6,322✔
2445
      eventTrace.clear();
5,394✔
2446
      eventTrace.setEnabled(SyncRes::s_event_trace_enabled != 0);
5,394✔
2447
      // eventTrace uses monotonic time, while OpenTelemetry uses absolute time. setEnabled()
2448
      // established the reference point, get an absolute TS as close as possible to the
2449
      // eventTrace start of trace time.
2450
      auto traceTS = pdns::trace::timestamp();
5,394✔
2451
      eventTrace.add(RecEventTrace::ReqRecv);
5,394✔
2452
      if (SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
5,394✔
2453
        otTrace.clear();
3✔
2454
        otTrace.start_time_unix_nano = traceTS;
3✔
2455
      }
3✔
2456
      firstQuery = false;
5,394✔
2457

2458
      if ((msgh.msg_flags & MSG_TRUNC) != 0) {
5,394!
2459
        t_Counters.at(rec::Counter::truncatedDrops)++;
×
2460
        if (!g_quiet) {
×
2461
          g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr));
×
2462
        }
×
2463
        return;
×
2464
      }
×
2465

2466
      data.resize(static_cast<size_t>(len));
5,394✔
2467

2468
      ComboAddress destaddr; // the address the query was sent to to
5,394✔
2469
      destaddr.reset(); // this makes sure we ignore this address if not explictly set below
5,394✔
2470
      const auto* loc = rplookup(g_listenSocketsAddresses, fileDesc);
5,394✔
2471
      if (HarvestDestinationAddress(&msgh, &destaddr)) {
5,394✔
2472
        // but.. need to get port too
2473
        if (loc != nullptr) {
1!
2474
          destaddr.sin4.sin_port = loc->sin4.sin_port;
1✔
2475
        }
1✔
2476
      }
1✔
2477
      else {
5,393✔
2478
        if (loc != nullptr) {
5,393!
2479
          destaddr = *loc;
5,393✔
2480
        }
5,393✔
2481
        else {
×
2482
          destaddr.sin4.sin_family = fromaddr.sin4.sin_family;
×
2483
          socklen_t slen = destaddr.getSocklen();
×
2484
          getsockname(fileDesc, reinterpret_cast<sockaddr*>(&destaddr), &slen); // if this fails, we're ok with it  // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
×
2485
        }
×
2486
      }
5,393✔
2487
      if (expectProxyProtocol(fromaddr, destaddr)) {
5,394✔
2488
        bool tcp = false;
26✔
2489
        ssize_t used = parseProxyHeader(data, proxyProto, source, destination, tcp, proxyProtocolValues);
26✔
2490
        if (used <= 0) {
26✔
2491
          ++t_Counters.at(rec::Counter::proxyProtocolInvalidCount);
4✔
2492
          if (!g_quiet) {
4!
2493
            g_slogudpin->info(Logr::Error, "Ignoring invalid proxy protocol query", "length", Logging::Loggable(len),
4✔
2494
                              "used", Logging::Loggable(used), "remote", Logging::Loggable(fromaddr));
4✔
2495
          }
4✔
2496
          return;
4✔
2497
        }
4✔
2498
        if (static_cast<size_t>(used) > g_proxyProtocolMaximumSize) {
22✔
2499
          if (g_quiet) {
2!
2500
            g_slogudpin->info(Logr::Error, "Proxy protocol header in UDP packet  is larger than proxy-protocol-maximum-size",
×
2501
                              "used", Logging::Loggable(used), "remote", Logging::Loggable(fromaddr));
×
2502
          }
×
2503
          ++t_Counters.at(rec::Counter::proxyProtocolInvalidCount);
2✔
2504
          return;
2✔
2505
        }
2✔
2506

2507
        data.erase(0, used);
20✔
2508
      }
20✔
2509
      else if (len > 512) {
5,368!
2510
        /* we only allow UDP packets larger than 512 for those with a proxy protocol header */
2511
        t_Counters.at(rec::Counter::truncatedDrops)++;
×
2512
        if (!g_quiet) {
×
2513
          g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr));
×
2514
        }
×
2515
        return;
×
2516
      }
×
2517

2518
      if (data.size() < sizeof(dnsheader)) {
5,388!
2519
        t_Counters.at(rec::Counter::ignoredCount)++;
×
2520
        if (!g_quiet) {
×
2521
          g_slogudpin->info(Logr::Error, "Ignoring too-short query", "length", Logging::Loggable(data.size()),
×
2522
                            "remote", Logging::Loggable(fromaddr));
×
2523
        }
×
2524
        return;
×
2525
      }
×
2526

2527
      if (!proxyProto) {
5,388✔
2528
        source = fromaddr;
5,370✔
2529
      }
5,370✔
2530
      ComboAddress mappedSource = source;
5,388✔
2531
      if (t_proxyMapping) {
5,388✔
2532
        if (const auto* iter = t_proxyMapping->lookup(source)) {
8!
2533
          mappedSource = iter->second.address;
8✔
2534
          ++iter->second.stats.netmaskMatches;
8✔
2535
        }
8✔
2536
      }
8✔
2537
      if (t_remotes) {
5,388!
2538
        t_remotes->push_back(source);
5,388✔
2539
      }
5,388✔
2540

2541
      if (t_allowFrom && !t_allowFrom->match(&mappedSource)) {
5,388!
2542
        if (!g_quiet) {
4!
2543
          g_slogudpin->info(Logr::Error, "Dropping UDP query, address not matched by allow-from", "source", Logging::Loggable(mappedSource));
4✔
2544
        }
4✔
2545

2546
        t_Counters.at(rec::Counter::unauthorizedUDP)++;
4✔
2547
        return;
4✔
2548
      }
4✔
2549

2550
      BOOST_STATIC_ASSERT(offsetof(sockaddr_in, sin_port) == offsetof(sockaddr_in6, sin6_port));
5,384✔
2551
      if (fromaddr.sin4.sin_port == 0) { // also works for IPv6
5,384!
2552
        if (!g_quiet) {
×
2553
          g_slogudpin->info(Logr::Error, "Dropping UDP query can't deal with port 0", "remote", Logging::Loggable(fromaddr));
×
2554
        }
×
2555

2556
        t_Counters.at(rec::Counter::clientParseError)++; // not quite the best place to put it, but needs to go somewhere
×
2557
        return;
×
2558
      }
×
2559

2560
      try {
5,384✔
2561
        const dnsheader_aligned headerdata(data.data());
5,384✔
2562
        const dnsheader* dnsheader = headerdata.get();
5,384✔
2563

2564
        if (dnsheader->qr) {
5,384!
2565
          t_Counters.at(rec::Counter::ignoredCount)++;
×
2566
          if (g_logCommonErrors) {
×
2567
            g_slogudpin->info(Logr::Error, "Ignoring answer on server socket", "remote", Logging::Loggable(fromaddr));
×
2568
          }
×
2569
        }
×
2570
        else if (dnsheader->opcode != static_cast<unsigned>(Opcode::Query) && dnsheader->opcode != static_cast<unsigned>(Opcode::Notify)) {
5,384✔
2571
          t_Counters.at(rec::Counter::ignoredCount)++;
3✔
2572
          if (g_logCommonErrors) {
3!
2573
            g_slogudpin->info(Logr::Error, "Ignoring unsupported opcode server socket", "remote", Logging::Loggable(fromaddr), "opcode", Logging::Loggable(Opcode::to_s(dnsheader->opcode)));
3✔
2574
          }
3✔
2575
        }
3✔
2576
        else if (dnsheader->qdcount == 0U) {
5,381!
2577
          t_Counters.at(rec::Counter::emptyQueriesCount)++;
×
2578
          if (g_logCommonErrors) {
×
2579
            g_slogudpin->info(Logr::Error, "Ignoring empty (qdcount == 0) query on server socket!", "remote", Logging::Loggable(fromaddr));
×
2580
          }
×
2581
        }
×
2582
        else {
5,381✔
2583
          if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
5,381✔
2584
            if (!t_allowNotifyFrom || !t_allowNotifyFrom->match(&mappedSource)) {
23!
2585
              if (!g_quiet) {
×
2586
                g_slogudpin->info(Logr::Error, "Dropping UDP NOTIFY from address not matched by allow-notify-from",
×
2587
                                  "source", Logging::Loggable(mappedSource));
×
2588
              }
×
2589

2590
              t_Counters.at(rec::Counter::sourceDisallowedNotify)++;
×
2591
              return;
×
2592
            }
×
2593
          }
23✔
2594

2595
          struct timeval tval = {0, 0};
5,381✔
2596
          HarvestTimestamp(&msgh, &tval);
5,381✔
2597
          if (!proxyProto) {
5,381✔
2598
            destination = destaddr;
5,367✔
2599
          }
5,367✔
2600

2601
          if (RecThreadInfo::weDistributeQueries()) {
5,381✔
2602
            std::string localdata = data;
3,612✔
2603
            distributeAsyncFunction(data, [localdata = std::move(localdata), fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace]() mutable {
3,612✔
2604
              return doProcessUDPQuestion(localdata, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace);
3,567✔
2605
            });
3,567✔
2606
          }
3,612✔
2607
          else {
1,769✔
2608
            doProcessUDPQuestion(data, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace);
1,769✔
2609
          }
1,769✔
2610
        }
5,381✔
2611
      }
5,384✔
2612
      catch (const MOADNSException& mde) {
5,384✔
2613
        t_Counters.at(rec::Counter::clientParseError)++;
×
2614
        if (g_logCommonErrors) {
×
2615
          g_slogudpin->error(Logr::Error, mde.what(), "Unable to parse packet from remote UDP client", "remote", Logging::Loggable(fromaddr), "exception", Logging::Loggable("MOADNSException"));
×
2616
        }
×
2617
      }
×
2618
      catch (const std::runtime_error& e) {
5,384✔
2619
        t_Counters.at(rec::Counter::clientParseError)++;
×
2620
        if (g_logCommonErrors) {
×
2621
          g_slogudpin->error(Logr::Error, e.what(), "Unable to parse packet from remote UDP client", "remote", Logging::Loggable(fromaddr), "exception", Logging::Loggable("std::runtime_error"));
×
2622
        }
×
2623
      }
×
2624
    }
5,384✔
2625
    else {
928✔
2626
      if (firstQuery && errno == EAGAIN) {
928!
2627
        t_Counters.at(rec::Counter::noPacketError)++;
×
2628
      }
×
2629

2630
      break;
928✔
2631
    }
928✔
2632
  }
6,322✔
2633
  t_Counters.updateSnap(g_regressionTestMode);
928✔
2634
}
928✔
2635

2636
// The two last arguments to makeUDPServerSockets are used for logging purposes only
2637
unsigned int makeUDPServerSockets(deferredAdd_t& deferredAdds, Logr::log_t log, bool doLog, unsigned int instances)
2638
{
339✔
2639
  int one = 1;
339✔
2640
  vector<string> localAddresses;
339✔
2641
  vector<string> logVec;
339✔
2642
  stringtok(localAddresses, ::arg()["local-address"], " ,");
339✔
2643

2644
  if (localAddresses.empty()) {
339!
2645
    throw PDNSException("No local address specified");
×
2646
  }
×
2647

2648
  const uint16_t defaultLocalPort = ::arg().asNum("local-port");
339✔
2649
  const vector<string> defaultVector = {"127.0.0.1", "::1"};
339✔
2650
  const auto configIsDefault = localAddresses == defaultVector;
339✔
2651

2652
  for (const auto& localAddress : localAddresses) {
358✔
2653
    ComboAddress address{localAddress, defaultLocalPort};
358✔
2654
    auto socketFd = FDWrapper(socket(address.sin4.sin_family, SOCK_DGRAM, 0));
358✔
2655
    if (socketFd < 0) {
358!
2656
      throw PDNSException("Making a UDP server socket for resolver: " + stringerror());
×
2657
    }
×
2658

2659
    if (!setSocketTimestamps(socketFd)) {
358!
2660
      log->info(Logr::Warning, "Unable to enable timestamp reporting for socket");
×
2661
    }
×
2662
    if (IsAnyAddress(address)) {
358✔
2663
      if (address.sin4.sin_family == AF_INET) {
2!
2664
        if (setsockopt(socketFd, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)) == 0) { // linux supports this, so why not - might fail on other systems
2!
2665
          g_fromtosockets.insert(socketFd);
2✔
2666
        }
2✔
2667
      }
2✔
2668
#ifdef IPV6_RECVPKTINFO
2✔
2669
      if (address.sin4.sin_family == AF_INET6) {
2!
2670
        if (setsockopt(socketFd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)) == 0) {
×
2671
          g_fromtosockets.insert(socketFd);
×
2672
        }
×
2673
      }
×
2674
#endif
2✔
2675
      if (address.sin6.sin6_family == AF_INET6 && setsockopt(socketFd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)) < 0) {
2!
2676
        int err = errno;
×
2677
        log->error(Logr::Warning, err, "Failed to set IPv6 socket to IPv6 only, continuing anyhow");
×
2678
      }
×
2679
    }
2✔
2680
    if (::arg().mustDo("non-local-bind")) {
358!
2681
      Utility::setBindAny(AF_INET6, socketFd);
×
2682
    }
×
2683

2684
    setCloseOnExec(socketFd);
358✔
2685

2686
    try {
358✔
2687
      setSocketReceiveBuffer(socketFd, 250000);
358✔
2688
    }
358✔
2689
    catch (const std::exception& e) {
358✔
2690
      log->error(Logr::Error, e.what(), "Exception while setting socket buffer size");
×
2691
    }
×
2692

2693
    if (g_reusePort) {
358✔
2694
#if defined(SO_REUSEPORT_LB)
2695
      try {
2696
        SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
2697
      }
2698
      catch (const std::exception& e) {
2699
        throw PDNSException(std::string("SO_REUSEPORT_LB: ") + e.what());
2700
      }
2701
#elif defined(SO_REUSEPORT)
2702
      try {
328✔
2703
        SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT, 1);
328✔
2704
      }
328✔
2705
      catch (const std::exception& e) {
328✔
2706
        throw PDNSException(std::string("SO_REUSEPORT: ") + e.what());
×
2707
      }
×
2708
#endif
328✔
2709
    }
328✔
2710

2711
    try {
358✔
2712
      setSocketIgnorePMTU(socketFd, address.sin4.sin_family);
358✔
2713
    }
358✔
2714
    catch (const std::exception& e) {
358✔
2715
      log->error(Logr::Warning, e.what(), "Failed to set IP_MTU_DISCOVER on UDP server socket");
×
2716
    }
×
2717

2718
    socklen_t socklen = address.getSocklen();
358✔
2719
    if (::bind(socketFd, reinterpret_cast<struct sockaddr*>(&address), socklen) < 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
358!
2720
      int err = errno;
×
2721
      if (!configIsDefault || address != ComboAddress{"::1", defaultLocalPort}) {
×
2722
        throw PDNSException("Resolver binding to server socket on " + address.toStringWithPort() + ": " + stringerror(err));
×
2723
      }
×
2724
      log->info(Logr::Warning, "Cannot listen on this address, skipping", "proto", Logging::Loggable("UDP"), "address", Logging::Loggable(address), "error", Logging::Loggable(stringerror(err)));
×
2725
      continue;
×
2726
    }
×
2727

2728
    setNonBlocking(socketFd);
358✔
2729

2730
    deferredAdds.emplace_back(socketFd, handleNewUDPQuestion);
358✔
2731
    g_listenSocketsAddresses[socketFd] = address; // this is written to only from the startup thread, not from the workers
358✔
2732
    logVec.emplace_back(address.toStringWithPort());
358✔
2733
    socketFd.release(); // to avoid auto-close by FDWrapper
358✔
2734
  }
358✔
2735
  if (doLog) {
339✔
2736
    log->info(Logr::Info, "Listening for queries", "proto", Logging::Loggable("UDP"), "addresses", Logging::IterLoggable(logVec.cbegin(), logVec.cend()), "socketInstances", Logging::Loggable(instances), "reuseport", Logging::Loggable(g_reusePort));
178✔
2737
  }
178✔
2738
  return localAddresses.size();
339✔
2739
}
339✔
2740

2741
static bool trySendingQueryToWorker(unsigned int target, ThreadMSG* tmsg)
2742
{
3,612✔
2743
  auto& targetInfo = RecThreadInfo::info(target);
3,612✔
2744
  if (!targetInfo.isWorker()) {
3,612!
2745
    g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() tried to assign a query to a non-worker thread");
×
2746
    _exit(1);
×
2747
  }
×
2748

2749
  const auto& tps = targetInfo.getPipes();
3,612✔
2750

2751
  ssize_t written = write(tps.writeQueriesToThread, &tmsg, sizeof(tmsg)); // NOLINT: correct sizeof
3,612✔
2752
  if (written > 0) {
3,612!
2753
    if (static_cast<size_t>(written) != sizeof(tmsg)) { // NOLINT: correct sizeof
3,612!
2754
      delete tmsg; // NOLINT: pointer ownership
×
2755
      unixDie("write to thread pipe returned wrong size or error");
×
2756
    }
×
2757
  }
3,612✔
2758
  else {
×
2759
    int error = errno;
×
2760
    if (error == EAGAIN || error == EWOULDBLOCK) {
×
2761
      return false;
×
2762
    }
×
2763
    delete tmsg; // NOLINT: pointer ownership
×
2764
    unixDie("write to thread pipe returned wrong size or error:" + std::to_string(error));
×
2765
  }
×
2766

2767
  return true;
3,612✔
2768
}
3,612✔
2769

2770
static unsigned int getWorkerLoad(size_t workerIdx)
2771
{
×
2772
  const auto* multiThreader = RecThreadInfo::info(RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + workerIdx).getMT();
×
2773
  if (multiThreader != nullptr) {
×
2774
    return multiThreader->numProcesses();
×
2775
  }
×
2776
  return 0;
×
2777
}
×
2778

2779
static unsigned int selectWorker(unsigned int hash)
2780
{
3,612✔
2781
  assert(RecThreadInfo::numUDPWorkers() != 0); // NOLINT: assert implementation
3,612✔
2782
  if (g_balancingFactor == 0) {
3,612!
2783
    return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + (hash % RecThreadInfo::numUDPWorkers());
3,612✔
2784
  }
3,612✔
2785

2786
  /* we start with one, representing the query we are currently handling */
2787
  double currentLoad = 1;
×
2788
  std::vector<unsigned int> load(RecThreadInfo::numUDPWorkers());
×
2789
  for (size_t idx = 0; idx < RecThreadInfo::numUDPWorkers(); idx++) {
×
2790
    load[idx] = getWorkerLoad(idx);
×
2791
    currentLoad += load[idx];
×
2792
  }
×
2793

2794
  double targetLoad = (currentLoad / RecThreadInfo::numUDPWorkers()) * g_balancingFactor;
×
2795

2796
  unsigned int worker = hash % RecThreadInfo::numUDPWorkers();
×
2797
  /* at least one server has to be at or below the average load */
2798
  if (load[worker] > targetLoad) {
×
2799
    ++t_Counters.at(rec::Counter::rebalancedQueries);
×
2800
    do {
×
2801
      worker = (worker + 1) % RecThreadInfo::numUDPWorkers();
×
2802
    } while (load[worker] > targetLoad);
×
2803
  }
×
2804

2805
  return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + worker;
×
2806
}
3,612✔
2807

2808
// This function is only called by the distributor threads, when pdns-distributes-queries is set
2809
void distributeAsyncFunction(const string& packet, const pipefunc_t& func)
2810
{
3,612✔
2811
  if (!RecThreadInfo::self().isDistributor()) {
3,612!
2812
    g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() has been called by a worker"); // tid will be added
×
2813
    _exit(1);
×
2814
  }
×
2815

2816
  bool hashOK = false;
3,612✔
2817
  unsigned int hash = hashQuestion(reinterpret_cast<const uint8_t*>(packet.data()), packet.length(), g_disthashseed, hashOK); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
3,612✔
2818
  if (!hashOK) {
3,612!
2819
    // hashQuestion does detect invalid names, so we might as well punt here instead of in the worker thread
2820
    t_Counters.at(rec::Counter::ignoredCount)++;
×
2821
    throw MOADNSException("too-short (" + std::to_string(packet.length()) + ") or invalid name");
×
2822
  }
×
2823
  unsigned int target = selectWorker(hash);
3,612✔
2824

2825
  ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: pointer ownership
3,612✔
2826
  tmsg->func = func;
3,612✔
2827
  tmsg->wantAnswer = false;
3,612✔
2828

2829
  if (!trySendingQueryToWorker(target, tmsg)) {
3,612!
2830
    /* if this function failed but did not raise an exception, it means that the pipe
2831
       was full, let's try another one */
2832
    unsigned int newTarget = 0;
×
2833
    do {
×
2834
      newTarget = RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + dns_random(RecThreadInfo::numUDPWorkers());
×
2835
    } while (newTarget == target);
×
2836

2837
    if (!trySendingQueryToWorker(newTarget, tmsg)) {
×
2838
      t_Counters.at(rec::Counter::queryPipeFullDrops)++;
×
2839
      delete tmsg; // NOLINT: pointer ownership
×
2840
    }
×
2841
  }
×
2842
  // coverity[leaked_storage]
2843
}
3,612✔
2844

2845
// resend event to everybody chained onto it
2846
static void doResends(MT_t::waiters_t::iterator& iter, const std::shared_ptr<PacketID>& resend, const PacketBuffer& content)
2847
{
10,037✔
2848
  // We close the chain for new entries, since they won't be processed anyway
2849
  iter->key->closed = true;
10,037✔
2850

2851
  if (iter->key->authReqChain.empty()) {
10,037✔
2852
    return;
9,658✔
2853
  }
9,658✔
2854

2855
  auto maxWeight = t_Counters.at(rec::Counter::maxChainWeight);
379✔
2856
  auto weight = iter->key->authReqChain.size() * content.size();
379✔
2857
  if (weight > maxWeight) {
379✔
2858
    t_Counters.at(rec::Counter::maxChainWeight) = weight;
100✔
2859
  }
100✔
2860

2861
  for (auto [fileDesc, qid] : iter->key->authReqChain) {
1,600✔
2862
    auto packetID = std::make_shared<PacketID>(*resend);
1,600✔
2863
    packetID->fd = fileDesc;
1,600✔
2864
    packetID->id = qid;
1,600✔
2865
    g_multiTasker->sendEvent(packetID, &content);
1,600✔
2866
    t_Counters.at(rec::Counter::chainResends)++;
1,600✔
2867
  }
1,600✔
2868
}
379✔
2869

2870
void mthreadSleep(unsigned int jitterMsec)
2871
{
×
2872
  auto neverHappens = std::make_shared<PacketID>();
×
2873
  neverHappens->id = dns_random_uint16();
×
2874
  neverHappens->type = dns_random_uint16();
×
2875
  neverHappens->remote = ComboAddress("100::"); // discard-only
×
2876
  neverHappens->remote.setPort(dns_random_uint16());
×
2877
  neverHappens->fd = -1;
×
2878
  assert(g_multiTasker->waitEvent(neverHappens, nullptr, jitterMsec) != -1); // NOLINT
×
2879
}
×
2880

2881
static bool checkIncomingECSSource(const PacketBuffer& packet, const Netmask& subnet)
2882
{
298✔
2883
  bool foundMatchingECS = false;
298✔
2884

2885
  // We sent out ECS, check if the response has the expected ECS info
2886
  EDNSOptionViewMap ednsOptions;
298✔
2887
  if (slowParseEDNSOptions(packet, ednsOptions)) {
298!
2888
    // check content
2889
    auto option = ednsOptions.find(EDNSOptionCode::ECS);
298✔
2890
    if (option != ednsOptions.end()) {
298✔
2891
      // found an ECS option
2892
      EDNSSubnetOpts ecs;
22✔
2893
      for (const auto& value : option->second.values) {
22!
2894
        if (EDNSSubnetOpts::getFromString(value.content, value.size, &ecs)) {
22!
2895
          if (ecs.getSource() == subnet) {
22✔
2896
            foundMatchingECS = true;
21✔
2897
          }
21✔
2898
        }
22✔
2899
        break; // The RFC isn't clear about multiple ECS options. We chose to handle it like cookies
22✔
2900
               // and only look at the first.
2901
      }
22✔
2902
    }
22✔
2903
  }
298✔
2904
  return foundMatchingECS;
298✔
2905
}
298✔
2906

2907
static void handleUDPServerResponse(int fileDesc, FDMultiplexer::funcparam_t& var)
2908
{
10,037✔
2909
  auto pid = boost::any_cast<std::shared_ptr<PacketID>>(var);
10,037✔
2910
  PacketBuffer packet;
10,037✔
2911
  packet.resize(g_outgoingEDNSBufsize);
10,037✔
2912
  ComboAddress fromaddr;
10,037✔
2913
  socklen_t addrlen = sizeof(fromaddr);
10,037✔
2914

2915
  ssize_t len = recvfrom(fileDesc, &packet.at(0), packet.size(), 0, reinterpret_cast<sockaddr*>(&fromaddr), &addrlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
10,037✔
2916

2917
  const ssize_t signed_sizeof_sdnsheader = sizeof(dnsheader);
10,037✔
2918

2919
  if (len < 0) {
10,037✔
2920
    // len < 0: error on socket
2921
    t_udpclientsocks->returnSocket(fileDesc);
64✔
2922

2923
    PacketBuffer empty;
64✔
2924
    auto iter = g_multiTasker->getWaiters().find(pid);
64✔
2925
    if (iter != g_multiTasker->getWaiters().end()) {
64!
2926
      doResends(iter, pid, empty);
64✔
2927
    }
64✔
2928
    g_multiTasker->sendEvent(pid, &empty); // this denotes error (does retry lookup using other NS)
64✔
2929
    return;
64✔
2930
  }
64✔
2931

2932
  if (len < signed_sizeof_sdnsheader) {
9,973!
2933
    // We have received a packet that cannot be a valid DNS packet, as it has no complete header
2934
    // Drop it, but continue to wait for other packets
2935
    t_Counters.at(rec::Counter::serverParseError)++;
×
2936
    if (g_logCommonErrors) {
×
2937
      g_slogout->info(Logr::Error, "Unable to parse too short packet from remote UDP server", "from", Logging::Loggable(fromaddr));
×
2938
    }
×
2939
    return;
×
2940
  }
×
2941

2942
  // We have at least a full header
2943
  packet.resize(len);
9,973✔
2944
  dnsheader dnsheader{};
9,973✔
2945
  memcpy(&dnsheader, &packet.at(0), sizeof(dnsheader));
9,973✔
2946

2947
  auto pident = std::make_shared<PacketID>();
9,973✔
2948
  pident->remote = fromaddr;
9,973✔
2949
  pident->id = dnsheader.id;
9,973✔
2950
  pident->fd = fileDesc;
9,973✔
2951

2952
  if (!dnsheader.qr && g_logCommonErrors) {
9,973!
2953
    g_slogout->info(Logr::Error, "Not taking data from question on outgoing socket", "from", Logging::Loggable(fromaddr));
×
2954
  }
×
2955

2956
  if (dnsheader.qdcount == 0U || // UPC, Nominum, very old BIND on FormErr, NSD
9,973✔
2957
      dnsheader.qr == 0U) { // one weird server
9,974✔
2958
    pident->domain.clear();
×
2959
    pident->type = 0;
×
2960
  }
×
2961
  else {
9,973✔
2962
    try {
9,973✔
2963
      if (len > signed_sizeof_sdnsheader) {
9,973!
2964
        pident->domain = DNSName(reinterpret_cast<const char*>(packet.data()), static_cast<int>(len), static_cast<int>(sizeof(dnsheader)), false, &pident->type); // don't copy this from above - we need to do the actual read  // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
9,973✔
2965
      }
9,973✔
UNCOV
2966
      else {
×
2967
        // len == sizeof(dnsheader), only header case
2968
        // We will do a full scan search later to see if we can match this reply even without a domain
UNCOV
2969
        pident->domain.clear();
×
UNCOV
2970
        pident->type = 0;
×
UNCOV
2971
      }
×
2972
    }
9,973✔
2973
    catch (std::exception& e) {
9,973✔
2974
      // Parse error, continue waiting for other packets
2975
      t_Counters.at(rec::Counter::serverParseError)++; // won't be fed to lwres.cc, so we have to increment
×
2976
      g_slogudpin->error(Logr::Warning, e.what(), "Error in packet from remote nameserver", "from", Logging::Loggable(fromaddr));
×
2977
      return;
×
2978
    }
×
2979
  }
9,973✔
2980

2981
  if (!pident->domain.empty()) {
9,974!
2982
    auto iter = g_multiTasker->getWaiters().find(pident);
9,974✔
2983
    if (iter != g_multiTasker->getWaiters().end()) {
9,974!
2984
      doResends(iter, pident, packet);
9,974✔
2985
    }
9,974✔
2986
  }
9,974✔
2987

2988
retryWithName:
9,974✔
2989

2990
  if (pident->domain.empty() || g_multiTasker->sendEvent(pident, &packet) == 0) {
9,974!
2991
    /* we did not find a match for this response, something is wrong */
2992

2993
    // we do a full scan for outstanding queries on unexpected answers. not too bad since we only accept them on the right port number, which is hard enough to guess
2994
    for (const auto& d_waiter : g_multiTasker->getWaiters()) {
×
2995
      if (pident->fd == d_waiter.key->fd && d_waiter.key->remote == pident->remote && d_waiter.key->type == pident->type && pident->domain == d_waiter.key->domain) {
×
2996
        /* we are expecting an answer from that exact source, on that exact port (since we are using connected sockets), for that qname/qtype,
2997
           but with a different message ID. That smells like a spoofing attempt. For now we will just increase the counter and will deal with
2998
           that later. */
2999
        d_waiter.key->nearMisses++;
×
3000
      }
×
3001

3002
      // be a bit paranoid here since we're weakening our matching
3003
      if (pident->domain.empty() && !d_waiter.key->domain.empty() && pident->type == 0 && d_waiter.key->type != 0 && pident->id == d_waiter.key->id && d_waiter.key->remote == pident->remote) {
×
3004
        pident->domain = d_waiter.key->domain;
×
3005
        pident->type = d_waiter.key->type;
×
3006
        goto retryWithName; // note that this only passes on an error, lwres will still reject the packet NOLINT(cppcoreguidelines-avoid-goto)
×
3007
      }
×
3008
    }
×
3009
    t_Counters.at(rec::Counter::unexpectedCount)++; // if we made it here, it really is an unexpected answer
×
3010
    if (g_logCommonErrors) {
×
3011
      g_slogudpin->info(Logr::Warning, "Discarding unexpected packet", "from", Logging::Loggable(fromaddr),
×
3012
                        "qname", Logging::Loggable(pident->domain),
×
3013
                        "qtype", Logging::Loggable(QType(pident->type)),
×
3014
                        "waiters", Logging::Loggable(g_multiTasker->getWaiters().size()));
×
3015
    }
×
3016
  }
×
3017
  else if (fileDesc >= 0) {
9,974✔
3018
    /* we either found a waiter (1) or encountered an issue (-1), it's up to us to clean the socket anyway */
3019
    t_udpclientsocks->returnSocket(fileDesc);
9,973✔
3020
  }
9,973✔
3021
}
9,973✔
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