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

PowerDNS / pdns / 18748609987

23 Oct 2025 12:38PM UTC coverage: 73.046% (+4.3%) from 68.781%
18748609987

Pull #16362

github

web-flow
Merge fb6974bbe into dec9583d8
Pull Request #16362: rec: implements new feature to only generate OpenTelemtry Traces on certain conditions

38418 of 63316 branches covered (60.68%)

Branch coverage included in aggregate %.

122 of 136 new or added lines in 11 files covered. (89.71%)

53 existing lines in 11 files now uncovered.

127614 of 163983 relevant lines covered (77.82%)

5916487.93 hits per line

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

74.56
/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
{
9,130✔
104
  *fileDesc = makeClientSocket(toaddr.sin4.sin_family, localAddress);
9,130✔
105
  if (*fileDesc < 0) { // temporary error - receive exception otherwise
9,130!
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))
9,130!
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++;
9,130✔
126
  return LWResult::Result::Success;
9,130✔
127
}
9,130✔
128

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

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

146
  --d_numsocks;
9,131✔
147
}
9,130✔
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
{
9,131✔
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)
9,131✔
153

154
  if (ret < 0 && errno == EMFILE) { // this is not a catastrophic error
9,131!
155
    return ret;
×
156
  }
×
157
  if (ret < 0) {
9,131!
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__)
9,131✔
165
  int tries = 10;
9,131✔
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;
9,131✔
170
  while (--tries != 0) {
9,142✔
171
    in_port_t port = 0;
9,139✔
172

173
    if (tries == 1) { // last iteration: fall back to kernel 'random'
9,139!
174
      port = 0;
×
175
    }
×
176
    else {
9,139✔
177
      do {
9,139✔
178
        port = g_minUdpSourcePort + dns_random(g_maxUdpSourcePort - g_minUdpSourcePort + 1);
9,139✔
179
      } while (g_avoidUdpSourcePorts.count(port) != 0);
9,139!
180
    }
9,139✔
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) {
9,139✔
185
      sin = *localAddress;
20✔
186
      sin.setPort(port);
20✔
187
    }
20✔
188
    else {
9,119✔
189
      sin = pdns::getQueryLocalAddress(family, port); // does htons for us
9,119✔
190
    }
9,119✔
191
    if (::bind(ret, reinterpret_cast<struct sockaddr*>(&sin), sin.getSocklen()) >= 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
9,140✔
192
      break;
9,129✔
193
    }
9,129✔
194
  }
9,139✔
195

196
  int err = errno;
9,131✔
197

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

203
  try {
9,131✔
204
    setReceiveSocketErrors(ret, family);
9,131✔
205
    setNonBlocking(ret);
9,131✔
206
  }
9,131✔
207
  catch (...) {
9,131✔
208
    closesocket(ret);
×
209
    throw;
×
210
  }
×
211
  return ret;
9,128✔
212
}
9,131✔
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
{
12,241✔
275
  const auto max = g_maxMThreads;
12,241✔
276
  const auto current = mtasker->numProcesses();
12,241✔
277
  const unsigned int cutoff = max / 10; // if we have less than 10% used,  do not reduce auth timeout
12,241✔
278
  if (current < cutoff) {
12,244✔
279
    return g_networkTimeoutMsec;
12,242✔
280
  }
12,242✔
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
}
12,241✔
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
{
10,666✔
289

290
  auto pident = std::make_shared<PacketID>();
10,666✔
291
  pident->domain = domain;
10,666✔
292
  pident->remote = toAddress;
10,666✔
293
  pident->type = qtype;
10,666✔
294
  if (ecs) {
10,666✔
295
    pident->ecsSubnet = ecs->getSource();
551✔
296
  }
551✔
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());
10,666✔
302

303
  for (; chain.first != chain.second; chain.first++) {
93,399✔
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
84,274✔
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) {
84,274✔
308
      auto currentChainSize = chain.first->key->authReqChain.size();
1,541✔
309
      *fileDesc = -static_cast<int>(currentChainSize + 1); // value <= -1, gets used in waitEvent / sendEvent later on
1,541✔
310
      if (g_maxChainLength > 0 && currentChainSize >= g_maxChainLength) {
1,541!
311
        return LWResult::Result::ChainLimitError;
×
312
      }
×
313
      assert(uSec(chain.first->key->creationTime) != 0); // NOLINT
1,541✔
314
      auto age = now - chain.first->key->creationTime;
×
315
      if (uSec(age) > static_cast<uint64_t>(1000) * authWaitTimeMSec(g_multiTasker) * 2 / 3) {
1,541!
316
        return LWResult::Result::ChainLimitError;
×
317
      }
×
318
      chain.first->key->authReqChain.emplace(*fileDesc, qid); // we can chain
1,541✔
319
      auto maxLength = t_Counters.at(rec::Counter::maxChainLength);
1,541✔
320
      if (currentChainSize + 1 > maxLength) {
1,541✔
321
        t_Counters.at(rec::Counter::maxChainLength) = currentChainSize + 1;
473✔
322
      }
473✔
323
      return LWResult::Result::Success;
1,541✔
324
    }
1,541✔
325
  }
84,274✔
326

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

332
  pident->fd = *fileDesc;
9,125✔
333
  pident->id = qid;
9,125✔
334

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

338
  int tmp = errno;
9,125✔
339

340
  if (sent < 0) {
9,125!
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;
9,125✔
347
}
9,125✔
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
{
10,664✔
354
  static const unsigned int nearMissLimit = ::arg().asNum("spoof-nearmiss-max");
10,664✔
355

356
  auto pident = std::make_shared<PacketID>();
10,664✔
357
  pident->fd = fileDesc;
10,664✔
358
  pident->id = qid;
10,664✔
359
  pident->domain = domain;
10,664✔
360
  pident->type = qtype;
10,664✔
361
  pident->remote = fromAddr;
10,664✔
362
  pident->creationTime = now;
10,664✔
363
  if (ecs) {
10,664✔
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();
551✔
369
  }
551✔
370
  int ret = g_multiTasker->waitEvent(pident, &packet, authWaitTimeMSec(g_multiTasker), &now);
10,664✔
371
  len = 0;
10,664✔
372

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

380
    len = packet.size();
10,606✔
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)) {
10,606✔
385
      t_Counters.at(rec::Counter::ecsMissingCount)++;
259✔
386
      return LWResult::Result::ECSMissing;
259✔
387
    }
259✔
388
    if (nearMissLimit > 0 && pident->nearMisses > nearMissLimit) {
10,347!
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;
10,347✔
401
  }
10,347✔
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);
4✔
405
  }
4✔
406

407
  return ret == 0 ? LWResult::Result::Timeout : LWResult::Result::PermanentError;
2,147,483,650✔
408
}
10,664✔
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,174✔
413
  if (packetsize > 1000 && t_largeanswerremotes) {
6,174!
414
    t_largeanswerremotes->push_back(remote);
36✔
415
  }
36✔
416
  switch (res) {
6,174✔
417
  case RCode::ServFail:
16✔
418
    if (t_servfailremotes) {
16!
419
      t_servfailremotes->push_back(remote);
16✔
420
      if (query != nullptr && t_servfailqueryring) { // packet cache
16!
421
        t_servfailqueryring->push_back({*query, qtype});
16✔
422
      }
16✔
423
    }
16✔
424
    ++t_Counters.at(rec::Counter::servFails);
16✔
425
    break;
16✔
426
  case RCode::NXDomain:
72✔
427
    ++t_Counters.at(rec::Counter::nxDomains);
72✔
428
    break;
72✔
429
  case RCode::NoError:
6,081✔
430
    t_Counters.at(rec::Counter::noErrors)++;
6,081✔
431
    break;
6,081✔
432
  }
6,174✔
433
}
6,174✔
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,805✔
463
  packetWritewr.startRecord(rec.d_name, rec.d_type, (rec.d_ttl > ttlCap ? ttlCap : rec.d_ttl), rec.d_class, rec.d_place);
9,805!
464

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

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

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

483
  return true;
9,802✔
484
}
9,805✔
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,203✔
502
    if (d_dc->d_tcp && !d_dc->d_tcpConnection) {
3,203!
503
      throw std::runtime_error("incoming TCP case without TCP connection");
×
504
    }
×
505
  }
3,203✔
506
  ~RunningResolveGuard()
507
  {
3,203✔
508
    if (!d_handled && d_dc->d_tcp) {
3,203✔
509
      try {
6✔
510
        finishTCPReply(d_dc, false, true);
6✔
511
      }
6✔
512
      catch (const FDMultiplexerException&) {
6✔
513
      }
×
514
    }
6✔
515
  }
3,203✔
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
{
18✔
894
  if (trace.empty()) {
18✔
895
    return;
17✔
896
  }
17✔
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,870✔
942
  if (hdr.rcode == RCode::NXDomain || (hdr.rcode == RCode::NoError && hdr.ancount == 0 && seenAuthSOA)) {
2,870✔
943
    ttl = std::min(ttl, SyncRes::s_packetcachenegativettl);
387✔
944
  }
387✔
945
  else if ((hdr.rcode != RCode::NoError && hdr.rcode != RCode::NXDomain) || (hdr.ancount == 0 && hdr.nscount == 0)) {
2,483!
946
    ttl = min(ttl, SyncRes::s_packetcacheservfailttl);
85✔
947
  }
85✔
948
  else {
2,398✔
949
    ttl = std::min(ttl, SyncRes::s_packetcachettl);
2,398✔
950
  }
2,398✔
951
  return ttl;
2,870✔
952
}
2,870✔
953

954
static void addPolicyTagsToPBMessageIfNeeded(DNSComboWriter& comboWriter, pdns::ProtoZero::RecMessage& pbMessage)
955
{
32✔
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()) {
32✔
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()) {
32✔
964
    pbMessage.addPolicyTags(comboWriter.d_policyTags);
3✔
965
  }
3✔
966
}
32✔
967

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

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

999
      if (!comboWriter->d_tcp) {
1,360✔
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);
593✔
1004
      }
593✔
1005
      ednsOpts = edo.d_options;
1,360✔
1006
      maxanswersize -= 11; // EDNS header size
1,360✔
1007

1008
      if (!comboWriter->d_responsePaddingDisabled && g_paddingFrom.match(comboWriter->d_remote)) {
1,360✔
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,360✔
1016
        if (option.first == EDNSOptionCode::ECS && g_useIncomingECS && !comboWriter->d_ecsParsed) {
522✔
1017
          comboWriter->d_ecsFound = EDNSSubnetOpts::getFromString(option.second, &comboWriter->d_ednssubnet);
435✔
1018
        }
435✔
1019
        else if (option.first == EDNSOptionCode::NSID) {
87✔
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) {
85!
1028
          addPaddingToResponse = true;
1✔
1029
        }
1✔
1030
      }
522✔
1031
    }
1,360✔
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,203✔
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,203✔
1042
    vector<DNSRecord> ret;
3,203✔
1043
    vector<uint8_t> packet;
3,203✔
1044

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

1055
      // RRSets added below
1056
    }
34✔
1057
    checkOutgoingProtobufExport(luaconfsLocal); // to pick up changed configs
3,203✔
1058
#ifdef HAVE_FSTRM
3,203✔
1059
    checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
3,203✔
1060
    checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
3,203✔
1061
#endif
3,203✔
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,203✔
1064

1065
    packetWriter.getHeader()->aa = 0;
