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

PowerDNS / pdns / 17235120617

26 Aug 2025 10:17AM UTC coverage: 65.959% (-0.02%) from 65.977%
17235120617

Pull #16016

github

web-flow
Merge d1e0ec6fc into 9eeac00a7
Pull Request #16016: auth: random doc nits

42117 of 92446 branches covered (45.56%)

Branch coverage included in aggregate %.

128034 of 165518 relevant lines covered (77.35%)

5925196.8 hits per line

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

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

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

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

146
  --d_numsocks;
8,867✔
147
}
8,867✔
148

149
// returns -1 for errors which might go away, throws for ones that won't
150
int UDPClientSocks::makeClientSocket(int family)
151
{
8,864✔
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)
8,864✔
153

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

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

182
    sin = pdns::getQueryLocalAddress(family, port); // does htons for us
8,872✔
183
    if (::bind(ret, reinterpret_cast<struct sockaddr*>(&sin), sin.getSocklen()) >= 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
8,872✔
184
      break;
8,865✔
185
    }
8,865✔
186
  }
8,872✔
187

188
  int err = errno;
8,864✔
189

190
  if (tries == 0) {
8,864!
191
    closesocket(ret);
×
192
    throw PDNSException("Resolver binding to local query client socket on " + sin.toString() + ": " + stringerror(err));
×
193
  }
×
194

195
  try {
8,864✔
196
    setReceiveSocketErrors(ret, family);
8,864✔
197
    setNonBlocking(ret);
8,864✔
198
  }
8,864✔
199
  catch (...) {
8,864✔
200
    closesocket(ret);
×
201
    throw;
×
202
  }
×
203
  return ret;
8,865✔
204
}
8,864✔
205

206
static void handleGenUDPQueryResponse(int fileDesc, FDMultiplexer::funcparam_t& var)
207
{
4✔
208
  auto pident = boost::any_cast<std::shared_ptr<PacketID>>(var);
4✔
209
  PacketBuffer resp;
4✔
210
  resp.resize(512);
4✔
211
  ComboAddress fromaddr;
4✔
212
  socklen_t addrlen = sizeof(fromaddr);
4✔
213

214
  ssize_t ret = recvfrom(fileDesc, resp.data(), resp.size(), 0, reinterpret_cast<sockaddr*>(&fromaddr), &addrlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
4✔
215
  if (fromaddr != pident->remote) {
4!
216
    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));
×
217
  }
×
218

219
  t_fdm->removeReadFD(fileDesc);
4✔
220
  if (ret >= 0) {
4!
221
    resp.resize(ret);
4✔
222
    g_multiTasker->sendEvent(pident, &resp);
4✔
223
  }
4✔
224
  else {
×
225
    PacketBuffer empty;
×
226
    g_multiTasker->sendEvent(pident, &empty);
×
227
  }
×
228
}
4✔
229

230
PacketBuffer GenUDPQueryResponse(const ComboAddress& dest, const string& query)
231
{
4✔
232
  Socket socket(dest.sin4.sin_family, SOCK_DGRAM);
4✔
233
  socket.setNonBlocking();
4✔
234
  ComboAddress local = pdns::getQueryLocalAddress(dest.sin4.sin_family, 0);
4✔
235

236
  socket.bind(local);
4✔
237
  socket.connect(dest);
4✔
238
  socket.send(query);
4✔
239

240
  std::shared_ptr<PacketID> pident = std::make_shared<PacketID>();
4✔
241
  pident->fd = socket.getHandle();
4✔
242
  pident->remote = dest;
4✔
243
  pident->type = 0;
4✔
244
  t_fdm->addReadFD(socket.getHandle(), handleGenUDPQueryResponse, pident);
4✔
245

246
  PacketBuffer data;
4✔
247
  int ret = g_multiTasker->waitEvent(pident, &data, authWaitTimeMSec(g_multiTasker));
4✔
248

249
  if (ret == 0 || ret == -1) { // timeout
4!
250
    t_fdm->removeReadFD(socket.getHandle());
×
251
  }
×
252
  else if (data.empty()) { // error, EOF or other
4!
253
    // we could special case this
254
    return data;
×
255
  }
×
256
  return data;
4✔
257
}
4✔
258

259
static void handleUDPServerResponse(int fileDesc, FDMultiplexer::funcparam_t& var);
260

261
thread_local std::unique_ptr<UDPClientSocks> t_udpclientsocks;
262

263
// If we have plenty of mthreads slot left, use default timeout.
264
// Otherwise reduce the timeout to be between g_networkTimeoutMsec/10 and g_networkTimeoutMsec
265
unsigned int authWaitTimeMSec(const std::unique_ptr<MT_t>& mtasker)
266
{
11,873✔
267
  const auto max = g_maxMThreads;
11,873✔
268
  const auto current = mtasker->numProcesses();
11,873✔
269
  const unsigned int cutoff = max / 10; // if we have less than 10% used,  do not reduce auth timeout
11,873✔
270
  if (current < cutoff) {
11,874✔
271
    return g_networkTimeoutMsec;
11,872✔
272
  }
11,872✔
273
  const auto avail = max - current;
2,147,483,647✔
274
  return std::max(g_networkTimeoutMsec / 10, g_networkTimeoutMsec * avail / (max - cutoff));
2,147,483,647✔
275
}
11,873✔
276

277
/* these two functions are used by LWRes */
278
LWResult::Result asendto(const void* data, size_t len, int /* flags */,
279
                         const ComboAddress& toAddress, uint16_t qid, const DNSName& domain, uint16_t qtype, const std::optional<EDNSSubnetOpts>& ecs, int* fileDesc, timeval& now)
280
{
10,345✔
281

282
  auto pident = std::make_shared<PacketID>();
10,345✔
283
  pident->domain = domain;
10,345✔
284
  pident->remote = toAddress;
10,345✔
285
  pident->type = qtype;
10,345✔
286
  if (ecs) {
10,345✔
287
    pident->ecsSubnet = ecs->getSource();
570✔
288
  }
570✔
289

290
  // See if there is an existing outstanding request we can chain on to, using partial equivalence
291
  // function looking for the same query (qname, qtype and ecs if applicable) to the same host, but
292
  // with a different message ID.
293
  auto chain = g_multiTasker->getWaiters().equal_range(pident, PacketIDBirthdayCompare());
10,345✔
294

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

319
  auto ret = t_udpclientsocks->getSocket(toAddress, fileDesc);
8,863✔
320
  if (ret != LWResult::Result::Success) {
8,863!
321
    return ret;
×
322
  }
×
323

324
  pident->fd = *fileDesc;
8,863✔
325
  pident->id = qid;
8,863✔
326

327
  t_fdm->addReadFD(*fileDesc, handleUDPServerResponse, pident);
8,863✔
328
  ssize_t sent = send(*fileDesc, data, len, 0);
8,863✔
329

330
  int tmp = errno;
8,863✔
331

332
  if (sent < 0) {
8,863!
333
    t_udpclientsocks->returnSocket(*fileDesc);
×
334
    errno = tmp; // this is for logging purposes only
×
335
    return LWResult::Result::PermanentError;
×
336
  }
×
337

338
  return LWResult::Result::Success;
8,863✔
339
}
8,863✔
340

341
static bool checkIncomingECSSource(const PacketBuffer& packet, const Netmask& subnet);
342

343
LWResult::Result arecvfrom(PacketBuffer& packet, int /* flags */, const ComboAddress& fromAddr, size_t& len,
344
                           uint16_t qid, const DNSName& domain, uint16_t qtype, int fileDesc, const std::optional<EDNSSubnetOpts>& ecs, const struct timeval& now)
345
{
10,347✔
346
  static const unsigned int nearMissLimit = ::arg().asNum("spoof-nearmiss-max");
10,347✔
347

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

365
  /* -1 means error, 0 means timeout, 1 means a result from handleUDPServerResponse() which might still be an error */
366
  if (ret > 0) {
10,349✔
367
    /* handleUDPServerResponse() will close the socket for us no matter what */
368
    if (packet.empty()) { // means "error"
10,343✔
369
      return LWResult::Result::PermanentError;
64✔
370
    }
64✔
371

372
    len = packet.size();
10,279✔
373

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

392
    return LWResult::Result::Success;
10,023✔
393
  }
10,023✔
394
  /* getting there means error or timeout, it's up to us to close the socket */
395
  if (fileDesc >= 0) {
2,147,483,650✔
396
    t_udpclientsocks->returnSocket(fileDesc);
5✔
397
  }
5✔
398

399
  return ret == 0 ? LWResult::Result::Timeout : LWResult::Result::PermanentError;
2,147,483,650✔
400
}
10,347✔
401

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

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

453
static bool addRecordToPacket(DNSPacketWriter& packetWritewr, const DNSRecord& rec, uint32_t& minTTL, uint32_t ttlCap, const uint16_t maxAnswerSize, bool& seenAuthSOA)
454
{
10,027✔
455
  packetWritewr.startRecord(rec.d_name, rec.d_type, (rec.d_ttl > ttlCap ? ttlCap : rec.d_ttl), rec.d_class, rec.d_place);
10,027!
456

457
  if (rec.d_type == QType::SOA && rec.d_place == DNSResourceRecord::AUTHORITY) {
10,027✔
458
    seenAuthSOA = true;
400✔
459
  }
400✔
460

461
  if (rec.d_type != QType::OPT) { // their TTL ain't real
10,027!
462
    minTTL = min(minTTL, rec.d_ttl);
10,027✔
463
  }
10,027✔
464

465
  rec.getContent()->toPacket(packetWritewr);
10,027✔
466
  if (packetWritewr.size() > static_cast<size_t>(maxAnswerSize)) {
10,027✔
467
    packetWritewr.rollback();
4✔
468
    if (rec.d_place != DNSResourceRecord::ADDITIONAL) {
4!
469
      packetWritewr.getHeader()->tc = 1;
4✔
470
      packetWritewr.truncate();
4✔
471
    }
4✔
472
    return false;
4✔
473
  }
4✔
474

475
  return true;
10,023✔
476
}
10,027✔
477

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

519
private:
520
  std::unique_ptr<DNSComboWriter>& d_dc; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
521
  bool d_handled{false};
522
};
523

524
enum class PolicyResult : uint8_t
525
{
526
  NoAction,
527
  HaveAnswer,
528
  Drop
529
};
530

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

539
  if (resolver.doLog() && appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
91!
540
    appliedPolicy.info(Logr::Warning, resolver.d_slog);
6✔
541
  }
6✔
542

543
  if (appliedPolicy.d_zoneData && appliedPolicy.d_zoneData->d_extendedErrorCode) {
91!
544
    comboWriter->d_extendedErrorCode = *appliedPolicy.d_zoneData->d_extendedErrorCode;
×
545
    comboWriter->d_extendedErrorExtra = appliedPolicy.d_zoneData->d_extendedErrorExtra;
×
546
  }
×
547

548
  switch (appliedPolicy.d_kind) {
91!
549

550
  case DNSFilterEngine::PolicyKind::NoAction:
77✔
551
    return PolicyResult::NoAction;
77✔
552

553
  case DNSFilterEngine::PolicyKind::Drop:
7✔
554
    tcpGuard.setDropOnIdle();
7✔
555
    ++t_Counters.at(rec::Counter::policyDrops);
7✔
556
    return PolicyResult::Drop;
7✔
557

558
  case DNSFilterEngine::PolicyKind::NXDOMAIN:
1✔
559
    ret.clear();
1✔
560
    appliedPolicy.addSOAtoRPZResult(ret);
1✔
561
    res = RCode::NXDomain;
1✔
562
    return PolicyResult::HaveAnswer;
1✔
563

564
  case DNSFilterEngine::PolicyKind::NODATA:
1✔
565
    ret.clear();
1✔
566
    appliedPolicy.addSOAtoRPZResult(ret);
1✔
567
    res = RCode::NoError;
1✔
568
    return PolicyResult::HaveAnswer;
1✔
569

570
  case DNSFilterEngine::PolicyKind::Truncate:
1✔
571
    if (!comboWriter->d_tcp) {
1!
572
      ret.clear();
1✔
573
      appliedPolicy.addSOAtoRPZResult(ret);
1✔
574
      res = RCode::NoError;
1✔
575
      packetWriter.getHeader()->tc = 1;
1✔
576
      return PolicyResult::HaveAnswer;
1✔
577
    }
1✔
578
    return PolicyResult::NoAction;
×
579

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

615
      appliedPolicy.addSOAtoRPZResult(ret);
4✔
616
      return PolicyResult::HaveAnswer;
4✔
617
    }
4✔
618
  }
91✔
619

620
  return PolicyResult::NoAction;
×
621
}
91✔
622

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

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

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

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

694
int followCNAMERecords(vector<DNSRecord>& ret, const QType qtype, int rcode)
695
{
4✔
696
  vector<DNSRecord> resolved;
4✔
697
  DNSName target;
4✔
698
  for (const DNSRecord& record : ret) {
4!
699
    if (record.d_type == QType::CNAME) {
4!
700
      auto rec = getRR<CNAMERecordContent>(record);
4✔
701
      if (rec) {
4!
702
        target = rec->getTarget();
4✔
703
        break;
4✔
704
      }
4✔
705
    }
4✔
706
  }
4✔
707

708
  if (target.empty()) {
4!
709
    return rcode;
×
710
  }
×
711

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

715
  if (g_dns64Prefix && qtype == QType::AAAA && dns64Candidate(qtype, rcode, resolved)) {
4!
716
    rcode = getFakeAAAARecords(target, *g_dns64Prefix, resolved);
2✔
717
  }
2✔
718

719
  for (DNSRecord& record : resolved) {
9✔
720
    if (record.d_place == DNSResourceRecord::ANSWER) {
9!
721
      ret.push_back(std::move(record));
9✔
722
    }
9✔
723
  }
9✔
724
  return rcode;
4✔
725
}
4✔
726

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

736
  ret.reserve(ret.size() + newRecords.size());
18✔
737
  for (auto& record : newRecords) {
31✔
738
    ret.push_back(std::move(record));
31✔
739
  }
31✔
740

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

762
  bool seenA = false;
18✔
763
  for (DNSRecord& record : ret) {
62✔
764
    if (record.d_type == QType::A && record.d_place == DNSResourceRecord::ANSWER) {
62!
765
      if (auto rec = getRR<ARecordContent>(record)) {
14!
766
        ComboAddress ipv4(rec->getCA());
14✔
767
        memcpy(&prefix.sin6.sin6_addr.s6_addr[12], &ipv4.sin4.sin_addr.s_addr, sizeof(ipv4.sin4.sin_addr.s_addr));
14✔
768
        record.setContent(std::make_shared<AAAARecordContent>(prefix));
14✔
769
        record.d_type = QType::AAAA;
14✔
770
      }
14✔
771
      seenA = true;
14✔
772
    }
14✔
773
  }
62✔
774

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

