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

PowerDNS / pdns / 15920880335

26 Jun 2025 03:30PM UTC coverage: 61.923% (-3.7%) from 65.652%
15920880335

push

github

web-flow
Merge pull request #15669 from miodvallat/serial_keyer

Increase zone serial number after zone key operations

38311 of 91850 branches covered (41.71%)

Branch coverage included in aggregate %.

27 of 29 new or added lines in 1 file covered. (93.1%)

6308 existing lines in 78 files now uncovered.

120482 of 164587 relevant lines covered (73.2%)

5965233.22 hits per line

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

29.63
/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

34
#include "ratelimitedlog.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
{
7,726✔
104
  *fileDesc = makeClientSocket(toaddr.sin4.sin_family);
7,726✔
105
  if (*fileDesc < 0) { // temporary error - receive exception otherwise
7,726!
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))
7,726!
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++;
7,726✔
127
  return LWResult::Result::Success;
7,726✔
128
}
7,726✔
129

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

140
  try {
7,726✔
141
    closesocket(fileDesc);
7,722✔
142
  }
7,722✔
143
  catch (const PDNSException& e) {
7,722✔
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;
7,724✔
149
}
7,723✔
150

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

156
  if (ret < 0 && errno == EMFILE) { // this is not a catastrophic error
7,724!
157
    return ret;
×
158
  }
×
159
  if (ret < 0) {
7,724!
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__)
7,724✔
167
  int tries = 10;
7,724✔
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;
7,724✔
172
  while (--tries != 0) {
7,735✔
173
    in_port_t port = 0;
7,729✔
174

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

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

190
  int err = errno;
7,724✔
191

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

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

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

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

UNCOV
222
  t_fdm->removeReadFD(fileDesc);
×
UNCOV
223
  if (ret >= 0) {
×
UNCOV
224
    resp.resize(ret);
×
UNCOV
225
    g_multiTasker->sendEvent(pident, &resp);
×
UNCOV
226
  }
×
227
  else {
×
228
    PacketBuffer empty;
×
229
    g_multiTasker->sendEvent(pident, &empty);
×
230
    //    cerr<<"Had some kind of error: "<<ret<<", "<<stringerror()<<endl;
231
  }
×
UNCOV
232
}
×
233

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

UNCOV
240
  socket.bind(local);
×
UNCOV
241
  socket.connect(dest);
×
UNCOV
242
  socket.send(query);
×
243

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

UNCOV
250
  PacketBuffer data;
×
UNCOV
251
  int ret = g_multiTasker->waitEvent(pident, &data, authWaitTimeMSec(g_multiTasker));
×
252

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

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

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

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

281
/* these two functions are used by LWRes */
282
LWResult::Result asendto(const void* data, size_t len, int /* flags */,
283
                         const ComboAddress& toAddress, uint16_t qid, const DNSName& domain, uint16_t qtype, bool ecs, int* fileDesc, timeval& now)
284
{
8,218✔
285

286
  auto pident = std::make_shared<PacketID>();
8,218✔
287
  pident->domain = domain;
8,218✔
288
  pident->remote = toAddress;
8,218✔
289
  pident->type = qtype;
8,218✔
290

291
  // We cannot merge ECS-enabled queries based on the ECS source only, as the scope
292
  // of the response might be narrower, so instead we do not chain ECS-enabled queries
293
  // at all.
294
  if (!ecs) {
8,222✔
295
    // See if there is an existing outstanding request we can chain on to, using partial equivalence
296
    // function looking for the same query (qname and qtype) to the same host, but with a different
297
    // message ID.
298
    auto chain = g_multiTasker->getWaiters().equal_range(pident, PacketIDBirthdayCompare());
8,222✔
299

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

325
  auto ret = t_udpclientsocks->getSocket(toAddress, fileDesc);
7,721✔
326
  if (ret != LWResult::Result::Success) {
7,721!
327
    return ret;
×
328
  }
×
329

330
  pident->fd = *fileDesc;
7,721✔
331
  pident->id = qid;
7,721✔
332

333
  t_fdm->addReadFD(*fileDesc, handleUDPServerResponse, pident);
7,721✔
334
  ssize_t sent = send(*fileDesc, data, len, 0);
7,721✔
335

336
  int tmp = errno;
7,721✔
337

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

344
  return LWResult::Result::Success;
7,721✔
345
}
7,721✔
346

347
LWResult::Result arecvfrom(PacketBuffer& packet, int /* flags */, const ComboAddress& fromAddr, size_t& len,
348
                           uint16_t qid, const DNSName& domain, uint16_t qtype, int fileDesc, const struct timeval& now)
349
{
8,221✔
350
  static const unsigned int nearMissLimit = ::arg().asNum("spoof-nearmiss-max");
8,221✔
351

352
  auto pident = std::make_shared<PacketID>();
8,221✔
353
  pident->fd = fileDesc;
8,221✔
354
  pident->id = qid;
8,221✔
355
  pident->domain = domain;
8,221✔
356
  pident->type = qtype;
8,221✔
357
  pident->remote = fromAddr;
8,221✔
358
  pident->creationTime = now;
8,221✔
359

360
  int ret = g_multiTasker->waitEvent(pident, &packet, authWaitTimeMSec(g_multiTasker), &now);
8,221✔
361
  len = 0;
8,221✔
362

363
  /* -1 means error, 0 means timeout, 1 means a result from handleUDPServerResponse() which might still be an error */
364
  if (ret > 0) {
8,223✔
365
    /* handleUDPServerResponse() will close the socket for us no matter what */
366
    if (packet.empty()) { // means "error"
8,221!
UNCOV
367
      return LWResult::Result::PermanentError;
×
UNCOV
368
    }
×
369

370
    len = packet.size();
8,221✔
371

372
    if (nearMissLimit > 0 && pident->nearMisses > nearMissLimit) {
8,221!
373
      /* we have received more than nearMissLimit answers on the right IP and port, from the right source (we are using connected sockets),
374
         for the correct qname and qtype, but with an unexpected message ID. That looks like a spoofing attempt. */
375
      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,
×
376
           g_slogudpin->info(Logr::Error, "Too many answers with a wrong message ID, assuming spoofing attempt",
×
377
                             "nearmisses", Logging::Loggable(pident->nearMisses),
×
378
                             "nearmisslimit", Logging::Loggable(nearMissLimit),
×
379
                             "qname", Logging::Loggable(domain),
×
380
                             "from", Logging::Loggable(fromAddr)));
×
381
      t_Counters.at(rec::Counter::spoofCount)++;
×
382
      return LWResult::Result::Spoofed;
×
383
    }
×
384

385
    return LWResult::Result::Success;
8,221✔
386
  }
8,221✔
387
  /* getting there means error or timeout, it's up to us to close the socket */
388
  if (fileDesc >= 0) {
2,147,483,647✔
389
    t_udpclientsocks->returnSocket(fileDesc);
2✔
390
  }
2✔
391

392
  return ret == 0 ? LWResult::Result::Timeout : LWResult::Result::PermanentError;
2,147,483,647✔
393
}
8,221✔
394

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

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

446
static bool addRecordToPacket(DNSPacketWriter& packetWritewr, const DNSRecord& rec, uint32_t& minTTL, uint32_t ttlCap, const uint16_t maxAnswerSize, bool& seenAuthSOA)
447
{
6,016✔
448
  packetWritewr.startRecord(rec.d_name, rec.d_type, (rec.d_ttl > ttlCap ? ttlCap : rec.d_ttl), rec.d_class, rec.d_place);
6,016!
449

450
  if (rec.d_type == QType::SOA && rec.d_place == DNSResourceRecord::AUTHORITY) {
6,016!
451
    seenAuthSOA = true;
300✔
452
  }
300✔
453

454
  if (rec.d_type != QType::OPT) { // their TTL ain't real
6,016✔
455
    minTTL = min(minTTL, rec.d_ttl);
6,015✔
456
  }
6,015✔
457

458
  rec.getContent()->toPacket(packetWritewr);
6,016✔
459
  if (packetWritewr.size() > static_cast<size_t>(maxAnswerSize)) {
6,016!
UNCOV
460
    packetWritewr.rollback();
×
UNCOV
461
    if (rec.d_place != DNSResourceRecord::ADDITIONAL) {
×
UNCOV
462
      packetWritewr.getHeader()->tc = 1;
×
UNCOV
463
      packetWritewr.truncate();
×
UNCOV
464
    }
×
UNCOV
465
    return false;
×
UNCOV
466
  }
×
467

468
  return true;
6,016✔
469
}
6,016✔
470

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

512
private:
513
  std::unique_ptr<DNSComboWriter>& d_dc; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
514
  bool d_handled{false};
515
};
516

517
enum class PolicyResult : uint8_t
518
{
519
  NoAction,
520
  HaveAnswer,
521
  Drop
522
};
523

524
static PolicyResult handlePolicyHit(const DNSFilterEngine::Policy& appliedPolicy, const std::unique_ptr<DNSComboWriter>& comboWriter, SyncRes& resolver, int& res, vector<DNSRecord>& ret, DNSPacketWriter& packetWriter, RunningResolveGuard& tcpGuard)
UNCOV
525
{
×
526
  /* don't account truncate actions for TCP queries, since they are not applied */
UNCOV
527
  if (appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::Truncate || !comboWriter->d_tcp) {
×
UNCOV
528
    ++t_Counters.at(rec::PolicyHistogram::policy).at(appliedPolicy.d_kind);
×
UNCOV
529
    ++t_Counters.at(rec::PolicyNameHits::policyName).counts[appliedPolicy.getName()];
×
UNCOV
530
  }
×
531

UNCOV
532
  if (resolver.doLog() && appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
×
UNCOV
533
    SLOG(g_log << Logger::Warning << comboWriter->d_mdp.d_qname << "|" << QType(comboWriter->d_mdp.d_qtype) << appliedPolicy.getLogString() << endl,
×
UNCOV
534
         appliedPolicy.info(Logr::Warning, resolver.d_slog));
×
UNCOV
535
  }
×
536

UNCOV
537
  if (appliedPolicy.d_zoneData && appliedPolicy.d_zoneData->d_extendedErrorCode) {
×
538
    comboWriter->d_extendedErrorCode = *appliedPolicy.d_zoneData->d_extendedErrorCode;
×
539
    comboWriter->d_extendedErrorExtra = appliedPolicy.d_zoneData->d_extendedErrorExtra;
×
540
  }
×
541

UNCOV
542
  switch (appliedPolicy.d_kind) {
×
543

UNCOV
544
  case DNSFilterEngine::PolicyKind::NoAction:
×
UNCOV
545
    return PolicyResult::NoAction;
×
546

UNCOV
547
  case DNSFilterEngine::PolicyKind::Drop:
×
UNCOV
548
    tcpGuard.setDropOnIdle();
×
UNCOV
549
    ++t_Counters.at(rec::Counter::policyDrops);
×
UNCOV
550
    return PolicyResult::Drop;
×
551

UNCOV
552
  case DNSFilterEngine::PolicyKind::NXDOMAIN:
×
UNCOV
553
    ret.clear();
×
UNCOV
554
    appliedPolicy.addSOAtoRPZResult(ret);
×
UNCOV
555
    res = RCode::NXDomain;
×
UNCOV
556
    return PolicyResult::HaveAnswer;
×
557

UNCOV
558
  case DNSFilterEngine::PolicyKind::NODATA:
×
UNCOV
559
    ret.clear();
×
UNCOV
560
    appliedPolicy.addSOAtoRPZResult(ret);
×
UNCOV
561
    res = RCode::NoError;
×
UNCOV
562
    return PolicyResult::HaveAnswer;
×
563

UNCOV
564
  case DNSFilterEngine::PolicyKind::Truncate:
×
UNCOV
565
    if (!comboWriter->d_tcp) {
×
UNCOV
566
      ret.clear();
×
UNCOV
567
      appliedPolicy.addSOAtoRPZResult(ret);
×
UNCOV
568
      res = RCode::NoError;
×
UNCOV
569
      packetWriter.getHeader()->tc = 1;
×
UNCOV
570
      return PolicyResult::HaveAnswer;
×
UNCOV
571
    }
×
572
    return PolicyResult::NoAction;
×
573

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

UNCOV
612
      appliedPolicy.addSOAtoRPZResult(ret);
×
UNCOV
613
      return PolicyResult::HaveAnswer;
×
UNCOV
614
    }
×
UNCOV
615
  }
×
616

617
  return PolicyResult::NoAction;
×
UNCOV
618
}
×
619

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

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

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

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

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

UNCOV
707
  if (target.empty()) {
×
708
    return rcode;
×
709
  }
×
710

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

UNCOV
714
  if (g_dns64Prefix && qtype == QType::AAAA && dns64Candidate(qtype, rcode, resolved)) {
×
UNCOV
715
    rcode = getFakeAAAARecords(target, *g_dns64Prefix, resolved);
×
UNCOV
716
  }
×
717

UNCOV
718
  for (DNSRecord& record : resolved) {
×
UNCOV
719
    if (record.d_place == DNSResourceRecord::ANSWER) {
×
UNCOV
720
      ret.push_back(std::move(record));
×
UNCOV
721
    }
×
UNCOV
722
  }
×
UNCOV
723
  return rcode;
×
UNCOV
724
}
×
725

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

UNCOV
735
  ret.reserve(ret.size() + newRecords.size());
×
UNCOV
736
  for (auto& record : newRecords) {
×
UNCOV
737
    ret.push_back(std::move(record));
×
UNCOV
738
  }
×
739

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

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

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

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

UNCOV
799
  if (parts.size() < 8) {
×
800
    return -1;
×
801
  }
×
802

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

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

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

UNCOV
822
  ret.insert(ret.end(), answers.begin(), answers.end());
×
823

UNCOV
824
  t_Counters.at(rec::Counter::dns64prefixanswers)++;
×
UNCOV
825
  return rcode;
×
UNCOV
826
}
×
827

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