3,203✔
1066
    packetWriter.getHeader()->ra = 1;
3,203✔
1067
    packetWriter.getHeader()->qr = 1;
3,203✔
1068
    packetWriter.getHeader()->tc = 0;
3,203✔
1069
    packetWriter.getHeader()->id = comboWriter->d_mdp.d_header.id;
3,203✔
1070
    packetWriter.getHeader()->rd = comboWriter->d_mdp.d_header.rd;
3,203✔
1071
    packetWriter.getHeader()->cd = comboWriter->d_mdp.d_header.cd;
3,203✔
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,203✔
1078
    bool seenAuthSOA = false;
3,203✔
1079

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

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

1091
      // Does the requestor want DNSSEC records?
1092
      if ((edo.d_extFlags & EDNSOpts::DNSSECOK) != 0) {
3,176✔
1093
        DNSSECOK = true;
1,176✔
1094
        t_Counters.at(rec::Counter::dnssecQueries)++;
1,176✔
1095
      }
1,176✔
1096
      if (comboWriter->d_mdp.d_header.cd) {
3,176✔
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);
317✔
1102
      }
317✔
1103
      if (comboWriter->d_mdp.d_header.ad) {
3,176✔
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);
772✔
1110
      }
772✔
1111
    }
3,176✔
1112
    else {
27✔
1113
      // Ignore the client-set CD flag
1114
      packetWriter.getHeader()->cd = 0;
27✔
1115
    }
27✔
1116
    resolver.setDNSSECValidationRequested(g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode == DNSSECMode::ValidateForLog || ((comboWriter->d_mdp.d_header.ad || DNSSECOK) && g_dnssecmode == DNSSECMode::Process));
3,203!
1117

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

1124
    bool useMapped = true;
3,203✔
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,203!
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,203✔
1143

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

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

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

1152
    DNSFilterEngine::Policy appliedPolicy;
3,203✔
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,203!
1154
    dnsQuestion.ednsFlags = &edo.d_extFlags;
3,203✔
1155
    dnsQuestion.ednsOptions = &ednsOpts;
3,203✔
1156
    dnsQuestion.tag = comboWriter->d_tag;
3,203✔
1157
    dnsQuestion.discardedPolicies = &resolver.d_discardedPolicies;
3,203✔
1158
    dnsQuestion.policyTags = &comboWriter->d_policyTags;
3,203✔
1159
    dnsQuestion.appliedPolicy = &appliedPolicy;
3,203✔
1160
    dnsQuestion.currentRecords = &ret;
3,203✔
1161
    dnsQuestion.dh = &comboWriter->d_mdp.d_header;
3,203✔
1162
    dnsQuestion.data = comboWriter->d_data;
3,203✔
1163
    dnsQuestion.requestorId = comboWriter->d_requestorId;
3,203✔
1164
    dnsQuestion.deviceId = comboWriter->d_deviceId;
3,203✔
1165
    dnsQuestion.deviceName = comboWriter->d_deviceName;
3,203✔
1166
    dnsQuestion.proxyProtocolValues = &comboWriter->d_proxyProtocolValues;
3,203✔
1167
    dnsQuestion.extendedErrorCode = &comboWriter->d_extendedErrorCode;
3,203✔
1168
    dnsQuestion.extendedErrorExtra = &comboWriter->d_extendedErrorExtra;
3,203✔
1169
    dnsQuestion.meta = std::move(comboWriter->d_meta);
3,203✔
1170
    dnsQuestion.fromAuthIP = &resolver.d_fromAuthIP;
3,203✔
1171

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

1180
    if (ednsExtRCode != 0 || comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Notify)) {
3,203!
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,179!
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,179!
1192
      resolver.setLogMode(SyncRes::Store);
×
1193
      tracedQuery = true;
×
1194
    }
×
1195

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

1200
    if (!comboWriter->d_mdp.d_header.rd) {
3,179✔
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,178✔
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,178!
1217

1218
      if (luaconfsLocal->dfe.getClientPolicy(comboWriter->d_source, resolver.d_discardedPolicies, appliedPolicy)) {
3,177!
1219
        mergePolicyTags(comboWriter->d_policyTags, appliedPolicy.getTags());
×
1220
      }
×
1221
    }
3,177✔
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,178✔
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,175✔
1260

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

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

1268
      if (wantsRPZ && appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
3,114✔
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,112✔
1291
        resolver.d_appliedPolicy = appliedPolicy;
3,112✔
1292
        resolver.d_policyTags = std::move(comboWriter->d_policyTags);
3,112✔
1293

1294
        if (!comboWriter->d_routingTag.empty()) {
3,112✔
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,112✔
1299
        res = resolver.beginResolve(comboWriter->d_mdp.d_qname, QType(comboWriter->d_mdp.d_qtype), comboWriter->d_mdp.d_qclass, ret);
3,112✔
1300
        shouldNotValidate = resolver.wasOutOfBand();
3,112✔
1301
      }
3,112✔
1302
      catch (const ImmediateQueryDropException& e) {
3,112✔
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,112✔
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,112✔
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,112✔
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,112✔
1327
        res = -2;
2✔
1328
      }
2✔
1329
      dnsQuestion.validationState = resolver.getValidationState();
3,106✔
1330
      appliedPolicy = resolver.d_appliedPolicy;
3,106✔
1331
      comboWriter->d_policyTags = std::move(resolver.d_policyTags);
3,106✔
1332

1333
      if (appliedPolicy.d_type != DNSFilterEngine::PolicyType::None && appliedPolicy.d_zoneData && appliedPolicy.d_zoneData->d_extendedErrorCode) {
3,106!
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,106✔
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,104✔
1353
      if (comboWriter->d_luaContext) {
3,104✔
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,100!
1376
        res = getFakeAAAARecords(dnsQuestion.qname, *g_dns64Prefix, ret);
10✔
1377
        shouldNotValidate = true;
10✔
1378
      }
10✔
1379

1380
      if (comboWriter->d_luaContext) {
3,100✔
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,100✔
1404
    else if (comboWriter->d_luaContext) {
59!
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,166✔
1415
    if (tracedQuery || res == -1 || res == RCode::ServFail || packetWriter.getHeader()->rcode == static_cast<unsigned>(RCode::ServFail)) {
3,166!
1416
      dumpTrace(resolver.getTrace(), resolver.d_fixednow);
18✔
1417
    }
18✔
1418

1419
    if (res == -1) {
3,166✔
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,164✔
1425
      packetWriter.getHeader()->rcode = res;
3,164✔
1426

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

1432
          string x_marker;
2,548✔
1433
          std::shared_ptr<Logr::Logger> log;
2,548✔
1434
          if (resolver.doLog() || vStateIsBogus(state)) {
2,548!
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));
322✔
1437
            if (resolver.getDNSSECLimitHit()) {
322!
1438
              log = log->withValues("dnsseclimithit", Logging::Loggable(true));
×
1439
            }
×
1440
            auto xdnssec = g_xdnssec.getLocal();
322✔
1441
            if (xdnssec->check(comboWriter->d_mdp.d_qname)) {
322!
1442
              log = log->withValues("in-x-dnssec-names", Logging::Loggable(1));
×
1443
              x_marker = " [in x-dnssec-names]";
×
1444
            }
×
1445
          }
322✔
1446
          if (state == vState::Secure) {
2,548✔
1447
            if (resolver.doLog()) {
794✔
1448
              log->info(Logr::Info, "Validates Correctly");
155✔
1449
            }
155✔
1450

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

1461
            packetWriter.getHeader()->ad = 0;
1,637✔
1462
          }
1,637✔
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,548✔
1490
        catch (const ImmediateServFailException& e) {
2,548✔
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,548✔
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,548✔
1503

1504
      if (!ret.empty()) {
3,118✔
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,081✔
1512
        if (auto listToSort = luaconfsLocal->sortlist.getOrderCmp(comboWriter->d_source)) {
3,081✔
1513
          stable_sort(ret.begin(), ret.end(), *listToSort);
1✔
1514
          variableAnswer = true;
1✔
1515
        }
1✔
1516
      }
3,081✔
1517

1518
      bool needCommit = false;
3,118✔
1519
      for (const auto& record : ret) {
9,980✔
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))))) {
9,980!
1521
          continue;
175✔
1522
        }
175✔
1523

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

1530
        bool udr = false;
9,802✔
1531
#ifdef NOD_ENABLED
9,802✔
1532
        if (g_udrEnabled) {
9,802✔
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,802✔
1539

1540
        if (t_protobufServers.servers) {
9,802✔
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) {
40!
1545
            pbMessage.addRR(record, luaconfsLocal->protobufExportConfig.exportTypes, udr);
40✔
1546
          }
40✔
1547
        }
40✔
1548
      }
9,802✔
1549
      if (needCommit) {
3,118✔
1550
        packetWriter.commit();
3,078✔
1551
      }
3,078✔
1552
#ifdef NOD_ENABLED
3,118✔
1553
#ifdef HAVE_FSTRM
3,118✔
1554
      if (hasUDR) {
3,118✔
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,118✔
1574
#endif // NOD_ENABLED
3,118✔
1575
    }
3,118✔
1576
  sendit:;
3,190✔
1577