793
int getFakePTRRecords(const DNSName& qname, vector<DNSRecord>& ret)
794
{
2✔
795
  /* qname has a reverse ordered IPv6 address, need to extract the underlying IPv4 address from it
796
     and turn it into an IPv4 in-addr.arpa query */
797
  ret.clear();
2✔
798
  vector<string> parts = qname.getRawLabels();
2✔
799

800
  if (parts.size() < 8) {
2!
801
    return -1;
×
802
  }
×
803

804
  string newquery;
2✔
805
  for (size_t octet = 0; octet < 4; ++octet) {
10✔
806
    newquery += std::to_string(stoll(parts[octet * 2], nullptr, 16) + 16 * stoll(parts[octet * 2 + 1], nullptr, 16));
8✔
807
    newquery.append(1, '.');
8✔
808
  }
8✔
809
  newquery += "in-addr.arpa.";
2✔
810

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

815
  DNSRecord record;
2✔
816
  record.d_name = qname;
2✔
817
  record.d_type = QType::CNAME;
2✔
818
  record.setContent(std::make_shared<CNAMERecordContent>(newquery));
2✔
819
  // Copy the TTL of the synthesized CNAME from the actual answer
820
  record.d_ttl = (rcode == RCode::NoError && !answers.empty()) ? answers.at(0).d_ttl : SyncRes::s_minimumTTL;
2!
821
  ret.push_back(std::move(record));
2✔
822

823
  ret.insert(ret.end(), answers.begin(), answers.end());
2✔
824

825
  t_Counters.at(rec::Counter::dns64prefixanswers)++;
2✔
826
  return rcode;
2✔
827
}
2✔
828

829
// RFC 6147 section 5.1 all rcodes except NXDomain should be candidate for dns64
830
// for NoError, check if it is NoData
831
static bool dns64Candidate(uint16_t requestedType, int rcode, const std::vector<DNSRecord>& records)
832
{
18✔
833
  if (rcode == RCode::NoError) {
18✔
834
    return SyncRes::answerIsNOData(requestedType, rcode, records);
16✔
835
  }
16✔
836
  return rcode != RCode::NXDomain;
2✔
837
}
18✔
838

839
bool isAllowNotifyForZone(DNSName qname)
840
{
24✔
841
  if (t_allowNotifyFor->empty()) {
24!
842
    return false;
×
843
  }
×
844

845
  do {
24✔
846
    auto ret = t_allowNotifyFor->find(qname);
24✔
847
    if (ret != t_allowNotifyFor->end()) {
24!
848
      return true;
24✔
849
    }
24✔
850
  } while (qname.chopOff());
24!
851
  return false;
×
852
}
24✔
853

854
#if defined(HAVE_FSTRM) && defined(NOD_ENABLED)
855
#include "dnstap.hh"
856
#include "fstrm_logger.hh"
857

