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

PowerDNS / pdns / 18743945403

23 Oct 2025 09:29AM UTC coverage: 65.845% (+0.02%) from 65.829%
18743945403

Pull #16356

github

web-flow
Merge 8a2027ef1 into efa3637e8
Pull Request #16356: auth 5.0: backport "pdnsutil: fix b2b-migrate to from sql to non-sql"

42073 of 92452 branches covered (45.51%)

Branch coverage included in aggregate %.

128008 of 165855 relevant lines covered (77.18%)

6379935.17 hits per line

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

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

23
#include "rec-main.hh"
24

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

126
  d_numsocks++;
9,083✔
127
  return LWResult::Result::Success;
9,083✔
128
}
9,083✔
129

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

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

148
  --d_numsocks;
9,083✔
149
}
9,082✔
150

151
// returns -1 for errors which might go away, throws for ones that won't
152
int UDPClientSocks::makeClientSocket(int family)
153
{
9,083✔
154
  int ret = socket(family, SOCK_DGRAM, 0); // turns out that setting CLO_EXEC and NONBLOCK from here is not a performance win on Linux (oddly enough)
9,083✔
155

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

164
  // The loop below runs the body with [tries-1 tries-2 ... 1]. Last iteration with tries == 1 is special: it uses a kernel
165
  // allocated UDP port.
166
#if !defined(__OpenBSD__)
9,083✔
167
  int tries = 10;
9,083✔
168
#else
169
  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
170
#endif
171
  ComboAddress sin;
9,083✔
172
  while (--tries != 0) {
9,092!
173
    in_port_t port = 0;
9,090✔
174

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

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

190
  int err = errno;
9,083✔
191

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

197
  try {
9,083✔
198
    setReceiveSocketErrors(ret, family);
9,083✔
199
    setNonBlocking(ret);
9,083✔
200
  }
9,083✔
201
  catch (...) {
9,083✔
202
    closesocket(ret);
×
203
    throw;
×
204
  }
×
205
  return ret;
9,081✔
206
}
9,083✔
207

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

216
  ssize_t ret = recvfrom(fileDesc, resp.data(), resp.size(), 0, reinterpret_cast<sockaddr*>(&fromaddr), &addrlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
4✔
217
  if (fromaddr != pident->remote) {
4!
218
    SLOG(g_log << Logger::Notice << "Response received from the wrong remote host (" << fromaddr.toStringWithPort() << " instead of " << pident->remote.toStringWithPort() << "), discarding" << endl,
×
219
         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)));
×
220
  }
×
221

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

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

239
  socket.bind(local);
4✔
240
  socket.connect(dest);
4✔
241
  socket.send(query);
4✔
242

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

249
  PacketBuffer data;
4✔
250
  int ret = g_multiTasker->waitEvent(pident, &data, authWaitTimeMSec(g_multiTasker));
4✔
251

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

262
static void handleUDPServerResponse(int fileDesc, FDMultiplexer::funcparam_t& var);
263

264
thread_local std::unique_ptr<UDPClientSocks> t_udpclientsocks;
265

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

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

285
  auto pident = std::make_shared<PacketID>();
10,668✔
286
  pident->domain = domain;
10,668✔
287
  pident->remote = toAddress;
10,668✔
288
  pident->type = qtype;
10,668✔
289
  if (ecs) {
10,668✔
290
    pident->ecsSubnet = ecs->getSource();
546✔
291
  }
546✔
292

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

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

322
  auto ret = t_udpclientsocks->getSocket(toAddress, fileDesc);
9,077✔
323
  if (ret != LWResult::Result::Success) {
9,077!
324
    return ret;
×
325
  }
×
326

327
  pident->fd = *fileDesc;
9,077✔
328
  pident->id = qid;
9,077✔
329

330
  t_fdm->addReadFD(*fileDesc, handleUDPServerResponse, pident);
9,077✔
331
  ssize_t sent = send(*fileDesc, data, len, 0);
9,077✔
332

333
  int tmp = errno;
9,077✔
334

335
  if (sent < 0) {
9,077!
336
    t_udpclientsocks->returnSocket(*fileDesc);
×
337
    errno = tmp; // this is for logging purposes only
×
338
    return LWResult::Result::PermanentError;
×
339
  }
×
340

341
  return LWResult::Result::Success;
9,077✔
342
}
9,077✔
343

344
static bool checkIncomingECSSource(const PacketBuffer& packet, const Netmask& subnet);
345

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

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

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

375
    len = packet.size();
10,606✔
376

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

396
    return LWResult::Result::Success;
10,344✔
397
  }
10,344✔
398
  /* getting there means error or timeout, it's up to us to close the socket */
399
  if (fileDesc >= 0) {
2,147,483,651✔
400
    t_udpclientsocks->returnSocket(fileDesc);
7✔
401
  }
7✔
402

403
  return ret == 0 ? LWResult::Result::Timeout : LWResult::Result::PermanentError;
2,147,483,651✔
404
}
10,673✔
405

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

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

457
static bool addRecordToPacket(DNSPacketWriter& packetWritewr, const DNSRecord& rec, uint32_t& minTTL, uint32_t ttlCap, const uint16_t maxAnswerSize, bool& seenAuthSOA)
458
{
9,677✔
459
  packetWritewr.startRecord(rec.d_name, rec.d_type, (rec.d_ttl > ttlCap ? ttlCap : rec.d_ttl), rec.d_class, rec.d_place);
9,677!
460

461
  if (rec.d_type == QType::SOA && rec.d_place == DNSResourceRecord::AUTHORITY) {
9,677✔
462
    seenAuthSOA = true;
415✔
463
  }
415✔
464

465
  if (rec.d_type != QType::OPT) { // their TTL ain't real
9,677!
466
    minTTL = min(minTTL, rec.d_ttl);
9,677✔
467
  }
9,677✔
468

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

479
  return true;
9,674✔
480
}
9,677✔
481

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

523
private:
524
  std::unique_ptr<DNSComboWriter>& d_dc; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
525
  bool d_handled{false};
526
};
527

528
enum class PolicyResult : uint8_t
529
{
530
  NoAction,
531
  HaveAnswer,
532
  Drop
533
};
534

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

543
  if (resolver.doLog() && appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
91!
544
    SLOG(g_log << Logger::Warning << comboWriter->d_mdp.d_qname << "|" << QType(comboWriter->d_mdp.d_qtype) << appliedPolicy.getLogString() << endl,
6✔
545
         appliedPolicy.info(Logr::Warning, resolver.d_slog));
6✔
546
  }
6✔
547

548
  if (appliedPolicy.d_zoneData && appliedPolicy.d_zoneData->d_extendedErrorCode) {
91!
549
    comboWriter->d_extendedErrorCode = *appliedPolicy.d_zoneData->d_extendedErrorCode;
×
550
    comboWriter->d_extendedErrorExtra = appliedPolicy.d_zoneData->d_extendedErrorExtra;
×
551
  }
×
552