1578
    if (g_useIncomingECS && comboWriter->d_ecsFound && !resolver.wasVariable() && !variableAnswer) {
3,190!
1579
      EDNSSubnetOpts ednsOptions;
385✔
1580
      ednsOptions.setSource(comboWriter->d_ednssubnet.getSource());
385✔
1581
      ComboAddress sourceAddr;
385✔
1582
      sourceAddr.reset();
385✔
1583
      sourceAddr.sin4.sin_family = ednsOptions.getFamily();
385✔
1584
      ednsOptions.setScopePrefixLength(0);
385✔
1585
      auto ecsPayload = ednsOptions.makeOptString();
385✔
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())) {
385!
1590

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

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

1597
    if (haveEDNS && addPaddingToResponse) {
3,190✔
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,190✔
1622
    if (haveEDNS) {
3,190✔
1623
      auto state = resolver.getValidationState();
1,348✔
1624
      if (comboWriter->d_extendedErrorCode || resolver.d_extendedError || (SyncRes::s_addExtendedResolutionDNSErrors && vStateIsBogus(state))) {
1,348✔
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,348✔
1699
      packetWriter.commit();
1,348✔
1700
    }
1,348✔
1701

1702
    t_Counters.at(rec::ResponseStats::responseStats).submitResponse(comboWriter->d_mdp.d_qtype, packet.size(), packetWriter.getHeader()->rcode);
3,190✔
1703
    updateResponseStats(res, comboWriter->d_source, packet.size(), &comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype);
3,190✔
1704
#ifdef NOD_ENABLED
3,190✔
1705
    bool nod = false;
3,190✔
1706
    if (g_nodEnabled) {
3,190✔
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,190✔
1732

1733
    if (variableAnswer || resolver.wasVariable()) {
3,190✔
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,190!
1738
      // Start constructing embedded DNSResponse object
1739
      pbMessage.setResponseCode(packetWriter.getHeader()->rcode);
32✔
1740
      if (!appliedPolicy.getName().empty()) {
32✔
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());
32✔
1748
      pbMessage.setValidationState(resolver.getValidationState());
32✔
1749
      // See if we want to store the policyTags into the PC
1750
      addPolicyTagsToPBMessageIfNeeded(*comboWriter, pbMessage);
32✔
1751
      if (eee) {
32✔
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{
32✔
1758
        pbMessage.getMessageBuf(),
32✔
1759
        pbMessage.getResponseBuf(),
32✔
1760
        !appliedPolicy.getName().empty() || !comboWriter->d_policyTags.empty()});
32✔
1761
#ifdef NOD_ENABLED
32✔
1762
      // if (g_udrEnabled) ??
1763
      pbMessage.clearUDR(pbDataForCache->d_response);
32✔
1764
#endif
32✔
1765
    }
32✔
1766

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

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

1783
    auto match = resolver.d_eventTrace.add(RecEventTrace::AnswerSent);
3,190✔
1784
    if (!comboWriter->d_tcp) {
3,190✔
1785
      struct msghdr msgh{};
2,405✔
1786
      struct iovec iov{};
2,405✔
1787
      cmsgbuf_aligned cbuf{};
2,405✔
1788
      fillMSGHdr(&msgh, &iov, &cbuf, 0, reinterpret_cast<char*>(&*packet.begin()), packet.size(), &comboWriter->d_remote); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2,405✔
1789
      msgh.msg_control = nullptr;
2,405✔
1790

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

1805
    resolver.d_eventTrace.add(RecEventTrace::AnswerSent, 0, false, match);
3,190✔
1806

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

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

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

1837
      pbMessage.setTime();
32✔
1838
      pbMessage.setEDNSSubnet(comboWriter->d_ednssubnet.getSource(), comboWriter->d_ednssubnet.getSource().isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
32!
1839
      pbMessage.setRequestorId(dnsQuestion.requestorId);
32✔
1840
      pbMessage.setDeviceId(dnsQuestion.deviceId);
32✔
1841
      pbMessage.setDeviceName(dnsQuestion.deviceName);
32✔
1842
      pbMessage.setToPort(comboWriter->d_destination.getPort());
32✔
1843
      pbMessage.addPolicyTags(comboWriter->d_gettagPolicyTags);
32✔
1844
      pbMessage.setWorkerId(RecThreadInfo::thread_local_id());
32✔
1845
      pbMessage.setPacketCacheHit(false);
32✔
1846
      pbMessage.setOutgoingQueries(resolver.d_outqueries);
32✔
1847
      for (const auto& metaValue : dnsQuestion.meta) {
32✔
1848
        pbMessage.setMeta(metaValue.first, metaValue.second.stringVal, metaValue.second.intVal);
2✔
1849
      }
2✔
1850
#ifdef NOD_ENABLED
32✔
1851
      if (g_nodEnabled) {
32!
1852
        if (nod) {
×
1853
          pbMessage.setNewlyObservedDomain(true);
×
1854
          pbMessage.addPolicyTag(g_nod_pbtag);
×
1855
        }
×
1856
        if (hasUDR) {
×
1857
          pbMessage.addPolicyTag(g_udr_pbtag);
×
1858
        }
×
1859
      }
×
1860
#endif /* NOD_ENABLED */
32✔
1861
      if (resolver.d_eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_pb)) {
32!
1862
        pbMessage.addEvents(resolver.d_eventTrace);
×
1863
      }
×
1864

1865
      if (resolver.d_eventTrace.enabled() && resolver.d_eventTrace.getThisOTTraceEnabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
32!
1866
        resolver.d_otTrace.setIDsIfNotSet();
3✔
1867
        auto otTrace = pdns::trace::TracesData::boilerPlate("rec", resolver.d_eventTrace.convertToOT(resolver.d_otTrace), {
3✔
1868
                                                                                                                            {"query.qname", {comboWriter->d_mdp.d_qname.toLogString()}},
3✔
1869
                                                                                                                            {"query.qtype", {QType(comboWriter->d_mdp.d_qtype).toString()}},
3✔
1870
                                                                                                                          },
3✔
1871
                                                            SyncRes::s_serverID);
3✔
1872
        string otData = otTrace.encode();
3✔
1873
        pbMessage.setOpenTelemetryData(otData);
3✔
1874
      }
3✔
1875
      // It can be set even if no OT Trace data was generated
1876
      if (resolver.d_otTrace.trace_id != pdns::trace::s_emptyTraceID) {
32✔
1877
        pbMessage.setOpenTelemetryTraceID(resolver.d_otTrace.trace_id);
6✔
1878
      }
6✔
1879
      if (comboWriter->d_logResponse) {
32✔
1880
        protobufLogResponse(pbMessage);
29✔
1881
      }
29✔
1882
    }
32✔
1883

1884
    if (resolver.d_eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_log)) {
3,190!
1885
      resolver.d_slog->info(Logr::Info, resolver.d_eventTrace.toString()); // Maybe we want it to be more fancy?
×
1886
    }
×
1887

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

1910
    if (comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Query)) {
3,190✔
1911
      if (resolver.d_outqueries != 0 || resolver.d_throttledqueries != 0 || resolver.d_authzonequeries != 0) {
3,166✔
1912
        g_recCache->incCacheMisses();
2,648✔
1913
      }
2,648✔
1914
      else {
518✔
1915
        g_recCache->incCacheHits();
518✔
1916
      }
518✔
1917
    }
3,166✔
1918

1919
    t_Counters.at(rec::Histogram::answers)(spentUsec);
3,190✔
1920
    t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
3,190✔
1921

1922
    auto newLat = static_cast<double>(spentUsec);
3,190✔
1923
    newLat = min(newLat, g_networkTimeoutMsec * 1000.0); // outliers of several minutes exist..
3,190✔
1924
    t_Counters.at(rec::DoubleWAvgCounter::avgLatencyUsec).addToRollingAvg(newLat, g_latencyStatSize);
3,190✔
1925
    // no worries, we do this for packet cache hits elsewhere
1926

1927
    if (spentUsec >= resolver.d_totUsec) {
3,190!
1928
      uint64_t ourtime = spentUsec - resolver.d_totUsec;
3,190✔
1929
      t_Counters.at(rec::Histogram::ourtime)(ourtime);
3,190✔
1930
      newLat = static_cast<double>(ourtime); // usec
3,190✔
1931
      t_Counters.at(rec::DoubleWAvgCounter::avgLatencyOursUsec).addToRollingAvg(newLat, g_latencyStatSize);
3,190✔
1932
    }
3,190✔
1933

1934
#ifdef NOD_ENABLED
3,190✔
1935
    if (nod) {
3,190✔
1936
      sendNODLookup(nodlogger, comboWriter->d_mdp.d_qname);
3✔
1937
    }
3✔
1938
#endif /* NOD_ENABLED */
3,190✔
1939

1940
    //    cout<<dc->d_mdp.d_qname<<"\t"<<MT->getUsec()<<"\t"<<sr.d_outqueries<<endl;
1941
  }
3,190✔
1942
  catch (const PDNSException& ae) {
3,203✔
1943
    resolver.d_slog->error(Logr::Error, ae.reason, "startDoResolve problem", "exception", Logging::Loggable("PDNSException"));
×
1944
  }
×
1945
  catch (const MOADNSException& mde) {
3,203✔
1946
    resolver.d_slog->error(Logr::Error, mde.what(), "DNS parser error");
×
1947
  }
×
1948
  catch (const std::exception& e) {
3,203✔
1949
    resolver.d_slog->error(Logr::Error, e.what(), "Exception in resolver context", "exception", Logging::Loggable("std::exception"));
×
1950

1951
    // Luawrapper nests the exception from Lua, so we unnest it here
1952
    try {
×
1953
      std::rethrow_if_nested(e);
×
1954
    }
×
1955
    catch (const std::exception& ne) {
×
1956
      resolver.d_slog->error(Logr::Error, ne.what(), "Nested exception in resolver context", Logging::Loggable("std::exception"));
×
1957
    }
×
1958
    catch (...) {
×
1959
      ;
×
1960
    }
×
1961
  }
×
1962
  catch (...) {
3,203✔
1963
    resolver.d_slog->info(Logr::Error, "Any other exception in a resolver context");
×
1964
  }
×
1965

1966
  runTaskOnce(g_logCommonErrors);
3,189✔
1967

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

1984
void getQNameAndSubnet(const std::string& question, DNSName* dnsname, uint16_t* qtype, uint16_t* qclass,
1985
                       bool& foundECS, EDNSSubnetOpts* ednssubnet, EDNSOptionViewMap* options, boost::optional<uint32_t>& ednsVersion)
1986
{
174✔
1987
  const dnsheader_aligned dnshead(question.data());
174✔
1988
  const dnsheader* dhPointer = dnshead.get();
174✔
1989
  size_t questionLen = question.length();
174✔
1990
  unsigned int consumed = 0;
174✔
1991
  *dnsname = DNSName(question.c_str(), static_cast<int>(questionLen), sizeof(dnsheader), false, qtype, qclass, &consumed);
174✔
1992

1993
  size_t pos = sizeof(dnsheader) + consumed + 4;
174✔
1994
  const size_t headerSize = /* root */ 1 + sizeof(dnsrecordheader);
174✔
1995
  const uint16_t arcount = ntohs(dhPointer->arcount);
174✔
1996

1997
  for (uint16_t arpos = 0; arpos < arcount && questionLen >= (pos + headerSize) && !foundECS; arpos++) {
197!
1998
    if (question.at(pos) != 0) {
132!
1999
      /* not an OPT, bye. */
2000
      return;
×
2001
    }
×
2002

2003
    pos += 1;
132✔
2004
    const auto* drh = reinterpret_cast<const dnsrecordheader*>(&question.at(pos)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
132✔
2005
    if (ntohs(drh->d_type) == QType::OPT) {
132!
2006
      uint32_t edns{};
132✔
2007
      memcpy(&edns, &drh->d_ttl, sizeof(edns)); // drh is not neccesarily aligned, so no uint32 assignment can be done
132✔
2008
      ednsVersion = edns;
132✔
2009
    }
132✔
2010
    pos += sizeof(dnsrecordheader);
132✔
2011

2012
    if (pos >= questionLen) {
132✔
2013
      return;
109✔
2014
    }
109✔
2015

2016
    /* OPT root label (1) followed by type (2) */
2017
    if (ntohs(drh->d_type) == QType::OPT) {
23!
2018
      if (options == nullptr) {
23✔
2019
        size_t ecsStartPosition = 0;
13✔
2020
        size_t ecsLen = 0;
13✔
2021
        /* we need to pass the record len */
2022
        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✔
2023
        if (res == 0 && ecsLen > 4) {
13!
2024
          EDNSSubnetOpts eso;
10✔
2025
          if (EDNSSubnetOpts::getFromString(&question.at(pos - sizeof(drh->d_clen) + ecsStartPosition + 4), ecsLen - 4, &eso)) {
10!
2026
            *ednssubnet = eso;
10✔
2027
            foundECS = true;
10✔
2028
          }
10✔
2029
        }
10✔
2030
      }
13✔
2031
      else {
10✔
2032
        /* we need to pass the record len */
2033
        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)
10✔
2034
        if (res == 0) {
10!
2035
          const auto iter = options->find(EDNSOptionCode::ECS);
10✔
2036
          if (iter != options->end() && !iter->second.values.empty() && iter->second.values.at(0).content != nullptr && iter->second.values.at(0).size > 0) {
10!
2037
            EDNSSubnetOpts eso;
4✔
2038
            if (EDNSSubnetOpts::getFromString(iter->second.values.at(0).content, iter->second.values.at(0).size, &eso)) {
4!
2039
              *ednssubnet = eso;
4✔
2040
              foundECS = true;
4✔
2041
            }
4✔
2042
          }
4✔
2043
        }
10✔
2044
      }
10✔
2045
    }
23✔
2046

2047
    pos += ntohs(drh->d_clen);
23✔
2048
  }
23✔
2049
}
174✔
2050

2051
bool checkForCacheHit(bool qnameParsed, unsigned int tag, const string& data,
2052
                      DNSName& qname, uint16_t& qtype, uint16_t& qclass,
2053
                      const struct timeval& now,
2054
                      string& response, uint32_t& qhash,
2055
                      RecursorPacketCache::OptPBData& pbData, bool tcp, const ComboAddress& source, const ComboAddress& mappedSource)
2056
{
6,160✔
2057
  if (!g_packetCache) {
6,160✔
2058
    return false;
144✔
2059
  }
144✔
2060
  bool cacheHit = false;
6,016✔
2061
  uint32_t age = 0;
6,016✔
2062
  vState valState = vState::Indeterminate;
6,016✔
2063

2064
  if (qnameParsed) {
6,016✔
2065
    cacheHit = g_packetCache->getResponsePacket(tag, data, qname, qtype, qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
150✔
2066
  }
150✔
2067
  else {
5,866✔
2068
    cacheHit = g_packetCache->getResponsePacket(tag, data, qname, &qtype, &qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
5,866✔
2069
  }
5,866✔
2070

2071
  if (cacheHit) {
6,016✔
2072
    if (vStateIsBogus(valState)) {
2,979!
2073
      if (t_bogusremotes) {
×
2074
        t_bogusremotes->push_back(source);
×
2075
      }
×
2076
      if (t_bogusqueryring) {
×
2077
        t_bogusqueryring->push_back({qname, qtype});
×
2078
      }
×
2079
    }
×
2080

2081
    // This is only to get the proxyMapping suffixMatch stats right i the case of a PC hit
2082
    if (t_proxyMapping && source != mappedSource) {
2,979!
2083
      if (const auto* found = t_proxyMapping->lookup(source)) {
×
2084
        if (found->second.suffixMatchNode) {
×
2085
          if (found->second.suffixMatchNode->check(qname)) {
×
2086
            ++found->second.stats.suffixMatches;
×
2087
          }
×
2088
        }
×
2089
      }
×
2090
    }
×
2091

2092
    if (response.length() >= sizeof(struct dnsheader)) {
2,979✔
2093
      dnsheader_aligned dh_aligned(response.data());
2,966✔
2094
      ageDNSPacket(response, age, dh_aligned);
2,966✔
2095
      const auto* dhp = dh_aligned.get();
2,966✔
2096
      updateResponseStats(dhp->rcode, source, response.length(), nullptr, 0);
2,966✔
2097
      t_Counters.at(rec::ResponseStats::responseStats).submitResponse(qtype, response.length(), dhp->rcode);
2,966✔
2098
    }
2,966✔
2099

2100
    // we assume 0 usec
2101
    t_Counters.at(rec::DoubleWAvgCounter::avgLatencyUsec).addToRollingAvg(0.0, g_latencyStatSize);
2,979✔
2102
    t_Counters.at(rec::DoubleWAvgCounter::avgLatencyOursUsec).addToRollingAvg(0.0, g_latencyStatSize);
2,979✔
2103
#if 0
2104
    // XXX changes behaviour compared to old code!
2105
    t_Counters.at(rec::Counter::answers)(0);
2106
    t_Counters.at(rec::Counter::ourtime)(0);
2107
#endif
2108
  }
2,979✔
2109

2110
  return cacheHit;
6,016✔
2111
}
6,160✔
2112

2113
static void* pleaseWipeCaches(const DNSName& canon, bool subtree, uint16_t qtype)
2114
{
2✔
2115
  auto res = wipeCaches(canon, subtree, qtype);
2✔
2116
  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✔
2117
  return nullptr;
2✔
2118
}
2✔
2119

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

2130
    unixDie("write to thread pipe returned wrong size or error");
×
2131
  }
×
2132
  // coverity[leaked_storage]
2133
}
2✔
2134

2135
bool expectProxyProtocol(const ComboAddress& from, const ComboAddress& listenAddress)
2136
{
5,834✔
2137
  if (!t_proxyProtocolACL) {
5,834✔
2138
    return false;
5,761✔
2139
  }
5,761✔
2140
  if (t_proxyProtocolACL->match(from)) {
73✔
2141
    if (!t_proxyProtocolExceptions) {
65✔
2142
      return true;
60✔
2143
    }
60✔
2144
    return t_proxyProtocolExceptions->count(listenAddress) == 0;
5✔
2145
  }
65✔
2146
  return false;
8✔
2147
}
73✔
2148

2149
bool matchOTConditions(const std::unique_ptr<OpenTelemetryTraceConditions>& conditions, const ComboAddress& source)
2150
{
20✔
2151
  if (conditions == nullptr || conditions->size() == 0) {
20!
NEW
2152
    return false;
×
NEW
2153
  }
×
2154
  if (auto const* match = conditions->lookup(source); match != nullptr) {
20!
2155
    const auto& condition = match->second;
20✔
2156
    if (condition.d_traceid_only) {
20✔
2157
      return false;
5✔
2158
    }
5✔
2159
  }
20✔
2160
  return true;
15✔
2161
}
20✔
2162

2163
bool matchOTConditions(RecEventTrace& eventTrace, const std::unique_ptr<OpenTelemetryTraceConditions>& conditions, const ComboAddress& source, const DNSName& qname, QType qtype, uint16_t qid, bool edns_option_present)
2164
{
20✔
2165
  if (conditions == nullptr || conditions->size() == 0) {
20!
NEW
2166
    return false;
×
NEW
2167
  }
×
2168
  if (auto const* match = conditions->lookup(source); match != nullptr) {
20!
2169
    const auto& condition = match->second;
20✔
2170
    if (condition.d_traceid_only) {
20✔
2171
      return false;
5✔
2172
    }
5✔
2173
    if (condition.d_edns_option_required && !edns_option_present) {
15!
NEW
2174
      return false;
×
NEW
2175
    }
×
2176
    if (condition.d_qid && condition.d_qid != qid) {
15!
NEW
2177
      return false;
×
NEW
2178
    }
×
2179
    if (condition.d_qtypes && condition.d_qtypes->count(qtype) == 0) {
15✔
2180
      return false;
1✔
2181
    }
1✔
2182
    if (condition.d_qnames && !condition.d_qnames->check(qname)) {
14✔
2183
      return false;
1✔
2184
    }
1✔
2185
  }
14✔
2186

2187
  eventTrace.setThisOTTraceEnabled();
13✔
2188
  return true;
13✔
2189
}
20✔
2190

2191
// fromaddr: the address the query is coming from
2192
// destaddr: the address the query was received on
2193
// source: the address we assume the query is coming from, might be set by proxy protocol
2194
// destination: the address we assume the query was sent to, might be set by proxy protocol
2195
// mappedSource: the address we assume the query is coming from. Differs from source if table based mapping has been applied
2196
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
2197
{
5,396✔
2198
  auto newParent = eventTrace.add(RecEventTrace::ProcessUDP);
5,396✔
2199
  auto oldParent = eventTrace.setParent(newParent);
5,396✔
2200
  RecEventTrace::EventScope traceScope(oldParent, eventTrace);
5,396✔
2201
  RecThreadInfo::self().incNumberOfDistributedQueries();
5,396✔
2202
  gettimeofday(&g_now, nullptr);
5,396✔
2203
  if (tval.tv_sec != 0) {
5,396✔
2204
    struct timeval diff = g_now - tval;
5,371✔
2205
    double delta = (static_cast<double>(diff.tv_sec) * 1000 + static_cast<double>(diff.tv_usec) / 1000.0);
5,371✔
2206

2207
    if (delta > 1000.0) {
5,371✔
2208
      t_Counters.at(rec::Counter::tooOldDrops)++;
67✔
2209
      return nullptr;
67✔
2210
    }
67✔
2211
  }
5,371✔
2212

2213
  ++t_Counters.at(rec::Counter::qcounter);
5,329✔
2214

2215
  if (fromaddr.sin4.sin_family == AF_INET6) {
5,329✔
2216
    t_Counters.at(rec::Counter::ipv6qcounter)++;
2✔
2217
  }
2✔
2218

2219
  string response;
5,329✔
2220
  const dnsheader_aligned headerdata(question.data());
5,329✔
2221
  const dnsheader* dnsheader = headerdata.get();
5,329✔
2222
  unsigned int ctag = 0;
5,329✔
2223
  uint32_t qhash = 0;
5,329✔
2224
  bool needEDNSParse = false;
5,329✔
2225
  std::unordered_set<std::string> policyTags;
5,329✔
2226
  std::map<std::string, RecursorLua4::MetaValue> meta;
5,329✔
2227
  LuaContext::LuaObject data;
5,329✔
2228
  string requestorId;
5,329✔
2229
  string deviceId;
5,329✔
2230
  string deviceName;
5,329✔
2231
  string routingTag;
5,329✔
2232
  bool logQuery = false;
5,329✔
2233
  bool logResponse = false;
5,329✔
2234
  boost::uuids::uuid uniqueId{};
5,329✔
2235
  auto luaconfsLocal = g_luaconfs.getLocal();
5,329✔
2236
  const auto pbExport = checkProtobufExport(luaconfsLocal);
5,329✔
2237
  const auto outgoingbExport = checkOutgoingProtobufExport(luaconfsLocal);
5,329✔
2238
  if (pbExport || outgoingbExport) {
5,332✔
2239
    if (pbExport) {
49✔
2240
      needEDNSParse = true;
42✔
2241
    }
42✔
2242
    uniqueId = getUniqueID();
49✔
2243
  }
49✔
2244
  logQuery = t_protobufServers.servers && luaconfsLocal->protobufExportConfig.logQueries;
5,329✔
2245
  logResponse = t_protobufServers.servers && luaconfsLocal->protobufExportConfig.logResponses;
5,329✔
2246
#ifdef HAVE_FSTRM
5,329✔
2247
  checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
5,329✔
2248
#endif
5,329✔
2249
  EDNSSubnetOpts ednssubnet;
5,329✔
2250
  bool ecsFound = false;
5,329✔
2251
  bool ecsParsed = false;
5,329✔
2252
  std::vector<DNSRecord> records;
5,329✔
2253
  std::string extendedErrorExtra;
5,329✔
2254
  boost::optional<int> rcode = boost::none;
5,329✔
2255
  boost::optional<uint16_t> extendedErrorCode{boost::none};
5,329✔
2256
  boost::optional<uint32_t> ednsVersion{boost::none};
5,329✔
2257
  uint32_t ttlCap = std::numeric_limits<uint32_t>::max();
5,329✔
2258
  bool variable = false;
5,329✔
2259
  bool followCNAMEs = false;
5,329✔
2260
  bool responsePaddingDisabled = false;
5,329✔
2261
  DNSName qname;
5,329✔
2262
  try {
5,329✔
2263
    uint16_t qtype = 0;
5,329✔
2264
    uint16_t qclass = 0;
5,329✔
2265
    bool qnameParsed = false;
5,329✔
2266
#ifdef MALLOC_TRACE
2267
    /*
2268
    static uint64_t last=0;
2269
    if(!last)
2270
      g_mtracer->clearAllocators();
2271
    cout<<g_mtracer->getAllocs()-last<<" "<<g_mtracer->getNumOut()<<" -- BEGIN TRACE"<<endl;
2272
    last=g_mtracer->getAllocs();
2273
    cout<<g_mtracer->topAllocatorsString()<<endl;
2274
    g_mtracer->clearAllocators();
2275
    */
2276
#endif
2277

2278
    // We do not have a SyncRes specific Lua context at this point yet, so ok to use t_pdl
2279
    if (needEDNSParse || (t_pdl && (t_pdl->hasGettagFunc() || t_pdl->hasGettagFFIFunc())) || dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
5,332✔
2280
      try {
121✔
2281
        auto parseMatch = eventTrace.add(RecEventTrace::PacketParse);
121✔
2282
        EDNSOptionViewMap ednsOptions;
121✔
2283

2284
        ecsFound = false;
121✔
2285

2286
        getQNameAndSubnet(question, &qname, &qtype, &qclass,
121✔
2287
                          ecsFound, &ednssubnet,
121✔
2288
                          (g_gettagNeedsEDNSOptions || SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) ? &ednsOptions : nullptr,
121✔
2289
                          ednsVersion);
121✔
2290

2291
        eventTrace.add(RecEventTrace::PacketParse, 0, false, parseMatch);
121✔
2292
        qnameParsed = true;
121✔
2293
        ecsParsed = true;
121✔
2294

2295
        if (SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
121✔
2296
          bool ednsFound = pdns::trace::extractOTraceIDs(ednsOptions, otTrace);
12✔
2297
          if (!matchOTConditions(eventTrace, t_OTConditions, mappedSource, qname, qtype, ntohs(headerdata->id), ednsFound) && SyncRes::eventTraceEnabledOnly(SyncRes::event_trace_to_ot)) {
12!
2298
            eventTrace.setEnabled(false);
5✔
2299
          }
5✔
2300
        }
12✔
2301

2302
        if (t_pdl) {
121✔
2303
          try {
86✔
2304
            if (t_pdl->hasGettagFFIFunc()) {
86✔
2305
              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✔
2306

2307
              auto match = eventTrace.add(RecEventTrace::LuaGetTagFFI);
42✔
2308
              ctag = t_pdl->gettag_ffi(params);
42✔
2309
              eventTrace.add(RecEventTrace::LuaGetTagFFI, ctag, false, match);
42✔
2310
            }
42✔
2311
            else if (t_pdl->hasGettagFunc()) {
44✔
2312
              auto match = eventTrace.add(RecEventTrace::LuaGetTag);
33✔
2313
              ctag = t_pdl->gettag(source, ednssubnet.getSource(), destination, qname, qtype, &policyTags, data, ednsOptions, false, requestorId, deviceId, deviceName, routingTag, proxyProtocolValues);
33✔
2314
              eventTrace.add(RecEventTrace::LuaGetTag, ctag, false, match);
33✔
2315
            }
33✔
2316
          }
86✔
2317
          catch (const MOADNSException& moadnsexception) {
86✔
2318
            if (g_logCommonErrors) {
×
2319
              g_slogudpin->error(moadnsexception.what(), "Error parsing a query packet for tag determination", "qname", Logging::Loggable(qname), "excepion", Logging::Loggable("MOADNSException"));
×
2320
            }
×
2321
          }
×
2322
          catch (const std::exception& stdException) {
86✔
2323
            g_rateLimitedLogger.log(g_slogudpin, "Error parsing a query packet for tag determination", stdException, "qname", Logging::Loggable(qname), "remote", Logging::Loggable(fromaddr));
×
2324
          }
×
2325
        }
86✔
2326
      }
121✔
2327
      catch (const std::exception& stdException) {
121✔
2328
        g_rateLimitedLogger.log(g_slogudpin, "Error parsing a query packet for tag determination, setting tag=0", stdException);
×
2329
      }
×
2330
    }
121✔
2331

2332
    RecursorPacketCache::OptPBData pbData{boost::none};
5,329✔
2333
    if (t_protobufServers.servers) {
5,329✔
2334
      if (logQuery && !(luaconfsLocal->protobufExportConfig.taggedOnly && policyTags.empty())) {
42✔
2335
        protobufLogQuery(luaconfsLocal, uniqueId, source, destination, mappedSource, ednssubnet.getSource(), false, question.size(), qname, qtype, qclass, policyTags, requestorId, deviceId, deviceName, meta, ednsVersion, *dnsheader, otTrace.trace_id);
33✔
2336
      }
33✔
2337
    }
42✔
2338

2339
    if (ctag == 0 && !responsePaddingDisabled && g_paddingFrom.match(fromaddr)) {
5,331✔
2340
      ctag = g_paddingTag;
10✔
2341
    }
10✔
2342

2343
    if (dnsheader->opcode == static_cast<unsigned>(Opcode::Query)) {
5,330✔
2344
      /* It might seem like a good idea to skip the packet cache lookup if we know that the answer is not cacheable,
2345
         but it means that the hash would not be computed. If some script decides at a later time to mark back the answer
2346
         as cacheable we would cache it with a wrong tag, so better safe than sorry. */
2347
      auto match = eventTrace.add(RecEventTrace::PCacheCheck);
5,306✔
2348
      bool cacheHit = checkForCacheHit(qnameParsed, ctag, question, qname, qtype, qclass, g_now, response, qhash, pbData, false, source, mappedSource);
5,306✔
2349
      eventTrace.add(RecEventTrace::PCacheCheck, cacheHit, false, match);
5,306✔
2350
      if (cacheHit) {
5,306✔
2351
        if (!g_quiet) {
2,909✔
2352
          g_slogudpin->info(Logr::Notice, "Question answered from packet cache", "tag", Logging::Loggable(ctag),
132✔
2353
                            "qname", Logging::Loggable(qname), "qtype", Logging::Loggable(QType(qtype)),
132✔
2354
                            "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
132✔
2355
        }
132✔
2356
        match = eventTrace.add(RecEventTrace::AnswerSent);
2,909✔
2357
        struct msghdr msgh{};
2,909✔
2358
        struct iovec iov{};
2,909✔
2359
        cmsgbuf_aligned cbuf{};
2,909✔
2360
        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,909✔
2361
        msgh.msg_control = nullptr;
2,909✔
2362

2363
        if (g_fromtosockets.count(fileDesc) != 0) {
2,909!
2364
          addCMsgSrcAddr(&msgh, &cbuf, &destaddr, 0);
×
2365
        }
×
2366
        int sendErr = sendOnNBSocket(fileDesc, &msgh);
2,909✔
2367
        eventTrace.add(RecEventTrace::AnswerSent, sendErr, false, match);
2,909✔
2368
        traceScope.close(0);
2,909✔
2369
        if (t_protobufServers.servers && logResponse && (!luaconfsLocal->protobufExportConfig.taggedOnly || (pbData && pbData->d_tagged))) {
2,909!
2370
          protobufLogResponse(qname, qtype, dnsheader, luaconfsLocal, pbData, tval, false, source, destination, mappedSource, ednssubnet, uniqueId, requestorId, deviceId, deviceName, meta, eventTrace, otTrace, policyTags);
13✔
2371
        }
13✔
2372

2373
        if (eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_log)) {
2,909!
2374
          g_slogudpin->info(Logr::Info, eventTrace.toString()); // Do we want more fancy logging here?
×
2375
        }
×
2376
        if (sendErr != 0 && g_logCommonErrors) {
2,909!
2377
          g_slogudpin->error(Logr::Error, sendErr, "Sending UDP reply to client failed", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
×
2378
        }
×
2379
        struct timeval now{};
2,909✔
2380
        Utility::gettimeofday(&now, nullptr);
2,909✔
2381
        uint64_t spentUsec = uSec(now - tval);
2,909✔
2382
        t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
2,909✔
2383
        t_Counters.updateSnap(g_regressionTestMode);
2,909✔
2384
        return nullptr;
2,909✔
2385
      }
2,909✔
2386
    }
5,306✔
2387
  }
5,329✔
2388
  catch (const std::exception& e) {
5,329✔
2389
    if (g_logCommonErrors) {
×
2390
      g_slogudpin->error(Logr::Error, e.what(), "Error processing or aging answer packet", "exception", Logging::Loggable("std::exception"));
×
2391
    }
×
2392
    return nullptr;
×
2393
  }
×
2394

2395
  if (t_pdl) {
2,412✔
2396
    bool ipf = t_pdl->ipfilter(source, destination, *dnsheader, eventTrace);
239✔
2397
    if (ipf) {
239✔
2398
      if (!g_quiet) {
2!
2399
        g_slogudpin->info(Logr::Notice, "Dropped question based on policy", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
2✔
2400
      }
2✔
2401
      t_Counters.at(rec::Counter::policyDrops)++;
2✔
2402
      return nullptr;
2✔
2403
    }
2✔
2404
  }
239✔
2405

2406
  if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
2,410✔
2407
    if (!isAllowNotifyForZone(qname)) {
23!
2408
      if (!g_quiet) {
×
2409
        g_slogudpin->info(Logr::Notice, "Dropping UDP NOTIFY, zone not matched by allow-notify-for", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
×
2410
      }
×
2411

2412
      t_Counters.at(rec::Counter::zoneDisallowedNotify)++;
×
2413
      return nullptr;
×
2414
    }
×
2415

2416
    if (!g_quiet) {
23!
2417
      g_slogudpin->info(Logr::Notice, "Got NOTIFY", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr), "qname", Logging::Loggable(qname));
23✔
2418
    }
23✔
2419
    if (!ZoneXFR::notifyZoneTracker(qname)) {
23✔
2420
      // It wasn't an RPZ
2421
      requestWipeCaches(qname);
1✔
2422
    }
1✔
2423

2424
    // the operation will now be treated as a Query, generating
2425
    // a normal response, as the rest of the code does not
2426
    // check dh->opcode, but we need to ensure that the response
2427
    // to this request does not get put into the packet cache
2428
    variable = true;
23✔
2429
  }
23✔
2430

2431
  if (g_multiTasker->numProcesses() >= g_maxMThreads) {
2,410!
2432
    if (!g_quiet) {
×
2433
      g_slogudpin->info(Logr::Notice, "Dropped question, over capacity", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
×
2434
    }
×
2435
    t_Counters.at(rec::Counter::overCapacityDrops)++;
×
2436
    return nullptr;
×
2437
  }
×
2438

2439
  auto comboWriter = std::make_unique<DNSComboWriter>(question, g_now, std::move(policyTags), t_pdl, std::move(data), std::move(records));
2,410✔
2440

2441
  comboWriter->setSocket(fileDesc);
2,410✔
2442
  comboWriter->d_tag = ctag;
2,410✔
2443
  comboWriter->d_qhash = qhash;
2,410✔
2444
  comboWriter->setRemote(fromaddr); // the address the query is coming from
2,410✔
2445
  comboWriter->setSource(source); // the address we assume the query is coming from, might be set by proxy protocol
2,410✔
2446
  comboWriter->setLocal(destaddr); // the address the query was received on
2,410✔
2447
  comboWriter->setDestination(destination); // the address we assume the query is sent to, might be set by proxy protocol
2,410✔
2448
  comboWriter->setMappedSource(mappedSource); // the address we assume the query is coming from. Differs from source if table-based mapping has been applied
2,410✔
2449
  comboWriter->d_tcp = false;
2,410✔
2450
  comboWriter->d_ecsFound = ecsFound;
2,410✔
2451
  comboWriter->d_ecsParsed = ecsParsed;
2,410✔
2452
  comboWriter->d_ednssubnet = ednssubnet;
2,410✔
2453
  comboWriter->d_ttlCap = ttlCap;
2,410✔
2454
  comboWriter->d_variable = variable;
2,410✔
2455
  comboWriter->d_followCNAMERecords = followCNAMEs;
2,410✔
2456
  comboWriter->d_rcode = rcode;
2,410✔
2457
  comboWriter->d_logResponse = logResponse;
2,410✔
2458
  if (t_protobufServers.servers || t_outgoingProtobufServers.servers) {
2,411✔
2459
    comboWriter->d_uuid = uniqueId;
35✔
2460
  }
35✔
2461
  comboWriter->d_requestorId = std::move(requestorId);
2,410✔
2462
  comboWriter->d_deviceId = std::move(deviceId);
2,410✔
2463
  comboWriter->d_deviceName = std::move(deviceName);
2,410✔
2464
  comboWriter->d_kernelTimestamp = tval;
2,410✔
2465
  comboWriter->d_proxyProtocolValues = std::move(proxyProtocolValues);
2,410✔
2466
  comboWriter->d_routingTag = std::move(routingTag);
2,410✔
2467
  comboWriter->d_extendedErrorCode = extendedErrorCode;
2,410✔
2468
  comboWriter->d_extendedErrorExtra = std::move(extendedErrorExtra);
2,410✔
2469
  comboWriter->d_responsePaddingDisabled = responsePaddingDisabled;
2,410✔
2470
  comboWriter->d_meta = std::move(meta);
2,410✔
2471

2472
  traceScope.close(0);
2,410✔
2473
  comboWriter->d_eventTrace = std::move(eventTrace);
2,410✔
2474
  comboWriter->d_otTrace = std::move(otTrace);
2,410✔
2475

2476
  g_multiTasker->makeThread(startDoResolve, (void*)comboWriter.release()); // deletes dc
2,410✔
2477

2478
  return nullptr;
2,410✔
2479
}
2,410✔
2480

2481
static void handleNewUDPQuestion(int fileDesc, FDMultiplexer::funcparam_t& /* var */) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
2482
{
957✔
2483
  const bool proxyActive = t_proxyProtocolACL && !t_proxyProtocolACL->empty();
957!
2484
  static const size_t maxIncomingQuerySize = !proxyActive ? 512 : (512 + g_proxyProtocolMaximumSize);
957✔
2485
  static thread_local std::string data;
957✔
2486
  ComboAddress fromaddr; // the address the query is coming from
957✔
2487
  ComboAddress source; // the address we assume the query is coming from, might be set by proxy protocol
957✔
2488
  ComboAddress destination; // the address we assume the query was sent to, might be set by proxy protocol
957✔
2489
  struct msghdr msgh{};
957✔
2490
  struct iovec iov{};
957✔
2491
  cmsgbuf_aligned cbuf;
957✔
2492
  bool firstQuery = true;
957✔
2493
  std::vector<ProxyProtocolValue> proxyProtocolValues;
957✔
2494
  RecEventTrace eventTrace;
957✔
2495
  pdns::trace::InitialSpanInfo otTrace;
957✔
2496

2497
  for (size_t queriesCounter = 0; queriesCounter < g_maxUDPQueriesPerRound; queriesCounter++) {
6,359!
2498
    bool proxyProto = false;
6,359✔
2499
    proxyProtocolValues.clear();
6,359✔
2500
    data.resize(maxIncomingQuerySize);
6,359✔
2501
    fromaddr.sin6.sin6_family = AF_INET6; // this makes sure fromaddr is big enough
6,359✔
2502
    fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), data.data(), data.size(), &fromaddr);
6,359✔
2503

2504
    if (ssize_t len = recvmsg(fileDesc, &msgh, 0); len >= 0) {
6,359✔
2505
      eventTrace.clear();
5,412✔
2506
      eventTrace.setEnabled(SyncRes::s_event_trace_enabled != 0);
5,412✔
2507
      // eventTrace uses monotonic time, while OpenTelemetry uses absolute time. setEnabled()
2508
      // established the reference point, get an absolute TS as close as possible to the
2509
      // eventTrace start of trace time.
2510
      auto traceTS = pdns::trace::timestamp();
5,412✔
2511
      auto match = eventTrace.add(RecEventTrace::ReqRecv);
5,412✔
2512
      if (SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
5,412✔
2513
        otTrace.clear();
12✔
2514
        otTrace.start_time_unix_nano = traceTS;
12✔
2515
      }
12✔
2516
      firstQuery = false;
5,412✔
2517

2518
      if ((msgh.msg_flags & MSG_TRUNC) != 0) {
5,412!
2519
        t_Counters.at(rec::Counter::truncatedDrops)++;
×
2520
        if (!g_quiet) {
×
2521
          g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr));
×
2522
        }
×
2523
        return;
×
2524
      }
×
2525

2526
      data.resize(static_cast<size_t>(len));
5,412✔
2527

2528
      ComboAddress destaddr; // the address the query was sent to to
5,412✔
2529
      destaddr.reset(); // this makes sure we ignore this address if not explictly set below
5,412✔
2530
      const auto* loc = rplookup(g_listenSocketsAddresses, fileDesc);
5,412✔
2531
      if (HarvestDestinationAddress(&msgh, &destaddr)) {
5,412✔
2532
        // but.. need to get port too
2533
        if (loc != nullptr) {
1!
2534
          destaddr.sin4.sin_port = loc->sin4.sin_port;
1✔
2535
        }
1✔
2536
      }
1✔
2537
      else {
5,411✔
2538
        if (loc != nullptr) {
5,411!
2539
          destaddr = *loc;
5,411✔
2540
        }
5,411✔
2541
        else {
×
2542
          destaddr.sin4.sin_family = fromaddr.sin4.sin_family;
×
2543
          socklen_t slen = destaddr.getSocklen();
×
2544
          getsockname(fileDesc, reinterpret_cast<sockaddr*>(&destaddr), &slen); // if this fails, we're ok with it  // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
×
2545
        }
×
2546
      }
5,411✔
2547
      if (expectProxyProtocol(fromaddr, destaddr)) {
5,412✔
2548
        bool tcp = false;
26✔
2549
        ssize_t used = parseProxyHeader(data, proxyProto, source, destination, tcp, proxyProtocolValues);
26✔
2550
        if (used <= 0) {
26✔
2551
          ++t_Counters.at(rec::Counter::proxyProtocolInvalidCount);
4✔
2552
          if (!g_quiet) {
4!
2553
            g_slogudpin->info(Logr::Error, "Ignoring invalid proxy protocol query", "length", Logging::Loggable(len),
4✔
2554
                              "used", Logging::Loggable(used), "remote", Logging::Loggable(fromaddr));
4✔
2555
          }
4✔
2556
          return;
4✔
2557
        }
4✔
2558
        if (static_cast<size_t>(used) > g_proxyProtocolMaximumSize) {
22✔
2559
          if (g_quiet) {
2!
2560
            g_slogudpin->info(Logr::Error, "Proxy protocol header in UDP packet  is larger than proxy-protocol-maximum-size",
×
2561
                              "used", Logging::Loggable(used), "remote", Logging::Loggable(fromaddr));
×
2562
          }
×
2563
          ++t_Counters.at(rec::Counter::proxyProtocolInvalidCount);
2✔
2564
          return;
2✔
2565
        }
2✔
2566

2567
        data.erase(0, used);
20✔
2568
      }
20✔
2569
      else if (len > 512) {
5,386!
2570
        /* we only allow UDP packets larger than 512 for those with a proxy protocol header */
2571
        t_Counters.at(rec::Counter::truncatedDrops)++;
×
2572
        if (!g_quiet) {
×
2573
          g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr));
×
2574
        }
×
2575
        return;
×
2576
      }