858
static bool isEnabledForNODs(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
859
{
3✔
860
  if (fstreamLoggers == nullptr) {
3!
861
    return false;
×
862
  }
×
863
  for (auto& logger : *fstreamLoggers) {
3✔
864
    if (logger->logNODs()) {
3✔
865
      return true;
2✔
866
    }
2✔
867
  }
3✔
868
  return false;
1✔
869
}
3✔
870
static bool isEnabledForUDRs(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
871
{
3✔
872
  if (fstreamLoggers == nullptr) {
3!
873
    return false;
×
874
  }
×
875
  for (auto& logger : *fstreamLoggers) {
3✔
876
    if (logger->logUDRs()) {
3✔
877
      return true;
2✔
878
    }
2✔
879
  }
3✔
880
  return false;
1✔
881
}
3✔
882
#endif // HAVE_FSTRM
883

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

932
static uint32_t capPacketCacheTTL(const struct dnsheader& hdr, uint32_t ttl, bool seenAuthSOA)
933
{
2,867✔
934
  if (hdr.rcode == RCode::NXDomain || (hdr.rcode == RCode::NoError && hdr.ancount == 0 && seenAuthSOA)) {
2,867✔
935
    ttl = std::min(ttl, SyncRes::s_packetcachenegativettl);
372✔
936
  }
372✔
937
  else if ((hdr.rcode != RCode::NoError && hdr.rcode != RCode::NXDomain) || (hdr.ancount == 0 && hdr.nscount == 0)) {
2,495!
938
    ttl = min(ttl, SyncRes::s_packetcacheservfailttl);
82✔
939
  }
82✔
940
  else {
2,413✔
941
    ttl = std::min(ttl, SyncRes::s_packetcachettl);
2,413✔
942
  }
2,413✔
943
  return ttl;
2,867✔
944
}
2,867✔
945

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

960
void startDoResolve(void* arg) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
961
{
3,192✔
962
  auto comboWriter = std::unique_ptr<DNSComboWriter>(static_cast<DNSComboWriter*>(arg));
3,192✔
963
  SyncRes resolver(comboWriter->d_now);
3,192✔
964
  try {
3,192✔
965
    if (t_queryring) {
3,192✔
966
      t_queryring->push_back({comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype});
3,190✔
967
    }
3,190✔
968

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

991
      if (!comboWriter->d_tcp) {
1,337✔
992
        /* rfc6891 6.2.3:
993
           "Values lower than 512 MUST be treated as equal to 512."
994
        */
995
        maxanswersize = min(static_cast<uint16_t>(edo.d_packetsize >= 512 ? edo.d_packetsize : 512), g_udpTruncationThreshold);
564✔
996
      }
564✔
997
      ednsOpts = edo.d_options;
1,337✔
998
      maxanswersize -= 11; // EDNS header size
1,337✔
999

1000
      if (!comboWriter->d_responsePaddingDisabled && g_paddingFrom.match(comboWriter->d_remote)) {
1,337✔
1001
        paddingAllowed = true;
7✔
1002
        if (g_paddingMode == PaddingMode::Always) {
7✔
1003
          addPaddingToResponse = true;
5✔
1004
        }
5✔
1005
      }
7✔
1006

1007
      for (const auto& option : edo.d_options) {
1,337✔
1008
        if (option.first == EDNSOptionCode::ECS && g_useIncomingECS && !comboWriter->d_ecsParsed) {
527✔
1009
          comboWriter->d_ecsFound = EDNSSubnetOpts::getFromString(option.second, &comboWriter->d_ednssubnet);
443✔
1010
        }
443✔
1011
        else if (option.first == EDNSOptionCode::NSID) {
84✔
1012
          const static string mode_server_id = ::arg()["server-id"];
2✔
1013
          if (mode_server_id != "disabled" && !mode_server_id.empty() && maxanswersize > (EDNSOptionCodeSize + EDNSOptionLengthSize + mode_server_id.size())) {
2!
1014
            returnedEdnsOptions.emplace_back(EDNSOptionCode::NSID, mode_server_id);
2✔
1015
            variableAnswer = true; // Can't packetcache an answer with NSID
2✔
1016
            maxanswersize -= EDNSOptionCodeSize + EDNSOptionLengthSize + mode_server_id.size();
2✔
1017
          }
2✔
1018
        }
2✔
1019
        else if (paddingAllowed && !addPaddingToResponse && g_paddingMode == PaddingMode::PaddedQueries && option.first == EDNSOptionCode::PADDING) {
82!
1020
          addPaddingToResponse = true;
1✔
1021
        }
1✔
1022
      }
527✔
1023
    }
1,337✔
1024

1025
    /* the lookup will be done _before_ knowing whether the query actually
1026
       has a padding option, so we need to use the separate tag even when the
1027
       query does not have padding, as long as it is from an allowed source */
1028
    if (paddingAllowed && comboWriter->d_tag == 0) {
3,192✔
1029
      comboWriter->d_tag = g_paddingTag;
2✔
1030
    }
2✔
1031

1032
    /* perhaps there was no EDNS or no ECS but by now we looked */
1033
    comboWriter->d_ecsParsed = true;
3,192✔
1034
    vector<DNSRecord> ret;
3,192✔
1035
    vector<uint8_t> packet;
3,192✔
1036

1037
    auto luaconfsLocal = g_luaconfs.getLocal();
3,192✔
1038
    // Used to tell syncres later on if we should apply NSDNAME and NSIP RPZ triggers for this query
1039
    bool wantsRPZ(true);
3,192✔
1040
    RecursorPacketCache::OptPBData pbDataForCache;
3,192✔
1041
    pdns::ProtoZero::RecMessage pbMessage;
3,192✔
1042
    if (checkProtobufExport(luaconfsLocal)) {
3,192✔
1043
      pbMessage.reserve(128, 128); // It's a bit of a guess...
30✔
1044
      pbMessage.setResponse(comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype, comboWriter->d_mdp.d_qclass);
30✔
1045
      pbMessage.setServerIdentity(SyncRes::s_serverID);
30✔
1046

1047
      // RRSets added below
1048
    }
30✔
1049
    checkOutgoingProtobufExport(luaconfsLocal); // to pick up changed configs
3,192✔
1050
#ifdef HAVE_FSTRM
3,192✔
1051
    checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
3,192✔
1052
    checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
3,192✔
1053
#endif
3,192✔
1054

1055
    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,192✔
1056

1057
    packetWriter.getHeader()->aa = 0;
3,192✔
1058
    packetWriter.getHeader()->ra = 1;
3,192✔
1059
    packetWriter.getHeader()->qr = 1;
3,192✔
1060
    packetWriter.getHeader()->tc = 0;
3,192✔
1061
    packetWriter.getHeader()->id = comboWriter->d_mdp.d_header.id;
3,192✔
1062
    packetWriter.getHeader()->rd = comboWriter->d_mdp.d_header.rd;
3,192✔
1063
    packetWriter.getHeader()->cd = comboWriter->d_mdp.d_header.cd;
3,192✔
1064

1065
    /* This is the lowest TTL seen in the records of the response,
1066
       so we can't cache it for longer than this value.
1067
       If we have a TTL cap, this value can't be larger than the
1068
       cap no matter what. */
1069
    uint32_t minTTL = comboWriter->d_ttlCap;
3,192✔
1070
    bool seenAuthSOA = false;
3,192✔
1071

1072
    resolver.d_eventTrace = std::move(comboWriter->d_eventTrace);
3,192✔
1073
    resolver.d_otTrace = std::move(comboWriter->d_otTrace);
3,192✔
1074
    resolver.setId(g_multiTasker->getTid());
3,192✔
1075

1076
    bool DNSSECOK = false;
3,192✔
1077
    if (comboWriter->d_luaContext) {
3,192✔
1078
      resolver.setLuaEngine(comboWriter->d_luaContext);
414✔
1079
    }
414✔
1080
    if (g_dnssecmode != DNSSECMode::Off) {
3,192✔
1081
      resolver.setDoDNSSEC(true);
3,166✔
1082

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

1110
    resolver.setInitialRequestId(comboWriter->d_uuid);
3,192✔
1111
    resolver.setOutgoingProtobufServers(t_outgoingProtobufServers.servers);
3,192✔
1112
#ifdef HAVE_FSTRM
3,192✔
1113
    resolver.setFrameStreamServers(t_frameStreamServersInfo.servers);
3,192✔
1114
#endif
3,192✔
1115

1116
    bool useMapped = true;
3,192✔
1117
    // 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
1118
    // (if a domain suffix match table is present in the config)
1119
    if (t_proxyMapping && comboWriter->d_source != comboWriter->d_mappedSource) {
3,192!
1120
      if (const auto* iter = t_proxyMapping->lookup(comboWriter->d_source)) {
9!
1121
        if (iter->second.suffixMatchNode) {
9✔
1122
          if (!iter->second.suffixMatchNode->check(comboWriter->d_mdp.d_qname)) {
3✔
1123
            // No match in domains, use original source
1124
            useMapped = false;
2✔
1125
          }
2✔
1126
          else {
1✔
1127
            ++iter->second.stats.suffixMatches;
1✔
1128
          }
1✔
1129
        }
3✔
1130
        // No suffix match node defined, use mapped address
1131
      }
9✔
1132
      // lookup failing cannot happen as dc->d_source != dc->d_mappedSource
1133
    }
9✔
1134
    resolver.setQuerySource(useMapped ? comboWriter->d_mappedSource : comboWriter->d_source, g_useIncomingECS && !comboWriter->d_ednssubnet.getSource().empty() ? boost::optional<const EDNSSubnetOpts&>(comboWriter->d_ednssubnet) : boost::none);
3,192✔
1135

1136
    resolver.setQueryReceivedOverTCP(comboWriter->d_tcp);
3,192✔
1137

1138
    bool tracedQuery = false; // we could consider letting Lua know about this too
3,192✔
1139
    bool shouldNotValidate = false;
3,192✔
1140

1141
    /* preresolve expects res (dq.rcode) to be set to RCode::NoError by default */
1142
    int res = RCode::NoError;
3,192✔
1143

1144
    DNSFilterEngine::Policy appliedPolicy;
3,192✔
1145
    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,192!
1146
    dnsQuestion.ednsFlags = &edo.d_extFlags;
3,192✔
1147
    dnsQuestion.ednsOptions = &ednsOpts;
3,192✔
1148
    dnsQuestion.tag = comboWriter->d_tag;
3,192✔
1149
    dnsQuestion.discardedPolicies = &resolver.d_discardedPolicies;
3,192✔
1150
    dnsQuestion.policyTags = &comboWriter->d_policyTags;
3,192✔
1151
    dnsQuestion.appliedPolicy = &appliedPolicy;
3,192✔
1152
    dnsQuestion.currentRecords = &ret;
3,192✔
1153
    dnsQuestion.dh = &comboWriter->d_mdp.d_header;
3,192✔
1154
    dnsQuestion.data = comboWriter->d_data;
3,192✔
1155
    dnsQuestion.requestorId = comboWriter->d_requestorId;
3,192✔
1156
    dnsQuestion.deviceId = comboWriter->d_deviceId;
3,192✔
1157
    dnsQuestion.deviceName = comboWriter->d_deviceName;
3,192✔
1158
    dnsQuestion.proxyProtocolValues = &comboWriter->d_proxyProtocolValues;
3,192✔
1159
    dnsQuestion.extendedErrorCode = &comboWriter->d_extendedErrorCode;
3,192✔
1160
    dnsQuestion.extendedErrorExtra = &comboWriter->d_extendedErrorExtra;
3,192✔
1161
    dnsQuestion.meta = std::move(comboWriter->d_meta);
3,192✔
1162
    dnsQuestion.fromAuthIP = &resolver.d_fromAuthIP;
3,192✔
1163

1164
    resolver.d_slog = resolver.d_slog->withValues("qname", Logging::Loggable(comboWriter->d_mdp.d_qname),
3,192✔
1165
                                                  "qtype", Logging::Loggable(QType(comboWriter->d_mdp.d_qtype)),
3,192✔
1166
                                                  "remote", Logging::Loggable(comboWriter->getRemote()),
3,192✔
1167
                                                  "proto", Logging::Loggable(comboWriter->d_tcp ? "tcp" : "udp"),
3,192✔
1168
                                                  "ecs", Logging::Loggable(comboWriter->d_ednssubnet.getSource().empty() ? "" : comboWriter->d_ednssubnet.getSource().toString()),
3,192✔
1169
                                                  "mtid", Logging::Loggable(g_multiTasker->getTid()));
3,192✔
1170
    RunningResolveGuard tcpGuard(comboWriter);
3,192✔
1171

1172
    if (ednsExtRCode != 0 || comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Notify)) {
3,192✔
1173
      goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
24✔
1174
    }
24✔
1175

1176
    if (comboWriter->d_mdp.d_qtype == QType::ANY && !comboWriter->d_tcp && g_anyToTcp) {
3,168!
1177
      packetWriter.getHeader()->tc = 1;
×
1178
      res = 0;
×
1179
      variableAnswer = true;
×
1180
      goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
×
1181
    }
×
1182

1183
    if (t_traceRegex && t_traceRegex->match(comboWriter->d_mdp.d_qname.toString())) {
3,168!
1184
      resolver.setLogMode(SyncRes::Store);
×
1185
      tracedQuery = true;
×
1186
    }
×
1187

1188
    if (!g_quiet || tracedQuery) {
3,168✔
1189
      resolver.d_slog->info(Logr::Info, "Question");
909✔
1190
    }
909✔
1191

1192
    if (!comboWriter->d_mdp.d_header.rd) {
3,168✔
1193
      if (g_allowNoRD) {
3✔
1194
        resolver.setCacheOnly();
2✔
1195
      }
2✔
1196
      else {
1✔
1197
        ret.clear();
1✔
1198
        res = RCode::Refused;
1✔
1199
        goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
1✔
1200
      }
1✔
1201
    }
3✔
1202

1203
    if (comboWriter->d_luaContext) {
3,167✔
1204
      comboWriter->d_luaContext->prerpz(dnsQuestion, res, resolver.d_eventTrace);
403✔
1205
    }
403✔
1206

1207
    // Check if the client has a policy attached to it
1208
    if (wantsRPZ && !appliedPolicy.wasHit()) {
3,167✔
1209

1210
      if (luaconfsLocal->dfe.getClientPolicy(comboWriter->d_source, resolver.d_discardedPolicies, appliedPolicy)) {
3,164!
1211
        mergePolicyTags(comboWriter->d_policyTags, appliedPolicy.getTags());
×
1212
      }
×
1213
    }
3,164✔
1214

1215
    /* If we already have an answer generated from gettag_ffi, let's see if the filtering policies
1216
       should be applied to it */
1217
    if (comboWriter->d_rcode != boost::none) {
3,167✔
1218

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

1234
        if (appliedPolicy.wasHit()) {
5✔
1235
          policyOverride = true;
2✔
1236
        }
2✔
1237
      }
5✔
1238

1239
      if (!policyOverride) {
5✔
1240
        /* No RPZ or gettag overrides it anyway */
1241
        ret = std::move(comboWriter->d_records);
3✔
1242
        res = *comboWriter->d_rcode;
3✔
1243
        if (res == RCode::NoError && comboWriter->d_followCNAMERecords) {
3!
1244
          res = followCNAMERecords(ret, QType(comboWriter->d_mdp.d_qtype), res);
×
1245
        }
×
1246
        goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
3✔
1247
      }
3✔
1248
    }
5✔
1249

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

1253
      if (!g_dns64PrefixReverse.empty() && dnsQuestion.qtype == QType::PTR && dnsQuestion.qname.isPartOf(g_dns64PrefixReverse)) {
3,104✔
1254
        res = getFakePTRRecords(dnsQuestion.qname, ret);
2✔
1255
        goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
2✔
1256
      }
2✔
1257

1258
      resolver.setWantsRPZ(wantsRPZ);
3,102✔
1259

1260
      if (wantsRPZ && appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
3,102✔
1261

1262
        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!
1263
          /* reset to no match */
1264
          appliedPolicy = DNSFilterEngine::Policy();
×
1265
        }
×
1266
        else {
2✔
1267
          auto policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
2✔
1268
          if (policyResult == PolicyResult::HaveAnswer) {
2!
1269
            if (g_dns64Prefix && dnsQuestion.qtype == QType::AAAA && dns64Candidate(comboWriter->d_mdp.d_qtype, res, ret)) {
2!
1270
              res = getFakeAAAARecords(dnsQuestion.qname, *g_dns64Prefix, ret);
2✔
1271
              shouldNotValidate = true;
2✔
1272
            }
2✔
1273
            goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
2✔
1274
          }
2✔
1275
          else if (policyResult == PolicyResult::Drop) {
×
1276
            return;
×
1277
          }
×
1278
        }
2✔
1279
      }
2✔
1280

1281
      // Query did not get handled for Client IP or QNAME Policy reasons, now actually go out to find an answer
1282
      try {
3,100✔
1283
        resolver.d_appliedPolicy = appliedPolicy;
3,100✔
1284
        resolver.d_policyTags = std::move(comboWriter->d_policyTags);
3,100✔
1285

1286
        if (!comboWriter->d_routingTag.empty()) {
3,100✔
1287
          resolver.d_routingTag = comboWriter->d_routingTag;
16✔
1288
        }
16✔
1289

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

1325
      if (appliedPolicy.d_type != DNSFilterEngine::PolicyType::None && appliedPolicy.d_zoneData && appliedPolicy.d_zoneData->d_extendedErrorCode) {
3,095!
1326
        comboWriter->d_extendedErrorCode = *appliedPolicy.d_zoneData->d_extendedErrorCode;
2✔
1327
        comboWriter->d_extendedErrorExtra = appliedPolicy.d_zoneData->d_extendedErrorExtra;
2✔
1328
      }
2✔
1329

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

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

1367
      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,089!
1368
        res = getFakeAAAARecords(dnsQuestion.qname, *g_dns64Prefix, ret);
10✔
1369
        shouldNotValidate = true;
10✔
1370
      }
10✔
1371

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

1406
  haveAnswer:;
3,156✔
1407
    if (tracedQuery || res == -1 || res == RCode::ServFail || packetWriter.getHeader()->rcode == static_cast<unsigned>(RCode::ServFail)) {
3,155!
1408
      dumpTrace(resolver.getTrace(), resolver.d_fixednow);
16✔
1409
    }
16✔
1410

1411
    if (res == -1) {
3,155✔
1412
      packetWriter.getHeader()->rcode = RCode::ServFail;
2✔
1413
      // no commit here, because no record
1414
      ++t_Counters.at(rec::Counter::servFails);
2✔
1415
    }
2✔
1416
    else {
3,153✔
1417
      packetWriter.getHeader()->rcode = res;
3,153✔
1418

1419
      // Does the validation mode or query demand validation?
1420
      if (!shouldNotValidate && resolver.isDNSSECValidationRequested()) {
3,153✔
1421
        try {
2,561✔
1422
          auto state = resolver.getValidationState();
2,561✔
1423

1424
          string x_marker;
2,561✔
1425
          std::shared_ptr<Logr::Logger> log;
2,561✔
1426
          if (resolver.doLog() || vStateIsBogus(state)) {
2,561!
1427
            // Only create logging object if needed below, beware if you change the logging logic!
1428
            log = resolver.d_slog->withValues("vstate", Logging::Loggable(state));
303✔
1429
            if (resolver.getDNSSECLimitHit()) {
303!
1430
              log = log->withValues("dnsseclimithit", Logging::Loggable(true));
×
1431
            }
×
1432
            auto xdnssec = g_xdnssec.getLocal();
303✔
1433
            if (xdnssec->check(comboWriter->d_mdp.d_qname)) {
303!
1434
              log = log->withValues("in-x-dnssec-names", Logging::Loggable(1));
×
1435
              x_marker = " [in x-dnssec-names]";
×
1436
            }
×
1437
          }
303✔
1438
          if (state == vState::Secure) {
2,561✔
1439
            if (resolver.doLog()) {
757✔
1440
              log->info(Logr::Info, "Validates Correctly");
140✔
1441
            }
140✔
1442

1443
            // Is the query source interested in the value of the ad-bit?
1444
            if (comboWriter->d_mdp.d_header.ad || DNSSECOK) {
757✔
1445
              packetWriter.getHeader()->ad = 1;
722✔
1446
            }
722✔
1447
          }
757✔
1448
          else if (state == vState::Insecure) {
1,804✔
1449
            if (resolver.doLog()) {
1,689✔
1450
              log->info(Logr::Info, "Validates as Insecure");
48✔
1451
            }
48✔
1452

1453
            packetWriter.getHeader()->ad = 0;
1,689✔
1454
          }
1,689✔
1455
          else if (vStateIsBogus(state)) {
115✔
1456
            if (t_bogusremotes) {
53!
1457
              t_bogusremotes->push_back(comboWriter->d_source);
53✔
1458
            }
53✔
1459
            if (t_bogusqueryring) {
53!
1460
              t_bogusqueryring->push_back({comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype});
53✔
1461
            }
53✔
1462
            if (g_dnssecLogBogus || resolver.doLog() || g_dnssecmode == DNSSECMode::ValidateForLog) {
53!
1463
              log->info(Logr::Notice, "Validates as Bogus");
53✔
1464
            }
53✔
1465

1466
            // Does the query or validation mode sending out a SERVFAIL on validation errors?
1467
            if (!packetWriter.getHeader()->cd && (g_dnssecmode == DNSSECMode::ValidateAll || comboWriter->d_mdp.d_header.ad || DNSSECOK)) {
53!
1468
              if (resolver.doLog()) {
44!
1469
                log->info(Logr::Notice, "Sending out SERVFAIL because recursor or query demands it for Bogus results");
44✔
1470
              }
44✔
1471

1472
              packetWriter.getHeader()->rcode = RCode::ServFail;
44✔
1473
              goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
44✔
1474
            }
44✔
1475
            else {
9✔
1476
              if (resolver.doLog()) {
9!
1477
                log->info(Logr::Notice, "Sending out SERVFAIL because recursor or query demands it for Bogus results");
9✔
1478
              }
9✔
1479
            }
9✔
1480
          }
53✔
1481
        }
2,561✔
1482
        catch (const ImmediateServFailException& e) {
2,561✔
1483
          if (g_logCommonErrors) {
×
1484
            resolver.d_slog->error(Logr::Notice, e.reason, "Sending SERVFAIL during validation", "exception", Logging::Loggable("ImmediateServFailException"));
×
1485
          }
×
1486
          goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
×
1487
        }
×
1488
        catch (const pdns::validation::TooManySEC3IterationsException& e) {
2,561✔
1489
          if (g_logCommonErrors || (g_dnssecLogBogus && resolver.getDNSSECLimitHit())) {
×
1490
            resolver.d_slog->error(Logr::Notice, e.what(), "Sending SERVFAIL during validation", "exception", Logging::Loggable("TooManySEC3IterationsException"), "dnsseclimithit", Logging::Loggable(resolver.getDNSSECLimitHit()));
×
1491
          }
×
1492
          goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
×
1493
        }
×
1494
      }
2,561✔
1495

1496
      if (!ret.empty()) {
3,109✔
1497
#ifdef notyet
1498
        // As dedupping is relatively expensive do not dedup in general. We do have a few cases
1499
        // where we call dedup explicitly, e.g. when doing NAT64 or when adding NSEC records in
1500
        // doCNAMECacheCheck
1501
        pdns::dedupRecords(ret);
1502
#endif
1503
        pdns::orderAndShuffle(ret, false);
3,074✔
1504
        if (auto listToSort = luaconfsLocal->sortlist.getOrderCmp(comboWriter->d_source)) {
3,074✔
1505
          stable_sort(ret.begin(), ret.end(), *listToSort);
1✔
1506
          variableAnswer = true;
1✔
1507
        }
1✔
1508
      }
3,074✔
1509

1510
      bool needCommit = false;
3,109✔
1511
      for (const auto& record : ret) {
10,142✔
1512
        if (!DNSSECOK && (record.d_type == QType::NSEC3 || ((record.d_type == QType::RRSIG || record.d_type == QType::NSEC) && ((comboWriter->d_mdp.d_qtype != record.d_type && comboWriter->d_mdp.d_qtype != QType::ANY) || (record.d_place != DNSResourceRecord::ANSWER && record.d_place != DNSResourceRecord::ADDITIONAL))))) {
10,142!
1513
          continue;
115✔
1514
        }
115✔
1515

1516
        if (!addRecordToPacket(packetWriter, record, minTTL, comboWriter->d_ttlCap, maxanswersize, seenAuthSOA)) {
10,027✔
1517
          needCommit = false;
4✔
1518
          break;
4✔
1519
        }
4✔
1520
        needCommit = true;
10,023✔
1521

1522
        bool udr = false;
10,023✔
1523
#ifdef NOD_ENABLED
10,023✔
1524
        if (g_udrEnabled) {
10,023✔
1525
          udr = udrCheckUniqueDNSRecord(nodlogger, comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype, record);
3✔
1526
          if (!hasUDR && udr) {
3!
1527
            hasUDR = true;
3✔
1528
          }
3✔
1529
        }
3✔
1530
#endif /* NOD ENABLED */
10,023✔
1531

1532
        if (t_protobufServers.servers) {
10,023✔
1533
          // Max size is 64k, but we're conservative here, as other fields are added after the answers have been added
1534
          // If a single answer causes a too big protobuf message, it will be dropped by queueData()
1535
          // But note addRR has code to prevent that
1536
          if (pbMessage.size() < std::numeric_limits<uint16_t>::max() / 2) {
36!
1537
            pbMessage.addRR(record, luaconfsLocal->protobufExportConfig.exportTypes, udr);
36✔
1538
          }
36✔
1539
        }
36✔
1540
      }
10,023✔
1541
      if (needCommit) {
3,109✔
1542
        packetWriter.commit();
3,070✔
1543
      }
3,070✔
1544
#ifdef NOD_ENABLED
3,109✔
1545
#ifdef HAVE_FSTRM
3,109✔
1546
      if (hasUDR) {
3,109✔
1547
        if (isEnabledForUDRs(t_nodFrameStreamServersInfo.servers)) {
3✔
1548
          struct timespec timeSpec{};
2✔
1549
          std::string str;
2✔
1550
          if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
2!
1551
            TIMEVAL_TO_TIMESPEC(&comboWriter->d_kernelTimestamp, &timeSpec); // NOLINT
×
1552
          }
×
1553
          else {
2✔
1554
            TIMEVAL_TO_TIMESPEC(&comboWriter->d_now, &timeSpec); // NOLINT
2✔
1555
          }
2✔
1556
          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!
1557
          str = message.getBuffer();
2✔
1558
          for (auto& logger : *(t_nodFrameStreamServersInfo.servers)) {
2✔
1559
            if (logger->logUDRs()) {
2!
1560
              remoteLoggerQueueData(*logger, str);
2✔
1561
            }
2✔
1562
          }
2✔
1563
        }
2✔
1564
      }
3✔
1565
#endif // HAVE_FSTRM
3,109✔
1566
#endif // NOD_ENABLED
3,109✔
1567
    }