553
  switch (appliedPolicy.d_kind) {
91!
554

555
  case DNSFilterEngine::PolicyKind::NoAction:
77✔
556
    return PolicyResult::NoAction;
77✔
557

558
  case DNSFilterEngine::PolicyKind::Drop:
7✔
559
    tcpGuard.setDropOnIdle();
7✔
560
    ++t_Counters.at(rec::Counter::policyDrops);
7✔
561
    return PolicyResult::Drop;
7✔
562

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

833
  ret.insert(ret.end(), answers.begin(), answers.end());
2✔
834

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

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

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

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

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

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

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

945
static uint32_t capPacketCacheTTL(const struct dnsheader& hdr, uint32_t ttl, bool seenAuthSOA)
946
{
2,830✔
947
  if (hdr.rcode == RCode::NXDomain || (hdr.rcode == RCode::NoError && hdr.ancount == 0 && seenAuthSOA)) {
2,830✔
948
    ttl = std::min(ttl, SyncRes::s_packetcachenegativettl);
387✔
949
  }
387✔
950
  else if ((hdr.rcode != RCode::NoError && hdr.rcode != RCode::NXDomain) || (hdr.ancount == 0 && hdr.nscount == 0)) {
2,443!
951
    ttl = min(ttl, SyncRes::s_packetcacheservfailttl);
81✔
952
  }
81✔
953
  else {
2,362✔
954
    ttl = std::min(ttl, SyncRes::s_packetcachettl);
2,362✔
955
  }
2,362✔
956
  return ttl;
2,830✔
957
}
2,830✔
958

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

973
void startDoResolve(void* arg) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
974
{
3,155✔
975
  auto comboWriter = std::unique_ptr<DNSComboWriter>(static_cast<DNSComboWriter*>(arg));
3,155✔
976
  SyncRes resolver(comboWriter->d_now);
3,155✔
977
  try {
3,155✔
978
    if (t_queryring) {
3,155✔
979
      t_queryring->push_back({comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype});
3,152✔
980
    }
3,152✔
981

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

1004
      if (!comboWriter->d_tcp) {
1,324✔
1005
        /* rfc6891 6.2.3:
1006
           "Values lower than 512 MUST be treated as equal to 512."
1007
        */
1008
        maxanswersize = min(static_cast<uint16_t>(edo.d_packetsize >= 512 ? edo.d_packetsize : 512), g_udpTruncationThreshold);
564✔
1009
      }
564✔
1010
      ednsOpts = edo.d_options;
1,324✔
1011
      maxanswersize -= 11; // EDNS header size
1,324✔
1012

1013
      if (!comboWriter->d_responsePaddingDisabled && g_paddingFrom.match(comboWriter->d_remote)) {
1,324✔
1014
        paddingAllowed = true;
7✔
1015
        if (g_paddingMode == PaddingMode::Always) {
7✔
1016
          addPaddingToResponse = true;
5✔
1017
        }
5✔
1018
      }
7✔
1019

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

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

1045
    /* perhaps there was no EDNS or no ECS but by now we looked */
1046
    comboWriter->d_ecsParsed = true;
3,155✔
1047
    vector<DNSRecord> ret;
3,155✔
1048
    vector<uint8_t> packet;
3,155✔
1049

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

1060
      // RRSets added below
1061
    }
30✔
1062
    checkOutgoingProtobufExport(luaconfsLocal); // to pick up changed configs
3,155✔
1063
#ifdef HAVE_FSTRM
3,155✔
1064
    checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
3,155✔
1065
    checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
3,155✔
1066
#endif
3,155✔
1067

1068
    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,155✔
1069

1070
    packetWriter.getHeader()->aa = 0;
3,155✔
1071
    packetWriter.getHeader()->ra = 1;
3,155✔
1072
    packetWriter.getHeader()->qr = 1;
3,155✔
1073
    packetWriter.getHeader()->tc = 0;
3,155✔
1074
    packetWriter.getHeader()->id = comboWriter->d_mdp.d_header.id;
3,155✔
1075
    packetWriter.getHeader()->rd = comboWriter->d_mdp.d_header.rd;
3,155✔
1076
    packetWriter.getHeader()->cd = comboWriter->d_mdp.d_header.cd;
3,155✔
1077

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

1085
    resolver.d_eventTrace = std::move(comboWriter->d_eventTrace);
3,155✔
1086
    resolver.d_otTrace = std::move(comboWriter->d_otTrace);
3,155✔
1087
    resolver.setId(g_multiTasker->getTid());
3,155✔
1088

1089
    bool DNSSECOK = false;
3,155✔
1090
    if (comboWriter->d_luaContext) {
3,155✔
1091
      resolver.setLuaEngine(comboWriter->d_luaContext);
414✔
1092
    }
414✔
1093
    if (g_dnssecmode != DNSSECMode::Off) {
3,155✔
1094
      resolver.setDoDNSSEC(true);
3,131✔
1095

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

1123
    resolver.setInitialRequestId(comboWriter->d_uuid);
3,155✔
1124
    resolver.setOutgoingProtobufServers(t_outgoingProtobufServers.servers);
3,155✔
1125
#ifdef HAVE_FSTRM
3,155✔
1126
    resolver.setFrameStreamServers(t_frameStreamServersInfo.servers);
3,155✔
1127
#endif
3,155✔
1128

1129
    bool useMapped = true;
3,155✔
1130
    // 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
1131
    // (if a domain suffix match table is present in the config)
1132
    if (t_proxyMapping && comboWriter->d_source != comboWriter->d_mappedSource) {
3,155!
1133
      if (const auto* iter = t_proxyMapping->lookup(comboWriter->d_source)) {
9!
1134
        if (iter->second.suffixMatchNode) {
9✔
1135
          if (!iter->second.suffixMatchNode->check(comboWriter->d_mdp.d_qname)) {
3✔
1136
            // No match in domains, use original source
1137
            useMapped = false;
2✔
1138
          }
2✔
1139
          else {
1✔
1140
            ++iter->second.stats.suffixMatches;
1✔
1141
          }
1✔
1142
        }
3✔
1143
        // No suffix match node defined, use mapped address
1144
      }
9✔
1145
      // lookup failing cannot happen as dc->d_source != dc->d_mappedSource
1146
    }
9✔
1147
    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,155✔
1148

1149
    resolver.setQueryReceivedOverTCP(comboWriter->d_tcp);
3,155✔
1150

1151
    bool tracedQuery = false; // we could consider letting Lua know about this too
3,155✔
1152
    bool shouldNotValidate = false;
3,155✔
1153

1154
    /* preresolve expects res (dq.rcode) to be set to RCode::NoError by default */
1155
    int res = RCode::NoError;
3,155✔
1156

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

1177
    resolver.d_slog = resolver.d_slog->withValues("qname", Logging::Loggable(comboWriter->d_mdp.d_qname),
3,155✔
1178
                                                  "qtype", Logging::Loggable(QType(comboWriter->d_mdp.d_qtype)),
3,155✔
1179
                                                  "remote", Logging::Loggable(comboWriter->getRemote()),
3,155✔
1180
                                                  "proto", Logging::Loggable(comboWriter->d_tcp ? "tcp" : "udp"),
3,155✔
1181
                                                  "ecs", Logging::Loggable(comboWriter->d_ednssubnet.getSource().empty() ? "" : comboWriter->d_ednssubnet.getSource().toString()),
3,155✔
1182
                                                  "mtid", Logging::Loggable(g_multiTasker->getTid()));
3,155✔
1183
    RunningResolveGuard tcpGuard(comboWriter);
3,155✔
1184

1185
    if (ednsExtRCode != 0 || comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Notify)) {
3,155✔
1186
      goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
24✔
1187
    }
24✔
1188

1189
    if (comboWriter->d_mdp.d_qtype == QType::ANY && !comboWriter->d_tcp && g_anyToTcp) {
3,131!
1190
      packetWriter.getHeader()->tc = 1;
×
1191
      res = 0;
×
1192
      variableAnswer = true;
×
1193
      goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
×
1194
    }
×
1195

1196
    if (t_traceRegex && t_traceRegex->match(comboWriter->d_mdp.d_qname.toString())) {
3,131!
1197
      resolver.setLogMode(SyncRes::Store);
×
1198
      tracedQuery = true;
×
1199
    }
×
1200

1201
    if (!g_quiet || tracedQuery) {
3,131✔
1202
      if (!g_slogStructured) {
909✔
1203
        g_log << Logger::Warning << RecThreadInfo::thread_local_id() << " [" << g_multiTasker->getTid() << "/" << g_multiTasker->numProcesses() << "] " << (comboWriter->d_tcp ? "TCP " : "") << "question for '" << comboWriter->d_mdp.d_qname << "|"
×
1204
              << QType(comboWriter->d_mdp.d_qtype) << "' from " << comboWriter->getRemote();
×
1205
        if (!comboWriter->d_ednssubnet.getSource().empty()) {
×
1206
          g_log << " (ecs " << comboWriter->d_ednssubnet.getSource().toString() << ")";
×
1207
        }
×
1208
        g_log << endl;
×
1209
      }
×
1210
      else {
909✔
1211
        resolver.d_slog->info(Logr::Info, "Question");
909✔
1212
      }
909✔
1213
    }
909✔
1214

1215
    if (!comboWriter->d_mdp.d_header.rd) {
3,131✔
1216
      if (g_allowNoRD) {
3✔
1217
        resolver.setCacheOnly();
2✔
1218
      }
2✔
1219
      else {
1✔
1220
        ret.clear();
1✔
1221
        res = RCode::Refused;
1✔
1222
        goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
1✔
1223
      }
1✔
1224
    }
3✔
1225

1226
    if (comboWriter->d_luaContext) {
3,130✔
1227
      comboWriter->d_luaContext->prerpz(dnsQuestion, res, resolver.d_eventTrace);
403✔
1228
    }
403✔
1229

1230
    // Check if the client has a policy attached to it
1231
    if (wantsRPZ && !appliedPolicy.wasHit()) {
3,130✔
1232

1233
      if (luaconfsLocal->dfe.getClientPolicy(comboWriter->d_source, resolver.d_discardedPolicies, appliedPolicy)) {
3,128!
1234
        mergePolicyTags(comboWriter->d_policyTags, appliedPolicy.getTags());
×
1235
      }
×
1236
    }
3,128✔
1237

1238
    /* If we already have an answer generated from gettag_ffi, let's see if the filtering policies
1239
       should be applied to it */
1240
    if (comboWriter->d_rcode != boost::none) {
3,130✔
1241

1242
      bool policyOverride = false;
5✔
1243
      /* Unless we already matched on the client IP, time to check the qname.
1244
         We normally check it in beginResolve() but it will be bypassed since we already have an answer */
1245
      if (wantsRPZ && appliedPolicy.policyOverridesGettag()) {
5!
1246
        if (appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
5!
1247
          // Client IP already matched
1248
        }
×
1249
        else {
5✔
1250
          // no match on the client IP, check the qname
1251
          if (luaconfsLocal->dfe.getQueryPolicy(comboWriter->d_mdp.d_qname, resolver.d_discardedPolicies, appliedPolicy)) {
5✔
1252
            // got a match
1253
            mergePolicyTags(comboWriter->d_policyTags, appliedPolicy.getTags());
2✔
1254
          }
2✔
1255
        }
5✔
1256

1257
        if (appliedPolicy.wasHit()) {
5✔
1258
          policyOverride = true;
2✔
1259
        }
2✔
1260
      }
5✔
1261

1262
      if (!policyOverride) {
5✔
1263
        /* No RPZ or gettag overrides it anyway */
1264
        ret = std::move(comboWriter->d_records);
3✔
1265
        res = *comboWriter->d_rcode;
3✔
1266
        if (res == RCode::NoError && comboWriter->d_followCNAMERecords) {
3!
1267
          res = followCNAMERecords(ret, QType(comboWriter->d_mdp.d_qtype), res);
×
1268
        }
×
1269
        goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
3✔
1270
      }
3✔
1271
    }
5✔
1272

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

1276
      if (!g_dns64PrefixReverse.empty() && dnsQuestion.qtype == QType::PTR && dnsQuestion.qname.isPartOf(g_dns64PrefixReverse)) {
3,065✔
1277
        res = getFakePTRRecords(dnsQuestion.qname, ret);
2✔
1278
        goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
2✔
1279
      }
2✔
1280

1281
      resolver.setWantsRPZ(wantsRPZ);
3,063✔
1282

1283
      if (wantsRPZ && appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
3,064✔
1284

1285
        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!
1286
          /* reset to no match */
1287
          appliedPolicy = DNSFilterEngine::Policy();
×
1288
        }
×
1289
        else {
2✔
1290
          auto policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
2✔
1291
          if (policyResult == PolicyResult::HaveAnswer) {
2!
1292
            if (g_dns64Prefix && dnsQuestion.qtype == QType::AAAA && dns64Candidate(comboWriter->d_mdp.d_qtype, res, ret)) {
2!
1293
              res = getFakeAAAARecords(dnsQuestion.qname, *g_dns64Prefix, ret);
2✔
1294
              shouldNotValidate = true;
2✔
1295
            }
2✔
1296
            goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
2✔
1297
          }
2✔
1298
          else if (policyResult == PolicyResult::Drop) {
×
1299
            return;
×
1300
          }
×
1301
        }
2✔
1302
      }
2✔
1303

1304
      // Query did not get handled for Client IP or QNAME Policy reasons, now actually go out to find an answer
1305
      try {
3,061✔
1306
        resolver.d_appliedPolicy = appliedPolicy;
3,061✔
1307
        resolver.d_policyTags = std::move(comboWriter->d_policyTags);
3,061✔
1308

1309
        if (!comboWriter->d_routingTag.empty()) {
3,061✔
1310
          resolver.d_routingTag = comboWriter->d_routingTag;
16✔
1311
        }
16✔
1312

1313
        ret.clear(); // policy might have filled it with custom records but we decided not to use them
3,061✔
1314
        res = resolver.beginResolve(comboWriter->d_mdp.d_qname, QType(comboWriter->d_mdp.d_qtype), comboWriter->d_mdp.d_qclass, ret);
3,061✔
1315
        shouldNotValidate = resolver.wasOutOfBand();
3,061✔
1316
      }
3,061✔
1317
      catch (const ImmediateQueryDropException& e) {
3,061✔
1318
        // XXX We need to export a protobuf message (and do a NOD lookup) if requested!
1319
        t_Counters.at(rec::Counter::policyDrops)++;
6✔
1320
        SLOG(g_log << Logger::Debug << "Dropping query because of a filtering policy " << makeLoginfo(comboWriter) << endl,
6✔
1321
             resolver.d_slog->info(Logr::Debug, "Dropping query because of a filtering policy"));
6✔
1322
        return;
6✔
1323
      }
6✔
1324
      catch (const ImmediateServFailException& e) {
3,061✔
1325
        if (g_logCommonErrors) {
4!
1326
          SLOG(g_log << Logger::Notice << "Sending SERVFAIL to " << comboWriter->getRemote() << " during resolve of '" << comboWriter->d_mdp.d_qname << "' because: " << e.reason << endl,
4✔
1327
               resolver.d_slog->error(Logr::Notice, e.reason, "Sending SERVFAIL during resolve"));
4✔
1328
        }
4✔
1329
        res = RCode::ServFail;
4✔
1330
      }
4✔
1331
      catch (const pdns::validation::TooManySEC3IterationsException& e) {
3,061✔
1332
        if (g_logCommonErrors) {
×
1333
          SLOG(g_log << Logger::Notice << "Sending SERVFAIL to " << comboWriter->getRemote() << " during resolve of '" << comboWriter->d_mdp.d_qname << "' because: " << e.what() << endl,
×
1334
               resolver.d_slog->error(Logr::Notice, e.what(), "Sending SERVFAIL during resolve", "dnsseclimithit", Logging::Loggable(true)));
×
1335
        }
×
1336
        res = RCode::ServFail;
×
1337
      }
×
1338
      catch (const SendTruncatedAnswerException& e) {
3,061✔
1339
        ret.clear();
3✔
1340
        resolver.d_appliedPolicy.addSOAtoRPZResult(ret);
3✔
1341
        res = RCode::NoError;
3✔
1342
        packetWriter.getHeader()->tc = 1;
3✔
1343
      }
3✔
1344
      catch (const PolicyHitException& e) {
3,061✔
1345
        res = -2;
2✔
1346
      }
2✔
1347
      dnsQuestion.validationState = resolver.getValidationState();
3,058✔
1348
      appliedPolicy = resolver.d_appliedPolicy;
3,058✔
1349
      comboWriter->d_policyTags = std::move(resolver.d_policyTags);
3,058✔
1350

1351
      if (appliedPolicy.d_type != DNSFilterEngine::PolicyType::None && appliedPolicy.d_zoneData && appliedPolicy.d_zoneData->d_extendedErrorCode) {
3,058!
1352
        comboWriter->d_extendedErrorCode = *appliedPolicy.d_zoneData->d_extendedErrorCode;
2✔
1353
        comboWriter->d_extendedErrorExtra = appliedPolicy.d_zoneData->d_extendedErrorExtra;
2✔
1354
      }
2✔
1355

1356
      // During lookup, an NSDNAME or NSIP trigger was hit in RPZ
1357
      if (res == -2) { // XXX This block should be macro'd, it is repeated post-resolve.
3,058✔
1358
        if (appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction) {
2!
1359
          throw PDNSException("NoAction policy returned while a NSDNAME or NSIP trigger was hit");
×
1360
        }
×
1361
        auto policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
2✔
1362
        if (policyResult == PolicyResult::HaveAnswer) {
2!
1363
          goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
2✔
1364
        }
2✔
1365
        else if (policyResult == PolicyResult::Drop) {
×
1366
          return;
×
1367
        }
×
1368
      }
2✔
1369

1370
      bool luaHookHandled = false;
3,056✔
1371
      if (comboWriter->d_luaContext) {
3,056✔
1372
        PolicyResult policyResult = PolicyResult::NoAction;
329✔
1373
        if (SyncRes::answerIsNOData(comboWriter->d_mdp.d_qtype, res, ret)) {
329✔
1374
          if (comboWriter->d_luaContext->nodata(dnsQuestion, res, resolver.d_eventTrace)) {
41✔
1375
            luaHookHandled = true;
12✔
1376
            shouldNotValidate = true;
12✔
1377
            policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
12✔
1378
          }
12✔
1379
        }
41✔
1380
        else if (res == RCode::NXDomain && comboWriter->d_luaContext->nxdomain(dnsQuestion, res, resolver.d_eventTrace)) {
288✔
1381
          luaHookHandled = true;
6✔
1382
          shouldNotValidate = true;
6✔
1383
          policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
6✔
1384
        }
6✔
1385
        if (policyResult == PolicyResult::HaveAnswer) {
329!
1386
          goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
×
1387
        }
×
1388
        else if (policyResult == PolicyResult::Drop) {
329✔
1389
          return;
4✔
1390
        }
4✔
1391
      } // dc->d_luaContext
329✔
1392

1393
      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,052!
1394
        res = getFakeAAAARecords(dnsQuestion.qname, *g_dns64Prefix, ret);
10✔
1395
        shouldNotValidate = true;
10✔
1396
      }
10✔
1397

1398
      if (comboWriter->d_luaContext) {
3,052✔
1399
        PolicyResult policyResult = PolicyResult::NoAction;
325✔
1400
        if (comboWriter->d_luaContext->hasPostResolveFFIfunc()) {
325✔
1401
          RecursorLua4::PostResolveFFIHandle handle(dnsQuestion);
7✔
1402
          auto match = resolver.d_eventTrace.add(RecEventTrace::LuaPostResolveFFI);
7✔
1403
          bool prResult = comboWriter->d_luaContext->postresolve_ffi(handle);
7✔
1404
          resolver.d_eventTrace.add(RecEventTrace::LuaPostResolveFFI, prResult, false, match);
7✔
1405
          if (prResult) {
7✔
1406
            shouldNotValidate = true;
6✔
1407
            policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
6✔
1408
          }
6✔
1409
        }
7✔
1410
        else if (comboWriter->d_luaContext->postresolve(dnsQuestion, res, resolver.d_eventTrace)) {
318✔
1411
          shouldNotValidate = true;
4✔
1412
          policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
4✔
1413
        }
4✔
1414
        if (policyResult == PolicyResult::HaveAnswer) {
325✔
1415
          goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
3✔
1416
        }
3✔
1417
        else if (policyResult == PolicyResult::Drop) {
322✔
1418
          return;
1✔
1419
        }
1✔
1420
      } // dc->d_luaContext