×
2577

2578
      if (data.size() < sizeof(dnsheader)) {
5,406!
2579
        t_Counters.at(rec::Counter::ignoredCount)++;
×
2580
        if (!g_quiet) {
×
2581
          g_slogudpin->info(Logr::Error, "Ignoring too-short query", "length", Logging::Loggable(data.size()),
×
2582
                            "remote", Logging::Loggable(fromaddr));
×
2583
        }
×
2584
        return;
×
2585
      }
×
2586

2587
      if (!proxyProto) {
5,406✔
2588
        source = fromaddr;
5,388✔
2589
      }
5,388✔
2590
      ComboAddress mappedSource = source;
5,406✔
2591
      if (t_proxyMapping) {
5,406✔
2592
        if (const auto* iter = t_proxyMapping->lookup(source)) {
8!
2593
          mappedSource = iter->second.address;
8✔
2594
          ++iter->second.stats.netmaskMatches;
8✔
2595
        }
8✔
2596
      }
8✔
2597
      if (t_remotes) {
5,406!
2598
        t_remotes->push_back(source);
5,406✔
2599
      }
5,406✔
2600

2601
      if (t_allowFrom && !t_allowFrom->match(&mappedSource)) {
5,406!
2602
        if (!g_quiet) {
4!
2603
          g_slogudpin->info(Logr::Error, "Dropping UDP query, address not matched by allow-from", "source", Logging::Loggable(mappedSource));
4✔
2604
        }
4✔
2605

2606
        t_Counters.at(rec::Counter::unauthorizedUDP)++;
4✔
2607
        return;
4✔
2608
      }