3,109✔
1568
  sendit:;
3,179✔
1569

1570
    if (g_useIncomingECS && comboWriter->d_ecsFound && !resolver.wasVariable() && !variableAnswer) {
3,179!
1571
      EDNSSubnetOpts ednsOptions;
393✔
1572
      ednsOptions.setSource(comboWriter->d_ednssubnet.getSource());
393✔
1573
      ComboAddress sourceAddr;
393✔
1574
      sourceAddr.reset();
393✔
1575
      sourceAddr.sin4.sin_family = ednsOptions.getFamily();
393✔
1576
      ednsOptions.setScopePrefixLength(0);
393✔
1577
      auto ecsPayload = ednsOptions.makeOptString();
393✔
1578

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

1583
        maxanswersize -= EDNSOptionCodeSize + EDNSOptionLengthSize + ecsPayload.size();
392✔
1584

1585
        returnedEdnsOptions.emplace_back(EDNSOptionCode::ECS, std::move(ecsPayload));
392✔
1586
      }
392✔
1587
    }
393✔
1588

1589
    if (haveEDNS && addPaddingToResponse) {
3,179✔
1590
      size_t currentSize = packetWriter.getSizeWithOpts(returnedEdnsOptions);
5✔
1591
      /* we don't use maxawnswersize because it accounts for some EDNS options, but
1592
         not all of them (for example ECS) */
1593
      size_t maxSize = min(static_cast<uint16_t>(edo.d_packetsize >= 512 ? edo.d_packetsize : 512), g_udpTruncationThreshold);
5!
1594

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

1613
    if (haveEDNS) {
3,179✔
1614
      auto state = resolver.getValidationState();
1,325✔
1615
      if (comboWriter->d_extendedErrorCode || resolver.d_extendedError || (SyncRes::s_addExtendedResolutionDNSErrors && vStateIsBogus(state))) {
1,325✔
1616
        EDNSExtendedError::code code = EDNSExtendedError::code::Other;
90✔
1617
        std::string extra;
90✔
1618

1619
        if (comboWriter->d_extendedErrorCode) {
90✔
1620
          code = static_cast<EDNSExtendedError::code>(*comboWriter->d_extendedErrorCode);
8✔
1621
          extra = std::move(comboWriter->d_extendedErrorExtra);
8✔
1622
        }
8✔
1623
        else if (resolver.d_extendedError) {
82✔
1624
          code = static_cast<EDNSExtendedError::code>(resolver.d_extendedError->infoCode);
40✔
1625
          extra = std::move(resolver.d_extendedError->extraText);
40✔
1626
        }
40✔
1627
        else {
42✔
1628
          switch (state) {
42✔
1629
          case vState::BogusNoValidDNSKEY:
29✔
1630
            code = EDNSExtendedError::code::DNSKEYMissing;
29✔
1631
            break;
29✔
1632
          case vState::BogusInvalidDenial:
×
1633
            code = EDNSExtendedError::code::NSECMissing;
×
1634
            break;
×
1635
          case vState::BogusUnableToGetDSs:
×
1636
            code = EDNSExtendedError::code::DNSSECBogus;
×
1637
            break;
×
1638
          case vState::BogusUnableToGetDNSKEYs:
×
1639
            code = EDNSExtendedError::code::DNSKEYMissing;
×
1640
            break;
×
1641
          case vState::BogusSelfSignedDS:
×
1642
            code = EDNSExtendedError::code::DNSSECBogus;
×
1643
            break;
×
1644
          case vState::BogusNoRRSIG:
2✔
1645
            code = EDNSExtendedError::code::RRSIGsMissing;
2✔
1646
            break;
2✔
1647
          case vState::BogusNoValidRRSIG:
4✔
1648
            code = EDNSExtendedError::code::DNSSECBogus;
4✔
1649
            break;
4✔
1650
          case vState::BogusMissingNegativeIndication:
1✔
1651
            code = EDNSExtendedError::code::NSECMissing;
1✔
1652
            break;
1✔
1653
          case vState::BogusSignatureNotYetValid:
2✔
1654
            code = EDNSExtendedError::code::SignatureNotYetValid;
2✔
1655
            break;
2✔
1656
          case vState::BogusSignatureExpired:
4✔
1657
            code = EDNSExtendedError::code::SignatureExpired;
4✔
1658
            break;
4✔
1659
          case vState::BogusUnsupportedDNSKEYAlgo:
×
1660
            code = EDNSExtendedError::code::UnsupportedDNSKEYAlgorithm;
×
1661
            break;
×
1662
          case vState::BogusUnsupportedDSDigestType:
×
1663
            code = EDNSExtendedError::code::UnsupportedDSDigestType;
×
1664
            break;
×
1665
          case vState::BogusNoZoneKeyBitSet:
×
1666
            code = EDNSExtendedError::code::NoZoneKeyBitSet;
×
1667
            break;
×
1668
          case vState::BogusRevokedDNSKEY:
×
1669
          case vState::BogusInvalidDNSKEYProtocol:
×
1670
            code = EDNSExtendedError::code::DNSSECBogus;
×
1671
            break;
×
1672
          default:
×
1673
            throw std::runtime_error("Bogus validation state not handled: " + vStateToString(state));
×
1674
          }
42✔
1675
        }
42✔
1676

1677
        EDNSExtendedError eee;
90✔
1678
        eee.infoCode = static_cast<uint16_t>(code);
90✔
1679
        eee.extraText = std::move(extra);
90✔
1680

1681
        if (packetWriter.size() < maxanswersize && (maxanswersize - packetWriter.size()) >= (EDNSOptionCodeSize + EDNSOptionLengthSize + sizeof(eee.infoCode) + eee.extraText.size())) {
90!
1682
          returnedEdnsOptions.emplace_back(EDNSOptionCode::EXTENDEDERROR, makeEDNSExtendedErrorOptString(eee));
89✔
1683
        }
89✔
1684
      }
90✔
1685

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

1696
    t_Counters.at(rec::ResponseStats::responseStats).submitResponse(comboWriter->d_mdp.d_qtype, packet.size(), packetWriter.getHeader()->rcode);
3,179✔
1697
    updateResponseStats(res, comboWriter->d_source, packet.size(), &comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype);
3,179✔
1698
#ifdef NOD_ENABLED
3,179✔
1699
    bool nod = false;
3,179✔
1700
    if (g_nodEnabled) {
3,179✔
1701
      if (nodCheckNewDomain(nodlogger, comboWriter->d_mdp.d_qname)) {
3!
1702
        nod = true;
3✔
1703
#ifdef HAVE_FSTRM
3✔
1704
        if (isEnabledForNODs(t_nodFrameStreamServersInfo.servers)) {
3✔
1705
          struct timespec timeSpec{};
2✔
1706
          std::string str;
2✔
1707
          if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
2!
1708
            TIMEVAL_TO_TIMESPEC(&comboWriter->d_kernelTimestamp, &timeSpec); // NOLINT
×
1709
          }
×
1710
          else {
2✔
1711
            TIMEVAL_TO_TIMESPEC(&comboWriter->d_now, &timeSpec); // NOLINT
2✔
1712
          }
2✔
1713
          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!
1714
          str = message.getBuffer();
2✔
1715

1716
          for (auto& logger : *(t_nodFrameStreamServersInfo.servers)) {
2✔
1717
            if (logger->logNODs()) {
2!
1718
              remoteLoggerQueueData(*logger, str);
2✔
1719
            }
2✔
1720
          }
2✔
1721
        }
2✔
1722
#endif // HAVE_FSTRM
3✔
1723
      }
3✔
1724
    }
3✔
1725
#endif /* NOD_ENABLED */
3,179✔
1726

1727
    if (variableAnswer || resolver.wasVariable()) {
3,179✔
1728
      t_Counters.at(rec::Counter::variableResponses)++;
178✔
1729
    }
178✔
1730

1731
    if (t_protobufServers.servers && !(luaconfsLocal->protobufExportConfig.taggedOnly && appliedPolicy.getName().empty() && comboWriter->d_policyTags.empty())) {
3,179!
1732
      // Start constructing embedded DNSResponse object
1733
      pbMessage.setResponseCode(packetWriter.getHeader()->rcode);
28✔
1734
      if (!appliedPolicy.getName().empty()) {
28✔
1735
        pbMessage.setAppliedPolicy(appliedPolicy.getName());
3✔
1736
        pbMessage.setAppliedPolicyType(appliedPolicy.d_type);
3✔
1737
        pbMessage.setAppliedPolicyTrigger(appliedPolicy.getTrigger());
3✔
1738
        pbMessage.setAppliedPolicyHit(appliedPolicy.getHit());
3✔
1739
        pbMessage.setAppliedPolicyKind(appliedPolicy.d_kind);
3✔
1740
      }
3✔
1741
      pbMessage.setInBytes(packet.size());
28✔
1742
      pbMessage.setValidationState(resolver.getValidationState());
28✔
1743
      // See if we want to store the policyTags into the PC
1744
      addPolicyTagsToPBMessageIfNeeded(*comboWriter, pbMessage);
28✔
1745

1746
      // Take s snap of the current protobuf buffer state to store in the PC
1747
      pbDataForCache = boost::make_optional(RecursorPacketCache::PBData{
28✔
1748
        pbMessage.getMessageBuf(),
28✔
1749
        pbMessage.getResponseBuf(),
28✔
1750
        !appliedPolicy.getName().empty() || !comboWriter->d_policyTags.empty()});
28✔
1751
#ifdef NOD_ENABLED
28✔
1752
      // if (g_udrEnabled) ??
1753
      pbMessage.clearUDR(pbDataForCache->d_response);
28✔
1754
#endif
28✔
1755
    }
28✔
1756

1757
    const bool intoPC = g_packetCache && !variableAnswer && !resolver.wasVariable();
3,179✔
1758
    if (intoPC) {
3,179✔
1759
      minTTL = capPacketCacheTTL(*packetWriter.getHeader(), minTTL, seenAuthSOA);
2,867✔
1760
      g_packetCache->insertResponsePacket(comboWriter->d_tag, comboWriter->d_qhash, std::move(comboWriter->d_query), comboWriter->d_mdp.d_qname,
2,867✔
1761
                                          comboWriter->d_mdp.d_qtype, comboWriter->d_mdp.d_qclass,
2,867✔
1762
                                          string(reinterpret_cast<const char*>(&*packet.begin()), packet.size()), // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2,867✔
1763
                                          g_now.tv_sec,
2,867✔
1764
                                          minTTL,
2,867✔
1765
                                          dnsQuestion.validationState,
2,867✔
1766
                                          std::move(pbDataForCache), comboWriter->d_tcp);
2,867✔
1767
    }
2,867✔
1768

1769
    if (g_regressionTestMode) {
3,179✔
1770
      t_Counters.updateSnap(g_regressionTestMode);
621✔
1771
    }
621✔
1772

1773
    if (!comboWriter->d_tcp) {
3,179✔
1774
      struct msghdr msgh{};
2,388✔
1775
      struct iovec iov{};
2,388✔
1776
      cmsgbuf_aligned cbuf{};
2,388✔
1777
      fillMSGHdr(&msgh, &iov, &cbuf, 0, reinterpret_cast<char*>(&*packet.begin()), packet.size(), &comboWriter->d_remote); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2,388✔
1778
      msgh.msg_control = nullptr;
2,388✔
1779

1780
      if (g_fromtosockets.count(comboWriter->d_socket) > 0) {
2,388✔
1781
        addCMsgSrcAddr(&msgh, &cbuf, &comboWriter->d_local, 0);
1✔
1782
      }
1✔
1783
      int sendErr = sendOnNBSocket(comboWriter->d_socket, &msgh);
2,388✔
1784
      if (sendErr != 0 && g_logCommonErrors) {
2,388!
1785
        g_slogudpin->error(Logr::Warning, sendErr, "Sending UDP reply to client failed");
×
1786
      }
×
1787
    }
2,388✔
1788
    else {
791✔
1789
      bool hadError = sendResponseOverTCP(comboWriter, packet);
791✔
1790
      finishTCPReply(comboWriter, hadError, true);
791✔
1791
      tcpGuard.setHandled();
791✔
1792
    }
791✔
1793

1794
    resolver.d_eventTrace.add(RecEventTrace::AnswerSent);
3,179✔
1795

1796
    // Now do the per query changing part of the protobuf message
1797
    if (t_protobufServers.servers && !(luaconfsLocal->protobufExportConfig.taggedOnly && appliedPolicy.getName().empty() && comboWriter->d_policyTags.empty())) {
3,179!
1798
      // Below are the fields that are not stored in the packet cache and will be appended here and on a cache hit
1799
      if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
28!
1800
        pbMessage.setQueryTime(comboWriter->d_kernelTimestamp.tv_sec, comboWriter->d_kernelTimestamp.tv_usec);
×
1801
      }
×
1802
      else {
28✔
1803
        pbMessage.setQueryTime(comboWriter->d_now.tv_sec, comboWriter->d_now.tv_usec);
28✔
1804
      }
28✔
1805
      pbMessage.setMessageIdentity(comboWriter->d_uuid);
28✔
1806
      pbMessage.setSocketProtocol(comboWriter->d_tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP);
28✔
1807

1808
      if (!luaconfsLocal->protobufExportConfig.logMappedFrom) {
28✔
1809
        pbMessage.setSocketFamily(comboWriter->d_source.sin4.sin_family);
26✔
1810
        Netmask requestorNM(comboWriter->d_source, comboWriter->d_source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
26!
1811
        ComboAddress requestor = requestorNM.getMaskedNetwork();
26✔
1812
        pbMessage.setFrom(requestor);
26✔
1813
        pbMessage.setFromPort(comboWriter->d_source.getPort());
26✔
1814
      }
26✔
1815
      else {
2✔
1816
        pbMessage.setSocketFamily(comboWriter->d_mappedSource.sin4.sin_family);
2✔
1817
        Netmask requestorNM(comboWriter->d_mappedSource, comboWriter->d_mappedSource.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
2!
1818
        ComboAddress requestor = requestorNM.getMaskedNetwork();
2✔
1819
        pbMessage.setFrom(requestor);
2✔
1820
        pbMessage.setFromPort(comboWriter->d_mappedSource.getPort());
2✔
1821
      }
2✔
1822

1823
      pbMessage.setTo(comboWriter->d_destination);
28✔
1824
      pbMessage.setId(comboWriter->d_mdp.d_header.id);
28✔
1825

1826
      pbMessage.setTime();
28✔
1827
      pbMessage.setEDNSSubnet(comboWriter->d_ednssubnet.getSource(), comboWriter->d_ednssubnet.getSource().isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
28!
1828
      pbMessage.setRequestorId(dnsQuestion.requestorId);
28✔
1829
      pbMessage.setDeviceId(dnsQuestion.deviceId);
28✔
1830
      pbMessage.setDeviceName(dnsQuestion.deviceName);
28✔
1831
      pbMessage.setToPort(comboWriter->d_destination.getPort());
28✔
1832
      pbMessage.addPolicyTags(comboWriter->d_gettagPolicyTags);
28✔
1833
      pbMessage.setWorkerId(RecThreadInfo::thread_local_id());
28✔
1834
      pbMessage.setPacketCacheHit(false);
28✔
1835
      pbMessage.setOutgoingQueries(resolver.d_outqueries);
28✔
1836
      for (const auto& metaValue : dnsQuestion.meta) {
28✔
1837
        pbMessage.setMeta(metaValue.first, metaValue.second.stringVal, metaValue.second.intVal);
2✔
1838
      }
2✔
1839
#ifdef NOD_ENABLED
28✔
1840
      if (g_nodEnabled) {
28!
1841
        if (nod) {
×
1842
          pbMessage.setNewlyObservedDomain(true);
×
1843
          pbMessage.addPolicyTag(g_nod_pbtag);
×
1844
        }
×
1845
        if (hasUDR) {
×
1846
          pbMessage.addPolicyTag(g_udr_pbtag);
×
1847
        }
×
1848
      }
×
1849
#endif /* NOD_ENABLED */
28✔
1850
      if (resolver.d_eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_pb)) {
28!
1851
        pbMessage.addEvents(resolver.d_eventTrace);
×
1852
      }
×
1853
      if (resolver.d_eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
28!
1854
        auto otTrace = pdns::trace::TracesData::boilerPlate("rec", comboWriter->d_mdp.d_qname.toLogString() + '/' + QType(comboWriter->d_mdp.d_qtype).toString(), resolver.d_eventTrace.convertToOT(resolver.d_otTrace));
×
1855
        string otData = otTrace.encode();
×
1856
        pbMessage.setOpenTelemetryData(otData);
×
1857
      }
×
1858
      if (comboWriter->d_logResponse) {
28✔
1859
        protobufLogResponse(pbMessage);
25✔
1860
      }
25✔
1861
    }
28✔
1862

1863
    if (resolver.d_eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_log)) {
3,179!
1864
      resolver.d_slog->info(Logr::Info, resolver.d_eventTrace.toString()); // Maybe we want it to be more fancy?
×
1865
    }
×
1866

1867
    // Originally this code used a mix of floats, doubles, uint64_t with different units.
1868
    // Now it always uses an integral number of microseconds, except for averages, which use doubles
1869
    uint64_t spentUsec = uSec(resolver.getNow() - comboWriter->d_now);
3,179✔
1870
    if (!g_quiet) {
3,179✔
1871
      resolver.d_slog->info(Logr::Info, "Answer", "rd", Logging::Loggable(comboWriter->d_mdp.d_header.rd),
920✔
1872
                            "tc", Logging::Loggable(packetWriter.getHeader()->tc),
920✔
1873
                            "answers", Logging::Loggable(ntohs(packetWriter.getHeader()->ancount)),
920✔
1874
                            "additional", Logging::Loggable(ntohs(packetWriter.getHeader()->arcount)),
920✔
1875
                            "outqueries", Logging::Loggable(resolver.d_outqueries),
920✔
1876
                            "netms", Logging::Loggable(resolver.d_totUsec / 1000.0),
920✔
1877
                            "totms", Logging::Loggable(static_cast<double>(spentUsec) / 1000.0),
920✔
1878
                            "throttled", Logging::Loggable(resolver.d_throttledqueries),
920✔
1879
                            "timeouts", Logging::Loggable(resolver.d_timeouts),
920✔
1880
                            "tcpout", Logging::Loggable(resolver.d_tcpoutqueries),
920✔
1881
                            "dotout", Logging::Loggable(resolver.d_dotoutqueries),
920✔
1882
                            "rcode", Logging::Loggable(res),
920✔
1883
                            "validationState", Logging::Loggable(resolver.getValidationState()),
920✔
1884
                            "answer-is-variable", Logging::Loggable(resolver.wasVariable()),
920✔
1885
                            "into-packetcache", Logging::Loggable(intoPC),
920✔
1886
                            "maxdepth", Logging::Loggable(resolver.d_maxdepth));
920✔
1887
    }
920✔
1888

1889
    if (comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Query)) {
3,179✔
1890
      if (resolver.d_outqueries != 0 || resolver.d_throttledqueries != 0 || resolver.d_authzonequeries != 0) {
3,155✔
1891
        g_recCache->incCacheMisses();
2,635✔
1892
      }
2,635✔
1893
      else {
520✔
1894
        g_recCache->incCacheHits();
520✔
1895
      }
520✔
1896
    }
