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

PowerDNS / pdns / 13012068652

28 Jan 2025 01:59PM UTC coverage: 64.71% (+0.01%) from 64.699%
13012068652

Pull #14724

github

web-flow
Merge b15562560 into db18c3a17
Pull Request #14724: dnsdist: Add meson support

38328 of 90334 branches covered (42.43%)

Branch coverage included in aggregate %.

361 of 513 new or added lines in 35 files covered. (70.37%)

42 existing lines in 13 files now uncovered.

128150 of 166934 relevant lines covered (76.77%)

4540890.91 hits per line

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

72.68
/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 struct timeval g_now; // timestamp, updated (too) frequently
63

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

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

87
bool g_lowercaseOutgoing;
88
unsigned int g_networkTimeoutMsec;
89
uint16_t g_outgoingEDNSBufsize;
90

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

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

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

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

120
    return LWResult::Result::PermanentError;
×
121
  }
×
122

123
  d_numsocks++;
7,310✔
124
  return LWResult::Result::Success;
7,310✔
125
}
7,310✔
126

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

137
  try {
7,310✔
138
    closesocket(fileDesc);
7,307✔
139
  }
7,307✔
140
  catch (const PDNSException& e) {
7,307✔
141
    SLOG(g_log << Logger::Error << "Error closing returned UDP socket: " << e.reason << endl,
×
142
         g_slogout->error(Logr::Error, e.reason, "Error closing returned UDP socket", "exception", Logging::Loggable("PDNSException")));
×
143
  }
×
144

145
  --d_numsocks;
7,310✔
146
}
7,306✔
147

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

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

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

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

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

187
  int err = errno;
7,309✔
188

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

194
  try {
7,309✔
195
    setReceiveSocketErrors(ret, family);
7,309✔
196
    setNonBlocking(ret);
7,309✔
197
  }
7,309✔
198
  catch (...) {
7,309✔
199
    closesocket(ret);
×
200
    throw;
×
201
  }
×
202
  return ret;
7,308✔
203
}
7,309✔
204

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

213
  ssize_t ret = recvfrom(fileDesc, resp.data(), resp.size(), 0, reinterpret_cast<sockaddr*>(&fromaddr), &addrlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
4✔
214
  if (fromaddr != pident->remote) {
4!
215
    SLOG(g_log << Logger::Notice << "Response received from the wrong remote host (" << fromaddr.toStringWithPort() << " instead of " << pident->remote.toStringWithPort() << "), discarding" << endl,
×
216
         g_slog->withName("lua")->info(Logr::Notice, "Response received from the wrong remote host. discarding", "method", Logging::Loggable("GenUDPQueryResponse"), "fromaddr", Logging::Loggable(fromaddr), "expected", Logging::Loggable(pident->remote)));
×
217
  }
×
218

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

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

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

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

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

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

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

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

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

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

283
  auto pident = std::make_shared<PacketID>();
8,670✔
284
  pident->domain = domain;
8,670✔
285
  pident->remote = toAddress;
8,670✔
286
  pident->type = qtype;
8,670✔
287

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

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

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

327
  pident->fd = *fileDesc;
7,305✔
328
  pident->id = qid;
7,305✔
329

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

333
  int tmp = errno;
7,305✔
334

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

341
  return LWResult::Result::Success;
7,305✔
342
}
7,305✔
343

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

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

357
  int ret = g_multiTasker->waitEvent(pident, &packet, authWaitTimeMSec(g_multiTasker), &now);
8,674✔
358
  len = 0;
8,674✔
359

360
  /* -1 means error, 0 means timeout, 1 means a result from handleUDPServerResponse() which might still be an error */
361
  if (ret > 0) {
8,675✔
362
    /* handleUDPServerResponse() will close the socket for us no matter what */
363
    if (packet.empty()) { // means "error"
8,668✔
364
      return LWResult::Result::PermanentError;
43✔
365
    }
43✔
366

367
    len = packet.size();
8,625✔
368

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

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

389
  return ret == 0 ? LWResult::Result::Timeout : LWResult::Result::PermanentError;
2,147,483,650✔
390
}
8,674✔
391

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

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

443
static bool addRecordToPacket(DNSPacketWriter& packetWritewr, const DNSRecord& rec, uint32_t& minTTL, uint32_t ttlCap, const uint16_t maxAnswerSize, bool& seenAuthSOA)
444
{
7,154✔
445
  packetWritewr.startRecord(rec.d_name, rec.d_type, (rec.d_ttl > ttlCap ? ttlCap : rec.d_ttl), rec.d_class, rec.d_place);
7,154!
446

447
  if (rec.d_type == QType::SOA && rec.d_place == DNSResourceRecord::AUTHORITY) {
7,154✔
448
    seenAuthSOA = true;
353✔
449
  }
353✔
450

451
  if (rec.d_type != QType::OPT) { // their TTL ain't real
7,154!
452
    minTTL = min(minTTL, rec.d_ttl);
7,154✔
453
  }
7,154✔
454

455
  rec.getContent()->toPacket(packetWritewr);
7,154✔
456
  if (packetWritewr.size() > static_cast<size_t>(maxAnswerSize)) {
7,154✔
457
    packetWritewr.rollback();
3✔
458
    if (rec.d_place != DNSResourceRecord::ADDITIONAL) {
3!
459
      packetWritewr.getHeader()->tc = 1;
3✔
460
      packetWritewr.truncate();
3✔
461
    }
3✔
462
    return false;
3✔
463
  }
3✔
464

465
  return true;
7,151✔
466
}
7,154✔
467

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

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

514
enum class PolicyResult : uint8_t
515
{
516
  NoAction,
517
  HaveAnswer,
518
  Drop
519
};
520

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

529
  if (resolver.doLog() && appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
89!
530
    SLOG(g_log << Logger::Warning << comboWriter->d_mdp.d_qname << "|" << QType(comboWriter->d_mdp.d_qtype) << appliedPolicy.getLogString() << endl,
6✔
531
         appliedPolicy.info(Logr::Warning, resolver.d_slog));
6✔
532
  }
6✔
533

534
  if (appliedPolicy.d_zoneData && appliedPolicy.d_zoneData->d_extendedErrorCode) {
89!
535
    comboWriter->d_extendedErrorCode = *appliedPolicy.d_zoneData->d_extendedErrorCode;
×
536
    comboWriter->d_extendedErrorExtra = appliedPolicy.d_zoneData->d_extendedErrorExtra;
×
537
  }
×
538

539
  switch (appliedPolicy.d_kind) {
89!
540

541
  case DNSFilterEngine::PolicyKind::NoAction:
75✔
542
    return PolicyResult::NoAction;
75✔
543

544
  case DNSFilterEngine::PolicyKind::Drop:
7✔
545
    tcpGuard.setDropOnIdle();
7✔
546
    ++t_Counters.at(rec::Counter::policyDrops);
7✔
547
    return PolicyResult::Drop;
7✔
548

549
  case DNSFilterEngine::PolicyKind::NXDOMAIN:
1✔
550
    ret.clear();
1✔
551
    appliedPolicy.addSOAtoRPZResult(ret);
1✔
552
    res = RCode::NXDomain;
1✔
553
    return PolicyResult::HaveAnswer;
1✔
554

555
  case DNSFilterEngine::PolicyKind::NODATA:
1✔
556
    ret.clear();
1✔
557
    appliedPolicy.addSOAtoRPZResult(ret);
1✔
558
    res = RCode::NoError;
1✔
559
    return PolicyResult::HaveAnswer;
1✔
560

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

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

609
      appliedPolicy.addSOAtoRPZResult(ret);
4✔
610
      return PolicyResult::HaveAnswer;
4✔
611
    }
4✔
612
  }
89✔
613

614
  return PolicyResult::NoAction;
×
615
}
89✔
616

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

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

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

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

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

704
  if (target.empty()) {
4!
705
    return rcode;
×
706
  }
×
707

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

711
  if (g_dns64Prefix && qtype == QType::AAAA && dns64Candidate(qtype, rcode, resolved)) {
4!
712
    rcode = getFakeAAAARecords(target, *g_dns64Prefix, resolved);
2✔
713
  }
2✔
714

715
  for (DNSRecord& record : resolved) {
9✔
716
    if (record.d_place == DNSResourceRecord::ANSWER) {
9!
717
      ret.push_back(std::move(record));
9✔
718
    }
9✔
719
  }
9✔
720
  return rcode;
4✔
721
}
4✔
722

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

732
  ret.reserve(ret.size() + newRecords.size());
18✔
733
  for (auto& record : newRecords) {
31✔
734
    ret.push_back(std::move(record));
31✔
735
  }
31✔
736

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

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

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

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

796
  if (parts.size() < 8) {
2!
797
    return -1;
×
798
  }
×
799

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

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

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

819
  ret.insert(ret.end(), answers.begin(), answers.end());
2✔
820

821
  t_Counters.at(rec::Counter::dns64prefixanswers)++;
2✔
822
  return rcode;
2✔
823
}
2✔
824

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

835
bool isAllowNotifyForZone(DNSName qname)
836
{
34✔
837
  if (t_allowNotifyFor->empty()) {
34✔
838
    return false;
4✔
839
  }
4✔
840

841
  do {
40✔
842
    auto ret = t_allowNotifyFor->find(qname);
40✔
843
    if (ret != t_allowNotifyFor->end()) {
40✔
844
      return true;
25✔
845
    }
25✔
846
  } while (qname.chopOff());
40✔
847
  return false;
5✔
848
}
30✔
849

850
#if defined(HAVE_FSTRM) && defined(NOD_ENABLED)
851
#include "dnstap.hh"
852
#include "fstrm_logger.hh"
853

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

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

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

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

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

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

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

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

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

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

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

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

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

1054
    DNSPacketWriter packetWriter(packet, comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype, comboWriter->d_mdp.d_qclass, comboWriter->d_mdp.d_header.opcode);
