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

PowerDNS / pdns / 18843183846

27 Oct 2025 01:46PM UTC coverage: 73.01% (+0.006%) from 73.004%
18843183846

Pull #16376

github

web-flow
Merge 56e38aee1 into 82ea647b4
Pull Request #16376: Bump actions

38260 of 63120 branches covered (60.61%)

Branch coverage included in aggregate %.

127459 of 163861 relevant lines covered (77.78%)

5830359.46 hits per line

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

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

23
#include "rec-main.hh"
24

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

146
  --d_numsocks;
10,098✔
147
}
10,097✔
148

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

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

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

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

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

196
  int err = errno;
10,096✔
197

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

338
  int tmp = errno;
10,083✔
339

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

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

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

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

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

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

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

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

400
    return LWResult::Result::Success;
11,321✔
401
  }
11,321✔
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);
9✔
405
  }
9✔
406

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

410
// the idea is, only do things that depend on the *response* here. Incoming accounting is on incoming.
411
static void updateResponseStats(int res, const ComboAddress& remote, unsigned int packetsize, const DNSName* query, uint16_t qtype)
412
{
6,195✔
413
  if (packetsize > 1000 && t_largeanswerremotes) {
6,195!
414
    t_largeanswerremotes->push_back(remote);
36✔
415
  }
36✔
416
  switch (res) {
6,195✔
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,101✔
430
    t_Counters.at(rec::Counter::noErrors)++;
6,101✔
431
    break;
6,101✔
432
  }
6,195✔
433
}
6,195✔
434

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

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

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

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

473
  rec.getContent()->toPacket(packetWritewr);
9,864✔
474
  if (packetWritewr.size() > static_cast<size_t>(maxAnswerSize)) {
9,864✔
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,861✔
484
}
9,864✔
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,213✔
502
    if (d_dc->d_tcp && !d_dc->d_tcpConnection) {
3,213!
503
      throw std::runtime_error("incoming TCP case without TCP connection");
×
504
    }
×
505
  }
3,213✔
506
  ~RunningResolveGuard()
507
  {
3,214✔
508
    if (!d_handled && d_dc->d_tcp) {
3,214✔
509
      try {
6✔
510
        finishTCPReply(d_dc, false, true);
6✔
511
      }
6✔
512
      catch (const FDMultiplexerException&) {
6✔
513
      }
×
514
    }
6✔
515
  }
3,214✔
516
  void setHandled()
517
  {
795✔
518
    d_handled = true;
795✔
519
  }
795✔
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,881✔
942
  if (hdr.rcode == RCode::NXDomain || (hdr.rcode == RCode::NoError && hdr.ancount == 0 && seenAuthSOA)) {
2,881✔
943
    ttl = std::min(ttl, SyncRes::s_packetcachenegativettl);
357✔
944
  }
357✔
945
  else if ((hdr.rcode != RCode::NoError && hdr.rcode != RCode::NXDomain) || (hdr.ancount == 0 && hdr.nscount == 0)) {
2,524!
946
    ttl = min(ttl, SyncRes::s_packetcacheservfailttl);
85✔
947
  }
85✔
948
  else {
2,439✔
949
    ttl = std::min(ttl, SyncRes::s_packetcachettl);
2,439✔
950
  }
2,439✔
951
  return ttl;
2,881✔
952
}
2,881✔
953

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

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

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

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

1008
      if (!comboWriter->d_responsePaddingDisabled && g_paddingFrom.match(comboWriter->d_remote)) {
1,368✔
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,368✔
1016
        if (option.first == EDNSOptionCode::ECS && g_useIncomingECS && !comboWriter->d_ecsParsed) {
531✔
1017
          comboWriter->d_ecsFound = EDNSSubnetOpts::getFromString(option.second, &comboWriter->d_ednssubnet);
447✔
1018
        }
447✔
1019
        else if (option.first == EDNSOptionCode::NSID) {
84✔
1020
          const static string mode_server_id = ::arg()["server-id"];
2✔
1021
          if (mode_server_id != "disabled" && !mode_server_id.empty() && maxanswersize > (EDNSOptionCodeSize + EDNSOptionLengthSize + mode_server_id.size())) {
2!
1022
            returnedEdnsOptions.emplace_back(EDNSOptionCode::NSID, mode_server_id);
2✔
1023
            variableAnswer = true; // Can't packetcache an answer with NSID
2✔
1024
            maxanswersize -= EDNSOptionCodeSize + EDNSOptionLengthSize + mode_server_id.size();
2✔
1025
          }
2✔
1026
        }
2✔
1027
        else if (paddingAllowed && !addPaddingToResponse && g_paddingMode == PaddingMode::PaddedQueries && option.first == EDNSOptionCode::PADDING) {
82!
1028
          addPaddingToResponse = true;
1✔
1029
        }
1✔
1030
      }
531✔
1031
    }
1,368✔
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,213✔
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,213✔
1042
    vector<DNSRecord> ret;
3,213✔
1043
    vector<uint8_t> packet;
3,213✔
1044

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

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

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

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

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

1091
      // Does the requestor want DNSSEC records?
1092
      if ((edo.d_extFlags & EDNSOpts::DNSSECOK) != 0) {
3,185✔
1093
        DNSSECOK = true;
1,184✔
1094
        t_Counters.at(rec::Counter::dnssecQueries)++;
1,184✔
1095
      }
1,184✔
1096
      if (comboWriter->d_mdp.d_header.cd) {
3,185✔
1097
        /* Per rfc6840 section 5.9, "When processing a request with
1098
           the Checking Disabled (CD) bit set, a resolver SHOULD attempt
1099
           to return all response data, even data that has failed DNSSEC
1100
           validation. */
1101
        ++t_Counters.at(rec::Counter::dnssecCheckDisabledQueries);
313✔
1102
      }
313✔
1103
      if (comboWriter->d_mdp.d_header.ad) {
3,185✔
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);
784✔
1110
      }
784✔
1111
    }
3,185✔
1112
    else {
2,147,483,673✔
1113
      // Ignore the client-set CD flag
1114
      packetWriter.getHeader()->cd = 0;
2,147,483,673✔
1115
    }
2,147,483,673✔
1116
    resolver.setDNSSECValidationRequested(g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode == DNSSECMode::ValidateForLog || ((comboWriter->d_mdp.d_header.ad || DNSSECOK) && g_dnssecmode == DNSSECMode::Process));
3,213!
1117

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

1124
    bool useMapped = true;
3,213✔
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,213!
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);
2,147,485,211✔
1143

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1432
          string x_marker;
2,563✔
1433
          std::shared_ptr<Logr::Logger> log;
