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