2,390✔
1055

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

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

1071
    resolver.d_eventTrace = std::move(comboWriter->d_eventTrace);
2,390✔
1072
    resolver.setId(g_multiTasker->getTid());
2,390✔
1073

1074
    bool DNSSECOK = false;
2,390✔
1075
    if (comboWriter->d_luaContext) {
2,390✔
1076
      resolver.setLuaEngine(comboWriter->d_luaContext);
408✔
1077
    }
408✔
1078
    if (g_dnssecmode != DNSSECMode::Off) {
2,390✔
1079
      resolver.setDoDNSSEC(true);
2,366✔
1080

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

1108
    resolver.setInitialRequestId(comboWriter->d_uuid);
2,390✔
1109
    resolver.setOutgoingProtobufServers(t_outgoingProtobufServers.servers);
2,390✔
1110
#ifdef HAVE_FSTRM
2,390✔
1111
    resolver.setFrameStreamServers(t_frameStreamServersInfo.servers);
2,390✔
1112
#endif
2,390✔
1113

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

1134
    resolver.setQueryReceivedOverTCP(comboWriter->d_tcp);
2,390✔
1135

1136
    bool tracedQuery = false; // we could consider letting Lua know about this too
2,390✔
1137
    bool shouldNotValidate = false;
2,390✔
1138

1139
    /* preresolve expects res (dq.rcode) to be set to RCode::NoError by default */
1140
    int res = RCode::NoError;
2,390✔
1141

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

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

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

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

1181
    if (t_traceRegex && t_traceRegex->match(comboWriter->d_mdp.d_qname.toString())) {
2,366!
1182
      resolver.setLogMode(SyncRes::Store);
×
1183
      tracedQuery = true;
×
1184
    }
×
1185

1186
    if (!g_quiet || tracedQuery) {
2,366✔
1187
      if (!g_slogStructured) {
823✔
1188
        g_log << Logger::Warning << RecThreadInfo::id() << " [" << g_multiTasker->getTid() << "/" << g_multiTasker->numProcesses() << "] " << (comboWriter->d_tcp ? "TCP " : "") << "question for '" << comboWriter->d_mdp.d_qname << "|"
×
1189
              << QType(comboWriter->d_mdp.d_qtype) << "' from " << comboWriter->getRemote();
×
1190
        if (!comboWriter->d_ednssubnet.getSource().empty()) {
×
1191
          g_log << " (ecs " << comboWriter->d_ednssubnet.getSource().toString() << ")";
×
1192
        }
×
1193
        g_log << endl;
×
1194
      }
×
1195
      else {
823✔
1196
        resolver.d_slog->info(Logr::Info, "Question");
823✔
1197
      }
823✔
1198
    }
823✔
1199

1200
    if (!comboWriter->d_mdp.d_header.rd) {
2,366✔
1201
      if (g_allowNoRD) {
3✔
1202
        resolver.setCacheOnly();
2✔
1203
      }
2✔
1204
      else {
1✔
1205
        ret.clear();
1✔
1206
        res = RCode::Refused;
1✔
1207
        goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
1✔
1208
      }
1✔
1209
    }
3✔
1210

1211
    if (comboWriter->d_luaContext) {
2,365✔
1212
      comboWriter->d_luaContext->prerpz(dnsQuestion, res, resolver.d_eventTrace);
397✔
1213
    }
397✔
1214

1215
    // Check if the client has a policy attached to it
1216
    if (wantsRPZ && !appliedPolicy.wasHit()) {
2,365✔
1217

1218
      if (luaconfsLocal->dfe.getClientPolicy(comboWriter->d_source, resolver.d_discardedPolicies, appliedPolicy)) {
2,365!
1219
        mergePolicyTags(comboWriter->d_policyTags, appliedPolicy.getTags());
×
1220
      }
×
1221
    }
2,365✔
1222

1223
    /* If we already have an answer generated from gettag_ffi, let's see if the filtering policies
1224
       should be applied to it */
1225
    if (comboWriter->d_rcode != boost::none) {
2,365✔
1226

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

1242
        if (appliedPolicy.wasHit()) {
5✔
1243
          policyOverride = true;
2✔
1244
        }
2✔
1245
      }
5✔
1246

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

1258
    // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve
1259
    if (!comboWriter->d_luaContext || !comboWriter->d_luaContext->preresolve(dnsQuestion, res, resolver.d_eventTrace)) {
2,362✔
1260

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

1266
      resolver.setWantsRPZ(wantsRPZ);
2,302✔
1267

1268
      if (wantsRPZ && appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
2,303✔
1269

1270
        if (comboWriter->d_luaContext && comboWriter->d_luaContext->policyHitEventFilter(comboWriter->d_source, comboWriter->d_mdp.d_qname, QType(comboWriter->d_mdp.d_qtype), comboWriter->d_tcp, appliedPolicy, comboWriter->d_policyTags, resolver.d_discardedPolicies)) {
2!
1271
          /* reset to no match */
1272
          appliedPolicy = DNSFilterEngine::Policy();
×
1273
        }
×
1274
        else {
2✔
1275
          auto policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
2✔
1276
          if (policyResult == PolicyResult::HaveAnswer) {
2!
1277
            if (g_dns64Prefix && dnsQuestion.qtype == QType::AAAA && dns64Candidate(comboWriter->d_mdp.d_qtype, res, ret)) {
2!
1278
              res = getFakeAAAARecords(dnsQuestion.qname, *g_dns64Prefix, ret);
2✔
1279
              shouldNotValidate = true;
2✔
1280
            }
2✔
1281
            goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
2✔
1282
          }
2✔
1283
          else if (policyResult == PolicyResult::Drop) {
×
1284
            return;
×
1285
          }
×
1286
        }
2✔
1287
      }
2✔
1288

1289
      // Query did not get handled for Client IP or QNAME Policy reasons, now actually go out to find an answer
1290
      try {
2,300✔
1291
        resolver.d_appliedPolicy = appliedPolicy;
2,300✔
1292
        resolver.d_policyTags = std::move(comboWriter->d_policyTags);
2,300✔
1293

1294
        if (!comboWriter->d_routingTag.empty()) {
2,300✔
1295
          resolver.d_routingTag = comboWriter->d_routingTag;
16✔
1296
        }
16✔
1297

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

1336
      if (appliedPolicy.d_type != DNSFilterEngine::PolicyType::None && appliedPolicy.d_zoneData && appliedPolicy.d_zoneData->d_extendedErrorCode) {
2,295!
1337
        comboWriter->d_extendedErrorCode = *appliedPolicy.d_zoneData->d_extendedErrorCode;
2✔
1338
        comboWriter->d_extendedErrorExtra = appliedPolicy.d_zoneData->d_extendedErrorExtra;
2✔
1339
      }
2✔
1340

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

1355
      bool luaHookHandled = false;
2,293✔
1356
      if (comboWriter->d_luaContext) {
2,293✔
1357
        PolicyResult policyResult = PolicyResult::NoAction;
325✔
1358
        if (SyncRes::answerIsNOData(comboWriter->d_mdp.d_qtype, res, ret)) {
325✔
1359
          if (comboWriter->d_luaContext->nodata(dnsQuestion, res, resolver.d_eventTrace)) {
41✔
1360
            luaHookHandled = true;
12✔
1361
            shouldNotValidate = true;
12✔
1362
            policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
12✔
1363
          }
12✔
1364
        }
41✔
1365
        else if (res == RCode::NXDomain && comboWriter->d_luaContext->nxdomain(dnsQuestion, res, resolver.d_eventTrace)) {
284✔
1366
          luaHookHandled = true;
6✔
1367
          shouldNotValidate = true;
6✔
1368
          policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
6✔
1369
        }
6✔
1370
        if (policyResult == PolicyResult::HaveAnswer) {
325!
1371
          goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
×
1372
        }
×
1373
        else if (policyResult == PolicyResult::Drop) {
325✔
1374
          return;
4✔
1375
        }
4✔
1376
      } // dc->d_luaContext
325✔
1377

1378
      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)) {
2,289!
1379
        res = getFakeAAAARecords(dnsQuestion.qname, *g_dns64Prefix, ret);
10✔
1380
        shouldNotValidate = true;
10✔
1381
      }
10✔
1382

1383
      if (comboWriter->d_luaContext) {
2,289✔
1384
        PolicyResult policyResult = PolicyResult::NoAction;
321✔
1385
        if (comboWriter->d_luaContext->hasPostResolveFFIfunc()) {
321✔
1386
          RecursorLua4::PostResolveFFIHandle handle(dnsQuestion);
7✔
1387
          resolver.d_eventTrace.add(RecEventTrace::LuaPostResolveFFI);
7✔
1388
          bool prResult = comboWriter->d_luaContext->postresolve_ffi(handle);
7✔
1389
          resolver.d_eventTrace.add(RecEventTrace::LuaPostResolveFFI, prResult, false);
7✔
1390
          if (prResult) {
7✔
1391
            shouldNotValidate = true;
6✔
1392
            policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
6✔
1393
          }
6✔
1394
        }
7✔
1395
        else if (comboWriter->d_luaContext->postresolve(dnsQuestion, res, resolver.d_eventTrace)) {
314✔
1396
          shouldNotValidate = true;
4✔
1397
          policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
4✔
1398
        }
4✔
1399
        if (policyResult == PolicyResult::HaveAnswer) {
321✔
1400
          goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
3✔
1401
        }
3✔
1402
        else if (policyResult == PolicyResult::Drop) {
318✔
1403
          return;
1✔
1404
        }
1✔
1405
      } // dc->d_luaContext
321✔
1406
    }
2,289✔
1407
    else if (comboWriter->d_luaContext) {
58✔
1408
      // preresolve returned true
1409
      shouldNotValidate = true;
57✔
1410
      auto policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
57✔
1411
      // haveAnswer case redundant
1412
      if (policyResult == PolicyResult::Drop) {
57✔
1413
        return;
2✔
1414
      }
2✔
1415
    }