2,563✔
1434
          if (resolver.doLog() || vStateIsBogus(state)) {
2,563!
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,563✔
1447
            if (resolver.doLog()) {
776✔
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) {
776✔
1453
              packetWriter.getHeader()->ad = 1;
741✔
1454
            }
741✔
1455
          }
776✔
1456
          else if (state == vState::Insecure) {
1,787✔
1457
            if (resolver.doLog()) {
1,670✔
1458
              log->info(Logr::Info, "Validates as Insecure");
50✔
1459
            }
50✔
1460

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

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

1518
      bool needCommit = false;
3,129✔
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;
116✔
1522
        }
116✔
1523

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

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

1540
        if (t_protobufServers.servers) {
9,861✔
1541
          // Max size is 64k, but we're conservative here, as other fields are added after the answers have been added
1542
          // If a single answer causes a too big protobuf message, it will be dropped by queueData()
1543
          // But note addRR has code to prevent that
1544
          if (pbMessage.size() < std::numeric_limits<uint16_t>::max() / 2) {
36!
1545
            pbMessage.addRR(record, luaconfsLocal->protobufExportConfig.exportTypes, udr);
36✔
1546
          }
36✔
1547
        }
36✔
1548
      }
9,861✔
1549
      if (needCommit) {
3,129✔
1550
        packetWriter.commit();
3,089✔
1551
      }
3,089✔
1552
#ifdef NOD_ENABLED
3,129✔
1553
#ifdef HAVE_FSTRM
3,129✔
1554
      if (hasUDR) {
3,129✔
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,129✔
1574
#endif // NOD_ENABLED
3,129✔
1575
    }
3,129✔
1576
  sendit:;
3,201✔
1577

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

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

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

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

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

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

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

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

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

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

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

1805
    resolver.d_eventTrace.add(RecEventTrace::AnswerSent, 0, false, match);
3,201✔
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,201!
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) {
28!
1811
        pbMessage.setQueryTime(comboWriter->d_kernelTimestamp.tv_sec, comboWriter->d_kernelTimestamp.tv_usec);
×
1812
      }
×
1813
      else {
28✔
1814
        pbMessage.setQueryTime(comboWriter->d_now.tv_sec, comboWriter->d_now.tv_usec);
28✔
1815
      }
28✔
1816
      pbMessage.setMessageIdentity(comboWriter->d_uuid);
28✔
1817
      pbMessage.setSocketProtocol(comboWriter->d_tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP);
28✔
1818

1819
      if (!luaconfsLocal->protobufExportConfig.logMappedFrom) {
28✔
1820
        pbMessage.setSocketFamily(comboWriter->d_source.sin4.sin_family);
26✔
1821
        Netmask requestorNM(comboWriter->d_source, comboWriter->d_source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
26!
1822
        ComboAddress requestor = requestorNM.getMaskedNetwork();
26✔
1823
        pbMessage.setFrom(requestor);
26✔
1824
        pbMessage.setFromPort(comboWriter->d_source.getPort());
26✔
1825
      }
26✔
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);
28✔
1835
      pbMessage.setId(comboWriter->d_mdp.d_header.id);
28✔
1836

1837
      pbMessage.setTime();
28✔
1838
      pbMessage.setEDNSSubnet(comboWriter->d_ednssubnet.getSource(), comboWriter->d_ednssubnet.getSource().isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
28!
1839
      pbMessage.setRequestorId(dnsQuestion.requestorId);
28✔
1840
      pbMessage.setDeviceId(dnsQuestion.deviceId);
28✔
1841
      pbMessage.setDeviceName(dnsQuestion.deviceName);
28✔
1842
      pbMessage.setToPort(comboWriter->d_destination.getPort());
28✔
1843
      pbMessage.addPolicyTags(comboWriter->d_gettagPolicyTags);
28✔
1844
      pbMessage.setWorkerId(RecThreadInfo::thread_local_id());
28✔
1845
      pbMessage.setPacketCacheHit(false);
28✔
1846
      pbMessage.setOutgoingQueries(resolver.d_outqueries);
28✔
1847
      for (const auto& metaValue : dnsQuestion.meta) {
28✔
1848
        pbMessage.setMeta(metaValue.first, metaValue.second.stringVal, metaValue.second.intVal);
2✔
1849
      }
2✔
1850
#ifdef NOD_ENABLED
28✔
1851
      if (g_nodEnabled) {
28!
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 */
28✔
1861
      if (resolver.d_eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_pb)) {
28!
1862
        pbMessage.addEvents(resolver.d_eventTrace);
×
1863
      }
×
1864
      if (resolver.d_eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
28!
1865
        auto otTrace = pdns::trace::TracesData::boilerPlate("rec", resolver.d_eventTrace.convertToOT(resolver.d_otTrace), {
2✔
1866
                                                                                                                            {"query.qname", {comboWriter->d_mdp.d_qname.toLogString()}},
2✔
1867
                                                                                                                            {"query.qtype", {QType(comboWriter->d_mdp.d_qtype).toString()}},
2✔
1868
                                                                                                                          },
2✔
1869
                                                            SyncRes::s_serverID);
2✔
1870
        string otData = otTrace.encode();
2✔
1871
        pbMessage.setOpenTelemetryData(otData);
2✔
1872
      }
2✔
1873
      // Currently only set if an OT trace is generated
1874
      if (resolver.d_otTrace.trace_id != pdns::trace::s_emptyTraceID) {
28✔
1875
        pbMessage.setOpenTelemetryTraceID(resolver.d_otTrace.trace_id);
2✔
1876
      }
2✔
1877
      if (comboWriter->d_logResponse) {
28✔
1878
        protobufLogResponse(pbMessage);
25✔
1879
      }
25✔
1880
    }
28✔
1881

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

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

1908
    if (comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Query)) {
3,201✔
1909
      if (resolver.d_outqueries != 0 || resolver.d_throttledqueries != 0 || resolver.d_authzonequeries != 0) {
3,177✔
1910
        g_recCache->incCacheMisses();
2,686✔
1911
      }
2,686✔
1912
      else {
491✔
1913
        g_recCache->incCacheHits();
491✔
1914
      }
491✔
1915
    }
3,177✔
1916

1917
    t_Counters.at(rec::Histogram::answers)(spentUsec);
3,201✔
1918
    t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
3,201✔
1919

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

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

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

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

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

1964
  runTaskOnce(g_logCommonErrors);
3,201✔
1965

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

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

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

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

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

2010
    if (pos >= questionLen) {
115✔
2011
      return;
98✔
2012
    }
98✔
2013

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

2045
    pos += ntohs(drh->d_clen);
17✔
2046
  }
17✔
2047
}
157✔
2048

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