838
bool isAllowNotifyForZone(DNSName qname)
UNCOV
839
{
×
UNCOV
840
  if (t_allowNotifyFor->empty()) {
×
841
    return false;
×
842
  }
×
843

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

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

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

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

934
static uint32_t capPacketCacheTTL(const struct dnsheader& hdr, uint32_t ttl, bool seenAuthSOA)
935
{
1,709✔
936
  if (hdr.rcode == RCode::NXDomain || (hdr.rcode == RCode::NoError && hdr.ancount == 0 && seenAuthSOA)) {
1,709!
937
    ttl = std::min(ttl, SyncRes::s_packetcachenegativettl);
300✔
938
  }
300✔
939
  else if ((hdr.rcode != RCode::NoError && hdr.rcode != RCode::NXDomain) || (hdr.ancount == 0 && hdr.nscount == 0)) {
1,409!
UNCOV
940
    ttl = min(ttl, SyncRes::s_packetcacheservfailttl);
×
UNCOV
941
  }
×
942
  else {
1,409✔
943
    ttl = std::min(ttl, SyncRes::s_packetcachettl);
1,409✔
944
  }
1,409✔
945
  return ttl;
1,709✔
946
}
1,709✔
947

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

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

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

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

UNCOV
1002
      if (!comboWriter->d_responsePaddingDisabled && g_paddingFrom.match(comboWriter->d_remote)) {
×
UNCOV
1003
        paddingAllowed = true;
×
UNCOV
1004
        if (g_paddingMode == PaddingMode::Always) {
×
UNCOV
1005
          addPaddingToResponse = true;
×
UNCOV
1006
        }
×
UNCOV
1007
      }
×
1008

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

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

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

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

1049
      // RRSets added below
UNCOV
1050
    }
×
1051
    checkOutgoingProtobufExport(luaconfsLocal); // to pick up changed configs
1,708✔
1052
#ifdef HAVE_FSTRM
1,708✔
1053
    checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
1,708✔
1054
    checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
1,708✔
1055
#endif
1,708✔
1056

1057
    DNSPacketWriter packetWriter(packet, comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype, comboWriter->d_mdp.d_qclass, comboWriter->d_mdp.d_header.opcode);
1,708✔
1058

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

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

1074
    resolver.d_eventTrace = std::move(comboWriter->d_eventTrace);
1,708✔
1075
    resolver.d_otTrace = std::move(comboWriter->d_otTrace);
1,708✔
1076
    resolver.setId(g_multiTasker->getTid());
1,708✔
1077

1078
    bool DNSSECOK = false;
1,708✔
1079
    if (comboWriter->d_luaContext) {
1,708!
UNCOV
1080
      resolver.setLuaEngine(comboWriter->d_luaContext);
×
UNCOV
1081
    }
×
1082
    if (g_dnssecmode != DNSSECMode::Off) {
1,709✔
1083
      resolver.setDoDNSSEC(true);
1,704✔
1084

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

1112
    resolver.setInitialRequestId(comboWriter->d_uuid);
1,708✔
1113
    resolver.setOutgoingProtobufServers(t_outgoingProtobufServers.servers);
1,708✔
1114
#ifdef HAVE_FSTRM
1,708✔
1115
    resolver.setFrameStreamServers(t_frameStreamServersInfo.servers);
1,708✔
1116
#endif
1,708✔
1117

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

1138
    resolver.setQueryReceivedOverTCP(comboWriter->d_tcp);
1,708✔
1139

1140
    bool tracedQuery = false; // we could consider letting Lua know about this too
1,708✔
1141
    bool shouldNotValidate = false;
1,708✔
1142

1143
    /* preresolve expects res (dq.rcode) to be set to RCode::NoError by default */
1144
    int res = RCode::NoError;
1,708✔
1145

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

1166
    resolver.d_slog = resolver.d_slog->withValues("qname", Logging::Loggable(comboWriter->d_mdp.d_qname),
1,708✔
1167
                                                  "qtype", Logging::Loggable(QType(comboWriter->d_mdp.d_qtype)),
1,708✔
1168
                                                  "remote", Logging::Loggable(comboWriter->getRemote()),
1,708✔
1169
                                                  "proto", Logging::Loggable(comboWriter->d_tcp ? "tcp" : "udp"),
1,708!
1170
                                                  "ecs", Logging::Loggable(comboWriter->d_ednssubnet.getSource().empty() ? "" : comboWriter->d_ednssubnet.getSource().toString()),
2,147,483,647✔
1171
                                                  "mtid", Logging::Loggable(g_multiTasker->getTid()));
1,708✔
1172
    RunningResolveGuard tcpGuard(comboWriter);
1,708✔
1173

1174
    if (ednsExtRCode != 0 || comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Notify)) {
1,709!
UNCOV
1175
      goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
×
UNCOV
1176
    }
×
1177

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

1185
    if (t_traceRegex && t_traceRegex->match(comboWriter->d_mdp.d_qname.toString())) {
1,708!
1186
      resolver.setLogMode(SyncRes::Store);
×
1187
      tracedQuery = true;
×
1188
    }
×
1189

1190
    if (!g_quiet || tracedQuery) {
1,709✔
UNCOV
1191
      if (!g_slogStructured) {
×
1192
        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 << "|"
×
1193
              << QType(comboWriter->d_mdp.d_qtype) << "' from " << comboWriter->getRemote();
×
1194
        if (!comboWriter->d_ednssubnet.getSource().empty()) {
×
1195
          g_log << " (ecs " << comboWriter->d_ednssubnet.getSource().toString() << ")";
×
1196
        }
×
1197
        g_log << endl;
×
1198
      }
×
UNCOV
1199
      else {
×
UNCOV
1200
        resolver.d_slog->info(Logr::Info, "Question");
×
UNCOV
1201
      }
×
UNCOV
1202
    }
×
1203

1204
    if (!comboWriter->d_mdp.d_header.rd) {
1,708!
UNCOV
1205
      if (g_allowNoRD) {
×
UNCOV
1206
        resolver.setCacheOnly();
×
UNCOV
1207
      }
×
UNCOV
1208
      else {
×
UNCOV
1209
        ret.clear();
×
UNCOV
1210
        res = RCode::Refused;
×
UNCOV
1211
        goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
×
UNCOV
1212
      }
×
UNCOV
1213
    }
×
1214

1215
    if (comboWriter->d_luaContext) {
1,708!
UNCOV
1216
      comboWriter->d_luaContext->prerpz(dnsQuestion, res, resolver.d_eventTrace);
×
UNCOV
1217
    }
×
1218

1219
    // Check if the client has a policy attached to it
1220
    if (wantsRPZ && !appliedPolicy.wasHit()) {
1,709✔
1221

1222
      if (luaconfsLocal->dfe.getClientPolicy(comboWriter->d_source, resolver.d_discardedPolicies, appliedPolicy)) {
1,707!
1223
        mergePolicyTags(comboWriter->d_policyTags, appliedPolicy.getTags());
×
1224
      }
×
1225
    }
1,707✔
1226

1227
    /* If we already have an answer generated from gettag_ffi, let's see if the filtering policies
1228
       should be applied to it */
1229
    if (comboWriter->d_rcode != boost::none) {
1,708!
1230

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

UNCOV
1246
        if (appliedPolicy.wasHit()) {
×
UNCOV
1247
          policyOverride = true;
×
UNCOV
1248
        }
×
UNCOV
1249
      }
×
1250

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

1262
    // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve
1263
    if (!comboWriter->d_luaContext || !comboWriter->d_luaContext->preresolve(dnsQuestion, res, resolver.d_eventTrace)) {
1,709!
1264

1265
      if (!g_dns64PrefixReverse.empty() && dnsQuestion.qtype == QType::PTR && dnsQuestion.qname.isPartOf(g_dns64PrefixReverse)) {
1,708!
UNCOV
1266
        res = getFakePTRRecords(dnsQuestion.qname, ret);
×
UNCOV
1267
        goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
×
UNCOV
1268
      }
×
1269

1270
      resolver.setWantsRPZ(wantsRPZ);
1,708✔
1271

1272
      if (wantsRPZ && appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
1,708!
1273

UNCOV
1274
        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)) {
×
1275
          /* reset to no match */
1276
          appliedPolicy = DNSFilterEngine::Policy();
×
1277
        }
×
UNCOV
1278
        else {
×
UNCOV
1279
          auto policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
×
UNCOV
1280
          if (policyResult == PolicyResult::HaveAnswer) {
×
UNCOV
1281
            if (g_dns64Prefix && dnsQuestion.qtype == QType::AAAA && dns64Candidate(comboWriter->d_mdp.d_qtype, res, ret)) {
×
UNCOV
1282
              res = getFakeAAAARecords(dnsQuestion.qname, *g_dns64Prefix, ret);
×
UNCOV
1283
              shouldNotValidate = true;
×
UNCOV
1284
            }
×
UNCOV
1285
            goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
×
UNCOV
1286
          }
×
1287
          else if (policyResult == PolicyResult::Drop) {
×
1288
            return;
×
1289
          }
×
UNCOV
1290
        }
×
UNCOV
1291
      }
×
1292

1293
      // Query did not get handled for Client IP or QNAME Policy reasons, now actually go out to find an answer
1294
      try {
1,708✔
1295
        resolver.d_appliedPolicy = appliedPolicy;
1,708✔
1296
        resolver.d_policyTags = std::move(comboWriter->d_policyTags);
1,708✔
1297

1298
        if (!comboWriter->d_routingTag.empty()) {
1,708!
UNCOV
1299
          resolver.d_routingTag = comboWriter->d_routingTag;
×
UNCOV
1300
        }
×
1301

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

1340
      if (appliedPolicy.d_type != DNSFilterEngine::PolicyType::None && appliedPolicy.d_zoneData && appliedPolicy.d_zoneData->d_extendedErrorCode) {
1,709!
UNCOV
1341
        comboWriter->d_extendedErrorCode = *appliedPolicy.d_zoneData->d_extendedErrorCode;
×
UNCOV
1342
        comboWriter->d_extendedErrorExtra = appliedPolicy.d_zoneData->d_extendedErrorExtra;
×
UNCOV
1343
      }
×
1344

1345
      // During lookup, an NSDNAME or NSIP trigger was hit in RPZ
1346
      if (res == -2) { // XXX This block should be macro'd, it is repeated post-resolve.
1,709!
UNCOV
1347
        if (appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction) {
×
1348
          throw PDNSException("NoAction policy returned while a NSDNAME or NSIP trigger was hit");
×
1349
        }
×
UNCOV
1350
        auto policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
×
UNCOV
1351
        if (policyResult == PolicyResult::HaveAnswer) {
×
UNCOV
1352
          goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
×
UNCOV
1353
        }
×
1354
        else if (policyResult == PolicyResult::Drop) {
×
1355
          return;
×
1356
        }
×
UNCOV
1357
      }
×
1358

1359
      bool luaHookHandled = false;
1,709✔
1360
      if (comboWriter->d_luaContext) {
1,709!
UNCOV
1361
        PolicyResult policyResult = PolicyResult::NoAction;
×
UNCOV
1362
        if (SyncRes::answerIsNOData(comboWriter->d_mdp.d_qtype, res, ret)) {
×
UNCOV
1363
          if (comboWriter->d_luaContext->nodata(dnsQuestion, res, resolver.d_eventTrace)) {
×
UNCOV
1364
            luaHookHandled = true;
×
UNCOV
1365
            shouldNotValidate = true;
×
UNCOV
1366
            policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
×
UNCOV
1367
          }
×
UNCOV
1368
        }
×
UNCOV
1369
        else if (res == RCode::NXDomain && comboWriter->d_luaContext->nxdomain(dnsQuestion, res, resolver.d_eventTrace)) {
×
UNCOV
1370
          luaHookHandled = true;
×
UNCOV
1371
          shouldNotValidate = true;
×
UNCOV
1372
          policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
×
UNCOV
1373
        }
×
UNCOV
1374
        if (policyResult == PolicyResult::HaveAnswer) {
×
1375
          goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
×
1376
        }
×
UNCOV
1377
        else if (policyResult == PolicyResult::Drop) {
×
UNCOV
1378
          return;
×
UNCOV
1379
        }
×
UNCOV
1380
      } // dc->d_luaContext
×
1381

1382
      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)) {
1,709!
UNCOV
1383
        res = getFakeAAAARecords(dnsQuestion.qname, *g_dns64Prefix, ret);
×
UNCOV
1384
        shouldNotValidate = true;
×
UNCOV
1385
      }
×
1386

1387
      if (comboWriter->d_luaContext) {
1,709!
UNCOV
1388
        PolicyResult policyResult = PolicyResult::NoAction;
×
UNCOV
1389
        if (comboWriter->d_luaContext->hasPostResolveFFIfunc()) {
×
UNCOV
1390
          RecursorLua4::PostResolveFFIHandle handle(dnsQuestion);
×
UNCOV
1391
          auto match = resolver.d_eventTrace.add(RecEventTrace::LuaPostResolveFFI);
×
UNCOV
1392
          bool prResult = comboWriter->d_luaContext->postresolve_ffi(handle);
×
UNCOV
1393
          resolver.d_eventTrace.add(RecEventTrace::LuaPostResolveFFI, prResult, false, match);
×
UNCOV
1394
          if (prResult) {
×
UNCOV
1395
            shouldNotValidate = true;
×
UNCOV
1396
            policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
×
UNCOV
1397
          }
×
UNCOV
1398
        }
×
UNCOV
1399
        else if (comboWriter->d_luaContext->postresolve(dnsQuestion, res, resolver.d_eventTrace)) {
×
UNCOV
1400
          shouldNotValidate = true;
×
UNCOV
1401
          policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
×
UNCOV
1402
        }
×
UNCOV
1403
        if (policyResult == PolicyResult::HaveAnswer) {
×
UNCOV
1404
          goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
×
UNCOV
1405
        }
×
UNCOV
1406
        else if (policyResult == PolicyResult::Drop) {
×
UNCOV
1407
          return;
×
UNCOV
1408
        }
×
UNCOV
1409
      } // dc->d_luaContext
×
1410
    }
1,709✔
1411
    else if (comboWriter->d_luaContext) {
2,147,483,647!
1412
      // preresolve returned true
UNCOV
1413
      shouldNotValidate = true;
×
UNCOV
1414
      auto policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
×
1415
      // haveAnswer case redundant
UNCOV
1416
      if (policyResult == PolicyResult::Drop) {
×
UNCOV
1417
        return;
×
UNCOV
1418
      }
×
UNCOV
1419
    }
×
1420

1421
  haveAnswer:;
1,710✔
1422
    if (tracedQuery || res == -1 || res == RCode::ServFail || packetWriter.getHeader()->rcode == static_cast<unsigned>(RCode::ServFail)) {
1,709!
UNCOV
1423
      dumpTrace(resolver.getTrace(), resolver.d_fixednow);
×
UNCOV
1424
    }
×
1425