57✔
1416

1417
  haveAnswer:;
2,354✔
1418
    if (tracedQuery || res == -1 || res == RCode::ServFail || packetWriter.getHeader()->rcode == static_cast<unsigned>(RCode::ServFail)) {
2,353!
1419
      dumpTrace(resolver.getTrace(), resolver.d_fixednow);
11✔
1420
    }
11✔
1421

1422
    if (res == -1) {
2,353✔
1423
      packetWriter.getHeader()->rcode = RCode::ServFail;
2✔
1424
      // no commit here, because no record
1425
      ++t_Counters.at(rec::Counter::servFails);
2✔
1426
    }
2✔
1427
    else {
2,351✔
1428
      packetWriter.getHeader()->rcode = res;
2,351✔
1429

1430
      // Does the validation mode or query demand validation?
1431
      if (!shouldNotValidate && resolver.isDNSSECValidationRequested()) {
2,351✔
1432
        try {
1,840✔
1433
          auto state = resolver.getValidationState();
1,840✔
1434

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

1455
            // Is the query source interested in the value of the ad-bit?
1456
            if (comboWriter->d_mdp.d_header.ad || DNSSECOK) {
363✔
1457
              packetWriter.getHeader()->ad = 1;
334✔
1458
            }
334✔
1459
          }
363✔
1460
          else if (state == vState::Insecure) {
1,477✔
1461
            if (resolver.doLog()) {
1,362✔
1462
              SLOG(g_log << Logger::Warning << "Answer to " << comboWriter->d_mdp.d_qname << "|" << QType(comboWriter->d_mdp.d_qtype) << x_marker << " for " << comboWriter->getRemote() << " validates as Insecure" << endl,
48✔
1463
                   log->info(Logr::Info, "Validates as Insecure"));
48✔
1464
            }
48✔
1465

1466
            packetWriter.getHeader()->ad = 0;
1,362✔
1467
          }
1,362✔
1468
          else if (vStateIsBogus(state)) {
115✔
1469
            if (t_bogusremotes) {
54!
1470
              t_bogusremotes->push_back(comboWriter->d_source);
54✔
1471
            }
54✔
1472
            if (t_bogusqueryring) {
54!
1473
              t_bogusqueryring->push_back({comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype});
54✔
1474
            }
54✔
1475
            if (g_dnssecLogBogus || resolver.doLog() || g_dnssecmode == DNSSECMode::ValidateForLog) {
54!
1476
              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,
54✔
1477
                   log->info(Logr::Notice, "Validates as Bogus"));
54✔
1478
            }
54✔
1479

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

1487
              packetWriter.getHeader()->rcode = RCode::ServFail;
45✔
1488
              goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
45✔
1489
            }
45✔
1490
            else {
9✔
1491
              if (resolver.doLog()) {
9!
1492
                SLOG(g_log << Logger::Warning << "Not sending out SERVFAIL for " << comboWriter->d_mdp.d_qname << "|" << QType(comboWriter->d_mdp.d_qtype) << x_marker << " Bogus validation since neither config nor query demands this" << endl,
9✔
1493
                     log->info(Logr::Notice, "Sending out SERVFAIL because recursor or query demands it for Bogus results"));
9✔
1494
              }
9✔
1495
            }
9✔
1496
          }
54✔
1497
        }
1,840✔
1498
        catch (const ImmediateServFailException& e) {
1,840✔
1499
          if (g_logCommonErrors) {
×
1500
            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,
×
1501
                 resolver.d_slog->error(Logr::Notice, e.reason, "Sending SERVFAIL during validation", "exception", Logging::Loggable("ImmediateServFailException")));
×
1502
          }
×
1503
          goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
×
1504
        }
×
1505
        catch (const pdns::validation::TooManySEC3IterationsException& e) {
1,840✔
1506
          if (g_logCommonErrors || (g_dnssecLogBogus && resolver.getDNSSECLimitHit())) {
×
1507
            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,
×
1508
                 resolver.d_slog->error(Logr::Notice, e.what(), "Sending SERVFAIL during validation", "exception", Logging::Loggable("TooManySEC3IterationsException"), "dnsseclimithit", Logging::Loggable(resolver.getDNSSECLimitHit())));
×
1509
          }
×
1510
          goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
×
1511
        }
×
1512
      }
1,840✔
1513

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

1528
      bool needCommit = false;
2,306✔
1529
      for (const auto& record : ret) {
7,260✔
1530
        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))))) {
7,260!
1531
          continue;
106✔
1532
        }
106✔
1533

1534
        if (!addRecordToPacket(packetWriter, record, minTTL, comboWriter->d_ttlCap, maxanswersize, seenAuthSOA)) {
7,154✔
1535
          needCommit = false;
3✔
1536
          break;
3✔
1537
        }
3✔
1538
        needCommit = true;
7,151✔
1539

1540
        bool udr = false;
7,151✔
1541
#ifdef NOD_ENABLED
7,151✔
1542
        if (g_udrEnabled) {
7,151✔
1543
          udr = udrCheckUniqueDNSRecord(nodlogger, comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype, record);
3✔
1544
          if (!hasUDR && udr) {
3!
1545
            hasUDR = true;
3✔
1546
          }
3✔
1547
        }
3✔
1548
#endif /* NOD ENABLED */
7,151✔
1549

1550
        if (t_protobufServers.servers) {
7,151✔
1551
          // Max size is 64k, but we're conservative here, as other fields are added after the answers have been added
1552
          // If a single answer causes a too big protobuf message, it will be dropped by queueData()
1553
          // But note addRR has code to prevent that
1554
          if (pbMessage.size() < std::numeric_limits<uint16_t>::max() / 2) {
36!
1555
            pbMessage.addRR(record, luaconfsLocal->protobufExportConfig.exportTypes, udr);
36✔
1556
          }
36✔
1557
        }
36✔
1558
      }
7,151✔
1559
      if (needCommit) {
2,306✔
1560
        packetWriter.commit();
2,273✔
1561
      }
2,273✔
1562
#ifdef NOD_ENABLED
2,306✔
1563
#ifdef HAVE_FSTRM
2,306✔
1564
      if (hasUDR) {
2,306✔
1565
        if (isEnabledForUDRs(t_nodFrameStreamServersInfo.servers)) {
3✔
1566
          struct timespec timeSpec
2✔
1567
          {
2✔
1568
          };
2✔
1569
          std::string str;
2✔
1570
          if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
2!
1571
            TIMEVAL_TO_TIMESPEC(&comboWriter->d_kernelTimestamp, &timeSpec); // NOLINT
×
1572
          }
×
1573
          else {
2✔
1574
            TIMEVAL_TO_TIMESPEC(&comboWriter->d_now, &timeSpec); // NOLINT
2✔
1575
          }
2✔
1576
          DnstapMessage message(std::move(str), DnstapMessage::MessageType::resolver_response, SyncRes::s_serverID, &comboWriter->d_source, &comboWriter->d_destination, comboWriter->d_tcp ? DnstapMessage::ProtocolType::DoTCP : DnstapMessage::ProtocolType::DoUDP, reinterpret_cast<const char*>(&*packet.begin()), packet.size(), &timeSpec, nullptr, comboWriter->d_mdp.d_qname); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2!
1577
          str = message.getBuffer();
2✔
1578
          for (auto& logger : *(t_nodFrameStreamServersInfo.servers)) {
2✔
1579
            if (logger->logUDRs()) {
2!
1580
              remoteLoggerQueueData(*logger, str);
2✔
1581
            }
2✔
1582
          }
2✔
1583
        }
2✔
1584
      }
3✔
1585
#endif // HAVE_FSTRM
2,306✔
1586
#endif // NOD_ENABLED
2,306✔
1587
    }
2,306✔
1588
  sendit:;
2,377✔
1589

1590
    if (g_useIncomingECS && comboWriter->d_ecsFound && !resolver.wasVariable() && !variableAnswer) {
2,377!
1591
      EDNSSubnetOpts ednsOptions;
4✔
1592
      ednsOptions.setSource(comboWriter->d_ednssubnet.getSource());
4✔
1593
      ComboAddress sourceAddr;
4✔
1594
      sourceAddr.reset();
4✔
1595
      sourceAddr.sin4.sin_family = ednsOptions.getFamily();
4✔
1596
      ednsOptions.setScopePrefixLength(0);
4✔
1597
      auto ecsPayload = ednsOptions.makeOptString();
4✔
1598

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

1603
        maxanswersize -= EDNSOptionCodeSize + EDNSOptionLengthSize + ecsPayload.size();
3✔
1604

1605
        returnedEdnsOptions.emplace_back(EDNSOptionCode::ECS, std::move(ecsPayload));
3✔
1606
      }
3✔
1607
    }
4✔
1608

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

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