3,155✔
1897

1898
    t_Counters.at(rec::Histogram::answers)(spentUsec);
3,179✔
1899
    t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
3,179✔
1900

1901
    auto newLat = static_cast<double>(spentUsec);
3,179✔
1902
    newLat = min(newLat, g_networkTimeoutMsec * 1000.0); // outliers of several minutes exist..
3,179✔
1903
    t_Counters.at(rec::DoubleWAvgCounter::avgLatencyUsec).addToRollingAvg(newLat, g_latencyStatSize);
3,179✔
1904
    // no worries, we do this for packet cache hits elsewhere
1905

1906
    if (spentUsec >= resolver.d_totUsec) {
3,179✔
1907
      uint64_t ourtime = spentUsec - resolver.d_totUsec;
3,178✔
1908
      t_Counters.at(rec::Histogram::ourtime)(ourtime);
3,178✔
1909
      newLat = static_cast<double>(ourtime); // usec
3,178✔
1910
      t_Counters.at(rec::DoubleWAvgCounter::avgLatencyOursUsec).addToRollingAvg(newLat, g_latencyStatSize);
3,178✔
1911
    }
3,178✔
1912

1913
#ifdef NOD_ENABLED
3,179✔
1914
    if (nod) {
3,179✔
1915
      sendNODLookup(nodlogger, comboWriter->d_mdp.d_qname);
3✔
1916
    }
3✔
1917
#endif /* NOD_ENABLED */
3,179✔
1918

1919
    //    cout<<dc->d_mdp.d_qname<<"\t"<<MT->getUsec()<<"\t"<<sr.d_outqueries<<endl;
1920
  }
3,179✔
1921
  catch (const PDNSException& ae) {
3,192✔
1922
    resolver.d_slog->error(Logr::Error, ae.reason, "startDoResolve problem", "exception", Logging::Loggable("PDNSException"));
×
1923
  }
×
1924
  catch (const MOADNSException& mde) {
3,192✔
1925
    resolver.d_slog->error(Logr::Error, mde.what(), "DNS parser error");
×
1926
  }
×
1927
  catch (const std::exception& e) {
3,192✔
1928
    resolver.d_slog->error(Logr::Error, e.what(), "Exception in resolver context", "exception", Logging::Loggable("std::exception"));
×
1929

1930
    // Luawrapper nests the exception from Lua, so we unnest it here
1931
    try {
×
1932
      std::rethrow_if_nested(e);
×
1933
    }
×
1934
    catch (const std::exception& ne) {
×
1935
      resolver.d_slog->error(Logr::Error, ne.what(), "Nested exception in resolver context", Logging::Loggable("std::exception"));
×
1936
    }
×
1937
    catch (...) {
×
1938
      ;
×
1939
    }
×
1940
  }
×
1941
  catch (...) {
3,192✔
1942
    resolver.d_slog->info(Logr::Error, "Any other exception in a resolver context");
×
1943
  }
×
1944

1945
  runTaskOnce(g_logCommonErrors);
3,179✔
1946

1947
  static const size_t stackSizeThreshold = 9 * ::arg().asNum("stack-size") / 10;
3,179✔
1948
  if (g_multiTasker->getMaxStackUsage() >= stackSizeThreshold) {
3,179!
1949
    resolver.d_slog->info(Logr::Error, "Reached mthread stack usage of 90%",
×
1950
                          "stackUsage", Logging::Loggable(g_multiTasker->getMaxStackUsage()),
×
1951
                          "outqueries", Logging::Loggable(resolver.d_outqueries),
×
1952
                          "netms", Logging::Loggable(resolver.d_totUsec / 1000.0),
×
1953
                          "throttled", Logging::Loggable(resolver.d_throttledqueries),
×
1954
                          "timeouts", Logging::Loggable(resolver.d_timeouts),
×
1955
                          "tcpout", Logging::Loggable(resolver.d_tcpoutqueries),
×
1956
                          "dotout", Logging::Loggable(resolver.d_dotoutqueries),
×
1957
                          "validationState", Logging::Loggable(resolver.getValidationState()));
×
1958
  }
×
1959
  t_Counters.at(rec::Counter::maxMThreadStackUsage) = max(g_multiTasker->getMaxStackUsage(), t_Counters.at(rec::Counter::maxMThreadStackUsage));
3,179✔
1960
  t_Counters.updateSnap(g_regressionTestMode);
3,179✔
1961
}
3,179✔
1962

1963
void getQNameAndSubnet(const std::string& question, DNSName* dnsname, uint16_t* qtype, uint16_t* qclass,
1964
                       bool& foundECS, EDNSSubnetOpts* ednssubnet, EDNSOptionViewMap* options, boost::optional<uint32_t>& ednsVersion)