4✔
2609

2610
      BOOST_STATIC_ASSERT(offsetof(sockaddr_in, sin_port) == offsetof(sockaddr_in6, sin6_port));
5,402✔
2611
      if (fromaddr.sin4.sin_port == 0) { // also works for IPv6
5,402!
2612
        if (!g_quiet) {
×
2613
          g_slogudpin->info(Logr::Error, "Dropping UDP query can't deal with port 0", "remote", Logging::Loggable(fromaddr));
×
2614
        }
×
2615

2616
        t_Counters.at(rec::Counter::clientParseError)++; // not quite the best place to put it, but needs to go somewhere
×
2617
        return;
×
2618
      }
×
2619

2620
      try {
5,402✔
2621
        const dnsheader_aligned headerdata(data.data());
5,402✔
2622
        const dnsheader* dnsheader = headerdata.get();
5,402✔
2623

2624
        if (dnsheader->qr) {
5,402!
2625
          t_Counters.at(rec::Counter::ignoredCount)++;
×
2626
          if (g_logCommonErrors) {
×
2627
            g_slogudpin->info(Logr::Error, "Ignoring answer on server socket", "remote", Logging::Loggable(fromaddr));
×
2628
          }
×
2629
        }
×
2630
        else if (dnsheader->opcode != static_cast<unsigned>(Opcode::Query) && dnsheader->opcode != static_cast<unsigned>(Opcode::Notify)) {
5,402✔
2631
          t_Counters.at(rec::Counter::ignoredCount)++;
3✔
2632
          if (g_logCommonErrors) {
3!
2633
            g_slogudpin->info(Logr::Error, "Ignoring unsupported opcode server socket", "remote", Logging::Loggable(fromaddr), "opcode", Logging::Loggable(Opcode::to_s(dnsheader->opcode)));
3✔
2634
          }
3✔
2635
        }
3✔
2636
        else if (dnsheader->qdcount == 0U) {
5,399!
2637
          t_Counters.at(rec::Counter::emptyQueriesCount)++;
×
2638
          if (g_logCommonErrors) {
×
2639
            g_slogudpin->info(Logr::Error, "Ignoring empty (qdcount == 0) query on server socket!", "remote", Logging::Loggable(fromaddr));
×
2640
          }
×
2641
        }
×
2642
        else {
5,399✔
2643
          if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
5,399✔
2644
            if (!t_allowNotifyFrom || !t_allowNotifyFrom->match(&mappedSource)) {
23!
2645
              if (!g_quiet) {
×
2646
                g_slogudpin->info(Logr::Error, "Dropping UDP NOTIFY from address not matched by allow-notify-from",
×
2647
                                  "source", Logging::Loggable(mappedSource));
×
2648
              }
×
2649

2650
              t_Counters.at(rec::Counter::sourceDisallowedNotify)++;
×
2651
              return;
×
2652
            }
×
2653
          }
23✔
2654

2655
          struct timeval tval = {0, 0};
5,399✔
2656
          HarvestTimestamp(&msgh, &tval);
5,399✔
2657
          if (!proxyProto) {
5,399✔
2658
            destination = destaddr;
5,385✔
2659
          }
5,385✔
2660

2661
          if (eventTrace.enabled() && !matchOTConditions(t_OTConditions, mappedSource) && SyncRes::eventTraceEnabledOnly(SyncRes::event_trace_to_ot)) {
5,399!
2662
            eventTrace.setEnabled(false);
3✔
2663
          }
3✔
2664
          eventTrace.add(RecEventTrace::ReqRecv, 0, false, match);
5,399✔
2665
          if (RecThreadInfo::weDistributeQueries()) {
5,399✔
2666
            std::string localdata = data;
3,612✔
2667
            distributeAsyncFunction(data, [localdata = std::move(localdata), fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace]() mutable {
3,612✔
2668
              return doProcessUDPQuestion(localdata, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace);
3,569✔
2669
            });
3,569✔
2670
          }
3,612✔
2671
          else {
1,787✔
2672
            doProcessUDPQuestion(data, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace);
1,787✔
2673
          }
1,787✔
2674
        }