1633
    if (haveEDNS) {
2,377✔
1634
      auto state = resolver.getValidationState();
880✔
1635
      if (comboWriter->d_extendedErrorCode || resolver.d_extendedError || (SyncRes::s_addExtendedResolutionDNSErrors && vStateIsBogus(state))) {
880✔
1636
        EDNSExtendedError::code code = EDNSExtendedError::code::Other;
89✔
1637
        std::string extra;
89✔
1638

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

1697
        EDNSExtendedError eee;
89✔
1698
        eee.infoCode = static_cast<uint16_t>(code);
89✔
1699
        eee.extraText = std::move(extra);
89✔
1700

1701
        if (packetWriter.size() < maxanswersize && (maxanswersize - packetWriter.size()) >= (EDNSOptionCodeSize + EDNSOptionLengthSize + sizeof(eee.infoCode) + eee.extraText.size())) {
89!
1702
          returnedEdnsOptions.emplace_back(EDNSOptionCode::EXTENDEDERROR, makeEDNSExtendedErrorOptString(eee));
88✔
1703
        }
88✔
1704
      }
89✔
1705

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

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

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

1749
    if (variableAnswer || resolver.wasVariable()) {
2,377✔
1750
      t_Counters.at(rec::Counter::variableResponses)++;
118✔
1751
    }
118✔
1752

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

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

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

1791
    if (g_regressionTestMode) {
2,377✔
1792
      t_Counters.updateSnap(g_regressionTestMode);
237✔
1793
    }
237✔
1794

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

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

1822
    resolver.d_eventTrace.add(RecEventTrace::AnswerSent);
2,377✔
1823

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

1836
      if (!luaconfsLocal->protobufExportConfig.logMappedFrom) {
28✔
1837
        pbMessage.setSocketFamily(comboWriter->d_source.sin4.sin_family);
26✔
1838
        Netmask requestorNM(comboWriter->d_source, comboWriter->d_source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
26!
1839
        ComboAddress requestor = requestorNM.getMaskedNetwork();
26✔
1840
        pbMessage.setFrom(requestor);
26✔
1841
        pbMessage.setFromPort(comboWriter->d_source.getPort());
26✔
1842
      }
26✔
1843
      else {
2✔
1844
        pbMessage.setSocketFamily(comboWriter->d_mappedSource.sin4.sin_family);
2✔
1845
        Netmask requestorNM(comboWriter->d_mappedSource, comboWriter->d_mappedSource.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
2!
1846
        ComboAddress requestor = requestorNM.getMaskedNetwork();
2✔
1847
        pbMessage.setFrom(requestor);
2✔
1848
        pbMessage.setFromPort(comboWriter->d_mappedSource.getPort());
2✔
1849
      }
2✔
1850

1851
      pbMessage.setTo(comboWriter->d_destination);
28✔
1852
      pbMessage.setId(comboWriter->d_mdp.d_header.id);
28✔
1853

1854
      pbMessage.setTime();
28✔
1855
      pbMessage.setEDNSSubnet(comboWriter->d_ednssubnet.getSource(), comboWriter->d_ednssubnet.getSource().isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
28!
1856
      pbMessage.setRequestorId(dnsQuestion.requestorId);
28✔
1857
      pbMessage.setDeviceId(dnsQuestion.deviceId);
28✔
1858
      pbMessage.setDeviceName(dnsQuestion.deviceName);
28✔
1859
      pbMessage.setToPort(comboWriter->d_destination.getPort());
28✔
1860
      pbMessage.addPolicyTags(comboWriter->d_gettagPolicyTags);
28✔
1861
      pbMessage.setWorkerId(RecThreadInfo::id());
28✔
1862
      pbMessage.setPacketCacheHit(false);
28✔
1863
      pbMessage.setOutgoingQueries(resolver.d_outqueries);
28✔
1864
      for (const auto& metaValue : dnsQuestion.meta) {
28✔
1865
        pbMessage.setMeta(metaValue.first, metaValue.second.stringVal, metaValue.second.intVal);
2✔
1866
      }
2✔
1867
#ifdef NOD_ENABLED
28✔
1868
      if (g_nodEnabled) {
28!
1869
        if (nod) {
×
1870
          pbMessage.setNewlyObservedDomain(true);
×
1871
          pbMessage.addPolicyTag(g_nod_pbtag);
×
1872
        }
×
1873
        if (hasUDR) {
×
1874
          pbMessage.addPolicyTag(g_udr_pbtag);
×
1875
        }
×
1876
      }
×
1877
#endif /* NOD_ENABLED */
28✔
1878
      if (resolver.d_eventTrace.enabled() && (SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_pb) != 0) {
28!
1879
        pbMessage.addEvents(resolver.d_eventTrace);
×
1880
      }
×
1881
      if (comboWriter->d_logResponse) {
28✔
1882
        protobufLogResponse(pbMessage);
25✔
1883
      }
25✔
1884
    }
28✔
1885

1886
    if (resolver.d_eventTrace.enabled() && (SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_log) != 0) {
2,377!
1887
      SLOG(g_log << Logger::Info << resolver.d_eventTrace.toString() << endl,
×
1888
           resolver.d_slog->info(Logr::Info, resolver.d_eventTrace.toString())); // Maybe we want it to be more fancy?
×
1889
    }
×
1890

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

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

1925
    if (comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Query)) {
2,377✔
1926
      if (resolver.d_outqueries != 0 || resolver.d_throttledqueries != 0 || resolver.d_authzonequeries != 0) {
2,353!
1927
        g_recCache->incCacheMisses();
1,915✔
1928
      }
1,915✔
1929
      else {
438✔
1930
        g_recCache->incCacheHits();
438✔
1931
      }
438✔
1932
    }
2,353✔
1933

1934
    t_Counters.at(rec::Histogram::answers)(spentUsec);
2,377✔
1935
    t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
2,377✔
1936

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

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

1949
#ifdef NOD_ENABLED
2,377✔
1950
    if (nod) {
2,377✔
1951
      sendNODLookup(nodlogger, comboWriter->d_mdp.d_qname);
3✔
1952
    }
3✔
1953
#endif /* NOD_ENABLED */
2,377✔
1954

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

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

1988
  runTaskOnce(g_logCommonErrors);
2,377✔
1989

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

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

2016
  size_t pos = sizeof(dnsheader) + consumed + 4;
157✔
2017
  const size_t headerSize = /* root */ 1 + sizeof(dnsrecordheader);
157✔
2018
  const uint16_t arcount = ntohs(dhPointer->arcount);
157✔
2019

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

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

2035
    if (pos >= questionLen) {
115✔
2036
      return;
98✔
2037
    }
98✔
2038

2039
    /* OPT root label (1) followed by type (2) */
2040
    if (ntohs(drh->d_type) == QType::OPT) {
17!
2041
      if (options == nullptr) {
17✔
2042
        size_t ecsStartPosition = 0;
13✔
2043
        size_t ecsLen = 0;
13✔
2044
        /* we need to pass the record len */
2045
        int res = getEDNSOption(reinterpret_cast<const char*>(&question.at(pos - sizeof(drh->d_clen))), questionLen - pos + sizeof(drh->d_clen), EDNSOptionCode::ECS, &ecsStartPosition, &ecsLen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
13✔
2046
        if (res == 0 && ecsLen > 4) {
13!
2047
          EDNSSubnetOpts eso;
10✔
2048
          if (EDNSSubnetOpts::getFromString(&question.at(pos - sizeof(drh->d_clen) + ecsStartPosition + 4), ecsLen - 4, &eso)) {
10!
2049
            *ednssubnet = eso;
10✔
2050
            foundECS = true;
10✔
2051
          }
10✔
2052
        }
10✔
2053
      }
13✔
2054
      else {
4✔
2055
        /* we need to pass the record len */
2056
        int res = getEDNSOptions(reinterpret_cast<const char*>(&question.at(pos - sizeof(drh->d_clen))), questionLen - pos + (sizeof(drh->d_clen)), *options); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
4✔
2057
        if (res == 0) {
4!
2058
          const auto& iter = options->find(EDNSOptionCode::ECS);
4✔
2059
          if (iter != options->end() && !iter->second.values.empty() && iter->second.values.at(0).content != nullptr && iter->second.values.at(0).size > 0) {
4!
2060
            EDNSSubnetOpts eso;
4✔
2061
            if (EDNSSubnetOpts::getFromString(iter->second.values.at(0).content, iter->second.values.at(0).size, &eso)) {
4!
2062
              *ednssubnet = eso;
4✔
2063
              foundECS = true;
4✔
2064
            }
4✔
2065
          }
4✔
2066
        }
4✔
2067
      }
4✔
2068
    }
17✔
2069

2070
    pos += ntohs(drh->d_clen);
17✔
2071
  }
17✔
2072
}
157✔
2073

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

2087
  if (qnameParsed) {
4,636✔
2088
    cacheHit = g_packetCache->getResponsePacket(tag, data, qname, qtype, qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
133✔
2089
  }
133✔
2090
  else {
4,503✔
2091
    cacheHit = g_packetCache->getResponsePacket(tag, data, qname, &qtype, &qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
4,503✔
2092
  }
4,503✔
2093

2094
  if (cacheHit) {
4,636✔
2095
    if (vStateIsBogus(valState)) {
2,404!
2096
      if (t_bogusremotes) {
×
2097
        t_bogusremotes->push_back(source);
×
2098
      }
×
2099
      if (t_bogusqueryring) {
×
2100
        t_bogusqueryring->push_back({qname, qtype});
×
2101
      }
×
2102
    }
×
2103

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

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

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

2133
  return cacheHit;
4,636✔
2134
}
4,772✔
2135

2136
static void* pleaseWipeCaches(const DNSName& canon, bool subtree, uint16_t qtype)
2137
{
2✔
2138
  auto res = wipeCaches(canon, subtree, qtype);
2✔
2139
  SLOG(g_log << Logger::Info << "Wiped caches for " << canon << ": " << res.record_count << " records; " << res.negative_record_count << " negative records; " << res.packet_count << " packets" << endl,
2✔
2140
       g_slog->withName("runtime")->info(Logr::Info, "Wiped cache", "qname", Logging::Loggable(canon), "records", Logging::Loggable(res.record_count), "negrecords", Logging::Loggable(res.negative_record_count), "packets", Logging::Loggable(res.packet_count)));
2✔
2141
  return nullptr;
2✔
2142
}
2✔
2143

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

2154
    unixDie("write to thread pipe returned wrong size or error");
×
2155
  }
×
2156
  // coverity[leaked_storage]
2157
}
2✔
2158

2159
bool expectProxyProtocol(const ComboAddress& from, const ComboAddress& listenAddress)
2160
{
4,939✔
2161
  return g_proxyProtocolACL.match(from) && g_proxyProtocolExceptions.count(listenAddress) == 0;
4,939✔
2162
}
4,939✔
2163

2164
// fromaddr: the address the query is coming from
2165
// destaddr: the address the query was received on
2166
// source: the address we assume the query is coming from, might be set by proxy protocol
2167
// destination: the address we assume the query was sent to, might be set by proxy protocol
2168
// mappedSource: the address we assume the query is coming from. Differs from source if table based mapping has been applied
2169
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) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
2170
{
4,543✔
2171
  RecThreadInfo::self().incNumberOfDistributedQueries();
4,543✔
2172
  gettimeofday(&g_now, nullptr);
4,543✔
2173
  if (tval.tv_sec != 0) {
4,545✔
2174
    struct timeval diff = g_now - tval;
4,523✔
2175
    double delta = (static_cast<double>(diff.tv_sec) * 1000 + static_cast<double>(diff.tv_usec) / 1000.0);
4,523✔
2176

2177
    if (delta > 1000.0) {
4,523!
UNCOV
2178
      t_Counters.at(rec::Counter::tooOldDrops)++;
×
UNCOV
2179
      return nullptr;
×
UNCOV
2180
    }
×
2181
  }
4,523✔
2182

2183
  ++t_Counters.at(rec::Counter::qcounter);
4,543✔
2184

2185
  if (fromaddr.sin4.sin_family == AF_INET6) {
4,543✔
2186
    t_Counters.at(rec::Counter::ipv6qcounter)++;
2✔
2187
  }
2✔
2188

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

2248
    // We do not have a SyncRes specific Lua context at this point yet, so ok to use t_pdl
2249
    if (needEDNSParse || (t_pdl && (t_pdl->hasGettagFunc() || t_pdl->hasGettagFFIFunc())) || dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
4,549✔
2250
      try {
112✔
2251
        EDNSOptionViewMap ednsOptions;
112✔
2252

2253
        ecsFound = false;
112✔
2254

2255
        getQNameAndSubnet(question, &qname, &qtype, &qclass,
112✔
2256
                          ecsFound, &ednssubnet, g_gettagNeedsEDNSOptions ? &ednsOptions : nullptr, ednsVersion);
112✔
2257

2258
        qnameParsed = true;
112✔
2259
        ecsParsed = true;
112✔
2260

2261
        if (t_pdl) {
112✔
2262
          try {
86✔
2263
            if (t_pdl->hasGettagFFIFunc()) {
86✔
2264
              RecursorLua4::FFIParams params(qname, qtype, destaddr, fromaddr, destination, source, ednssubnet.getSource(), data, policyTags, records, ednsOptions, proxyProtocolValues, requestorId, deviceId, deviceName, routingTag, rcode, ttlCap, variable, false, logQuery, logResponse, followCNAMEs, extendedErrorCode, extendedErrorExtra, responsePaddingDisabled, meta);
42✔
2265

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

2291
    RecursorPacketCache::OptPBData pbData{boost::none};
4,543✔
2292
    if (t_protobufServers.servers) {
4,543✔
2293
      if (logQuery && !(luaconfsLocal->protobufExportConfig.taggedOnly && policyTags.empty())) {
33✔
2294
        protobufLogQuery(luaconfsLocal, uniqueId, source, destination, mappedSource, ednssubnet.getSource(), false, question.size(), qname, qtype, qclass, policyTags, requestorId, deviceId, deviceName, meta, ednsVersion, *dnsheader);
24✔
2295
      }
24✔
2296
    }
33✔
2297

2298
    if (ctag == 0 && !responsePaddingDisabled && g_paddingFrom.match(fromaddr)) {
4,549✔
2299
      ctag = g_paddingTag;
10✔
2300
    }
10✔
2301

2302
    if (dnsheader->opcode == static_cast<unsigned>(Opcode::Query)) {
4,546✔
2303
      /* It might seem like a good idea to skip the packet cache lookup if we know that the answer is not cacheable,
2304
         but it means that the hash would not be computed. If some script decides at a later time to mark back the answer
2305
         as cacheable we would cache it with a wrong tag, so better safe than sorry. */
2306
      eventTrace.add(RecEventTrace::PCacheCheck);
4,521✔
2307
      bool cacheHit = checkForCacheHit(qnameParsed, ctag, question, qname, qtype, qclass, g_now, response, qhash, pbData, false, source, mappedSource);
4,521✔
2308
      eventTrace.add(RecEventTrace::PCacheCheck, cacheHit, false);
4,521✔
2309
      if (cacheHit) {
4,521✔
2310
        if (!g_quiet) {
2,352✔
2311
          SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " question answered from packet cache tag=" << ctag << " from " << source.toStringWithPort() << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << endl,
103✔
2312
               g_slogudpin->info(Logr::Notice, "Question answered from packet cache", "tag", Logging::Loggable(ctag),
103✔
2313
                                 "qname", Logging::Loggable(qname), "qtype", Logging::Loggable(QType(qtype)),
103✔
2314
                                 "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
103✔
2315
        }
103✔
2316
        struct msghdr msgh
2,352✔
2317
        {
2,352✔
2318
        };
2,352✔
2319
        struct iovec iov
2,352✔
2320
        {
2,352✔
2321
        };
2,352✔
2322
        cmsgbuf_aligned cbuf{};
2,352✔
2323
        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,352✔
2324
        msgh.msg_control = nullptr;
2,352✔
2325

2326
        if (g_fromtosockets.count(fileDesc) != 0) {
2,352!
2327
          addCMsgSrcAddr(&msgh, &cbuf, &destaddr, 0);
×
2328
        }
×
2329
        int sendErr = sendOnNBSocket(fileDesc, &msgh);
2,352✔
2330
        eventTrace.add(RecEventTrace::AnswerSent);
2,352✔
2331

2332
        if (t_protobufServers.servers && logResponse && (!luaconfsLocal->protobufExportConfig.taggedOnly || (pbData && pbData->d_tagged))) {
2,352!
2333
          protobufLogResponse(dnsheader, luaconfsLocal, pbData, tval, false, source, destination, mappedSource, ednssubnet, uniqueId, requestorId, deviceId, deviceName, meta, eventTrace, policyTags);
6✔
2334
        }
6✔
2335

2336
        if (eventTrace.enabled() && (SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_log) != 0) {
2,352!
2337
          SLOG(g_log << Logger::Info << eventTrace.toString() << endl,
×
2338
               g_slogudpin->info(Logr::Info, eventTrace.toString())); // Do we want more fancy logging here?
×
2339
        }
×
2340
        if (sendErr != 0 && g_logCommonErrors) {
2,352!
2341
          SLOG(g_log << Logger::Warning << "Sending UDP reply to client " << source.toStringWithPort()
×
2342
                     << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << " failed with: "
×
2343
                     << stringerror(sendErr) << endl,
×
2344
               g_slogudpin->error(Logr::Error, sendErr, "Sending UDP reply to client failed", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
×
2345
        }
×
2346
        struct timeval now
2,352✔
2347
        {
2,352✔
2348
        };
2,352✔
2349
        Utility::gettimeofday(&now, nullptr);
2,352✔
2350
        uint64_t spentUsec = uSec(now - tval);
2,352✔
2351
        t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
2,352✔
2352
        t_Counters.updateSnap(g_regressionTestMode);
2,352✔
2353
        return nullptr;
2,352✔
2354
      }
2,352✔
2355
    }
4,521✔
2356
  }
4,543✔
2357
  catch (const std::exception& e) {
4,543✔
2358
    if (g_logCommonErrors) {
×
2359
      SLOG(g_log << Logger::Error << "Error processing or aging answer packet: " << e.what() << endl,
×
2360
           g_slogudpin->error(Logr::Error, e.what(), "Error processing or aging answer packet", "exception", Logging::Loggable("std::exception")));
×
2361
    }
×
2362
    return nullptr;
×
2363
  }
×
2364

2365
  if (t_pdl) {
2,184✔
2366
    bool ipf = t_pdl->ipfilter(source, destination, *dnsheader, eventTrace);
236✔
2367
    if (ipf) {
236✔
2368
      if (!g_quiet) {
2!
2369
        SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " [" << g_multiTasker->getTid() << "/" << g_multiTasker->numProcesses() << "] DROPPED question from " << source.toStringWithPort() << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << " based on policy" << endl,
2✔
2370
             g_slogudpin->info(Logr::Notice, "Dropped question based on policy", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
2✔
2371
      }
2✔
2372
      t_Counters.at(rec::Counter::policyDrops)++;
2✔
2373
      return nullptr;
2✔
2374
    }
2✔
2375
  }
236✔
2376

2377
  if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
2,182✔
2378
    if (!isAllowNotifyForZone(qname)) {
23!
2379
      if (!g_quiet) {
×
2380
        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,
×
2381
             g_slogudpin->info(Logr::Notice, "Dropping UDP NOTIFY, zone not matched by allow-notify-for", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
×
2382
      }
×
2383

2384
      t_Counters.at(rec::Counter::zoneDisallowedNotify)++;
×
2385
      return nullptr;
×
2386
    }
×
2387

2388
    if (!g_quiet) {
23!
2389
      SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " got NOTIFY for " << qname.toLogString() << " from " << source.toStringWithPort() << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << endl,
23✔
2390
           g_slogudpin->info(Logr::Notice, "Got NOTIFY", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr), "qname", Logging::Loggable(qname)));
23✔
2391
    }
23✔
2392
    if (!ZoneXFR::notifyZoneTracker(qname)) {
23✔
2393
      // It wasn't an RPZ
2394
      requestWipeCaches(qname);
1✔
2395
    }
1✔
2396

2397
    // the operation will now be treated as a Query, generating
2398
    // a normal response, as the rest of the code does not
2399
    // check dh->opcode, but we need to ensure that the response
2400
    // to this request does not get put into the packet cache
2401
    variable = true;
23✔
2402
  }
23✔
2403

2404
  if (g_multiTasker->numProcesses() >= g_maxMThreads) {
2,182!
2405
    if (!g_quiet) {
×
2406
      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,
×
2407
           g_slogudpin->info(Logr::Notice, "Dropped question, over capacity", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
×
2408
    }
×
2409
    t_Counters.at(rec::Counter::overCapacityDrops)++;
×
2410
    return nullptr;
×
2411
  }
×
2412

2413
  auto comboWriter = std::make_unique<DNSComboWriter>(question, g_now, std::move(policyTags), t_pdl, std::move(data), std::move(records));
2,182✔
2414

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

2446
  comboWriter->d_eventTrace = std::move(eventTrace);
2,182✔
2447
  g_multiTasker->makeThread(startDoResolve, (void*)comboWriter.release()); // deletes dc
2,182✔
2448

2449
  return nullptr;
2,182✔
2450
}
2,182✔
2451

2452
static void handleNewUDPQuestion(int fileDesc, FDMultiplexer::funcparam_t& /* var */) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
2453
{
804✔
2454
  static const size_t maxIncomingQuerySize = g_proxyProtocolACL.empty() ? 512 : (512 + g_proxyProtocolMaximumSize);
804✔
2455
  static thread_local std::string data;
804✔
2456
  ComboAddress fromaddr; // the address the query is coming from
804✔
2457
  ComboAddress source; // the address we assume the query is coming from, might be set by proxy protocol
804✔
2458
  ComboAddress destination; // the address we assume the query was sent to, might be set by proxy protocol
804✔
2459
  struct msghdr msgh
804✔
2460
  {
804✔
2461
  };
804✔
2462
  struct iovec iov
804✔
2463
  {
804✔
2464
  };
804✔
2465
  cmsgbuf_aligned cbuf;
804✔
2466
  bool firstQuery = true;
804✔
2467
  std::vector<ProxyProtocolValue> proxyProtocolValues;
804✔
2468
  RecEventTrace eventTrace;
804✔
2469

2470
  for (size_t queriesCounter = 0; queriesCounter < g_maxUDPQueriesPerRound; queriesCounter++) {
5,357!
2471
    bool proxyProto = false;
5,357✔
2472
    proxyProtocolValues.clear();
5,357✔
2473
    data.resize(maxIncomingQuerySize);
5,357✔
2474
    fromaddr.sin6.sin6_family = AF_INET6; // this makes sure fromaddr is big enough
5,357✔
2475
    fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), data.data(), data.size(), &fromaddr);
5,357✔
2476

2477
    if (ssize_t len = recvmsg(fileDesc, &msgh, 0); len >= 0) {
5,357✔
2478
      eventTrace.clear();
4,563✔
2479
      eventTrace.setEnabled(SyncRes::s_event_trace_enabled != 0);
4,563✔
2480
      eventTrace.add(RecEventTrace::ReqRecv);
4,563✔
2481

2482
      firstQuery = false;
4,563✔
2483

2484
      if ((msgh.msg_flags & MSG_TRUNC) != 0) {
4,563!
2485
        t_Counters.at(rec::Counter::truncatedDrops)++;
×
2486
        if (!g_quiet) {
×
2487
          SLOG(g_log << Logger::Error << "Ignoring truncated query from " << fromaddr.toString() << endl,
×
2488
               g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr)));
×
2489
        }
×
2490
        return;
×
2491
      }
×
2492

2493
      data.resize(static_cast<size_t>(len));
4,563✔
2494

2495
      ComboAddress destaddr; // the address the query was sent to to
4,563✔
2496
      destaddr.reset(); // this makes sure we ignore this address if not explictly set below
4,563✔
2497
      const auto* loc = rplookup(g_listenSocketsAddresses, fileDesc);
4,563✔
2498
      if (HarvestDestinationAddress(&msgh, &destaddr)) {
4,563✔
2499
        // but.. need to get port too
2500
        if (loc != nullptr) {
1!
2501
          destaddr.sin4.sin_port = loc->sin4.sin_port;
1✔
2502
        }
1✔
2503
      }
1✔
2504
      else {
4,562✔
2505
        if (loc != nullptr) {
4,562!
2506
          destaddr = *loc;
4,562✔
2507
        }
4,562✔
2508
        else {
×
2509
          destaddr.sin4.sin_family = fromaddr.sin4.sin_family;
×
2510
          socklen_t slen = destaddr.getSocklen();
×
2511
          getsockname(fileDesc, reinterpret_cast<sockaddr*>(&destaddr), &slen); // if this fails, we're ok with it  // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
×
2512
        }
×
2513
      }
4,562✔
2514
      if (expectProxyProtocol(fromaddr, destaddr)) {
4,563✔
2515
        bool tcp = false;
24✔
2516
        ssize_t used = parseProxyHeader(data, proxyProto, source, destination, tcp, proxyProtocolValues);
24✔
2517
        if (used <= 0) {
24✔
2518
          ++t_Counters.at(rec::Counter::proxyProtocolInvalidCount);
4✔
2519
          if (!g_quiet) {
4!
2520
            SLOG(g_log << Logger::Error << "Ignoring invalid proxy protocol (" << std::to_string(len) << ", " << std::to_string(used) << ") query from " << fromaddr.toStringWithPort() << endl,
4✔
2521
                 g_slogudpin->info(Logr::Error, "Ignoring invalid proxy protocol query", "length", Logging::Loggable(len),
4✔
2522
                                   "used", Logging::Loggable(used), "remote", Logging::Loggable(fromaddr)));
4✔
2523
          }
4✔
2524
          return;
4✔
2525
        }
4✔
2526
        if (static_cast<size_t>(used) > g_proxyProtocolMaximumSize) {
20✔
2527
          if (g_quiet) {
2!
2528
            SLOG(g_log << Logger::Error << "Proxy protocol header in UDP packet from " << fromaddr.toStringWithPort() << " is larger than proxy-protocol-maximum-size (" << used << "), dropping" << endl,
×
2529
                 g_slogudpin->info(Logr::Error, "Proxy protocol header in UDP packet  is larger than proxy-protocol-maximum-size",
×
2530
                                   "used", Logging::Loggable(used), "remote", Logging::Loggable(fromaddr)));
×
2531
          }
×
2532
          ++t_Counters.at(rec::Counter::proxyProtocolInvalidCount);
2✔
2533
          return;
2✔
2534
        }
2✔
2535

2536
        data.erase(0, used);
18✔
2537
      }
18✔
2538
      else if (len > 512) {
4,539!
2539
        /* we only allow UDP packets larger than 512 for those with a proxy protocol header */
2540
        t_Counters.at(rec::Counter::truncatedDrops)++;
×
2541
        if (!g_quiet) {
×
2542
          SLOG(g_log << Logger::Error << "Ignoring truncated query from " << fromaddr.toStringWithPort() << endl,
×
2543
               g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr)));
×
2544
        }
×
2545
        return;
×
2546
      }
×
2547

2548
      if (data.size() < sizeof(dnsheader)) {
4,557!
2549
        t_Counters.at(rec::Counter::ignoredCount)++;
×
2550
        if (!g_quiet) {
×
2551
          SLOG(g_log << Logger::Error << "Ignoring too-short (" << std::to_string(data.size()) << ") query from " << fromaddr.toString() << endl,
×
2552
               g_slogudpin->info(Logr::Error, "Ignoring too-short query", "length", Logging::Loggable(data.size()),
×
2553
                                 "remote", Logging::Loggable(fromaddr)));
×
2554
        }
×
2555
        return;
×
2556
      }
×
2557

2558
      if (!proxyProto) {
4,557✔
2559
        source = fromaddr;
4,541✔
2560
      }
4,541✔
2561
      ComboAddress mappedSource = source;
4,557✔
2562
      if (t_proxyMapping) {
4,557✔
2563
        if (const auto* iter = t_proxyMapping->lookup(source)) {
8!
2564
          mappedSource = iter->second.address;
8✔
2565
          ++iter->second.stats.netmaskMatches;
8✔
2566
        }
8✔
2567
      }
8✔
2568
      if (t_remotes) {
4,557!
2569
        t_remotes->push_back(source);
4,557✔
2570
      }
4,557✔
2571

2572
      if (t_allowFrom && !t_allowFrom->match(&mappedSource)) {
4,557!
2573
        if (!g_quiet) {
4!
2574
          SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping UDP query from " << mappedSource.toString() << ", address not matched by allow-from" << endl,
4✔
2575
               g_slogudpin->info(Logr::Error, "Dropping UDP query, address not matched by allow-from", "source", Logging::Loggable(mappedSource)));
4✔
2576
        }
4✔
2577

2578
        t_Counters.at(rec::Counter::unauthorizedUDP)++;
4✔
2579
        return;
4✔
2580
      }
4✔
2581

2582
      BOOST_STATIC_ASSERT(offsetof(sockaddr_in, sin_port) == offsetof(sockaddr_in6, sin6_port));
4,553✔
2583
      if (fromaddr.sin4.sin_port == 0) { // also works for IPv6
4,553!
2584
        if (!g_quiet) {
×
2585
          SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping UDP query from " << fromaddr.toStringWithPort() << ", can't deal with port 0" << endl,
×
2586
               g_slogudpin->info(Logr::Error, "Dropping UDP query can't deal with port 0", "remote", Logging::Loggable(fromaddr)));
×
2587
        }
×
2588

2589
        t_Counters.at(rec::Counter::clientParseError)++; // not quite the best place to put it, but needs to go somewhere
×
2590
        return;
×
2591
      }
×
2592

2593
      try {
4,553✔
2594
        const dnsheader_aligned headerdata(data.data());
4,553✔
2595
        const dnsheader* dnsheader = headerdata.get();
4,553✔
2596

2597
        if (dnsheader->qr) {
4,553!
2598
          t_Counters.at(rec::Counter::ignoredCount)++;
×
2599
          if (g_logCommonErrors) {
×
2600
            SLOG(g_log << Logger::Error << "Ignoring answer from " << fromaddr.toString() << " on server socket!" << endl,
×
2601
                 g_slogudpin->info(Logr::Error, "Ignoring answer on server socket", "remote", Logging::Loggable(fromaddr)));
×
2602
          }
×
2603
        }
×
2604
        else if (dnsheader->opcode != static_cast<unsigned>(Opcode::Query) && dnsheader->opcode != static_cast<unsigned>(Opcode::Notify)) {
4,553✔
2605
          t_Counters.at(rec::Counter::ignoredCount)++;
2✔
2606
          if (g_logCommonErrors) {
2!
2607
            SLOG(g_log << Logger::Error << "Ignoring unsupported opcode " << Opcode::to_s(dnsheader->opcode) << " from " << fromaddr.toString() << " on server socket!" << endl,
2✔
2608
                 g_slogudpin->info(Logr::Error, "Ignoring unsupported opcode server socket", "remote", Logging::Loggable(fromaddr), "opcode", Logging::Loggable(Opcode::to_s(dnsheader->opcode))));
2✔
2609
          }
2✔
2610
        }
2✔
2611
        else if (dnsheader->qdcount == 0U) {
4,551!
2612
          t_Counters.at(rec::Counter::emptyQueriesCount)++;
×
2613
          if (g_logCommonErrors) {
×
2614
            SLOG(g_log << Logger::Error << "Ignoring empty (qdcount == 0) query from " << fromaddr.toString() << " on server socket!" << endl,
×
2615
                 g_slogudpin->info(Logr::Error, "Ignoring empty (qdcount == 0) query on server socket!", "remote", Logging::Loggable(fromaddr)));
×
2616
          }
×
2617
        }
×
2618
        else {
4,551✔
2619
          if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
4,551✔
2620
            if (!t_allowNotifyFrom || !t_allowNotifyFrom->match(&mappedSource)) {
23!
2621
              if (!g_quiet) {
×
2622
                SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping UDP NOTIFY from " << mappedSource.toString() << ", address not matched by allow-notify-from" << endl,
×
2623
                     g_slogudpin->info(Logr::Error, "Dropping UDP NOTIFY from address not matched by allow-notify-from",
×
2624
                                       "source", Logging::Loggable(mappedSource)));
×
2625
              }
×
2626

2627
              t_Counters.at(rec::Counter::sourceDisallowedNotify)++;
×
2628
              return;
×
2629
            }
×
2630
          }
23✔
2631

2632
          struct timeval tval = {0, 0};
4,551✔
2633
          HarvestTimestamp(&msgh, &tval);
4,551✔
2634
          if (!proxyProto) {
4,551✔
2635
            destination = destaddr;
4,539✔
2636
          }
4,539✔
2637

2638
          if (RecThreadInfo::weDistributeQueries()) {
4,551✔
2639
            std::string localdata = data;
3,611✔
2640
            distributeAsyncFunction(data, [localdata = std::move(localdata), fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace]() mutable {
3,611✔
2641
              return doProcessUDPQuestion(localdata, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace);
3,561✔
2642
            });
3,561✔
2643
          }
3,611✔
2644
          else {
940✔
2645
            doProcessUDPQuestion(data, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace);
940✔
2646
          }
940✔
2647
        }
4,551✔
2648
      }