1965
{
157✔
1966
  const dnsheader_aligned dnshead(question.data());
157✔
1967
  const dnsheader* dhPointer = dnshead.get();
157✔
1968
  size_t questionLen = question.length();
157✔
1969
  unsigned int consumed = 0;
157✔
1970
  *dnsname = DNSName(question.c_str(), static_cast<int>(questionLen), sizeof(dnsheader), false, qtype, qclass, &consumed);
157✔
1971

1972
  size_t pos = sizeof(dnsheader) + consumed + 4;
157✔
1973
  const size_t headerSize = /* root */ 1 + sizeof(dnsrecordheader);
157✔
1974
  const uint16_t arcount = ntohs(dhPointer->arcount);
157✔
1975

1976
  for (uint16_t arpos = 0; arpos < arcount && questionLen >= (pos + headerSize) && !foundECS; arpos++) {
174!
1977
    if (question.at(pos) != 0) {
115!
1978
      /* not an OPT, bye. */
1979
      return;
×
1980
    }
×
1981

1982
    pos += 1;
115✔
1983
    const auto* drh = reinterpret_cast<const dnsrecordheader*>(&question.at(pos)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
115✔
1984
    if (ntohs(drh->d_type) == QType::OPT) {
115!
1985
      uint32_t edns{};
115✔
1986
      memcpy(&edns, &drh->d_ttl, sizeof(edns)); // drh is not neccesarily aligned, so no uint32 assignment can be done
115✔
1987
      ednsVersion = edns;
115✔
1988
    }
115✔
1989
    pos += sizeof(dnsrecordheader);
115✔
1990

1991
    if (pos >= questionLen) {
115✔
1992
      return;
98✔
1993
    }
98✔
1994

1995
    /* OPT root label (1) followed by type (2) */
1996
    if (ntohs(drh->d_type) == QType::OPT) {
17!
1997
      if (options == nullptr) {
17✔
1998
        size_t ecsStartPosition = 0;
13✔
1999
        size_t ecsLen = 0;
13✔
2000
        /* we need to pass the record len */
2001
        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✔
2002
        if (res == 0 && ecsLen > 4) {
13!
2003
          EDNSSubnetOpts eso;
10✔
2004
          if (EDNSSubnetOpts::getFromString(&question.at(pos - sizeof(drh->d_clen) + ecsStartPosition + 4), ecsLen - 4, &eso)) {
10!
2005
            *ednssubnet = eso;
10✔
2006
            foundECS = true;
10✔
2007
          }
10✔
2008
        }
10✔
2009
      }
13✔
2010
      else {
4✔
2011
        /* we need to pass the record len */
2012
        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✔
2013
        if (res == 0) {
4!
2014
          const auto iter = options->find(EDNSOptionCode::ECS);
4✔
2015
          if (iter != options->end() && !iter->second.values.empty() && iter->second.values.at(0).content != nullptr && iter->second.values.at(0).size > 0) {
4!
2016
            EDNSSubnetOpts eso;
4✔
2017
            if (EDNSSubnetOpts::getFromString(iter->second.values.at(0).content, iter->second.values.at(0).size, &eso)) {
4!
2018
              *ednssubnet = eso;
4✔
2019
              foundECS = true;
4✔
2020
            }
4✔
2021
          }
4✔
2022
        }
4✔
2023
      }
4✔
2024
    }
17✔
2025

2026
    pos += ntohs(drh->d_clen);
17✔
2027
  }
17✔
2028
}
157✔
2029

2030
bool checkForCacheHit(bool qnameParsed, unsigned int tag, const string& data,
2031
                      DNSName& qname, uint16_t& qtype, uint16_t& qclass,
2032
                      const struct timeval& now,
2033
                      string& response, uint32_t& qhash,
2034
                      RecursorPacketCache::OptPBData& pbData, bool tcp, const ComboAddress& source, const ComboAddress& mappedSource)
2035
{
6,157✔
2036
  if (!g_packetCache) {
6,157✔
2037
    return false;
136✔
2038
  }
136✔
2039
  bool cacheHit = false;
6,021✔
2040
  uint32_t age = 0;
6,021✔
2041
  vState valState = vState::Indeterminate;
6,021✔
2042

2043
  if (qnameParsed) {
6,021✔
2044
    cacheHit = g_packetCache->getResponsePacket(tag, data, qname, qtype, qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
133✔
2045
  }
133✔
2046
  else {
5,888✔
2047
    cacheHit = g_packetCache->getResponsePacket(tag, data, qname, &qtype, &qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
5,888✔
2048
  }
5,888✔
2049

2050
  if (cacheHit) {
6,021✔
2051
    if (vStateIsBogus(valState)) {
2,990!
2052
      if (t_bogusremotes) {
×
2053
        t_bogusremotes->push_back(source);
×
2054
      }
×
2055
      if (t_bogusqueryring) {
×
2056
        t_bogusqueryring->push_back({qname, qtype});
×
2057
      }
×
2058
    }
×
2059

2060
    // This is only to get the proxyMapping suffixMatch stats right i the case of a PC hit
2061
    if (t_proxyMapping && source != mappedSource) {
2,990!
2062
      if (const auto* found = t_proxyMapping->lookup(source)) {
×
2063
        if (found->second.suffixMatchNode) {
×
2064
          if (found->second.suffixMatchNode->check(qname)) {
×
2065
            ++found->second.stats.suffixMatches;
×
2066
          }
×
2067
        }
×
2068
      }
×
2069
    }
×
2070

2071
    if (response.length() >= sizeof(struct dnsheader)) {
2,991✔
2072
      dnsheader_aligned dh_aligned(response.data());
2,975✔
2073
      ageDNSPacket(response, age, dh_aligned);
2,975✔
2074
      const auto* dhp = dh_aligned.get();
2,975✔
2075
      updateResponseStats(dhp->rcode, source, response.length(), nullptr, 0);
2,975✔
2076
      t_Counters.at(rec::ResponseStats::responseStats).submitResponse(qtype, response.length(), dhp->rcode);
2,975✔
2077
    }
2,975✔
2078

2079
    // we assume 0 usec
2080
    t_Counters.at(rec::DoubleWAvgCounter::avgLatencyUsec).addToRollingAvg(0.0, g_latencyStatSize);
2,990✔
2081
    t_Counters.at(rec::DoubleWAvgCounter::avgLatencyOursUsec).addToRollingAvg(0.0, g_latencyStatSize);
2,990✔
2082
#if 0
2083
    // XXX changes behaviour compared to old code!
2084
    t_Counters.at(rec::Counter::answers)(0);
2085
    t_Counters.at(rec::Counter::ourtime)(0);
2086
#endif
2087
  }
2,990✔
2088

2089
  return cacheHit;
6,021✔
2090
}
6,157✔
2091

2092
static void* pleaseWipeCaches(const DNSName& canon, bool subtree, uint16_t qtype)
2093
{
2✔
2094
  auto res = wipeCaches(canon, subtree, qtype);
2✔
2095
  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✔
2096
  return nullptr;
2✔
2097
}
2✔
2098

2099
void requestWipeCaches(const DNSName& canon)
2100
{
2✔
2101
  // send a message to the handler thread asking it
2102
  // to wipe all of the caches
2103
  ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: pointer owner
2✔
2104
  tmsg->func = [=] { return pleaseWipeCaches(canon, true, 0xffff); };
2✔
2105
  tmsg->wantAnswer = false;
2✔
2106
  if (write(RecThreadInfo::info(0).getPipes().writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // NOLINT: correct sizeof
2!
2107
    delete tmsg; // NOLINT: pointer owner
×
2108

2109
    unixDie("write to thread pipe returned wrong size or error");
×
2110
  }
×
2111
  // coverity[leaked_storage]
2112
}
2✔
2113

2114
bool expectProxyProtocol(const ComboAddress& from, const ComboAddress& listenAddress)
2115
{
5,768✔
2116
  if (!t_proxyProtocolACL) {
5,768✔
2117
    return false;
5,695✔
2118
  }
5,695✔
2119
  if (t_proxyProtocolACL->match(from)) {
73✔
2120
    if (!t_proxyProtocolExceptions) {
65✔
2121
      return true;
60✔
2122
    }
60✔
2123
    return t_proxyProtocolExceptions->count(listenAddress) == 0;
5✔
2124
  }
65✔
2125
  return false;
8✔
2126
}
73✔
2127

2128
// fromaddr: the address the query is coming from
2129
// destaddr: the address the query was received on
2130
// source: the address we assume the query is coming from, might be set by proxy protocol
2131
// destination: the address we assume the query was sent to, might be set by proxy protocol
2132
// mappedSource: the address we assume the query is coming from. Differs from source if table based mapping has been applied
2133
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
2134
{
5,340✔
2135
  RecThreadInfo::self().incNumberOfDistributedQueries();
5,340✔
2136
  gettimeofday(&g_now, nullptr);
5,340✔
2137
  if (tval.tv_sec != 0) {
5,340✔
2138
    struct timeval diff = g_now - tval;
5,306✔
2139
    double delta = (static_cast<double>(diff.tv_sec) * 1000 + static_cast<double>(diff.tv_usec) / 1000.0);
5,306✔
2140

2141
    if (delta > 1000.0) {
5,306✔
2142
      t_Counters.at(rec::Counter::tooOldDrops)++;
10✔
2143
      return nullptr;
10✔
2144
    }
10✔
2145
  }
5,306✔
2146

2147
  ++t_Counters.at(rec::Counter::qcounter);
5,330✔
2148

2149
  if (fromaddr.sin4.sin_family == AF_INET6) {
5,330✔
2150
    t_Counters.at(rec::Counter::ipv6qcounter)++;
2✔
2151
  }
2✔
2152

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

2212
    // We do not have a SyncRes specific Lua context at this point yet, so ok to use t_pdl
2213
    if (needEDNSParse || (t_pdl && (t_pdl->hasGettagFunc() || t_pdl->hasGettagFFIFunc())) || dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
5,339✔
2214
      try {
112✔
2215
        EDNSOptionViewMap ednsOptions;
112✔
2216

2217
        ecsFound = false;
112✔
2218

2219
        getQNameAndSubnet(question, &qname, &qtype, &qclass,
112✔
2220
                          ecsFound, &ednssubnet,
112✔
2221
                          (g_gettagNeedsEDNSOptions || SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) ? &ednsOptions : nullptr,
112!
2222
                          ednsVersion);
112✔
2223

2224
        qnameParsed = true;
112✔
2225
        ecsParsed = true;
112✔
2226

2227
        if (SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
112!
2228
          pdns::trace::extractOTraceIDs(ednsOptions, otTrace);
×
2229
        }
×
2230
        if (t_pdl) {
112✔
2231
          try {
86✔
2232
            if (t_pdl->hasGettagFFIFunc()) {
86✔
2233
              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✔
2234

2235
              auto match = eventTrace.add(RecEventTrace::LuaGetTagFFI);
42✔
2236
              ctag = t_pdl->gettag_ffi(params);
42✔
2237
              eventTrace.add(RecEventTrace::LuaGetTagFFI, ctag, false, match);
42✔
2238
            }
42✔
2239
            else if (t_pdl->hasGettagFunc()) {
44✔
2240
              auto match = eventTrace.add(RecEventTrace::LuaGetTag);
33✔
2241
              ctag = t_pdl->gettag(source, ednssubnet.getSource(), destination, qname, qtype, &policyTags, data, ednsOptions, false, requestorId, deviceId, deviceName, routingTag, proxyProtocolValues);
33✔
2242
              eventTrace.add(RecEventTrace::LuaGetTag, ctag, false, match);
33✔
2243
            }
33✔
2244
          }
86✔
2245
          catch (const MOADNSException& moadnsexception) {
86✔
2246
            if (g_logCommonErrors) {
×
2247
              g_slogudpin->error(moadnsexception.what(), "Error parsing a query packet for tag determination", "qname", Logging::Loggable(qname), "excepion", Logging::Loggable("MOADNSException"));
×
2248
            }
×
2249
          }
×
2250
          catch (const std::exception& stdException) {
86✔
2251
            g_rateLimitedLogger.log(g_slogudpin, "Error parsing a query packet for tag determination", stdException, "qname", Logging::Loggable(qname), "remote", Logging::Loggable(fromaddr));
×
2252
          }
×
2253
        }
86✔
2254
      }
112✔
2255
      catch (const std::exception& stdException) {
112✔
2256
        g_rateLimitedLogger.log(g_slogudpin, "Error parsing a query packet for tag determination, setting tag=0", stdException);
×
2257
      }
×
2258
    }
112✔
2259

2260
    RecursorPacketCache::OptPBData pbData{boost::none};
5,330✔
2261
    if (t_protobufServers.servers) {
5,330✔
2262
      if (logQuery && !(luaconfsLocal->protobufExportConfig.taggedOnly && policyTags.empty())) {
33✔
2263
        protobufLogQuery(luaconfsLocal, uniqueId, source, destination, mappedSource, ednssubnet.getSource(), false, question.size(), qname, qtype, qclass, policyTags, requestorId, deviceId, deviceName, meta, ednsVersion, *dnsheader);
24✔
2264
      }
24✔
2265
    }
33✔
2266

2267
    if (ctag == 0 && !responsePaddingDisabled && g_paddingFrom.match(fromaddr)) {
5,338✔
2268
      ctag = g_paddingTag;
10✔
2269
    }
10✔
2270

2271
    if (dnsheader->opcode == static_cast<unsigned>(Opcode::Query)) {
5,340✔
2272
      /* It might seem like a good idea to skip the packet cache lookup if we know that the answer is not cacheable,
2273
         but it means that the hash would not be computed. If some script decides at a later time to mark back the answer
2274
         as cacheable we would cache it with a wrong tag, so better safe than sorry. */
2275
      auto match = eventTrace.add(RecEventTrace::PCacheCheck);
5,315✔
2276
      bool cacheHit = checkForCacheHit(qnameParsed, ctag, question, qname, qtype, qclass, g_now, response, qhash, pbData, false, source, mappedSource);
5,315✔
2277
      eventTrace.add(RecEventTrace::PCacheCheck, cacheHit, false, match);
5,315✔
2278
      if (cacheHit) {
5,315✔
2279
        if (!g_quiet) {
2,939✔
2280
          g_slogudpin->info(Logr::Notice, "Question answered from packet cache", "tag", Logging::Loggable(ctag),
125✔
2281
                            "qname", Logging::Loggable(qname), "qtype", Logging::Loggable(QType(qtype)),
125✔
2282
                            "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
125✔
2283
        }
125✔
2284
        struct msghdr msgh{};
2,939✔
2285
        struct iovec iov{};
2,939✔
2286
        cmsgbuf_aligned cbuf{};
2,939✔
2287
        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,939✔
2288
        msgh.msg_control = nullptr;
2,939✔
2289

2290
        if (g_fromtosockets.count(fileDesc) != 0) {
2,939!
2291
          addCMsgSrcAddr(&msgh, &cbuf, &destaddr, 0);
×
2292
        }
×
2293
        int sendErr = sendOnNBSocket(fileDesc, &msgh);
2,939✔
2294
        eventTrace.add(RecEventTrace::AnswerSent);
2,939✔
2295

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

2300
        if (eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_log)) {
2,939!
2301
          g_slogudpin->info(Logr::Info, eventTrace.toString()); // Do we want more fancy logging here?
×
2302
        }
×
2303
        if (sendErr != 0 && g_logCommonErrors) {
2,939!
2304
          g_slogudpin->error(Logr::Error, sendErr, "Sending UDP reply to client failed", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
×
2305
        }
×
2306
        struct timeval now{};
2,939✔
2307
        Utility::gettimeofday(&now, nullptr);
2,939✔
2308
        uint64_t spentUsec = uSec(now - tval);
2,939✔
2309
        t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
2,939✔
2310
        t_Counters.updateSnap(g_regressionTestMode);
2,939✔
2311
        return nullptr;
2,939✔
2312
      }
2,939✔
2313
    }
5,315✔
2314
  }
5,330✔
2315
  catch (const std::exception& e) {
5,330✔
2316
    if (g_logCommonErrors) {
×
2317
      g_slogudpin->error(Logr::Error, e.what(), "Error processing or aging answer packet", "exception", Logging::Loggable("std::exception"));
×
2318
    }
×
2319
    return nullptr;
×
2320
  }
×
2321

2322
  if (t_pdl) {
2,396✔
2323
    bool ipf = t_pdl->ipfilter(source, destination, *dnsheader, eventTrace);
239✔
2324
    if (ipf) {
239✔
2325
      if (!g_quiet) {
2!
2326
        g_slogudpin->info(Logr::Notice, "Dropped question based on policy", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
2✔
2327
      }
2✔
2328
      t_Counters.at(rec::Counter::policyDrops)++;
2✔
2329
      return nullptr;
2✔
2330
    }
2✔
2331
  }
239✔
2332

2333
  if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
2,394✔
2334
    if (!isAllowNotifyForZone(qname)) {
23!
2335
      if (!g_quiet) {
×
2336
        g_slogudpin->info(Logr::Notice, "Dropping UDP NOTIFY, zone not matched by allow-notify-for", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
×
2337
      }
×
2338

2339
      t_Counters.at(rec::Counter::zoneDisallowedNotify)++;
×
2340
      return nullptr;
×
2341
    }
×
2342

2343
    if (!g_quiet) {
23!
2344
      g_slogudpin->info(Logr::Notice, "Got NOTIFY", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr), "qname", Logging::Loggable(qname));
23✔
2345
    }
23✔
2346
    if (!ZoneXFR::notifyZoneTracker(qname)) {
23✔
2347
      // It wasn't an RPZ
2348
      requestWipeCaches(qname);
1✔
2349
    }
1✔
2350

2351
    // the operation will now be treated as a Query, generating
2352
    // a normal response, as the rest of the code does not
2353
    // check dh->opcode, but we need to ensure that the response
2354
    // to this request does not get put into the packet cache
2355
    variable = true;
23✔
2356
  }
23✔
2357

2358
  if (g_multiTasker->numProcesses() >= g_maxMThreads) {
2,394!
2359
    if (!g_quiet) {
×
2360
      g_slogudpin->info(Logr::Notice, "Dropped question, over capacity", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr));
×
2361
    }
×
2362
    t_Counters.at(rec::Counter::overCapacityDrops)++;
×
2363
    return nullptr;
×
2364
  }
×
2365

2366
  auto comboWriter = std::make_unique<DNSComboWriter>(question, g_now, std::move(policyTags), t_pdl, std::move(data), std::move(records));
2,394✔
2367

2368
  comboWriter->setSocket(fileDesc);
2,394✔
2369
  comboWriter->d_tag = ctag;
2,394✔
2370
  comboWriter->d_qhash = qhash;
2,394✔
2371
  comboWriter->setRemote(fromaddr); // the address the query is coming from
2,394✔
2372
  comboWriter->setSource(source); // the address we assume the query is coming from, might be set by proxy protocol
2,394✔
2373
  comboWriter->setLocal(destaddr); // the address the query was received on
2,394✔
2374
  comboWriter->setDestination(destination); // the address we assume the query is sent to, might be set by proxy protocol
2,394✔
2375
  comboWriter->setMappedSource(mappedSource); // the address we assume the query is coming from. Differs from source if table-based mapping has been applied
2,394✔
2376
  comboWriter->d_tcp = false;
2,394✔
2377
  comboWriter->d_ecsFound = ecsFound;
2,394✔
2378
  comboWriter->d_ecsParsed = ecsParsed;
2,394✔
2379
  comboWriter->d_ednssubnet = ednssubnet;
2,394✔
2380
  comboWriter->d_ttlCap = ttlCap;
2,394✔
2381
  comboWriter->d_variable = variable;
2,394✔
2382
  comboWriter->d_followCNAMERecords = followCNAMEs;
2,394✔
2383
  comboWriter->d_rcode = rcode;
2,394✔
2384
  comboWriter->d_logResponse = logResponse;
2,394✔
2385
  if (t_protobufServers.servers || t_outgoingProtobufServers.servers) {
2,395✔
2386
    comboWriter->d_uuid = uniqueId;
33✔
2387
  }
33✔
2388
  comboWriter->d_requestorId = std::move(requestorId);
2,394✔
2389
  comboWriter->d_deviceId = std::move(deviceId);
2,394✔
2390
  comboWriter->d_deviceName = std::move(deviceName);
2,394✔
2391
  comboWriter->d_kernelTimestamp = tval;
2,394✔
2392
  comboWriter->d_proxyProtocolValues = std::move(proxyProtocolValues);
2,394✔
2393
  comboWriter->d_routingTag = std::move(routingTag);
2,394✔
2394
  comboWriter->d_extendedErrorCode = extendedErrorCode;
2,394✔
2395
  comboWriter->d_extendedErrorExtra = std::move(extendedErrorExtra);
2,394✔
2396
  comboWriter->d_responsePaddingDisabled = responsePaddingDisabled;
2,394✔
2397
  comboWriter->d_meta = std::move(meta);
2,394✔
2398

2399
  comboWriter->d_eventTrace = std::move(eventTrace);
2,394✔
2400
  comboWriter->d_otTrace = std::move(otTrace);
2,394✔
2401

2402
  g_multiTasker->makeThread(startDoResolve, (void*)comboWriter.release()); // deletes dc
2,394✔
2403

2404
  return nullptr;
2,394✔
2405
}
2,394✔
2406

2407
static void handleNewUDPQuestion(int fileDesc, FDMultiplexer::funcparam_t& /* var */) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
2408
{
909✔
2409
  const bool proxyActive = t_proxyProtocolACL && !t_proxyProtocolACL->empty();
909!
2410
  static const size_t maxIncomingQuerySize = !proxyActive ? 512 : (512 + g_proxyProtocolMaximumSize);
909✔
2411
  static thread_local std::string data;
909✔
2412
  ComboAddress fromaddr; // the address the query is coming from
909✔
2413
  ComboAddress source; // the address we assume the query is coming from, might be set by proxy protocol
909✔
2414
  ComboAddress destination; // the address we assume the query was sent to, might be set by proxy protocol
909✔
2415
  struct msghdr msgh{};
909✔
2416
  struct iovec iov{};
909✔
2417
  cmsgbuf_aligned cbuf;
909✔
2418
  bool firstQuery = true;
909✔
2419
  std::vector<ProxyProtocolValue> proxyProtocolValues;
909✔
2420
  RecEventTrace eventTrace;
909✔
2421
  pdns::trace::InitialSpanInfo otTrace;
909✔
2422

2423
  for (size_t queriesCounter = 0; queriesCounter < g_maxUDPQueriesPerRound; queriesCounter++) {
6,263!
2424
    bool proxyProto = false;
6,263✔
2425
    proxyProtocolValues.clear();
6,263✔
2426
    data.resize(maxIncomingQuerySize);
6,263✔
2427
    fromaddr.sin6.sin6_family = AF_INET6; // this makes sure fromaddr is big enough
6,263✔
2428
    fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), data.data(), data.size(), &fromaddr);
6,263✔
2429

2430
    if (ssize_t len = recvmsg(fileDesc, &msgh, 0); len >= 0) {
6,263✔
2431
      eventTrace.clear();
5,364✔
2432
      eventTrace.setEnabled(SyncRes::s_event_trace_enabled != 0);
5,364✔
2433
      // eventTrace uses monotonic time, while OpenTelemetry uses absolute time. setEnabled()
2434
      // established the reference point, get an absolute TS as close as possible to the
2435
      // eventTrace start of trace time.
2436
      auto traceTS = pdns::trace::timestamp();
5,364✔
2437
      eventTrace.add(RecEventTrace::ReqRecv);
5,364✔
2438
      if (SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
5,364!
2439
        otTrace.clear();
×
2440
        otTrace.start_time_unix_nano = traceTS;
×
2441
      }
×
2442
      firstQuery = false;
5,364✔
2443

2444
      if ((msgh.msg_flags & MSG_TRUNC) != 0) {
5,364!
2445
        t_Counters.at(rec::Counter::truncatedDrops)++;
×
2446
        if (!g_quiet) {
×
2447
          g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr));
×
2448
        }
×
2449
        return;
×
2450
      }
×
2451

2452
      data.resize(static_cast<size_t>(len));
5,364✔
2453

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

2493
        data.erase(0, used);
20✔
2494
      }
20✔
2495
      else if (len > 512) {
5,338!
2496
        /* we only allow UDP packets larger than 512 for those with a proxy protocol header */
2497
        t_Counters.at(rec::Counter::truncatedDrops)++;
×
2498
        if (!g_quiet) {
×
2499
          g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr));
×
2500
        }