5,399✔
2675
      }
5,402✔
2676
      catch (const MOADNSException& mde) {
5,402✔
2677
        t_Counters.at(rec::Counter::clientParseError)++;
×
2678
        if (g_logCommonErrors) {
×
2679
          g_slogudpin->error(Logr::Error, mde.what(), "Unable to parse packet from remote UDP client", "remote", Logging::Loggable(fromaddr), "exception", Logging::Loggable("MOADNSException"));
×
2680
        }
×
2681
      }
×
2682
      catch (const std::runtime_error& e) {
5,402✔
2683
        t_Counters.at(rec::Counter::clientParseError)++;
×
2684
        if (g_logCommonErrors) {
×
2685
          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"));
×
2686
        }
×
2687
      }
×
2688
    }
5,402✔
2689
    else {
947✔
2690
      if (firstQuery && errno == EAGAIN) {
947!
2691
        t_Counters.at(rec::Counter::noPacketError)++;
×
2692
      }
×
2693

2694
      break;
947✔
2695
    }
947✔
2696
  }
6,359✔
2697
  t_Counters.updateSnap(g_regressionTestMode);
947✔
2698
}
947✔
2699

2700
// The two last arguments to makeUDPServerSockets are used for logging purposes only
2701
unsigned int makeUDPServerSockets(deferredAdd_t& deferredAdds, Logr::log_t log, bool doLog, unsigned int instances)
2702
{
353✔
2703
  int one = 1;
353✔
2704
  vector<string> localAddresses;
353✔
2705
  vector<string> logVec;
353✔
2706
  stringtok(localAddresses, ::arg()["local-address"], " ,");
353✔
2707

2708
  if (localAddresses.empty()) {
353!
2709
    throw PDNSException("No local address specified");
×
2710
  }
×
2711

2712
  const uint16_t defaultLocalPort = ::arg().asNum("local-port");
353✔
2713
  const vector<string> defaultVector = {"127.0.0.1", "::1"};
353✔
2714
  const auto configIsDefault = localAddresses == defaultVector;
353✔
2715

2716
  for (const auto& localAddress : localAddresses) {
372✔
2717
    ComboAddress address{localAddress, defaultLocalPort};
372✔
2718
    auto socketFd = FDWrapper(socket(address.sin4.sin_family, SOCK_DGRAM, 0));
372✔
2719
    if (socketFd < 0) {
372!
2720
      throw PDNSException("Making a UDP server socket for resolver: " + stringerror());
×
2721
    }
×
2722

2723
    if (!setSocketTimestamps(socketFd)) {
372!
2724
      log->info(Logr::Warning, "Unable to enable timestamp reporting for socket");
×
2725
    }
×
2726
    if (IsAnyAddress(address)) {
372✔
2727
      if (address.sin4.sin_family == AF_INET) {
2!
2728
        if (setsockopt(socketFd, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)) == 0) { // linux supports this, so why not - might fail on other systems
2!
2729
          g_fromtosockets.insert(socketFd);
2✔
2730
        }
2✔
2731
      }
2✔
2732
#ifdef IPV6_RECVPKTINFO
2✔
2733
      if (address.sin4.sin_family == AF_INET6) {
2!
2734
        if (setsockopt(socketFd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)) == 0) {
×
2735
          g_fromtosockets.insert(socketFd);
×
2736
        }
×
2737
      }
×
2738
#endif
2✔
2739
      if (address.sin6.sin6_family == AF_INET6 && setsockopt(socketFd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)) < 0) {
2!
2740
        int err = errno;
×
2741
        log->error(Logr::Warning, err, "Failed to set IPv6 socket to IPv6 only, continuing anyhow");
×
2742
      }