4,553✔
2649
      catch (const MOADNSException& mde) {
4,553✔
2650
        t_Counters.at(rec::Counter::clientParseError)++;
×
2651
        if (g_logCommonErrors) {
×
2652
          SLOG(g_log << Logger::Error << "Unable to parse packet from remote UDP client " << fromaddr.toString() << ": " << mde.what() << endl,
×
2653
               g_slogudpin->error(Logr::Error, mde.what(), "Unable to parse packet from remote UDP client", "remote", Logging::Loggable(fromaddr), "exception", Logging::Loggable("MOADNSException")));
×
2654
        }
×
2655
      }
×
2656
      catch (const std::runtime_error& e) {
4,553✔
2657
        t_Counters.at(rec::Counter::clientParseError)++;
×
2658
        if (g_logCommonErrors) {
×
2659
          SLOG(g_log << Logger::Error << "Unable to parse packet from remote UDP client " << fromaddr.toString() << ": " << e.what() << endl,
×
2660
               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")));
×
2661
        }
×
2662
      }
×
2663
    }
4,553✔
2664
    else {
794✔
2665
      // cerr<<t_id<<" had error: "<<stringerror()<<endl;
2666
      if (firstQuery && errno == EAGAIN) {
794!
2667
        t_Counters.at(rec::Counter::noPacketError)++;
×
2668
      }
×
2669

2670
      break;
794✔
2671
    }