2062
  if (qnameParsed) {
6,037✔
2063
    cacheHit = g_packetCache->getResponsePacket(tag, data, qname, qtype, qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
133✔
2064
  }
133✔
2065
  else {
5,904✔
2066
    cacheHit = g_packetCache->getResponsePacket(tag, data, qname, &qtype, &qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
5,904✔
2067
  }
5,904✔
2068

2069
  if (cacheHit) {
6,037✔
2070
    if (vStateIsBogus(valState)) {
2,992!
2071
      if (t_bogusremotes) {
×
2072
        t_bogusremotes->push_back(source);
×
2073
      }
×
2074
      if (t_bogusqueryring) {
×
2075
        t_bogusqueryring->push_back({qname, qtype});
×
2076
      }
×
2077
    }
×
2078

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

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

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

2108
  return cacheHit;
6,037✔
2109
}
6,181✔
2110

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

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

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

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

2147
// fromaddr: the address the query is coming from
2148
// destaddr: the address the query was received on
2149
// source: the address we assume the query is coming from, might be set by proxy protocol
2150
// destination: the address we assume the query was sent to, might be set by proxy protocol
2151
// mappedSource: the address we assume the query is coming from. Differs from source if table based mapping has been applied
2152
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
2153
{
5,379✔
2154
  auto newParent = eventTrace.add(RecEventTrace::ProcessUDP);
5,379✔
2155
  auto oldParent = eventTrace.setParent(newParent);
5,379✔
2156
  RecEventTrace::EventScope traceScope(oldParent, eventTrace);
5,379✔
2157
  RecThreadInfo::self().incNumberOfDistributedQueries();
5,379✔
2158
  gettimeofday(&g_now, nullptr);
5,379✔
2159
  if (tval.tv_sec != 0) {
5,389✔
2160
    struct timeval diff = g_now - tval;
5,383✔
2161
    double delta = (static_cast<double>(diff.tv_sec) * 1000 + static_cast<double>(diff.tv_usec) / 1000.0);
5,383✔
2162

2163
    if (delta > 1000.0) {
5,383✔
2164
      t_Counters.at(rec::Counter::tooOldDrops)++;
27✔
2165
      return nullptr;
27✔
2166
    }
27✔
2167
  }
5,383✔
2168

2169
  ++t_Counters.at(rec::Counter::qcounter);
5,352✔
2170

2171
  if (fromaddr.sin4.sin_family == AF_INET6) {
5,352✔
2172
    t_Counters.at(rec::Counter::ipv6qcounter)++;
2✔
2173
  }
2✔
2174

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

2234
    // We do not have a SyncRes specific Lua context at this point yet, so ok to use t_pdl
2235
    if (needEDNSParse || (t_pdl && (t_pdl->hasGettagFunc() || t_pdl->hasGettagFFIFunc())) || dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
5,359✔
2236
      try {
112✔
2237
        auto parseMatch = eventTrace.add(RecEventTrace::PacketParse);
112✔
2238
        EDNSOptionViewMap ednsOptions;
112✔
2239

2240
        ecsFound = false;
112✔
2241

2242
        getQNameAndSubnet(question, &qname, &qtype, &qclass,
112✔
2243
                          ecsFound, &ednssubnet,
112✔
2244
                          (g_gettagNeedsEDNSOptions || SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) ? &ednsOptions : nullptr,
112✔
2245
                          ednsVersion);
112✔
2246

2247
        eventTrace.add(RecEventTrace::PacketParse, 0, false, parseMatch);
112✔
2248
        qnameParsed = true;
112✔
2249
        ecsParsed = true;
112✔
2250

2251
        if (SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
112✔
2252
          pdns::trace::extractOTraceIDs(ednsOptions, otTrace);
3✔
2253
        }
3✔
2254

2255
        if (t_pdl) {
112✔
2256
          try {
86✔
2257
            if (t_pdl->hasGettagFFIFunc()) {
86✔
2258
              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✔
2259

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

2285
    RecursorPacketCache::OptPBData pbData{boost::none};
5,352✔
2286
    if (t_protobufServers.servers) {
5,352✔
2287
      if (logQuery && !(luaconfsLocal->protobufExportConfig.taggedOnly && policyTags.empty())) {
33✔
2288
        protobufLogQuery(luaconfsLocal, uniqueId, source, destination, mappedSource, ednssubnet.getSource(), false, question.size(), qname, qtype, qclass, policyTags, requestorId, deviceId, deviceName, meta, ednsVersion, *dnsheader, otTrace.trace_id);
24✔
2289
      }
24✔
2290
    }
33✔
2291

2292
    if (ctag == 0 && !responsePaddingDisabled && g_paddingFrom.match(fromaddr)) {
5,358✔
2293
      ctag = g_paddingTag;
10✔
2294
    }
10✔
2295

2296
    if (dnsheader->opcode == static_cast<unsigned>(Opcode::Query)) {
5,358✔
2297
      /* It might seem like a good idea to skip the packet cache lookup if we know that the answer is not cacheable,
2298
         but it means that the hash would not be computed. If some script decides at a later time to mark back the answer
2299
         as cacheable we would cache it with a wrong tag, so better safe than sorry. */
2300
      auto match = eventTrace.add(RecEventTrace::PCacheCheck);
5,335✔
2301
      bool cacheHit = checkForCacheHit(qnameParsed, ctag, question, qname, qtype, qclass, g_now, response, qhash, pbData, false, source, mappedSource);
5,335✔
2302
      eventTrace.add(RecEventTrace::PCacheCheck, cacheHit, false, match);
5,335✔
2303
      if (cacheHit) {
5,335✔
2304
        if (!g_quiet) {
2,944✔
2305
          g_slogudpin->info(Logr::Notice, "Question answered from packet cache", "tag", Logging::Loggable(ctag),
125✔
2306
                            "qname", Logging::Loggable(qname), "qtype", Logging::Loggable(QType(qtype)),
125✔
2307
                            "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
125✔
2308
        }
125✔
2309
        match = eventTrace.add(RecEventTrace::AnswerSent);
2,944✔
2310
        struct msghdr msgh{};
2,944✔
2311
        struct iovec iov{};
2,944✔
2312
        cmsgbuf_aligned cbuf{};
2,944✔
2313
        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,944✔
2314
        msgh.msg_control = nullptr;
2,944✔
2315

2316
        if (g_fromtosockets.count(fileDesc) != 0) {
2,944!
2317
          addCMsgSrcAddr(&msgh, &cbuf, &destaddr, 0);
×
2318
        }
×
2319
        int sendErr = sendOnNBSocket(fileDesc, &msgh);
2,944✔
2320
        eventTrace.add(RecEventTrace::AnswerSent, sendErr, false, match);
2,944✔
2321
        traceScope.close(0);
2,944✔
2322
        if (t_protobufServers.servers && logResponse && (!luaconfsLocal->protobufExportConfig.taggedOnly || (pbData && pbData->d_tagged))) {
2,944!
2323
          protobufLogResponse(qname, qtype, dnsheader, luaconfsLocal, pbData, tval, false, source, destination, mappedSource, ednssubnet, uniqueId, requestorId, deviceId, deviceName, meta, eventTrace, otTrace, policyTags);
6✔
2324
        }
6✔
2325

2326
        if (eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_log)) {
2,944!
2327
          g_slogudpin->info(Logr::Info, eventTrace.toString()); // Do we want more fancy logging here?
×
2328
        }
×
2329
        if (sendErr != 0 && g_logCommonErrors) {
2,944!
2330
          g_slogudpin->error(Logr::Error, sendErr, "Sending UDP reply to client failed", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
×
2331
        }
×
2332
        struct timeval now{};
2,944✔
2333
        Utility::gettimeofday(&now, nullptr);
2,944✔
2334
        uint64_t spentUsec = uSec(now - tval);
2,944✔
2335
        t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
2,944✔
2336
        t_Counters.updateSnap(g_regressionTestMode);
2,944✔
2337
        return nullptr;
2,944✔
2338
      }
2,944✔
2339
    }
5,335✔
2340
  }
5,352✔
2341
  catch (const std::exception& e) {
5,352✔
2342
    if (g_logCommonErrors) {
×
2343
      g_slogudpin->error(Logr::Error, e.what(), "Error processing or aging answer packet", "exception", Logging::Loggable("std::exception"));
×
2344
    }
×
2345
    return nullptr;
×
2346
  }
×
2347

2348
  if (t_pdl) {
2,414✔
2349
    bool ipf = t_pdl->ipfilter(source, destination, *dnsheader, eventTrace);
239✔
2350
    if (ipf) {
239✔
2351
      if (!g_quiet) {
2!
2352
        g_slogudpin->info(Logr::Notice, "Dropped question based on policy", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
2✔
2353
      }
2✔
2354
      t_Counters.at(rec::Counter::policyDrops)++;
2✔
2355
      return nullptr;
2✔
2356
    }
2✔
2357
  }
239✔
2358

2359
  if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
2,412✔
2360
    if (!isAllowNotifyForZone(qname)) {
23!
2361
      if (!g_quiet) {
×
2362
        g_slogudpin->info(Logr::Notice, "Dropping UDP NOTIFY, zone not matched by allow-notify-for", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
×
2363
      }
×
2364

2365
      t_Counters.at(rec::Counter::zoneDisallowedNotify)++;
×
2366
      return nullptr;
×
2367
    }
×
2368

2369
    if (!g_quiet) {
23!
2370
      g_slogudpin->info(Logr::Notice, "Got NOTIFY", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr), "qname", Logging::Loggable(qname));
23✔
2371
    }
23✔
2372
    if (!ZoneXFR::notifyZoneTracker(qname)) {
23✔
2373
      // It wasn't an RPZ
2374
      requestWipeCaches(qname);
1✔
2375
    }
1✔
2376

2377
    // the operation will now be treated as a Query, generating
2378
    // a normal response, as the rest of the code does not
2379
    // check dh->opcode, but we need to ensure that the response
2380
    // to this request does not get put into the packet cache
2381
    variable = true;
23✔
2382
  }
23✔
2383

2384
  if (g_multiTasker->numProcesses() >= g_maxMThreads) {
2,412!
2385
    if (!g_quiet) {
×
2386
      g_slogudpin->info(Logr::Notice, "Dropped question, over capacity", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
×
2387
    }
×
2388
    t_Counters.at(rec::Counter::overCapacityDrops)++;
×
2389
    return nullptr;
×
2390
  }
×
2391

2392
  auto comboWriter = std::make_unique<DNSComboWriter>(question, g_now, std::move(policyTags), t_pdl, std::move(data), std::move(records));
2,412✔
2393

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

2425
  traceScope.close(0);
2,412✔
2426
  comboWriter->d_eventTrace = std::move(eventTrace);
2,412✔
2427
  comboWriter->d_otTrace = std::move(otTrace);
2,412✔
2428

2429
  g_multiTasker->makeThread(startDoResolve, (void*)comboWriter.release()); // deletes dc
2,412✔
2430

2431
  return nullptr;
2,412✔
2432
}
2,412✔
2433

2434
static void handleNewUDPQuestion(int fileDesc, FDMultiplexer::funcparam_t& /* var */) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
2435
{
949✔
2436
  const bool proxyActive = t_proxyProtocolACL && !t_proxyProtocolACL->empty();
949!
2437
  static const size_t maxIncomingQuerySize = !proxyActive ? 512 : (512 + g_proxyProtocolMaximumSize);
949✔
2438
  static thread_local std::string data;
949✔
2439
  ComboAddress fromaddr; // the address the query is coming from
949✔
2440
  ComboAddress source; // the address we assume the query is coming from, might be set by proxy protocol
949✔
2441
  ComboAddress destination; // the address we assume the query was sent to, might be set by proxy protocol
949✔
2442
  struct msghdr msgh{};
949✔
2443
  struct iovec iov{};
949✔
2444
  cmsgbuf_aligned cbuf;
949✔
2445
  bool firstQuery = true;
949✔
2446
  std::vector<ProxyProtocolValue> proxyProtocolValues;
949✔
2447
  RecEventTrace eventTrace;
949✔
2448
  pdns::trace::InitialSpanInfo otTrace;
949✔
2449

2450
  for (size_t queriesCounter = 0; queriesCounter < g_maxUDPQueriesPerRound; queriesCounter++) {
6,342!
2451
    bool proxyProto = false;
6,342✔
2452
    proxyProtocolValues.clear();
6,342✔
2453
    data.resize(maxIncomingQuerySize);
6,342✔
2454
    fromaddr.sin6.sin6_family = AF_INET6; // this makes sure fromaddr is big enough
6,342✔
2455
    fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), data.data(), data.size(), &fromaddr);
6,342✔
2456

2457
    if (ssize_t len = recvmsg(fileDesc, &msgh, 0); len >= 0) {
6,342✔
2458
      eventTrace.clear();
5,403✔
2459
      eventTrace.setEnabled(SyncRes::s_event_trace_enabled != 0);
5,403✔
2460
      // eventTrace uses monotonic time, while OpenTelemetry uses absolute time. setEnabled()
2461
      // established the reference point, get an absolute TS as close as possible to the
2462
      // eventTrace start of trace time.
2463
      auto traceTS = pdns::trace::timestamp();
5,403✔
2464
      auto match = eventTrace.add(RecEventTrace::ReqRecv);
5,403✔
2465
      if (SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
5,403✔
2466
        otTrace.clear();
3✔
2467
        otTrace.start_time_unix_nano = traceTS;
3✔
2468
      }
3✔
2469
      firstQuery = false;
5,403✔
2470

2471
      if ((msgh.msg_flags & MSG_TRUNC) != 0) {
5,403!
2472
        t_Counters.at(rec::Counter::truncatedDrops)++;
×
2473
        if (!g_quiet) {
×
2474
          g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr));
×
2475
        }
×
2476
        return;
×
2477
      }
×
2478

2479
      data.resize(static_cast<size_t>(len));
5,403✔
2480

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

2520
        data.erase(0, used);
20✔
2521
      }
20✔
2522
      else if (len > 512) {
5,377!
2523
        /* we only allow UDP packets larger than 512 for those with a proxy protocol header */
2524
        t_Counters.at(rec::Counter::truncatedDrops)++;
×
2525
        if (!g_quiet) {
×
2526
          g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr));
×
2527
        }
×
2528
        return;
×
2529
      }
×
2530

2531
      if (data.size() < sizeof(dnsheader)) {
5,397!
2532
        t_Counters.at(rec::Counter::ignoredCount)++;
×
2533
        if (!g_quiet) {
×
2534
          g_slogudpin->info(Logr::Error, "Ignoring too-short query", "length", Logging::Loggable(data.size()),
×
2535
                            "remote", Logging::Loggable(fromaddr));
×
2536
        }
×
2537
        return;
×
2538
      }
×
2539

2540
      if (!proxyProto) {
5,397✔
2541
        source = fromaddr;
5,379✔
2542
      }
5,379✔
2543
      ComboAddress mappedSource = source;
5,397✔
2544
      if (t_proxyMapping) {
5,397✔
2545
        if (const auto* iter = t_proxyMapping->lookup(source)) {
8!
2546
          mappedSource = iter->second.address;
8✔
2547
          ++iter->second.stats.netmaskMatches;
8✔
2548
        }
8✔
2549
      }
8✔
2550
      if (t_remotes) {
5,397!
2551
        t_remotes->push_back(source);
5,397✔
2552
      }
5,397✔
2553

2554
      if (t_allowFrom && !t_allowFrom->match(&mappedSource)) {
5,397!
2555
        if (!g_quiet) {
4!
2556
          g_slogudpin->info(Logr::Error, "Dropping UDP query, address not matched by allow-from", "source", Logging::Loggable(mappedSource));
4✔
2557
        }
4✔
2558

2559
        t_Counters.at(rec::Counter::unauthorizedUDP)++;
4✔
2560
        return;
4✔
2561
      }
4✔
2562

2563
      BOOST_STATIC_ASSERT(offsetof(sockaddr_in, sin_port) == offsetof(sockaddr_in6, sin6_port));
5,393✔
2564
      if (fromaddr.sin4.sin_port == 0) { // also works for IPv6
5,393!
2565
        if (!g_quiet) {
×
2566
          g_slogudpin->info(Logr::Error, "Dropping UDP query can't deal with port 0", "remote", Logging::Loggable(fromaddr));
×
2567
        }
×
2568

2569
        t_Counters.at(rec::Counter::clientParseError)++; // not quite the best place to put it, but needs to go somewhere
×
2570
        return;
×
2571
      }
×
2572

2573
      try {
5,393✔
2574
        const dnsheader_aligned headerdata(data.data());
5,393✔
2575
        const dnsheader* dnsheader = headerdata.get();
5,393✔
2576

2577
        if (dnsheader->qr) {
5,393!
2578
          t_Counters.at(rec::Counter::ignoredCount)++;
×
2579
          if (g_logCommonErrors) {
×
2580
            g_slogudpin->info(Logr::Error, "Ignoring answer on server socket", "remote", Logging::Loggable(fromaddr));
×
2581
          }
×
2582
        }
×
2583
        else if (dnsheader->opcode != static_cast<unsigned>(Opcode::Query) && dnsheader->opcode != static_cast<unsigned>(Opcode::Notify)) {
5,393✔
2584
          t_Counters.at(rec::Counter::ignoredCount)++;
3✔
2585
          if (g_logCommonErrors) {
3!
2586
            g_slogudpin->info(Logr::Error, "Ignoring unsupported opcode server socket", "remote", Logging::Loggable(fromaddr), "opcode", Logging::Loggable(Opcode::to_s(dnsheader->opcode)));
3✔
2587
          }
3✔
2588
        }
3✔
2589
        else if (dnsheader->qdcount == 0U) {
5,390!
2590
          t_Counters.at(rec::Counter::emptyQueriesCount)++;
×
2591
          if (g_logCommonErrors) {
×
2592
            g_slogudpin->info(Logr::Error, "Ignoring empty (qdcount == 0) query on server socket!", "remote", Logging::Loggable(fromaddr));
×
2593
          }
×
2594
        }
×
2595
        else {
5,390✔
2596
          if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
5,390✔
2597
            if (!t_allowNotifyFrom || !t_allowNotifyFrom->match(&mappedSource)) {
23!
2598
              if (!g_quiet) {
×
2599
                g_slogudpin->info(Logr::Error, "Dropping UDP NOTIFY from address not matched by allow-notify-from",
×
2600
                                  "source", Logging::Loggable(mappedSource));
×
2601
              }
×
2602

2603
              t_Counters.at(rec::Counter::sourceDisallowedNotify)++;
×
2604
              return;
×
2605
            }
×
2606
          }
23✔
2607

2608
          struct timeval tval = {0, 0};
5,390✔
2609
          HarvestTimestamp(&msgh, &tval);
5,390✔
2610
          if (!proxyProto) {
5,390✔
2611
            destination = destaddr;
5,376✔
2612
          }
5,376✔
2613

2614
          eventTrace.add(RecEventTrace::ReqRecv, 0, false, match);
5,390✔
2615
          if (RecThreadInfo::weDistributeQueries()) {
5,390✔
2616
            std::string localdata = data;
3,612✔
2617
            distributeAsyncFunction(data, [localdata = std::move(localdata), fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace]() mutable {
3,612✔
2618
              return doProcessUDPQuestion(localdata, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace);
3,595✔
2619
            });
3,595✔
2620
          }
3,612✔
2621
          else {
1,778✔
2622
            doProcessUDPQuestion(data, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace);
1,778✔
2623
          }
1,778✔
2624
        }
5,390✔
2625
      }
5,393✔
2626
      catch (const MOADNSException& mde) {
5,393✔
2627
        t_Counters.at(rec::Counter::clientParseError)++;
×
2628
        if (g_logCommonErrors) {
×
2629
          g_slogudpin->error(Logr::Error, mde.what(), "Unable to parse packet from remote UDP client", "remote", Logging::Loggable(fromaddr), "exception", Logging::Loggable("MOADNSException"));
×
2630
        }
×
2631
      }
×
2632
      catch (const std::runtime_error& e) {
5,393✔
2633
        t_Counters.at(rec::Counter::clientParseError)++;
×
2634
        if (g_logCommonErrors) {
×
2635
          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"));
×
2636
        }
×
2637
      }
×
2638
    }
5,393✔
2639
    else {
939✔
2640
      if (firstQuery && errno == EAGAIN) {
939!
2641
        t_Counters.at(rec::Counter::noPacketError)++;
×
2642
      }
×
2643

2644
      break;
939✔
2645
    }
939✔
2646
  }
6,342✔
2647
  t_Counters.updateSnap(g_regressionTestMode);
939✔
2648
}
939✔
2649