×
2743
    }
2✔
2744
    if (::arg().mustDo("non-local-bind")) {
372!
2745
      Utility::setBindAny(AF_INET6, socketFd);
×
2746
    }
×
2747

2748
    setCloseOnExec(socketFd);
372✔
2749

2750
    try {
372✔
2751
      setSocketReceiveBuffer(socketFd, 250000);
372✔
2752
    }
372✔
2753
    catch (const std::exception& e) {
372✔
2754
      log->error(Logr::Error, e.what(), "Exception while setting socket buffer size");
×
2755
    }
×
2756

2757
    if (g_reusePort) {
372✔
2758
#if defined(SO_REUSEPORT_LB)
2759
      try {
2760
        SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
2761
      }
2762
      catch (const std::exception& e) {
2763
        throw PDNSException(std::string("SO_REUSEPORT_LB: ") + e.what());
2764
      }
2765
#elif defined(SO_REUSEPORT)
2766
      try {
342✔
2767
        SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT, 1);
342✔
2768
      }
342✔
2769
      catch (const std::exception& e) {
342✔
2770
        throw PDNSException(std::string("SO_REUSEPORT: ") + e.what());
×
2771
      }
×
2772
#endif
342✔
2773
    }
342✔
2774

2775
    try {
372✔
2776
      setSocketIgnorePMTU(socketFd, address.sin4.sin_family);
372✔
2777
    }
372✔
2778
    catch (const std::exception& e) {
372✔
2779
      log->error(Logr::Warning, e.what(), "Failed to set IP_MTU_DISCOVER on UDP server socket");
×
2780
    }
×
2781

2782
    socklen_t socklen = address.getSocklen();
372✔
2783
    if (::bind(socketFd, reinterpret_cast<struct sockaddr*>(&address), socklen) < 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
372!
2784
      int err = errno;
×
2785
      if (!configIsDefault || address != ComboAddress{"::1", defaultLocalPort}) {
×
2786
        throw PDNSException("Resolver binding to server socket on " + address.toStringWithPort() + ": " + stringerror(err));
×
2787
      }
×
2788
      log->info(Logr::Warning, "Cannot listen on this address, skipping", "proto", Logging::Loggable("UDP"), "address", Logging::Loggable(address), "error", Logging::Loggable(stringerror(err)));
×
2789
      continue;
×
2790
    }
×
2791

2792
    setNonBlocking(socketFd);
372✔
2793

2794
    deferredAdds.emplace_back(socketFd, handleNewUDPQuestion);
372✔
2795
    g_listenSocketsAddresses[socketFd] = address; // this is written to only from the startup thread, not from the workers
372✔
2796
    logVec.emplace_back(address.toStringWithPort());
372✔
2797
    socketFd.release(); // to avoid auto-close by FDWrapper
372✔
2798
  }
372✔
2799
  if (doLog) {
353✔
2800
    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));
185✔
2801
  }
185✔
2802
  return localAddresses.size();
353✔
2803
}
353✔
2804

2805
static bool trySendingQueryToWorker(unsigned int target, ThreadMSG* tmsg)
2806
{
3,612✔
2807
  auto& targetInfo = RecThreadInfo::info(target);
3,612✔
2808
  if (!targetInfo.isWorker()) {
3,612!
2809
    g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() tried to assign a query to a non-worker thread");
×
2810
    _exit(1);
×
2811
  }
×
2812

2813
  const auto& tps = targetInfo.getPipes();
3,612✔
2814

2815
  ssize_t written = write(tps.writeQueriesToThread, &tmsg, sizeof(tmsg)); // NOLINT: correct sizeof
3,612✔
2816
  if (written > 0) {
3,612!
2817
    if (static_cast<size_t>(written) != sizeof(tmsg)) { // NOLINT: correct sizeof
3,612!
2818
      delete tmsg; // NOLINT: pointer ownership
×
2819
      unixDie("write to thread pipe returned wrong size or error");
×
2820
    }
×
2821
  }
3,612✔
2822
  else {
×
2823
    int error = errno;
×
2824
    if (error == EAGAIN || error == EWOULDBLOCK) {
×
2825
      return false;
×
2826
    }
×
2827
    delete tmsg; // NOLINT: pointer ownership
×
2828
    unixDie("write to thread pipe returned wrong size or error:" + std::to_string(error));
×
2829
  }
×
2830

2831
  return true;
3,612✔
2832
}
3,612✔
2833

2834
static unsigned int getWorkerLoad(size_t workerIdx)
2835
{
×
2836
  const auto* multiThreader = RecThreadInfo::info(RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + workerIdx).getMT();
×
2837
  if (multiThreader != nullptr) {
×
2838
    return multiThreader->numProcesses();
×
2839
  }
×
2840
  return 0;
×
2841
}
×
2842

2843
static unsigned int selectWorker(unsigned int hash)
2844
{
3,612✔
2845
  assert(RecThreadInfo::numUDPWorkers() != 0); // NOLINT: assert implementation
3,612✔
2846
  if (g_balancingFactor == 0) {
3,612!
2847
    return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + (hash % RecThreadInfo::numUDPWorkers());
3,612✔
2848
  }
3,612✔
2849

2850
  /* we start with one, representing the query we are currently handling */
2851
  double currentLoad = 1;
×
2852
  std::vector<unsigned int> load(RecThreadInfo::numUDPWorkers());
×
2853
  for (size_t idx = 0; idx < RecThreadInfo::numUDPWorkers(); idx++) {
×
2854
    load[idx] = getWorkerLoad(idx);
×
2855
    currentLoad += load[idx];
×
2856
  }
×
2857

2858
  double targetLoad = (currentLoad / RecThreadInfo::numUDPWorkers()) * g_balancingFactor;
×
2859

2860
  unsigned int worker = hash % RecThreadInfo::numUDPWorkers();
×
2861
  /* at least one server has to be at or below the average load */
2862
  if (load[worker] > targetLoad) {
×
2863
    ++t_Counters.at(rec::Counter::rebalancedQueries);
×
2864
    do {
×
2865
      worker = (worker + 1) % RecThreadInfo::numUDPWorkers();
×
2866
    } while (load[worker] > targetLoad);
×
2867
  }
×
2868

2869
  return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + worker;
×
2870
}
3,612✔
2871

