]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/recursordist/pdns_recursor.cc
9f9c8eeacf1d847f89bf794bd4a641210c1a4756
[thirdparty/pdns.git] / 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 #ifdef HAVE_SYSTEMD
35 #include <systemd/sd-daemon.h>
36 #endif
37
38 #ifdef NOD_ENABLED
39 #include "nod.hh"
40 #include "logging.hh"
41 #endif /* NOD_ENABLED */
42
43 thread_local std::shared_ptr<RecursorLua4> t_pdl;
44 thread_local std::shared_ptr<Regex> t_traceRegex;
45 thread_local FDWrapper t_tracefd = -1;
46 thread_local ProtobufServersInfo t_protobufServers;
47 thread_local ProtobufServersInfo t_outgoingProtobufServers;
48
49 thread_local std::unique_ptr<MT_t> g_multiTasker; // the big MTasker
50 std::unique_ptr<MemRecursorCache> g_recCache;
51 std::unique_ptr<NegCache> g_negCache;
52 std::unique_ptr<RecursorPacketCache> g_packetCache;
53
54 thread_local std::unique_ptr<FDMultiplexer> t_fdm;
55 thread_local std::unique_ptr<addrringbuf_t> t_remotes, t_servfailremotes, t_largeanswerremotes, t_bogusremotes;
56 thread_local std::unique_ptr<boost::circular_buffer<pair<DNSName, uint16_t>>> t_queryring, t_servfailqueryring, t_bogusqueryring;
57 thread_local std::shared_ptr<NetmaskGroup> t_allowFrom;
58 thread_local std::shared_ptr<NetmaskGroup> t_allowNotifyFrom;
59 thread_local std::shared_ptr<notifyset_t> t_allowNotifyFor;
60 __thread struct timeval g_now; // timestamp, updated (too) frequently
61
62 using listenSocketsAddresses_t = map<int, ComboAddress>; // is shared across all threads right now
63
64 static listenSocketsAddresses_t g_listenSocketsAddresses; // is shared across all threads right now
65 static set<int> g_fromtosockets; // listen sockets that use 'sendfromto()' mechanism (without actually using sendfromto())
66 NetmaskGroup g_paddingFrom;
67 size_t g_proxyProtocolMaximumSize;
68 size_t g_maxUDPQueriesPerRound;
69 unsigned int g_maxMThreads;
70 unsigned int g_paddingTag;
71 PaddingMode g_paddingMode;
72 uint16_t g_udpTruncationThreshold;
73 std::atomic<bool> g_quiet;
74 bool g_allowNoRD;
75 bool g_logCommonErrors;
76 bool g_reusePort{false};
77 bool g_gettagNeedsEDNSOptions{false};
78 bool g_useKernelTimestamp;
79 std::atomic<uint32_t> g_maxCacheEntries, g_maxPacketCacheEntries;
80 #ifdef HAVE_BOOST_CONTAINER_FLAT_SET_HPP
81 boost::container::flat_set<uint16_t> g_avoidUdpSourcePorts;
82 #else
83 std::set<uint16_t> g_avoidUdpSourcePorts;
84 #endif
85 uint16_t g_minUdpSourcePort;
86 uint16_t g_maxUdpSourcePort;
87 double g_balancingFactor;
88
89 bool g_lowercaseOutgoing;
90 unsigned int g_networkTimeoutMsec;
91 uint16_t g_outgoingEDNSBufsize;
92
93 // Used in Syncres to counts DNSSEC stats for names in a different "universe"
94 GlobalStateHolder<SuffixMatchNode> g_xdnssec;
95 // Used in the Syncres to not throttle certain servers
96 GlobalStateHolder<SuffixMatchNode> g_dontThrottleNames;
97 GlobalStateHolder<NetmaskGroup> g_dontThrottleNetmasks;
98 GlobalStateHolder<SuffixMatchNode> g_DoTToAuthNames;
99 uint64_t g_latencyStatSize;
100
101 LWResult::Result UDPClientSocks::getSocket(const ComboAddress& toaddr, int* fileDesc)
102 {
103 *fileDesc = makeClientSocket(toaddr.sin4.sin_family);
104 if (*fileDesc < 0) { // temporary error - receive exception otherwise
105 return LWResult::Result::OSLimitError;
106 }
107
108 if (connect(*fileDesc, reinterpret_cast<const struct sockaddr*>(&toaddr), toaddr.getSocklen()) < 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast))
109 int err = errno;
110 try {
111 closesocket(*fileDesc);
112 }
113 catch (const PDNSException& e) {
114 SLOG(g_log << Logger::Error << "Error closing UDP socket after connect() failed: " << e.reason << endl,
115 g_slogout->error(Logr::Error, e.reason, "Error closing UDP socket after connect() failed", "exception", Logging::Loggable("PDNSException")));
116 }
117
118 if (err == ENETUNREACH) { // Seth "My Interfaces Are Like A Yo Yo" Arnold special
119 return LWResult::Result::OSLimitError;
120 }
121
122 return LWResult::Result::PermanentError;
123 }
124
125 d_numsocks++;
126 return LWResult::Result::Success;
127 }
128
129 // return a socket to the pool, or simply erase it
130 void UDPClientSocks::returnSocket(int fileDesc)
131 {
132 try {
133 t_fdm->removeReadFD(fileDesc);
134 }
135 catch (const FDMultiplexerException& e) {
136 // we sometimes return a socket that has not yet been assigned to t_fdm
137 }
138
139 try {
140 closesocket(fileDesc);
141 }
142 catch (const PDNSException& e) {
143 SLOG(g_log << Logger::Error << "Error closing returned UDP socket: " << e.reason << endl,
144 g_slogout->error(Logr::Error, e.reason, "Error closing returned UDP socket", "exception", Logging::Loggable("PDNSException")));
145 }
146
147 --d_numsocks;
148 }
149
150 // returns -1 for errors which might go away, throws for ones that won't
151 int UDPClientSocks::makeClientSocket(int family)
152 {
153 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)
154
155 if (ret < 0 && errno == EMFILE) { // this is not a catastrophic error
156 return ret;
157 }
158 if (ret < 0) {
159 int err = errno;
160 throw PDNSException("Making a socket for resolver (family = " + std::to_string(family) + "): " + stringerror(err));
161 }
162
163 // The loop below runs the body with [tries-1 tries-2 ... 1]. Last iteration with tries == 1 is special: it uses a kernel
164 // allocated UDP port.
165 #if !defined(__OpenBSD__)
166 int tries = 10;
167 #else
168 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
169 #endif
170 ComboAddress sin;
171 while (--tries != 0) {
172 in_port_t port = 0;
173
174 if (tries == 1) { // last iteration: fall back to kernel 'random'
175 port = 0;
176 }
177 else {
178 do {
179 port = g_minUdpSourcePort + dns_random(g_maxUdpSourcePort - g_minUdpSourcePort + 1);
180 } while (g_avoidUdpSourcePorts.count(port) != 0);
181 }
182
183 sin = pdns::getQueryLocalAddress(family, port); // does htons for us
184 if (::bind(ret, reinterpret_cast<struct sockaddr*>(&sin), sin.getSocklen()) >= 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
185 break;
186 }
187 }
188
189 int err = errno;
190
191 if (tries == 0) {
192 closesocket(ret);
193 throw PDNSException("Resolver binding to local query client socket on " + sin.toString() + ": " + stringerror(err));
194 }
195
196 try {
197 setReceiveSocketErrors(ret, family);
198 setNonBlocking(ret);
199 }
200 catch (...) {
201 closesocket(ret);
202 throw;
203 }
204 return ret;
205 }
206
207 static void handleGenUDPQueryResponse(int fileDesc, FDMultiplexer::funcparam_t& var)
208 {
209 auto pident = boost::any_cast<std::shared_ptr<PacketID>>(var);
210 PacketBuffer resp;
211 resp.resize(512);
212 ComboAddress fromaddr;
213 socklen_t addrlen = sizeof(fromaddr);
214
215 ssize_t ret = recvfrom(fileDesc, resp.data(), resp.size(), 0, reinterpret_cast<sockaddr*>(&fromaddr), &addrlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
216 if (fromaddr != pident->remote) {
217 SLOG(g_log << Logger::Notice << "Response received from the wrong remote host (" << fromaddr.toStringWithPort() << " instead of " << pident->remote.toStringWithPort() << "), discarding" << endl,
218 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)));
219 }
220
221 t_fdm->removeReadFD(fileDesc);
222 if (ret >= 0) {
223 resp.resize(ret);
224 g_multiTasker->sendEvent(pident, &resp);
225 }
226 else {
227 PacketBuffer empty;
228 g_multiTasker->sendEvent(pident, &empty);
229 // cerr<<"Had some kind of error: "<<ret<<", "<<stringerror()<<endl;
230 }
231 }
232
233 PacketBuffer GenUDPQueryResponse(const ComboAddress& dest, const string& query)
234 {
235 Socket socket(dest.sin4.sin_family, SOCK_DGRAM);
236 socket.setNonBlocking();
237 ComboAddress local = pdns::getQueryLocalAddress(dest.sin4.sin_family, 0);
238
239 socket.bind(local);
240 socket.connect(dest);
241 socket.send(query);
242
243 std::shared_ptr<PacketID> pident = std::make_shared<PacketID>();
244 pident->fd = socket.getHandle();
245 pident->remote = dest;
246 pident->type = 0;
247 t_fdm->addReadFD(socket.getHandle(), handleGenUDPQueryResponse, pident);
248
249 PacketBuffer data;
250 int ret = g_multiTasker->waitEvent(pident, &data, g_networkTimeoutMsec);
251
252 if (ret == 0 || ret == -1) { // timeout
253 t_fdm->removeReadFD(socket.getHandle());
254 }
255 else if (data.empty()) { // error, EOF or other
256 // we could special case this
257 return data;
258 }
259 return data;
260 }
261
262 static void handleUDPServerResponse(int fileDesc, FDMultiplexer::funcparam_t& var);
263
264 thread_local std::unique_ptr<UDPClientSocks> t_udpclientsocks;
265
266 /* these two functions are used by LWRes */
267 LWResult::Result asendto(const void* data, size_t len, int /* flags */,
268 const ComboAddress& toAddress, uint16_t qid, const DNSName& domain, uint16_t qtype, bool ecs, int* fileDesc)
269 {
270
271 auto pident = std::make_shared<PacketID>();
272 pident->domain = domain;
273 pident->remote = toAddress;
274 pident->type = qtype;
275
276 // We cannot merge ECS-enabled queries based on the ECS source only, as the scope
277 // of the response might be narrower, so instead we do not chain ECS-enabled queries
278 // at all.
279 if (!ecs) {
280 // See if there is an existing outstanding request we can chain on to, using partial equivalence
281 // function looking for the same query (qname and qtype) to the same host, but with a different
282 // message ID.
283 auto chain = g_multiTasker->getWaiters().equal_range(pident, PacketIDBirthdayCompare());
284
285 for (; chain.first != chain.second; chain.first++) {
286 // Line below detected an issue with the two ways of ordering PacketIDs (birthday and non-birthday)
287 assert(chain.first->key->domain == pident->domain); // NOLINT
288 // don't chain onto existing chained waiter or a chain already processed
289 if (chain.first->key->fd > -1 && !chain.first->key->closed) {
290 chain.first->key->chain.insert(qid); // we can chain
291 *fileDesc = -1; // gets used in waitEvent / sendEvent later on
292 return LWResult::Result::Success;
293 }
294 }
295 }
296
297 auto ret = t_udpclientsocks->getSocket(toAddress, fileDesc);
298 if (ret != LWResult::Result::Success) {
299 return ret;
300 }
301
302 pident->fd = *fileDesc;
303 pident->id = qid;
304
305 t_fdm->addReadFD(*fileDesc, handleUDPServerResponse, pident);
306 ssize_t sent = send(*fileDesc, data, len, 0);
307
308 int tmp = errno;
309
310 if (sent < 0) {
311 t_udpclientsocks->returnSocket(*fileDesc);
312 errno = tmp; // this is for logging purposes only
313 return LWResult::Result::PermanentError;
314 }
315
316 return LWResult::Result::Success;
317 }
318
319 LWResult::Result arecvfrom(PacketBuffer& packet, int /* flags */, const ComboAddress& fromAddr, size_t& len,
320 uint16_t qid, const DNSName& domain, uint16_t qtype, int fileDesc, const struct timeval& now)
321 {
322 static const unsigned int nearMissLimit = ::arg().asNum("spoof-nearmiss-max");
323
324 auto pident = std::make_shared<PacketID>();
325 pident->fd = fileDesc;
326 pident->id = qid;
327 pident->domain = domain;
328 pident->type = qtype;
329 pident->remote = fromAddr;
330
331 int ret = g_multiTasker->waitEvent(pident, &packet, g_networkTimeoutMsec, &now);
332 len = 0;
333
334 /* -1 means error, 0 means timeout, 1 means a result from handleUDPServerResponse() which might still be an error */
335 if (ret > 0) {
336 /* handleUDPServerResponse() will close the socket for us no matter what */
337 if (packet.empty()) { // means "error"
338 return LWResult::Result::PermanentError;
339 }
340
341 len = packet.size();
342
343 if (nearMissLimit > 0 && pident->nearMisses > nearMissLimit) {
344 /* we have received more than nearMissLimit answers on the right IP and port, from the right source (we are using connected sockets),
345 for the correct qname and qtype, but with an unexpected message ID. That looks like a spoofing attempt. */
346 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,
347 g_slogudpin->info(Logr::Error, "Too many answers with a wrong message ID, assuming spoofing attempt",
348 "nearmisses", Logging::Loggable(pident->nearMisses),
349 "nearmisslimit", Logging::Loggable(nearMissLimit),
350 "qname", Logging::Loggable(domain),
351 "from", Logging::Loggable(fromAddr)));
352 t_Counters.at(rec::Counter::spoofCount)++;
353 return LWResult::Result::Spoofed;
354 }
355
356 return LWResult::Result::Success;
357 }
358 /* getting there means error or timeout, it's up to us to close the socket */
359 if (fileDesc >= 0) {
360 t_udpclientsocks->returnSocket(fileDesc);
361 }
362
363 return ret == 0 ? LWResult::Result::Timeout : LWResult::Result::PermanentError;
364 }
365
366 // the idea is, only do things that depend on the *response* here. Incoming accounting is on incoming.
367 static void updateResponseStats(int res, const ComboAddress& remote, unsigned int packetsize, const DNSName* query, uint16_t qtype)
368 {
369 if (packetsize > 1000 && t_largeanswerremotes) {
370 t_largeanswerremotes->push_back(remote);
371 }
372 switch (res) {
373 case RCode::ServFail:
374 if (t_servfailremotes) {
375 t_servfailremotes->push_back(remote);
376 if (query != nullptr && t_servfailqueryring) { // packet cache
377 t_servfailqueryring->push_back({*query, qtype});
378 }
379 }
380 ++t_Counters.at(rec::Counter::servFails);
381 break;
382 case RCode::NXDomain:
383 ++t_Counters.at(rec::Counter::nxDomains);
384 break;
385 case RCode::NoError:
386 t_Counters.at(rec::Counter::noErrors)++;
387 break;
388 }
389 }
390
391 static string makeLoginfo(const std::unique_ptr<DNSComboWriter>& comboWriter)
392 try {
393 return "(" + comboWriter->d_mdp.d_qname.toLogString() + "/" + DNSRecordContent::NumberToType(comboWriter->d_mdp.d_qtype) + " from " + (comboWriter->getRemote()) + ")";
394 }
395 catch (...) {
396 return "Exception making error message for exception";
397 }
398
399 /**
400 * Chases the CNAME provided by the PolicyCustom RPZ policy.
401 *
402 * @param spoofed: The DNSRecord that was created by the policy, should already be added to ret
403 * @param qtype: The QType of the original query
404 * @param sr: A SyncRes
405 * @param res: An integer that will contain the RCODE of the lookup we do
406 * @param ret: A vector of DNSRecords where the result of the CNAME chase should be appended to
407 */
408 static void handleRPZCustom(const DNSRecord& spoofed, const QType& qtype, SyncRes& resolver, int& res, vector<DNSRecord>& ret)
409 {
410 if (spoofed.d_type == QType::CNAME) {
411 bool oldWantsRPZ = resolver.getWantsRPZ();
412 resolver.setWantsRPZ(false);
413 vector<DNSRecord> ans;
414 res = resolver.beginResolve(DNSName(spoofed.getContent()->getZoneRepresentation()), qtype, QClass::IN, ans);
415 for (const auto& rec : ans) {
416 if (rec.d_place == DNSResourceRecord::ANSWER) {
417 ret.push_back(rec);
418 }
419 }
420 // Reset the RPZ state of the SyncRes
421 resolver.setWantsRPZ(oldWantsRPZ);
422 }
423 }
424
425 static bool addRecordToPacket(DNSPacketWriter& packetWritewr, const DNSRecord& rec, uint32_t& minTTL, uint32_t ttlCap, const uint16_t maxAnswerSize, bool& seenAuthSOA)
426 {
427 packetWritewr.startRecord(rec.d_name, rec.d_type, (rec.d_ttl > ttlCap ? ttlCap : rec.d_ttl), rec.d_class, rec.d_place);
428
429 if (rec.d_type == QType::SOA && rec.d_place == DNSResourceRecord::AUTHORITY) {
430 seenAuthSOA = true;
431 }
432
433 if (rec.d_type != QType::OPT) { // their TTL ain't real
434 minTTL = min(minTTL, rec.d_ttl);
435 }
436
437 rec.getContent()->toPacket(packetWritewr);
438 if (packetWritewr.size() > static_cast<size_t>(maxAnswerSize)) {
439 packetWritewr.rollback();
440 if (rec.d_place != DNSResourceRecord::ADDITIONAL) {
441 packetWritewr.getHeader()->tc = 1;
442 packetWritewr.truncate();
443 }
444 return false;
445 }
446
447 return true;
448 }
449
450 /**
451 * A helper class that handles the TCP in-flight bookkeeping on
452 * destruct. This class ise used by startDoResolve() to not forget
453 * that. You can also signal that the TCP connection must be closed
454 * once the in-flight connections drop to zero.
455 **/
456 class RunningResolveGuard
457 {
458 public:
459 RunningResolveGuard(const RunningResolveGuard&) = default;
460 RunningResolveGuard(RunningResolveGuard&&) = delete;
461 RunningResolveGuard& operator=(const RunningResolveGuard&) = delete;
462 RunningResolveGuard& operator=(RunningResolveGuard&&) = delete;
463 RunningResolveGuard(std::unique_ptr<DNSComboWriter>& comboWriter) :
464 d_dc(comboWriter)
465 {
466 if (d_dc->d_tcp && !d_dc->d_tcpConnection) {
467 throw std::runtime_error("incoming TCP case without TCP connection");
468 }
469 }
470 ~RunningResolveGuard()
471 {
472 if (!d_handled && d_dc->d_tcp) {
473 try {
474 finishTCPReply(d_dc, false, true);
475 }
476 catch (const FDMultiplexerException&) {
477 }
478 }
479 }
480 void setHandled()
481 {
482 d_handled = true;
483 }
484 void setDropOnIdle()
485 {
486 if (d_dc->d_tcp) {
487 d_dc->d_tcpConnection->setDropOnIdle();
488 }
489 }
490
491 private:
492 std::unique_ptr<DNSComboWriter>& d_dc;
493 bool d_handled{false};
494 };
495
496 enum class PolicyResult : uint8_t
497 {
498 NoAction,
499 HaveAnswer,
500 Drop
501 };
502
503 static PolicyResult handlePolicyHit(const DNSFilterEngine::Policy& appliedPolicy, const std::unique_ptr<DNSComboWriter>& comboWriter, SyncRes& resolver, int& res, vector<DNSRecord>& ret, DNSPacketWriter& packetWriter, RunningResolveGuard& tcpGuard)
504 {
505 /* don't account truncate actions for TCP queries, since they are not applied */
506 if (appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::Truncate || !comboWriter->d_tcp) {
507 ++t_Counters.at(rec::PolicyHistogram::policy).at(appliedPolicy.d_kind);
508 ++t_Counters.at(rec::PolicyNameHits::policyName).counts[appliedPolicy.getName()];
509 }
510
511 if (resolver.doLog() && appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
512 SLOG(g_log << Logger::Warning << comboWriter->d_mdp.d_qname << "|" << QType(comboWriter->d_mdp.d_qtype) << appliedPolicy.getLogString() << endl,
513 appliedPolicy.info(Logr::Warning, resolver.d_slog));
514 }
515
516 if (appliedPolicy.d_zoneData && appliedPolicy.d_zoneData->d_extendedErrorCode) {
517 comboWriter->d_extendedErrorCode = *appliedPolicy.d_zoneData->d_extendedErrorCode;
518 comboWriter->d_extendedErrorExtra = appliedPolicy.d_zoneData->d_extendedErrorExtra;
519 }
520
521 switch (appliedPolicy.d_kind) {
522
523 case DNSFilterEngine::PolicyKind::NoAction:
524 return PolicyResult::NoAction;
525
526 case DNSFilterEngine::PolicyKind::Drop:
527 tcpGuard.setDropOnIdle();
528 ++t_Counters.at(rec::Counter::policyDrops);
529 return PolicyResult::Drop;
530
531 case DNSFilterEngine::PolicyKind::NXDOMAIN:
532 ret.clear();
533 appliedPolicy.addSOAtoRPZResult(ret);
534 res = RCode::NXDomain;
535 return PolicyResult::HaveAnswer;
536
537 case DNSFilterEngine::PolicyKind::NODATA:
538 ret.clear();
539 appliedPolicy.addSOAtoRPZResult(ret);
540 res = RCode::NoError;
541 return PolicyResult::HaveAnswer;
542
543 case DNSFilterEngine::PolicyKind::Truncate:
544 if (!comboWriter->d_tcp) {
545 ret.clear();
546 appliedPolicy.addSOAtoRPZResult(ret);
547 res = RCode::NoError;
548 packetWriter.getHeader()->tc = 1;
549 return PolicyResult::HaveAnswer;
550 }
551 return PolicyResult::NoAction;
552
553 case DNSFilterEngine::PolicyKind::Custom:
554 res = RCode::NoError;
555 {
556 auto spoofed = appliedPolicy.getCustomRecords(comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype);
557 for (auto& record : spoofed) {
558 ret.push_back(record);
559 try {
560 handleRPZCustom(record, QType(comboWriter->d_mdp.d_qtype), resolver, res, ret);
561 }
562 catch (const ImmediateServFailException& e) {
563 if (g_logCommonErrors) {
564 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,
565 resolver.d_slog->error(Logr::Notice, e.reason, "Sending SERVFAIL during resolve of the custom filter policy",
566 "policyName", Logging::Loggable(appliedPolicy.getName()), "exception", Logging::Loggable("ImmediateServFailException")));
567 }
568 res = RCode::ServFail;
569 break;
570 }
571 catch (const PolicyHitException& e) {
572 if (g_logCommonErrors) {
573 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,
574 resolver.d_slog->info(Logr::Notice, "Sending SERVFAIL during resolve of the custom filter policy because another RPZ policy was hit",
575 "policyName", Logging::Loggable(appliedPolicy.getName()), "exception", Logging::Loggable("PolicyHitException")));
576 }
577 res = RCode::ServFail;
578 break;
579 }
580 }
581
582 appliedPolicy.addSOAtoRPZResult(ret);
583 return PolicyResult::HaveAnswer;
584 }
585 }
586
587 return PolicyResult::NoAction;
588 }
589
590 #ifdef NOD_ENABLED
591 static bool nodCheckNewDomain(Logr::log_t nodlogger, const DNSName& dname)
592 {
593 bool ret = false;
594 // First check the (sub)domain isn't ignored for NOD purposes
595 if (!g_nodDomainWL.check(dname)) {
596 // Now check the NODDB (note this is probabilistic so can have FNs/FPs)
597 if (t_nodDBp && t_nodDBp->isNewDomain(dname)) {
598 if (g_nodLog) {
599 // This should probably log to a dedicated log file
600 SLOG(g_log << Logger::Notice << "Newly observed domain nod=" << dname << endl,
601 nodlogger->info(Logr::Notice, "New domain observed"));
602 }
603 t_Counters.at(rec::Counter::nodCount)++;
604 ret = true;
605 }
606 }
607 return ret;
608 }
609
610 static void sendNODLookup(Logr::log_t nodlogger, const DNSName& dname)
611 {
612 if (!(g_nodLookupDomain.isRoot())) {
613 // Send a DNS A query to <domain>.g_nodLookupDomain
614 DNSName qname;
615 try {
616 qname = dname + g_nodLookupDomain;
617 }
618 catch (const std::range_error& e) {
619 if (g_logCommonErrors) {
620 nodlogger->v(10)->error(Logr::Error, "DNSName too long", "Unable to send NOD lookup");
621 }
622 ++t_Counters.at(rec::Counter::nodLookupsDroppedOversize);
623 return;
624 }
625 nodlogger->v(10)->info(Logr::Debug, "Sending NOD lookup", "nodqname", Logging::Loggable(qname));
626 vector<DNSRecord> dummy;
627 directResolve(qname, QType::A, QClass::IN, dummy, nullptr, false, nodlogger);
628 }
629 }
630
631 static bool udrCheckUniqueDNSRecord(Logr::log_t nodlogger, const DNSName& dname, uint16_t qtype, const DNSRecord& record)
632 {
633 bool ret = false;
634 if (record.d_place == DNSResourceRecord::ANSWER || record.d_place == DNSResourceRecord::ADDITIONAL) {
635 // Create a string that represent a triplet of (qname, qtype and RR[type, name, content])
636 std::stringstream strStream;
637 strStream << dname.toDNSStringLC() << ":" << qtype << ":" << qtype << ":" << record.d_type << ":" << record.d_name.toDNSStringLC() << ":" << record.getContent()->getZoneRepresentation();
638 if (t_udrDBp && t_udrDBp->isUniqueResponse(strStream.str())) {
639 if (g_udrLog) {
640 // This should also probably log to a dedicated file.
641 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,
642 nodlogger->info(Logr::Notice, "New response observed",
643 "qtype", Logging::Loggable(QType(qtype)),
644 "rrtype", Logging::Loggable(QType(record.d_type)),
645 "rrname", Logging::Loggable(record.d_name),
646 "rrcontent", Logging::Loggable(record.getContent()->getZoneRepresentation())););
647 }
648 t_Counters.at(rec::Counter::udrCount)++;
649 ret = true;
650 }
651 }
652 return ret;
653 }
654 #endif /* NOD_ENABLED */
655
656 static bool dns64Candidate(uint16_t requestedType, int rcode, const std::vector<DNSRecord>& records);
657
658 int followCNAMERecords(vector<DNSRecord>& ret, const QType qtype, int rcode)
659 {
660 vector<DNSRecord> resolved;
661 DNSName target;
662 for (const DNSRecord& record : ret) {
663 if (record.d_type == QType::CNAME) {
664 auto rec = getRR<CNAMERecordContent>(record);
665 if (rec) {
666 target = rec->getTarget();
667 break;
668 }
669 }
670 }
671
672 if (target.empty()) {
673 return rcode;
674 }
675
676 auto log = g_slog->withName("lua")->withValues("method", Logging::Loggable("followCNAMERecords"));
677 rcode = directResolve(target, qtype, QClass::IN, resolved, t_pdl, log);
678
679 if (g_dns64Prefix && qtype == QType::AAAA && dns64Candidate(qtype, rcode, resolved)) {
680 rcode = getFakeAAAARecords(target, *g_dns64Prefix, resolved);
681 }
682
683 for (DNSRecord& record : resolved) {
684 if (record.d_place == DNSResourceRecord::ANSWER) {
685 ret.push_back(std::move(record));
686 }
687 }
688 return rcode;
689 }
690
691 int getFakeAAAARecords(const DNSName& qname, ComboAddress prefix, vector<DNSRecord>& ret)
692 {
693 auto log = g_slog->withName("dns64")->withValues("method", Logging::Loggable("getAAAA"));
694 /* we pass a separate vector of records because we will be resolving the initial qname
695 again, possibly encountering the same CNAME(s), and we don't want to trigger the CNAME
696 loop detection. */
697 vector<DNSRecord> newRecords;
698 int rcode = directResolve(qname, QType::A, QClass::IN, newRecords, t_pdl, log);
699
700 ret.reserve(ret.size() + newRecords.size());
701 for (auto& record : newRecords) {
702 ret.push_back(std::move(record));
703 }
704
705 // Remove double CNAME records
706 std::set<DNSName> seenCNAMEs;
707 ret.erase(std::remove_if(
708 ret.begin(),
709 ret.end(),
710 [&seenCNAMEs](DNSRecord& record) {
711 if (record.d_type == QType::CNAME) {
712 auto target = getRR<CNAMERecordContent>(record);
713 if (target == nullptr) {
714 return false;
715 }
716 if (seenCNAMEs.count(target->getTarget()) > 0) {
717 // We've had this CNAME before, remove it
718 return true;
719 }
720 seenCNAMEs.insert(target->getTarget());
721 }
722 return false;
723 }),
724 ret.end());
725
726 bool seenA = false;
727 for (DNSRecord& record : ret) {
728 if (record.d_type == QType::A && record.d_place == DNSResourceRecord::ANSWER) {
729 if (auto rec = getRR<ARecordContent>(record)) {
730 ComboAddress ipv4(rec->getCA());
731 memcpy(&prefix.sin6.sin6_addr.s6_addr[12], &ipv4.sin4.sin_addr.s_addr, sizeof(ipv4.sin4.sin_addr.s_addr));
732 record.setContent(std::make_shared<AAAARecordContent>(prefix));
733 record.d_type = QType::AAAA;
734 }
735 seenA = true;
736 }
737 }
738
739 if (seenA) {
740 // We've seen an A in the ANSWER section, so there is no need to keep any
741 // SOA in the AUTHORITY section as this is not a NODATA response.
742 ret.erase(std::remove_if(
743 ret.begin(),
744 ret.end(),
745 [](DNSRecord& record) {
746 return (record.d_type == QType::SOA && record.d_place == DNSResourceRecord::AUTHORITY);
747 }),
748 ret.end());
749 }
750 t_Counters.at(rec::Counter::dns64prefixanswers)++;
751 return rcode;
752 }
753
754 int getFakePTRRecords(const DNSName& qname, vector<DNSRecord>& ret)
755 {
756 /* qname has a reverse ordered IPv6 address, need to extract the underlying IPv4 address from it
757 and turn it into an IPv4 in-addr.arpa query */
758 ret.clear();
759 vector<string> parts = qname.getRawLabels();
760
761 if (parts.size() < 8) {
762 return -1;
763 }
764
765 string newquery;
766 for (size_t octet = 0; octet < 4; ++octet) {
767 newquery += std::to_string(stoll(parts[octet * 2], nullptr, 16) + 16 * stoll(parts[octet * 2 + 1], nullptr, 16));
768 newquery.append(1, '.');
769 }
770 newquery += "in-addr.arpa.";
771
772 auto log = g_slog->withName("dns64")->withValues("method", Logging::Loggable("getPTR"));
773 vector<DNSRecord> answers;
774 int rcode = directResolve(DNSName(newquery), QType::PTR, QClass::IN, answers, t_pdl, log);
775
776 DNSRecord record;
777 record.d_name = qname;
778 record.d_type = QType::CNAME;
779 record.setContent(std::make_shared<CNAMERecordContent>(newquery));
780 // Copy the TTL of the synthesized CNAME from the actual answer
781 record.d_ttl = (rcode == RCode::NoError && !answers.empty()) ? answers.at(0).d_ttl : SyncRes::s_minimumTTL;
782 ret.push_back(record);
783
784 ret.insert(ret.end(), answers.begin(), answers.end());
785
786 t_Counters.at(rec::Counter::dns64prefixanswers)++;
787 return rcode;
788 }
789
790 // RFC 6147 section 5.1 all rcodes except NXDomain should be candidate for dns64
791 // for NoError, check if it is NoData
792 static bool dns64Candidate(uint16_t requestedType, int rcode, const std::vector<DNSRecord>& records)
793 {
794 if (rcode == RCode::NoError) {
795 return SyncRes::answerIsNOData(requestedType, rcode, records);
796 }
797 return rcode != RCode::NXDomain;
798 }
799
800 bool isAllowNotifyForZone(DNSName qname)
801 {
802 if (t_allowNotifyFor->empty()) {
803 return false;
804 }
805
806 notifyset_t::const_iterator ret;
807 do {
808 ret = t_allowNotifyFor->find(qname);
809 if (ret != t_allowNotifyFor->end()) {
810 return true;
811 }
812 } while (qname.chopOff());
813 return false;
814 }
815
816 #if defined(HAVE_FSTRM) && defined(NOD_ENABLED)
817 #include "dnstap.hh"
818 #include "fstrm_logger.hh"
819
820 static bool isEnabledForNODs(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
821 {
822 if (fstreamLoggers == nullptr) {
823 return false;
824 }
825 for (auto& logger : *fstreamLoggers) {
826 if (logger->logNODs()) {
827 return true;
828 }
829 }
830 return false;
831 }
832 static bool isEnabledForUDRs(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
833 {
834 if (fstreamLoggers == nullptr) {
835 return false;
836 }
837 for (auto& logger : *fstreamLoggers) {
838 if (logger->logUDRs()) {
839 return true;
840 }
841 }
842 return false;
843 }
844 #endif // HAVE_FSTRM
845
846 static void dumpTrace(const string& trace, const timeval& timev)
847 {
848 if (trace.empty()) {
849 return;
850 }
851 timeval now{};
852 Utility::gettimeofday(&now);
853 int traceFd = dup(t_tracefd);
854 if (traceFd == -1) {
855 int err = errno;
856 SLOG(g_log << Logger::Error << "Could not dup trace file: " << stringerror(err) << endl,
857 g_slog->withName("trace")->error(Logr::Error, err, "Could not dup trace file"));
858 return;
859 }
860 setNonBlocking(traceFd);
861 auto filep = std::unique_ptr<FILE, decltype(&fclose)>(fdopen(traceFd, "a"), &fclose);
862 if (!filep) {
863 int err = errno;
864 SLOG(g_log << Logger::Error << "Could not write to trace file: " << stringerror(err) << endl,
865 g_slog->withName("trace")->error(Logr::Error, err, "Could not write to trace file"));
866 close(traceFd);
867 return;
868 }
869 timebuf_t timebuf;
870 isoDateTimeMillis(timev, timebuf);
871 fprintf(filep.get(), " us === START OF TRACE %s ===\n", timebuf.data());
872 fprintf(filep.get(), "%s", trace.c_str());
873 isoDateTimeMillis(now, timebuf);
874 if (ferror(filep.get()) != 0) {
875 int err = errno;
876 SLOG(g_log << Logger::Error << "Problems writing to trace file: " << stringerror(err) << endl,
877 g_slog->withName("trace")->error(Logr::Error, err, "Problems writing to trace file"));
878 // There's no guarantee the message below will end up in the stream, but we try our best
879 clearerr(filep.get());
880 fprintf(filep.get(), "=== TRACE %s TRUNCATED; USE FILE ARGUMENT INSTEAD OF `-' ===\n", timebuf.data());
881 }
882 else {
883 fprintf(filep.get(), "=== END OF TRACE %s ===\n", timebuf.data());
884 }
885 // fclose by unique_ptr does implicit flush
886 }
887
888 static uint32_t capPacketCacheTTL(const struct dnsheader& hdr, uint32_t ttl, bool seenAuthSOA)
889 {
890 if (hdr.rcode == RCode::NXDomain || (hdr.rcode == RCode::NoError && hdr.ancount == 0 && seenAuthSOA)) {
891 ttl = std::min(ttl, SyncRes::s_packetcachenegativettl);
892 }
893 else if ((hdr.rcode != RCode::NoError && hdr.rcode != RCode::NXDomain) || (hdr.ancount == 0 && hdr.nscount == 0)) {
894 ttl = min(ttl, SyncRes::s_packetcacheservfailttl);
895 }
896 else {
897 ttl = std::min(ttl, SyncRes::s_packetcachettl);
898 }
899 return ttl;
900 }
901
902 static void addPolicyTagsToPBMessageIfNeeded(DNSComboWriter& comboWriter, pdns::ProtoZero::RecMessage& pbMessage)
903 {
904 /* we do _not_ want to store policy tags set by the gettag hook into the packet cache,
905 since the call to gettag for subsequent queries could yield the same PC tag but different policy tags */
906 if (!comboWriter.d_gettagPolicyTags.empty()) {
907 for (const auto& tag : comboWriter.d_gettagPolicyTags) {
908 comboWriter.d_policyTags.erase(tag);
909 }
910 }
911 if (!comboWriter.d_policyTags.empty()) {
912 pbMessage.addPolicyTags(comboWriter.d_policyTags);
913 }
914 }
915
916 void startDoResolve(void* arg) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
917 {
918 auto comboWriter = std::unique_ptr<DNSComboWriter>(static_cast<DNSComboWriter*>(arg));
919 SyncRes resolver(comboWriter->d_now);
920 try {
921 if (t_queryring) {
922 t_queryring->push_back({comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype});
923 }
924
925 uint16_t maxanswersize = comboWriter->d_tcp ? 65535 : min(static_cast<uint16_t>(512), g_udpTruncationThreshold);
926 EDNSOpts edo;
927 std::vector<pair<uint16_t, string>> ednsOpts;
928 bool variableAnswer = comboWriter->d_variable;
929 bool haveEDNS = false;
930 bool paddingAllowed = false;
931 bool addPaddingToResponse = false;
932 #ifdef NOD_ENABLED
933 bool hasUDR = false;
934 std::shared_ptr<Logr::Logger> nodlogger{nullptr};
935 if (g_udrEnabled || g_nodEnabled) {
936 nodlogger = g_slog->withName("nod")->v(1)->withValues("qname", Logging::Loggable(comboWriter->d_mdp.d_qname));
937 }
938 #endif /* NOD_ENABLED */
939 DNSPacketWriter::optvect_t returnedEdnsOptions; // Here we stuff all the options for the return packet
940 uint8_t ednsExtRCode = 0;
941 if (getEDNSOpts(comboWriter->d_mdp, &edo)) {
942 haveEDNS = true;
943 if (edo.d_version != 0) {
944 ednsExtRCode = ERCode::BADVERS;
945 }
946
947 if (!comboWriter->d_tcp) {
948 /* rfc6891 6.2.3:
949 "Values lower than 512 MUST be treated as equal to 512."
950 */
951 maxanswersize = min(static_cast<uint16_t>(edo.d_packetsize >= 512 ? edo.d_packetsize : 512), g_udpTruncationThreshold);
952 }
953 ednsOpts = edo.d_options;
954 maxanswersize -= 11; // EDNS header size
955
956 if (!comboWriter->d_responsePaddingDisabled && g_paddingFrom.match(comboWriter->d_remote)) {
957 paddingAllowed = true;
958 if (g_paddingMode == PaddingMode::Always) {
959 addPaddingToResponse = true;
960 }
961 }
962
963 for (const auto& option : edo.d_options) {
964 if (option.first == EDNSOptionCode::ECS && g_useIncomingECS && !comboWriter->d_ecsParsed) {
965 comboWriter->d_ecsFound = getEDNSSubnetOptsFromString(option.second, &comboWriter->d_ednssubnet);
966 }
967 else if (option.first == EDNSOptionCode::NSID) {
968 const static string mode_server_id = ::arg()["server-id"];
969 if (mode_server_id != "disabled" && !mode_server_id.empty() && maxanswersize > (EDNSOptionCodeSize + EDNSOptionLengthSize + mode_server_id.size())) {
970 returnedEdnsOptions.emplace_back(EDNSOptionCode::NSID, mode_server_id);
971 variableAnswer = true; // Can't packetcache an answer with NSID
972 maxanswersize -= EDNSOptionCodeSize + EDNSOptionLengthSize + mode_server_id.size();
973 }
974 }
975 else if (paddingAllowed && !addPaddingToResponse && g_paddingMode == PaddingMode::PaddedQueries && option.first == EDNSOptionCode::PADDING) {
976 addPaddingToResponse = true;
977 }
978 }
979 }
980
981 /* the lookup will be done _before_ knowing whether the query actually
982 has a padding option, so we need to use the separate tag even when the
983 query does not have padding, as long as it is from an allowed source */
984 if (paddingAllowed && comboWriter->d_tag == 0) {
985 comboWriter->d_tag = g_paddingTag;
986 }
987
988 /* perhaps there was no EDNS or no ECS but by now we looked */
989 comboWriter->d_ecsParsed = true;
990 vector<DNSRecord> ret;
991 vector<uint8_t> packet;
992
993 auto luaconfsLocal = g_luaconfs.getLocal();
994 // Used to tell syncres later on if we should apply NSDNAME and NSIP RPZ triggers for this query
995 bool wantsRPZ(true);
996 RecursorPacketCache::OptPBData pbDataForCache;
997 pdns::ProtoZero::RecMessage pbMessage;
998 if (checkProtobufExport(luaconfsLocal)) {
999 pbMessage.reserve(128, 128); // It's a bit of a guess...
1000 pbMessage.setResponse(comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype, comboWriter->d_mdp.d_qclass);
1001 pbMessage.setServerIdentity(SyncRes::s_serverID);
1002
1003 // RRSets added below
1004 }
1005 checkOutgoingProtobufExport(luaconfsLocal); // to pick up changed configs
1006 #ifdef HAVE_FSTRM
1007 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
1008 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->nodFrameStreamExportConfig, t_nodFrameStreamServersInfo);
1009 #endif
1010
1011 DNSPacketWriter packetWriter(packet, comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype, comboWriter->d_mdp.d_qclass, comboWriter->d_mdp.d_header.opcode);
1012
1013 packetWriter.getHeader()->aa = 0;
1014 packetWriter.getHeader()->ra = 1;
1015 packetWriter.getHeader()->qr = 1;
1016 packetWriter.getHeader()->tc = 0;
1017 packetWriter.getHeader()->id = comboWriter->d_mdp.d_header.id;
1018 packetWriter.getHeader()->rd = comboWriter->d_mdp.d_header.rd;
1019 packetWriter.getHeader()->cd = comboWriter->d_mdp.d_header.cd;
1020
1021 /* This is the lowest TTL seen in the records of the response,
1022 so we can't cache it for longer than this value.
1023 If we have a TTL cap, this value can't be larger than the
1024 cap no matter what. */
1025 uint32_t minTTL = comboWriter->d_ttlCap;
1026 bool seenAuthSOA = false;
1027
1028 resolver.d_eventTrace = std::move(comboWriter->d_eventTrace);
1029 resolver.setId(g_multiTasker->getTid());
1030
1031 bool DNSSECOK = false;
1032 if (comboWriter->d_luaContext) {
1033 resolver.setLuaEngine(comboWriter->d_luaContext);
1034 }
1035 if (g_dnssecmode != DNSSECMode::Off) {
1036 resolver.setDoDNSSEC(true);
1037
1038 // Does the requestor want DNSSEC records?
1039 if ((edo.d_extFlags & EDNSOpts::DNSSECOK) != 0) {
1040 DNSSECOK = true;
1041 t_Counters.at(rec::Counter::dnssecQueries)++;
1042 }
1043 if (comboWriter->d_mdp.d_header.cd) {
1044 /* Per rfc6840 section 5.9, "When processing a request with
1045 the Checking Disabled (CD) bit set, a resolver SHOULD attempt
1046 to return all response data, even data that has failed DNSSEC
1047 validation. */
1048 ++t_Counters.at(rec::Counter::dnssecCheckDisabledQueries);
1049 }
1050 if (comboWriter->d_mdp.d_header.ad) {
1051 /* Per rfc6840 section 5.7, "the AD bit in a query as a signal
1052 indicating that the requester understands and is interested in the
1053 value of the AD bit in the response. This allows a requester to
1054 indicate that it understands the AD bit without also requesting
1055 DNSSEC data via the DO bit. */
1056 ++t_Counters.at(rec::Counter::dnssecAuthenticDataQueries);
1057 }
1058 }
1059 else {
1060 // Ignore the client-set CD flag
1061 packetWriter.getHeader()->cd = 0;
1062 }
1063 resolver.setDNSSECValidationRequested(g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode == DNSSECMode::ValidateForLog || ((comboWriter->d_mdp.d_header.ad || DNSSECOK) && g_dnssecmode == DNSSECMode::Process));
1064
1065 resolver.setInitialRequestId(comboWriter->d_uuid);
1066 resolver.setOutgoingProtobufServers(t_outgoingProtobufServers.servers);
1067 #ifdef HAVE_FSTRM
1068 resolver.setFrameStreamServers(t_frameStreamServersInfo.servers);
1069 #endif
1070
1071 bool useMapped = true;
1072 // 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
1073 // (if a domain suffix match table is present in the config)
1074 if (t_proxyMapping && comboWriter->d_source != comboWriter->d_mappedSource) {
1075 if (const auto* iter = t_proxyMapping->lookup(comboWriter->d_source)) {
1076 if (iter->second.suffixMatchNode) {
1077 if (!iter->second.suffixMatchNode->check(comboWriter->d_mdp.d_qname)) {
1078 // No match in domains, use original source
1079 useMapped = false;
1080 }
1081 else {
1082 ++iter->second.stats.suffixMatches;
1083 }
1084 }
1085 // No suffix match node defined, use mapped address
1086 }
1087 // lookup failing cannot happen as dc->d_source != dc->d_mappedSource
1088 }
1089 resolver.setQuerySource(useMapped ? comboWriter->d_mappedSource : comboWriter->d_source, g_useIncomingECS && !comboWriter->d_ednssubnet.source.empty() ? boost::optional<const EDNSSubnetOpts&>(comboWriter->d_ednssubnet) : boost::none);
1090
1091 resolver.setQueryReceivedOverTCP(comboWriter->d_tcp);
1092
1093 bool tracedQuery = false; // we could consider letting Lua know about this too
1094 bool shouldNotValidate = false;
1095
1096 /* preresolve expects res (dq.rcode) to be set to RCode::NoError by default */
1097 int res = RCode::NoError;
1098
1099 DNSFilterEngine::Policy appliedPolicy;
1100 RecursorLua4::DNSQuestion dnsQuestion(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);
1101 dnsQuestion.ednsFlags = &edo.d_extFlags;
1102 dnsQuestion.ednsOptions = &ednsOpts;
1103 dnsQuestion.tag = comboWriter->d_tag;
1104 dnsQuestion.discardedPolicies = &resolver.d_discardedPolicies;
1105 dnsQuestion.policyTags = &comboWriter->d_policyTags;
1106 dnsQuestion.appliedPolicy = &appliedPolicy;
1107 dnsQuestion.currentRecords = &ret;
1108 dnsQuestion.dh = &comboWriter->d_mdp.d_header;
1109 dnsQuestion.data = comboWriter->d_data;
1110 dnsQuestion.requestorId = comboWriter->d_requestorId;
1111 dnsQuestion.deviceId = comboWriter->d_deviceId;
1112 dnsQuestion.deviceName = comboWriter->d_deviceName;
1113 dnsQuestion.proxyProtocolValues = &comboWriter->d_proxyProtocolValues;
1114 dnsQuestion.extendedErrorCode = &comboWriter->d_extendedErrorCode;
1115 dnsQuestion.extendedErrorExtra = &comboWriter->d_extendedErrorExtra;
1116 dnsQuestion.meta = std::move(comboWriter->d_meta);
1117 dnsQuestion.fromAuthIP = &resolver.d_fromAuthIP;
1118
1119 resolver.d_slog = resolver.d_slog->withValues("qname", Logging::Loggable(comboWriter->d_mdp.d_qname),
1120 "qtype", Logging::Loggable(QType(comboWriter->d_mdp.d_qtype)),
1121 "remote", Logging::Loggable(comboWriter->getRemote()),
1122 "proto", Logging::Loggable(comboWriter->d_tcp ? "tcp" : "udp"),
1123 "ecs", Logging::Loggable(comboWriter->d_ednssubnet.source.empty() ? "" : comboWriter->d_ednssubnet.source.toString()),
1124 "mtid", Logging::Loggable(g_multiTasker->getTid()));
1125 RunningResolveGuard tcpGuard(comboWriter);
1126
1127 if (ednsExtRCode != 0 || comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Notify)) {
1128 goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
1129 }
1130
1131 if (comboWriter->d_mdp.d_qtype == QType::ANY && !comboWriter->d_tcp && g_anyToTcp) {
1132 packetWriter.getHeader()->tc = 1;
1133 res = 0;
1134 variableAnswer = true;
1135 goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
1136 }
1137
1138 if (t_traceRegex && t_traceRegex->match(comboWriter->d_mdp.d_qname.toString())) {
1139 resolver.setLogMode(SyncRes::Store);
1140 tracedQuery = true;
1141 }
1142
1143 if (!g_quiet || tracedQuery) {
1144 if (!g_slogStructured) {
1145 g_log << Logger::Warning << RecThreadInfo::id() << " [" << g_multiTasker->getTid() << "/" << g_multiTasker->numProcesses() << "] " << (comboWriter->d_tcp ? "TCP " : "") << "question for '" << comboWriter->d_mdp.d_qname << "|"
1146 << QType(comboWriter->d_mdp.d_qtype) << "' from " << comboWriter->getRemote();
1147 if (!comboWriter->d_ednssubnet.source.empty()) {
1148 g_log << " (ecs " << comboWriter->d_ednssubnet.source.toString() << ")";
1149 }
1150 g_log << endl;
1151 }
1152 else {
1153 resolver.d_slog->info(Logr::Info, "Question");
1154 }
1155 }
1156
1157 if (!comboWriter->d_mdp.d_header.rd) {
1158 if (g_allowNoRD) {
1159 resolver.setCacheOnly();
1160 }
1161 else {
1162 ret.clear();
1163 res = RCode::Refused;
1164 goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
1165 }
1166 }
1167
1168 if (comboWriter->d_luaContext) {
1169 comboWriter->d_luaContext->prerpz(dnsQuestion, res, resolver.d_eventTrace);
1170 }
1171
1172 // Check if the client has a policy attached to it
1173 if (wantsRPZ && !appliedPolicy.wasHit()) {
1174
1175 if (luaconfsLocal->dfe.getClientPolicy(comboWriter->d_source, resolver.d_discardedPolicies, appliedPolicy)) {
1176 mergePolicyTags(comboWriter->d_policyTags, appliedPolicy.getTags());
1177 }
1178 }
1179
1180 /* If we already have an answer generated from gettag_ffi, let's see if the filtering policies
1181 should be applied to it */
1182 if (comboWriter->d_rcode != boost::none) {
1183
1184 bool policyOverride = false;
1185 /* Unless we already matched on the client IP, time to check the qname.
1186 We normally check it in beginResolve() but it will be bypassed since we already have an answer */
1187 if (wantsRPZ && appliedPolicy.policyOverridesGettag()) {
1188 if (appliedPolicy.d_type != DNSFilterEngine::PolicyType::None) {
1189 // Client IP already matched
1190 }
1191 else {
1192 // no match on the client IP, check the qname
1193 if (luaconfsLocal->dfe.getQueryPolicy(comboWriter->d_mdp.d_qname, resolver.d_discardedPolicies, appliedPolicy)) {
1194 // got a match
1195 mergePolicyTags(comboWriter->d_policyTags, appliedPolicy.getTags());
1196 }
1197 }
1198
1199 if (appliedPolicy.wasHit()) {
1200 policyOverride = true;
1201 }
1202 }
1203
1204 if (!policyOverride) {
1205 /* No RPZ or gettag overrides it anyway */
1206 ret = std::move(comboWriter->d_records);
1207 res = *comboWriter->d_rcode;
1208 if (res == RCode::NoError && comboWriter->d_followCNAMERecords) {
1209 res = followCNAMERecords(ret, QType(comboWriter->d_mdp.d_qtype), res);
1210 }
1211 goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
1212 }
1213 }
1214
1215 // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve
1216 if (!comboWriter->d_luaContext || !comboWriter->d_luaContext->preresolve(dnsQuestion, res, resolver.d_eventTrace)) {
1217
1218 if (!g_dns64PrefixReverse.empty() && dnsQuestion.qtype == QType::PTR && dnsQuestion.qname.isPartOf(g_dns64PrefixReverse)) {
1219 res = getFakePTRRecords(dnsQuestion.qname, ret);
1220 goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
1221 }
1222
1223 resolver.setWantsRPZ(wantsRPZ);
1224
1225 if (wantsRPZ && appliedPolicy.d_kind != DNSFilterEngine::PolicyKind::NoAction) {
1226
1227 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)) {
1228 /* reset to no match */
1229 appliedPolicy = DNSFilterEngine::Policy();
1230 }
1231 else {
1232 auto policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
1233 if (policyResult == PolicyResult::HaveAnswer) {
1234 if (g_dns64Prefix && dnsQuestion.qtype == QType::AAAA && dns64Candidate(comboWriter->d_mdp.d_qtype, res, ret)) {
1235 res = getFakeAAAARecords(dnsQuestion.qname, *g_dns64Prefix, ret);
1236 shouldNotValidate = true;
1237 }
1238 goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
1239 }
1240 else if (policyResult == PolicyResult::Drop) {
1241 return;
1242 }
1243 }
1244 }
1245
1246 // Query did not get handled for Client IP or QNAME Policy reasons, now actually go out to find an answer
1247 try {
1248 resolver.d_appliedPolicy = appliedPolicy;
1249 resolver.d_policyTags = std::move(comboWriter->d_policyTags);
1250
1251 if (!comboWriter->d_routingTag.empty()) {
1252 resolver.d_routingTag = comboWriter->d_routingTag;
1253 }
1254
1255 ret.clear(); // policy might have filled it with custom records but we decided not to use them
1256 res = resolver.beginResolve(comboWriter->d_mdp.d_qname, QType(comboWriter->d_mdp.d_qtype), comboWriter->d_mdp.d_qclass, ret);
1257 shouldNotValidate = resolver.wasOutOfBand();
1258 }
1259 catch (const ImmediateQueryDropException& e) {
1260 // XXX We need to export a protobuf message (and do a NOD lookup) if requested!
1261 t_Counters.at(rec::Counter::policyDrops)++;
1262 SLOG(g_log << Logger::Debug << "Dropping query because of a filtering policy " << makeLoginfo(comboWriter) << endl,
1263 resolver.d_slog->info(Logr::Debug, "Dropping query because of a filtering policy"));
1264 return;
1265 }
1266 catch (const ImmediateServFailException& e) {
1267 if (g_logCommonErrors) {
1268 SLOG(g_log << Logger::Notice << "Sending SERVFAIL to " << comboWriter->getRemote() << " during resolve of '" << comboWriter->d_mdp.d_qname << "' because: " << e.reason << endl,
1269 resolver.d_slog->error(Logr::Notice, e.reason, "Sending SERVFAIL during resolve"));
1270 }
1271 res = RCode::ServFail;
1272 }
1273 catch (const SendTruncatedAnswerException& e) {
1274 ret.clear();
1275 resolver.d_appliedPolicy.addSOAtoRPZResult(ret);
1276 res = RCode::NoError;
1277 packetWriter.getHeader()->tc = 1;
1278 }
1279 catch (const PolicyHitException& e) {
1280 res = -2;
1281 }
1282 dnsQuestion.validationState = resolver.getValidationState();
1283 appliedPolicy = resolver.d_appliedPolicy;
1284 comboWriter->d_policyTags = std::move(resolver.d_policyTags);
1285
1286 if (appliedPolicy.d_type != DNSFilterEngine::PolicyType::None && appliedPolicy.d_zoneData && appliedPolicy.d_zoneData->d_extendedErrorCode) {
1287 comboWriter->d_extendedErrorCode = *appliedPolicy.d_zoneData->d_extendedErrorCode;
1288 comboWriter->d_extendedErrorExtra = appliedPolicy.d_zoneData->d_extendedErrorExtra;
1289 }
1290
1291 // During lookup, an NSDNAME or NSIP trigger was hit in RPZ
1292 if (res == -2) { // XXX This block should be macro'd, it is repeated post-resolve.
1293 if (appliedPolicy.d_kind == DNSFilterEngine::PolicyKind::NoAction) {
1294 throw PDNSException("NoAction policy returned while a NSDNAME or NSIP trigger was hit");
1295 }
1296 auto policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
1297 if (policyResult == PolicyResult::HaveAnswer) {
1298 goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
1299 }
1300 else if (policyResult == PolicyResult::Drop) {
1301 return;
1302 }
1303 }
1304
1305 bool luaHookHandled = false;
1306 if (comboWriter->d_luaContext) {
1307 PolicyResult policyResult = PolicyResult::NoAction;
1308 if (SyncRes::answerIsNOData(comboWriter->d_mdp.d_qtype, res, ret)) {
1309 if (comboWriter->d_luaContext->nodata(dnsQuestion, res, resolver.d_eventTrace)) {
1310 luaHookHandled = true;
1311 shouldNotValidate = true;
1312 policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
1313 }
1314 }
1315 else if (res == RCode::NXDomain && comboWriter->d_luaContext->nxdomain(dnsQuestion, res, resolver.d_eventTrace)) {
1316 luaHookHandled = true;
1317 shouldNotValidate = true;
1318 policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
1319 }
1320 if (policyResult == PolicyResult::HaveAnswer) {
1321 goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
1322 }
1323 else if (policyResult == PolicyResult::Drop) {
1324 return;
1325 }
1326 } // dc->d_luaContext
1327
1328 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)) {
1329 res = getFakeAAAARecords(dnsQuestion.qname, *g_dns64Prefix, ret);
1330 shouldNotValidate = true;
1331 }
1332
1333 if (comboWriter->d_luaContext) {
1334 PolicyResult policyResult = PolicyResult::NoAction;
1335 if (comboWriter->d_luaContext->d_postresolve_ffi) {
1336 RecursorLua4::PostResolveFFIHandle handle(dnsQuestion);
1337 resolver.d_eventTrace.add(RecEventTrace::LuaPostResolveFFI);
1338 bool prResult = comboWriter->d_luaContext->postresolve_ffi(handle);
1339 resolver.d_eventTrace.add(RecEventTrace::LuaPostResolveFFI, prResult, false);
1340 if (prResult) {
1341 shouldNotValidate = true;
1342 policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
1343 }
1344 }
1345 else if (comboWriter->d_luaContext->postresolve(dnsQuestion, res, resolver.d_eventTrace)) {
1346 shouldNotValidate = true;
1347 policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
1348 }
1349 if (policyResult == PolicyResult::HaveAnswer) {
1350 goto haveAnswer; // NOLINT(cppcoreguidelines-avoid-goto)
1351 }
1352 else if (policyResult == PolicyResult::Drop) {
1353 return;
1354 }
1355 } // dc->d_luaContext
1356 }
1357 else if (comboWriter->d_luaContext) {
1358 // preresolve returned true
1359 shouldNotValidate = true;
1360 auto policyResult = handlePolicyHit(appliedPolicy, comboWriter, resolver, res, ret, packetWriter, tcpGuard);
1361 // haveAnswer case redundant
1362 if (policyResult == PolicyResult::Drop) {
1363 return;
1364 }
1365 }
1366
1367 haveAnswer:;
1368 if (tracedQuery || res == -1 || res == RCode::ServFail || packetWriter.getHeader()->rcode == static_cast<unsigned>(RCode::ServFail)) {
1369 dumpTrace(resolver.getTrace(), resolver.d_fixednow);
1370 }
1371
1372 if (res == -1) {
1373 packetWriter.getHeader()->rcode = RCode::ServFail;
1374 // no commit here, because no record
1375 ++t_Counters.at(rec::Counter::servFails);
1376 }
1377 else {
1378 packetWriter.getHeader()->rcode = res;
1379
1380 // Does the validation mode or query demand validation?
1381 if (!shouldNotValidate && resolver.isDNSSECValidationRequested()) {
1382 try {
1383 auto state = resolver.getValidationState();
1384
1385 string x_marker;
1386 std::shared_ptr<Logr::Logger> log;
1387 if (resolver.doLog() || vStateIsBogus(state)) {
1388 // Only create logging object if needed below, beware if you change the logging logic!
1389 log = resolver.d_slog->withValues("vstate", Logging::Loggable(state));
1390 auto xdnssec = g_xdnssec.getLocal();
1391 if (xdnssec->check(comboWriter->d_mdp.d_qname)) {
1392 log = log->withValues("in-x-dnssec-names", Logging::Loggable(1));
1393 x_marker = " [in x-dnssec-names]";
1394 }
1395 }
1396 if (state == vState::Secure) {
1397 if (resolver.doLog()) {
1398 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,
1399 log->info(Logr::Info, "Validates Correctly"));
1400 }
1401
1402 // Is the query source interested in the value of the ad-bit?
1403 if (comboWriter->d_mdp.d_header.ad || DNSSECOK) {
1404 packetWriter.getHeader()->ad = 1;
1405 }
1406 }
1407 else if (state == vState::Insecure) {
1408 if (resolver.doLog()) {
1409 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,
1410 log->info(Logr::Info, "Validates as Insecure"));
1411 }
1412
1413 packetWriter.getHeader()->ad = 0;
1414 }
1415 else if (vStateIsBogus(state)) {
1416 if (t_bogusremotes) {
1417 t_bogusremotes->push_back(comboWriter->d_source);
1418 }
1419 if (t_bogusqueryring) {
1420 t_bogusqueryring->push_back({comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype});
1421 }
1422 if (g_dnssecLogBogus || resolver.doLog() || g_dnssecmode == DNSSECMode::ValidateForLog) {
1423 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,
1424 log->info(Logr::Notice, "Validates as Bogus"));
1425 }
1426
1427 // Does the query or validation mode sending out a SERVFAIL on validation errors?
1428 if (!packetWriter.getHeader()->cd && (g_dnssecmode == DNSSECMode::ValidateAll || comboWriter->d_mdp.d_header.ad || DNSSECOK)) {
1429 if (resolver.doLog()) {
1430 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,
1431 log->info(Logr::Notice, "Sending out SERVFAIL because recursor or query demands it for Bogus results"));
1432 }
1433
1434 packetWriter.getHeader()->rcode = RCode::ServFail;
1435 goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
1436 }
1437 else {
1438 if (resolver.doLog()) {
1439 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,
1440 log->info(Logr::Notice, "Sending out SERVFAIL because recursor or query demands it for Bogus results"));
1441 }
1442 }
1443 }
1444 }
1445 catch (const ImmediateServFailException& e) {
1446 if (g_logCommonErrors) {
1447 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,
1448 resolver.d_slog->error(Logr::Notice, e.reason, "Sending SERVFAIL during validation", "exception", Logging::Loggable("ImmediateServFailException")));
1449 }
1450 goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
1451 }
1452 }
1453
1454 if (!ret.empty()) {
1455 pdns::orderAndShuffle(ret, false);
1456 if (auto listToSort = luaconfsLocal->sortlist.getOrderCmp(comboWriter->d_source)) {
1457 stable_sort(ret.begin(), ret.end(), *listToSort);
1458 variableAnswer = true;
1459 }
1460 }
1461
1462 bool needCommit = false;
1463 for (const auto& record : ret) {
1464 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))))) {
1465 continue;
1466 }
1467
1468 if (!addRecordToPacket(packetWriter, record, minTTL, comboWriter->d_ttlCap, maxanswersize, seenAuthSOA)) {
1469 needCommit = false;
1470 break;
1471 }
1472 needCommit = true;
1473
1474 bool udr = false;
1475 #ifdef NOD_ENABLED
1476 if (g_udrEnabled) {
1477 udr = udrCheckUniqueDNSRecord(nodlogger, comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype, record);
1478 if (!hasUDR && udr) {
1479 hasUDR = true;
1480 }
1481 }
1482 #endif /* NOD ENABLED */
1483
1484 if (t_protobufServers.servers) {
1485 // Max size is 64k, but we're conservative here, as other fields are added after the answers have been added
1486 // If a single answer causes a too big protobuf message, it wil be dropped by queueData()
1487 // But note addRR has code to prevent that
1488 if (pbMessage.size() < std::numeric_limits<uint16_t>::max() / 2) {
1489 pbMessage.addRR(record, luaconfsLocal->protobufExportConfig.exportTypes, udr);
1490 }
1491 }
1492 }
1493 if (needCommit) {
1494 packetWriter.commit();
1495 }
1496 #ifdef NOD_ENABLED
1497 #ifdef HAVE_FSTRM
1498 if (hasUDR) {
1499 if (isEnabledForUDRs(t_nodFrameStreamServersInfo.servers)) {
1500 struct timespec timeSpec
1501 {
1502 };
1503 std::string str;
1504 if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
1505 TIMEVAL_TO_TIMESPEC(&comboWriter->d_kernelTimestamp, &timeSpec); // NOLINT
1506 }
1507 else {
1508 TIMEVAL_TO_TIMESPEC(&comboWriter->d_now, &timeSpec); // NOLINT
1509 }
1510 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)
1511 str = message.getBuffer();
1512 for (auto& logger : *(t_nodFrameStreamServersInfo.servers)) {
1513 if (logger->logUDRs()) {
1514 remoteLoggerQueueData(*logger, str);
1515 }
1516 }
1517 }
1518 }
1519 #endif // HAVE_FSTRM
1520 #endif // NOD_ENABLED
1521 }
1522 sendit:;
1523
1524 if (g_useIncomingECS && comboWriter->d_ecsFound && !resolver.wasVariable() && !variableAnswer) {
1525 EDNSSubnetOpts ednsOptions;
1526 ednsOptions.source = comboWriter->d_ednssubnet.source;
1527 ComboAddress sourceAddr;
1528 sourceAddr.reset();
1529 sourceAddr.sin4.sin_family = ednsOptions.source.getNetwork().sin4.sin_family;
1530 ednsOptions.scope = Netmask(sourceAddr, 0);
1531 auto ecsPayload = makeEDNSSubnetOptsString(ednsOptions);
1532
1533 // if we don't have enough space available let's just not set that scope of zero,
1534 // it will prevent some caching, mostly from dnsdist, but that's fine
1535 if (packetWriter.size() < maxanswersize && (maxanswersize - packetWriter.size()) >= (EDNSOptionCodeSize + EDNSOptionLengthSize + ecsPayload.size())) {
1536
1537 maxanswersize -= EDNSOptionCodeSize + EDNSOptionLengthSize + ecsPayload.size();
1538
1539 returnedEdnsOptions.emplace_back(EDNSOptionCode::ECS, std::move(ecsPayload));
1540 }
1541 }
1542
1543 if (haveEDNS && addPaddingToResponse) {
1544 size_t currentSize = packetWriter.getSizeWithOpts(returnedEdnsOptions);
1545 /* we don't use maxawnswersize because it accounts for some EDNS options, but
1546 not all of them (for example ECS) */
1547 size_t maxSize = min(static_cast<uint16_t>(edo.d_packetsize >= 512 ? edo.d_packetsize : 512), g_udpTruncationThreshold);
1548
1549 if (currentSize < (maxSize - 4)) {
1550 size_t remaining = maxSize - (currentSize + 4);
1551 /* from rfc8647, "4.1. Recommended Strategy: Block-Length Padding":
1552 If a server receives a query that includes the EDNS(0) "Padding"
1553 option, it MUST pad the corresponding response (see Section 4 of
1554 RFC 7830) and SHOULD pad the corresponding response to a
1555 multiple of 468 octets (see below).
1556 */
1557 const size_t blockSize = 468;
1558 size_t modulo = (currentSize + 4) % blockSize;
1559 size_t padSize = 0;
1560 if (modulo > 0) {
1561 padSize = std::min(blockSize - modulo, remaining);
1562 }
1563 returnedEdnsOptions.emplace_back(EDNSOptionCode::PADDING, makeEDNSPaddingOptString(padSize));
1564 }
1565 }
1566
1567 if (haveEDNS) {
1568 auto state = resolver.getValidationState();
1569 if (comboWriter->d_extendedErrorCode || resolver.d_extendedError || (SyncRes::s_addExtendedResolutionDNSErrors && vStateIsBogus(state))) {
1570 EDNSExtendedError::code code = EDNSExtendedError::code::Other;
1571 std::string extra;
1572
1573 if (comboWriter->d_extendedErrorCode) {
1574 code = static_cast<EDNSExtendedError::code>(*comboWriter->d_extendedErrorCode);
1575 extra = std::move(comboWriter->d_extendedErrorExtra);
1576 }
1577 else if (resolver.d_extendedError) {
1578 code = static_cast<EDNSExtendedError::code>(resolver.d_extendedError->infoCode);
1579 extra = std::move(resolver.d_extendedError->extraText);
1580 }
1581 else {
1582 switch (state) {
1583 case vState::BogusNoValidDNSKEY:
1584 code = EDNSExtendedError::code::DNSKEYMissing;
1585 break;
1586 case vState::BogusInvalidDenial:
1587 code = EDNSExtendedError::code::NSECMissing;
1588 break;
1589 case vState::BogusUnableToGetDSs:
1590 code = EDNSExtendedError::code::DNSSECBogus;
1591 break;
1592 case vState::BogusUnableToGetDNSKEYs:
1593 code = EDNSExtendedError::code::DNSKEYMissing;
1594 break;
1595 case vState::BogusSelfSignedDS:
1596 code = EDNSExtendedError::code::DNSSECBogus;
1597 break;
1598 case vState::BogusNoRRSIG:
1599 code = EDNSExtendedError::code::RRSIGsMissing;
1600 break;
1601 case vState::BogusNoValidRRSIG:
1602 code = EDNSExtendedError::code::DNSSECBogus;
1603 break;
1604 case vState::BogusMissingNegativeIndication:
1605 code = EDNSExtendedError::code::NSECMissing;
1606 break;
1607 case vState::BogusSignatureNotYetValid:
1608 code = EDNSExtendedError::code::SignatureNotYetValid;
1609 break;
1610 case vState::BogusSignatureExpired:
1611 code = EDNSExtendedError::code::SignatureExpired;
1612 break;
1613 case vState::BogusUnsupportedDNSKEYAlgo:
1614 code = EDNSExtendedError::code::UnsupportedDNSKEYAlgorithm;
1615 break;
1616 case vState::BogusUnsupportedDSDigestType:
1617 code = EDNSExtendedError::code::UnsupportedDSDigestType;
1618 break;
1619 case vState::BogusNoZoneKeyBitSet:
1620 code = EDNSExtendedError::code::NoZoneKeyBitSet;
1621 break;
1622 case vState::BogusRevokedDNSKEY:
1623 case vState::BogusInvalidDNSKEYProtocol:
1624 code = EDNSExtendedError::code::DNSSECBogus;
1625 break;
1626 default:
1627 throw std::runtime_error("Bogus validation state not handled: " + vStateToString(state));
1628 }
1629 }
1630
1631 EDNSExtendedError eee;
1632 eee.infoCode = static_cast<uint16_t>(code);
1633 eee.extraText = std::move(extra);
1634
1635 if (packetWriter.size() < maxanswersize && (maxanswersize - packetWriter.size()) >= (EDNSOptionCodeSize + EDNSOptionLengthSize + sizeof(eee.infoCode) + eee.extraText.size())) {
1636 returnedEdnsOptions.emplace_back(EDNSOptionCode::EXTENDEDERROR, makeEDNSExtendedErrorOptString(eee));
1637 }
1638 }
1639
1640 /* we try to add the EDNS OPT RR even for truncated answers,
1641 as rfc6891 states:
1642 "The minimal response MUST be the DNS header, question section, and an
1643 OPT record. This MUST also occur when a truncated response (using
1644 the DNS header's TC bit) is returned."
1645 */
1646 packetWriter.addOpt(512, ednsExtRCode, DNSSECOK ? EDNSOpts::DNSSECOK : 0, returnedEdnsOptions);
1647 packetWriter.commit();
1648 }
1649
1650 t_Counters.at(rec::ResponseStats::responseStats).submitResponse(comboWriter->d_mdp.d_qtype, packet.size(), packetWriter.getHeader()->rcode);
1651 updateResponseStats(res, comboWriter->d_source, packet.size(), &comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype);
1652 #ifdef NOD_ENABLED
1653 bool nod = false;
1654 if (g_nodEnabled) {
1655 if (nodCheckNewDomain(nodlogger, comboWriter->d_mdp.d_qname)) {
1656 nod = true;
1657 #ifdef HAVE_FSTRM
1658 if (isEnabledForNODs(t_nodFrameStreamServersInfo.servers)) {
1659 struct timespec timeSpec
1660 {
1661 };
1662 std::string str;
1663 if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
1664 TIMEVAL_TO_TIMESPEC(&comboWriter->d_kernelTimestamp, &timeSpec); // NOLINT
1665 }
1666 else {
1667 TIMEVAL_TO_TIMESPEC(&comboWriter->d_now, &timeSpec); // NOLINT
1668 }
1669 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);
1670 str = message.getBuffer();
1671
1672 for (auto& logger : *(t_nodFrameStreamServersInfo.servers)) {
1673 if (logger->logNODs()) {
1674 remoteLoggerQueueData(*logger, str);
1675 }
1676 }
1677 }
1678 #endif // HAVE_FSTRM
1679 }
1680 }
1681 #endif /* NOD_ENABLED */
1682
1683 if (variableAnswer || resolver.wasVariable()) {
1684 t_Counters.at(rec::Counter::variableResponses)++;
1685 }
1686
1687 if (t_protobufServers.servers && !(luaconfsLocal->protobufExportConfig.taggedOnly && appliedPolicy.getName().empty() && comboWriter->d_policyTags.empty())) {
1688 // Start constructing embedded DNSResponse object
1689 pbMessage.setResponseCode(packetWriter.getHeader()->rcode);
1690 if (!appliedPolicy.getName().empty()) {
1691 pbMessage.setAppliedPolicy(appliedPolicy.getName());
1692 pbMessage.setAppliedPolicyType(appliedPolicy.d_type);
1693 pbMessage.setAppliedPolicyTrigger(appliedPolicy.d_trigger);
1694 pbMessage.setAppliedPolicyHit(appliedPolicy.d_hit);
1695 pbMessage.setAppliedPolicyKind(appliedPolicy.d_kind);
1696 }
1697 pbMessage.setInBytes(packet.size());
1698 pbMessage.setValidationState(resolver.getValidationState());
1699 // See if we want to store the policyTags into the PC
1700 addPolicyTagsToPBMessageIfNeeded(*comboWriter, pbMessage);
1701
1702 // Take s snap of the current protobuf buffer state to store in the PC
1703 pbDataForCache = boost::make_optional(RecursorPacketCache::PBData{
1704 pbMessage.getMessageBuf(),
1705 pbMessage.getResponseBuf(),
1706 !appliedPolicy.getName().empty() || !comboWriter->d_policyTags.empty()});
1707 #ifdef NOD_ENABLED
1708 // if (g_udrEnabled) ??
1709 pbMessage.clearUDR(pbDataForCache->d_response);
1710 #endif
1711 }
1712
1713 const bool intoPC = g_packetCache && !variableAnswer && !resolver.wasVariable();
1714 if (intoPC) {
1715 minTTL = capPacketCacheTTL(*packetWriter.getHeader(), minTTL, seenAuthSOA);
1716 g_packetCache->insertResponsePacket(comboWriter->d_tag, comboWriter->d_qhash, std::move(comboWriter->d_query), comboWriter->d_mdp.d_qname,
1717 comboWriter->d_mdp.d_qtype, comboWriter->d_mdp.d_qclass,
1718 string(reinterpret_cast<const char*>(&*packet.begin()), packet.size()), // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
1719 g_now.tv_sec,
1720 minTTL,
1721 dnsQuestion.validationState,
1722 std::move(pbDataForCache), comboWriter->d_tcp);
1723 }
1724
1725 if (g_regressionTestMode) {
1726 t_Counters.updateSnap(g_regressionTestMode);
1727 }
1728
1729 if (!comboWriter->d_tcp) {
1730 struct msghdr msgh
1731 {
1732 };
1733 struct iovec iov
1734 {
1735 };
1736 cmsgbuf_aligned cbuf{};
1737 fillMSGHdr(&msgh, &iov, &cbuf, 0, reinterpret_cast<char*>(&*packet.begin()), packet.size(), &comboWriter->d_remote); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
1738 msgh.msg_control = nullptr;
1739
1740 if (g_fromtosockets.count(comboWriter->d_socket) > 0) {
1741 addCMsgSrcAddr(&msgh, &cbuf, &comboWriter->d_local, 0);
1742 }
1743 int sendErr = sendOnNBSocket(comboWriter->d_socket, &msgh);
1744 if (sendErr != 0 && g_logCommonErrors) {
1745 SLOG(g_log << Logger::Warning << "Sending UDP reply to client " << comboWriter->getRemote() << " failed with: "
1746 << stringerror(sendErr) << endl,
1747 g_slogudpin->error(Logr::Warning, sendErr, "Sending UDP reply to client failed"));
1748 }
1749 }
1750 else {
1751 bool hadError = sendResponseOverTCP(comboWriter, packet);
1752 finishTCPReply(comboWriter, hadError, true);
1753 tcpGuard.setHandled();
1754 }
1755
1756 resolver.d_eventTrace.add(RecEventTrace::AnswerSent);
1757
1758 // Now do the per query changing part ot the protobuf message
1759 if (t_protobufServers.servers && !(luaconfsLocal->protobufExportConfig.taggedOnly && appliedPolicy.getName().empty() && comboWriter->d_policyTags.empty())) {
1760 // Below are the fields that are not stored in the packet cache and will be appended here and on a cache hit
1761 if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
1762 pbMessage.setQueryTime(comboWriter->d_kernelTimestamp.tv_sec, comboWriter->d_kernelTimestamp.tv_usec);
1763 }
1764 else {
1765 pbMessage.setQueryTime(comboWriter->d_now.tv_sec, comboWriter->d_now.tv_usec);
1766 }
1767 pbMessage.setMessageIdentity(comboWriter->d_uuid);
1768 pbMessage.setSocketProtocol(comboWriter->d_tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP);
1769
1770 if (!luaconfsLocal->protobufExportConfig.logMappedFrom) {
1771 pbMessage.setSocketFamily(comboWriter->d_source.sin4.sin_family);
1772 Netmask requestorNM(comboWriter->d_source, comboWriter->d_source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
1773 ComboAddress requestor = requestorNM.getMaskedNetwork();
1774 pbMessage.setFrom(requestor);
1775 pbMessage.setFromPort(comboWriter->d_source.getPort());
1776 }
1777 else {
1778 pbMessage.setSocketFamily(comboWriter->d_mappedSource.sin4.sin_family);
1779 Netmask requestorNM(comboWriter->d_mappedSource, comboWriter->d_mappedSource.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
1780 ComboAddress requestor = requestorNM.getMaskedNetwork();
1781 pbMessage.setFrom(requestor);
1782 pbMessage.setFromPort(comboWriter->d_mappedSource.getPort());
1783 }
1784
1785 pbMessage.setTo(comboWriter->d_destination);
1786 pbMessage.setId(comboWriter->d_mdp.d_header.id);
1787
1788 pbMessage.setTime();
1789 pbMessage.setEDNSSubnet(comboWriter->d_ednssubnet.source, comboWriter->d_ednssubnet.source.isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
1790 pbMessage.setRequestorId(dnsQuestion.requestorId);
1791 pbMessage.setDeviceId(dnsQuestion.deviceId);
1792 pbMessage.setDeviceName(dnsQuestion.deviceName);
1793 pbMessage.setToPort(comboWriter->d_destination.getPort());
1794 pbMessage.addPolicyTags(comboWriter->d_gettagPolicyTags);
1795
1796 for (const auto& metaValue : dnsQuestion.meta) {
1797 pbMessage.setMeta(metaValue.first, metaValue.second.stringVal, metaValue.second.intVal);
1798 }
1799 #ifdef NOD_ENABLED
1800 if (g_nodEnabled) {
1801 if (nod) {
1802 pbMessage.setNewlyObservedDomain(true);
1803 pbMessage.addPolicyTag(g_nod_pbtag);
1804 }
1805 if (hasUDR) {
1806 pbMessage.addPolicyTag(g_udr_pbtag);
1807 }
1808 }
1809 #endif /* NOD_ENABLED */
1810 if (resolver.d_eventTrace.enabled() && (SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_pb) != 0) {
1811 pbMessage.addEvents(resolver.d_eventTrace);
1812 }
1813 if (comboWriter->d_logResponse) {
1814 protobufLogResponse(pbMessage);
1815 }
1816 }
1817
1818 if (resolver.d_eventTrace.enabled() && (SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_log) != 0) {
1819 SLOG(g_log << Logger::Info << resolver.d_eventTrace.toString() << endl,
1820 resolver.d_slog->info(Logr::Info, resolver.d_eventTrace.toString())); // Maybe we want it to be more fancy?
1821 }
1822
1823 // Originally this code used a mix of floats, doubles, uint64_t with different units.
1824 // Now it always uses an integral number of microseconds, except for averages, which use doubles
1825 uint64_t spentUsec = uSec(resolver.getNow() - comboWriter->d_now);
1826 if (!g_quiet) {
1827 if (!g_slogStructured) {
1828 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);
1829 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;
1830
1831 if (!shouldNotValidate && resolver.isDNSSECValidationRequested()) {
1832 g_log << ", dnssec=" << resolver.getValidationState();
1833 }
1834 g_log << " answer-is-variable=" << resolver.wasVariable() << ", into-packetcache=" << intoPC;
1835 g_log << " maxdepth=" << resolver.d_maxdepth;
1836 g_log << endl;
1837 }
1838 else {
1839 resolver.d_slog->info(Logr::Info, "Answer", "rd", Logging::Loggable(comboWriter->d_mdp.d_header.rd),
1840 "answers", Logging::Loggable(ntohs(packetWriter.getHeader()->ancount)),
1841 "additional", Logging::Loggable(ntohs(packetWriter.getHeader()->arcount)),
1842 "outqueries", Logging::Loggable(resolver.d_outqueries),
1843 "netms", Logging::Loggable(resolver.d_totUsec / 1000.0),
1844 "totms", Logging::Loggable(static_cast<double>(spentUsec) / 1000.0),
1845 "throttled", Logging::Loggable(resolver.d_throttledqueries),
1846 "timeouts", Logging::Loggable(resolver.d_timeouts),
1847 "tcpout", Logging::Loggable(resolver.d_tcpoutqueries),
1848 "dotout", Logging::Loggable(resolver.d_dotoutqueries),
1849 "rcode", Logging::Loggable(res),
1850 "validationState", Logging::Loggable(resolver.getValidationState()),
1851 "answer-is-variable", Logging::Loggable(resolver.wasVariable()),
1852 "into-packetcache", Logging::Loggable(intoPC),
1853 "maxdepth", Logging::Loggable(resolver.d_maxdepth));
1854 }
1855 }
1856
1857 if (comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Query)) {
1858 if (resolver.d_outqueries != 0 || resolver.d_throttledqueries != 0 || resolver.d_authzonequeries != 0) {
1859 g_recCache->incCacheMisses();
1860 }
1861 else {
1862 g_recCache->incCacheHits();
1863 }
1864 }
1865
1866 t_Counters.at(rec::Histogram::answers)(spentUsec);
1867 t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
1868
1869 auto newLat = static_cast<double>(spentUsec);
1870 newLat = min(newLat, g_networkTimeoutMsec * 1000.0); // outliers of several minutes exist..
1871 t_Counters.at(rec::DoubleWAvgCounter::avgLatencyUsec).addToRollingAvg(newLat, g_latencyStatSize);
1872 // no worries, we do this for packet cache hits elsewhere
1873
1874 if (spentUsec >= resolver.d_totUsec) {
1875 uint64_t ourtime = spentUsec - resolver.d_totUsec;
1876 t_Counters.at(rec::Histogram::ourtime)(ourtime);
1877 newLat = static_cast<double>(ourtime); // usec
1878 t_Counters.at(rec::DoubleWAvgCounter::avgLatencyOursUsec).addToRollingAvg(newLat, g_latencyStatSize);
1879 }
1880
1881 #ifdef NOD_ENABLED
1882 if (nod) {
1883 sendNODLookup(nodlogger, comboWriter->d_mdp.d_qname);
1884 }
1885 #endif /* NOD_ENABLED */
1886
1887 // cout<<dc->d_mdp.d_qname<<"\t"<<MT->getUsec()<<"\t"<<sr.d_outqueries<<endl;
1888 }
1889 catch (const PDNSException& ae) {
1890 SLOG(g_log << Logger::Error << "startDoResolve problem " << makeLoginfo(comboWriter) << ": " << ae.reason << endl,
1891 resolver.d_slog->error(Logr::Error, ae.reason, "startDoResolve problem", "exception", Logging::Loggable("PDNSException")));
1892 }
1893 catch (const MOADNSException& mde) {
1894 SLOG(g_log << Logger::Error << "DNS parser error " << makeLoginfo(comboWriter) << ": " << comboWriter->d_mdp.d_qname << ", " << mde.what() << endl,
1895 resolver.d_slog->error(Logr::Error, mde.what(), "DNS parser error"));
1896 }
1897 catch (const std::exception& e) {
1898 SLOG(g_log << Logger::Error << "STL error " << makeLoginfo(comboWriter) << ": " << e.what(),
1899 resolver.d_slog->error(Logr::Error, e.what(), "Exception in resolver context", "exception", Logging::Loggable("std::exception")));
1900
1901 // Luawrapper nests the exception from Lua, so we unnest it here
1902 try {
1903 std::rethrow_if_nested(e);
1904 }
1905 catch (const std::exception& ne) {
1906 SLOG(g_log << ". Extra info: " << ne.what(),
1907 resolver.d_slog->error(Logr::Error, ne.what(), "Nested exception in resolver context", Logging::Loggable("std::exception")));
1908 }
1909 catch (...) {
1910 }
1911 if (!g_slogStructured) {
1912 g_log << endl;
1913 }
1914 }
1915 catch (...) {
1916 SLOG(g_log << Logger::Error << "Any other exception in a resolver context " << makeLoginfo(comboWriter) << endl,
1917 resolver.d_slog->info(Logr::Error, "Any other exception in a resolver context"));
1918 }
1919
1920 runTaskOnce(g_logCommonErrors);
1921
1922 static const size_t stackSizeThreshold = 9 * ::arg().asNum("stack-size") / 10;
1923 if (g_multiTasker->getMaxStackUsage() >= stackSizeThreshold) {
1924 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,
1925 resolver.d_slog->info(Logr::Error, "Reached mthread stack usage of 90%",
1926 "stackUsage", Logging::Loggable(g_multiTasker->getMaxStackUsage()),
1927 "outqueries", Logging::Loggable(resolver.d_outqueries),
1928 "netms", Logging::Loggable(resolver.d_totUsec / 1000.0),
1929 "throttled", Logging::Loggable(resolver.d_throttledqueries),
1930 "timeouts", Logging::Loggable(resolver.d_timeouts),
1931 "tcpout", Logging::Loggable(resolver.d_tcpoutqueries),
1932 "dotout", Logging::Loggable(resolver.d_dotoutqueries),
1933 "validationState", Logging::Loggable(resolver.getValidationState())));
1934 }
1935 t_Counters.at(rec::Counter::maxMThreadStackUsage) = max(g_multiTasker->getMaxStackUsage(), t_Counters.at(rec::Counter::maxMThreadStackUsage));
1936 t_Counters.updateSnap(g_regressionTestMode);
1937 }
1938
1939 void getQNameAndSubnet(const std::string& question, DNSName* dnsname, uint16_t* qtype, uint16_t* qclass,
1940 bool& foundECS, EDNSSubnetOpts* ednssubnet, EDNSOptionViewMap* options)
1941 {
1942 const bool lookForECS = ednssubnet != nullptr;
1943 const dnsheader_aligned dnshead(question.data());
1944 const dnsheader* dhPointer = dnshead.get();
1945 size_t questionLen = question.length();
1946 unsigned int consumed = 0;
1947 *dnsname = DNSName(question.c_str(), static_cast<int>(questionLen), sizeof(dnsheader), false, qtype, qclass, &consumed);
1948
1949 size_t pos = sizeof(dnsheader) + consumed + 4;
1950 const size_t headerSize = /* root */ 1 + sizeof(dnsrecordheader);
1951 const uint16_t arcount = ntohs(dhPointer->arcount);
1952
1953 for (uint16_t arpos = 0; arpos < arcount && questionLen > (pos + headerSize) && (lookForECS && !foundECS); arpos++) {
1954 if (question.at(pos) != 0) {
1955 /* not an OPT, bye. */
1956 return;
1957 }
1958
1959 pos += 1;
1960 const auto* drh = reinterpret_cast<const dnsrecordheader*>(&question.at(pos)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
1961 pos += sizeof(dnsrecordheader);
1962
1963 if (pos >= questionLen) {
1964 return;
1965 }
1966
1967 /* OPT root label (1) followed by type (2) */
1968 if (lookForECS && ntohs(drh->d_type) == QType::OPT) {
1969 if (options == nullptr) {
1970 size_t ecsStartPosition = 0;
1971 size_t ecsLen = 0;
1972 /* we need to pass the record len */
1973 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)
1974 if (res == 0 && ecsLen > 4) {
1975 EDNSSubnetOpts eso;
1976 if (getEDNSSubnetOptsFromString(&question.at(pos - sizeof(drh->d_clen) + ecsStartPosition + 4), ecsLen - 4, &eso)) {
1977 *ednssubnet = eso;
1978 foundECS = true;
1979 }
1980 }
1981 }
1982 else {
1983 /* we need to pass the record len */
1984 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)
1985 if (res == 0) {
1986 const auto& iter = options->find(EDNSOptionCode::ECS);
1987 if (iter != options->end() && !iter->second.values.empty() && iter->second.values.at(0).content != nullptr && iter->second.values.at(0).size > 0) {
1988 EDNSSubnetOpts eso;
1989 if (getEDNSSubnetOptsFromString(iter->second.values.at(0).content, iter->second.values.at(0).size, &eso)) {
1990 *ednssubnet = eso;
1991 foundECS = true;
1992 }
1993 }
1994 }
1995 }
1996 }
1997
1998 pos += ntohs(drh->d_clen);
1999 }
2000 }
2001
2002 bool checkForCacheHit(bool qnameParsed, unsigned int tag, const string& data,
2003 DNSName& qname, uint16_t& qtype, uint16_t& qclass,
2004 const struct timeval& now,
2005 string& response, uint32_t& qhash,
2006 RecursorPacketCache::OptPBData& pbData, bool tcp, const ComboAddress& source, const ComboAddress& mappedSource)
2007 {
2008 if (!g_packetCache) {
2009 return false;
2010 }
2011 bool cacheHit = false;
2012 uint32_t age = 0;
2013 vState valState = vState::Indeterminate;
2014
2015 if (qnameParsed) {
2016 cacheHit = g_packetCache->getResponsePacket(tag, data, qname, qtype, qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
2017 }
2018 else {
2019 cacheHit = g_packetCache->getResponsePacket(tag, data, qname, &qtype, &qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
2020 }
2021
2022 if (cacheHit) {
2023 if (vStateIsBogus(valState)) {
2024 if (t_bogusremotes) {
2025 t_bogusremotes->push_back(source);
2026 }
2027 if (t_bogusqueryring) {
2028 t_bogusqueryring->push_back({qname, qtype});
2029 }
2030 }
2031
2032 // This is only to get the proxyMapping suffixMatch stats right i the case of a PC hit
2033 if (t_proxyMapping && source != mappedSource) {
2034 if (const auto* found = t_proxyMapping->lookup(source)) {
2035 if (found->second.suffixMatchNode) {
2036 if (found->second.suffixMatchNode->check(qname)) {
2037 ++found->second.stats.suffixMatches;
2038 }
2039 }
2040 }
2041 }
2042
2043 t_Counters.at(rec::Counter::packetCacheHits)++;
2044 t_Counters.at(rec::Counter::syncresqueries)++; // XXX
2045 if (response.length() >= sizeof(struct dnsheader)) {
2046 dnsheader_aligned dh_aligned(response.data());
2047 ageDNSPacket(response, age, dh_aligned);
2048 const auto* dhp = dh_aligned.get();
2049 updateResponseStats(dhp->rcode, source, response.length(), nullptr, 0);
2050 t_Counters.at(rec::ResponseStats::responseStats).submitResponse(qtype, response.length(), dhp->rcode);
2051 }
2052
2053 // we assume 0 usec
2054 t_Counters.at(rec::DoubleWAvgCounter::avgLatencyUsec).addToRollingAvg(0.0, g_latencyStatSize);
2055 t_Counters.at(rec::DoubleWAvgCounter::avgLatencyOursUsec).addToRollingAvg(0.0, g_latencyStatSize);
2056 #if 0
2057 // XXX changes behaviour compared to old code!
2058 t_Counters.at(rec::Counter::answers)(0);
2059 t_Counters.at(rec::Counter::ourtime)(0);
2060 #endif
2061 }
2062
2063 return cacheHit;
2064 }
2065
2066 static void* pleaseWipeCaches(const DNSName& canon, bool subtree, uint16_t qtype)
2067 {
2068 auto res = wipeCaches(canon, subtree, qtype);
2069 SLOG(g_log << Logger::Info << "Wiped caches for " << canon << ": " << res.record_count << " records; " << res.negative_record_count << " negative records; " << res.packet_count << " packets" << endl,
2070 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)));
2071 return nullptr;
2072 }
2073
2074 void requestWipeCaches(const DNSName& canon)
2075 {
2076 // send a message to the handler thread asking it
2077 // to wipe all of the caches
2078 ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: pointer owner
2079 tmsg->func = [=] { return pleaseWipeCaches(canon, true, 0xffff); };
2080 tmsg->wantAnswer = false;
2081 if (write(RecThreadInfo::info(0).getPipes().writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // NOLINT: correct sizeof
2082 delete tmsg; // NOLINT: pointer owner
2083
2084 unixDie("write to thread pipe returned wrong size or error");
2085 }
2086 // coverity[leaked_storage]
2087 }
2088
2089 bool expectProxyProtocol(const ComboAddress& from)
2090 {
2091 return g_proxyProtocolACL.match(from);
2092 }
2093
2094 // fromaddr: the address the query is coming from
2095 // destaddr: the address the query was received on
2096 // source: the address we assume the query is coming from, might be set by proxy protocol
2097 // destination: the address we assume the query was sent to, might be set by proxy protocol
2098 // mappedSource: the address we assume the query is coming from. Differs from source if table based mapping has been applied
2099 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
2100 {
2101 RecThreadInfo::self().incNumberOfDistributedQueries();
2102 gettimeofday(&g_now, nullptr);
2103 if (tval.tv_sec != 0) {
2104 struct timeval diff = g_now - tval;
2105 double delta = (static_cast<double>(diff.tv_sec) * 1000 + static_cast<double>(diff.tv_usec) / 1000.0);
2106
2107 if (delta > 1000.0) {
2108 t_Counters.at(rec::Counter::tooOldDrops)++;
2109 return nullptr;
2110 }
2111 }
2112
2113 ++t_Counters.at(rec::Counter::qcounter);
2114
2115 if (fromaddr.sin4.sin_family == AF_INET6) {
2116 t_Counters.at(rec::Counter::ipv6qcounter)++;
2117 }
2118
2119 string response;
2120 const dnsheader_aligned headerdata(question.data());
2121 const dnsheader* dnsheader = headerdata.get();
2122 unsigned int ctag = 0;
2123 uint32_t qhash = 0;
2124 bool needECS = false;
2125 std::unordered_set<std::string> policyTags;
2126 std::map<std::string, RecursorLua4::MetaValue> meta;
2127 LuaContext::LuaObject data;
2128 string requestorId;
2129 string deviceId;
2130 string deviceName;
2131 string routingTag;
2132 bool logQuery = false;
2133 bool logResponse = false;
2134 boost::uuids::uuid uniqueId{};
2135 auto luaconfsLocal = g_luaconfs.getLocal();
2136 const auto pbExport = checkProtobufExport(luaconfsLocal);
2137 const auto outgoingbExport = checkOutgoingProtobufExport(luaconfsLocal);
2138 if (pbExport || outgoingbExport) {
2139 if (pbExport) {
2140 needECS = true;
2141 }
2142 uniqueId = getUniqueID();
2143 }
2144 logQuery = t_protobufServers.servers && luaconfsLocal->protobufExportConfig.logQueries;
2145 logResponse = t_protobufServers.servers && luaconfsLocal->protobufExportConfig.logResponses;
2146 #ifdef HAVE_FSTRM
2147 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
2148 #endif
2149 EDNSSubnetOpts ednssubnet;
2150 bool ecsFound = false;
2151 bool ecsParsed = false;
2152 std::vector<DNSRecord> records;
2153 std::string extendedErrorExtra;
2154 boost::optional<int> rcode = boost::none;
2155 boost::optional<uint16_t> extendedErrorCode{boost::none};
2156 uint32_t ttlCap = std::numeric_limits<uint32_t>::max();
2157 bool variable = false;
2158 bool followCNAMEs = false;
2159 bool responsePaddingDisabled = false;
2160 DNSName qname;
2161 try {
2162 uint16_t qtype = 0;
2163 uint16_t qclass = 0;
2164 bool qnameParsed = false;
2165 #ifdef MALLOC_TRACE
2166 /*
2167 static uint64_t last=0;
2168 if(!last)
2169 g_mtracer->clearAllocators();
2170 cout<<g_mtracer->getAllocs()-last<<" "<<g_mtracer->getNumOut()<<" -- BEGIN TRACE"<<endl;
2171 last=g_mtracer->getAllocs();
2172 cout<<g_mtracer->topAllocatorsString()<<endl;
2173 g_mtracer->clearAllocators();
2174 */
2175 #endif
2176
2177 // We do not have a SyncRes specific Lua context at this point yet, so ok to use t_pdl
2178 if (needECS || (t_pdl && (t_pdl->d_gettag || t_pdl->d_gettag_ffi)) || dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
2179 try {
2180 EDNSOptionViewMap ednsOptions;
2181
2182 ecsFound = false;
2183
2184 getQNameAndSubnet(question, &qname, &qtype, &qclass,
2185 ecsFound, &ednssubnet, g_gettagNeedsEDNSOptions ? &ednsOptions : nullptr);
2186
2187 qnameParsed = true;
2188 ecsParsed = true;
2189
2190 if (t_pdl) {
2191 try {
2192 if (t_pdl->d_gettag_ffi) {
2193 RecursorLua4::FFIParams params(qname, qtype, destination, source, ednssubnet.source, data, policyTags, records, ednsOptions, proxyProtocolValues, requestorId, deviceId, deviceName, routingTag, rcode, ttlCap, variable, false, logQuery, logResponse, followCNAMEs, extendedErrorCode, extendedErrorExtra, responsePaddingDisabled, meta);
2194
2195 eventTrace.add(RecEventTrace::LuaGetTagFFI);
2196 ctag = t_pdl->gettag_ffi(params);
2197 eventTrace.add(RecEventTrace::LuaGetTagFFI, ctag, false);
2198 }
2199 else if (t_pdl->d_gettag) {
2200 eventTrace.add(RecEventTrace::LuaGetTag);
2201 ctag = t_pdl->gettag(source, ednssubnet.source, destination, qname, qtype, &policyTags, data, ednsOptions, false, requestorId, deviceId, deviceName, routingTag, proxyProtocolValues);
2202 eventTrace.add(RecEventTrace::LuaGetTag, ctag, false);
2203 }
2204 }
2205 catch (const std::exception& e) {
2206 if (g_logCommonErrors) {
2207 SLOG(g_log << Logger::Warning << "Error parsing a query packet qname='" << qname << "' for tag determination, setting tag=0: " << e.what() << endl,
2208 g_slogudpin->error(Logr::Warning, e.what(), "Error parsing a query packet for tag determination, setting tag=0", "qname", Logging::Loggable(qname), "remote", Logging::Loggable(fromaddr), "exception", Logging::Loggable("std;:exception")));
2209 }
2210 }
2211 }
2212 }
2213 catch (const std::exception& e) {
2214 if (g_logCommonErrors) {
2215 SLOG(g_log << Logger::Warning << "Error parsing a query packet for tag determination, setting tag=0: " << e.what() << endl,
2216 g_slogudpin->error(Logr::Warning, e.what(), "Error parsing a query packet for tag determination, setting tag=0", "remote", Logging::Loggable(fromaddr), "exception", Logging::Loggable("std;:exception")));
2217 }
2218 }
2219 }
2220
2221 RecursorPacketCache::OptPBData pbData{boost::none};
2222 if (t_protobufServers.servers) {
2223 if (logQuery && !(luaconfsLocal->protobufExportConfig.taggedOnly && policyTags.empty())) {
2224 protobufLogQuery(luaconfsLocal, uniqueId, source, destination, mappedSource, ednssubnet.source, false, dnsheader->id, question.size(), qname, qtype, qclass, policyTags, requestorId, deviceId, deviceName, meta);
2225 }
2226 }
2227
2228 if (ctag == 0 && !responsePaddingDisabled && g_paddingFrom.match(fromaddr)) {
2229 ctag = g_paddingTag;
2230 }
2231
2232 if (dnsheader->opcode == static_cast<unsigned>(Opcode::Query)) {
2233 /* It might seem like a good idea to skip the packet cache lookup if we know that the answer is not cacheable,
2234 but it means that the hash would not be computed. If some script decides at a later time to mark back the answer
2235 as cacheable we would cache it with a wrong tag, so better safe than sorry. */
2236 eventTrace.add(RecEventTrace::PCacheCheck);
2237 bool cacheHit = checkForCacheHit(qnameParsed, ctag, question, qname, qtype, qclass, g_now, response, qhash, pbData, false, source, mappedSource);
2238 eventTrace.add(RecEventTrace::PCacheCheck, cacheHit, false);
2239 if (cacheHit) {
2240 if (!g_quiet) {
2241 SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " question answered from packet cache tag=" << ctag << " from " << source.toStringWithPort() << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << endl,
2242 g_slogudpin->info(Logr::Notice, "Question answered from packet cache", "tag", Logging::Loggable(ctag),
2243 "qname", Logging::Loggable(qname), "qtype", Logging::Loggable(QType(qtype)),
2244 "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
2245 }
2246 struct msghdr msgh
2247 {
2248 };
2249 struct iovec iov
2250 {
2251 };
2252 cmsgbuf_aligned cbuf{};
2253 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)
2254 msgh.msg_control = nullptr;
2255
2256 if (g_fromtosockets.count(fileDesc) != 0) {
2257 addCMsgSrcAddr(&msgh, &cbuf, &destaddr, 0);
2258 }
2259 int sendErr = sendOnNBSocket(fileDesc, &msgh);
2260 eventTrace.add(RecEventTrace::AnswerSent);
2261
2262 if (t_protobufServers.servers && logResponse && (!luaconfsLocal->protobufExportConfig.taggedOnly || !pbData || pbData->d_tagged)) {
2263 protobufLogResponse(dnsheader, luaconfsLocal, pbData, tval, false, source, destination, mappedSource, ednssubnet, uniqueId, requestorId, deviceId, deviceName, meta, eventTrace, policyTags);
2264 }
2265
2266 if (eventTrace.enabled() && (SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_log) != 0) {
2267 SLOG(g_log << Logger::Info << eventTrace.toString() << endl,
2268 g_slogudpin->info(Logr::Info, eventTrace.toString())); // Do we want more fancy logging here?
2269 }
2270 if (sendErr != 0 && g_logCommonErrors) {
2271 SLOG(g_log << Logger::Warning << "Sending UDP reply to client " << source.toStringWithPort()
2272 << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << " failed with: "
2273 << stringerror(sendErr) << endl,
2274 g_slogudpin->error(Logr::Error, sendErr, "Sending UDP reply to client failed", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
2275 }
2276 struct timeval now
2277 {
2278 };
2279 Utility::gettimeofday(&now, nullptr);
2280 uint64_t spentUsec = uSec(now - tval);
2281 t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
2282 t_Counters.updateSnap(g_regressionTestMode);
2283 return nullptr;
2284 }
2285 }
2286 }
2287 catch (const std::exception& e) {
2288 if (g_logCommonErrors) {
2289 SLOG(g_log << Logger::Error << "Error processing or aging answer packet: " << e.what() << endl,
2290 g_slogudpin->error(Logr::Error, e.what(), "Error processing or aging answer packet", "exception", Logging::Loggable("std::exception")));
2291 }
2292 return nullptr;
2293 }
2294
2295 if (t_pdl) {
2296 bool ipf = t_pdl->ipfilter(source, destination, *dnsheader, eventTrace);
2297 if (ipf) {
2298 if (!g_quiet) {
2299 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,
2300 g_slogudpin->info(Logr::Notice, "Dropped question based on policy", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
2301 }
2302 t_Counters.at(rec::Counter::policyDrops)++;
2303 return nullptr;
2304 }
2305 }
2306
2307 if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
2308 if (!isAllowNotifyForZone(qname)) {
2309 if (!g_quiet) {
2310 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,
2311 g_slogudpin->info(Logr::Notice, "Dropping UDP NOTIFY, zone not matched by allow-notify-for", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
2312 }
2313
2314 t_Counters.at(rec::Counter::zoneDisallowedNotify)++;
2315 return nullptr;
2316 }
2317
2318 if (!g_quiet) {
2319 SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " got NOTIFY for " << qname.toLogString() << " from " << source.toStringWithPort() << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << endl,
2320 g_slogudpin->info(Logr::Notice, "Got NOTIFY", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr), "qname", Logging::Loggable(qname)));
2321 }
2322 if (!notifyRPZTracker(qname)) {
2323 // It wasn't an RPZ
2324 requestWipeCaches(qname);
2325 }
2326
2327 // the operation will now be treated as a Query, generating
2328 // a normal response, as the rest of the code does not
2329 // check dh->opcode, but we need to ensure that the response
2330 // to this request does not get put into the packet cache
2331 variable = true;
2332 }
2333
2334 if (g_multiTasker->numProcesses() > g_maxMThreads) {
2335 if (!g_quiet) {
2336 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,
2337 g_slogudpin->info(Logr::Notice, "Dropped question, over capacity", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
2338 }
2339 t_Counters.at(rec::Counter::overCapacityDrops)++;
2340 return nullptr;
2341 }
2342
2343 auto comboWriter = std::make_unique<DNSComboWriter>(question, g_now, std::move(policyTags), t_pdl, std::move(data), std::move(records));
2344
2345 comboWriter->setSocket(fileDesc);
2346 comboWriter->d_tag = ctag;
2347 comboWriter->d_qhash = qhash;
2348 comboWriter->setRemote(fromaddr); // the address the query is coming from
2349 comboWriter->setSource(source); // the address we assume the query is coming from, might be set by proxy protocol
2350 comboWriter->setLocal(destaddr); // the address the query was received on
2351 comboWriter->setDestination(destination); // the address we assume the query is sent to, might be set by proxy protocol
2352 comboWriter->setMappedSource(mappedSource); // the address we assume the query is coming from. Differs from source if table-based mapping has been applied
2353 comboWriter->d_tcp = false;
2354 comboWriter->d_ecsFound = ecsFound;
2355 comboWriter->d_ecsParsed = ecsParsed;
2356 comboWriter->d_ednssubnet = ednssubnet;
2357 comboWriter->d_ttlCap = ttlCap;
2358 comboWriter->d_variable = variable;
2359 comboWriter->d_followCNAMERecords = followCNAMEs;
2360 comboWriter->d_rcode = rcode;
2361 comboWriter->d_logResponse = logResponse;
2362 if (t_protobufServers.servers || t_outgoingProtobufServers.servers) {
2363 comboWriter->d_uuid = uniqueId;
2364 }
2365 comboWriter->d_requestorId = std::move(requestorId);
2366 comboWriter->d_deviceId = std::move(deviceId);
2367 comboWriter->d_deviceName = std::move(deviceName);
2368 comboWriter->d_kernelTimestamp = tval;
2369 comboWriter->d_proxyProtocolValues = std::move(proxyProtocolValues);
2370 comboWriter->d_routingTag = std::move(routingTag);
2371 comboWriter->d_extendedErrorCode = extendedErrorCode;
2372 comboWriter->d_extendedErrorExtra = std::move(extendedErrorExtra);
2373 comboWriter->d_responsePaddingDisabled = responsePaddingDisabled;
2374 comboWriter->d_meta = std::move(meta);
2375
2376 comboWriter->d_eventTrace = std::move(eventTrace);
2377 g_multiTasker->makeThread(startDoResolve, (void*)comboWriter.release()); // deletes dc
2378
2379 return nullptr;
2380 }
2381
2382 static void handleNewUDPQuestion(int fileDesc, FDMultiplexer::funcparam_t& /* var */) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
2383 {
2384 static const size_t maxIncomingQuerySize = g_proxyProtocolACL.empty() ? 512 : (512 + g_proxyProtocolMaximumSize);
2385 static thread_local std::string data;
2386 ComboAddress fromaddr; // the address the query is coming from
2387 ComboAddress source; // the address we assume the query is coming from, might be set by proxy protocol
2388 ComboAddress destination; // the address we assume the query was sent to, might be set by proxy protocol
2389 struct msghdr msgh
2390 {
2391 };
2392 struct iovec iov
2393 {
2394 };
2395 cmsgbuf_aligned cbuf;
2396 bool firstQuery = true;
2397 std::vector<ProxyProtocolValue> proxyProtocolValues;
2398 RecEventTrace eventTrace;
2399
2400 for (size_t queriesCounter = 0; queriesCounter < g_maxUDPQueriesPerRound; queriesCounter++) {
2401 bool proxyProto = false;
2402 proxyProtocolValues.clear();
2403 data.resize(maxIncomingQuerySize);
2404 fromaddr.sin6.sin6_family = AF_INET6; // this makes sure fromaddr is big enough
2405 fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), data.data(), data.size(), &fromaddr);
2406
2407 if (ssize_t len = recvmsg(fileDesc, &msgh, 0); len >= 0) {
2408 eventTrace.clear();
2409 eventTrace.setEnabled(SyncRes::s_event_trace_enabled != 0);
2410 eventTrace.add(RecEventTrace::ReqRecv);
2411
2412 firstQuery = false;
2413
2414 if ((msgh.msg_flags & MSG_TRUNC) != 0) {
2415 t_Counters.at(rec::Counter::truncatedDrops)++;
2416 if (!g_quiet) {
2417 SLOG(g_log << Logger::Error << "Ignoring truncated query from " << fromaddr.toString() << endl,
2418 g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr)));
2419 }
2420 return;
2421 }
2422
2423 data.resize(static_cast<size_t>(len));
2424
2425 if (expectProxyProtocol(fromaddr)) {
2426 bool tcp = false;
2427 ssize_t used = parseProxyHeader(data, proxyProto, source, destination, tcp, proxyProtocolValues);
2428 if (used <= 0) {
2429 ++t_Counters.at(rec::Counter::proxyProtocolInvalidCount);
2430 if (!g_quiet) {
2431 SLOG(g_log << Logger::Error << "Ignoring invalid proxy protocol (" << std::to_string(len) << ", " << std::to_string(used) << ") query from " << fromaddr.toStringWithPort() << endl,
2432 g_slogudpin->info(Logr::Error, "Ignoring invalid proxy protocol query", "length", Logging::Loggable(len),
2433 "used", Logging::Loggable(used), "remote", Logging::Loggable(fromaddr)));
2434 }
2435 return;
2436 }
2437 if (static_cast<size_t>(used) > g_proxyProtocolMaximumSize) {
2438 if (g_quiet) {
2439 SLOG(g_log << Logger::Error << "Proxy protocol header in UDP packet from " << fromaddr.toStringWithPort() << " is larger than proxy-protocol-maximum-size (" << used << "), dropping" << endl,
2440 g_slogudpin->info(Logr::Error, "Proxy protocol header in UDP packet is larger than proxy-protocol-maximum-size",
2441 "used", Logging::Loggable(used), "remote", Logging::Loggable(fromaddr)));
2442 }
2443 ++t_Counters.at(rec::Counter::proxyProtocolInvalidCount);
2444 return;
2445 }
2446
2447 data.erase(0, used);
2448 }
2449 else if (len > 512) {
2450 /* we only allow UDP packets larger than 512 for those with a proxy protocol header */
2451 t_Counters.at(rec::Counter::truncatedDrops)++;
2452 if (!g_quiet) {
2453 SLOG(g_log << Logger::Error << "Ignoring truncated query from " << fromaddr.toStringWithPort() << endl,
2454 g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr)));
2455 }
2456 return;
2457 }
2458
2459 if (data.size() < sizeof(dnsheader)) {
2460 t_Counters.at(rec::Counter::ignoredCount)++;
2461 if (!g_quiet) {
2462 SLOG(g_log << Logger::Error << "Ignoring too-short (" << std::to_string(data.size()) << ") query from " << fromaddr.toString() << endl,
2463 g_slogudpin->info(Logr::Error, "Ignoring too-short query", "length", Logging::Loggable(data.size()),
2464 "remote", Logging::Loggable(fromaddr)));
2465 }
2466 return;
2467 }
2468
2469 if (!proxyProto) {
2470 source = fromaddr;
2471 }
2472 ComboAddress mappedSource = source;
2473 if (t_proxyMapping) {
2474 if (const auto* iter = t_proxyMapping->lookup(source)) {
2475 mappedSource = iter->second.address;
2476 ++iter->second.stats.netmaskMatches;
2477 }
2478 }
2479 if (t_remotes) {
2480 t_remotes->push_back(fromaddr);
2481 }
2482
2483 if (t_allowFrom && !t_allowFrom->match(&mappedSource)) {
2484 if (!g_quiet) {
2485 SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping UDP query from " << mappedSource.toString() << ", address not matched by allow-from" << endl,
2486 g_slogudpin->info(Logr::Error, "Dropping UDP query, address not matched by allow-from", "source", Logging::Loggable(mappedSource)));
2487 }
2488
2489 t_Counters.at(rec::Counter::unauthorizedUDP)++;
2490 return;
2491 }
2492
2493 BOOST_STATIC_ASSERT(offsetof(sockaddr_in, sin_port) == offsetof(sockaddr_in6, sin6_port));
2494 if (fromaddr.sin4.sin_port == 0) { // also works for IPv6
2495 if (!g_quiet) {
2496 SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping UDP query from " << fromaddr.toStringWithPort() << ", can't deal with port 0" << endl,
2497 g_slogudpin->info(Logr::Error, "Dropping UDP query can't deal with port 0", "remote", Logging::Loggable(fromaddr)));
2498 }
2499
2500 t_Counters.at(rec::Counter::clientParseError)++; // not quite the best place to put it, but needs to go somewhere
2501 return;
2502 }
2503
2504 try {
2505 const dnsheader_aligned headerdata(data.data());
2506 const dnsheader* dnsheader = headerdata.get();
2507
2508 if (dnsheader->qr) {
2509 t_Counters.at(rec::Counter::ignoredCount)++;
2510 if (g_logCommonErrors) {
2511 SLOG(g_log << Logger::Error << "Ignoring answer from " << fromaddr.toString() << " on server socket!" << endl,
2512 g_slogudpin->info(Logr::Error, "Ignoring answer on server socket", "remote", Logging::Loggable(fromaddr)));
2513 }
2514 }
2515 else if (dnsheader->opcode != static_cast<unsigned>(Opcode::Query) && dnsheader->opcode != static_cast<unsigned>(Opcode::Notify)) {
2516 t_Counters.at(rec::Counter::ignoredCount)++;
2517 if (g_logCommonErrors) {
2518 SLOG(g_log << Logger::Error << "Ignoring unsupported opcode " << Opcode::to_s(dnsheader->opcode) << " from " << fromaddr.toString() << " on server socket!" << endl,
2519 g_slogudpin->info(Logr::Error, "Ignoring unsupported opcode server socket", "remote", Logging::Loggable(fromaddr), "opcode", Logging::Loggable(Opcode::to_s(dnsheader->opcode))));
2520 }
2521 }
2522 else if (dnsheader->qdcount == 0U) {
2523 t_Counters.at(rec::Counter::emptyQueriesCount)++;
2524 if (g_logCommonErrors) {
2525 SLOG(g_log << Logger::Error << "Ignoring empty (qdcount == 0) query from " << fromaddr.toString() << " on server socket!" << endl,
2526 g_slogudpin->info(Logr::Error, "Ignoring empty (qdcount == 0) query on server socket!", "remote", Logging::Loggable(fromaddr)));
2527 }
2528 }
2529 else {
2530 if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
2531 if (!t_allowNotifyFrom || !t_allowNotifyFrom->match(&mappedSource)) {
2532 if (!g_quiet) {
2533 SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping UDP NOTIFY from " << mappedSource.toString() << ", address not matched by allow-notify-from" << endl,
2534 g_slogudpin->info(Logr::Error, "Dropping UDP NOTIFY from address not matched by allow-notify-from",
2535 "source", Logging::Loggable(mappedSource)));
2536 }
2537
2538 t_Counters.at(rec::Counter::sourceDisallowedNotify)++;
2539 return;
2540 }
2541 }
2542
2543 struct timeval tval = {0, 0};
2544 HarvestTimestamp(&msgh, &tval);
2545 ComboAddress destaddr; // the address the query was sent to to
2546 destaddr.reset(); // this makes sure we ignore this address if not returned by recvmsg above
2547 const auto* loc = rplookup(g_listenSocketsAddresses, fileDesc);
2548 if (HarvestDestinationAddress(&msgh, &destaddr)) {
2549 // but.. need to get port too
2550 if (loc != nullptr) {
2551 destaddr.sin4.sin_port = loc->sin4.sin_port;
2552 }
2553 }
2554 else {
2555 if (loc != nullptr) {
2556 destaddr = *loc;
2557 }
2558 else {
2559 destaddr.sin4.sin_family = fromaddr.sin4.sin_family;
2560 socklen_t slen = destaddr.getSocklen();
2561 getsockname(fileDesc, reinterpret_cast<sockaddr*>(&destaddr), &slen); // if this fails, we're ok with it // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2562 }
2563 }
2564 if (!proxyProto) {
2565 destination = destaddr;
2566 }
2567
2568 if (RecThreadInfo::weDistributeQueries()) {
2569 std::string localdata = data;
2570 distributeAsyncFunction(data, [localdata = std::move(localdata), fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace]() mutable {
2571 return doProcessUDPQuestion(localdata, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace);
2572 });
2573 }
2574 else {
2575 doProcessUDPQuestion(data, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace);
2576 }
2577 }
2578 }
2579 catch (const MOADNSException& mde) {
2580 t_Counters.at(rec::Counter::clientParseError)++;
2581 if (g_logCommonErrors) {
2582 SLOG(g_log << Logger::Error << "Unable to parse packet from remote UDP client " << fromaddr.toString() << ": " << mde.what() << endl,
2583 g_slogudpin->error(Logr::Error, mde.what(), "Unable to parse packet from remote UDP client", "remote", Logging::Loggable(fromaddr), "exception", Logging::Loggable("MOADNSException")));
2584 }
2585 }
2586 catch (const std::runtime_error& e) {
2587 t_Counters.at(rec::Counter::clientParseError)++;
2588 if (g_logCommonErrors) {
2589 SLOG(g_log << Logger::Error << "Unable to parse packet from remote UDP client " << fromaddr.toString() << ": " << e.what() << endl,
2590 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")));
2591 }
2592 }
2593 }
2594 else {
2595 // cerr<<t_id<<" had error: "<<stringerror()<<endl;
2596 if (firstQuery && errno == EAGAIN) {
2597 t_Counters.at(rec::Counter::noPacketError)++;
2598 }
2599
2600 break;
2601 }
2602 }
2603 t_Counters.updateSnap(g_regressionTestMode);
2604 }
2605
2606 void makeUDPServerSockets(deferredAdd_t& deferredAdds, Logr::log_t log)
2607 {
2608 int one = 1;
2609 vector<string> localAddresses;
2610 stringtok(localAddresses, ::arg()["local-address"], " ,");
2611
2612 if (localAddresses.empty()) {
2613 throw PDNSException("No local address specified");
2614 }
2615
2616 const uint16_t defaultLocalPort = ::arg().asNum("local-port");
2617 for (const auto& localAddress : localAddresses) {
2618 ComboAddress address{localAddress, defaultLocalPort};
2619 const int socketFd = socket(address.sin4.sin_family, SOCK_DGRAM, 0);
2620 if (socketFd < 0) {
2621 throw PDNSException("Making a UDP server socket for resolver: " + stringerror());
2622 }
2623 if (!setSocketTimestamps(socketFd)) {
2624 SLOG(g_log << Logger::Warning << "Unable to enable timestamp reporting for socket" << endl,
2625 log->info(Logr::Warning, "Unable to enable timestamp reporting for socket"));
2626 }
2627 if (IsAnyAddress(address)) {
2628 if (address.sin4.sin_family == AF_INET) {
2629 if (setsockopt(socketFd, IPPROTO_IP, GEN_IP_PKTINFO, &one, sizeof(one)) == 0) { // linux supports this, so why not - might fail on other systems
2630 g_fromtosockets.insert(socketFd);
2631 }
2632 }
2633 #ifdef IPV6_RECVPKTINFO
2634 if (address.sin4.sin_family == AF_INET6) {
2635 if (setsockopt(socketFd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)) == 0) {
2636 g_fromtosockets.insert(socketFd);
2637 }
2638 }
2639 #endif
2640 if (address.sin6.sin6_family == AF_INET6 && setsockopt(socketFd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)) < 0) {
2641 int err = errno;
2642 SLOG(g_log << Logger::Warning << "Failed to set IPv6 socket to IPv6 only, continuing anyhow: " << stringerror(err) << endl,
2643 log->error(Logr::Warning, "Failed to set IPv6 socket to IPv6 only, continuing anyhow"));
2644 }
2645 }
2646 if (::arg().mustDo("non-local-bind")) {
2647 Utility::setBindAny(AF_INET6, socketFd);
2648 }
2649
2650 setCloseOnExec(socketFd);
2651
2652 try {
2653 setSocketReceiveBuffer(socketFd, 250000);
2654 }
2655 catch (const std::exception& e) {
2656 SLOG(g_log << Logger::Error << e.what() << endl,
2657 log->error(Logr::Error, e.what(), "Exception while setting socket buffer size"));
2658 }
2659
2660 if (g_reusePort) {
2661 #if defined(SO_REUSEPORT_LB)
2662 try {
2663 SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
2664 }
2665 catch (const std::exception& e) {
2666 throw PDNSException(std::string("SO_REUSEPORT_LB: ") + e.what());
2667 }
2668 #elif defined(SO_REUSEPORT)
2669 try {
2670 SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT, 1);
2671 }
2672 catch (const std::exception& e) {
2673 throw PDNSException(std::string("SO_REUSEPORT: ") + e.what());
2674 }
2675 #endif
2676 }
2677
2678 try {
2679 setSocketIgnorePMTU(socketFd, address.sin4.sin_family);
2680 }
2681 catch (const std::exception& e) {
2682 SLOG(g_log << Logger::Warning << "Failed to set IP_MTU_DISCOVER on UDP server socket: " << e.what() << endl,
2683 log->error(Logr::Warning, e.what(), "Failed to set IP_MTU_DISCOVER on UDP server socket"));
2684 }
2685
2686 socklen_t socklen = address.getSocklen();
2687 if (::bind(socketFd, reinterpret_cast<struct sockaddr*>(&address), socklen) < 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2688 throw PDNSException("Resolver binding to server socket on " + address.toStringWithPort() + ": " + stringerror());
2689 }
2690
2691 setNonBlocking(socketFd);
2692
2693 deferredAdds.emplace_back(socketFd, handleNewUDPQuestion);
2694 g_listenSocketsAddresses[socketFd] = address; // this is written to only from the startup thread, not from the workers
2695 SLOG(g_log << Logger::Info << "Listening for UDP queries on " << address.toStringWithPort() << endl,
2696 log->info(Logr::Info, "Listening for queries", "proto", Logging::Loggable("UDP"), "address", Logging::Loggable(address)));
2697 }
2698 }
2699
2700 static bool trySendingQueryToWorker(unsigned int target, ThreadMSG* tmsg)
2701 {
2702 auto& targetInfo = RecThreadInfo::info(target);
2703 if (!targetInfo.isWorker()) {
2704 SLOG(g_log << Logger::Error << "distributeAsyncFunction() tried to assign a query to a non-worker thread" << endl,
2705 g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() tried to assign a query to a non-worker thread"));
2706 _exit(1);
2707 }
2708
2709 const auto& tps = targetInfo.getPipes();
2710
2711 ssize_t written = write(tps.writeQueriesToThread, &tmsg, sizeof(tmsg)); // NOLINT: correct sizeof
2712 if (written > 0) {
2713 if (static_cast<size_t>(written) != sizeof(tmsg)) { // NOLINT: correct sizeof
2714 delete tmsg; // NOLINT: pointer ownership
2715 unixDie("write to thread pipe returned wrong size or error");
2716 }
2717 }
2718 else {
2719 int error = errno;
2720 if (error == EAGAIN || error == EWOULDBLOCK) {
2721 return false;
2722 }
2723 delete tmsg; // NOLINT: pointer ownership
2724 unixDie("write to thread pipe returned wrong size or error:" + std::to_string(error));
2725 }
2726
2727 return true;
2728 }
2729
2730 static unsigned int getWorkerLoad(size_t workerIdx)
2731 {
2732 const auto* multiThreader = RecThreadInfo::info(RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + workerIdx).getMT();
2733 if (multiThreader != nullptr) {
2734 return multiThreader->numProcesses();
2735 }
2736 return 0;
2737 }
2738
2739 static unsigned int selectWorker(unsigned int hash)
2740 {
2741 assert(RecThreadInfo::numUDPWorkers() != 0); // NOLINT: assert implementation
2742 if (g_balancingFactor == 0) {
2743 return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + (hash % RecThreadInfo::numUDPWorkers());
2744 }
2745
2746 /* we start with one, representing the query we are currently handling */
2747 double currentLoad = 1;
2748 std::vector<unsigned int> load(RecThreadInfo::numUDPWorkers());
2749 for (size_t idx = 0; idx < RecThreadInfo::numUDPWorkers(); idx++) {
2750 load[idx] = getWorkerLoad(idx);
2751 currentLoad += load[idx];
2752 }
2753
2754 double targetLoad = (currentLoad / RecThreadInfo::numUDPWorkers()) * g_balancingFactor;
2755
2756 unsigned int worker = hash % RecThreadInfo::numUDPWorkers();
2757 /* at least one server has to be at or below the average load */
2758 if (load[worker] > targetLoad) {
2759 ++t_Counters.at(rec::Counter::rebalancedQueries);
2760 do {
2761 worker = (worker + 1) % RecThreadInfo::numUDPWorkers();
2762 } while (load[worker] > targetLoad);
2763 }
2764
2765 return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + worker;
2766 }
2767
2768 // This function is only called by the distributor threads, when pdns-distributes-queries is set
2769 void distributeAsyncFunction(const string& packet, const pipefunc_t& func)
2770 {
2771 if (!RecThreadInfo::self().isDistributor()) {
2772 SLOG(g_log << Logger::Error << "distributeAsyncFunction() has been called by a worker (" << RecThreadInfo::id() << ")" << endl,
2773 g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() has been called by a worker")); // tid will be added
2774 _exit(1);
2775 }
2776
2777 bool hashOK = false;
2778 unsigned int hash = hashQuestion(reinterpret_cast<const uint8_t*>(packet.data()), packet.length(), g_disthashseed, hashOK); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2779 if (!hashOK) {
2780 // hashQuestion does detect invalid names, so we might as well punt here instead of in the worker thread
2781 t_Counters.at(rec::Counter::ignoredCount)++;
2782 throw MOADNSException("too-short (" + std::to_string(packet.length()) + ") or invalid name");
2783 }
2784 unsigned int target = selectWorker(hash);
2785
2786 ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: pointer ownership
2787 tmsg->func = func;
2788 tmsg->wantAnswer = false;
2789
2790 if (!trySendingQueryToWorker(target, tmsg)) {
2791 /* if this function failed but did not raise an exception, it means that the pipe
2792 was full, let's try another one */
2793 unsigned int newTarget = 0;
2794 do {
2795 newTarget = RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + dns_random(RecThreadInfo::numUDPWorkers());
2796 } while (newTarget == target);
2797
2798 if (!trySendingQueryToWorker(newTarget, tmsg)) {
2799 t_Counters.at(rec::Counter::queryPipeFullDrops)++;
2800 delete tmsg; // NOLINT: pointer ownership
2801 }
2802 }
2803 // coverity[leaked_storage]
2804 }
2805
2806 // resend event to everybody chained onto it
2807 static void doResends(MT_t::waiters_t::iterator& iter, const std::shared_ptr<PacketID>& resend, const PacketBuffer& content)
2808 {
2809 // We close the chain for new entries, since they won't be processed anyway
2810 iter->key->closed = true;
2811
2812 if (iter->key->chain.empty()) {
2813 return;
2814 }
2815 for (auto i = iter->key->chain.begin(); i != iter->key->chain.end(); ++i) {
2816 auto packetID = std::make_shared<PacketID>(*resend);
2817 packetID->fd = -1;
2818 packetID->id = *i;
2819 g_multiTasker->sendEvent(packetID, &content);
2820 t_Counters.at(rec::Counter::chainResends)++;
2821 }
2822 }
2823
2824 static void handleUDPServerResponse(int fileDesc, FDMultiplexer::funcparam_t& var)
2825 {
2826 auto pid = boost::any_cast<std::shared_ptr<PacketID>>(var);
2827 PacketBuffer packet;
2828 packet.resize(g_outgoingEDNSBufsize);
2829 ComboAddress fromaddr;
2830 socklen_t addrlen = sizeof(fromaddr);
2831
2832 ssize_t len = recvfrom(fileDesc, &packet.at(0), packet.size(), 0, reinterpret_cast<sockaddr*>(&fromaddr), &addrlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2833
2834 const ssize_t signed_sizeof_sdnsheader = sizeof(dnsheader);
2835
2836 if (len < 0) {
2837 // len < 0: error on socket
2838 t_udpclientsocks->returnSocket(fileDesc);
2839
2840 PacketBuffer empty;
2841 auto iter = g_multiTasker->getWaiters().find(pid);
2842 if (iter != g_multiTasker->getWaiters().end()) {
2843 doResends(iter, pid, empty);
2844 }
2845 g_multiTasker->sendEvent(pid, &empty); // this denotes error (does retry lookup using other NS)
2846 return;
2847 }
2848
2849 if (len < signed_sizeof_sdnsheader) {
2850 // We have received a packet that cannot be a valid DNS packet, as it has no complete header
2851 // Drop it, but continue to wait for other packets
2852 t_Counters.at(rec::Counter::serverParseError)++;
2853 if (g_logCommonErrors) {
2854 SLOG(g_log << Logger::Error << "Unable to parse too short packet from remote UDP server " << fromaddr.toString() << ": packet smaller than DNS header" << endl,
2855 g_slogout->info(Logr::Error, "Unable to parse too short packet from remote UDP server", "from", Logging::Loggable(fromaddr)));
2856 }
2857 return;
2858 }
2859
2860 // We have at least a full header
2861 packet.resize(len);
2862 dnsheader dnsheader{};
2863 memcpy(&dnsheader, &packet.at(0), sizeof(dnsheader));
2864
2865 auto pident = std::make_shared<PacketID>();
2866 pident->remote = fromaddr;
2867 pident->id = dnsheader.id;
2868 pident->fd = fileDesc;
2869
2870 if (!dnsheader.qr && g_logCommonErrors) {
2871 SLOG(g_log << Logger::Notice << "Not taking data from question on outgoing socket from " << fromaddr.toStringWithPort() << endl,
2872 g_slogout->info(Logr::Error, "Not taking data from question on outgoing socket", "from", Logging::Loggable(fromaddr)));
2873 }
2874
2875 if (dnsheader.qdcount == 0U || // UPC, Nominum, very old BIND on FormErr, NSD
2876 dnsheader.qr == 0U) { // one weird server
2877 pident->domain.clear();
2878 pident->type = 0;
2879 }
2880 else {
2881 try {
2882 if (len > signed_sizeof_sdnsheader) {
2883 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)
2884 }
2885 else {
2886 // len == sizeof(dnsheader), only header case
2887 // We will do a full scan search later to see if we can match this reply even without a domain
2888 pident->domain.clear();
2889 pident->type = 0;
2890 }
2891 }
2892 catch (std::exception& e) {
2893 // Parse error, continue waiting for other packets
2894 t_Counters.at(rec::Counter::serverParseError)++; // won't be fed to lwres.cc, so we have to increment
2895 SLOG(g_log << Logger::Warning << "Error in packet from remote nameserver " << fromaddr.toStringWithPort() << ": " << e.what() << endl,
2896 g_slogudpin->error(Logr::Warning, e.what(), "Error in packet from remote nameserver", "from", Logging::Loggable(fromaddr)));
2897 return;
2898 }
2899 }
2900
2901 if (!pident->domain.empty()) {
2902 auto iter = g_multiTasker->getWaiters().find(pident);
2903 if (iter != g_multiTasker->getWaiters().end()) {
2904 doResends(iter, pident, packet);
2905 }
2906 }
2907
2908 retryWithName:
2909
2910 if (pident->domain.empty() || g_multiTasker->sendEvent(pident, &packet) == 0) {
2911 /* we did not find a match for this response, something is wrong */
2912
2913 // 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
2914 for (const auto& d_waiter : g_multiTasker->getWaiters()) {
2915 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) {
2916 /* we are expecting an answer from that exact source, on that exact port (since we are using connected sockets), for that qname/qtype,
2917 but with a different message ID. That smells like a spoofing attempt. For now we will just increase the counter and will deal with
2918 that later. */
2919 d_waiter.key->nearMisses++;
2920 }
2921
2922 // be a bit paranoid here since we're weakening our matching
2923 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) {
2924 // cerr<<"Empty response, rest matches though, sending to a waiter"<<endl;
2925 pident->domain = d_waiter.key->domain;
2926 pident->type = d_waiter.key->type;
2927 goto retryWithName; // note that this only passes on an error, lwres will still reject the packet NOLINT(cppcoreguidelines-avoid-goto)
2928 }
2929 }
2930 t_Counters.at(rec::Counter::unexpectedCount)++; // if we made it here, it really is an unexpected answer
2931 if (g_logCommonErrors) {
2932 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,
2933 g_slogudpin->info(Logr::Warning, "Discarding unexpected packet", "from", Logging::Loggable(fromaddr),
2934 "qname", Logging::Loggable(pident->domain),
2935 "qtype", Logging::Loggable(QType(pident->type)),
2936 "waiters", Logging::Loggable(g_multiTasker->getWaiters().size())));
2937 }
2938 }
2939 else if (fileDesc >= 0) {
2940 /* we either found a waiter (1) or encountered an issue (-1), it's up to us to clean the socket anyway */
2941 t_udpclientsocks->returnSocket(fileDesc);
2942 }
2943 }