2650
// The two last arguments to makeUDPServerSockets are used for logging purposes only
2651
unsigned int makeUDPServerSockets(deferredAdd_t& deferredAdds, Logr::log_t log, bool doLog, unsigned int instances)
2652
{
353✔
2653
  int one = 1;
353✔
2654
  vector<string> localAddresses;
353✔
2655
  vector<string> logVec;
353✔
2656
  stringtok(localAddresses, ::arg()["local-address"], " ,");
353✔
2657

2658
  if (localAddresses.empty()) {
353!
2659
    throw PDNSException("No local address specified");
×
2660
  }
×
2661

2662
  const uint16_t defaultLocalPort = ::arg().asNum("local-port");
353✔
2663
  const vector<string> defaultVector = {"127.0.0.1", "::1"};
353✔
2664
  const auto configIsDefault = localAddresses == defaultVector;
353✔
2665

2666
  for (const auto& localAddress : localAddresses) {
372✔
2667
    ComboAddress address{localAddress, defaultLocalPort};
372✔
2668
    auto socketFd = FDWrapper(socket(address.sin4.sin_family, SOCK_DGRAM, 0));
372✔
2669
    if (socketFd < 0) {
372!
2670
      throw PDNSException("Making a UDP server socket for resolver: " + stringerror());
×
2671
    }
×
2672

2673
    if (!setSocketTimestamps(socketFd)) {
372!
2674
      log->info(Logr::Warning, "Unable to enable timestamp reporting for socket");
×
2675
    }
×
2676
    if (IsAnyAddress(address)) {
372✔
2677
      if (address.sin4.sin_family == AF_INET) {
2!
2678
        if (setsockopt(socketFd, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)) == 0) { // linux supports this, so why not - might fail on other systems
2!
2679
          g_fromtosockets.insert(socketFd);
2✔
2680
        }
2✔
2681
      }
2✔
2682
#ifdef IPV6_RECVPKTINFO
2✔
2683
      if (address.sin4.sin_family == AF_INET6) {
2!
2684
        if (setsockopt(socketFd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)) == 0) {
×
2685
          g_fromtosockets.insert(socketFd);
×
2686
        }
×
2687
      }
×
2688
#endif
2✔
2689
      if (address.sin6.sin6_family == AF_INET6 && setsockopt(socketFd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)) < 0) {
2!
2690
        int err = errno;
×
2691
        log->error(Logr::Warning, err, "Failed to set IPv6 socket to IPv6 only, continuing anyhow");
×
2692
      }