1426
    if (res == -1) {
1,709!
UNCOV
1427
      packetWriter.getHeader()->rcode = RCode::ServFail;
×
1428
      // no commit here, because no record
UNCOV
1429
      ++t_Counters.at(rec::Counter::servFails);
×
UNCOV
1430
    }
×
1431
    else {
1,709✔
1432
      packetWriter.getHeader()->rcode = res;
1,709✔
1433

1434
      // Does the validation mode or query demand validation?
1435
      if (!shouldNotValidate && resolver.isDNSSECValidationRequested()) {
1,709!
1436
        try {
1,709✔
1437
          auto state = resolver.getValidationState();
1,709✔
1438

1439
          string x_marker;
1,709✔
1440
          std::shared_ptr<Logr::Logger> log;
1,709✔
1441
          if (resolver.doLog() || vStateIsBogus(state)) {
1,709!
1442
            // Only create logging object if needed below, beware if you change the logging logic!
UNCOV
1443
            log = resolver.d_slog->withValues("vstate", Logging::Loggable(state));
×
UNCOV
1444
            if (resolver.getDNSSECLimitHit()) {
×
1445
              log = log->withValues("dnsseclimithit", Logging::Loggable(true));
×
1446
            }
×
UNCOV
1447
            auto xdnssec = g_xdnssec.getLocal();
×
UNCOV
1448
            if (xdnssec->check(comboWriter->d_mdp.d_qname)) {
×
1449
              log = log->withValues("in-x-dnssec-names", Logging::Loggable(1));
×
1450
              x_marker = " [in x-dnssec-names]";
×
1451
            }
×
UNCOV
1452
          }
×
1453
          if (state == vState::Secure) {
1,709✔
1454
            if (resolver.doLog()) {
30!
UNCOV
1455
              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,
×
UNCOV
1456
                   log->info(Logr::Info, "Validates Correctly"));
×
UNCOV
1457
            }
×
1458

1459
            // Is the query source interested in the value of the ad-bit?
1460
            if (comboWriter->d_mdp.d_header.ad || DNSSECOK) {
30!
UNCOV
1461
              packetWriter.getHeader()->ad = 1;
×
UNCOV
1462
            }
×
1463
          }
30✔
1464
          else if (state == vState::Insecure) {
1,679!
1465
            if (resolver.doLog()) {
1,679!
UNCOV
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 as Insecure" << endl,
×
UNCOV
1467
                   log->info(Logr::Info, "Validates as Insecure"));
×
UNCOV
1468
            }
×
1469

1470
            packetWriter.getHeader()->ad = 0;
1,679✔
1471
          }
1,679✔
UNCOV
1472
          else if (vStateIsBogus(state)) {
×
UNCOV
1473
            if (t_bogusremotes) {
×
UNCOV
1474
              t_bogusremotes->push_back(comboWriter->d_source);
×
UNCOV
1475
            }
×
UNCOV
1476
            if (t_bogusqueryring) {
×
UNCOV
1477
              t_bogusqueryring->push_back({comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype});
×
UNCOV
1478
            }
×
UNCOV
1479
            if (g_dnssecLogBogus || resolver.doLog() || g_dnssecmode == DNSSECMode::ValidateForLog) {
×
UNCOV
1480
              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,
×
UNCOV
1481
                   log->info(Logr::Notice, "Validates as Bogus"));
×
UNCOV
1482
            }
×
1483

1484
            // Does the query or validation mode sending out a SERVFAIL on validation errors?
UNCOV
1485
            if (!packetWriter.getHeader()->cd && (g_dnssecmode == DNSSECMode::ValidateAll || comboWriter->d_mdp.d_header.ad || DNSSECOK)) {
×
UNCOV
1486
              if (resolver.doLog()) {
×
UNCOV
1487
                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,
×
UNCOV
1488
                     log->info(Logr::Notice, "Sending out SERVFAIL because recursor or query demands it for Bogus results"));
×
UNCOV
1489
              }
×
1490

UNCOV
1491
              packetWriter.getHeader()->rcode = RCode::ServFail;
×
UNCOV
1492
              goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
×
UNCOV
1493
            }
×
UNCOV
1494
            else {
×
UNCOV
1495
              if (resolver.doLog()) {
×
UNCOV
1496
                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,
×
UNCOV
1497
                     log->info(Logr::Notice, "Sending out SERVFAIL because recursor or query demands it for Bogus results"));
×
UNCOV
1498
              }
×
UNCOV
1499
            }
×
UNCOV
1500
          }
×
1501
        }
1,709✔
1502
        catch (const ImmediateServFailException& e) {
1,709✔
1503
          if (g_logCommonErrors) {
×
1504
            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,
×
1505
                 resolver.d_slog->error(Logr::Notice, e.reason, "Sending SERVFAIL during validation", "exception", Logging::Loggable("ImmediateServFailException")));
×
1506
          }
×
1507
          goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
×
1508
        }
×
1509
        catch (const pdns::validation::TooManySEC3IterationsException& e) {
1,709✔
1510
          if (g_logCommonErrors || (g_dnssecLogBogus && resolver.getDNSSECLimitHit())) {
×
1511
            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,
×
1512
                 resolver.d_slog->error(Logr::Notice, e.what(), "Sending SERVFAIL during validation", "exception", Logging::Loggable("TooManySEC3IterationsException"), "dnsseclimithit", Logging::Loggable(resolver.getDNSSECLimitHit())));
×
1513
          }
×
1514
          goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
×
1515
        }
×
1516
      }
1,709✔
1517

1518
      if (!ret.empty()) {
1,709!
1519
#ifdef notyet
1520
        // As dedupping is relatively expensive do not dedup in general. We do have a few cases
1521
        // where we call dedup explicitly, e.g. when doing NAT64 or when adding NSEC records in
1522
        // doCNAMECacheCheck
1523
        pdns::dedupRecords(ret);
1524
#endif
1525
        pdns::orderAndShuffle(ret, false);
1,709✔
1526
        if (auto listToSort = luaconfsLocal->sortlist.getOrderCmp(comboWriter->d_source)) {
1,709!
UNCOV
1527
          stable_sort(ret.begin(), ret.end(), *listToSort);
×
UNCOV
1528
          variableAnswer = true;
×
UNCOV
1529
        }
×
1530
      }
1,709✔
1531

1532
      bool needCommit = false;
1,709✔
1533
      for (const auto& record : ret) {
6,061✔
1534
        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))))) {
6,061!
1535
          continue;
45✔
1536
        }
45✔
1537

1538
        if (!addRecordToPacket(packetWriter, record, minTTL, comboWriter->d_ttlCap, maxanswersize, seenAuthSOA)) {
6,016!
UNCOV
1539
          needCommit = false;
×
UNCOV
1540
          break;
×
UNCOV
1541
        }
×
1542
        needCommit = true;
6,016✔
1543

1544
        bool udr = false;
6,016✔
1545
#ifdef NOD_ENABLED
6,016✔
1546
        if (g_udrEnabled) {
6,016!
UNCOV
1547
          udr = udrCheckUniqueDNSRecord(nodlogger, comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype, record);
×
UNCOV
1548
          if (!hasUDR && udr) {
×
UNCOV
1549
            hasUDR = true;
×
UNCOV
1550
          }
×
UNCOV
1551
        }
×
1552
#endif /* NOD ENABLED */
6,016✔
1553

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

1592
    if (g_useIncomingECS && comboWriter->d_ecsFound && !resolver.wasVariable() && !variableAnswer) {
1,709!
UNCOV
1593
      EDNSSubnetOpts ednsOptions;
×
UNCOV
1594
      ednsOptions.setSource(comboWriter->d_ednssubnet.getSource());
×
UNCOV
1595
      ComboAddress sourceAddr;
×
UNCOV
1596
      sourceAddr.reset();
×
UNCOV
1597
      sourceAddr.sin4.sin_family = ednsOptions.getFamily();
×
UNCOV
1598
      ednsOptions.setScopePrefixLength(0);
×
UNCOV
1599
      auto ecsPayload = ednsOptions.makeOptString();
×
1600

1601
      // if we don't have enough space available let's just not set that scope of zero,
1602
      // it will prevent some caching, mostly from dnsdist, but that's fine
UNCOV
1603
      if (packetWriter.size() < maxanswersize && (maxanswersize - packetWriter.size()) >= (EDNSOptionCodeSize + EDNSOptionLengthSize + ecsPayload.size())) {
×
1604

UNCOV
1605
        maxanswersize -= EDNSOptionCodeSize + EDNSOptionLengthSize + ecsPayload.size();
×
1606

UNCOV
1607
        returnedEdnsOptions.emplace_back(EDNSOptionCode::ECS, std::move(ecsPayload));
×
UNCOV
1608
      }
×
UNCOV
1609
    }
×
1610

1611
    if (haveEDNS && addPaddingToResponse) {
1,709!
UNCOV
1612
      size_t currentSize = packetWriter.getSizeWithOpts(returnedEdnsOptions);
×
1613
      /* we don't use maxawnswersize because it accounts for some EDNS options, but
1614
         not all of them (for example ECS) */
UNCOV
1615
      size_t maxSize = min(static_cast<uint16_t>(edo.d_packetsize >= 512 ? edo.d_packetsize : 512), g_udpTruncationThreshold);
×
1616

UNCOV
1617
      if (currentSize < (maxSize - 4)) {
×
UNCOV
1618
        size_t remaining = maxSize - (currentSize + 4);
×
1619
        /* from rfc8647, "4.1.  Recommended Strategy: Block-Length Padding":
1620
           If a server receives a query that includes the EDNS(0) "Padding"
1621
           option, it MUST pad the corresponding response (see Section 4 of
1622
           RFC 7830) and SHOULD pad the corresponding response to a
1623
           multiple of 468 octets (see below).
1624
        */
UNCOV
1625
        const size_t blockSize = 468;
×
UNCOV
1626
        size_t modulo = (currentSize + 4) % blockSize;
×
UNCOV
1627
        size_t padSize = 0;
×
UNCOV
1628
        if (modulo > 0) {
×
UNCOV
1629
          padSize = std::min(blockSize - modulo, remaining);
×
UNCOV
1630
        }
×
UNCOV
1631
        returnedEdnsOptions.emplace_back(EDNSOptionCode::PADDING, makeEDNSPaddingOptString(padSize));
×
UNCOV
1632
      }
×
UNCOV
1633
    }
×
1634

1635
    if (haveEDNS) {
1,709!
UNCOV
1636
      auto state = resolver.getValidationState();
×
UNCOV
1637
      if (comboWriter->d_extendedErrorCode || resolver.d_extendedError || (SyncRes::s_addExtendedResolutionDNSErrors && vStateIsBogus(state))) {
×
UNCOV
1638
        EDNSExtendedError::code code = EDNSExtendedError::code::Other;
×
UNCOV
1639
        std::string extra;
×
1640

UNCOV
1641
        if (comboWriter->d_extendedErrorCode) {
×
UNCOV
1642
          code = static_cast<EDNSExtendedError::code>(*comboWriter->d_extendedErrorCode);
×
UNCOV
1643
          extra = std::move(comboWriter->d_extendedErrorExtra);
×
UNCOV
1644
        }
×
UNCOV
1645
        else if (resolver.d_extendedError) {
×
UNCOV
1646
          code = static_cast<EDNSExtendedError::code>(resolver.d_extendedError->infoCode);
×
UNCOV
1647
          extra = std::move(resolver.d_extendedError->extraText);
×
UNCOV
1648
        }
×
UNCOV
1649
        else {
×
UNCOV
1650
          switch (state) {
×
UNCOV
1651
          case vState::BogusNoValidDNSKEY:
×
UNCOV
1652
            code = EDNSExtendedError::code::DNSKEYMissing;
×
UNCOV
1653
            break;
×
1654
          case vState::BogusInvalidDenial:
×
1655
            code = EDNSExtendedError::code::NSECMissing;
×
1656
            break;
×
1657
          case vState::BogusUnableToGetDSs:
×
1658
            code = EDNSExtendedError::code::DNSSECBogus;
×
1659
            break;
×
1660
          case vState::BogusUnableToGetDNSKEYs:
×
1661
            code = EDNSExtendedError::code::DNSKEYMissing;
×
1662
            break;
×
1663
          case vState::BogusSelfSignedDS:
×
1664
            code = EDNSExtendedError::code::DNSSECBogus;
×
1665
            break;
×
UNCOV
1666
          case vState::BogusNoRRSIG:
×
UNCOV
1667
            code = EDNSExtendedError::code::RRSIGsMissing;
×
UNCOV
1668
            break;
×
UNCOV
1669
          case vState::BogusNoValidRRSIG:
×
UNCOV
1670
            code = EDNSExtendedError::code::DNSSECBogus;
×
UNCOV
1671
            break;
×
UNCOV
1672
          case vState::BogusMissingNegativeIndication:
×
UNCOV
1673
            code = EDNSExtendedError::code::NSECMissing;
×
UNCOV
1674
            break;
×
UNCOV
1675
          case vState::BogusSignatureNotYetValid:
×
UNCOV
1676
            code = EDNSExtendedError::code::SignatureNotYetValid;
×
UNCOV
1677
            break;
×
UNCOV
1678
          case vState::BogusSignatureExpired:
×
UNCOV
1679
            code = EDNSExtendedError::code::SignatureExpired;
×
UNCOV
1680
            break;
×
1681
          case vState::BogusUnsupportedDNSKEYAlgo:
×
1682
            code = EDNSExtendedError::code::UnsupportedDNSKEYAlgorithm;
×
1683
            break;
×
1684
          case vState::BogusUnsupportedDSDigestType:
×
1685
            code = EDNSExtendedError::code::UnsupportedDSDigestType;
×
1686
            break;
×
1687
          case vState::BogusNoZoneKeyBitSet:
×
1688
            code = EDNSExtendedError::code::NoZoneKeyBitSet;
×
1689
            break;
×
1690
          case vState::BogusRevokedDNSKEY:
×
1691
          case vState::BogusInvalidDNSKEYProtocol:
×
1692
            code = EDNSExtendedError::code::DNSSECBogus;
×
1693
            break;
×
1694
          default:
×
1695
            throw std::runtime_error("Bogus validation state not handled: " + vStateToString(state));
×
UNCOV
1696
          }
×
UNCOV
1697
        }
×
1698

UNCOV
1699
        EDNSExtendedError eee;
×
UNCOV
1700
        eee.infoCode = static_cast<uint16_t>(code);
×
UNCOV
1701
        eee.extraText = std::move(extra);
×
1702

UNCOV
1703
        if (packetWriter.size() < maxanswersize && (maxanswersize - packetWriter.size()) >= (EDNSOptionCodeSize + EDNSOptionLengthSize + sizeof(eee.infoCode) + eee.extraText.size())) {
×
UNCOV
1704
          returnedEdnsOptions.emplace_back(EDNSOptionCode::EXTENDEDERROR, makeEDNSExtendedErrorOptString(eee));
×
UNCOV
1705
        }
×
UNCOV
1706
      }
×
1707

1708
      /* we try to add the EDNS OPT RR even for truncated answers,
1709
         as rfc6891 states:
1710
         "The minimal response MUST be the DNS header, question section, and an
1711
         OPT record.  This MUST also occur when a truncated response (using
1712
         the DNS header's TC bit) is returned."
1713
      */
UNCOV
1714
      packetWriter.addOpt(512, ednsExtRCode, DNSSECOK ? EDNSOpts::DNSSECOK : 0, returnedEdnsOptions);
×
UNCOV
1715
      packetWriter.commit();
×
UNCOV
1716
    }