×
2501
        return;
×
2502
      }
×
2503

2504
      if (data.size() < sizeof(dnsheader)) {
5,358!
2505
        t_Counters.at(rec::Counter::ignoredCount)++;
×
2506
        if (!g_quiet) {
×
2507
          g_slogudpin->info(Logr::Error, "Ignoring too-short query", "length", Logging::Loggable(data.size()),
×
2508
                            "remote", Logging::Loggable(fromaddr));
×
2509
        }
×
2510
        return;
×
2511
      }
×
2512

2513
      if (!proxyProto) {
5,358✔
2514
        source = fromaddr;
5,340✔
2515
      }
5,340✔
2516
      ComboAddress mappedSource = source;
5,358✔
2517
      if (t_proxyMapping) {
5,358✔
2518
        if (const auto* iter = t_proxyMapping->lookup(source)) {
8!
2519
          mappedSource = iter->second.address;
8✔
2520
          ++iter->second.stats.netmaskMatches;
8✔
2521
        }
8✔
2522
      }
8✔
2523
      if (t_remotes) {
5,358!
2524
        t_remotes->push_back(source);
5,358✔
2525
      }
5,358✔
2526

2527
      if (t_allowFrom && !t_allowFrom->match(&mappedSource)) {
5,358!
2528
        if (!g_quiet) {
4!
2529
          g_slogudpin->info(Logr::Error, "Dropping UDP query, address not matched by allow-from", "source", Logging::Loggable(mappedSource));
4✔
2530
        }
4✔
2531

2532
        t_Counters.at(rec::Counter::unauthorizedUDP)++;
4✔
2533
        return;
4✔
2534
      }
4✔
2535

2536
      BOOST_STATIC_ASSERT(offsetof(sockaddr_in, sin_port) == offsetof(sockaddr_in6, sin6_port));
5,354✔
2537
      if (fromaddr.sin4.sin_port == 0) { // also works for IPv6
5,354!
2538
        if (!g_quiet) {
×
2539
          g_slogudpin->info(Logr::Error, "Dropping UDP query can't deal with port 0", "remote", Logging::Loggable(fromaddr));
×
2540
        }
×
2541

2542
        t_Counters.at(rec::Counter::clientParseError)++; // not quite the best place to put it, but needs to go somewhere
×
2543
        return;
×
2544
      }
×
2545

2546
      try {
5,354✔
2547
        const dnsheader_aligned headerdata(data.data());
5,354✔
2548
        const dnsheader* dnsheader = headerdata.get();
5,354✔
2549

2550
        if (dnsheader->qr) {
5,354!
2551
          t_Counters.at(rec::Counter::ignoredCount)++;
×
2552
          if (g_logCommonErrors) {
×
2553
            g_slogudpin->info(Logr::Error, "Ignoring answer on server socket", "remote", Logging::Loggable(fromaddr));
×
2554
          }
×
2555
        }
×
2556
        else if (dnsheader->opcode != static_cast<unsigned>(Opcode::Query) && dnsheader->opcode != static_cast<unsigned>(Opcode::Notify)) {
5,354✔
2557
          t_Counters.at(rec::Counter::ignoredCount)++;
3✔
2558
          if (g_logCommonErrors) {
3!
2559
            g_slogudpin->info(Logr::Error, "Ignoring unsupported opcode server socket", "remote", Logging::Loggable(fromaddr), "opcode", Logging::Loggable(Opcode::to_s(dnsheader->opcode)));
3✔
2560
          }
3✔
2561
        }
3✔
2562
        else if (dnsheader->qdcount == 0U) {
5,351!
2563
          t_Counters.at(rec::Counter::emptyQueriesCount)++;
×
2564
          if (g_logCommonErrors) {
×
2565
            g_slogudpin->info(Logr::Error, "Ignoring empty (qdcount == 0) query on server socket!", "remote", Logging::Loggable(fromaddr));
×
2566
          }
×
2567
        }
×
2568
        else {
5,351✔
2569
          if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
5,351✔
2570
            if (!t_allowNotifyFrom || !t_allowNotifyFrom->match(&mappedSource)) {
23!
2571
              if (!g_quiet) {
×
2572
                g_slogudpin->info(Logr::Error, "Dropping UDP NOTIFY from address not matched by allow-notify-from",
×
2573
                                  "source", Logging::Loggable(mappedSource));
×
2574
              }
×
2575

2576
              t_Counters.at(rec::Counter::sourceDisallowedNotify)++;
×
2577
              return;
×
2578
            }
×
2579
          }
23✔
2580

2581
          struct timeval tval = {0, 0};
5,351✔
2582
          HarvestTimestamp(&msgh, &tval);
5,351✔
2583
          if (!proxyProto) {
5,351✔
2584
            destination = destaddr;
5,337✔
2585
          }
5,337✔
2586

2587
          if (RecThreadInfo::weDistributeQueries()) {
5,351✔
2588
            std::string localdata = data;
3,612✔
2589
            distributeAsyncFunction(data, [localdata = std::move(localdata), fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace]() mutable {
3,612✔
2590
              return doProcessUDPQuestion(localdata, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace);
3,568✔
2591
            });
3,568✔
2592
          }
3,612✔
2593
          else {
1,739✔
2594
            doProcessUDPQuestion(data, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace);
1,739✔
2595
          }
1,739✔
2596
        }
5,351✔
2597
      }
5,354✔
2598
      catch (const MOADNSException& mde) {
5,354✔
2599
        t_Counters.at(rec::Counter::clientParseError)++;
×
2600
        if (g_logCommonErrors) {
×
2601
          g_slogudpin->error(Logr::Error, mde.what(), "Unable to parse packet from remote UDP client", "remote", Logging::Loggable(fromaddr), "exception", Logging::Loggable("MOADNSException"));
×
2602
        }
×
2603
      }
×
2604
      catch (const std::runtime_error& e) {
5,354✔
2605
        t_Counters.at(rec::Counter::clientParseError)++;
×
2606
        if (g_logCommonErrors) {
×
2607
          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"));
×
2608
        }
×
2609
      }
×
2610
    }
5,354✔
2611
    else {
899✔
2612
      if (firstQuery && errno == EAGAIN) {
899!
2613
        t_Counters.at(rec::Counter::noPacketError)++;
×
2614
      }
×
2615

2616
      break;
899✔
2617
    }
899✔
2618
  }
6,263✔
2619
  t_Counters.updateSnap(g_regressionTestMode);
899✔
2620
}
899✔
2621

2622
// The two last arguments to makeUDPServerSockets are used for logging purposes only
2623
unsigned int makeUDPServerSockets(deferredAdd_t& deferredAdds, Logr::log_t log, bool doLog, unsigned int instances)
2624
{
333✔
2625
  int one = 1;
333✔
2626
  vector<string> localAddresses;
333✔
2627
  vector<string> logVec;
333✔
2628
  stringtok(localAddresses, ::arg()["local-address"], " ,");
333✔
2629

2630
  if (localAddresses.empty()) {
333!
2631
    throw PDNSException("No local address specified");
×
2632
  }
×
2633

2634
  const uint16_t defaultLocalPort = ::arg().asNum("local-port");
333✔
2635
  const vector<string> defaultVector = {"127.0.0.1", "::1"};
333✔
2636
  const auto configIsDefault = localAddresses == defaultVector;
333✔
2637

2638
  for (const auto& localAddress : localAddresses) {
352✔
2639
    ComboAddress address{localAddress, defaultLocalPort};
352✔
2640
    auto socketFd = FDWrapper(socket(address.sin4.sin_family, SOCK_DGRAM, 0));
352✔
2641
    if (socketFd < 0) {
352!
2642
      throw PDNSException("Making a UDP server socket for resolver: " + stringerror());
×
2643
    }
×
2644

2645
    if (!setSocketTimestamps(socketFd)) {
352!
2646
      log->info(Logr::Warning, "Unable to enable timestamp reporting for socket");
×
2647
    }
×
2648
    if (IsAnyAddress(address)) {
352✔
2649
      if (address.sin4.sin_family == AF_INET) {
2!
2650
        if (setsockopt(socketFd, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)) == 0) { // linux supports this, so why not - might fail on other systems
2!
2651
          g_fromtosockets.insert(socketFd);
2✔
2652
        }
2✔
2653
      }
2✔
2654
#ifdef IPV6_RECVPKTINFO
2✔
2655
      if (address.sin4.sin_family == AF_INET6) {
2!
2656
        if (setsockopt(socketFd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)) == 0) {
×
2657
          g_fromtosockets.insert(socketFd);
×
2658
        }
×
2659
      }
×
2660
#endif
2✔
2661
      if (address.sin6.sin6_family == AF_INET6 && setsockopt(socketFd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)) < 0) {
2!
2662
        int err = errno;
×
2663
        log->error(Logr::Warning, err, "Failed to set IPv6 socket to IPv6 only, continuing anyhow");
×
2664
      }
×
2665
    }
2✔
2666
    if (::arg().mustDo("non-local-bind")) {
352!
2667
      Utility::setBindAny(AF_INET6, socketFd);
×
2668
    }
×
2669

2670
    setCloseOnExec(socketFd);
352✔
2671

2672
    try {
352✔
2673
      setSocketReceiveBuffer(socketFd, 250000);
352✔
2674
    }
352✔
2675
    catch (const std::exception& e) {
352✔
2676
      log->error(Logr::Error, e.what(), "Exception while setting socket buffer size");
×
2677
    }
×
2678

2679
    if (g_reusePort) {
352✔
2680
#if defined(SO_REUSEPORT_LB)
2681
      try {
2682
        SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
2683
      }
2684
      catch (const std::exception& e) {
2685
        throw PDNSException(std::string("SO_REUSEPORT_LB: ") + e.what());
2686
      }
2687
#elif defined(SO_REUSEPORT)
2688
      try {
322✔
2689
        SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT, 1);
322✔
2690
      }
322✔
2691
      catch (const std::exception& e) {
322✔
2692
        throw PDNSException(std::string("SO_REUSEPORT: ") + e.what());
×
2693
      }
×
2694
#endif
322✔
2695
    }
322✔
2696

2697
    try {
352✔
2698
      setSocketIgnorePMTU(socketFd, address.sin4.sin_family);
352✔
2699
    }
352✔
2700
    catch (const std::exception& e) {
352✔
2701
      log->error(Logr::Warning, e.what(), "Failed to set IP_MTU_DISCOVER on UDP server socket");
×
2702
    }
×
2703

2704
    socklen_t socklen = address.getSocklen();
352✔
2705
    if (::bind(socketFd, reinterpret_cast<struct sockaddr*>(&address), socklen) < 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
352!
2706
      int err = errno;
×
2707
      if (!configIsDefault || address != ComboAddress{"::1", defaultLocalPort}) {
×
2708
        throw PDNSException("Resolver binding to server socket on " + address.toStringWithPort() + ": " + stringerror(err));
×
2709
      }
×
2710
      log->info(Logr::Warning, "Cannot listen on this address, skipping", "proto", Logging::Loggable("UDP"), "address", Logging::Loggable(address), "error", Logging::Loggable(stringerror(err)));
×
2711
      continue;
×
2712
    }