×
2693
    }
2✔
2694
    if (::arg().mustDo("non-local-bind")) {
372!
2695
      Utility::setBindAny(AF_INET6, socketFd);
×
2696
    }
×
2697

2698
    setCloseOnExec(socketFd);
372✔
2699

2700
    try {
372✔
2701
      setSocketReceiveBuffer(socketFd, 250000);
372✔
2702
    }
372✔
2703
    catch (const std::exception& e) {
372✔
2704
      log->error(Logr::Error, e.what(), "Exception while setting socket buffer size");
×
2705
    }
×
2706

2707
    if (g_reusePort) {
372✔
2708
#if defined(SO_REUSEPORT_LB)
2709
      try {
2710
        SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
2711
      }
2712
      catch (const std::exception& e) {
2713
        throw PDNSException(std::string("SO_REUSEPORT_LB: ") + e.what());
2714
      }
2715
#elif defined(SO_REUSEPORT)
2716
      try {
342✔
2717
        SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT, 1);
342✔
2718
      }
342✔
2719
      catch (const std::exception& e) {
342✔
2720
        throw PDNSException(std::string("SO_REUSEPORT: ") + e.what());
×
2721
      }
×
2722
#endif
342✔
2723
    }
342✔
2724

2725
    try {
372✔
2726
      setSocketIgnorePMTU(socketFd, address.sin4.sin_family);
372✔
2727
    }