794✔
2672
  }
5,357✔
2673
  t_Counters.updateSnap(g_regressionTestMode);
794✔
2674
}
794✔
2675

2676
// The two last arguments to makeUDPServerSockets are used for logging purposes only
2677
unsigned int makeUDPServerSockets(deferredAdd_t& deferredAdds, Logr::log_t log, bool doLog, unsigned int instances)
2678
{
292✔
2679
  int one = 1;
292✔
2680
  vector<string> localAddresses;
292✔
2681
  vector<string> logVec;
292✔
2682
  stringtok(localAddresses, ::arg()["local-address"], " ,");
292✔
2683

2684
  if (localAddresses.empty()) {
292!
2685
    throw PDNSException("No local address specified");
×
2686
  }
×
2687

2688
  const uint16_t defaultLocalPort = ::arg().asNum("local-port");
292✔
2689
  for (const auto& localAddress : localAddresses) {
294✔
2690
    ComboAddress address{localAddress, defaultLocalPort};
294✔
2691
    const int socketFd = socket(address.sin4.sin_family, SOCK_DGRAM, 0);
294✔
2692
    if (socketFd < 0) {
294!
2693
      throw PDNSException("Making a UDP server socket for resolver: " + stringerror());
×
2694
    }
×
2695
    logVec.emplace_back(address.toStringWithPort());
294✔
2696
    if (!setSocketTimestamps(socketFd)) {
294!
2697
      SLOG(g_log << Logger::Warning << "Unable to enable timestamp reporting for socket" << endl,
×
2698
           log->info(Logr::Warning, "Unable to enable timestamp reporting for socket"));
×
2699
    }
×
2700
    if (IsAnyAddress(address)) {
294✔
2701
      if (address.sin4.sin_family == AF_INET) {
2!
2702
        if (setsockopt(socketFd, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)) == 0) { // linux supports this, so why not - might fail on other systems
2!
2703
          g_fromtosockets.insert(socketFd);
2✔
2704
        }
2✔
2705
      }
2✔
2706
#ifdef IPV6_RECVPKTINFO
2✔
2707
      if (address.sin4.sin_family == AF_INET6) {
2!
2708
        if (setsockopt(socketFd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)) == 0) {
×
2709
          g_fromtosockets.insert(socketFd);
×
2710
        }
×
2711
      }
×
2712
#endif
2✔
2713
      if (address.sin6.sin6_family == AF_INET6 && setsockopt(socketFd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)) < 0) {
2!
2714
        int err = errno;
×
2715
        SLOG(g_log << Logger::Warning << "Failed to set IPv6 socket to IPv6 only, continuing anyhow: " << stringerror(err) << endl,
×
2716
             log->error(Logr::Warning, err, "Failed to set IPv6 socket to IPv6 only, continuing anyhow"));
×
2717
      }