325✔
1421
    }
3,052✔
1422
    else if (comboWriter->d_luaContext) {
62✔
1423
      // preresolve returned true
1424
      shouldNotValidate = true;
59✔
1425
      auto policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
59✔
1426
      // haveAnswer case redundant
1427
      if (policyResult == PolicyResult::Drop) {
59✔
1428
        return;
2✔
1429
      }
2✔
1430
    }
59✔
1431

1432
  haveAnswer:;
3,121✔
1433
    if (tracedQuery || res == -1 || res == RCode::ServFail || packetWriter.getHeader()->rcode == static_cast<unsigned>(RCode::ServFail)) {
3,118!
1434
      dumpTrace(resolver.getTrace(), resolver.d_fixednow);
16✔
1435
    }
16✔
1436

1437
    if (res == -1) {
3,118✔
1438
      packetWriter.getHeader()->rcode = RCode::ServFail;
2✔
1439
      // no commit here, because no record
1440
      ++t_Counters.at(rec::Counter::servFails);
2✔
1441
    }
2✔
1442
    else {
3,116✔
1443
      packetWriter.getHeader()->rcode = res;
3,116✔
1444

1445
      // Does the validation mode or query demand validation?
1446
      if (!shouldNotValidate && resolver.isDNSSECValidationRequested()) {
3,116✔
1447
        try {
2,524✔
1448
          auto state = resolver.getValidationState();
2,524✔
1449

1450
          string x_marker;
2,524✔
1451
          std::shared_ptr<Logr::Logger> log;
2,524✔
1452
          if (resolver.doLog() || vStateIsBogus(state)) {
2,524!
1453
            // Only create logging object if needed below, beware if you change the logging logic!
1454
            log = resolver.d_slog->withValues("vstate", Logging::Loggable(state));
303✔
1455
            if (resolver.getDNSSECLimitHit()) {
303!
1456
              log = log->withValues("dnsseclimithit", Logging::Loggable(true));
×
1457
            }
×
1458
            auto xdnssec = g_xdnssec.getLocal();
303✔
1459
            if (xdnssec->check(comboWriter->d_mdp.d_qname)) {
303!
1460
              log = log->withValues("in-x-dnssec-names", Logging::Loggable(1));
×
1461
              x_marker = " [in x-dnssec-names]";
×
1462
            }
×
1463
          }
303✔
1464
          if (state == vState::Secure) {
2,524✔
1465
            if (resolver.doLog()) {
774✔
1466
              SLOG(g_log << Logger::Warning << "Answer to " << comboWriter->d_mdp.d_qname << "|" << QType(comboWriter->d_mdp.d_qtype) << x_marker << " for " << comboWriter->getRemote() << " validates correctly" << endl,
140✔
1467
                   log->info(Logr::Info, "Validates Correctly"));
140✔
1468
            }
140✔
1469

1470
            // Is the query source interested in the value of the ad-bit?
1471
            if (comboWriter->d_mdp.d_header.ad || DNSSECOK) {
774✔
1472
              packetWriter.getHeader()->ad = 1;
709✔
1473
            }
709✔
1474
          }
774✔
1475
          else if (state == vState::Insecure) {
1,750✔
1476
            if (resolver.doLog()) {
1,635✔
1477
              SLOG(g_log << Logger::Warning << "Answer to " << comboWriter->d_mdp.d_qname << "|" << QType(comboWriter->d_mdp.d_qtype) << x_marker << " for " << comboWriter->getRemote() << " validates as Insecure" << endl,
48✔
1478
                   log->info(Logr::Info, "Validates as Insecure"));
48✔
1479
            }
48✔
1480

1481
            packetWriter.getHeader()->ad = 0;
1,635✔
1482
          }
1,635✔
1483
          else if (vStateIsBogus(state)) {
115✔
1484
            if (t_bogusremotes) {
53!
1485
              t_bogusremotes->push_back(comboWriter->d_source);
53✔
1486
            }
53✔
1487
            if (t_bogusqueryring) {
53!
1488
              t_bogusqueryring->push_back({comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype});
53✔
1489
            }
53✔
1490
            if (g_dnssecLogBogus || resolver.doLog() || g_dnssecmode == DNSSECMode::ValidateForLog) {
53!
1491
              SLOG(g_log << Logger::Warning << "Answer to " << comboWriter->d_mdp.d_qname << "|" << QType(comboWriter->d_mdp.d_qtype) << x_marker << " for " << comboWriter->getRemote() << " validates as " << vStateToString(state) << endl,
53✔
1492
                   log->info(Logr::Notice, "Validates as Bogus"));
53✔
1493
            }
53✔
1494

1495
            // Does the query or validation mode sending out a SERVFAIL on validation errors?
1496
            if (!packetWriter.getHeader()->cd && (g_dnssecmode == DNSSECMode::ValidateAll || comboWriter->d_mdp.d_header.ad || DNSSECOK)) {
53!
1497
              if (resolver.doLog()) {
44!
1498
                SLOG(g_log << Logger::Warning << "Sending out SERVFAIL for " << comboWriter->d_mdp.d_qname << "|" << QType(comboWriter->d_mdp.d_qtype) << " because recursor or query demands it for Bogus results" << endl,
44✔
1499
                     log->info(Logr::Notice, "Sending out SERVFAIL because recursor or query demands it for Bogus results"));
44✔
1500
              }
44✔
1501

1502
              packetWriter.getHeader()->rcode = RCode::ServFail;
44✔
1503
              goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
44✔
1504
            }
44✔
1505
            else {
9✔
1506
              if (resolver.doLog()) {
9!
1507
                SLOG(g_log << Logger::Warning << "Not sending out SERVFAIL for " << comboWriter->d_mdp.d_qname << "|" << QType(comboWriter->d_mdp.d_qtype) << x_marker << " Bogus validation since neither config nor query demands this" << endl,
9✔
1508
                     log->info(Logr::Notice, "Sending out SERVFAIL because recursor or query demands it for Bogus results"));
9✔
1509
              }
9✔
1510
            }
9✔
1511
          }
53✔
1512
        }
2,524✔
1513
        catch (const ImmediateServFailException& e) {
2,524✔
1514
          if (g_logCommonErrors) {
×
1515
            SLOG(g_log << Logger::Notice << "Sending SERVFAIL to " << comboWriter->getRemote() << " during validation of '" << comboWriter->d_mdp.d_qname << "|" << QType(comboWriter->d_mdp.d_qtype) << "' because: " << e.reason << endl,
×
1516
                 resolver.d_slog->error(Logr::Notice, e.reason, "Sending SERVFAIL during validation", "exception", Logging::Loggable("ImmediateServFailException")));
×
1517
          }
×
1518
          goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
×
1519
        }
×
1520
        catch (const pdns::validation::TooManySEC3IterationsException& e) {
2,524✔
1521
          if (g_logCommonErrors || (g_dnssecLogBogus && resolver.getDNSSECLimitHit())) {
×
1522
            SLOG(g_log << Logger::Notice << "Sending SERVFAIL to " << comboWriter->getRemote() << " during validation of '" << comboWriter->d_mdp.d_qname << "|" << QType(comboWriter->d_mdp.d_qtype) << "' because: " << e.what() << endl,
×
1523
                 resolver.d_slog->error(Logr::Notice, e.what(), "Sending SERVFAIL during validation", "exception", Logging::Loggable("TooManySEC3IterationsException"), "dnsseclimithit", Logging::Loggable(resolver.getDNSSECLimitHit())));
×
1524
          }
×
1525
          goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
×
1526
        }
×
1527
      }
2,524✔
1528

1529
      if (!ret.empty()) {
3,072✔
1530
#ifdef notyet
1531
        // As dedupping is relatively expensive do not dedup in general. We do have a few cases
1532
        // where we call dedup explicitly, e.g. when doing NAT64 or when adding NSEC records in
1533
        // doCNAMECacheCheck
1534
        pdns::dedupRecords(ret);
1535
#endif
1536
        pdns::orderAndShuffle(ret, false);
3,037✔
1537
        if (auto listToSort = luaconfsLocal->sortlist.getOrderCmp(comboWriter->d_source)) {
3,037✔
1538
          stable_sort(ret.begin(), ret.end(), *listToSort);
1✔
1539
          variableAnswer = true;
1✔
1540
        }
1✔
1541
      }
3,037✔
1542

1543
      bool needCommit = false;
3,072✔
1544
      for (const auto& record : ret) {
9,853✔
1545
        if (!DNSSECOK && (record.d_type == QType::NSEC3 || ((record.d_type == QType::RRSIG || record.d_type == QType::NSEC) && ((comboWriter->d_mdp.d_qtype != record.d_type && comboWriter->d_mdp.d_qtype != QType::ANY) || (record.d_place != DNSResourceRecord::ANSWER && record.d_place != DNSResourceRecord::ADDITIONAL))))) {
9,853!
1546
          continue;
176✔
1547
        }
176✔
1548

1549
        if (!addRecordToPacket(packetWriter, record, minTTL, comboWriter->d_ttlCap, maxanswersize, seenAuthSOA)) {
9,677✔
1550
          needCommit = false;
3✔
1551
          break;
3✔
1552
        }
3✔
1553
        needCommit = true;
9,674✔
1554

1555
        bool udr = false;
9,674✔
1556
#ifdef NOD_ENABLED
9,674✔
1557
        if (g_udrEnabled) {
9,674✔
1558
          udr = udrCheckUniqueDNSRecord(nodlogger, comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype, record);
3✔
1559
          if (!hasUDR && udr) {
3!
1560
            hasUDR = true;
3✔
1561
          }
3✔
1562
        }
3✔
1563
#endif /* NOD ENABLED */
9,674✔
1564

1565
        if (t_protobufServers.servers) {
9,674✔
1566
          // Max size is 64k, but we're conservative here, as other fields are added after the answers have been added
1567
          // If a single answer causes a too big protobuf message, it will be dropped by queueData()
1568
          // But note addRR has code to prevent that
1569
          if (pbMessage.size() < std::numeric_limits<uint16_t>::max() / 2) {
36!
1570
            pbMessage.addRR(record, luaconfsLocal->protobufExportConfig.exportTypes, udr);
36✔
1571
          }
36✔
1572
        }
36✔
1573
      }
9,674✔
1574
      if (needCommit) {
3,072✔
1575
        packetWriter.commit();
3,034✔
1576
      }
3,034✔
1577
#ifdef NOD_ENABLED
3,072✔
1578
#ifdef HAVE_FSTRM
3,072✔
1579
      if (hasUDR) {
3,072✔
1580
        if (isEnabledForUDRs(t_nodFrameStreamServersInfo.servers)) {
3✔
1581
          struct timespec timeSpec{};
2✔
1582
          std::string str;
2✔
1583
          if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
2!
1584
            TIMEVAL_TO_TIMESPEC(&comboWriter->d_kernelTimestamp, &timeSpec); // NOLINT
×
1585
          }
×
1586
          else {
2✔
1587
            TIMEVAL_TO_TIMESPEC(&comboWriter->d_now, &timeSpec); // NOLINT
2✔
1588
          }
2✔
1589
          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!
1590
          str = message.getBuffer();
2✔
1591
          for (auto& logger : *(t_nodFrameStreamServersInfo.servers)) {
2✔
1592
            if (logger->logUDRs()) {
2!
1593
              remoteLoggerQueueData(*logger, str);
2✔
1594
            }
2✔
1595
          }
2✔
1596
        }
2✔
1597
      }
3✔
1598
#endif // HAVE_FSTRM
3,072✔
1599
#endif // NOD_ENABLED
3,072✔
1600
    }
3,072✔
1601
  sendit:;
3,142✔
1602

1603
    if (g_useIncomingECS && comboWriter->d_ecsFound && !resolver.wasVariable() && !variableAnswer) {
3,142!
1604
      EDNSSubnetOpts ednsOptions;
380✔
1605
      ednsOptions.setSource(comboWriter->d_ednssubnet.getSource());
380✔
1606
      ComboAddress sourceAddr;
380✔
1607
      sourceAddr.reset();
380✔
1608
      sourceAddr.sin4.sin_family = ednsOptions.getFamily();
380✔
1609
      ednsOptions.setScopePrefixLength(0);
380✔
1610
      auto ecsPayload = ednsOptions.makeOptString();
380✔
1611

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

1616
        maxanswersize -= EDNSOptionCodeSize + EDNSOptionLengthSize + ecsPayload.size();
379✔
1617

1618
        returnedEdnsOptions.emplace_back(EDNSOptionCode::ECS, std::move(ecsPayload));
379✔
1619
      }
379✔
1620
    }
380✔
1621

1622
    if (haveEDNS && addPaddingToResponse) {
3,142✔
1623
      size_t currentSize = packetWriter.getSizeWithOpts(returnedEdnsOptions);
5✔
1624
      /* we don't use maxawnswersize because it accounts for some EDNS options, but
1625
         not all of them (for example ECS) */
1626
      size_t maxSize = min(static_cast<uint16_t>(edo.d_packetsize >= 512 ? edo.d_packetsize : 512), g_udpTruncationThreshold);
5!
1627

1628
      if (currentSize < (maxSize - 4)) {
5!
1629
        size_t remaining = maxSize - (currentSize + 4);
5✔
1630
        /* from rfc8647, "4.1.  Recommended Strategy: Block-Length Padding":
1631
           If a server receives a query that includes the EDNS(0) "Padding"
1632
           option, it MUST pad the corresponding response (see Section 4 of
1633
           RFC 7830) and SHOULD pad the corresponding response to a
1634
           multiple of 468 octets (see below).
1635
        */
1636
        const size_t blockSize = 468;
5✔
1637
        size_t modulo = (currentSize + 4) % blockSize;
5✔
1638
        size_t padSize = 0;
5✔
1639
        if (modulo > 0) {
5!
1640
          padSize = std::min(blockSize - modulo, remaining);
5✔
1641
        }
5✔
1642
        returnedEdnsOptions.emplace_back(EDNSOptionCode::PADDING, makeEDNSPaddingOptString(padSize));
5✔
1643
      }
5✔
1644
    }
