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