×
1717

1718
    t_Counters.at(rec::ResponseStats::responseStats).submitResponse(comboWriter->d_mdp.d_qtype, packet.size(), packetWriter.getHeader()->rcode);
1,709✔
1719
    updateResponseStats(res, comboWriter->d_source, packet.size(), &comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype);
1,709✔
1720
#ifdef NOD_ENABLED
1,709✔
1721
    bool nod = false;
1,709✔
1722
    if (g_nodEnabled) {
1,709!
UNCOV
1723
      if (nodCheckNewDomain(nodlogger, comboWriter->d_mdp.d_qname)) {
×
UNCOV
1724
        nod = true;
×
UNCOV
1725
#ifdef HAVE_FSTRM
×
UNCOV
1726
        if (isEnabledForNODs(t_nodFrameStreamServersInfo.servers)) {
×
UNCOV
1727
          struct timespec timeSpec{};
×
UNCOV
1728
          std::string str;
×
UNCOV
1729
          if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
×
1730
            TIMEVAL_TO_TIMESPEC(&comboWriter->d_kernelTimestamp, &timeSpec); // NOLINT
×
1731
          }
×
UNCOV
1732
          else {
×
UNCOV
1733
            TIMEVAL_TO_TIMESPEC(&comboWriter->d_now, &timeSpec); // NOLINT
×
UNCOV
1734
          }
×
UNCOV
1735
          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);
×
UNCOV
1736
          str = message.getBuffer();
×
1737

UNCOV
1738
          for (auto& logger : *(t_nodFrameStreamServersInfo.servers)) {
×
UNCOV
1739
            if (logger->logNODs()) {
×
UNCOV
1740
              remoteLoggerQueueData(*logger, str);
×
UNCOV
1741
            }
×
UNCOV
1742
          }
×
UNCOV
1743
        }
×
UNCOV
1744
#endif // HAVE_FSTRM
×
UNCOV
1745
      }
×
UNCOV
1746
    }
×
1747
#endif /* NOD_ENABLED */
1,709✔
1748

1749
    if (variableAnswer || resolver.wasVariable()) {
1,709!
UNCOV
1750
      t_Counters.at(rec::Counter::variableResponses)++;
×
UNCOV
1751
    }
×
1752

1753
    if (t_protobufServers.servers && !(luaconfsLocal->protobufExportConfig.taggedOnly && appliedPolicy.getName().empty() && comboWriter->d_policyTags.empty())) {
1,709!
1754
      // Start constructing embedded DNSResponse object
UNCOV
1755
      pbMessage.setResponseCode(packetWriter.getHeader()->rcode);
×
UNCOV
1756
      if (!appliedPolicy.getName().empty()) {
×
UNCOV
1757
        pbMessage.setAppliedPolicy(appliedPolicy.getName());
×
UNCOV
1758
        pbMessage.setAppliedPolicyType(appliedPolicy.d_type);
×
UNCOV
1759
        pbMessage.setAppliedPolicyTrigger(appliedPolicy.getTrigger());
×
UNCOV
1760
        pbMessage.setAppliedPolicyHit(appliedPolicy.getHit());
×
UNCOV
1761
        pbMessage.setAppliedPolicyKind(appliedPolicy.d_kind);
×
UNCOV
1762
      }
×
UNCOV
1763
      pbMessage.setInBytes(packet.size());
×
UNCOV
1764
      pbMessage.setValidationState(resolver.getValidationState());
×
1765
      // See if we want to store the policyTags into the PC
UNCOV
1766
      addPolicyTagsToPBMessageIfNeeded(*comboWriter, pbMessage);
×
1767

1768
      // Take s snap of the current protobuf buffer state to store in the PC
UNCOV
1769
      pbDataForCache = boost::make_optional(RecursorPacketCache::PBData{
×
UNCOV
1770
        pbMessage.getMessageBuf(),
×
UNCOV
1771
        pbMessage.getResponseBuf(),
×
UNCOV
1772
        !appliedPolicy.getName().empty() || !comboWriter->d_policyTags.empty()});
×
UNCOV
1773
#ifdef NOD_ENABLED
×
1774
      // if (g_udrEnabled) ??
UNCOV
1775
      pbMessage.clearUDR(pbDataForCache->d_response);
×
UNCOV
1776
#endif
×
UNCOV
1777
    }
×
1778

1779
    const bool intoPC = g_packetCache && !variableAnswer && !resolver.wasVariable();
1,709!
1780
    if (intoPC) {
1,709!
1781
      minTTL = capPacketCacheTTL(*packetWriter.getHeader(), minTTL, seenAuthSOA);
1,709✔
1782
      g_packetCache->insertResponsePacket(comboWriter->d_tag, comboWriter->d_qhash, std::move(comboWriter->d_query), comboWriter->d_mdp.d_qname,
1,709✔
1783
                                          comboWriter->d_mdp.d_qtype, comboWriter->d_mdp.d_qclass,
1,709✔
1784
                                          string(reinterpret_cast<const char*>(&*packet.begin()), packet.size()), // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
1,709✔
1785
                                          g_now.tv_sec,
1,709✔
1786
                                          minTTL,
1,709✔
1787
                                          dnsQuestion.validationState,
1,709✔
1788
                                          std::move(pbDataForCache), comboWriter->d_tcp);
1,709✔
1789
    }
1,709✔
1790

1791
    if (g_regressionTestMode) {
1,709!
UNCOV
1792
      t_Counters.updateSnap(g_regressionTestMode);
×
UNCOV
1793
    }
×
1794

1795
    if (!comboWriter->d_tcp) {
1,709!
1796
      struct msghdr msgh{};
1,709✔
1797
      struct iovec iov{};
1,709✔
1798
      cmsgbuf_aligned cbuf{};
1,709✔
1799
      fillMSGHdr(&msgh, &iov, &cbuf, 0, reinterpret_cast<char*>(&*packet.begin()), packet.size(), &comboWriter->d_remote); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
1,709✔
1800
      msgh.msg_control = nullptr;
1,709✔
1801

1802
      if (g_fromtosockets.count(comboWriter->d_socket) > 0) {
1,709!
UNCOV
1803
        addCMsgSrcAddr(&msgh, &cbuf, &comboWriter->d_local, 0);
×
UNCOV
1804
      }
×
1805
      int sendErr = sendOnNBSocket(comboWriter->d_socket, &msgh);
1,709✔
1806
      if (sendErr != 0 && g_logCommonErrors) {
1,709!
1807
        SLOG(g_log << Logger::Warning << "Sending UDP reply to client " << comboWriter->getRemote() << " failed with: "
×
1808
                   << stringerror(sendErr) << endl,
×
1809
             g_slogudpin->error(Logr::Warning, sendErr, "Sending UDP reply to client failed"));
×
1810
      }
×
1811
    }
1,709✔
UNCOV
1812
    else {
×
UNCOV
1813
      bool hadError = sendResponseOverTCP(comboWriter, packet);
×
UNCOV
1814
      finishTCPReply(comboWriter, hadError, true);
×
UNCOV
1815
      tcpGuard.setHandled();
×
UNCOV
1816
    }
×
1817

1818
    resolver.d_eventTrace.add(RecEventTrace::AnswerSent);
1,709✔
1819

1820
    // Now do the per query changing part of the protobuf message
1821
    if (t_protobufServers.servers && !(luaconfsLocal->protobufExportConfig.taggedOnly && appliedPolicy.getName().empty() && comboWriter->d_policyTags.empty())) {
1,709!
1822
      // Below are the fields that are not stored in the packet cache and will be appended here and on a cache hit
UNCOV
1823
      if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
×
1824
        pbMessage.setQueryTime(comboWriter->d_kernelTimestamp.tv_sec, comboWriter->d_kernelTimestamp.tv_usec);
×
1825
      }
×
UNCOV
1826
      else {
×
UNCOV
1827
        pbMessage.setQueryTime(comboWriter->d_now.tv_sec, comboWriter->d_now.tv_usec);
×
UNCOV
1828
      }
×
UNCOV
1829
      pbMessage.setMessageIdentity(comboWriter->d_uuid);
×
UNCOV
1830
      pbMessage.setSocketProtocol(comboWriter->d_tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP);
×
1831

UNCOV
1832
      if (!luaconfsLocal->protobufExportConfig.logMappedFrom) {
×
UNCOV
1833
        pbMessage.setSocketFamily(comboWriter->d_source.sin4.sin_family);
×
UNCOV
1834
        Netmask requestorNM(comboWriter->d_source, comboWriter->d_source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
×
UNCOV
1835
        ComboAddress requestor = requestorNM.getMaskedNetwork();
×
UNCOV
1836
        pbMessage.setFrom(requestor);
×
UNCOV
1837
        pbMessage.setFromPort(comboWriter->d_source.getPort());
×
UNCOV
1838
      }
×
UNCOV
1839
      else {
×
UNCOV
1840
        pbMessage.setSocketFamily(comboWriter->d_mappedSource.sin4.sin_family);
×
UNCOV
1841
        Netmask requestorNM(comboWriter->d_mappedSource, comboWriter->d_mappedSource.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
×
UNCOV
1842
        ComboAddress requestor = requestorNM.getMaskedNetwork();
×
UNCOV
1843
        pbMessage.setFrom(requestor);
×
UNCOV
1844
        pbMessage.setFromPort(comboWriter->d_mappedSource.getPort());
×
UNCOV
1845
      }
×
1846

UNCOV
1847
      pbMessage.setTo(comboWriter->d_destination);
×
UNCOV
1848
      pbMessage.setId(comboWriter->d_mdp.d_header.id);
×
1849

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

1888
    if (resolver.d_eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_log)) {
1,709!
1889
      SLOG(g_log << Logger::Info << resolver.d_eventTrace.toString() << endl,
×
1890
           resolver.d_slog->info(Logr::Info, resolver.d_eventTrace.toString())); // Maybe we want it to be more fancy?
×
1891
    }
×
1892

1893
    // Originally this code used a mix of floats, doubles, uint64_t with different units.
1894
    // Now it always uses an integral number of microseconds, except for averages, which use doubles
1895
    uint64_t spentUsec = uSec(resolver.getNow() - comboWriter->d_now);
1,709✔
1896
    if (!g_quiet) {
1,709!
UNCOV
1897
      if (!g_slogStructured) {
×
1898
        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);
×
1899
        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;
×
1900

1901
        if (!shouldNotValidate && resolver.isDNSSECValidationRequested()) {
×
1902
          g_log << ", dnssec=" << resolver.getValidationState();
×
1903
        }
×
1904
        g_log << " answer-is-variable=" << resolver.wasVariable() << ", into-packetcache=" << intoPC;
×
1905
        g_log << " maxdepth=" << resolver.d_maxdepth;
×
1906
        g_log << endl;
×
1907
      }
×
UNCOV
1908
      else {
×
UNCOV
1909
        resolver.d_slog->info(Logr::Info, "Answer", "rd", Logging::Loggable(comboWriter->d_mdp.d_header.rd),
×
UNCOV
1910
                              "answers", Logging::Loggable(ntohs(packetWriter.getHeader()->ancount)),
×
UNCOV
1911
                              "additional", Logging::Loggable(ntohs(packetWriter.getHeader()->arcount)),
×
UNCOV
1912
                              "outqueries", Logging::Loggable(resolver.d_outqueries),
×
UNCOV
1913
                              "netms", Logging::Loggable(resolver.d_totUsec / 1000.0),
×
UNCOV
1914
                              "totms", Logging::Loggable(static_cast<double>(spentUsec) / 1000.0),
×
UNCOV
1915
                              "throttled", Logging::Loggable(resolver.d_throttledqueries),
×
UNCOV
1916
                              "timeouts", Logging::Loggable(resolver.d_timeouts),
×
UNCOV
1917
                              "tcpout", Logging::Loggable(resolver.d_tcpoutqueries),
×
UNCOV
1918
                              "dotout", Logging::Loggable(resolver.d_dotoutqueries),
×
UNCOV
1919
                              "rcode", Logging::Loggable(res),
×
UNCOV
1920
                              "validationState", Logging::Loggable(resolver.getValidationState()),
×
UNCOV
1921
                              "answer-is-variable", Logging::Loggable(resolver.wasVariable()),
×
UNCOV
1922
                              "into-packetcache", Logging::Loggable(intoPC),
×
UNCOV
1923
                              "maxdepth", Logging::Loggable(resolver.d_maxdepth));
×
UNCOV
1924
      }
×
UNCOV
1925
    }
×
1926

1927
    if (comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Query)) {
1,709!
1928
      if (resolver.d_outqueries != 0 || resolver.d_throttledqueries != 0 || resolver.d_authzonequeries != 0) {
1,709!
1929
        g_recCache->incCacheMisses();
1,650✔
1930
      }
1,650✔
1931
      else {
59✔
1932
        g_recCache->incCacheHits();
59✔
1933
      }
59✔
1934
    }
1,709✔
1935

1936
    t_Counters.at(rec::Histogram::answers)(spentUsec);
1,709✔
1937
    t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
1,709✔
1938

1939
    auto newLat = static_cast<double>(spentUsec);
1,709✔
1940
    newLat = min(newLat, g_networkTimeoutMsec * 1000.0); // outliers of several minutes exist..
1,709✔
1941
    t_Counters.at(rec::DoubleWAvgCounter::avgLatencyUsec).addToRollingAvg(newLat, g_latencyStatSize);
1,709✔
1942
    // no worries, we do this for packet cache hits elsewhere
1943