5✔
1645

1646
    if (haveEDNS) {
3,142✔
1647
      auto state = resolver.getValidationState();
1,312✔
1648
      if (comboWriter->d_extendedErrorCode || resolver.d_extendedError || (SyncRes::s_addExtendedResolutionDNSErrors && vStateIsBogus(state))) {
1,312✔
1649
        EDNSExtendedError::code code = EDNSExtendedError::code::Other;
90✔
1650
        std::string extra;
90✔
1651

1652
        if (comboWriter->d_extendedErrorCode) {
90✔
1653
          code = static_cast<EDNSExtendedError::code>(*comboWriter->d_extendedErrorCode);
8✔
1654
          extra = std::move(comboWriter->d_extendedErrorExtra);
8✔
1655
        }
8✔
1656
        else if (resolver.d_extendedError) {
82✔
1657
          code = static_cast<EDNSExtendedError::code>(resolver.d_extendedError->infoCode);
40✔
1658
          extra = std::move(resolver.d_extendedError->extraText);
40✔
1659
        }
40✔
1660
        else {
42✔
1661
          switch (state) {
42✔
1662
          case vState::BogusNoValidDNSKEY:
29✔
1663
            code = EDNSExtendedError::code::DNSKEYMissing;
29✔
1664
            break;
29✔
1665
          case vState::BogusInvalidDenial:
×
1666
            code = EDNSExtendedError::code::NSECMissing;
×
1667
            break;
×
1668
          case vState::BogusUnableToGetDSs:
×
1669
            code = EDNSExtendedError::code::DNSSECBogus;
×
1670
            break;
×
1671
          case vState::BogusUnableToGetDNSKEYs:
×
1672
            code = EDNSExtendedError::code::DNSKEYMissing;
×
1673
            break;
×
1674
          case vState::BogusSelfSignedDS:
×
1675
            code = EDNSExtendedError::code::DNSSECBogus;
×
1676
            break;
×
1677
          case vState::BogusNoRRSIG:
2✔
1678
            code = EDNSExtendedError::code::RRSIGsMissing;
2✔
1679
            break;
2✔
1680
          case vState::BogusNoValidRRSIG:
4✔
1681
            code = EDNSExtendedError::code::DNSSECBogus;
4✔
1682
            break;
4✔
1683
          case vState::BogusMissingNegativeIndication:
1✔
1684
            code = EDNSExtendedError::code::NSECMissing;
1✔
1685
            break;
1✔
1686
          case vState::BogusSignatureNotYetValid:
2✔
1687
            code = EDNSExtendedError::code::SignatureNotYetValid;
2✔
1688
            break;
2✔
1689
          case vState::BogusSignatureExpired:
4✔
1690
            code = EDNSExtendedError::code::SignatureExpired;
4✔
1691
            break;
4✔
1692
          case vState::BogusUnsupportedDNSKEYAlgo:
×
1693
            code = EDNSExtendedError::code::UnsupportedDNSKEYAlgorithm;
×
1694
            break;
×
1695
          case vState::BogusUnsupportedDSDigestType:
×
1696
            code = EDNSExtendedError::code::UnsupportedDSDigestType;
×
1697
            break;
×
1698
          case vState::BogusNoZoneKeyBitSet:
×
1699
            code = EDNSExtendedError::code::NoZoneKeyBitSet;
×
1700
            break;
×
1701
          case vState::BogusRevokedDNSKEY:
×
1702
          case vState::BogusInvalidDNSKEYProtocol:
×
1703
            code = EDNSExtendedError::code::DNSSECBogus;
×
1704
            break;
×
1705
          default:
×
1706
            throw std::runtime_error("Bogus validation state not handled: " + vStateToString(state));
×
1707
          }
42✔
1708
        }
42✔
1709

1710
        EDNSExtendedError eee;
90✔
1711
        eee.infoCode = static_cast<uint16_t>(code);
90✔
1712
        eee.extraText = std::move(extra);
90✔
1713

1714
        if (packetWriter.size() < maxanswersize && (maxanswersize - packetWriter.size()) >= (EDNSOptionCodeSize + EDNSOptionLengthSize + sizeof(eee.infoCode) + eee.extraText.size())) {
90!
1715
          returnedEdnsOptions.emplace_back(EDNSOptionCode::EXTENDEDERROR, makeEDNSExtendedErrorOptString(eee));
89✔
1716
        }
89✔
1717
      }
90✔
1718

1719
      /* we try to add the EDNS OPT RR even for truncated answers,
1720
         as rfc6891 states:
1721
         "The minimal response MUST be the DNS header, question section, and an
1722
         OPT record.  This MUST also occur when a truncated response (using
1723
         the DNS header's TC bit) is returned."
1724
      */
1725
      packetWriter.addOpt(512, ednsExtRCode, DNSSECOK ? EDNSOpts::DNSSECOK : 0, returnedEdnsOptions);
1,312✔
1726
      packetWriter.commit();
1,312✔
1727
    }
1,312✔
1728

1729
    t_Counters.at(rec::ResponseStats::responseStats).submitResponse(comboWriter->d_mdp.d_qtype, packet.size(), packetWriter.getHeader()->rcode);
3,142✔
1730
    updateResponseStats(res, comboWriter->d_source, packet.size(), &comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype);
3,142✔
1731
#ifdef NOD_ENABLED
3,142✔
1732
    bool nod = false;
3,142✔
1733
    if (g_nodEnabled) {
3,142✔
1734
      if (nodCheckNewDomain(nodlogger, comboWriter->d_mdp.d_qname)) {
3!
1735
        nod = true;
3✔
1736
#ifdef HAVE_FSTRM
3✔
1737
        if (isEnabledForNODs(t_nodFrameStreamServersInfo.servers)) {
3✔
1738
          struct timespec timeSpec{};
2✔
1739
          std::string str;
2✔
1740
          if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
2!
1741
            TIMEVAL_TO_TIMESPEC(&comboWriter->d_kernelTimestamp, &timeSpec); // NOLINT
×
1742
          }
×
1743
          else {
2✔
1744
            TIMEVAL_TO_TIMESPEC(&comboWriter->d_now, &timeSpec); // NOLINT
2✔
1745
          }
2✔
1746
          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!
1747
          str = message.getBuffer();
2✔
1748

1749
          for (auto& logger : *(t_nodFrameStreamServersInfo.servers)) {
2✔
1750
            if (logger->logNODs()) {
2!
1751
              remoteLoggerQueueData(*logger, str);
2✔
1752
            }
2✔
1753
          }
2✔
1754
        }
2✔
1755
#endif // HAVE_FSTRM
3✔
1756
      }
3✔
1757
    }
3✔
1758
#endif /* NOD_ENABLED */
3,142✔
1759

1760
    if (variableAnswer || resolver.wasVariable()) {
3,142✔
1761
      t_Counters.at(rec::Counter::variableResponses)++;
178✔
1762
    }
178✔
1763

1764
    if (t_protobufServers.servers && !(luaconfsLocal->protobufExportConfig.taggedOnly && appliedPolicy.getName().empty() && comboWriter->d_policyTags.empty())) {
3,142!
1765
      // Start constructing embedded DNSResponse object
1766
      pbMessage.setResponseCode(packetWriter.getHeader()->rcode);
28✔
1767
      if (!appliedPolicy.getName().empty()) {
28✔
1768
        pbMessage.setAppliedPolicy(appliedPolicy.getName());
3✔
1769
        pbMessage.setAppliedPolicyType(appliedPolicy.d_type);
3✔
1770
        pbMessage.setAppliedPolicyTrigger(appliedPolicy.getTrigger());
3✔
1771
        pbMessage.setAppliedPolicyHit(appliedPolicy.getHit());
3✔
1772
        pbMessage.setAppliedPolicyKind(appliedPolicy.d_kind);
3✔
1773
      }
3✔
1774
      pbMessage.setInBytes(packet.size());
28✔
1775
      pbMessage.setValidationState(resolver.getValidationState());
28✔
1776
      // See if we want to store the policyTags into the PC
1777
      addPolicyTagsToPBMessageIfNeeded(*comboWriter, pbMessage);
28✔
1778

1779
      // Take s snap of the current protobuf buffer state to store in the PC
1780
      pbDataForCache = boost::make_optional(RecursorPacketCache::PBData{
28✔
1781
        pbMessage.getMessageBuf(),
28✔
1782
        pbMessage.getResponseBuf(),
28✔
1783
        !appliedPolicy.getName().empty() || !comboWriter->d_policyTags.empty()});
28✔
1784
#ifdef NOD_ENABLED
28✔
1785
      // if (g_udrEnabled) ??
1786
      pbMessage.clearUDR(pbDataForCache->d_response);
28✔
1787
#endif
28✔
1788
    }
28✔
1789

1790
    const bool intoPC = g_packetCache && !variableAnswer && !resolver.wasVariable();
3,142✔
1791
    if (intoPC) {
3,142✔
1792
      minTTL = capPacketCacheTTL(*packetWriter.getHeader(), minTTL, seenAuthSOA);
2,830✔
1793
      g_packetCache->insertResponsePacket(comboWriter->d_tag, comboWriter->d_qhash, std::move(comboWriter->d_query), comboWriter->d_mdp.d_qname,
2,830✔
1794
                                          comboWriter->d_mdp.d_qtype, comboWriter->d_mdp.d_qclass,
2,830✔
1795
                                          string(reinterpret_cast<const char*>(&*packet.begin()), packet.size()), // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2,830✔
1796
                                          g_now.tv_sec,
2,830✔
1797
                                          minTTL,
2,830✔
1798
                                          dnsQuestion.validationState,
2,830✔
1799
                                          std::move(pbDataForCache), comboWriter->d_tcp);
2,830✔
1800
    }
2,830✔
1801

1802
    if (g_regressionTestMode) {
3,142✔
1803
      t_Counters.updateSnap(g_regressionTestMode);
608✔
1804
    }
608✔
1805

1806
    if (!comboWriter->d_tcp) {
3,142✔
1807
      struct msghdr msgh{};
2,363✔
1808
      struct iovec iov{};
2,363✔
1809
      cmsgbuf_aligned cbuf{};
2,363✔
1810
      fillMSGHdr(&msgh, &iov, &cbuf, 0, reinterpret_cast<char*>(&*packet.begin()), packet.size(), &comboWriter->d_remote); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2,363✔
1811
      msgh.msg_control = nullptr;
2,363✔
1812

1813
      if (g_fromtosockets.count(comboWriter->d_socket) > 0) {
2,363✔
1814
        addCMsgSrcAddr(&msgh, &cbuf, &comboWriter->d_local, 0);
1✔
1815
      }
1✔
1816
      int sendErr = sendOnNBSocket(comboWriter->d_socket, &msgh);
2,363✔
1817
      if (sendErr != 0 && g_logCommonErrors) {
2,363!
1818
        SLOG(g_log << Logger::Warning << "Sending UDP reply to client " << comboWriter->getRemote() << " failed with: "
×
1819
                   << stringerror(sendErr) << endl,
×
1820
             g_slogudpin->error(Logr::Warning, sendErr, "Sending UDP reply to client failed"));
×
1821
      }
×
1822
    }
2,363✔
1823
    else {
779✔
1824
      bool hadError = sendResponseOverTCP(comboWriter, packet);
779✔
1825
      finishTCPReply(comboWriter, hadError, true);
779✔
1826
      tcpGuard.setHandled();
779✔
1827
    }
779✔
1828

1829
    resolver.d_eventTrace.add(RecEventTrace::AnswerSent);
3,142✔
1830

1831
    // Now do the per query changing part of the protobuf message
1832
    if (t_protobufServers.servers && !(luaconfsLocal->protobufExportConfig.taggedOnly && appliedPolicy.getName().empty() && comboWriter->d_policyTags.empty())) {
3,142!
1833
      // Below are the fields that are not stored in the packet cache and will be appended here and on a cache hit
1834
      if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
28!
1835
        pbMessage.setQueryTime(comboWriter->d_kernelTimestamp.tv_sec, comboWriter->d_kernelTimestamp.tv_usec);
×
1836
      }
×
1837
      else {
28✔
1838
        pbMessage.setQueryTime(comboWriter->d_now.tv_sec, comboWriter->d_now.tv_usec);
28✔
1839
      }
28✔
1840
      pbMessage.setMessageIdentity(comboWriter->d_uuid);
28✔
1841
      pbMessage.setSocketProtocol(comboWriter->d_tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP);
28✔
1842