372✔
2728
    catch (const std::exception& e) {
372✔
2729
      log->error(Logr::Warning, e.what(), "Failed to set IP_MTU_DISCOVER on UDP server socket");
×
2730
    }
×
2731

2732
    socklen_t socklen = address.getSocklen();
372✔
2733
    if (::bind(socketFd, reinterpret_cast<struct sockaddr*>(&address), socklen) < 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
372!
2734
      int err = errno;
×
2735
      if (!configIsDefault || address != ComboAddress{"::1", defaultLocalPort}) {
×
2736
        throw PDNSException("Resolver binding to server socket on " + address.toStringWithPort() + ": " + stringerror(err));
×
2737
      }
×
2738
      log->info(Logr::Warning, "Cannot listen on this address, skipping", "proto", Logging::Loggable("UDP"), "address", Logging::Loggable(address), "error", Logging::Loggable(stringerror(err)));
×
2739
      continue;
×
2740
    }
×
2741

2742
    setNonBlocking(socketFd);
372✔
2743

2744
    deferredAdds.emplace_back(socketFd, handleNewUDPQuestion);
372✔
2745
    g_listenSocketsAddresses[socketFd] = address; // this is written to only from the startup thread, not from the workers
372✔
2746
    logVec.emplace_back(address.toStringWithPort());
372✔
2747
    socketFd.release(); // to avoid auto-close by FDWrapper
372✔
2748
  }
372✔
2749
  if (doLog) {
353✔
2750
    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✔
2751
  }
185✔
2752
  return localAddresses.size();
353✔
2753
}
353✔
2754