×
2718
    }
2✔
2719
    if (::arg().mustDo("non-local-bind")) {
294!
2720
      Utility::setBindAny(AF_INET6, socketFd);
×
2721
    }
×
2722

2723
    setCloseOnExec(socketFd);
294✔
2724

2725
    try {
294✔
2726
      setSocketReceiveBuffer(socketFd, 250000);
294✔
2727
    }
294✔
2728
    catch (const std::exception& e) {
294✔
2729
      SLOG(g_log << Logger::Error << e.what() << endl,
×
2730
           log->error(Logr::Error, e.what(), "Exception while setting socket buffer size"));
×
2731
    }
×
2732

2733
    if (g_reusePort) {
294✔
2734
#if defined(SO_REUSEPORT_LB)
2735
      try {
2736
        SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
2737
      }
2738
      catch (const std::exception& e) {
2739
        throw PDNSException(std::string("SO_REUSEPORT_LB: ") + e.what());
2740
      }
2741
#elif defined(SO_REUSEPORT)
2742
      try {
282✔
2743
        SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT, 1);
282✔
2744
      }
282✔
2745
      catch (const std::exception& e) {
282✔
2746
        throw PDNSException(std::string("SO_REUSEPORT: ") + e.what());
×
2747
      }
×
2748
#endif
282✔
2749
    }
282✔
2750

2751
    try {
294✔
2752
      setSocketIgnorePMTU(socketFd, address.sin4.sin_family);
294✔
2753
    }
294✔
2754
    catch (const std::exception& e) {
294✔
2755
      SLOG(g_log << Logger::Warning << "Failed to set IP_MTU_DISCOVER on UDP server socket: " << e.what() << endl,
×
2756
           log->error(Logr::Warning, e.what(), "Failed to set IP_MTU_DISCOVER on UDP server socket"));
×
2757
    }
×
2758

2759
    socklen_t socklen = address.getSocklen();
294✔
2760
    if (::bind(socketFd, reinterpret_cast<struct sockaddr*>(&address), socklen) < 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
294!
2761
      throw PDNSException("Resolver binding to server socket on " + address.toStringWithPort() + ": " + stringerror());
×
2762
    }
×
2763

2764
    setNonBlocking(socketFd);
294✔
2765

2766
    deferredAdds.emplace_back(socketFd, handleNewUDPQuestion);
294✔
2767
    g_listenSocketsAddresses[socketFd] = address; // this is written to only from the startup thread, not from the workers