×
2713

2714
    setNonBlocking(socketFd);
352✔
2715

2716
    deferredAdds.emplace_back(socketFd, handleNewUDPQuestion);
352✔
2717
    g_listenSocketsAddresses[socketFd] = address; // this is written to only from the startup thread, not from the workers
352✔
2718
    logVec.emplace_back(address.toStringWithPort());
352✔
2719
    socketFd.release(); // to avoid auto-close by FDWrapper
352✔
2720
  }
352✔
2721
  if (doLog) {
333✔
2722
    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));
175✔
2723
  }
175✔
2724
  return localAddresses.size();
333✔
2725
}
333✔
2726

2727
static bool trySendingQueryToWorker(unsigned int target, ThreadMSG* tmsg)
2728
{
3,612✔
2729
  auto& targetInfo = RecThreadInfo::info(target);
3,612✔
2730
  if (!targetInfo.isWorker()) {
3,612!
2731
    g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() tried to assign a query to a non-worker thread");
×
2732
    _exit(1);
×
2733
  }
×
2734

2735
  const auto& tps = targetInfo.getPipes();
3,612✔
2736

2737
  ssize_t written = write(tps.writeQueriesToThread, &tmsg, sizeof(tmsg)); // NOLINT: correct sizeof
3,612✔
2738
  if (written > 0) {
3,612!
2739
    if (static_cast<size_t>(written) != sizeof(tmsg)) { // NOLINT: correct sizeof
3,612!
2740
      delete tmsg; // NOLINT: pointer ownership
×
2741
      unixDie("write to thread pipe returned wrong size or error");
×
2742
    }
×
2743
  }
3,612✔
2744
  else {
×
2745
    int error = errno;
×
2746
    if (error == EAGAIN || error == EWOULDBLOCK) {
×
2747
      return false;
×
2748
    }
×
2749
    delete tmsg; // NOLINT: pointer ownership
×
2750
    unixDie("write to thread pipe returned wrong size or error:" + std::to_string(error));
×
2751
  }
×
2752

2753
  return true;
3,612✔
2754
}
3,612✔
2755

2756
static unsigned int getWorkerLoad(size_t workerIdx)
2757
{
×
2758
  const auto* multiThreader = RecThreadInfo::info(RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + workerIdx).getMT();
×
2759
  if (multiThreader != nullptr) {
×
2760
    return multiThreader->numProcesses();
×
2761
  }
×
2762
  return 0;
×
2763
}
×
2764

2765
static unsigned int selectWorker(unsigned int hash)
2766
{
3,612✔
2767
  assert(RecThreadInfo::numUDPWorkers() != 0); // NOLINT: assert implementation
3,612✔
2768
  if (g_balancingFactor == 0) {
3,612!
2769
    return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + (hash % RecThreadInfo::numUDPWorkers());
3,612✔
2770
  }
3,612✔
2771

2772
  /* we start with one, representing the query we are currently handling */
2773
  double currentLoad = 1;
×
2774
  std::vector<unsigned int> load(RecThreadInfo::numUDPWorkers());
×
2775
  for (size_t idx = 0; idx < RecThreadInfo::numUDPWorkers(); idx++) {
×
2776
    load[idx] = getWorkerLoad(idx);
×
2777
    currentLoad += load[idx];
×
2778
  }
×
2779

2780
  double targetLoad = (currentLoad / RecThreadInfo::numUDPWorkers()) * g_balancingFactor;
×
2781

2782
  unsigned int worker = hash % RecThreadInfo::numUDPWorkers();
×
2783
  /* at least one server has to be at or below the average load */
2784
  if (load[worker] > targetLoad) {
×
2785
    ++t_Counters.at(rec::Counter::rebalancedQueries);
×
2786
    do {
×
2787
      worker = (worker + 1) % RecThreadInfo::numUDPWorkers();
×
2788
    } while (load[worker] > targetLoad);
×
2789
  }
×
2790

2791
  return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + worker;
×
2792
}
3,612✔
2793

2794
// This function is only called by the distributor threads, when pdns-distributes-queries is set
2795
void distributeAsyncFunction(const string& packet, const pipefunc_t& func)
2796
{
3,612✔
2797
  if (!RecThreadInfo::self().isDistributor()) {
3,612!
2798
    g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() has been called by a worker"); // tid will be added
×
2799
    _exit(1);
×
2800
  }
×
2801

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

2811
  ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: pointer ownership
3,612✔
2812
  tmsg->func = func;
3,612✔
2813
  tmsg->wantAnswer = false;
3,612✔
2814

2815
  if (!trySendingQueryToWorker(target, tmsg)) {
3,612!
2816
    /* if this function failed but did not raise an exception, it means that the pipe
2817
       was full, let's try another one */
2818
    unsigned int newTarget = 0;
×
2819
    do {
×
2820
      newTarget = RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + dns_random(RecThreadInfo::numUDPWorkers());
×
2821
    } while (newTarget == target);
×
2822

2823
    if (!trySendingQueryToWorker(newTarget, tmsg)) {
×
2824
      t_Counters.at(rec::Counter::queryPipeFullDrops)++;
×
2825
      delete tmsg; // NOLINT: pointer ownership
×
2826
    }
×
2827
  }
×
2828
  // coverity[leaked_storage]
2829
}
3,612✔
2830

2831
// resend event to everybody chained onto it
2832
static void doResends(MT_t::waiters_t::iterator& iter, const std::shared_ptr<PacketID>& resend, const PacketBuffer& content)
2833
{
8,861✔
2834
  // We close the chain for new entries, since they won't be processed anyway
2835
  iter->key->closed = true;
8,861✔
2836

2837
  if (iter->key->authReqChain.empty()) {
8,861✔
2838
    return;
8,530✔
2839
  }
8,530✔
2840

2841
  auto maxWeight = t_Counters.at(rec::Counter::maxChainWeight);
331✔
2842
  auto weight = iter->key->authReqChain.size() * content.size();
331✔
2843
  if (weight > maxWeight) {
331✔
2844
    t_Counters.at(rec::Counter::maxChainWeight) = weight;
78✔
2845
  }
78✔
2846

2847
  for (auto [fileDesc, qid] : iter->key->authReqChain) {
1,482✔
2848
    auto packetID = std::make_shared<PacketID>(*resend);
1,482✔
2849
    packetID->fd = fileDesc;
1,482✔
2850
    packetID->id = qid;
1,482✔
2851
    g_multiTasker->sendEvent(packetID, &content);
1,482✔
2852
    t_Counters.at(rec::Counter::chainResends)++;
1,482✔
2853
  }
1,482✔
2854
}
331✔
2855

2856
void mthreadSleep(unsigned int jitterMsec)
2857
{
×
2858
  auto neverHappens = std::make_shared<PacketID>();
×
2859
  neverHappens->id = dns_random_uint16();
×
2860
  neverHappens->type = dns_random_uint16();
×
2861
  neverHappens->remote = ComboAddress("100::"); // discard-only
×
2862
  neverHappens->remote.setPort(dns_random_uint16());
×
2863
  neverHappens->fd = -1;
×
2864
  assert(g_multiTasker->waitEvent(neverHappens, nullptr, jitterMsec) != -1); // NOLINT
×
2865
}
×
2866

2867
static bool checkIncomingECSSource(const PacketBuffer& packet, const Netmask& subnet)
2868
{
277✔
2869
  bool foundMatchingECS = false;
277✔
2870

2871
  // We sent out ECS, check if the response has the expected ECS info
2872
  EDNSOptionViewMap ednsOptions;
277✔
2873
  if (slowParseEDNSOptions(packet, ednsOptions)) {
277!
2874
    // check content
2875
    auto option = ednsOptions.find(EDNSOptionCode::ECS);
277✔
2876
    if (option != ednsOptions.end()) {
277✔
2877
      // found an ECS option
2878
      EDNSSubnetOpts ecs;
22✔
2879
      for (const auto& value : option->second.values) {
22!
2880
        if (EDNSSubnetOpts::getFromString(value.content, value.size, &ecs)) {
22!
2881
          if (ecs.getSource() == subnet) {
22✔
2882
            foundMatchingECS = true;
21✔
2883
          }
21✔
2884
        }
22✔
2885
        break; // The RFC isn't clear about multiple ECS options. We chose to handle it like cookies
22✔
2886
               // and only look at the first.
2887
      }
22✔
2888
    }
22✔
2889
  }
277✔
2890
  return foundMatchingECS;
277✔
2891
}
277✔
2892

2893
static void handleUDPServerResponse(int fileDesc, FDMultiplexer::funcparam_t& var)
2894
{
8,862✔
2895
  auto pid = boost::any_cast<std::shared_ptr<PacketID>>(var);
8,862✔
2896
  PacketBuffer packet;
8,862✔
2897
  packet.resize(g_outgoingEDNSBufsize);
8,862✔
2898
  ComboAddress fromaddr;
8,862✔
2899
  socklen_t addrlen = sizeof(fromaddr);
8,862✔
2900

2901
  ssize_t len = recvfrom(fileDesc, &packet.at(0), packet.size(), 0, reinterpret_cast<sockaddr*>(&fromaddr), &addrlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
8,862✔
2902

2903
  const ssize_t signed_sizeof_sdnsheader = sizeof(dnsheader);
8,862✔
2904

2905
  if (len < 0) {
8,862✔
2906
    // len < 0: error on socket
2907
    t_udpclientsocks->returnSocket(fileDesc);
64✔
2908

2909
    PacketBuffer empty;
64✔
2910
    auto iter = g_multiTasker->getWaiters().find(pid);
64✔
2911
    if (iter != g_multiTasker->getWaiters().end()) {
64!
2912
      doResends(iter, pid, empty);
64✔
2913
    }
64✔
2914
    g_multiTasker->sendEvent(pid, &empty); // this denotes error (does retry lookup using other NS)
64✔
2915
    return;
64✔
2916
  }
64✔
2917

2918
  if (len < signed_sizeof_sdnsheader) {
8,798!
2919
    // We have received a packet that cannot be a valid DNS packet, as it has no complete header
2920
    // Drop it, but continue to wait for other packets
2921
    t_Counters.at(rec::Counter::serverParseError)++;
×
2922
    if (g_logCommonErrors) {
×
2923
      g_slogout->info(Logr::Error, "Unable to parse too short packet from remote UDP server", "from", Logging::Loggable(fromaddr));
×
2924
    }
×
2925
    return;
×
2926
  }
×
2927

2928
  // We have at least a full header
2929
  packet.resize(len);
8,798✔
2930
  dnsheader dnsheader{};
8,798✔
2931
  memcpy(&dnsheader, &packet.at(0), sizeof(dnsheader));
8,798✔
2932

2933
  auto pident = std::make_shared<PacketID>();
8,798✔
2934
  pident->remote = fromaddr;
8,798✔
2935
  pident->id = dnsheader.id;
8,798✔
2936
  pident->fd = fileDesc;
8,798✔
2937

2938
  if (!dnsheader.qr && g_logCommonErrors) {
8,798!
2939
    g_slogout->info(Logr::Error, "Not taking data from question on outgoing socket", "from", Logging::Loggable(fromaddr));
×
2940
  }
×
2941

2942
  if (dnsheader.qdcount == 0U || // UPC, Nominum, very old BIND on FormErr, NSD
8,798✔
2943
      dnsheader.qr == 0U) { // one weird server
8,798✔
2944
    pident->domain.clear();
×
2945
    pident->type = 0;
×
2946
  }
×
2947
  else {
8,798✔
2948
    try {
8,798✔
2949
      if (len > signed_sizeof_sdnsheader) {
8,798✔
2950
        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)
8,797✔
2951
      }
8,797✔
2952
      else {
1✔
2953
        // len == sizeof(dnsheader), only header case
2954
        // We will do a full scan search later to see if we can match this reply even without a domain
2955
        pident->domain.clear();
1✔
2956
        pident->type = 0;
1✔
2957
      }
1✔
2958
    }
8,798✔
2959
    catch (std::exception& e) {
8,798✔
2960
      // Parse error, continue waiting for other packets
2961
      t_Counters.at(rec::Counter::serverParseError)++; // won't be fed to lwres.cc, so we have to increment
×
2962
      g_slogudpin->error(Logr::Warning, e.what(), "Error in packet from remote nameserver", "from", Logging::Loggable(fromaddr));
×
2963
      return;
×
2964
    }
×
2965
  }
8,798✔
2966

2967
  if (!pident->domain.empty()) {
8,798✔
2968
    auto iter = g_multiTasker->getWaiters().find(pident);
8,795✔
2969
    if (iter != g_multiTasker->getWaiters().end()) {
8,798✔
2970
      doResends(iter, pident, packet);
8,797✔
2971
    }
8,797✔
2972
  }
8,795✔
2973

2974
retryWithName:
8,798✔
2975

2976
  if (pident->domain.empty() || g_multiTasker->sendEvent(pident, &packet) == 0) {
8,798✔
2977
    /* we did not find a match for this response, something is wrong */
2978

2979
    // 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
2980
    for (const auto& d_waiter : g_multiTasker->getWaiters()) {
×
2981
      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) {
×
2982
        /* we are expecting an answer from that exact source, on that exact port (since we are using connected sockets), for that qname/qtype,
2983
           but with a different message ID. That smells like a spoofing attempt. For now we will just increase the counter and will deal with
2984
           that later. */
2985
        d_waiter.key->nearMisses++;
×
2986
      }
×
2987

2988
      // be a bit paranoid here since we're weakening our matching
2989
      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) {
×
2990
        pident->domain = d_waiter.key->domain;
×
2991
        pident->type = d_waiter.key->type;
×
2992
        goto retryWithName; // note that this only passes on an error, lwres will still reject the packet NOLINT(cppcoreguidelines-avoid-goto)
×
2993
      }
×
2994
    }
×
2995
    t_Counters.at(rec::Counter::unexpectedCount)++; // if we made it here, it really is an unexpected answer
×
2996
    if (g_logCommonErrors) {
×
2997
      g_slogudpin->info(Logr::Warning, "Discarding unexpected packet", "from", Logging::Loggable(fromaddr),
×
2998
                        "qname", Logging::Loggable(pident->domain),
×
2999
                        "qtype", Logging::Loggable(QType(pident->type)),
×
3000
                        "waiters", Logging::Loggable(g_multiTasker->getWaiters().size()));
×
3001
    }
×
3002
  }
×
3003
  else if (fileDesc >= 0) {
8,798✔
3004
    /* we either found a waiter (1) or encountered an issue (-1), it's up to us to clean the socket anyway */
3005
    t_udpclientsocks->returnSocket(fileDesc);
8,796✔
3006
  }
8,796✔
3007
}
8,797✔
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