2755
static bool trySendingQueryToWorker(unsigned int target, ThreadMSG* tmsg)
2756
{
3,612✔
2757
  auto& targetInfo = RecThreadInfo::info(target);
3,612✔
2758
  if (!targetInfo.isWorker()) {
3,612!
2759
    g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() tried to assign a query to a non-worker thread");
×
2760
    _exit(1);
×
2761
  }
×
2762

2763
  const auto& tps = targetInfo.getPipes();
3,612✔
2764

2765
  ssize_t written = write(tps.writeQueriesToThread, &tmsg, sizeof(tmsg)); // NOLINT: correct sizeof
3,612✔
2766
  if (written > 0) {
3,612!
2767
    if (static_cast<size_t>(written) != sizeof(tmsg)) { // NOLINT: correct sizeof
3,612!
2768
      delete tmsg; // NOLINT: pointer ownership
×
2769
      unixDie("write to thread pipe returned wrong size or error");
×
2770
    }
×
2771
  }
3,612✔
2772
  else {
×
2773
    int error = errno;
×
2774
    if (error == EAGAIN || error == EWOULDBLOCK) {
×
2775
      return false;
×
2776
    }
×
2777
    delete tmsg; // NOLINT: pointer ownership
×
2778
    unixDie("write to thread pipe returned wrong size or error:" + std::to_string(error));
×
2779
  }
×
2780

2781
  return true;
3,612✔
2782
}
3,612✔
2783

2784
static unsigned int getWorkerLoad(size_t workerIdx)
2785
{
×
2786
  const auto* multiThreader = RecThreadInfo::info(RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + workerIdx).getMT();
×
2787
  if (multiThreader != nullptr) {
×
2788
    return multiThreader->numProcesses();
×
2789
  }
×
2790
  return 0;
×
2791
}
×
2792

2793
static unsigned int selectWorker(unsigned int hash)
2794
{
3,612✔
2795
  assert(RecThreadInfo::numUDPWorkers() != 0); // NOLINT: assert implementation
3,612✔
2796
  if (g_balancingFactor == 0) {
3,612!
2797
    return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + (hash % RecThreadInfo::numUDPWorkers());
3,612✔
2798
  }
3,612✔
2799

2800
  /* we start with one, representing the query we are currently handling */
2801
  double currentLoad = 1;
×
2802
  std::vector<unsigned int> load(RecThreadInfo::numUDPWorkers());
×
2803
  for (size_t idx = 0; idx < RecThreadInfo::numUDPWorkers(); idx++) {
×
2804
    load[idx] = getWorkerLoad(idx);
×
2805
    currentLoad += load[idx];
×
2806
  }
×
2807

2808
  double targetLoad = (currentLoad / RecThreadInfo::numUDPWorkers()) * g_balancingFactor;
×
2809

2810
  unsigned int worker = hash % RecThreadInfo::numUDPWorkers();
×
2811
  /* at least one server has to be at or below the average load */
2812
  if (load[worker] > targetLoad) {
×
2813
    ++t_Counters.at(rec::Counter::rebalancedQueries);
×
2814
    do {
×
2815
      worker = (worker + 1) % RecThreadInfo::numUDPWorkers();
×
2816
    } while (load[worker] > targetLoad);
×
2817
  }
×
2818

2819
  return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + worker;
×
2820
}
3,612✔
2821

2822
// This function is only called by the distributor threads, when pdns-distributes-queries is set
2823
void distributeAsyncFunction(const string& packet, const pipefunc_t& func)
2824
{
3,612✔
2825
  if (!RecThreadInfo::self().isDistributor()) {
3,612!
2826
    g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() has been called by a worker"); // tid will be added
×
2827
    _exit(1);
×
2828
  }
×
2829

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

2839
  ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: pointer ownership
3,612✔
2840
  tmsg->func = func;
3,612✔
2841
  tmsg->wantAnswer = false;
3,612✔
2842

2843
  if (!trySendingQueryToWorker(target, tmsg)) {
3,612!
2844
    /* if this function failed but did not raise an exception, it means that the pipe
2845
       was full, let's try another one */
2846
    unsigned int newTarget = 0;
×
2847
    do {
×
2848
      newTarget = RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + dns_random(RecThreadInfo::numUDPWorkers());
×
2849
    } while (newTarget == target);
×
2850

2851
    if (!trySendingQueryToWorker(newTarget, tmsg)) {
×
2852
      t_Counters.at(rec::Counter::queryPipeFullDrops)++;
×
2853
      delete tmsg; // NOLINT: pointer ownership
×
2854
    }
×
2855
  }
×
2856
  // coverity[leaked_storage]
2857
}
3,612✔
2858

2859
// resend event to everybody chained onto it
2860
static void doResends(MT_t::waiters_t::iterator& iter, const std::shared_ptr<PacketID>& resend, const PacketBuffer& content)
2861
{
10,089✔
2862
  // We close the chain for new entries, since they won't be processed anyway
2863
  iter->key->closed = true;
10,089✔
2864

2865
  if (iter->key->authReqChain.empty()) {
10,089✔
2866
    return;
9,689✔
2867
  }
9,689✔
2868

2869
  auto maxWeight = t_Counters.at(rec::Counter::maxChainWeight);
400✔
2870
  auto weight = iter->key->authReqChain.size() * content.size();
400✔
2871
  if (weight > maxWeight) {
400✔
2872
    t_Counters.at(rec::Counter::maxChainWeight) = weight;
82✔
2873
  }
82✔
2874

2875
  for (auto [fileDesc, qid] : iter->key->authReqChain) {
1,572✔
2876
    auto packetID = std::make_shared<PacketID>(*resend);
1,571✔
2877
    packetID->fd = fileDesc;
1,571✔
2878
    packetID->id = qid;
1,571✔
2879
    g_multiTasker->sendEvent(packetID, &content);
1,571✔
2880
    t_Counters.at(rec::Counter::chainResends)++;
1,571✔
2881
  }
1,571✔
2882
}
400✔
2883

2884
void mthreadSleep(unsigned int jitterMsec)
2885
{
62✔
2886
  auto neverHappens = std::make_shared<PacketID>();
62✔
2887
  neverHappens->id = dns_random_uint16();
62✔
2888
  neverHappens->type = dns_random_uint16();
62✔
2889
  neverHappens->remote = ComboAddress("100::"); // discard-only
62✔
2890
  neverHappens->remote.setPort(dns_random_uint16());
62✔
2891
  neverHappens->fd = -1;
62✔
2892
  assert(g_multiTasker->waitEvent(neverHappens, nullptr, jitterMsec) != -1); // NOLINT
62✔
2893
}
62✔
2894

2895
static bool checkIncomingECSSource(const PacketBuffer& packet, const Netmask& subnet)
2896
{
297✔
2897
  bool foundMatchingECS = false;
297✔
2898

2899
  // We sent out ECS, check if the response has the expected ECS info
2900
  EDNSOptionViewMap ednsOptions;
297✔
2901
  if (slowParseEDNSOptions(packet, ednsOptions)) {
297!
2902
    // check content
2903
    auto option = ednsOptions.find(EDNSOptionCode::ECS);
297✔
2904
    if (option != ednsOptions.end()) {
297✔
2905
      // found an ECS option
2906
      EDNSSubnetOpts ecs;
22✔
2907
      for (const auto& value : option->second.values) {
22!
2908
        if (EDNSSubnetOpts::getFromString(value.content, value.size, &ecs)) {
22!
2909
          if (ecs.getSource() == subnet) {
22✔
2910
            foundMatchingECS = true;
21✔
2911
          }
21✔
2912
        }
22✔
2913
        break; // The RFC isn't clear about multiple ECS options. We chose to handle it like cookies
22✔
2914
               // and only look at the first.
2915
      }
22✔
2916
    }
22✔
2917
  }
297✔
2918
  return foundMatchingECS;
297✔
2919
}
297✔
2920