1944
    if (spentUsec >= resolver.d_totUsec) {
1,709!
1945
      uint64_t ourtime = spentUsec - resolver.d_totUsec;
1,709✔
1946
      t_Counters.at(rec::Histogram::ourtime)(ourtime);
1,709✔
1947
      newLat = static_cast<double>(ourtime); // usec
1,709✔
1948
      t_Counters.at(rec::DoubleWAvgCounter::avgLatencyOursUsec).addToRollingAvg(newLat, g_latencyStatSize);
1,709✔
1949
    }
1,709✔
1950

1951
#ifdef NOD_ENABLED
1,709✔
1952
    if (nod) {
1,709!
UNCOV
1953
      sendNODLookup(nodlogger, comboWriter->d_mdp.d_qname);
×
UNCOV
1954
    }
×
1955
#endif /* NOD_ENABLED */
1,709✔
1956

1957
    //    cout<<dc->d_mdp.d_qname<<"\t"<<MT->getUsec()<<"\t"<<sr.d_outqueries<<endl;
1958
  }
1,709✔
1959
  catch (const PDNSException& ae) {
1,708✔
1960
    SLOG(g_log << Logger::Error << "startDoResolve problem " << makeLoginfo(comboWriter) << ": " << ae.reason << endl,
×
1961
         resolver.d_slog->error(Logr::Error, ae.reason, "startDoResolve problem", "exception", Logging::Loggable("PDNSException")));
×
1962
  }
×
1963
  catch (const MOADNSException& mde) {
1,708✔
1964
    SLOG(g_log << Logger::Error << "DNS parser error " << makeLoginfo(comboWriter) << ": " << comboWriter->d_mdp.d_qname << ", " << mde.what() << endl,
×
1965
         resolver.d_slog->error(Logr::Error, mde.what(), "DNS parser error"));
×
1966
  }
×
1967
  catch (const std::exception& e) {
1,708✔
1968
    SLOG(g_log << Logger::Error << "STL error " << makeLoginfo(comboWriter) << ": " << e.what(),
×
1969
         resolver.d_slog->error(Logr::Error, e.what(), "Exception in resolver context", "exception", Logging::Loggable("std::exception")));
×
1970

1971
    // Luawrapper nests the exception from Lua, so we unnest it here
1972
    try {
×
1973
      std::rethrow_if_nested(e);
×
1974
    }
×
1975
    catch (const std::exception& ne) {
×
1976
      SLOG(g_log << ". Extra info: " << ne.what(),
×
1977
           resolver.d_slog->error(Logr::Error, ne.what(), "Nested exception in resolver context", Logging::Loggable("std::exception")));
×
1978
    }
×
1979
    catch (...) {
×
1980
    }
×
1981
    if (!g_slogStructured) {
×
1982
      g_log << endl;
×
1983
    }
×
1984
  }
×
1985
  catch (...) {
1,708✔
1986
    SLOG(g_log << Logger::Error << "Any other exception in a resolver context " << makeLoginfo(comboWriter) << endl,
×
1987
         resolver.d_slog->info(Logr::Error, "Any other exception in a resolver context"));
×
1988
  }
×
1989

1990
  runTaskOnce(g_logCommonErrors);
1,709✔
1991

1992
  static const size_t stackSizeThreshold = 9 * ::arg().asNum("stack-size") / 10;
1,709✔
1993
  if (g_multiTasker->getMaxStackUsage() >= stackSizeThreshold) {
1,709!
1994
    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,
×
1995
         resolver.d_slog->info(Logr::Error, "Reached mthread stack usage of 90%",
×
1996
                               "stackUsage", Logging::Loggable(g_multiTasker->getMaxStackUsage()),
×
1997
                               "outqueries", Logging::Loggable(resolver.d_outqueries),
×
1998
                               "netms", Logging::Loggable(resolver.d_totUsec / 1000.0),
×
1999
                               "throttled", Logging::Loggable(resolver.d_throttledqueries),
×
2000
                               "timeouts", Logging::Loggable(resolver.d_timeouts),
×
2001
                               "tcpout", Logging::Loggable(resolver.d_tcpoutqueries),
×
2002
                               "dotout", Logging::Loggable(resolver.d_dotoutqueries),
×
2003
                               "validationState", Logging::Loggable(resolver.getValidationState())));
×
2004
  }
×
2005
  t_Counters.at(rec::Counter::maxMThreadStackUsage) = max(g_multiTasker->getMaxStackUsage(), t_Counters.at(rec::Counter::maxMThreadStackUsage));
1,709✔
2006
  t_Counters.updateSnap(g_regressionTestMode);
1,709✔
2007
}
1,709✔
2008

2009
void getQNameAndSubnet(const std::string& question, DNSName* dnsname, uint16_t* qtype, uint16_t* qclass,
2010
                       bool& foundECS, EDNSSubnetOpts* ednssubnet, EDNSOptionViewMap* options, boost::optional<uint32_t>& ednsVersion)
UNCOV
2011
{
×
UNCOV
2012
  const dnsheader_aligned dnshead(question.data());
×
UNCOV
2013
  const dnsheader* dhPointer = dnshead.get();
×
UNCOV
2014
  size_t questionLen = question.length();
×
UNCOV
2015
  unsigned int consumed = 0;
×
UNCOV
2016
  *dnsname = DNSName(question.c_str(), static_cast<int>(questionLen), sizeof(dnsheader), false, qtype, qclass, &consumed);
×
2017

UNCOV
2018
  size_t pos = sizeof(dnsheader) + consumed + 4;
×
UNCOV
2019
  const size_t headerSize = /* root */ 1 + sizeof(dnsrecordheader);
×
UNCOV
2020
  const uint16_t arcount = ntohs(dhPointer->arcount);
×
2021

UNCOV
2022
  for (uint16_t arpos = 0; arpos < arcount && questionLen >= (pos + headerSize) && !foundECS; arpos++) {
×
UNCOV
2023
    if (question.at(pos) != 0) {
×
2024
      /* not an OPT, bye. */
2025
      return;
×
2026
    }
×
2027

UNCOV
2028
    pos += 1;
×
UNCOV
2029
    const auto* drh = reinterpret_cast<const dnsrecordheader*>(&question.at(pos)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
×
UNCOV
2030
    if (ntohs(drh->d_type) == QType::OPT) {
×
UNCOV
2031
      uint32_t edns{};
×
UNCOV
2032
      memcpy(&edns, &drh->d_ttl, sizeof(edns)); // drh is not neccesarily aligned, so no uint32 assignment can be done
×
UNCOV
2033
      ednsVersion = edns;
×
UNCOV
2034
    }
×
UNCOV
2035
    pos += sizeof(dnsrecordheader);
×
2036

UNCOV
2037
    if (pos >= questionLen) {
×
UNCOV
2038
      return;
×
UNCOV
2039
    }
×
2040

2041
    /* OPT root label (1) followed by type (2) */
UNCOV
2042
    if (ntohs(drh->d_type) == QType::OPT) {
×
UNCOV
2043
      if (options == nullptr) {
×
UNCOV
2044
        size_t ecsStartPosition = 0;
×
UNCOV
2045
        size_t ecsLen = 0;
×
2046
        /* we need to pass the record len */
UNCOV
2047
        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)
×
UNCOV
2048
        if (res == 0 && ecsLen > 4) {
×
UNCOV
2049
          EDNSSubnetOpts eso;
×
UNCOV
2050
          if (EDNSSubnetOpts::getFromString(&question.at(pos - sizeof(drh->d_clen) + ecsStartPosition + 4), ecsLen - 4, &eso)) {
×
UNCOV
2051
            *ednssubnet = eso;
×
UNCOV
2052
            foundECS = true;
×
UNCOV
2053
          }
×
UNCOV
2054
        }
×
UNCOV
2055
      }
×
UNCOV
2056
      else {
×
2057
        /* we need to pass the record len */
UNCOV
2058
        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)
×
UNCOV
2059
        if (res == 0) {
×
UNCOV
2060
          const auto& iter = options->find(EDNSOptionCode::ECS);
×
UNCOV
2061
          if (iter != options->end() && !iter->second.values.empty() && iter->second.values.at(0).content != nullptr && iter->second.values.at(0).size > 0) {
×
UNCOV
2062
            EDNSSubnetOpts eso;
×
UNCOV
2063
            if (EDNSSubnetOpts::getFromString(iter->second.values.at(0).content, iter->second.values.at(0).size, &eso)) {
×
UNCOV
2064
              *ednssubnet = eso;
×
UNCOV
2065
              foundECS = true;
×
UNCOV
2066
            }
×
UNCOV
2067
          }
×
UNCOV
2068
        }
×
UNCOV
2069
      }
×
UNCOV
2070
    }
×
2071

UNCOV
2072
    pos += ntohs(drh->d_clen);
×
UNCOV
2073
  }
×
UNCOV
2074
}
×
2075

2076
bool checkForCacheHit(bool qnameParsed, unsigned int tag, const string& data,
2077
                      DNSName& qname, uint16_t& qtype, uint16_t& qclass,
2078
                      const struct timeval& now,
2079
                      string& response, uint32_t& qhash,
2080
                      RecursorPacketCache::OptPBData& pbData, bool tcp, const ComboAddress& source, const ComboAddress& mappedSource)
2081
{
4,418✔
2082
  if (!g_packetCache) {
4,418!
UNCOV
2083
    return false;
×
UNCOV
2084
  }
×
2085
  bool cacheHit = false;
4,418✔
2086
  uint32_t age = 0;
4,418✔
2087
  vState valState = vState::Indeterminate;
4,418✔
2088

2089
  if (qnameParsed) {
4,418!
UNCOV
2090
    cacheHit = g_packetCache->getResponsePacket(tag, data, qname, qtype, qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
×
UNCOV
2091
  }
×
2092
  else {
4,418✔
2093
    cacheHit = g_packetCache->getResponsePacket(tag, data, qname, &qtype, &qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
4,418✔
2094
  }
4,418✔
2095

2096
  if (cacheHit) {
4,418✔
2097
    if (vStateIsBogus(valState)) {
2,710!
2098
      if (t_bogusremotes) {
×
2099
        t_bogusremotes->push_back(source);
×
2100
      }
×
2101
      if (t_bogusqueryring) {
×
2102
        t_bogusqueryring->push_back({qname, qtype});
×
2103
      }
×
2104
    }
×
2105

2106
    // This is only to get the proxyMapping suffixMatch stats right i the case of a PC hit
2107
    if (t_proxyMapping && source != mappedSource) {
2,710!
2108
      if (const auto* found = t_proxyMapping->lookup(source)) {
×
2109
        if (found->second.suffixMatchNode) {
×
2110
          if (found->second.suffixMatchNode->check(qname)) {
×
2111
            ++found->second.stats.suffixMatches;
×
2112
          }
×
2113
        }
×
2114
      }
×
2115
    }
×
2116

2117
    if (response.length() >= sizeof(struct dnsheader)) {
2,711✔
2118
      dnsheader_aligned dh_aligned(response.data());
2,701✔
2119
      ageDNSPacket(response, age, dh_aligned);
2,701✔
2120
      const auto* dhp = dh_aligned.get();
2,701✔
2121
      updateResponseStats(dhp->rcode, source, response.length(), nullptr, 0);
2,701✔
2122
      t_Counters.at(rec::ResponseStats::responseStats).submitResponse(qtype, response.length(), dhp->rcode);
2,701✔
2123
    }
2,701✔
2124

2125
    // we assume 0 usec
2126
    t_Counters.at(rec::DoubleWAvgCounter::avgLatencyUsec).addToRollingAvg(0.0, g_latencyStatSize);
2,710✔
2127
    t_Counters.at(rec::DoubleWAvgCounter::avgLatencyOursUsec).addToRollingAvg(0.0, g_latencyStatSize);
2,710✔
2128
#if 0
2129
    // XXX changes behaviour compared to old code!
2130
    t_Counters.at(rec::Counter::answers)(0);
2131
    t_Counters.at(rec::Counter::ourtime)(0);
2132
#endif
2133
  }
2,710✔
2134

2135
  return cacheHit;
4,418✔
2136
}
4,418✔
2137

2138
static void* pleaseWipeCaches(const DNSName& canon, bool subtree, uint16_t qtype)
UNCOV
2139
{
×
UNCOV
2140
  auto res = wipeCaches(canon, subtree, qtype);
×
UNCOV
2141
  SLOG(g_log << Logger::Info << "Wiped caches for " << canon << ": " << res.record_count << " records; " << res.negative_record_count << " negative records; " << res.packet_count << " packets" << endl,
×
UNCOV
2142
       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)));
×
UNCOV
2143
  return nullptr;
×
UNCOV
2144
}
×
2145