1843
      if (!luaconfsLocal->protobufExportConfig.logMappedFrom) {
28✔
1844
        pbMessage.setSocketFamily(comboWriter->d_source.sin4.sin_family);
26✔
1845
        Netmask requestorNM(comboWriter->d_source, comboWriter->d_source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
26!
1846
        ComboAddress requestor = requestorNM.getMaskedNetwork();
26✔
1847
        pbMessage.setFrom(requestor);
26✔
1848
        pbMessage.setFromPort(comboWriter->d_source.getPort());
26✔
1849
      }
26✔
1850
      else {
2✔
1851
        pbMessage.setSocketFamily(comboWriter->d_mappedSource.sin4.sin_family);
2✔
1852
        Netmask requestorNM(comboWriter->d_mappedSource, comboWriter->d_mappedSource.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
2!
1853
        ComboAddress requestor = requestorNM.getMaskedNetwork();
2✔
1854
        pbMessage.setFrom(requestor);
2✔
1855
        pbMessage.setFromPort(comboWriter->d_mappedSource.getPort());
2✔
1856
      }
2✔
1857

1858
      pbMessage.setTo(comboWriter->d_destination);
28✔
1859
      pbMessage.setId(comboWriter->d_mdp.d_header.id);
28✔
1860

1861
      pbMessage.setTime();
28✔
1862
      pbMessage.setEDNSSubnet(comboWriter->d_ednssubnet.getSource(), comboWriter->d_ednssubnet.getSource().isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
28!
1863
      pbMessage.setRequestorId(dnsQuestion.requestorId);
28✔
1864
      pbMessage.setDeviceId(dnsQuestion.deviceId);
28✔
1865
      pbMessage.setDeviceName(dnsQuestion.deviceName);
28✔
1866
      pbMessage.setToPort(comboWriter->d_destination.getPort());
28✔
1867
      pbMessage.addPolicyTags(comboWriter->d_gettagPolicyTags);
28✔
1868
      pbMessage.setWorkerId(RecThreadInfo::thread_local_id());
28✔
1869
      pbMessage.setPacketCacheHit(false);
28✔
1870
      pbMessage.setOutgoingQueries(resolver.d_outqueries);
28✔
1871
      for (const auto& metaValue : dnsQuestion.meta) {
28✔
1872
        pbMessage.setMeta(metaValue.first, metaValue.second.stringVal, metaValue.second.intVal);
2✔
1873
      }
2✔
1874
#ifdef NOD_ENABLED
28✔
1875
      if (g_nodEnabled) {
28!
1876
        if (nod) {
×
1877
          pbMessage.setNewlyObservedDomain(true);
×
1878
          pbMessage.addPolicyTag(g_nod_pbtag);
×
1879
        }
×
1880
        if (hasUDR) {
×
1881
          pbMessage.addPolicyTag(g_udr_pbtag);
×
1882
        }
×
1883
      }
×
1884
#endif /* NOD_ENABLED */
28✔
1885
      if (resolver.d_eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_pb)) {
28!
1886
        pbMessage.addEvents(resolver.d_eventTrace);
×
1887
      }
×
1888
      if (resolver.d_eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
28!
1889
        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));
×
1890
        string otData = otTrace.encode();
×
1891
        pbMessage.setOpenTelemetryData(otData);
×
1892
      }
×
1893
      if (comboWriter->d_logResponse) {
28✔
1894
        protobufLogResponse(pbMessage);
25✔
1895
      }
25✔
1896
    }
28✔
1897

1898
    if (resolver.d_eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_log)) {
3,142!
1899
      SLOG(g_log << Logger::Info << resolver.d_eventTrace.toString() << endl,
×
1900
           resolver.d_slog->info(Logr::Info, resolver.d_eventTrace.toString())); // Maybe we want it to be more fancy?
×
1901
    }
×
1902

1903
    // Originally this code used a mix of floats, doubles, uint64_t with different units.
1904
    // Now it always uses an integral number of microseconds, except for averages, which use doubles
1905
    uint64_t spentUsec = uSec(resolver.getNow() - comboWriter->d_now);
3,142✔
1906
    if (!g_quiet) {
3,142✔
1907
      if (!g_slogStructured) {
920✔
1908
        g_log << Logger::Error << RecThreadInfo::thread_local_id() << " [" << g_multiTasker->getTid() << "/" << g_multiTasker->numProcesses() << "] answer to " << (comboWriter->d_mdp.d_header.rd ? "" : "non-rd ") << "question '" << comboWriter->d_mdp.d_qname << "|" << DNSRecordContent::NumberToType(comboWriter->d_mdp.d_qtype);
×
1909
        g_log << "': " << ntohs(packetWriter.getHeader()->ancount) << " answers, " << ntohs(packetWriter.getHeader()->arcount) << " additional, took " << resolver.d_outqueries << " packets, " << resolver.d_totUsec / 1000.0 << " netw ms, " << static_cast<double>(spentUsec) / 1000.0 << " tot ms, " << resolver.d_throttledqueries << " throttled, " << resolver.d_timeouts << " timeouts, " << resolver.d_tcpoutqueries << "/" << resolver.d_dotoutqueries << " tcp/dot connections, rcode=" << res;
×
1910

1911
        if (!shouldNotValidate && resolver.isDNSSECValidationRequested()) {
×
1912
          g_log << ", dnssec=" << resolver.getValidationState();
×
1913
        }
×
1914
        g_log << " answer-is-variable=" << resolver.wasVariable() << ", into-packetcache=" << intoPC;
×
1915
        g_log << " maxdepth=" << resolver.d_maxdepth;
×
1916
        g_log << endl;
×
1917
      }
×
1918
      else {
920✔
1919
        resolver.d_slog->info(Logr::Info, "Answer", "rd", Logging::Loggable(comboWriter->d_mdp.d_header.rd),
920✔
1920
                              "answers", Logging::Loggable(ntohs(packetWriter.getHeader()->ancount)),
920✔
1921
                              "additional", Logging::Loggable(ntohs(packetWriter.getHeader()->arcount)),
920✔
1922
                              "outqueries", Logging::Loggable(resolver.d_outqueries),
920✔
1923
                              "netms", Logging::Loggable(resolver.d_totUsec / 1000.0),
920✔
1924
                              "totms", Logging::Loggable(static_cast<double>(spentUsec) / 1000.0),
920✔
1925
                              "throttled", Logging::Loggable(resolver.d_throttledqueries),
920✔
1926
                              "timeouts", Logging::Loggable(resolver.d_timeouts),
920✔
1927
                              "tcpout", Logging::Loggable(resolver.d_tcpoutqueries),
920✔
1928
                              "dotout", Logging::Loggable(resolver.d_dotoutqueries),
920✔
1929
                              "rcode", Logging::Loggable(res),
920✔
1930
                              "validationState", Logging::Loggable(resolver.getValidationState()),
920✔
1931
                              "answer-is-variable", Logging::Loggable(resolver.wasVariable()),
920✔
1932
                              "into-packetcache", Logging::Loggable(intoPC),
920✔
1933
                              "maxdepth", Logging::Loggable(resolver.d_maxdepth));
920✔
1934
      }
920✔
1935
    }
920✔
1936

1937
    if (comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Query)) {
3,142✔
1938
      if (resolver.d_outqueries != 0 || resolver.d_throttledqueries != 0 || resolver.d_authzonequeries != 0) {
3,118✔
1939
        g_recCache->incCacheMisses();
2,616✔
1940
      }
2,616✔
1941
      else {
502✔
1942
        g_recCache->incCacheHits();
502✔
1943
      }
502✔
1944
    }
3,118✔
1945

1946
    t_Counters.at(rec::Histogram::answers)(spentUsec);
3,142✔
1947
    t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
3,142✔
1948

1949
    auto newLat = static_cast<double>(spentUsec);
3,142✔
1950
    newLat = min(newLat, g_networkTimeoutMsec * 1000.0); // outliers of several minutes exist..
3,142✔
1951
    t_Counters.at(rec::DoubleWAvgCounter::avgLatencyUsec).addToRollingAvg(newLat, g_latencyStatSize);
3,142✔
1952
    // no worries, we do this for packet cache hits elsewhere
1953

1954
    if (spentUsec >= resolver.d_totUsec) {
3,142✔
1955
      uint64_t ourtime = spentUsec - resolver.d_totUsec;
3,141✔
1956
      t_Counters.at(rec::Histogram::ourtime)(ourtime);
3,141✔
1957
      newLat = static_cast<double>(ourtime); // usec
3,141✔
1958
      t_Counters.at(rec::DoubleWAvgCounter::avgLatencyOursUsec).addToRollingAvg(newLat, g_latencyStatSize);
3,141✔
1959
    }
3,141✔
1960

1961
#ifdef NOD_ENABLED
3,142✔
1962
    if (nod) {
3,142✔
1963
      sendNODLookup(nodlogger, comboWriter->d_mdp.d_qname);
3✔
1964
    }
3✔
1965
#endif /* NOD_ENABLED */
3,142✔
1966

1967
    //    cout<<dc->d_mdp.d_qname<<"\t"<<MT->getUsec()<<"\t"<<sr.d_outqueries<<endl;
1968
  }
3,142✔
1969
  catch (const PDNSException& ae) {
3,155✔
1970
    SLOG(g_log << Logger::Error << "startDoResolve problem " << makeLoginfo(comboWriter) << ": " << ae.reason << endl,
×
1971
         resolver.d_slog->error(Logr::Error, ae.reason, "startDoResolve problem", "exception", Logging::Loggable("PDNSException")));
×
1972
  }
×
1973
  catch (const MOADNSException& mde) {
3,155✔
1974
    SLOG(g_log << Logger::Error << "DNS parser error " << makeLoginfo(comboWriter) << ": " << comboWriter->d_mdp.d_qname << ", " << mde.what() << endl,
×
1975
         resolver.d_slog->error(Logr::Error, mde.what(), "DNS parser error"));
×
1976
  }
×
1977
  catch (const std::exception& e) {
3,155✔
1978
    SLOG(g_log << Logger::Error << "STL error " << makeLoginfo(comboWriter) << ": " << e.what(),
×
1979
         resolver.d_slog->error(Logr::Error, e.what(), "Exception in resolver context", "exception", Logging::Loggable("std::exception")));
×
1980

1981
    // Luawrapper nests the exception from Lua, so we unnest it here
1982
    try {
×
1983
      std::rethrow_if_nested(e);
×
1984
    }
×
1985
    catch (const std::exception& ne) {
×
1986
      SLOG(g_log << ". Extra info: " << ne.what(),
×
1987
           resolver.d_slog->error(Logr::Error, ne.what(), "Nested exception in resolver context", Logging::Loggable("std::exception")));
×
1988
    }
×
1989
    catch (...) {
×
1990
    }
×
1991
    if (!g_slogStructured) {
×
1992
      g_log << endl;
×
1993
    }
×
1994
  }
×
1995
  catch (...) {
3,155✔
1996
    SLOG(g_log << Logger::Error << "Any other exception in a resolver context " << makeLoginfo(comboWriter) << endl,
×
1997
         resolver.d_slog->info(Logr::Error, "Any other exception in a resolver context"));
×
1998
  }
×
1999

2000
  runTaskOnce(g_logCommonErrors);
3,142✔
2001

2002
  static const size_t stackSizeThreshold = 9 * ::arg().asNum("stack-size") / 10;
3,142✔
2003
  if (g_multiTasker->getMaxStackUsage() >= stackSizeThreshold) {
3,142!
2004
    SLOG(g_log << Logger::Error << "Reached mthread stack usage of 90%: " << g_multiTasker->getMaxStackUsage() << " " << makeLoginfo(comboWriter) << " after " << resolver.d_outqueries << " out queries, " << resolver.d_tcpoutqueries << " TCP out queries, " << resolver.d_dotoutqueries << " DoT out queries" << endl,
×
2005
         resolver.d_slog->info(Logr::Error, "Reached mthread stack usage of 90%",
×
2006
                               "stackUsage", Logging::Loggable(g_multiTasker->getMaxStackUsage()),
×
2007
                               "outqueries", Logging::Loggable(resolver.d_outqueries),
×
2008
                               "netms", Logging::Loggable(resolver.d_totUsec / 1000.0),
×
2009
                               "throttled", Logging::Loggable(resolver.d_throttledqueries),
×
2010
                               "timeouts", Logging::Loggable(resolver.d_timeouts),
×
2011
                               "tcpout", Logging::Loggable(resolver.d_tcpoutqueries),
×
2012
                               "dotout", Logging::Loggable(resolver.d_dotoutqueries),
×
2013
                               "validationState", Logging::Loggable(resolver.getValidationState())));
×
2014
  }
×
2015
  t_Counters.at(rec::Counter::maxMThreadStackUsage) = max(g_multiTasker->getMaxStackUsage(), t_Counters.at(rec::Counter::maxMThreadStackUsage));
3,142✔
2016
  t_Counters.updateSnap(g_regressionTestMode);
3,142✔
2017
}
3,142✔
2018

2019
void getQNameAndSubnet(const std::string& question, DNSName* dnsname, uint16_t* qtype, uint16_t* qclass,
2020
                       bool& foundECS, EDNSSubnetOpts* ednssubnet, EDNSOptionViewMap* options, boost::optional<uint32_t>& ednsVersion)