2921
static void handleUDPServerResponse(int fileDesc, FDMultiplexer::funcparam_t& var)
2922
{
10,089✔
2923
  auto pid = boost::any_cast<std::shared_ptr<PacketID>>(var);
10,089✔
2924
  PacketBuffer packet;
10,089✔
2925
  packet.resize(g_outgoingEDNSBufsize);
10,089✔
2926
  ComboAddress fromaddr;
10,089✔
2927
  socklen_t addrlen = sizeof(fromaddr);
10,089✔
2928

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

2931
  const ssize_t signed_sizeof_sdnsheader = sizeof(dnsheader);
10,089✔
2932

2933
  if (len < 0) {
10,089✔
2934
    // len < 0: error on socket
2935
    t_udpclientsocks->returnSocket(fileDesc);
61✔
2936

2937
    PacketBuffer empty;
61✔
2938
    auto iter = g_multiTasker->getWaiters().find(pid);
61✔
2939
    if (iter != g_multiTasker->getWaiters().end()) {
61!
2940
      doResends(iter, pid, empty);
61✔
2941
    }
61✔
2942
    g_multiTasker->sendEvent(pid, &empty); // this denotes error (does retry lookup using other NS)
61✔
2943
    return;
61✔
2944
  }
61✔
2945

2946
  if (len < signed_sizeof_sdnsheader) {
10,028!
2947
    // We have received a packet that cannot be a valid DNS packet, as it has no complete header
2948
    // Drop it, but continue to wait for other packets
2949
    t_Counters.at(rec::Counter::serverParseError)++;
×
2950
    if (g_logCommonErrors) {
×
2951
      g_slogout->info(Logr::Error, "Unable to parse too short packet from remote UDP server", "from", Logging::Loggable(fromaddr));
×
2952
    }
×
2953
    return;
×
2954
  }
×
2955

2956
  // We have at least a full header
2957
  packet.resize(len);
10,028✔
2958
  dnsheader dnsheader{};
10,028✔
2959
  memcpy(&dnsheader, &packet.at(0), sizeof(dnsheader));
10,028✔
2960

2961
  auto pident = std::make_shared<PacketID>();
10,028✔
2962
  pident->remote = fromaddr;
10,028✔
2963
  pident->id = dnsheader.id;
10,028✔
2964
  pident->fd = fileDesc;
10,028✔
2965

2966
  if (!dnsheader.qr && g_logCommonErrors) {
10,028!
2967
    g_slogout->info(Logr::Error, "Not taking data from question on outgoing socket", "from", Logging::Loggable(fromaddr));
×
2968
  }
×
2969

2970
  if (dnsheader.qdcount == 0U || // UPC, Nominum, very old BIND on FormErr, NSD
10,028✔
2971
      dnsheader.qr == 0U) { // one weird server
10,028!
2972
    pident->domain.clear();
×
2973
    pident->type = 0;
×
2974
  }
×
2975
  else {
10,028✔
2976
    try {
10,028✔
2977
      if (len > signed_sizeof_sdnsheader) {
10,028!
2978
        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)
10,028✔
2979
      }
10,028✔
2980
      else {
×
2981
        // len == sizeof(dnsheader), only header case
2982
        // We will do a full scan search later to see if we can match this reply even without a domain
2983
        pident->domain.clear();
×
2984
        pident->type = 0;
×
2985
      }
×
2986
    }
10,028✔
2987
    catch (std::exception& e) {
10,028✔
2988
      // Parse error, continue waiting for other packets
2989
      t_Counters.at(rec::Counter::serverParseError)++; // won't be fed to lwres.cc, so we have to increment
×
2990
      g_slogudpin->error(Logr::Warning, e.what(), "Error in packet from remote nameserver", "from", Logging::Loggable(fromaddr));
×
2991
      return;
×
2992
    }
×
2993
  }
10,028✔
2994

2995
  if (!pident->domain.empty()) {
10,028✔
2996
    auto iter = g_multiTasker->getWaiters().find(pident);
10,026✔
2997
    if (iter != g_multiTasker->getWaiters().end()) {
10,028✔
2998
      doResends(iter, pident, packet);
10,028✔
2999
    }
10,028✔
3000
  }
10,026✔
3001

3002
retryWithName:
10,027✔
3003

3004
  if (pident->domain.empty() || g_multiTasker->sendEvent(pident, &packet) == 0) {
10,027!
3005
    /* we did not find a match for this response, something is wrong */
3006

3007
    // 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
3008
    for (const auto& d_waiter : g_multiTasker->getWaiters()) {
×
3009
      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) {
×
3010
        /* we are expecting an answer from that exact source, on that exact port (since we are using connected sockets), for that qname/qtype,
3011
           but with a different message ID. That smells like a spoofing attempt. For now we will just increase the counter and will deal with
3012
           that later. */
3013
        d_waiter.key->nearMisses++;
×
3014
      }
×
3015

3016
      // be a bit paranoid here since we're weakening our matching
3017
      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) {
×
3018
        pident->domain = d_waiter.key->domain;
×
3019
        pident->type = d_waiter.key->type;
×
3020
        goto retryWithName; // note that this only passes on an error, lwres will still reject the packet NOLINT(cppcoreguidelines-avoid-goto)
×
3021
      }
×
3022
    }
×
3023
    t_Counters.at(rec::Counter::unexpectedCount)++; // if we made it here, it really is an unexpected answer
×
3024
    if (g_logCommonErrors) {
×
3025
      g_slogudpin->info(Logr::Warning, "Discarding unexpected packet", "from", Logging::Loggable(fromaddr),
×
3026
                        "qname", Logging::Loggable(pident->domain),
×
3027
                        "qtype", Logging::Loggable(QType(pident->type)),
×
3028
                        "waiters", Logging::Loggable(g_multiTasker->getWaiters().size()));
×
3029
    }
×
3030
  }
×
3031
  else if (fileDesc >= 0) {
10,027✔
3032
    /* we either found a waiter (1) or encountered an issue (-1), it's up to us to clean the socket anyway */
3033
    t_udpclientsocks->returnSocket(fileDesc);
10,026✔
3034
  }
10,026✔
3035
}
10,027✔
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