2146
void requestWipeCaches(const DNSName& canon)
UNCOV
2147
{
×
2148
  // send a message to the handler thread asking it
2149
  // to wipe all of the caches
UNCOV
2150
  ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: pointer owner
×
UNCOV
2151
  tmsg->func = [=] { return pleaseWipeCaches(canon, true, 0xffff); };
×
UNCOV
2152
  tmsg->wantAnswer = false;
×
UNCOV
2153
  if (write(RecThreadInfo::info(0).getPipes().writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // NOLINT: correct sizeof
×
2154
    delete tmsg; // NOLINT: pointer owner
×
2155

2156
    unixDie("write to thread pipe returned wrong size or error");
×
2157
  }
×
2158
  // coverity[leaked_storage]
UNCOV
2159
}
×
2160

2161
bool expectProxyProtocol(const ComboAddress& from, const ComboAddress& listenAddress)
2162
{
4,500✔
2163
  if (!t_proxyProtocolACL) {
4,500!
2164
    return false;
4,500✔
2165
  }
4,500✔
UNCOV
2166
  if (t_proxyProtocolACL->match(from)) {
×
UNCOV
2167
    if (!t_proxyProtocolExceptions) {
×
UNCOV
2168
      return true;
×
UNCOV
2169
    }
×
UNCOV
2170
    return t_proxyProtocolExceptions->count(listenAddress) == 0;
×
UNCOV
2171
  }
×
UNCOV
2172
  return false;
×
UNCOV
2173
}
×
2174

2175
// fromaddr: the address the query is coming from
2176
// destaddr: the address the query was received on
2177
// source: the address we assume the query is coming from, might be set by proxy protocol
2178
// destination: the address we assume the query was sent to, might be set by proxy protocol
2179
// mappedSource: the address we assume the query is coming from. Differs from source if table based mapping has been applied
2180
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::Span& otTrace) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
2181
{
4,487✔
2182
  RecThreadInfo::self().incNumberOfDistributedQueries();
4,487✔
2183
  gettimeofday(&g_now, nullptr);
4,487✔
2184
  if (tval.tv_sec != 0) {
4,487✔
2185
    struct timeval diff = g_now - tval;
4,472✔
2186
    double delta = (static_cast<double>(diff.tv_sec) * 1000 + static_cast<double>(diff.tv_usec) / 1000.0);
4,472✔
2187

2188
    if (delta > 1000.0) {
4,472✔
2189
      t_Counters.at(rec::Counter::tooOldDrops)++;
74✔
2190
      return nullptr;
74✔
2191
    }
74✔
2192
  }
4,472✔
2193

2194
  ++t_Counters.at(rec::Counter::qcounter);
4,413✔
2195

2196
  if (fromaddr.sin4.sin_family == AF_INET6) {
4,413!
UNCOV
2197
    t_Counters.at(rec::Counter::ipv6qcounter)++;
×
UNCOV
2198
  }
×
2199

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

2259
    // We do not have a SyncRes specific Lua context at this point yet, so ok to use t_pdl
2260
    if (needEDNSParse || (t_pdl && (t_pdl->hasGettagFunc() || t_pdl->hasGettagFFIFunc())) || dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
4,426!
UNCOV
2261
      try {
×
UNCOV
2262
        EDNSOptionViewMap ednsOptions;
×
2263

UNCOV
2264
        ecsFound = false;
×
2265

UNCOV
2266
        getQNameAndSubnet(question, &qname, &qtype, &qclass,
×
UNCOV
2267
                          ecsFound, &ednssubnet,
×
UNCOV
2268
                          (g_gettagNeedsEDNSOptions || SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) ? &ednsOptions : nullptr,
×
UNCOV
2269
                          ednsVersion);
×
2270

UNCOV
2271
        qnameParsed = true;
×
UNCOV
2272
        ecsParsed = true;
×
2273

UNCOV
2274
        if (SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
×
2275
          pdns::trace::extractOTraceIDs(ednsOptions, otTrace);
×
2276
        }
×
UNCOV
2277
        if (t_pdl) {
×
UNCOV
2278
          try {
×
UNCOV
2279
            if (t_pdl->hasGettagFFIFunc()) {
×
UNCOV
2280
              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);
×
2281

UNCOV
2282
              auto match = eventTrace.add(RecEventTrace::LuaGetTagFFI);
×
UNCOV
2283
              ctag = t_pdl->gettag_ffi(params);
×
UNCOV
2284
              eventTrace.add(RecEventTrace::LuaGetTagFFI, ctag, false, match);
×
UNCOV
2285
            }
×
UNCOV
2286
            else if (t_pdl->hasGettagFunc()) {
×
UNCOV
2287
              auto match = eventTrace.add(RecEventTrace::LuaGetTag);
×
UNCOV
2288
              ctag = t_pdl->gettag(source, ednssubnet.getSource(), destination, qname, qtype, &policyTags, data, ednsOptions, false, requestorId, deviceId, deviceName, routingTag, proxyProtocolValues);
×
UNCOV
2289
              eventTrace.add(RecEventTrace::LuaGetTag, ctag, false, match);
×
UNCOV
2290
            }
×
UNCOV
2291
          }
×
UNCOV
2292
          catch (const MOADNSException& moadnsexception) {
×
2293
            if (g_logCommonErrors) {
×
2294
              g_slogudpin->error(moadnsexception.what(), "Error parsing a query packet for tag determination", "qname", Logging::Loggable(qname), "excepion", Logging::Loggable("MOADNSException"));
×
2295
            }
×
2296
          }
×
UNCOV
2297
          catch (const std::exception& stdException) {
×
2298
            g_rateLimitedLogger.log(g_slogudpin, "Error parsing a query packet for tag determination", stdException, "qname", Logging::Loggable(qname), "remote", Logging::Loggable(fromaddr));
×
2299
          }
×
UNCOV
2300
        }
×
UNCOV
2301
      }
×
UNCOV
2302
      catch (const std::exception& stdException) {
×
2303
        g_rateLimitedLogger.log(g_slogudpin, "Error parsing a query packet for tag determination, setting tag=0", stdException);
×
2304
      }
×
UNCOV
2305
    }
×
2306

2307
    RecursorPacketCache::OptPBData pbData{boost::none};
4,413✔
2308
    if (t_protobufServers.servers) {
4,413!
UNCOV
2309
      if (logQuery && !(luaconfsLocal->protobufExportConfig.taggedOnly && policyTags.empty())) {
×
UNCOV
2310
        protobufLogQuery(luaconfsLocal, uniqueId, source, destination, mappedSource, ednssubnet.getSource(), false, question.size(), qname, qtype, qclass, policyTags, requestorId, deviceId, deviceName, meta, ednsVersion, *dnsheader);
×
UNCOV
2311
      }
×
UNCOV
2312
    }
×
2313

2314
    if (ctag == 0 && !responsePaddingDisabled && g_paddingFrom.match(fromaddr)) {
4,424!
UNCOV
2315
      ctag = g_paddingTag;
×
UNCOV
2316
    }
×
2317

2318
    if (dnsheader->opcode == static_cast<unsigned>(Opcode::Query)) {
4,423✔
2319
      /* It might seem like a good idea to skip the packet cache lookup if we know that the answer is not cacheable,
2320
         but it means that the hash would not be computed. If some script decides at a later time to mark back the answer
2321
         as cacheable we would cache it with a wrong tag, so better safe than sorry. */
2322
      auto match = eventTrace.add(RecEventTrace::PCacheCheck);
4,422✔
2323
      bool cacheHit = checkForCacheHit(qnameParsed, ctag, question, qname, qtype, qclass, g_now, response, qhash, pbData, false, source, mappedSource);
4,422✔
2324
      eventTrace.add(RecEventTrace::PCacheCheck, cacheHit, false, match);
4,422✔
2325
      if (cacheHit) {
4,422✔
2326
        if (!g_quiet) {
2,707!
UNCOV
2327
          SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " question answered from packet cache tag=" << ctag << " from " << source.toStringWithPort() << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << endl,
×
UNCOV
2328
               g_slogudpin->info(Logr::Notice, "Question answered from packet cache", "tag", Logging::Loggable(ctag),
×
UNCOV
2329
                                 "qname", Logging::Loggable(qname), "qtype", Logging::Loggable(QType(qtype)),
×
UNCOV
2330
                                 "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
×
UNCOV
2331
        }
×
2332
        struct msghdr msgh{};
2,707✔
2333
        struct iovec iov{};
2,707✔
2334
        cmsgbuf_aligned cbuf{};
2,707✔
2335
        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,707✔
2336
        msgh.msg_control = nullptr;
2,707✔
2337

2338
        if (g_fromtosockets.count(fileDesc) != 0) {
2,707!
2339
          addCMsgSrcAddr(&msgh, &cbuf, &destaddr, 0);
×
2340
        }
×
2341
        int sendErr = sendOnNBSocket(fileDesc, &msgh);
2,707✔
2342
        eventTrace.add(RecEventTrace::AnswerSent);
2,707✔
2343

2344
        if (t_protobufServers.servers && logResponse && (!luaconfsLocal->protobufExportConfig.taggedOnly || (pbData && pbData->d_tagged))) {
2,707!
UNCOV
2345
          protobufLogResponse(qname, qtype, dnsheader, luaconfsLocal, pbData, tval, false, source, destination, mappedSource, ednssubnet, uniqueId, requestorId, deviceId, deviceName, meta, eventTrace, otTrace, policyTags);
×
UNCOV
2346
        }
×
2347

2348
        if (eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_log)) {
2,707!
2349
          SLOG(g_log << Logger::Info << eventTrace.toString() << endl,
×
2350
               g_slogudpin->info(Logr::Info, eventTrace.toString())); // Do we want more fancy logging here?
×
2351
        }
×
2352
        if (sendErr != 0 && g_logCommonErrors) {
2,707!
2353
          SLOG(g_log << Logger::Warning << "Sending UDP reply to client " << source.toStringWithPort()
×
2354
                     << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << " failed with: "
×
2355
                     << stringerror(sendErr) << endl,
×
2356
               g_slogudpin->error(Logr::Error, sendErr, "Sending UDP reply to client failed", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
×
2357
        }
×
2358
        struct timeval now{};
2,707✔
2359
        Utility::gettimeofday(&now, nullptr);
2,707✔
2360
        uint64_t spentUsec = uSec(now - tval);
2,707✔
2361
        t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
2,707✔
2362
        t_Counters.updateSnap(g_regressionTestMode);
2,707✔
2363
        return nullptr;
2,707✔
2364
      }
2,707✔
2365
    }
4,422✔
2366
  }
4,413✔
2367
  catch (const std::exception& e) {
4,413✔
2368
    if (g_logCommonErrors) {
×
2369
      SLOG(g_log << Logger::Error << "Error processing or aging answer packet: " << e.what() << endl,
×
2370
           g_slogudpin->error(Logr::Error, e.what(), "Error processing or aging answer packet", "exception", Logging::Loggable("std::exception")));
×
2371
    }
×
2372
    return nullptr;
×
2373
  }
×
2374

2375
  if (t_pdl) {
1,708!
UNCOV
2376
    bool ipf = t_pdl->ipfilter(source, destination, *dnsheader, eventTrace);
×
UNCOV
2377
    if (ipf) {
×
UNCOV
2378
      if (!g_quiet) {
×
UNCOV
2379
        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,
×
UNCOV
2380
             g_slogudpin->info(Logr::Notice, "Dropped question based on policy", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
×
UNCOV
2381
      }
×
UNCOV
2382
      t_Counters.at(rec::Counter::policyDrops)++;
×
UNCOV
2383
      return nullptr;
×
UNCOV
2384
    }
×
UNCOV
2385
  }
×
2386

2387
  if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
1,708!
UNCOV
2388
    if (!isAllowNotifyForZone(qname)) {
×
2389
      if (!g_quiet) {
×
2390
        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,
×
2391
             g_slogudpin->info(Logr::Notice, "Dropping UDP NOTIFY, zone not matched by allow-notify-for", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
×
2392
      }
×
2393

2394
      t_Counters.at(rec::Counter::zoneDisallowedNotify)++;
×
2395
      return nullptr;
×
2396
    }
×
2397

UNCOV
2398
    if (!g_quiet) {
×
UNCOV
2399
      SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " got NOTIFY for " << qname.toLogString() << " from " << source.toStringWithPort() << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << endl,
×
UNCOV
2400
           g_slogudpin->info(Logr::Notice, "Got NOTIFY", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr), "qname", Logging::Loggable(qname)));
×
UNCOV
2401
    }
×
UNCOV
2402
    if (!ZoneXFR::notifyZoneTracker(qname)) {
×
2403
      // It wasn't an RPZ
UNCOV
2404
      requestWipeCaches(qname);
×
UNCOV
2405
    }
×
2406

2407
    // the operation will now be treated as a Query, generating
2408
    // a normal response, as the rest of the code does not
2409
    // check dh->opcode, but we need to ensure that the response
2410
    // to this request does not get put into the packet cache
UNCOV
2411
    variable = true;
×
UNCOV
2412
  }
×
2413

2414
  if (g_multiTasker->numProcesses() >= g_maxMThreads) {
1,708!
2415
    if (!g_quiet) {
×
2416
      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,
×
2417
           g_slogudpin->info(Logr::Notice, "Dropped question, over capacity", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
×
2418
    }
×
2419
    t_Counters.at(rec::Counter::overCapacityDrops)++;
×
2420
    return nullptr;
×
2421
  }
×
2422

2423
  auto comboWriter = std::make_unique<DNSComboWriter>(question, g_now, std::move(policyTags), t_pdl, std::move(data), std::move(records));
1,708✔
2424

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

2456
  comboWriter->d_eventTrace = std::move(eventTrace);
1,708✔
2457
  comboWriter->d_otTrace = std::move(otTrace);
1,708✔
2458

2459
  g_multiTasker->makeThread(startDoResolve, (void*)comboWriter.release()); // deletes dc
1,708✔
2460

2461
  return nullptr;
1,708✔
2462
}
1,708✔
2463

2464
static void handleNewUDPQuestion(int fileDesc, FDMultiplexer::funcparam_t& /* var */) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
2465
{
45✔
2466
  const bool proxyActive = t_proxyProtocolACL && !t_proxyProtocolACL->empty();
45!
2467
  static const size_t maxIncomingQuerySize = !proxyActive ? 512 : (512 + g_proxyProtocolMaximumSize);
45✔
2468
  static thread_local std::string data;
45✔
2469
  ComboAddress fromaddr; // the address the query is coming from
45✔
2470
  ComboAddress source; // the address we assume the query is coming from, might be set by proxy protocol
45✔
2471
  ComboAddress destination; // the address we assume the query was sent to, might be set by proxy protocol
45✔
2472
  struct msghdr msgh{};
45✔
2473
  struct iovec iov{};
45✔
2474
  cmsgbuf_aligned cbuf;
45✔
2475
  bool firstQuery = true;
45✔
2476
  std::vector<ProxyProtocolValue> proxyProtocolValues;
45✔
2477
  RecEventTrace eventTrace;
45✔
2478
  pdns::trace::Span otTrace;
45✔
2479

2480
  for (size_t queriesCounter = 0; queriesCounter < g_maxUDPQueriesPerRound; queriesCounter++) {
4,545!
2481
    bool proxyProto = false;
4,545✔
2482
    proxyProtocolValues.clear();
4,545✔
2483
    data.resize(maxIncomingQuerySize);
4,545✔
2484
    fromaddr.sin6.sin6_family = AF_INET6; // this makes sure fromaddr is big enough
4,545✔
2485
    fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), data.data(), data.size(), &fromaddr);
4,545✔
2486

2487
    if (ssize_t len = recvmsg(fileDesc, &msgh, 0); len >= 0) {
4,545✔
2488
      eventTrace.clear();
4,500✔
2489
      eventTrace.setEnabled(SyncRes::s_event_trace_enabled != 0);
4,500✔
2490
      // eventTrace uses monotonic time, while OpenTelemetry uses absolute time. setEnabled()
2491
      // established the reference point, get an absolute TS as close as possible to the
2492
      // eventTrace start of trace time.
2493
      auto traceTS = pdns::trace::timestamp();
4,500✔
2494
      eventTrace.add(RecEventTrace::ReqRecv);
4,500✔
2495
      if (SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
4,500!
2496
        otTrace.clear();
×
2497
        otTrace.start_time_unix_nano = traceTS;
×
2498
        otTrace.name = "RecRequest";
×
2499
      }
×
2500
      firstQuery = false;
4,500✔
2501

2502
      if ((msgh.msg_flags & MSG_TRUNC) != 0) {
4,500!
2503
        t_Counters.at(rec::Counter::truncatedDrops)++;
×
2504
        if (!g_quiet) {
×
2505
          SLOG(g_log << Logger::Error << "Ignoring truncated query from " << fromaddr.toString() << endl,
×
2506
               g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr)));
×
2507
        }