2872
// This function is only called by the distributor threads, when pdns-distributes-queries is set
2873
void distributeAsyncFunction(const string& packet, const pipefunc_t& func)
2874
{
3,612✔
2875
  if (!RecThreadInfo::self().isDistributor()) {
3,612!
2876
    g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() has been called by a worker"); // tid will be added
×
2877
    _exit(1);
×
2878
  }
×
2879

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

2889
  ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: pointer ownership
3,612✔
2890
  tmsg->func = func;
3,612✔
2891
  tmsg->wantAnswer = false;
3,612✔
2892

2893
  if (!trySendingQueryToWorker(target, tmsg)) {
3,612!
2894
    /* if this function failed but did not raise an exception, it means that the pipe
2895
       was full, let's try another one */
2896
    unsigned int newTarget = 0;
×
2897
    do {
×
2898
      newTarget = RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + dns_random(RecThreadInfo::numUDPWorkers());
×
2899
    } while (newTarget == target);
×
2900

2901
    if (!trySendingQueryToWorker(newTarget, tmsg)) {
×
2902
      t_Counters.at(rec::Counter::queryPipeFullDrops)++;
×
2903
      delete tmsg; // NOLINT: pointer ownership
×
2904
    }
×
2905
  }
×
2906
  // coverity[leaked_storage]
2907
}
3,612✔
2908

2909
// resend event to everybody chained onto it
2910
static void doResends(MT_t::waiters_t::iterator& iter, const std::shared_ptr<PacketID>& resend, const PacketBuffer& content)
2911
{
9,127✔
2912
  // We close the chain for new entries, since they won't be processed anyway
2913
  iter->key->closed = true;
9,127✔
2914

2915
  if (iter->key->authReqChain.empty()) {
9,127✔
2916
    return;
8,774✔
2917
  }
8,774✔
2918

2919
  auto maxWeight = t_Counters.at(rec::Counter::maxChainWeight);
353✔
2920
  auto weight = iter->key->authReqChain.size() * content.size();
353✔
2921
  if (weight > maxWeight) {
353✔
2922
    t_Counters.at(rec::Counter::maxChainWeight) = weight;
81✔
2923
  }
81✔
2924

2925
  for (auto [fileDesc, qid] : iter->key->authReqChain) {
1,541✔
2926
    auto packetID = std::make_shared<PacketID>(*resend);
1,541✔
2927
    packetID->fd = fileDesc;
1,541✔
2928
    packetID->id = qid;
1,541✔
2929
    g_multiTasker->sendEvent(packetID, &content);
1,541✔
2930
    t_Counters.at(rec::Counter::chainResends)++;
1,541✔
2931
  }
1,541✔
2932
}
353✔
2933

2934
void mthreadSleep(unsigned int jitterMsec)
2935
{
×
2936
  auto neverHappens = std::make_shared<PacketID>();
×
2937
  neverHappens->id = dns_random_uint16();
×
2938
  neverHappens->type = dns_random_uint16();
×
2939
  neverHappens->remote = ComboAddress("100::"); // discard-only
×
2940
  neverHappens->remote.setPort(dns_random_uint16());
×
2941
  neverHappens->fd = -1;
×
2942
  assert(g_multiTasker->waitEvent(neverHappens, nullptr, jitterMsec) != -1); // NOLINT
×
2943
}
×
2944

2945
static bool checkIncomingECSSource(const PacketBuffer& packet, const Netmask& subnet)
2946
{
280✔
2947
  bool foundMatchingECS = false;
280✔
2948

2949
  // We sent out ECS, check if the response has the expected ECS info
2950
  EDNSOptionViewMap ednsOptions;
280✔
2951
  if (slowParseEDNSOptions(packet, ednsOptions)) {
280!
2952
    // check content
2953
    auto option = ednsOptions.find(EDNSOptionCode::ECS);
280✔
2954
    if (option != ednsOptions.end()) {
280✔
2955
      // found an ECS option
2956
      EDNSSubnetOpts ecs;
22✔
2957
      for (const auto& value : option->second.values) {
22!
2958
        if (EDNSSubnetOpts::getFromString(value.content, value.size, &ecs)) {
22!
2959
          if (ecs.getSource() == subnet) {
22✔
2960
            foundMatchingECS = true;
21✔
2961
          }
21✔
2962
        }
22✔
2963
        break; // The RFC isn't clear about multiple ECS options. We chose to handle it like cookies
22✔
2964
               // and only look at the first.
2965
      }
22✔
2966
    }
22✔
2967
  }
280✔
2968
  return foundMatchingECS;
280✔
2969
}
280✔
2970

2971
static void handleUDPServerResponse(int fileDesc, FDMultiplexer::funcparam_t& var)
2972
{
9,127✔
2973
  auto pid = boost::any_cast<std::shared_ptr<PacketID>>(var);
9,127✔
2974
  PacketBuffer packet;
9,127✔
2975
  packet.resize(g_outgoingEDNSBufsize);
9,127✔
2976
  ComboAddress fromaddr;
9,127✔
2977
  socklen_t addrlen = sizeof(fromaddr);
9,127✔
2978

2979
  ssize_t len = recvfrom(fileDesc, &packet.at(0), packet.size(), 0, reinterpret_cast<sockaddr*>(&fromaddr), &addrlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
9,127✔
2980

2981
  const ssize_t signed_sizeof_sdnsheader = sizeof(dnsheader);
9,127✔
2982

2983
  if (len < 0) {
9,127✔
2984
    // len < 0: error on socket
2985
    t_udpclientsocks->returnSocket(fileDesc);
62✔
2986

2987
    PacketBuffer empty;
62✔
2988
    auto iter = g_multiTasker->getWaiters().find(pid);
62✔
2989
    if (iter != g_multiTasker->getWaiters().end()) {
62!
2990
      doResends(iter, pid, empty);
62✔
2991
    }
62✔
2992
    g_multiTasker->sendEvent(pid, &empty); // this denotes error (does retry lookup using other NS)
62✔
2993
    return;
62✔
2994
  }
62✔
2995

2996
  if (len < signed_sizeof_sdnsheader) {
9,065!
2997
    // We have received a packet that cannot be a valid DNS packet, as it has no complete header
2998
    // Drop it, but continue to wait for other packets
2999
    t_Counters.at(rec::Counter::serverParseError)++;
×
3000
    if (g_logCommonErrors) {
×
3001
      g_slogout->info(Logr::Error, "Unable to parse too short packet from remote UDP server", "from", Logging::Loggable(fromaddr));
×
3002
    }
×
3003
    return;
×
3004
  }
×
3005

3006
  // We have at least a full header
3007
  packet.resize(len);
9,065✔
3008
  dnsheader dnsheader{};
9,065✔
3009
  memcpy(&dnsheader, &packet.at(0), sizeof(dnsheader));
9,065✔
3010

3011
  auto pident = std::make_shared<PacketID>();
9,065✔
3012
  pident->remote = fromaddr;
9,065✔
3013
  pident->id = dnsheader.id;
9,065✔
3014
  pident->fd = fileDesc;
9,065✔
3015

3016
  if (!dnsheader.qr && g_logCommonErrors) {
9,065!
3017
    g_slogout->info(Logr::Error, "Not taking data from question on outgoing socket", "from", Logging::Loggable(fromaddr));
×
3018
  }
×
3019

3020
  if (dnsheader.qdcount == 0U || // UPC, Nominum, very old BIND on FormErr, NSD
9,065✔
3021
      dnsheader.qr == 0U) { // one weird server
9,065✔
3022
    pident->domain.clear();
×
3023
    pident->type = 0;
×
3024
  }
×
3025
  else {
9,065✔
3026
    try {
9,065✔
3027
      if (len > signed_sizeof_sdnsheader) {
9,065✔
3028
        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,063✔
3029
      }
9,063✔
3030
      else {
2✔
3031
        // len == sizeof(dnsheader), only header case
3032
        // We will do a full scan search later to see if we can match this reply even without a domain
3033
        pident->domain.clear();
2✔
3034
        pident->type = 0;
2✔
3035
      }
2✔
3036
    }
9,065✔
3037
    catch (std::exception& e) {
9,065✔
3038
      // Parse error, continue waiting for other packets
3039
      t_Counters.at(rec::Counter::serverParseError)++; // won't be fed to lwres.cc, so we have to increment
×
3040
      g_slogudpin->error(Logr::Warning, e.what(), "Error in packet from remote nameserver", "from", Logging::Loggable(fromaddr));
×
3041
      return;
×
3042
    }
×
3043
  }
9,065✔
3044

3045
  if (!pident->domain.empty()) {
9,065✔
3046
    auto iter = g_multiTasker->getWaiters().find(pident);
9,064✔
3047
    if (iter != g_multiTasker->getWaiters().end()) {
9,065✔
3048
      doResends(iter, pident, packet);
9,065✔
3049
    }
9,065✔
3050
  }
9,064✔
3051

3052
retryWithName:
9,065✔
3053

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

3057
    // 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
3058
    for (const auto& d_waiter : g_multiTasker->getWaiters()) {
×
3059
      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) {
×
3060
        /* we are expecting an answer from that exact source, on that exact port (since we are using connected sockets), for that qname/qtype,
3061
           but with a different message ID. That smells like a spoofing attempt. For now we will just increase the counter and will deal with
3062
           that later. */
3063
        d_waiter.key->nearMisses++;
×
3064
      }
×
3065

3066
      // be a bit paranoid here since we're weakening our matching
3067
      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) {
×
3068
        pident->domain = d_waiter.key->domain;
×
3069
        pident->type = d_waiter.key->type;
×
3070
        goto retryWithName; // note that this only passes on an error, lwres will still reject the packet NOLINT(cppcoreguidelines-avoid-goto)
×
3071
      }
×
3072
    }
×
3073
    t_Counters.at(rec::Counter::unexpectedCount)++; // if we made it here, it really is an unexpected answer
×
3074
    if (g_logCommonErrors) {
×
3075
      g_slogudpin->info(Logr::Warning, "Discarding unexpected packet", "from", Logging::Loggable(fromaddr),
×
3076
                        "qname", Logging::Loggable(pident->domain),
×
3077
                        "qtype", Logging::Loggable(QType(pident->type)),
×
3078
                        "waiters", Logging::Loggable(g_multiTasker->getWaiters().size()));
×
3079
    }
×
3080
  }
×
3081
  else if (fileDesc >= 0) {
9,065!
3082
    /* we either found a waiter (1) or encountered an issue (-1), it's up to us to clean the socket anyway */
3083
    t_udpclientsocks->returnSocket(fileDesc);
9,065✔
3084
  }
9,065✔
3085
}
9,065✔
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