294✔
2768
  }
294✔
2769
  if (doLog) {
292✔
2770
    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));
153✔
2771
  }
153✔
2772
  return localAddresses.size();
292✔
2773
}
292✔
2774

2775
static bool trySendingQueryToWorker(unsigned int target, ThreadMSG* tmsg)
2776
{
3,611✔
2777
  auto& targetInfo = RecThreadInfo::info(target);
3,611✔
2778
  if (!targetInfo.isWorker()) {
3,611!
2779
    SLOG(g_log << Logger::Error << "distributeAsyncFunction() tried to assign a query to a non-worker thread" << endl,
×
2780
         g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() tried to assign a query to a non-worker thread"));
×
2781
    _exit(1);
×
2782
  }
×
2783

2784
  const auto& tps = targetInfo.getPipes();
3,611✔
2785

2786
  ssize_t written = write(tps.writeQueriesToThread, &tmsg, sizeof(tmsg)); // NOLINT: correct sizeof
3,611✔
2787
  if (written > 0) {
3,611!
2788
    if (static_cast<size_t>(written) != sizeof(tmsg)) { // NOLINT: correct sizeof
3,611!
2789
      delete tmsg; // NOLINT: pointer ownership
×
2790
      unixDie("write to thread pipe returned wrong size or error");
×
2791
    }
×
2792
  }
3,611✔
2793
  else {
×
2794
    int error = errno;
×
2795
    if (error == EAGAIN || error == EWOULDBLOCK) {
×
2796
      return false;
×
2797
    }
×
2798
    delete tmsg; // NOLINT: pointer ownership
×
2799
    unixDie("write to thread pipe returned wrong size or error:" + std::to_string(error));
×
2800
  }
×
2801

2802
  return true;
3,611✔
2803
}
3,611✔
2804

2805
static unsigned int getWorkerLoad(size_t workerIdx)
2806
{
×
2807
  const auto* multiThreader = RecThreadInfo::info(RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + workerIdx).getMT();
×
2808
  if (multiThreader != nullptr) {
×
2809
    return multiThreader->numProcesses();
×
2810
  }
×
2811
  return 0;
×
2812
}
×
2813

2814
static unsigned int selectWorker(unsigned int hash)
2815
{
3,611✔
2816
  assert(RecThreadInfo::numUDPWorkers() != 0); // NOLINT: assert implementation
3,611✔
2817
  if (g_balancingFactor == 0) {
3,611!
2818
    return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + (hash % RecThreadInfo::numUDPWorkers());
3,611✔
2819
  }
3,611✔
2820

2821
  /* we start with one, representing the query we are currently handling */
2822
  double currentLoad = 1;
×
2823
  std::vector<unsigned int> load(RecThreadInfo::numUDPWorkers());
×
2824
  for (size_t idx = 0; idx < RecThreadInfo::numUDPWorkers(); idx++) {
×
2825
    load[idx] = getWorkerLoad(idx);
×
2826
    currentLoad += load[idx];
×
2827
  }
×
2828

2829
  double targetLoad = (currentLoad / RecThreadInfo::numUDPWorkers()) * g_balancingFactor;
×
2830

2831
  unsigned int worker = hash % RecThreadInfo::numUDPWorkers();
×
2832
  /* at least one server has to be at or below the average load */
2833
  if (load[worker] > targetLoad) {
×
2834
    ++t_Counters.at(rec::Counter::rebalancedQueries);
×
2835
    do {
×
2836
      worker = (worker + 1) % RecThreadInfo::numUDPWorkers();
×
2837
    } while (load[worker] > targetLoad);
×
2838
  }
×
2839

2840
  return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + worker;
×
2841
}
3,611✔
2842

2843
// This function is only called by the distributor threads, when pdns-distributes-queries is set
2844
void distributeAsyncFunction(const string& packet, const pipefunc_t& func)
2845
{
3,611✔
2846
  if (!RecThreadInfo::self().isDistributor()) {
3,611!
2847
    SLOG(g_log << Logger::Error << "distributeAsyncFunction() has been called by a worker (" << RecThreadInfo::id() << ")" << endl,
×
2848
         g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() has been called by a worker")); // tid will be added
×
2849
    _exit(1);
×
2850
  }
×
2851

2852
  bool hashOK = false;
3,611✔
2853
  unsigned int hash = hashQuestion(reinterpret_cast<const uint8_t*>(packet.data()), packet.length(), g_disthashseed, hashOK); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
3,611✔
2854
  if (!hashOK) {
3,611!
2855
    // hashQuestion does detect invalid names, so we might as well punt here instead of in the worker thread
2856
    t_Counters.at(rec::Counter::ignoredCount)++;
×
2857
    throw MOADNSException("too-short (" + std::to_string(packet.length()) + ") or invalid name");
×
2858
  }
×
2859
  unsigned int target = selectWorker(hash);
3,611✔
2860

2861
  ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: pointer ownership
3,611✔
2862
  tmsg->func = func;
3,611✔
2863
  tmsg->wantAnswer = false;
3,611✔
2864

2865
  if (!trySendingQueryToWorker(target, tmsg)) {
3,611!
2866
    /* if this function failed but did not raise an exception, it means that the pipe
2867
       was full, let's try another one */
2868
    unsigned int newTarget = 0;
×
2869
    do {
×
2870
      newTarget = RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + dns_random(RecThreadInfo::numUDPWorkers());
×
2871
    } while (newTarget == target);
×
2872

2873
    if (!trySendingQueryToWorker(newTarget, tmsg)) {
×
2874
      t_Counters.at(rec::Counter::queryPipeFullDrops)++;
×
2875
      delete tmsg; // NOLINT: pointer ownership
×
2876
    }
×
2877
  }
×
2878
  // coverity[leaked_storage]
2879
}
3,611✔
2880

2881
// resend event to everybody chained onto it
2882
static void doResends(MT_t::waiters_t::iterator& iter, const std::shared_ptr<PacketID>& resend, const PacketBuffer& content)
2883
{
7,303✔
2884
  // We close the chain for new entries, since they won't be processed anyway
2885
  iter->key->closed = true;
7,303✔
2886

2887
  if (iter->key->authReqChain.empty()) {
7,303✔
2888
    return;
7,139✔
2889
  }
7,139✔
2890

2891
  auto maxWeight = t_Counters.at(rec::Counter::maxChainWeight);
164✔
2892
  auto weight = iter->key->authReqChain.size() * content.size();
164✔
2893
  if (weight > maxWeight) {
164✔
2894
    t_Counters.at(rec::Counter::maxChainWeight) = weight;
58✔
2895
  }
58✔
2896

2897
  for (auto [fileDesc, qid] : iter->key->authReqChain) {
1,365✔
2898
    auto packetID = std::make_shared<PacketID>(*resend);
1,365✔
2899
    packetID->fd = fileDesc;
1,365✔
2900
    packetID->id = qid;
1,365✔
2901
    g_multiTasker->sendEvent(packetID, &content);
1,365✔
2902
    t_Counters.at(rec::Counter::chainResends)++;
1,365✔
2903
  }
1,365✔
2904
}
164✔
2905

2906
void mthreadSleep(unsigned int jitterMsec)
2907
{
×
2908
  auto neverHappens = std::make_shared<PacketID>();
×
2909
  neverHappens->id = dns_random_uint16();
×
2910
  neverHappens->type = dns_random_uint16();
×
2911
  neverHappens->remote = ComboAddress("100::"); // discard-only
×
2912
  neverHappens->remote.setPort(dns_random_uint16());
×
2913
  neverHappens->fd = -1;
×
2914
  assert(g_multiTasker->waitEvent(neverHappens, nullptr, jitterMsec) != -1); // NOLINT
×
2915
}
×
2916

2917
static void handleUDPServerResponse(int fileDesc, FDMultiplexer::funcparam_t& var)
2918
{
7,302✔
2919
  auto pid = boost::any_cast<std::shared_ptr<PacketID>>(var);
7,302✔
2920
  PacketBuffer packet;
7,302✔
2921
  packet.resize(g_outgoingEDNSBufsize);
7,302✔
2922
  ComboAddress fromaddr;
7,302✔
2923
  socklen_t addrlen = sizeof(fromaddr);
7,302✔
2924

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

2927
  const ssize_t signed_sizeof_sdnsheader = sizeof(dnsheader);
7,302✔
2928

2929
  if (len < 0) {
7,302✔
2930
    // len < 0: error on socket
2931
    t_udpclientsocks->returnSocket(fileDesc);
43✔
2932

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

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

2953
  // We have at least a full header
2954
  packet.resize(len);
7,259✔
2955
  dnsheader dnsheader{};
7,259✔
2956
  memcpy(&dnsheader, &packet.at(0), sizeof(dnsheader));
7,259✔
2957

2958
  auto pident = std::make_shared<PacketID>();
7,259✔
2959
  pident->remote = fromaddr;
7,259✔
2960
  pident->id = dnsheader.id;
7,259✔
2961
  pident->fd = fileDesc;
7,259✔
2962

2963
  if (!dnsheader.qr && g_logCommonErrors) {
7,259!
2964
    SLOG(g_log << Logger::Notice << "Not taking data from question on outgoing socket from " << fromaddr.toStringWithPort() << endl,
×
2965
         g_slogout->info(Logr::Error, "Not taking data from question on outgoing socket", "from", Logging::Loggable(fromaddr)));
×
2966
  }
×
2967

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

2994
  if (!pident->domain.empty()) {
7,259!
2995
    auto iter = g_multiTasker->getWaiters().find(pident);
7,259✔
2996
    if (iter != g_multiTasker->getWaiters().end()) {
7,260✔
2997
      doResends(iter, pident, packet);
7,260✔
2998
    }
7,260✔
2999
  }
7,259✔
3000

3001
retryWithName:
7,259✔
3002

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

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

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