2021
{
157✔
2022
  const dnsheader_aligned dnshead(question.data());
157✔
2023
  const dnsheader* dhPointer = dnshead.get();
157✔
2024
  size_t questionLen = question.length();
157✔
2025
  unsigned int consumed = 0;
157✔
2026
  *dnsname = DNSName(question.c_str(), static_cast<int>(questionLen), sizeof(dnsheader), false, qtype, qclass, &consumed);
157✔
2027

2028
  size_t pos = sizeof(dnsheader) + consumed + 4;
157✔
2029
  const size_t headerSize = /* root */ 1 + sizeof(dnsrecordheader);
157✔
2030
  const uint16_t arcount = ntohs(dhPointer->arcount);
157✔
2031

2032
  for (uint16_t arpos = 0; arpos < arcount && questionLen >= (pos + headerSize) && !foundECS; arpos++) {
174!
2033
    if (question.at(pos) != 0) {
115!
2034
      /* not an OPT, bye. */
2035
      return;
×
2036
    }
×
2037

2038
    pos += 1;
115✔
2039
    const auto* drh = reinterpret_cast<const dnsrecordheader*>(&question.at(pos)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
115✔
2040
    if (ntohs(drh->d_type) == QType::OPT) {
115!
2041
      uint32_t edns{};
115✔
2042
      memcpy(&edns, &drh->d_ttl, sizeof(edns)); // drh is not neccesarily aligned, so no uint32 assignment can be done
115✔
2043
      ednsVersion = edns;
115✔
2044
    }
115✔
2045
    pos += sizeof(dnsrecordheader);
115✔
2046

2047
    if (pos >= questionLen) {
115✔
2048
      return;
98✔
2049
    }
98✔
2050

2051
    /* OPT root label (1) followed by type (2) */
2052
    if (ntohs(drh->d_type) == QType::OPT) {
17!
2053
      if (options == nullptr) {
17✔
2054
        size_t ecsStartPosition = 0;
13✔
2055
        size_t ecsLen = 0;
13✔
2056
        /* we need to pass the record len */
2057
        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✔
2058
        if (res == 0 && ecsLen > 4) {
13!
2059
          EDNSSubnetOpts eso;
10✔
2060
          if (EDNSSubnetOpts::getFromString(&question.at(pos - sizeof(drh->d_clen) + ecsStartPosition + 4), ecsLen - 4, &eso)) {
10!
2061
            *ednssubnet = eso;
10✔
2062
            foundECS = true;
10✔
2063
          }
10✔
2064
        }
10✔
2065
      }
13✔
2066
      else {
4✔
2067
        /* we need to pass the record len */
2068
        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✔
2069
        if (res == 0) {
4!
2070
          const auto iter = options->find(EDNSOptionCode::ECS);
4✔
2071
          if (iter != options->end() && !iter->second.values.empty() && iter->second.values.at(0).content != nullptr && iter->second.values.at(0).size > 0) {
4!
2072
            EDNSSubnetOpts eso;
4✔
2073
            if (EDNSSubnetOpts::getFromString(iter->second.values.at(0).content, iter->second.values.at(0).size, &eso)) {
4!
2074
              *ednssubnet = eso;
4✔
2075
              foundECS = true;
4✔
2076
            }
4✔
2077
          }
4✔
2078
        }
4✔
2079
      }
4✔
2080
    }
17✔
2081

2082
    pos += ntohs(drh->d_clen);
17✔
2083
  }
17✔
2084
}
157✔
2085

2086
bool checkForCacheHit(bool qnameParsed, unsigned int tag, const string& data,
2087
                      DNSName& qname, uint16_t& qtype, uint16_t& qclass,
2088
                      const struct timeval& now,
2089
                      string& response, uint32_t& qhash,
2090
                      RecursorPacketCache::OptPBData& pbData, bool tcp, const ComboAddress& source, const ComboAddress& mappedSource)
2091
{
6,110✔
2092
  if (!g_packetCache) {
6,110✔
2093
    return false;
136✔
2094
  }
136✔
2095
  bool cacheHit = false;
5,974✔
2096
  uint32_t age = 0;
5,974✔
2097
  vState valState = vState::Indeterminate;
5,974✔
2098

2099
  if (qnameParsed) {
5,974✔
2100
    cacheHit = g_packetCache->getResponsePacket(tag, data, qname, qtype, qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
133✔
2101
  }
133✔
2102
  else {
5,841✔
2103
    cacheHit = g_packetCache->getResponsePacket(tag, data, qname, &qtype, &qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
5,841✔
2104
  }
5,841✔
2105

2106
  if (cacheHit) {
5,974✔
2107
    if (vStateIsBogus(valState)) {
2,978!
2108
      if (t_bogusremotes) {
×
2109
        t_bogusremotes->push_back(source);
×
2110
      }
×
2111
      if (t_bogusqueryring) {
×
2112
        t_bogusqueryring->push_back({qname, qtype});
×
2113
      }
×
2114
    }
×
2115

2116
    // This is only to get the proxyMapping suffixMatch stats right i the case of a PC hit
2117
    if (t_proxyMapping && source != mappedSource) {
2,978!
2118
      if (const auto* found = t_proxyMapping->lookup(source)) {
×
2119
        if (found->second.suffixMatchNode) {
×
2120
          if (found->second.suffixMatchNode->check(qname)) {
×
2121
            ++found->second.stats.suffixMatches;
×
2122
          }
×
2123
        }
×
2124
      }
×
2125
    }
×
2126

2127
    if (response.length() >= sizeof(struct dnsheader)) {
2,978✔
2128
      dnsheader_aligned dh_aligned(response.data());
2,964✔
2129
      ageDNSPacket(response, age, dh_aligned);
2,964✔
2130
      const auto* dhp = dh_aligned.get();
2,964✔
2131
      updateResponseStats(dhp->rcode, source, response.length(), nullptr, 0);
2,964✔
2132
      t_Counters.at(rec::ResponseStats::responseStats).submitResponse(qtype, response.length(), dhp->rcode);
2,964✔
2133
    }
2,964✔
2134

2135
    // we assume 0 usec
2136
    t_Counters.at(rec::DoubleWAvgCounter::avgLatencyUsec).addToRollingAvg(0.0, g_latencyStatSize);
2,978✔
2137
    t_Counters.at(rec::DoubleWAvgCounter::avgLatencyOursUsec).addToRollingAvg(0.0, g_latencyStatSize);
2,978✔
2138
#if 0
2139
    // XXX changes behaviour compared to old code!
2140
    t_Counters.at(rec::Counter::answers)(0);
2141
    t_Counters.at(rec::Counter::ourtime)(0);
2142
#endif
2143
  }
2,978✔
2144

2145
  return cacheHit;
5,974✔
2146
}
6,110✔
2147

2148
static void* pleaseWipeCaches(const DNSName& canon, bool subtree, uint16_t qtype)
2149
{
2✔
2150
  auto res = wipeCaches(canon, subtree, qtype);
2✔
2151
  SLOG(g_log << Logger::Info << "Wiped caches for " << canon << ": " << res.record_count << " records; " << res.negative_record_count << " negative records; " << res.packet_count << " packets" << endl,
2✔
2152
       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✔
2153
  return nullptr;
2✔
2154
}
2✔
2155

2156
void requestWipeCaches(const DNSName& canon)
2157
{
2✔
2158
  // send a message to the handler thread asking it
2159
  // to wipe all of the caches
2160
  ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: pointer owner
2✔
2161
  tmsg->func = [=] { return pleaseWipeCaches(canon, true, 0xffff); };
2✔
2162
  tmsg->wantAnswer = false;
2✔
2163
  if (write(RecThreadInfo::info(0).getPipes().writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // NOLINT: correct sizeof
2!
2164
    delete tmsg; // NOLINT: pointer owner
×
2165

2166
    unixDie("write to thread pipe returned wrong size or error");
×
2167
  }
×
2168
  // coverity[leaked_storage]
2169
}
2✔
2170

2171
bool expectProxyProtocol(const ComboAddress& from, const ComboAddress& listenAddress)
2172
{
5,768✔
2173
  if (!t_proxyProtocolACL) {
5,768✔
2174
    return false;
5,695✔
2175
  }
5,695✔
2176
  if (t_proxyProtocolACL->match(from)) {
73✔
2177
    if (!t_proxyProtocolExceptions) {
65✔
2178
      return true;
60✔
2179
    }
60✔
2180
    return t_proxyProtocolExceptions->count(listenAddress) == 0;
5✔
2181
  }
65✔
2182
  return false;
8✔
2183
}
73✔
2184

2185
// fromaddr: the address the query is coming from
2186
// destaddr: the address the query was received on
2187
// source: the address we assume the query is coming from, might be set by proxy protocol
2188
// destination: the address we assume the query was sent to, might be set by proxy protocol
2189
// mappedSource: the address we assume the query is coming from. Differs from source if table based mapping has been applied
2190
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
2191
{
5,341✔
2192
  RecThreadInfo::self().incNumberOfDistributedQueries();
5,341✔
2193
  gettimeofday(&g_now, nullptr);
5,341✔
2194
  if (tval.tv_sec != 0) {
5,341✔
2195
    struct timeval diff = g_now - tval;
5,315✔
2196
    double delta = (static_cast<double>(diff.tv_sec) * 1000 + static_cast<double>(diff.tv_usec) / 1000.0);
5,315✔
2197

2198
    if (delta > 1000.0) {
5,315✔
2199
      t_Counters.at(rec::Counter::tooOldDrops)++;
62✔
2200
      return nullptr;
62✔
2201
    }
62✔
2202
  }
5,315✔
2203

2204
  ++t_Counters.at(rec::Counter::qcounter);
5,279✔
2205

2206
  if (fromaddr.sin4.sin_family == AF_INET6) {
5,279✔
2207
    t_Counters.at(rec::Counter::ipv6qcounter)++;
2✔
2208
  }
2✔
2209

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

2269
    // We do not have a SyncRes specific Lua context at this point yet, so ok to use t_pdl
2270
    if (needEDNSParse || (t_pdl && (t_pdl->hasGettagFunc() || t_pdl->hasGettagFFIFunc())) || dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
5,288✔
2271
      try {
112✔
2272
        EDNSOptionViewMap ednsOptions;
112✔
2273

2274
        ecsFound = false;
112✔
2275

2276
        getQNameAndSubnet(question, &qname, &qtype, &qclass,
112✔
2277
                          ecsFound, &ednssubnet,
112✔
2278
                          (g_gettagNeedsEDNSOptions || SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) ? &ednsOptions : nullptr,
112!
2279
                          ednsVersion);
112✔
2280

2281
        qnameParsed = true;
112✔
2282
        ecsParsed = true;
112✔
2283

2284
        if (SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
112!
2285
          pdns::trace::extractOTraceIDs(ednsOptions, otTrace);
×
2286
        }
×
2287
        if (t_pdl) {
112✔
2288
          try {
86✔
2289
            if (t_pdl->hasGettagFFIFunc()) {
86✔
2290
              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✔
2291

2292
              auto match = eventTrace.add(RecEventTrace::LuaGetTagFFI);
42✔
2293
              ctag = t_pdl->gettag_ffi(params);
42✔
2294
              eventTrace.add(RecEventTrace::LuaGetTagFFI, ctag, false, match);
42✔
2295
            }
42✔
2296
            else if (t_pdl->hasGettagFunc()) {
44✔
2297
              auto match = eventTrace.add(RecEventTrace::LuaGetTag);
33✔
2298
              ctag = t_pdl->gettag(source, ednssubnet.getSource(), destination, qname, qtype, &policyTags, data, ednsOptions, false, requestorId, deviceId, deviceName, routingTag, proxyProtocolValues);
33✔
2299
              eventTrace.add(RecEventTrace::LuaGetTag, ctag, false, match);
33✔
2300
            }
33✔
2301
          }
86✔
2302
          catch (const MOADNSException& moadnsexception) {
86✔
2303
            if (g_logCommonErrors) {
×
2304
              g_slogudpin->error(moadnsexception.what(), "Error parsing a query packet for tag determination", "qname", Logging::Loggable(qname), "excepion", Logging::Loggable("MOADNSException"));
×
2305
            }
×
2306
          }
×
2307
          catch (const std::exception& stdException) {
86✔
2308
            g_rateLimitedLogger.log(g_slogudpin, "Error parsing a query packet for tag determination", stdException, "qname", Logging::Loggable(qname), "remote", Logging::Loggable(fromaddr));
×
2309
          }
×
2310
        }
86✔
2311
      }
112✔
2312
      catch (const std::exception& stdException) {
112✔
2313
        g_rateLimitedLogger.log(g_slogudpin, "Error parsing a query packet for tag determination, setting tag=0", stdException);
×
2314
      }
×
2315
    }
112✔
2316

2317
    RecursorPacketCache::OptPBData pbData{boost::none};
5,279✔
2318
    if (t_protobufServers.servers) {
5,279✔
2319
      if (logQuery && !(luaconfsLocal->protobufExportConfig.taggedOnly && policyTags.empty())) {
33✔
2320
        protobufLogQuery(luaconfsLocal, uniqueId, source, destination, mappedSource, ednssubnet.getSource(), false, question.size(), qname, qtype, qclass, policyTags, requestorId, deviceId, deviceName, meta, ednsVersion, *dnsheader);
24✔
2321
      }
24✔
2322
    }
33✔
2323

2324
    if (ctag == 0 && !responsePaddingDisabled && g_paddingFrom.match(fromaddr)) {
5,287✔
2325
      ctag = g_paddingTag;
10✔
2326
    }
10✔
2327

2328
    if (dnsheader->opcode == static_cast<unsigned>(Opcode::Query)) {
5,285✔
2329
      /* It might seem like a good idea to skip the packet cache lookup if we know that the answer is not cacheable,
2330
         but it means that the hash would not be computed. If some script decides at a later time to mark back the answer
2331
         as cacheable we would cache it with a wrong tag, so better safe than sorry. */
2332
      auto match = eventTrace.add(RecEventTrace::PCacheCheck);
5,259✔
2333
      bool cacheHit = checkForCacheHit(qnameParsed, ctag, question, qname, qtype, qclass, g_now, response, qhash, pbData, false, source, mappedSource);
5,259✔
2334
      eventTrace.add(RecEventTrace::PCacheCheck, cacheHit, false, match);
5,259✔
2335
      if (cacheHit) {
5,259✔
2336
        if (!g_quiet) {
2,909✔
2337
          SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " question answered from packet cache tag=" << ctag << " from " << source.toStringWithPort() << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << endl,
125✔
2338
               g_slogudpin->info(Logr::Notice, "Question answered from packet cache", "tag", Logging::Loggable(ctag),
125✔
2339
                                 "qname", Logging::Loggable(qname), "qtype", Logging::Loggable(QType(qtype)),
125✔
2340
                                 "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
125✔
2341
        }
125✔
2342
        struct msghdr msgh{};
2,909✔
2343
        struct iovec iov{};
2,909✔
2344
        cmsgbuf_aligned cbuf{};
2,909✔
2345
        fillMSGHdr(&msgh, &iov, &cbuf, 0, reinterpret_cast<char*>(response.data()), response.length(), const_cast<ComboAddress*>(&fromaddr)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-type-const-cast)
2,909✔
2346
        msgh.msg_control = nullptr;
2,909✔
2347

2348
        if (g_fromtosockets.count(fileDesc) != 0) {
2,909!
2349
          addCMsgSrcAddr(&msgh, &cbuf, &destaddr, 0);
×
2350
        }
×
2351
        int sendErr = sendOnNBSocket(fileDesc, &msgh);
2,909✔
2352
        eventTrace.add(RecEventTrace::AnswerSent);
2,909✔
2353

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

2358
        if (eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_log)) {
2,909!
2359
          SLOG(g_log << Logger::Info << eventTrace.toString() << endl,
×
2360
               g_slogudpin->info(Logr::Info, eventTrace.toString())); // Do we want more fancy logging here?
×
2361
        }
×
2362
        if (sendErr != 0 && g_logCommonErrors) {
2,909!
2363
          SLOG(g_log << Logger::Warning << "Sending UDP reply to client " << source.toStringWithPort()
×
2364
                     << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << " failed with: "
×
2365
                     << stringerror(sendErr) << endl,
×
2366
               g_slogudpin->error(Logr::Error, sendErr, "Sending UDP reply to client failed", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
×
2367
        }
×
2368
        struct timeval now{};
2,909✔
2369
        Utility::gettimeofday(&now, nullptr);
2,909✔
2370
        uint64_t spentUsec = uSec(now - tval);
2,909✔
2371
        t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
2,909✔
2372
        t_Counters.updateSnap(g_regressionTestMode);
2,909✔
2373
        return nullptr;
2,909✔
2374
      }
2,909✔
2375
    }
5,259✔
2376
  }
5,279✔
2377
  catch (const std::exception& e) {
5,279✔
2378
    if (g_logCommonErrors) {
×
2379
      SLOG(g_log << Logger::Error << "Error processing or aging answer packet: " << e.what() << endl,
×
2380
           g_slogudpin->error(Logr::Error, e.what(), "Error processing or aging answer packet", "exception", Logging::Loggable("std::exception")));
×
2381
    }
×
2382
    return nullptr;
×
2383
  }
×
2384

2385
  if (t_pdl) {
2,372✔
2386
    bool ipf = t_pdl->ipfilter(source, destination, *dnsheader, eventTrace);
239✔
2387
    if (ipf) {
239✔
2388
      if (!g_quiet) {
2!
2389
        SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " [" << g_multiTasker->getTid() << "/" << g_multiTasker->numProcesses() << "] DROPPED question from " << source.toStringWithPort() << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << " based on policy" << endl,
2✔
2390
             g_slogudpin->info(Logr::Notice, "Dropped question based on policy", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
2✔
2391
      }
2✔
2392
      t_Counters.at(rec::Counter::policyDrops)++;
2✔
2393
      return nullptr;
2✔
2394
    }
2✔
2395
  }
239✔
2396

2397
  if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
2,370✔
2398
    if (!isAllowNotifyForZone(qname)) {
23!
2399
      if (!g_quiet) {
×
2400
        SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping UDP NOTIFY from " << source.toStringWithPort() << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << ", for " << qname.toLogString() << ", zone not matched by allow-notify-for" << endl,
×
2401
             g_slogudpin->info(Logr::Notice, "Dropping UDP NOTIFY, zone not matched by allow-notify-for", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
×
2402
      }
×
2403

2404
      t_Counters.at(rec::Counter::zoneDisallowedNotify)++;
×
2405
      return nullptr;
×
2406
    }
×
2407

2408
    if (!g_quiet) {
23!
2409
      SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " got NOTIFY for " << qname.toLogString() << " from " << source.toStringWithPort() << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << endl,
23✔
2410
           g_slogudpin->info(Logr::Notice, "Got NOTIFY", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr), "qname", Logging::Loggable(qname)));
23✔
2411
    }
23✔
2412
    if (!ZoneXFR::notifyZoneTracker(qname)) {
23✔
2413
      // It wasn't an RPZ
2414
      requestWipeCaches(qname);
1✔
2415
    }
1✔
2416

2417
    // the operation will now be treated as a Query, generating
2418
    // a normal response, as the rest of the code does not
2419
    // check dh->opcode, but we need to ensure that the response
2420
    // to this request does not get put into the packet cache
2421
    variable = true;
23✔
2422
  }
23✔
2423

2424
  if (g_multiTasker->numProcesses() >= g_maxMThreads) {
2,370!
2425
    if (!g_quiet) {
×
2426
      SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " [" << g_multiTasker->getTid() << "/" << g_multiTasker->numProcesses() << "] DROPPED question from " << source.toStringWithPort() << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << ", over capacity" << endl,
×
2427
           g_slogudpin->info(Logr::Notice, "Dropped question, over capacity", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
×
2428
    }
×
2429
    t_Counters.at(rec::Counter::overCapacityDrops)++;
×
2430
    return nullptr;
×
2431
  }
×
2432

2433
  auto comboWriter = std::make_unique<DNSComboWriter>(question, g_now, std::move(policyTags), t_pdl, std::move(data), std::move(records));
2,370✔
2434

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

2466
  comboWriter->d_eventTrace = std::move(eventTrace);
2,370✔
2467
  comboWriter->d_otTrace = std::move(otTrace);
2,370✔
2468

2469
  g_multiTasker->makeThread(startDoResolve, (void*)comboWriter.release()); // deletes dc
2,370✔
2470

2471
  return nullptr;
2,370✔
2472
}
2,370✔
2473

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

2490
  for (size_t queriesCounter = 0; queriesCounter < g_maxUDPQueriesPerRound; queriesCounter++) {
6,263!
2491
    bool proxyProto = false;
6,263✔
2492
    proxyProtocolValues.clear();
6,263✔
2493
    data.resize(maxIncomingQuerySize);
6,263✔
2494
    fromaddr.sin6.sin6_family = AF_INET6; // this makes sure fromaddr is big enough
6,263✔
2495
    fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), data.data(), data.size(), &fromaddr);
6,263✔
2496

2497
    if (ssize_t len = recvmsg(fileDesc, &msgh, 0); len >= 0) {
6,263✔
2498
      eventTrace.clear();
5,364✔
2499
      eventTrace.setEnabled(SyncRes::s_event_trace_enabled != 0);
5,364✔
2500
      // eventTrace uses monotonic time, while OpenTelemetry uses absolute time. setEnabled()
2501
      // established the reference point, get an absolute TS as close as possible to the
2502
      // eventTrace start of trace time.
2503
      auto traceTS = pdns::trace::timestamp();
5,364✔
2504
      eventTrace.add(RecEventTrace::ReqRecv);
5,364✔
2505
      if (SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
5,364!
2506
        otTrace.clear();
×
2507
        otTrace.start_time_unix_nano = traceTS;
×
2508
      }
×
2509
      firstQuery = false;
5,364✔
2510

2511
      if ((msgh.msg_flags & MSG_TRUNC) != 0) {
5,364!
2512
        t_Counters.at(rec::Counter::truncatedDrops)++;
×
2513
        if (!g_quiet) {
×
2514
          SLOG(g_log << Logger::Error << "Ignoring truncated query from " << fromaddr.toString() << endl,
×
2515
               g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr)));
×
2516
        }
×
2517
        return;
×
2518
      }
×
2519

2520
      data.resize(static_cast<size_t>(len));
5,364✔
2521

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

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

2575
      if (data.size() < sizeof(dnsheader)) {
5,358!
2576
        t_Counters.at(rec::Counter::ignoredCount)++;
×
2577
        if (!g_quiet) {
×
2578
          SLOG(g_log << Logger::Error << "Ignoring too-short (" << std::to_string(data.size()) << ") query from " << fromaddr.toString() << endl,
×
2579
               g_slogudpin->info(Logr::Error, "Ignoring too-short query", "length", Logging::Loggable(data.size()),
×
2580
                                 "remote", Logging::Loggable(fromaddr)));
×
2581
        }
×
2582
        return;
×
2583
      }
×
2584

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

2599
      if (t_allowFrom && !t_allowFrom->match(&mappedSource)) {
5,358!
2600
        if (!g_quiet) {
4!
2601
          SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping UDP query from " << mappedSource.toString() << ", address not matched by allow-from" << endl,
4✔
2602
               g_slogudpin->info(Logr::Error, "Dropping UDP query, address not matched by allow-from", "source", Logging::Loggable(mappedSource)));
4✔
2603
        }
4✔
2604

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

2609
      BOOST_STATIC_ASSERT(offsetof(sockaddr_in, sin_port) == offsetof(sockaddr_in6, sin6_port));
5,354✔
2610
      if (fromaddr.sin4.sin_port == 0) { // also works for IPv6
5,354!
2611
        if (!g_quiet) {
×
2612
          SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping UDP query from " << fromaddr.toStringWithPort() << ", can't deal with port 0" << endl,
×
2613
               g_slogudpin->info(Logr::Error, "Dropping UDP query can't deal with port 0", "remote", Logging::Loggable(fromaddr)));
×
2614
        }
×
2615

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

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

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

2654
              t_Counters.at(rec::Counter::sourceDisallowedNotify)++;
×
2655
              return;
×
2656
            }
×
2657
          }
23✔
2658

2659
          struct timeval tval = {0, 0};
5,351✔
2660
          HarvestTimestamp(&msgh, &tval);
5,351✔
2661
          if (!proxyProto) {
5,351✔
2662
            destination = destaddr;
5,337✔
2663
          }
5,337✔
2664

2665
          if (RecThreadInfo::weDistributeQueries()) {
5,351✔
2666
            std::string localdata = data;
3,612✔
2667
            distributeAsyncFunction(data, [localdata = std::move(localdata), fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace]() mutable {
3,612✔
2668
              return doProcessUDPQuestion(localdata, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace);
3,580✔
2669
            });
3,580✔
2670
          }
3,612✔
2671
          else {
1,739✔
2672
            doProcessUDPQuestion(data, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace);
1,739✔
2673
          }
1,739✔
2674
        }
5,351✔
2675
      }