×
2508
        return;
×
2509
      }
×
2510

2511
      data.resize(static_cast<size_t>(len));
4,500✔
2512

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

UNCOV
2554
        data.erase(0, used);
×
UNCOV
2555
      }
×
2556
      else if (len > 512) {
4,500!
2557
        /* we only allow UDP packets larger than 512 for those with a proxy protocol header */
2558
        t_Counters.at(rec::Counter::truncatedDrops)++;
×
2559
        if (!g_quiet) {
×
2560
          SLOG(g_log << Logger::Error << "Ignoring truncated query from " << fromaddr.toStringWithPort() << endl,
×
2561
               g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr)));
×
2562
        }
×
2563
        return;
×
2564
      }
×
2565

2566
      if (data.size() < sizeof(dnsheader)) {
4,500!
2567
        t_Counters.at(rec::Counter::ignoredCount)++;
×
2568
        if (!g_quiet) {
×
2569
          SLOG(g_log << Logger::Error << "Ignoring too-short (" << std::to_string(data.size()) << ") query from " << fromaddr.toString() << endl,
×
2570
               g_slogudpin->info(Logr::Error, "Ignoring too-short query", "length", Logging::Loggable(data.size()),
×
2571
                                 "remote", Logging::Loggable(fromaddr)));
×
2572
        }
×
2573
        return;
×
2574
      }
×
2575

2576
      if (!proxyProto) {
4,500!
2577
        source = fromaddr;
4,500✔
2578
      }
4,500✔
2579
      ComboAddress mappedSource = source;
4,500✔
2580
      if (t_proxyMapping) {
4,500!
UNCOV
2581
        if (const auto* iter = t_proxyMapping->lookup(source)) {
×
UNCOV
2582
          mappedSource = iter->second.address;
×
UNCOV
2583
          ++iter->second.stats.netmaskMatches;
×
UNCOV
2584
        }
×
UNCOV
2585
      }
×
2586
      if (t_remotes) {
4,500!
2587
        t_remotes->push_back(source);
4,500✔
2588
      }
4,500✔
2589

2590
      if (t_allowFrom && !t_allowFrom->match(&mappedSource)) {
4,500!
UNCOV
2591
        if (!g_quiet) {
×
UNCOV
2592
          SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping UDP query from " << mappedSource.toString() << ", address not matched by allow-from" << endl,
×
UNCOV
2593
               g_slogudpin->info(Logr::Error, "Dropping UDP query, address not matched by allow-from", "source", Logging::Loggable(mappedSource)));
×
UNCOV
2594
        }
×
2595

UNCOV
2596
        t_Counters.at(rec::Counter::unauthorizedUDP)++;
×
UNCOV
2597
        return;
×
UNCOV
2598
      }
×
2599

2600
      BOOST_STATIC_ASSERT(offsetof(sockaddr_in, sin_port) == offsetof(sockaddr_in6, sin6_port));
4,500✔
2601
      if (fromaddr.sin4.sin_port == 0) { // also works for IPv6
4,500!
2602
        if (!g_quiet) {
×
2603
          SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping UDP query from " << fromaddr.toStringWithPort() << ", can't deal with port 0" << endl,
×
2604
               g_slogudpin->info(Logr::Error, "Dropping UDP query can't deal with port 0", "remote", Logging::Loggable(fromaddr)));
×
2605
        }
×
2606

2607
        t_Counters.at(rec::Counter::clientParseError)++; // not quite the best place to put it, but needs to go somewhere
×
2608
        return;
×
2609
      }
×
2610

2611
      try {
4,500✔
2612
        const dnsheader_aligned headerdata(data.data());
4,500✔
2613
        const dnsheader* dnsheader = headerdata.get();
4,500✔
2614

2615
        if (dnsheader->qr) {
4,500!
2616
          t_Counters.at(rec::Counter::ignoredCount)++;
×
2617
          if (g_logCommonErrors) {
×
2618
            SLOG(g_log << Logger::Error << "Ignoring answer from " << fromaddr.toString() << " on server socket!" << endl,
×
2619
                 g_slogudpin->info(Logr::Error, "Ignoring answer on server socket", "remote", Logging::Loggable(fromaddr)));
×
2620
          }
×
2621
        }
×
2622
        else if (dnsheader->opcode != static_cast<unsigned>(Opcode::Query) && dnsheader->opcode != static_cast<unsigned>(Opcode::Notify)) {
4,500!
UNCOV
2623
          t_Counters.at(rec::Counter::ignoredCount)++;
×
UNCOV
2624
          if (g_logCommonErrors) {
×
UNCOV
2625
            SLOG(g_log << Logger::Error << "Ignoring unsupported opcode " << Opcode::to_s(dnsheader->opcode) << " from " << fromaddr.toString() << " on server socket!" << endl,
×
UNCOV
2626
                 g_slogudpin->info(Logr::Error, "Ignoring unsupported opcode server socket", "remote", Logging::Loggable(fromaddr), "opcode", Logging::Loggable(Opcode::to_s(dnsheader->opcode))));
×
UNCOV
2627
          }
×
UNCOV
2628
        }
×
2629
        else if (dnsheader->qdcount == 0U) {
4,500!
2630
          t_Counters.at(rec::Counter::emptyQueriesCount)++;
×
2631
          if (g_logCommonErrors) {
×
2632
            SLOG(g_log << Logger::Error << "Ignoring empty (qdcount == 0) query from " << fromaddr.toString() << " on server socket!" << endl,
×
2633
                 g_slogudpin->info(Logr::Error, "Ignoring empty (qdcount == 0) query on server socket!", "remote", Logging::Loggable(fromaddr)));
×
2634
          }
×
2635
        }
×
2636
        else {
4,500✔
2637
          if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
4,500!
UNCOV
2638
            if (!t_allowNotifyFrom || !t_allowNotifyFrom->match(&mappedSource)) {
×
2639
              if (!g_quiet) {
×
2640
                SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping UDP NOTIFY from " << mappedSource.toString() << ", address not matched by allow-notify-from" << endl,
×
2641
                     g_slogudpin->info(Logr::Error, "Dropping UDP NOTIFY from address not matched by allow-notify-from",
×
2642
                                       "source", Logging::Loggable(mappedSource)));
×
2643
              }
×
2644

2645
              t_Counters.at(rec::Counter::sourceDisallowedNotify)++;
×
2646
              return;
×
2647
            }
×
UNCOV
2648
          }
×
2649

2650
          struct timeval tval = {0, 0};
4,500✔
2651
          HarvestTimestamp(&msgh, &tval);
4,500✔
2652
          if (!proxyProto) {
4,500!
2653
            destination = destaddr;
4,500✔
2654
          }
4,500✔
2655

2656
          if (RecThreadInfo::weDistributeQueries()) {
4,500✔
2657
            std::string localdata = data;
3,600✔
2658
            distributeAsyncFunction(data, [localdata = std::move(localdata), fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace]() mutable {
3,600✔
2659
              return doProcessUDPQuestion(localdata, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace);
3,566✔
2660
            });
3,566✔
2661
          }
3,600✔
2662
          else {
900✔
2663
            doProcessUDPQuestion(data, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace, otTrace);
900✔
2664
          }
900✔
2665
        }
4,500✔
2666
      }
4,500✔
2667
      catch (const MOADNSException& mde) {
4,500✔
2668
        t_Counters.at(rec::Counter::clientParseError)++;
×
2669
        if (g_logCommonErrors) {
×
2670
          SLOG(g_log << Logger::Error << "Unable to parse packet from remote UDP client " << fromaddr.toString() << ": " << mde.what() << endl,
×
2671
               g_slogudpin->error(Logr::Error, mde.what(), "Unable to parse packet from remote UDP client", "remote", Logging::Loggable(fromaddr), "exception", Logging::Loggable("MOADNSException")));
×
2672
        }
×
2673
      }
×
2674
      catch (const std::runtime_error& e) {
4,500✔
2675
        t_Counters.at(rec::Counter::clientParseError)++;
×
2676
        if (g_logCommonErrors) {
×
2677
          SLOG(g_log << Logger::Error << "Unable to parse packet from remote UDP client " << fromaddr.toString() << ": " << e.what() << endl,
×
2678
               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")));
×
2679
        }
×
2680
      }
×
2681
    }
4,500✔
2682
    else {
45✔
2683
      // cerr<<t_id<<" had error: "<<stringerror()<<endl;
2684
      if (firstQuery && errno == EAGAIN) {
45!
2685
        t_Counters.at(rec::Counter::noPacketError)++;
×
2686
      }
×
2687

2688
      break;
45✔
2689
    }
45✔
2690
  }
4,545✔
2691
  t_Counters.updateSnap(g_regressionTestMode);
45✔
2692
}
45✔
2693

2694
// The two last arguments to makeUDPServerSockets are used for logging purposes only
2695
unsigned int makeUDPServerSockets(deferredAdd_t& deferredAdds, Logr::log_t log, bool doLog, unsigned int instances)
2696
{
15✔
2697
  int one = 1;
15✔
2698
  vector<string> localAddresses;
15✔
2699
  vector<string> logVec;
15✔
2700
  stringtok(localAddresses, ::arg()["local-address"], " ,");
15✔
2701

2702
  if (localAddresses.empty()) {
15!
2703
    throw PDNSException("No local address specified");
×
2704
  }
×
2705

2706
  const uint16_t defaultLocalPort = ::arg().asNum("local-port");
15✔
2707
  for (const auto& localAddress : localAddresses) {
15✔
2708
    ComboAddress address{localAddress, defaultLocalPort};
15✔
2709
    const int socketFd = socket(address.sin4.sin_family, SOCK_DGRAM, 0);
15✔
2710
    if (socketFd < 0) {
15!
2711
      throw PDNSException("Making a UDP server socket for resolver: " + stringerror());
×
2712
    }
×
2713
    logVec.emplace_back(address.toStringWithPort());
15✔
2714
    if (!setSocketTimestamps(socketFd)) {
15!
2715
      SLOG(g_log << Logger::Warning << "Unable to enable timestamp reporting for socket" << endl,
×
2716
           log->info(Logr::Warning, "Unable to enable timestamp reporting for socket"));
×
2717
    }
×
2718
    if (IsAnyAddress(address)) {
15!
UNCOV
2719
      if (address.sin4.sin_family == AF_INET) {
×
UNCOV
2720
        if (setsockopt(socketFd, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)) == 0) { // linux supports this, so why not - might fail on other systems
×
UNCOV
2721
          g_fromtosockets.insert(socketFd);
×
UNCOV
2722
        }
×
UNCOV
2723
      }
×
UNCOV
2724
#ifdef IPV6_RECVPKTINFO
×
UNCOV
2725
      if (address.sin4.sin_family == AF_INET6) {
×
2726
        if (setsockopt(socketFd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)) == 0) {
×
2727
          g_fromtosockets.insert(socketFd);
×
2728
        }
×
2729
      }
×
UNCOV
2730
#endif
×
UNCOV
2731
      if (address.sin6.sin6_family == AF_INET6 && setsockopt(socketFd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)) < 0) {
×
2732
        int err = errno;
×
2733
        SLOG(g_log << Logger::Warning << "Failed to set IPv6 socket to IPv6 only, continuing anyhow: " << stringerror(err) << endl,
×
2734
             log->error(Logr::Warning, err, "Failed to set IPv6 socket to IPv6 only, continuing anyhow"));
×
2735
      }
×
UNCOV
2736
    }
×
2737
    if (::arg().mustDo("non-local-bind")) {
15!
2738
      Utility::setBindAny(AF_INET6, socketFd);
×
2739
    }
×
2740

2741
    setCloseOnExec(socketFd);
15✔
2742

2743
    try {
15✔
2744
      setSocketReceiveBuffer(socketFd, 250000);
15✔
2745
    }
15✔
2746
    catch (const std::exception& e) {
15✔
2747
      SLOG(g_log << Logger::Error << e.what() << endl,
×
2748
           log->error(Logr::Error, e.what(), "Exception while setting socket buffer size"));
×
2749
    }
×
2750

2751
    if (g_reusePort) {
15!
2752
#if defined(SO_REUSEPORT_LB)
2753
      try {
2754
        SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
2755
      }
2756
      catch (const std::exception& e) {
2757
        throw PDNSException(std::string("SO_REUSEPORT_LB: ") + e.what());
2758
      }
2759
#elif defined(SO_REUSEPORT)
UNCOV
2760
      try {
×
UNCOV
2761
        SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT, 1);
×
UNCOV
2762
      }
×
UNCOV
2763
      catch (const std::exception& e) {
×
2764
        throw PDNSException(std::string("SO_REUSEPORT: ") + e.what());
×
2765
      }
×
UNCOV
2766
#endif
×
UNCOV
2767
    }
×
2768

2769
    try {
15✔
2770
      setSocketIgnorePMTU(socketFd, address.sin4.sin_family);
15✔
2771
    }
15✔
2772
    catch (const std::exception& e) {
15✔
2773
      SLOG(g_log << Logger::Warning << "Failed to set IP_MTU_DISCOVER on UDP server socket: " << e.what() << endl,
×
2774
           log->error(Logr::Warning, e.what(), "Failed to set IP_MTU_DISCOVER on UDP server socket"));
×
2775
    }
×
2776

2777
    socklen_t socklen = address.getSocklen();
15✔
2778
    if (::bind(socketFd, reinterpret_cast<struct sockaddr*>(&address), socklen) < 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
15!
2779
      throw PDNSException("Resolver binding to server socket on " + address.toStringWithPort() + ": " + stringerror());
×
2780
    }
×
2781

2782
    setNonBlocking(socketFd);
15✔
2783

2784
    deferredAdds.emplace_back(socketFd, handleNewUDPQuestion);
15✔
2785
    g_listenSocketsAddresses[socketFd] = address; // this is written to only from the startup thread, not from the workers
15✔
2786
  }
15✔
2787
  if (doLog) {
15!
2788
    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));
15✔
2789
  }