5,354✔
2676
      catch (const MOADNSException& mde) {
5,354✔
2677
        t_Counters.at(rec::Counter::clientParseError)++;
×
2678
        if (g_logCommonErrors) {
×
2679
          SLOG(g_log << Logger::Error << "Unable to parse packet from remote UDP client " << fromaddr.toString() << ": " << mde.what() << endl,
×
2680
               g_slogudpin->error(Logr::Error, mde.what(), "Unable to parse packet from remote UDP client", "remote", Logging::Loggable(fromaddr), "exception", Logging::Loggable("MOADNSException")));
×
2681
        }
×
2682
      }
×
2683
      catch (const std::runtime_error& e) {
5,354✔
2684
        t_Counters.at(rec::Counter::clientParseError)++;
×
2685
        if (g_logCommonErrors) {
×
2686
          SLOG(g_log << Logger::Error << "Unable to parse packet from remote UDP client " << fromaddr.toString() << ": " << e.what() << endl,
×
2687
               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")));
×
2688
        }
×
2689
      }
×
2690
    }
5,354✔
2691
    else {
899✔
2692
      if (firstQuery && errno == EAGAIN) {
899!
2693
        t_Counters.at(rec::Counter::noPacketError)++;
×
2694
      }
×
2695

2696
      break;
899✔
2697
    }
899✔
2698
  }
6,263✔
2699
  t_Counters.updateSnap(g_regressionTestMode);
899✔
2700
}
899✔
2701

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

2710
  if (localAddresses.empty()) {
333!
2711
    throw PDNSException("No local address specified");
×
2712
  }
×
2713

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

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

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

2752
    setCloseOnExec(socketFd);
352✔
2753

2754
    try {
352✔
2755
      setSocketReceiveBuffer(socketFd, 250000);
352✔
2756
    }
352✔
2757
    catch (const std::exception& e) {
352✔
2758
      SLOG(g_log << Logger::Error << e.what() << endl,
×
2759
           log->error(Logr::Error, e.what(), "Exception while setting socket buffer size"));
×
2760
    }
×
2761

2762
    if (g_reusePort) {
352✔
2763
#if defined(SO_REUSEPORT_LB)
2764
      try {
2765
        SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
2766
      }
2767
      catch (const std::exception& e) {
2768
        throw PDNSException(std::string("SO_REUSEPORT_LB: ") + e.what());
2769
      }
2770
#elif defined(SO_REUSEPORT)
2771
      try {
322✔
2772
        SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT, 1);
322✔
2773
      }
322✔
2774
      catch (const std::exception& e) {
322✔
2775
        throw PDNSException(std::string("SO_REUSEPORT: ") + e.what());
×
2776
      }
×
2777
#endif
322✔
2778
    }
322✔
2779

2780
    try {
352✔
2781
      setSocketIgnorePMTU(socketFd, address.sin4.sin_family);
352✔
2782
    }
352✔
2783
    catch (const std::exception& e) {
352✔
2784
      SLOG(g_log << Logger::Warning << "Failed to set IP_MTU_DISCOVER on UDP server socket: " << e.what() << endl,
×
2785
           log->error(Logr::Warning, e.what(), "Failed to set IP_MTU_DISCOVER on UDP server socket"));
×
2786
    }