15✔
2790
  return localAddresses.size();
15✔
2791
}
15✔
2792

2793
static bool trySendingQueryToWorker(unsigned int target, ThreadMSG* tmsg)
2794
{
3,600✔
2795
  auto& targetInfo = RecThreadInfo::info(target);
3,600✔
2796
  if (!targetInfo.isWorker()) {
3,600!
2797
    SLOG(g_log << Logger::Error << "distributeAsyncFunction() tried to assign a query to a non-worker thread" << endl,
×
2798
         g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() tried to assign a query to a non-worker thread"));
×
2799
    _exit(1);
×
2800
  }
×
2801

2802
  const auto& tps = targetInfo.getPipes();
3,600✔
2803

2804
  ssize_t written = write(tps.writeQueriesToThread, &tmsg, sizeof(tmsg)); // NOLINT: correct sizeof
3,600✔
2805
  if (written > 0) {
3,600!
2806
    if (static_cast<size_t>(written) != sizeof(tmsg)) { // NOLINT: correct sizeof
3,600!
2807
      delete tmsg; // NOLINT: pointer ownership
×
2808
      unixDie("write to thread pipe returned wrong size or error");
×
2809
    }
×
2810
  }
3,600✔
2811
  else {
×
2812
    int error = errno;
×
2813
    if (error == EAGAIN || error == EWOULDBLOCK) {
×
2814
      return false;
×
2815
    }
×
2816
    delete tmsg; // NOLINT: pointer ownership
×
2817
    unixDie("write to thread pipe returned wrong size or error:" + std::to_string(error));
×
2818
  }
×
2819

2820
  return true;
3,600✔
2821
}
3,600✔
2822

2823
static unsigned int getWorkerLoad(size_t workerIdx)
2824
{
×
2825
  const auto* multiThreader = RecThreadInfo::info(RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + workerIdx).getMT();
×
2826
  if (multiThreader != nullptr) {
×
2827
    return multiThreader->numProcesses();
×
2828
  }
×
2829
  return 0;
×
2830
}
×
2831

2832
static unsigned int selectWorker(unsigned int hash)
2833
{
3,600✔
2834
  assert(RecThreadInfo::numUDPWorkers() != 0); // NOLINT: assert implementation
3,600✔
2835
  if (g_balancingFactor == 0) {
3,600!
2836
    return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + (hash % RecThreadInfo::numUDPWorkers());
3,600✔
2837
  }
3,600✔
2838

2839
  /* we start with one, representing the query we are currently handling */
2840
  double currentLoad = 1;
×
2841
  std::vector<unsigned int> load(RecThreadInfo::numUDPWorkers());
×
2842
  for (size_t idx = 0; idx < RecThreadInfo::numUDPWorkers(); idx++) {
×
2843
    load[idx] = getWorkerLoad(idx);
×
2844
    currentLoad += load[idx];
×
2845
  }
×
2846

2847
  double targetLoad = (currentLoad / RecThreadInfo::numUDPWorkers()) * g_balancingFactor;
×
2848

2849
  unsigned int worker = hash % RecThreadInfo::numUDPWorkers();
×
2850
  /* at least one server has to be at or below the average load */
2851
  if (load[worker] > targetLoad) {
×
2852
    ++t_Counters.at(rec::Counter::rebalancedQueries);
×
2853
    do {
×
2854
      worker = (worker + 1) % RecThreadInfo::numUDPWorkers();
×
2855
    } while (load[worker] > targetLoad);
×
2856
  }
×
2857

2858
  return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + worker;
×
2859
}
3,600✔
2860

2861
// This function is only called by the distributor threads, when pdns-distributes-queries is set
2862
void distributeAsyncFunction(const string& packet, const pipefunc_t& func)
2863
{
3,600✔
2864
  if (!RecThreadInfo::self().isDistributor()) {
3,600!
2865
    SLOG(g_log << Logger::Error << "distributeAsyncFunction() has been called by a worker (" << RecThreadInfo::id() << ")" << endl,
×
2866
         g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() has been called by a worker")); // tid will be added
×
2867
    _exit(1);
×
2868
  }
×
2869

2870
  bool hashOK = false;
3,600✔
2871
  unsigned int hash = hashQuestion(reinterpret_cast<const uint8_t*>(packet.data()), packet.length(), g_disthashseed, hashOK); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
3,600✔
2872
  if (!hashOK) {
3,600!
2873
    // hashQuestion does detect invalid names, so we might as well punt here instead of in the worker thread
2874
    t_Counters.at(rec::Counter::ignoredCount)++;
×
2875
    throw MOADNSException("too-short (" + std::to_string(packet.length()) + ") or invalid name");
×
2876
  }
×
2877
  unsigned int target = selectWorker(hash);
3,600✔
2878

2879
  ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: pointer ownership
3,600✔
2880
  tmsg->func = func;
3,600✔
2881
  tmsg->wantAnswer = false;
3,600✔
2882

2883
  if (!trySendingQueryToWorker(target, tmsg)) {
3,600!
2884
    /* if this function failed but did not raise an exception, it means that the pipe
2885
       was full, let's try another one */
2886
    unsigned int newTarget = 0;
×
2887
    do {
×
2888
      newTarget = RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + dns_random(RecThreadInfo::numUDPWorkers());
×
2889
    } while (newTarget == target);
×
2890

2891
    if (!trySendingQueryToWorker(newTarget, tmsg)) {
×
2892
      t_Counters.at(rec::Counter::queryPipeFullDrops)++;
×
2893
      delete tmsg; // NOLINT: pointer ownership
×
2894
    }
×
2895
  }
×
2896
  // coverity[leaked_storage]
2897
}
3,600✔
2898

2899
// resend event to everybody chained onto it
2900
static void doResends(MT_t::waiters_t::iterator& iter, const std::shared_ptr<PacketID>& resend, const PacketBuffer& content)
2901
{
7,723✔
2902
  // We close the chain for new entries, since they won't be processed anyway
2903
  iter->key->closed = true;
7,723✔
2904

2905
  if (iter->key->authReqChain.empty()) {
7,723✔
2906
    return;
7,352✔
2907
  }
7,352✔
2908

2909
  auto maxWeight = t_Counters.at(rec::Counter::maxChainWeight);
371✔
2910
  auto weight = iter->key->authReqChain.size() * content.size();
371✔
2911
  if (weight > maxWeight) {
371✔
2912
    t_Counters.at(rec::Counter::maxChainWeight) = weight;
82✔
2913
  }
82✔
2914

2915
  for (auto [fileDesc, qid] : iter->key->authReqChain) {
497✔
2916
    auto packetID = std::make_shared<PacketID>(*resend);
497✔
2917
    packetID->fd = fileDesc;
497✔
2918
    packetID->id = qid;
497✔
2919
    g_multiTasker->sendEvent(packetID, &content);
497✔
2920
    t_Counters.at(rec::Counter::chainResends)++;
497✔
2921
  }
497✔
2922
}
371✔
2923

2924
void mthreadSleep(unsigned int jitterMsec)
2925
{
×
2926
  auto neverHappens = std::make_shared<PacketID>();
×
2927
  neverHappens->id = dns_random_uint16();
×
2928
  neverHappens->type = dns_random_uint16();
×
2929
  neverHappens->remote = ComboAddress("100::"); // discard-only
×
2930
  neverHappens->remote.setPort(dns_random_uint16());
×
2931
  neverHappens->fd = -1;
×
2932
  assert(g_multiTasker->waitEvent(neverHappens, nullptr, jitterMsec) != -1); // NOLINT
×
2933
}
×
2934

2935
static void handleUDPServerResponse(int fileDesc, FDMultiplexer::funcparam_t& var)
2936
{
7,723✔
2937
  auto pid = boost::any_cast<std::shared_ptr<PacketID>>(var);
7,723✔
2938
  PacketBuffer packet;
7,723✔
2939
  packet.resize(g_outgoingEDNSBufsize);
7,723✔
2940
  ComboAddress fromaddr;
7,723✔
2941
  socklen_t addrlen = sizeof(fromaddr);
7,723✔
2942

2943
  ssize_t len = recvfrom(fileDesc, &packet.at(0), packet.size(), 0, reinterpret_cast<sockaddr*>(&fromaddr), &addrlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
7,723✔
2944

2945
  const ssize_t signed_sizeof_sdnsheader = sizeof(dnsheader);
7,723✔
2946

2947
  if (len < 0) {
7,723!
2948
    // len < 0: error on socket
UNCOV
2949
    t_udpclientsocks->returnSocket(fileDesc);
×
2950

UNCOV
2951
    PacketBuffer empty;
×
UNCOV
2952
    auto iter = g_multiTasker->getWaiters().find(pid);
×
UNCOV
2953
    if (iter != g_multiTasker->getWaiters().end()) {
×
UNCOV
2954
      doResends(iter, pid, empty);
×
UNCOV
2955
    }
×
UNCOV
2956
    g_multiTasker->sendEvent(pid, &empty); // this denotes error (does retry lookup using other NS)
×
UNCOV
2957
    return;
×
UNCOV
2958
  }
×
2959

2960
  if (len < signed_sizeof_sdnsheader) {
7,723!
2961
    // We have received a packet that cannot be a valid DNS packet, as it has no complete header
2962
    // Drop it, but continue to wait for other packets
2963
    t_Counters.at(rec::Counter::serverParseError)++;
×
2964
    if (g_logCommonErrors) {
×
2965
      SLOG(g_log << Logger::Error << "Unable to parse too short packet from remote UDP server " << fromaddr.toString() << ": packet smaller than DNS header" << endl,
×
2966
           g_slogout->info(Logr::Error, "Unable to parse too short packet from remote UDP server", "from", Logging::Loggable(fromaddr)));
×
2967
    }
×
2968
    return;
×
2969
  }
×
2970

2971
  // We have at least a full header
2972
  packet.resize(len);
7,723✔
2973
  dnsheader dnsheader{};
7,723✔
2974
  memcpy(&dnsheader, &packet.at(0), sizeof(dnsheader));
7,723✔
2975

2976
  auto pident = std::make_shared<PacketID>();
7,723✔
2977
  pident->remote = fromaddr;
7,723✔
2978
  pident->id = dnsheader.id;
7,723✔
2979
  pident->fd = fileDesc;
7,723✔
2980

2981
  if (!dnsheader.qr && g_logCommonErrors) {
7,723!
2982
    SLOG(g_log << Logger::Notice << "Not taking data from question on outgoing socket from " << fromaddr.toStringWithPort() << endl,
×
2983
         g_slogout->info(Logr::Error, "Not taking data from question on outgoing socket", "from", Logging::Loggable(fromaddr)));
×
2984
  }
×
2985

2986
  if (dnsheader.qdcount == 0U || // UPC, Nominum, very old BIND on FormErr, NSD
7,723✔
2987
      dnsheader.qr == 0U) { // one weird server
7,724!
2988
    pident->domain.clear();
×
2989
    pident->type = 0;
×
2990
  }
×
2991
  else {
7,723✔
2992
    try {
7,723✔
2993
      if (len > signed_sizeof_sdnsheader) {
7,724✔
2994
        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)
7,724✔
2995
      }
7,724✔
2996
      else {
2,147,483,647✔
2997
        // len == sizeof(dnsheader), only header case
2998
        // We will do a full scan search later to see if we can match this reply even without a domain
2999
        pident->domain.clear();
2,147,483,647✔
3000
        pident->type = 0;
2,147,483,647✔
3001
      }
2,147,483,647✔
3002
    }
7,723✔
3003
    catch (std::exception& e) {
7,723✔
3004
      // Parse error, continue waiting for other packets
3005
      t_Counters.at(rec::Counter::serverParseError)++; // won't be fed to lwres.cc, so we have to increment
×
3006
      SLOG(g_log << Logger::Warning << "Error in packet from remote nameserver " << fromaddr.toStringWithPort() << ": " << e.what() << endl,
×
3007
           g_slogudpin->error(Logr::Warning, e.what(), "Error in packet from remote nameserver", "from", Logging::Loggable(fromaddr)));
×
3008
      return;
×
3009
    }
×
3010
  }
7,723✔
3011

3012
  if (!pident->domain.empty()) {
7,724✔
3013
    auto iter = g_multiTasker->getWaiters().find(pident);
7,723✔
3014
    if (iter != g_multiTasker->getWaiters().end()) {
7,724✔
3015
      doResends(iter, pident, packet);
7,723✔
3016
    }
7,723✔
3017
  }
7,723✔
3018

3019
retryWithName:
7,724✔
3020

3021
  if (pident->domain.empty() || g_multiTasker->sendEvent(pident, &packet) == 0) {
7,724!
3022
    /* we did not find a match for this response, something is wrong */
3023

3024
    // 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
3025
    for (const auto& d_waiter : g_multiTasker->getWaiters()) {
×
3026
      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) {
×
3027
        /* we are expecting an answer from that exact source, on that exact port (since we are using connected sockets), for that qname/qtype,
3028
           but with a different message ID. That smells like a spoofing attempt. For now we will just increase the counter and will deal with
3029
           that later. */
3030
        d_waiter.key->nearMisses++;
×
3031
      }
×
3032

3033
      // be a bit paranoid here since we're weakening our matching
3034
      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) {
×
3035
        // cerr<<"Empty response, rest matches though, sending to a waiter"<<endl;
3036
        pident->domain = d_waiter.key->domain;
×
3037
        pident->type = d_waiter.key->type;
×
3038
        goto retryWithName; // note that this only passes on an error, lwres will still reject the packet NOLINT(cppcoreguidelines-avoid-goto)
×
3039
      }
×
3040
    }
×
3041
    t_Counters.at(rec::Counter::unexpectedCount)++; // if we made it here, it really is an unexpected answer
×
3042
    if (g_logCommonErrors) {
×
3043
      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,
×
3044
           g_slogudpin->info(Logr::Warning, "Discarding unexpected packet", "from", Logging::Loggable(fromaddr),
×
3045
                             "qname", Logging::Loggable(pident->domain),
×
3046
                             "qtype", Logging::Loggable(QType(pident->type)),
×
3047
                             "waiters", Logging::Loggable(g_multiTasker->getWaiters().size())));
×
3048
    }
×
3049
  }
×
3050
  else if (fileDesc >= 0) {
7,724!
3051
    /* we either found a waiter (1) or encountered an issue (-1), it's up to us to clean the socket anyway */
3052
    t_udpclientsocks->returnSocket(fileDesc);
7,724✔
3053
  }
7,724✔
3054
}
7,724✔
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