×
2787

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

2798
    setNonBlocking(socketFd);
352✔
2799

2800
    deferredAdds.emplace_back(socketFd, handleNewUDPQuestion);
352✔
2801
    g_listenSocketsAddresses[socketFd] = address; // this is written to only from the startup thread, not from the workers
352✔
2802
    logVec.emplace_back(address.toStringWithPort());
352✔
2803
    socketFd.release(); // to avoid auto-close by FDWrapper
352✔
2804
  }
352✔
2805
  if (doLog) {
333✔
2806
    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✔
2807
  }
175✔
2808
  return localAddresses.size();
333✔
2809
}
333✔
2810

2811
static bool trySendingQueryToWorker(unsigned int target, ThreadMSG* tmsg)
2812
{
3,612✔
2813
  auto& targetInfo = RecThreadInfo::info(target);
3,612✔
2814
  if (!targetInfo.isWorker()) {
3,612!
2815
    SLOG(g_log << Logger::Error << "distributeAsyncFunction() tried to assign a query to a non-worker thread" << endl,
×
2816
         g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() tried to assign a query to a non-worker thread"));
×
2817
    _exit(1);
×
2818
  }
×
2819

2820
  const auto& tps = targetInfo.getPipes();
3,612✔
2821

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

2838
  return true;
3,612✔
2839
}
3,612✔
2840

2841
static unsigned int getWorkerLoad(size_t workerIdx)
2842
{
×
2843
  const auto* multiThreader = RecThreadInfo::info(RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + workerIdx).getMT();
×
2844
  if (multiThreader != nullptr) {
×
2845
    return multiThreader->numProcesses();
×
2846
  }
×
2847
  return 0;
×
2848
}
×
2849

2850
static unsigned int selectWorker(unsigned int hash)
2851
{
3,612✔
2852
  assert(RecThreadInfo::numUDPWorkers() != 0); // NOLINT: assert implementation
3,612✔
2853
  if (g_balancingFactor == 0) {
3,612!
2854
    return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + (hash % RecThreadInfo::numUDPWorkers());
3,612✔
2855
  }
3,612✔
2856

2857
  /* we start with one, representing the query we are currently handling */
2858
  double currentLoad = 1;
×
2859
  std::vector<unsigned int> load(RecThreadInfo::numUDPWorkers());
×
2860
  for (size_t idx = 0; idx < RecThreadInfo::numUDPWorkers(); idx++) {
×
2861
    load[idx] = getWorkerLoad(idx);
×
2862
    currentLoad += load[idx];
×
2863
  }
×
2864

2865
  double targetLoad = (currentLoad / RecThreadInfo::numUDPWorkers()) * g_balancingFactor;
×
2866

2867
  unsigned int worker = hash % RecThreadInfo::numUDPWorkers();
×
2868
  /* at least one server has to be at or below the average load */
2869
  if (load[worker] > targetLoad) {
×
2870
    ++t_Counters.at(rec::Counter::rebalancedQueries);
×
2871
    do {
×
2872
      worker = (worker + 1) % RecThreadInfo::numUDPWorkers();
×
2873
    } while (load[worker] > targetLoad);
×
2874
  }
×
2875

2876
  return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + worker;
×
2877
}
3,612✔
2878

2879
// This function is only called by the distributor threads, when pdns-distributes-queries is set
2880
void distributeAsyncFunction(const string& packet, const pipefunc_t& func)
2881
{
3,612✔
2882
  if (!RecThreadInfo::self().isDistributor()) {
3,612!
2883
    SLOG(g_log << Logger::Error << "distributeAsyncFunction() has been called by a worker (" << RecThreadInfo::id() << ")" << endl,
×
2884
         g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() has been called by a worker")); // tid will be added
×
2885
    _exit(1);
×
2886
  }
×
2887

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

2897
  ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: pointer ownership
3,612✔
2898
  tmsg->func = func;
3,612✔
2899
  tmsg->wantAnswer = false;
3,612✔
2900

2901
  if (!trySendingQueryToWorker(target, tmsg)) {
3,612!
2902
    /* if this function failed but did not raise an exception, it means that the pipe
2903
       was full, let's try another one */
2904
    unsigned int newTarget = 0;
×
2905
    do {
×
2906
      newTarget = RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + dns_random(RecThreadInfo::numUDPWorkers());
×
2907
    } while (newTarget == target);
×
2908

2909
    if (!trySendingQueryToWorker(newTarget, tmsg)) {
×
2910
      t_Counters.at(rec::Counter::queryPipeFullDrops)++;
×
2911
      delete tmsg; // NOLINT: pointer ownership
×
2912
    }
×
2913
  }
×
2914
  // coverity[leaked_storage]
2915
}
3,612✔
2916

2917
// resend event to everybody chained onto it
2918
static void doResends(MT_t::waiters_t::iterator& iter, const std::shared_ptr<PacketID>& resend, const PacketBuffer& content)
2919
{
9,076✔
2920
  // We close the chain for new entries, since they won't be processed anyway
2921
  iter->key->closed = true;
9,076✔
2922

2923
  if (iter->key->authReqChain.empty()) {
9,076✔
2924
    return;
8,694✔
2925
  }
8,694✔
2926

2927
  auto maxWeight = t_Counters.at(rec::Counter::maxChainWeight);
382✔
2928
  auto weight = iter->key->authReqChain.size() * content.size();
382✔
2929
  if (weight > maxWeight) {
382✔
2930
    t_Counters.at(rec::Counter::maxChainWeight) = weight;
82✔
2931
  }
82✔
2932

2933
  for (auto [fileDesc, qid] : iter->key->authReqChain) {
1,591✔
2934
    auto packetID = std::make_shared<PacketID>(*resend);
1,591✔
2935
    packetID->fd = fileDesc;
1,591✔
2936
    packetID->id = qid;
1,591✔
2937
    g_multiTasker->sendEvent(packetID, &content);
1,591✔
2938
    t_Counters.at(rec::Counter::chainResends)++;
1,591✔
2939
  }
1,591✔
2940
}
382✔
2941

2942
void mthreadSleep(unsigned int jitterMsec)
2943
{
×
2944
  auto neverHappens = std::make_shared<PacketID>();
×
2945
  neverHappens->id = dns_random_uint16();
×
2946
  neverHappens->type = dns_random_uint16();
×
2947
  neverHappens->remote = ComboAddress("100::"); // discard-only
×
2948
  neverHappens->remote.setPort(dns_random_uint16());
×
2949
  neverHappens->fd = -1;
×
2950
  assert(g_multiTasker->waitEvent(neverHappens, nullptr, jitterMsec) != -1); // NOLINT
×
2951
}
×
2952

2953
static bool checkIncomingECSSource(const PacketBuffer& packet, const Netmask& subnet)
2954
{
283✔
2955
  bool foundMatchingECS = false;
283✔
2956

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

2979
static void handleUDPServerResponse(int fileDesc, FDMultiplexer::funcparam_t& var)
2980
{
9,076✔
2981
  auto pid = boost::any_cast<std::shared_ptr<PacketID>>(var);
9,076✔
2982
  PacketBuffer packet;
9,076✔
2983
  packet.resize(g_outgoingEDNSBufsize);
9,076✔
2984
  ComboAddress fromaddr;
9,076✔
2985
  socklen_t addrlen = sizeof(fromaddr);
9,076✔
2986

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

2989
  const ssize_t signed_sizeof_sdnsheader = sizeof(dnsheader);
9,076✔
2990

2991
  if (len < 0) {
9,076✔
2992
    // len < 0: error on socket
2993
    t_udpclientsocks->returnSocket(fileDesc);
61✔
2994

2995
    PacketBuffer empty;
61✔
2996
    auto iter = g_multiTasker->getWaiters().find(pid);
61✔
2997
    if (iter != g_multiTasker->getWaiters().end()) {
61!
2998
      doResends(iter, pid, empty);
61✔
2999
    }
61✔
3000
    g_multiTasker->sendEvent(pid, &empty); // this denotes error (does retry lookup using other NS)
61✔
3001
    return;
61✔
3002
  }
61✔
3003

3004
  if (len < signed_sizeof_sdnsheader) {
9,015!
3005
    // We have received a packet that cannot be a valid DNS packet, as it has no complete header
3006
    // Drop it, but continue to wait for other packets
3007
    t_Counters.at(rec::Counter::serverParseError)++;
×
3008
    if (g_logCommonErrors) {
×
3009
      SLOG(g_log << Logger::Error << "Unable to parse too short packet from remote UDP server " << fromaddr.toString() << ": packet smaller than DNS header" << endl,
×
3010
           g_slogout->info(Logr::Error, "Unable to parse too short packet from remote UDP server", "from", Logging::Loggable(fromaddr)));
×
3011
    }
×
3012
    return;
×
3013
  }
×
3014

3015
  // We have at least a full header
3016
  packet.resize(len);
9,015✔
3017
  dnsheader dnsheader{};
9,015✔
3018
  memcpy(&dnsheader, &packet.at(0), sizeof(dnsheader));
9,015✔
3019

3020
  auto pident = std::make_shared<PacketID>();
9,015✔
3021
  pident->remote = fromaddr;
9,015✔
3022
  pident->id = dnsheader.id;
9,015✔
3023
  pident->fd = fileDesc;
9,015✔
3024

3025
  if (!dnsheader.qr && g_logCommonErrors) {
9,015!
3026
    SLOG(g_log << Logger::Notice << "Not taking data from question on outgoing socket from " << fromaddr.toStringWithPort() << endl,
×
3027
         g_slogout->info(Logr::Error, "Not taking data from question on outgoing socket", "from", Logging::Loggable(fromaddr)));
×
3028
  }
×
3029

3030
  if (dnsheader.qdcount == 0U || // UPC, Nominum, very old BIND on FormErr, NSD
9,015✔
3031
      dnsheader.qr == 0U) { // one weird server
9,015!
3032
    pident->domain.clear();
×
3033
    pident->type = 0;
×
3034
  }
×
3035
  else {
9,015✔
3036
    try {
9,015✔
3037
      if (len > signed_sizeof_sdnsheader) {
9,015✔
3038
        pident->domain = DNSName(reinterpret_cast<const char*>(packet.data()), static_cast<int>(len), static_cast<int>(sizeof(dnsheader)), false, &pident->type); // don't copy this from above - we need to do the actual read  // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
9,014✔
3039
      }
9,014✔
3040
      else {
1✔
3041
        // len == sizeof(dnsheader), only header case
3042
        // We will do a full scan search later to see if we can match this reply even without a domain
3043
        pident->domain.clear();
1✔
3044
        pident->type = 0;
1✔
3045
      }
1✔
3046
    }
9,015✔
3047
    catch (std::exception& e) {
9,015✔
3048
      // Parse error, continue waiting for other packets
3049
      t_Counters.at(rec::Counter::serverParseError)++; // won't be fed to lwres.cc, so we have to increment
×
3050
      SLOG(g_log << Logger::Warning << "Error in packet from remote nameserver " << fromaddr.toStringWithPort() << ": " << e.what() << endl,
×
3051
           g_slogudpin->error(Logr::Warning, e.what(), "Error in packet from remote nameserver", "from", Logging::Loggable(fromaddr)));
×
3052
      return;
×
3053
    }
×
3054
  }
9,015✔
3055

3056
  if (!pident->domain.empty()) {
9,014✔
3057
    auto iter = g_multiTasker->getWaiters().find(pident);
9,013✔
3058
    if (iter != g_multiTasker->getWaiters().end()) {
9,015✔
3059
      doResends(iter, pident, packet);
9,015✔
3060
    }
9,015✔
3061
  }
9,013✔
3062

3063
retryWithName:
9,015✔
3064

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

3068
    // 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
3069
    for (const auto& d_waiter : g_multiTasker->getWaiters()) {
×
3070
      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) {
×
3071
        /* we are expecting an answer from that exact source, on that exact port (since we are using connected sockets), for that qname/qtype,
3072
           but with a different message ID. That smells like a spoofing attempt. For now we will just increase the counter and will deal with
3073
           that later. */
3074
        d_waiter.key->nearMisses++;
×
3075
      }
×
3076

3077
      // be a bit paranoid here since we're weakening our matching
3078
      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) {
×
3079
        pident->domain = d_waiter.key->domain;
×
3080
        pident->type = d_waiter.key->type;
×
3081
        goto retryWithName; // note that this only passes on an error, lwres will still reject the packet NOLINT(cppcoreguidelines-avoid-goto)
×
3082
      }
×
3083
    }
×
3084
    t_Counters.at(rec::Counter::unexpectedCount)++; // if we made it here, it really is an unexpected answer
×
3085
    if (g_logCommonErrors) {
×
3086
      SLOG(g_log << Logger::Warning << "Discarding unexpected packet from " << fromaddr.toStringWithPort() << ": " << (pident->domain.empty() ? "<empty>" : pident->domain.toString()) << ", " << pident->type << ", " << g_multiTasker->getWaiters().size() << " waiters" << endl,
×
3087
           g_slogudpin->info(Logr::Warning, "Discarding unexpected packet", "from", Logging::Loggable(fromaddr),
×
3088
                             "qname", Logging::Loggable(pident->domain),
×
3089
                             "qtype", Logging::Loggable(QType(pident->type)),
×
3090
                             "waiters", Logging::Loggable(g_multiTasker->getWaiters().size())));
×
3091
    }
×
3092
  }
×
3093
  else if (fileDesc >= 0) {
9,015✔
3094
    /* we either found a waiter (1) or encountered an issue (-1), it's up to us to clean the socket anyway */
3095
    t_udpclientsocks->returnSocket(fileDesc);
9,014✔
3096
  }
9,014✔
3097
}
9,015✔
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