2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
23 #include "rec-main.hh"
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"
32 #include "validate-recursor.hh"
35 #include <systemd/sd-daemon.h>
41 #endif /* NOD_ENABLED */
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
;
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
;
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
62 using listenSocketsAddresses_t
= map
<int, ComboAddress
>; // is shared across all threads right now
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
;
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
;
83 std::set
<uint16_t> g_avoidUdpSourcePorts
;
85 uint16_t g_minUdpSourcePort
;
86 uint16_t g_maxUdpSourcePort
;
87 double g_balancingFactor
;
89 bool g_lowercaseOutgoing
;
90 unsigned int g_networkTimeoutMsec
;
91 uint16_t g_outgoingEDNSBufsize
;
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
;
101 LWResult::Result
UDPClientSocks::getSocket(const ComboAddress
& toaddr
, int* fileDesc
)
103 *fileDesc
= makeClientSocket(toaddr
.sin4
.sin_family
);
104 if (*fileDesc
< 0) { // temporary error - receive exception otherwise
105 return LWResult::Result::OSLimitError
;
108 if (connect(*fileDesc
, reinterpret_cast<const struct sockaddr
*>(&toaddr
), toaddr
.getSocklen()) < 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast))
111 closesocket(*fileDesc
);
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")));
118 if (err
== ENETUNREACH
) { // Seth "My Interfaces Are Like A Yo Yo" Arnold special
119 return LWResult::Result::OSLimitError
;
122 return LWResult::Result::PermanentError
;
126 return LWResult::Result::Success
;
129 // return a socket to the pool, or simply erase it
130 void UDPClientSocks::returnSocket(int fileDesc
)
133 t_fdm
->removeReadFD(fileDesc
);
135 catch (const FDMultiplexerException
& e
) {
136 // we sometimes return a socket that has not yet been assigned to t_fdm
140 closesocket(fileDesc
);
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")));
150 // returns -1 for errors which might go away, throws for ones that won't
151 int UDPClientSocks::makeClientSocket(int family
)
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)
155 if (ret
< 0 && errno
== EMFILE
) { // this is not a catastrophic error
160 throw PDNSException("Making a socket for resolver (family = " + std::to_string(family
) + "): " + stringerror(err
));
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__)
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
171 while (--tries
!= 0) {
174 if (tries
== 1) { // last iteration: fall back to kernel 'random'
179 port
= g_minUdpSourcePort
+ dns_random(g_maxUdpSourcePort
- g_minUdpSourcePort
+ 1);
180 } while (g_avoidUdpSourcePorts
.count(port
) != 0);
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)
193 throw PDNSException("Resolver binding to local query client socket on " + sin
.toString() + ": " + stringerror(err
));
197 setReceiveSocketErrors(ret
, family
);
207 static void handleGenUDPQueryResponse(int fileDesc
, FDMultiplexer::funcparam_t
& var
)
209 auto pident
= boost::any_cast
<std::shared_ptr
<PacketID
>>(var
);
212 ComboAddress fromaddr
;
213 socklen_t addrlen
= sizeof(fromaddr
);
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
)));
221 t_fdm
->removeReadFD(fileDesc
);
224 g_multiTasker
->sendEvent(pident
, &resp
);
228 g_multiTasker
->sendEvent(pident
, &empty
);
229 // cerr<<"Had some kind of error: "<<ret<<", "<<stringerror()<<endl;
233 PacketBuffer
GenUDPQueryResponse(const ComboAddress
& dest
, const string
& query
)
235 Socket
socket(dest
.sin4
.sin_family
, SOCK_DGRAM
);
236 socket
.setNonBlocking();
237 ComboAddress local
= pdns::getQueryLocalAddress(dest
.sin4
.sin_family
, 0);
240 socket
.connect(dest
);
243 std::shared_ptr
<PacketID
> pident
= std::make_shared
<PacketID
>();
244 pident
->fd
= socket
.getHandle();
245 pident
->remote
= dest
;
247 t_fdm
->addReadFD(socket
.getHandle(), handleGenUDPQueryResponse
, pident
);
250 int ret
= g_multiTasker
->waitEvent(pident
, &data
, g_networkTimeoutMsec
);
252 if (ret
== 0 || ret
== -1) { // timeout
253 t_fdm
->removeReadFD(socket
.getHandle());
255 else if (data
.empty()) { // error, EOF or other
256 // we could special case this
262 static void handleUDPServerResponse(int fileDesc
, FDMultiplexer::funcparam_t
& var
);
264 thread_local
std::unique_ptr
<UDPClientSocks
> t_udpclientsocks
;
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
)
271 auto pident
= std::make_shared
<PacketID
>();
272 pident
->domain
= domain
;
273 pident
->remote
= toAddress
;
274 pident
->type
= qtype
;
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
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
283 auto chain
= g_multiTasker
->getWaiters().equal_range(pident
, PacketIDBirthdayCompare());
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
;
297 auto ret
= t_udpclientsocks
->getSocket(toAddress
, fileDesc
);
298 if (ret
!= LWResult::Result::Success
) {
302 pident
->fd
= *fileDesc
;
305 t_fdm
->addReadFD(*fileDesc
, handleUDPServerResponse
, pident
);
306 ssize_t sent
= send(*fileDesc
, data
, len
, 0);
311 t_udpclientsocks
->returnSocket(*fileDesc
);
312 errno
= tmp
; // this is for logging purposes only
313 return LWResult::Result::PermanentError
;
316 return LWResult::Result::Success
;
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
)
322 static const unsigned int nearMissLimit
= ::arg().asNum("spoof-nearmiss-max");
324 auto pident
= std::make_shared
<PacketID
>();
325 pident
->fd
= fileDesc
;
327 pident
->domain
= domain
;
328 pident
->type
= qtype
;
329 pident
->remote
= fromAddr
;
331 int ret
= g_multiTasker
->waitEvent(pident
, &packet
, g_networkTimeoutMsec
, &now
);
334 /* -1 means error, 0 means timeout, 1 means a result from handleUDPServerResponse() which might still be an error */
336 /* handleUDPServerResponse() will close the socket for us no matter what */
337 if (packet
.empty()) { // means "error"
338 return LWResult::Result::PermanentError
;
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
;
356 return LWResult::Result::Success
;
358 /* getting there means error or timeout, it's up to us to close the socket */
360 t_udpclientsocks
->returnSocket(fileDesc
);
363 return ret
== 0 ? LWResult::Result::Timeout
: LWResult::Result::PermanentError
;
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
)
369 if (packetsize
> 1000 && t_largeanswerremotes
) {
370 t_largeanswerremotes
->push_back(remote
);
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
});
380 ++t_Counters
.at(rec::Counter::servFails
);
382 case RCode::NXDomain
:
383 ++t_Counters
.at(rec::Counter::nxDomains
);
386 t_Counters
.at(rec::Counter::noErrors
)++;
392 * Chases the CNAME provided by the PolicyCustom RPZ policy.
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
400 static void handleRPZCustom(const DNSRecord
& spoofed
, const QType
& qtype
, SyncRes
& resolver
, int& res
, vector
<DNSRecord
>& ret
)
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
) {
412 // Reset the RPZ state of the SyncRes
413 resolver
.setWantsRPZ(oldWantsRPZ
);
417 static bool addRecordToPacket(DNSPacketWriter
& packetWritewr
, const DNSRecord
& rec
, uint32_t& minTTL
, uint32_t ttlCap
, const uint16_t maxAnswerSize
, bool& seenAuthSOA
)
419 packetWritewr
.startRecord(rec
.d_name
, rec
.d_type
, (rec
.d_ttl
> ttlCap
? ttlCap
: rec
.d_ttl
), rec
.d_class
, rec
.d_place
);
421 if (rec
.d_type
== QType::SOA
&& rec
.d_place
== DNSResourceRecord::AUTHORITY
) {
425 if (rec
.d_type
!= QType::OPT
) { // their TTL ain't real
426 minTTL
= min(minTTL
, rec
.d_ttl
);
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();
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.
448 class RunningResolveGuard
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
) :
458 if (d_dc
->d_tcp
&& !d_dc
->d_tcpConnection
) {
459 throw std::runtime_error("incoming TCP case without TCP connection");
462 ~RunningResolveGuard()
464 if (!d_handled
&& d_dc
->d_tcp
) {
466 finishTCPReply(d_dc
, false, true);
468 catch (const FDMultiplexerException
&) {
479 d_dc
->d_tcpConnection
->setDropOnIdle();
484 std::unique_ptr
<DNSComboWriter
>& d_dc
;
485 bool d_handled
{false};
488 enum class PolicyResult
: uint8_t
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
)
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()];
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
));
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
;
513 switch (appliedPolicy
.d_kind
) {
515 case DNSFilterEngine::PolicyKind::NoAction
:
516 return PolicyResult::NoAction
;
518 case DNSFilterEngine::PolicyKind::Drop
:
519 tcpGuard
.setDropOnIdle();
520 ++t_Counters
.at(rec::Counter::policyDrops
);
521 return PolicyResult::Drop
;
523 case DNSFilterEngine::PolicyKind::NXDOMAIN
:
525 appliedPolicy
.addSOAtoRPZResult(ret
);
526 res
= RCode::NXDomain
;
527 return PolicyResult::HaveAnswer
;
529 case DNSFilterEngine::PolicyKind::NODATA
:
531 appliedPolicy
.addSOAtoRPZResult(ret
);
532 res
= RCode::NoError
;
533 return PolicyResult::HaveAnswer
;
535 case DNSFilterEngine::PolicyKind::Truncate
:
536 if (!comboWriter
->d_tcp
) {
538 appliedPolicy
.addSOAtoRPZResult(ret
);
539 res
= RCode::NoError
;
540 packetWriter
.getHeader()->tc
= 1;
541 return PolicyResult::HaveAnswer
;
543 return PolicyResult::NoAction
;
545 case DNSFilterEngine::PolicyKind::Custom
:
546 res
= RCode::NoError
;
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
);
552 handleRPZCustom(record
, QType(comboWriter
->d_mdp
.d_qtype
), resolver
, res
, ret
);
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")));
560 res
= RCode::ServFail
;
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())));
569 res
= RCode::ServFail
;
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")));
578 res
= RCode::ServFail
;
583 appliedPolicy
.addSOAtoRPZResult(ret
);
584 return PolicyResult::HaveAnswer
;
588 return PolicyResult::NoAction
;
592 static bool nodCheckNewDomain(Logr::log_t nodlogger
, const DNSName
& dname
)
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
)) {
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"));
604 t_Counters
.at(rec::Counter::nodCount
)++;
611 static void sendNODLookup(Logr::log_t nodlogger
, const DNSName
& dname
)
613 if (!(g_nodLookupDomain
.isRoot())) {
614 // Send a DNS A query to <domain>.g_nodLookupDomain
617 qname
= dname
+ g_nodLookupDomain
;
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");
623 ++t_Counters
.at(rec::Counter::nodLookupsDroppedOversize
);
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
);
632 static bool udrCheckUniqueDNSRecord(Logr::log_t nodlogger
, const DNSName
& dname
, uint16_t qtype
, const DNSRecord
& record
)
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())) {
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())););
649 t_Counters
.at(rec::Counter::udrCount
)++;
655 #endif /* NOD_ENABLED */
657 static bool dns64Candidate(uint16_t requestedType
, int rcode
, const std::vector
<DNSRecord
>& records
);
659 int followCNAMERecords(vector
<DNSRecord
>& ret
, const QType qtype
, int rcode
)
661 vector
<DNSRecord
> resolved
;
663 for (const DNSRecord
& record
: ret
) {
664 if (record
.d_type
== QType::CNAME
) {
665 auto rec
= getRR
<CNAMERecordContent
>(record
);
667 target
= rec
->getTarget();
673 if (target
.empty()) {
677 auto log
= g_slog
->withName("lua")->withValues("method", Logging::Loggable("followCNAMERecords"));
678 rcode
= directResolve(target
, qtype
, QClass::IN
, resolved
, t_pdl
, log
);
680 if (g_dns64Prefix
&& qtype
== QType::AAAA
&& dns64Candidate(qtype
, rcode
, resolved
)) {
681 rcode
= getFakeAAAARecords(target
, *g_dns64Prefix
, resolved
);
684 for (DNSRecord
& record
: resolved
) {
685 if (record
.d_place
== DNSResourceRecord::ANSWER
) {
686 ret
.push_back(std::move(record
));
692 int getFakeAAAARecords(const DNSName
& qname
, ComboAddress prefix
, vector
<DNSRecord
>& ret
)
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
698 vector
<DNSRecord
> newRecords
;
699 int rcode
= directResolve(qname
, QType::A
, QClass::IN
, newRecords
, t_pdl
, log
);
701 ret
.reserve(ret
.size() + newRecords
.size());
702 for (auto& record
: newRecords
) {
703 ret
.push_back(std::move(record
));
706 // Remove double CNAME records
707 std::set
<DNSName
> seenCNAMEs
;
708 ret
.erase(std::remove_if(
711 [&seenCNAMEs
](DNSRecord
& record
) {
712 if (record
.d_type
== QType::CNAME
) {
713 auto target
= getRR
<CNAMERecordContent
>(record
);
714 if (target
== nullptr) {
717 if (seenCNAMEs
.count(target
->getTarget()) > 0) {
718 // We've had this CNAME before, remove it
721 seenCNAMEs
.insert(target
->getTarget());
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
;
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(
746 [](DNSRecord
& record
) {
747 return (record
.d_type
== QType::SOA
&& record
.d_place
== DNSResourceRecord::AUTHORITY
);
751 t_Counters
.at(rec::Counter::dns64prefixanswers
)++;
755 int getFakePTRRecords(const DNSName
& qname
, vector
<DNSRecord
>& ret
)
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 */
760 vector
<string
> parts
= qname
.getRawLabels();
762 if (parts
.size() < 8) {
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, '.');
771 newquery
+= "in-addr.arpa.";
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
);
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
);
785 ret
.insert(ret
.end(), answers
.begin(), answers
.end());
787 t_Counters
.at(rec::Counter::dns64prefixanswers
)++;
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
)
795 if (rcode
== RCode::NoError
) {
796 return SyncRes::answerIsNOData(requestedType
, rcode
, records
);
798 return rcode
!= RCode::NXDomain
;
801 bool isAllowNotifyForZone(DNSName qname
)
803 if (t_allowNotifyFor
->empty()) {
807 notifyset_t::const_iterator ret
;
809 ret
= t_allowNotifyFor
->find(qname
);
810 if (ret
!= t_allowNotifyFor
->end()) {
813 } while (qname
.chopOff());
817 #if defined(HAVE_FSTRM) && defined(NOD_ENABLED)
819 #include "fstrm_logger.hh"
821 static bool isEnabledForNODs(const std::shared_ptr
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>>& fstreamLoggers
)
823 if (fstreamLoggers
== nullptr) {
826 for (auto& logger
: *fstreamLoggers
) {
827 if (logger
->logNODs()) {
833 static bool isEnabledForUDRs(const std::shared_ptr
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>>& fstreamLoggers
)
835 if (fstreamLoggers
== nullptr) {
838 for (auto& logger
: *fstreamLoggers
) {
839 if (logger
->logUDRs()) {
847 static void dumpTrace(const string
& trace
, const timeval
& timev
)
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
;
858 g_log
<< Logger::Warning
<< "=== END OF FAIL TRACE ====" << endl
;
862 Utility::gettimeofday(&now
);
863 int traceFd
= dup(t_tracefd
);
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"));
870 setNonBlocking(traceFd
);
871 auto filep
= pdns::UniqueFilePtr(fdopen(traceFd
, "a"));
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"));
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) {
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());
893 fprintf(filep
.get(), "=== END OF TRACE %s ===\n", timebuf
.data());
895 // fclose by unique_ptr does implicit flush
898 static uint32_t capPacketCacheTTL(const struct dnsheader
& hdr
, uint32_t ttl
, bool seenAuthSOA
)
900 if (hdr
.rcode
== RCode::NXDomain
|| (hdr
.rcode
== RCode::NoError
&& hdr
.ancount
== 0 && seenAuthSOA
)) {
901 ttl
= std::min(ttl
, SyncRes::s_packetcachenegativettl
);
903 else if ((hdr
.rcode
!= RCode::NoError
&& hdr
.rcode
!= RCode::NXDomain
) || (hdr
.ancount
== 0 && hdr
.nscount
== 0)) {
904 ttl
= min(ttl
, SyncRes::s_packetcacheservfailttl
);
907 ttl
= std::min(ttl
, SyncRes::s_packetcachettl
);
912 static void addPolicyTagsToPBMessageIfNeeded(DNSComboWriter
& comboWriter
, pdns::ProtoZero::RecMessage
& pbMessage
)
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
);
921 if (!comboWriter
.d_policyTags
.empty()) {
922 pbMessage
.addPolicyTags(comboWriter
.d_policyTags
);
926 void startDoResolve(void* arg
) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
928 auto comboWriter
= std::unique_ptr
<DNSComboWriter
>(static_cast<DNSComboWriter
*>(arg
));
929 SyncRes
resolver(comboWriter
->d_now
);
932 t_queryring
->push_back({comboWriter
->d_mdp
.d_qname
, comboWriter
->d_mdp
.d_qtype
});
935 uint16_t maxanswersize
= comboWriter
->d_tcp
? 65535 : min(static_cast<uint16_t>(512), g_udpTruncationThreshold
);
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;
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
));
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
)) {
953 if (edo
.d_version
!= 0) {
954 ednsExtRCode
= ERCode::BADVERS
;
957 if (!comboWriter
->d_tcp
) {
959 "Values lower than 512 MUST be treated as equal to 512."
961 maxanswersize
= min(static_cast<uint16_t>(edo
.d_packetsize
>= 512 ? edo
.d_packetsize
: 512), g_udpTruncationThreshold
);
963 ednsOpts
= edo
.d_options
;
964 maxanswersize
-= 11; // EDNS header size
966 if (!comboWriter
->d_responsePaddingDisabled
&& g_paddingFrom
.match(comboWriter
->d_remote
)) {
967 paddingAllowed
= true;
968 if (g_paddingMode
== PaddingMode::Always
) {
969 addPaddingToResponse
= true;
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
);
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();
985 else if (paddingAllowed
&& !addPaddingToResponse
&& g_paddingMode
== PaddingMode::PaddedQueries
&& option
.first
== EDNSOptionCode::PADDING
) {
986 addPaddingToResponse
= true;
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
;
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
;
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
);
1013 // RRSets added below
1015 checkOutgoingProtobufExport(luaconfsLocal
); // to pick up changed configs
1017 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->frameStreamExportConfig
, t_frameStreamServersInfo
);
1018 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->nodFrameStreamExportConfig
, t_nodFrameStreamServersInfo
);
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
);
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
;
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;
1038 resolver
.d_eventTrace
= std::move(comboWriter
->d_eventTrace
);
1039 resolver
.setId(g_multiTasker
->getTid());
1041 bool DNSSECOK
= false;
1042 if (comboWriter
->d_luaContext
) {
1043 resolver
.setLuaEngine(comboWriter
->d_luaContext
);
1045 if (g_dnssecmode
!= DNSSECMode::Off
) {
1046 resolver
.setDoDNSSEC(true);
1048 // Does the requestor want DNSSEC records?
1049 if ((edo
.d_extFlags
& EDNSOpts::DNSSECOK
) != 0) {
1051 t_Counters
.at(rec::Counter::dnssecQueries
)++;
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
1058 ++t_Counters
.at(rec::Counter::dnssecCheckDisabledQueries
);
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
);
1070 // Ignore the client-set CD flag
1071 packetWriter
.getHeader()->cd
= 0;
1073 resolver
.setDNSSECValidationRequested(g_dnssecmode
== DNSSECMode::ValidateAll
|| g_dnssecmode
== DNSSECMode::ValidateForLog
|| ((comboWriter
->d_mdp
.d_header
.ad
|| DNSSECOK
) && g_dnssecmode
== DNSSECMode::Process
));
1075 resolver
.setInitialRequestId(comboWriter
->d_uuid
);
1076 resolver
.setOutgoingProtobufServers(t_outgoingProtobufServers
.servers
);
1078 resolver
.setFrameStreamServers(t_frameStreamServersInfo
.servers
);
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
1092 ++iter
->second
.stats
.suffixMatches
;
1095 // No suffix match node defined, use mapped address
1097 // lookup failing cannot happen as dc->d_source != dc->d_mappedSource
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
);
1101 resolver
.setQueryReceivedOverTCP(comboWriter
->d_tcp
);
1103 bool tracedQuery
= false; // we could consider letting Lua know about this too
1104 bool shouldNotValidate
= false;
1106 /* preresolve expects res (dq.rcode) to be set to RCode::NoError by default */
1107 int res
= RCode::NoError
;
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
;
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
);
1137 if (ednsExtRCode
!= 0 || comboWriter
->d_mdp
.d_header
.opcode
== static_cast<unsigned>(Opcode::Notify
)) {
1138 goto sendit
; // NOLINT(cppcoreguidelines-avoid-goto)
1141 if (comboWriter
->d_mdp
.d_qtype
== QType::ANY
&& !comboWriter
->d_tcp
&& g_anyToTcp
) {
1142 packetWriter
.getHeader()->tc
= 1;
1144 variableAnswer
= true;
1145 goto sendit
; // NOLINT(cppcoreguidelines-avoid-goto)
1148 if (t_traceRegex
&& t_traceRegex
->match(comboWriter
->d_mdp
.d_qname
.toString())) {
1149 resolver
.setLogMode(SyncRes::Store
);
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() << ")";
1163 resolver
.d_slog
->info(Logr::Info
, "Question");
1167 if (!comboWriter
->d_mdp
.d_header
.rd
) {
1169 resolver
.setCacheOnly();
1173 res
= RCode::Refused
;
1174 goto haveAnswer
; // NOLINT(cppcoreguidelines-avoid-goto)
1178 if (comboWriter
->d_luaContext
) {
1179 comboWriter
->d_luaContext
->prerpz(dnsQuestion
, res
, resolver
.d_eventTrace
);
1182 // Check if the client has a policy attached to it
1183 if (wantsRPZ
&& !appliedPolicy
.wasHit()) {
1185 if (luaconfsLocal
->dfe
.getClientPolicy(comboWriter
->d_source
, resolver
.d_discardedPolicies
, appliedPolicy
)) {
1186 mergePolicyTags(comboWriter
->d_policyTags
, appliedPolicy
.getTags());
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
) {
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
1202 // no match on the client IP, check the qname
1203 if (luaconfsLocal
->dfe
.getQueryPolicy(comboWriter
->d_mdp
.d_qname
, resolver
.d_discardedPolicies
, appliedPolicy
)) {
1205 mergePolicyTags(comboWriter
->d_policyTags
, appliedPolicy
.getTags());
1209 if (appliedPolicy
.wasHit()) {
1210 policyOverride
= true;
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
);
1221 goto haveAnswer
; // NOLINT(cppcoreguidelines-avoid-goto)
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
)) {
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)
1233 resolver
.setWantsRPZ(wantsRPZ
);
1235 if (wantsRPZ
&& appliedPolicy
.d_kind
!= DNSFilterEngine::PolicyKind::NoAction
) {
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();
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;
1248 goto haveAnswer
; // NOLINT(cppcoreguidelines-avoid-goto)
1250 else if (policyResult
== PolicyResult::Drop
) {
1256 // Query did not get handled for Client IP or QNAME Policy reasons, now actually go out to find an answer
1258 resolver
.d_appliedPolicy
= appliedPolicy
;
1259 resolver
.d_policyTags
= std::move(comboWriter
->d_policyTags
);
1261 if (!comboWriter
->d_routingTag
.empty()) {
1262 resolver
.d_routingTag
= comboWriter
->d_routingTag
;
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();
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"));
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"));
1281 res
= RCode::ServFail
;
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)));
1288 res
= RCode::ServFail
;
1290 catch (const SendTruncatedAnswerException
& e
) {
1292 resolver
.d_appliedPolicy
.addSOAtoRPZResult(ret
);
1293 res
= RCode::NoError
;
1294 packetWriter
.getHeader()->tc
= 1;
1296 catch (const PolicyHitException
& e
) {
1299 dnsQuestion
.validationState
= resolver
.getValidationState();
1300 appliedPolicy
= resolver
.d_appliedPolicy
;
1301 comboWriter
->d_policyTags
= std::move(resolver
.d_policyTags
);
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
;
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");
1313 auto policyResult
= handlePolicyHit(appliedPolicy
, comboWriter
, resolver
, res
, ret
, packetWriter
, tcpGuard
);
1314 if (policyResult
== PolicyResult::HaveAnswer
) {
1315 goto haveAnswer
; // NOLINT(cppcoreguidelines-avoid-goto)
1317 else if (policyResult
== PolicyResult::Drop
) {
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
);
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
);
1337 if (policyResult
== PolicyResult::HaveAnswer
) {
1338 goto haveAnswer
; // NOLINT(cppcoreguidelines-avoid-goto)
1340 else if (policyResult
== PolicyResult::Drop
) {
1343 } // dc->d_luaContext
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;
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);
1358 shouldNotValidate
= true;
1359 policyResult
= handlePolicyHit(appliedPolicy
, comboWriter
, resolver
, res
, ret
, packetWriter
, tcpGuard
);
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
);
1366 if (policyResult
== PolicyResult::HaveAnswer
) {
1367 goto haveAnswer
; // NOLINT(cppcoreguidelines-avoid-goto)
1369 else if (policyResult
== PolicyResult::Drop
) {
1372 } // dc->d_luaContext
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
) {
1385 if (tracedQuery
|| res
== -1 || res
== RCode::ServFail
|| packetWriter
.getHeader()->rcode
== static_cast<unsigned>(RCode::ServFail
)) {
1386 dumpTrace(resolver
.getTrace(), resolver
.d_fixednow
);
1390 packetWriter
.getHeader()->rcode
= RCode::ServFail
;
1391 // no commit here, because no record
1392 ++t_Counters
.at(rec::Counter::servFails
);
1395 packetWriter
.getHeader()->rcode
= res
;
1397 // Does the validation mode or query demand validation?
1398 if (!shouldNotValidate
&& resolver
.isDNSSECValidationRequested()) {
1400 auto state
= resolver
.getValidationState();
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));
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]";
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"));
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;
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"));
1433 packetWriter
.getHeader()->ad
= 0;
1435 else if (vStateIsBogus(state
)) {
1436 if (t_bogusremotes
) {
1437 t_bogusremotes
->push_back(comboWriter
->d_source
);
1439 if (t_bogusqueryring
) {
1440 t_bogusqueryring
->push_back({comboWriter
->d_mdp
.d_qname
, comboWriter
->d_mdp
.d_qtype
});
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"));
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"));
1454 packetWriter
.getHeader()->rcode
= RCode::ServFail
;
1455 goto sendit
; // NOLINT(cppcoreguidelines-avoid-goto)
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"));
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")));
1470 goto sendit
; // NOLINT(cppcoreguidelines-avoid-goto)
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())));
1477 goto sendit
; // NOLINT(cppcoreguidelines-avoid-goto)
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;
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
))))) {
1495 if (!addRecordToPacket(packetWriter
, record
, minTTL
, comboWriter
->d_ttlCap
, maxanswersize
, seenAuthSOA
)) {
1504 udr
= udrCheckUniqueDNSRecord(nodlogger
, comboWriter
->d_mdp
.d_qname
, comboWriter
->d_mdp
.d_qtype
, record
);
1505 if (!hasUDR
&& udr
) {
1509 #endif /* NOD ENABLED */
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
);
1521 packetWriter
.commit();
1526 if (isEnabledForUDRs(t_nodFrameStreamServersInfo
.servers
)) {
1527 struct timespec timeSpec
1531 if (g_useKernelTimestamp
&& comboWriter
->d_kernelTimestamp
.tv_sec
!= 0) {
1532 TIMEVAL_TO_TIMESPEC(&comboWriter
->d_kernelTimestamp
, &timeSpec
); // NOLINT
1535 TIMEVAL_TO_TIMESPEC(&comboWriter
->d_now
, &timeSpec
); // NOLINT
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
);
1546 #endif // HAVE_FSTRM
1547 #endif // NOD_ENABLED
1551 if (g_useIncomingECS
&& comboWriter
->d_ecsFound
&& !resolver
.wasVariable() && !variableAnswer
) {
1552 EDNSSubnetOpts ednsOptions
;
1553 ednsOptions
.source
= comboWriter
->d_ednssubnet
.source
;
1554 ComboAddress sourceAddr
;
1556 sourceAddr
.sin4
.sin_family
= ednsOptions
.source
.getNetwork().sin4
.sin_family
;
1557 ednsOptions
.scope
= Netmask(sourceAddr
, 0);
1558 auto ecsPayload
= makeEDNSSubnetOptsString(ednsOptions
);
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())) {
1564 maxanswersize
-= EDNSOptionCodeSize
+ EDNSOptionLengthSize
+ ecsPayload
.size();
1566 returnedEdnsOptions
.emplace_back(EDNSOptionCode::ECS
, std::move(ecsPayload
));
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
);
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).
1584 const size_t blockSize
= 468;
1585 size_t modulo
= (currentSize
+ 4) % blockSize
;
1588 padSize
= std::min(blockSize
- modulo
, remaining
);
1590 returnedEdnsOptions
.emplace_back(EDNSOptionCode::PADDING
, makeEDNSPaddingOptString(padSize
));
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
;
1600 if (comboWriter
->d_extendedErrorCode
) {
1601 code
= static_cast<EDNSExtendedError::code
>(*comboWriter
->d_extendedErrorCode
);
1602 extra
= std::move(comboWriter
->d_extendedErrorExtra
);
1604 else if (resolver
.d_extendedError
) {
1605 code
= static_cast<EDNSExtendedError::code
>(resolver
.d_extendedError
->infoCode
);
1606 extra
= std::move(resolver
.d_extendedError
->extraText
);
1610 case vState::BogusNoValidDNSKEY
:
1611 code
= EDNSExtendedError::code::DNSKEYMissing
;
1613 case vState::BogusInvalidDenial
:
1614 code
= EDNSExtendedError::code::NSECMissing
;
1616 case vState::BogusUnableToGetDSs
:
1617 code
= EDNSExtendedError::code::DNSSECBogus
;
1619 case vState::BogusUnableToGetDNSKEYs
:
1620 code
= EDNSExtendedError::code::DNSKEYMissing
;
1622 case vState::BogusSelfSignedDS
:
1623 code
= EDNSExtendedError::code::DNSSECBogus
;
1625 case vState::BogusNoRRSIG
:
1626 code
= EDNSExtendedError::code::RRSIGsMissing
;
1628 case vState::BogusNoValidRRSIG
:
1629 code
= EDNSExtendedError::code::DNSSECBogus
;
1631 case vState::BogusMissingNegativeIndication
:
1632 code
= EDNSExtendedError::code::NSECMissing
;
1634 case vState::BogusSignatureNotYetValid
:
1635 code
= EDNSExtendedError::code::SignatureNotYetValid
;
1637 case vState::BogusSignatureExpired
:
1638 code
= EDNSExtendedError::code::SignatureExpired
;
1640 case vState::BogusUnsupportedDNSKEYAlgo
:
1641 code
= EDNSExtendedError::code::UnsupportedDNSKEYAlgorithm
;
1643 case vState::BogusUnsupportedDSDigestType
:
1644 code
= EDNSExtendedError::code::UnsupportedDSDigestType
;
1646 case vState::BogusNoZoneKeyBitSet
:
1647 code
= EDNSExtendedError::code::NoZoneKeyBitSet
;
1649 case vState::BogusRevokedDNSKEY
:
1650 case vState::BogusInvalidDNSKEYProtocol
:
1651 code
= EDNSExtendedError::code::DNSSECBogus
;
1654 throw std::runtime_error("Bogus validation state not handled: " + vStateToString(state
));
1658 EDNSExtendedError eee
;
1659 eee
.infoCode
= static_cast<uint16_t>(code
);
1660 eee
.extraText
= std::move(extra
);
1662 if (packetWriter
.size() < maxanswersize
&& (maxanswersize
- packetWriter
.size()) >= (EDNSOptionCodeSize
+ EDNSOptionLengthSize
+ sizeof(eee
.infoCode
) + eee
.extraText
.size())) {
1663 returnedEdnsOptions
.emplace_back(EDNSOptionCode::EXTENDEDERROR
, makeEDNSExtendedErrorOptString(eee
));
1667 /* we try to add the EDNS OPT RR even for truncated answers,
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."
1673 packetWriter
.addOpt(512, ednsExtRCode
, DNSSECOK
? EDNSOpts::DNSSECOK
: 0, returnedEdnsOptions
);
1674 packetWriter
.commit();
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
);
1682 if (nodCheckNewDomain(nodlogger
, comboWriter
->d_mdp
.d_qname
)) {
1685 if (isEnabledForNODs(t_nodFrameStreamServersInfo
.servers
)) {
1686 struct timespec timeSpec
1690 if (g_useKernelTimestamp
&& comboWriter
->d_kernelTimestamp
.tv_sec
!= 0) {
1691 TIMEVAL_TO_TIMESPEC(&comboWriter
->d_kernelTimestamp
, &timeSpec
); // NOLINT
1694 TIMEVAL_TO_TIMESPEC(&comboWriter
->d_now
, &timeSpec
); // NOLINT
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();
1699 for (auto& logger
: *(t_nodFrameStreamServersInfo
.servers
)) {
1700 if (logger
->logNODs()) {
1701 remoteLoggerQueueData(*logger
, str
);
1705 #endif // HAVE_FSTRM
1708 #endif /* NOD_ENABLED */
1710 if (variableAnswer
|| resolver
.wasVariable()) {
1711 t_Counters
.at(rec::Counter::variableResponses
)++;
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
);
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
);
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()});
1735 // if (g_udrEnabled) ??
1736 pbMessage
.clearUDR(pbDataForCache
->d_response
);
1740 const bool intoPC
= g_packetCache
&& !variableAnswer
&& !resolver
.wasVariable();
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)
1748 dnsQuestion
.validationState
,
1749 std::move(pbDataForCache
), comboWriter
->d_tcp
);
1752 if (g_regressionTestMode
) {
1753 t_Counters
.updateSnap(g_regressionTestMode
);
1756 if (!comboWriter
->d_tcp
) {
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;
1767 if (g_fromtosockets
.count(comboWriter
->d_socket
) > 0) {
1768 addCMsgSrcAddr(&msgh
, &cbuf
, &comboWriter
->d_local
, 0);
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"));
1778 bool hadError
= sendResponseOverTCP(comboWriter
, packet
);
1779 finishTCPReply(comboWriter
, hadError
, true);
1780 tcpGuard
.setHandled();
1783 resolver
.d_eventTrace
.add(RecEventTrace::AnswerSent
);
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
);
1792 pbMessage
.setQueryTime(comboWriter
->d_now
.tv_sec
, comboWriter
->d_now
.tv_usec
);
1794 pbMessage
.setMessageIdentity(comboWriter
->d_uuid
);
1795 pbMessage
.setSocketProtocol(comboWriter
->d_tcp
? pdns::ProtoZero::Message::TransportProtocol::TCP
: pdns::ProtoZero::Message::TransportProtocol::UDP
);
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());
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());
1812 pbMessage
.setTo(comboWriter
->d_destination
);
1813 pbMessage
.setId(comboWriter
->d_mdp
.d_header
.id
);
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
);
1823 for (const auto& metaValue
: dnsQuestion
.meta
) {
1824 pbMessage
.setMeta(metaValue
.first
, metaValue
.second
.stringVal
, metaValue
.second
.intVal
);
1829 pbMessage
.setNewlyObservedDomain(true);
1830 pbMessage
.addPolicyTag(g_nod_pbtag
);
1833 pbMessage
.addPolicyTag(g_udr_pbtag
);
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
);
1840 if (comboWriter
->d_logResponse
) {
1841 protobufLogResponse(pbMessage
);
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?
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
);
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
;
1858 if (!shouldNotValidate
&& resolver
.isDNSSECValidationRequested()) {
1859 g_log
<< ", dnssec=" << resolver
.getValidationState();
1861 g_log
<< " answer-is-variable=" << resolver
.wasVariable() << ", into-packetcache=" << intoPC
;
1862 g_log
<< " maxdepth=" << resolver
.d_maxdepth
;
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
));
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();
1889 g_recCache
->incCacheHits();
1893 t_Counters
.at(rec::Histogram::answers
)(spentUsec
);
1894 t_Counters
.at(rec::Histogram::cumulativeAnswers
)(spentUsec
);
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
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
);
1910 sendNODLookup(nodlogger
, comboWriter
->d_mdp
.d_qname
);
1912 #endif /* NOD_ENABLED */
1914 // cout<<dc->d_mdp.d_qname<<"\t"<<MT->getUsec()<<"\t"<<sr.d_outqueries<<endl;
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")));
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"));
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")));
1928 // Luawrapper nests the exception from Lua, so we unnest it here
1930 std::rethrow_if_nested(e
);
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")));
1938 if (!g_slogStructured
) {
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"));
1947 runTaskOnce(g_logCommonErrors
);
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())));
1962 t_Counters
.at(rec::Counter::maxMThreadStackUsage
) = max(g_multiTasker
->getMaxStackUsage(), t_Counters
.at(rec::Counter::maxMThreadStackUsage
));
1963 t_Counters
.updateSnap(g_regressionTestMode
);
1966 void getQNameAndSubnet(const std::string
& question
, DNSName
* dnsname
, uint16_t* qtype
, uint16_t* qclass
,
1967 bool& foundECS
, EDNSSubnetOpts
* ednssubnet
, EDNSOptionViewMap
* options
)
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
);
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
);
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. */
1987 const auto* drh
= reinterpret_cast<const dnsrecordheader
*>(&question
.at(pos
)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
1988 pos
+= sizeof(dnsrecordheader
);
1990 if (pos
>= questionLen
) {
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;
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) {
2003 if (getEDNSSubnetOptsFromString(&question
.at(pos
- sizeof(drh
->d_clen
) + ecsStartPosition
+ 4), ecsLen
- 4, &eso
)) {
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)
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) {
2016 if (getEDNSSubnetOptsFromString(iter
->second
.values
.at(0).content
, iter
->second
.values
.at(0).size
, &eso
)) {
2025 pos
+= ntohs(drh
->d_clen
);
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
)
2035 if (!g_packetCache
) {
2038 bool cacheHit
= false;
2040 vState valState
= vState::Indeterminate
;
2043 cacheHit
= g_packetCache
->getResponsePacket(tag
, data
, qname
, qtype
, qclass
, now
.tv_sec
, &response
, &age
, &valState
, &qhash
, &pbData
, tcp
);
2046 cacheHit
= g_packetCache
->getResponsePacket(tag
, data
, qname
, &qtype
, &qclass
, now
.tv_sec
, &response
, &age
, &valState
, &qhash
, &pbData
, tcp
);
2050 if (vStateIsBogus(valState
)) {
2051 if (t_bogusremotes
) {
2052 t_bogusremotes
->push_back(source
);
2054 if (t_bogusqueryring
) {
2055 t_bogusqueryring
->push_back({qname
, qtype
});
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
;
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
);
2081 t_Counters
.at(rec::DoubleWAvgCounter::avgLatencyUsec
).addToRollingAvg(0.0, g_latencyStatSize
);
2082 t_Counters
.at(rec::DoubleWAvgCounter::avgLatencyOursUsec
).addToRollingAvg(0.0, g_latencyStatSize
);
2084 // XXX changes behaviour compared to old code!
2085 t_Counters
.at(rec::Counter::answers
)(0);
2086 t_Counters
.at(rec::Counter::ourtime
)(0);
2093 static void* pleaseWipeCaches(const DNSName
& canon
, bool subtree
, uint16_t qtype
)
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
)));
2101 void requestWipeCaches(const DNSName
& canon
)
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
2111 unixDie("write to thread pipe returned wrong size or error");
2113 // coverity[leaked_storage]
2116 bool expectProxyProtocol(const ComboAddress
& from
, const ComboAddress
& listenAddress
)
2118 return g_proxyProtocolACL
.match(from
) && g_proxyProtocolExceptions
.count(listenAddress
) == 0;
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
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);
2134 if (delta
> 1000.0) {
2135 t_Counters
.at(rec::Counter::tooOldDrops
)++;
2140 ++t_Counters
.at(rec::Counter::qcounter
);
2142 if (fromaddr
.sin4
.sin_family
== AF_INET6
) {
2143 t_Counters
.at(rec::Counter::ipv6qcounter
)++;
2147 const dnsheader_aligned
headerdata(question
.data());
2148 const dnsheader
* dnsheader
= headerdata
.get();
2149 unsigned int ctag
= 0;
2151 bool needECS
= false;
2152 std::unordered_set
<std::string
> policyTags
;
2153 std::map
<std::string
, RecursorLua4::MetaValue
> meta
;
2154 LuaContext::LuaObject data
;
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
) {
2169 uniqueId
= getUniqueID();
2171 logQuery
= t_protobufServers
.servers
&& luaconfsLocal
->protobufExportConfig
.logQueries
;
2172 logResponse
= t_protobufServers
.servers
&& luaconfsLocal
->protobufExportConfig
.logResponses
;
2174 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->frameStreamExportConfig
, t_frameStreamServersInfo
);
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;
2190 uint16_t qclass
= 0;
2191 bool qnameParsed
= false;
2194 static uint64_t last=0;
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();
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
)) {
2207 EDNSOptionViewMap ednsOptions
;
2211 getQNameAndSubnet(question
, &qname
, &qtype
, &qclass
,
2212 ecsFound
, &ednssubnet
, g_gettagNeedsEDNSOptions
? &ednsOptions
: nullptr);
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
);
2222 eventTrace
.add(RecEventTrace::LuaGetTagFFI
);
2223 ctag
= t_pdl
->gettag_ffi(params
);
2224 eventTrace
.add(RecEventTrace::LuaGetTagFFI
, ctag
, false);
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);
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")));
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")));
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
);
2255 if (ctag
== 0 && !responsePaddingDisabled
&& g_paddingFrom
.match(fromaddr
)) {
2256 ctag
= g_paddingTag
;
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);
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
)));
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;
2283 if (g_fromtosockets
.count(fileDesc
) != 0) {
2284 addCMsgSrcAddr(&msgh
, &cbuf
, &destaddr
, 0);
2286 int sendErr
= sendOnNBSocket(fileDesc
, &msgh
);
2287 eventTrace
.add(RecEventTrace::AnswerSent
);
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
);
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?
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
)));
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
);
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")));
2323 bool ipf
= t_pdl
->ipfilter(source
, destination
, *dnsheader
, eventTrace
);
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
)));
2329 t_Counters
.at(rec::Counter::policyDrops
)++;
2334 if (dnsheader
->opcode
== static_cast<unsigned>(Opcode::Notify
)) {
2335 if (!isAllowNotifyForZone(qname
)) {
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
)));
2341 t_Counters
.at(rec::Counter::zoneDisallowedNotify
)++;
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
)));
2349 if (!notifyRPZTracker(qname
)) {
2351 requestWipeCaches(qname
);
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
2361 if (g_multiTasker
->numProcesses() > g_maxMThreads
) {
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
)));
2366 t_Counters
.at(rec::Counter::overCapacityDrops
)++;
2370 auto comboWriter
= std::make_unique
<DNSComboWriter
>(question
, g_now
, std::move(policyTags
), t_pdl
, std::move(data
), std::move(records
));
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
;
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
);
2403 comboWriter
->d_eventTrace
= std::move(eventTrace
);
2404 g_multiTasker
->makeThread(startDoResolve
, (void*)comboWriter
.release()); // deletes dc
2409 static void handleNewUDPQuestion(int fileDesc
, FDMultiplexer::funcparam_t
& /* var */) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
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
2422 cmsgbuf_aligned cbuf
;
2423 bool firstQuery
= true;
2424 std::vector
<ProxyProtocolValue
> proxyProtocolValues
;
2425 RecEventTrace eventTrace
;
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
);
2434 if (ssize_t len
= recvmsg(fileDesc
, &msgh
, 0); len
>= 0) {
2436 eventTrace
.setEnabled(SyncRes::s_event_trace_enabled
!= 0);
2437 eventTrace
.add(RecEventTrace::ReqRecv
);
2441 if ((msgh
.msg_flags
& MSG_TRUNC
) != 0) {
2442 t_Counters
.at(rec::Counter::truncatedDrops
)++;
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
)));
2450 data
.resize(static_cast<size_t>(len
));
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
;
2462 if (loc
!= nullptr) {
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)
2471 if (expectProxyProtocol(fromaddr
, destaddr
)) {
2473 ssize_t used
= parseProxyHeader(data
, proxyProto
, source
, destination
, tcp
, proxyProtocolValues
);
2475 ++t_Counters
.at(rec::Counter::proxyProtocolInvalidCount
);
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
)));
2483 if (static_cast<size_t>(used
) > g_proxyProtocolMaximumSize
) {
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
)));
2489 ++t_Counters
.at(rec::Counter::proxyProtocolInvalidCount
);
2493 data
.erase(0, used
);
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
)++;
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
)));
2505 if (data
.size() < sizeof(dnsheader
)) {
2506 t_Counters
.at(rec::Counter::ignoredCount
)++;
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
)));
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
;
2526 t_remotes
->push_back(fromaddr
);
2529 if (t_allowFrom
&& !t_allowFrom
->match(&mappedSource
)) {
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
)));
2535 t_Counters
.at(rec::Counter::unauthorizedUDP
)++;
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
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
)));
2546 t_Counters
.at(rec::Counter::clientParseError
)++; // not quite the best place to put it, but needs to go somewhere
2551 const dnsheader_aligned
headerdata(data
.data());
2552 const dnsheader
* dnsheader
= headerdata
.get();
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
)));
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
))));
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
)));
2576 if (dnsheader
->opcode
== static_cast<unsigned>(Opcode::Notify
)) {
2577 if (!t_allowNotifyFrom
|| !t_allowNotifyFrom
->match(&mappedSource
)) {
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
)));
2584 t_Counters
.at(rec::Counter::sourceDisallowedNotify
)++;
2589 struct timeval tval
= {0, 0};
2590 HarvestTimestamp(&msgh
, &tval
);
2592 destination
= destaddr
;
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
);
2602 doProcessUDPQuestion(data
, fromaddr
, destaddr
, source
, destination
, mappedSource
, tval
, fileDesc
, proxyProtocolValues
, eventTrace
);
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")));
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")));
2622 // cerr<<t_id<<" had error: "<<stringerror()<<endl;
2623 if (firstQuery
&& errno
== EAGAIN
) {
2624 t_Counters
.at(rec::Counter::noPacketError
)++;
2630 t_Counters
.updateSnap(g_regressionTestMode
);
2633 void makeUDPServerSockets(deferredAdd_t
& deferredAdds
, Logr::log_t log
)
2636 vector
<string
> localAddresses
;
2637 stringtok(localAddresses
, ::arg()["local-address"], " ,");
2639 if (localAddresses
.empty()) {
2640 throw PDNSException("No local address specified");
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);
2648 throw PDNSException("Making a UDP server socket for resolver: " + stringerror());
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"));
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
);
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
);
2667 if (address
.sin6
.sin6_family
== AF_INET6
&& setsockopt(socketFd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &one
, sizeof(one
)) < 0) {
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"));
2673 if (::arg().mustDo("non-local-bind")) {
2674 Utility::setBindAny(AF_INET6
, socketFd
);
2677 setCloseOnExec(socketFd
);
2680 setSocketReceiveBuffer(socketFd
, 250000);
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"));
2688 #if defined(SO_REUSEPORT_LB)
2690 SSetsockopt(socketFd
, SOL_SOCKET
, SO_REUSEPORT_LB
, 1);
2692 catch (const std::exception
& e
) {
2693 throw PDNSException(std::string("SO_REUSEPORT_LB: ") + e
.what());
2695 #elif defined(SO_REUSEPORT)
2697 SSetsockopt(socketFd
, SOL_SOCKET
, SO_REUSEPORT
, 1);
2699 catch (const std::exception
& e
) {
2700 throw PDNSException(std::string("SO_REUSEPORT: ") + e
.what());
2706 setSocketIgnorePMTU(socketFd
, address
.sin4
.sin_family
);
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"));
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());
2718 setNonBlocking(socketFd
);
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
)));
2727 static bool trySendingQueryToWorker(unsigned int target
, ThreadMSG
* tmsg
)
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"));
2736 const auto& tps
= targetInfo
.getPipes();
2738 ssize_t written
= write(tps
.writeQueriesToThread
, &tmsg
, sizeof(tmsg
)); // NOLINT: correct sizeof
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");
2747 if (error
== EAGAIN
|| error
== EWOULDBLOCK
) {
2750 delete tmsg
; // NOLINT: pointer ownership
2751 unixDie("write to thread pipe returned wrong size or error:" + std::to_string(error
));
2757 static unsigned int getWorkerLoad(size_t workerIdx
)
2759 const auto* multiThreader
= RecThreadInfo::info(RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + workerIdx
).getMT();
2760 if (multiThreader
!= nullptr) {
2761 return multiThreader
->numProcesses();
2766 static unsigned int selectWorker(unsigned int hash
)
2768 assert(RecThreadInfo::numUDPWorkers() != 0); // NOLINT: assert implementation
2769 if (g_balancingFactor
== 0) {
2770 return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + (hash
% RecThreadInfo::numUDPWorkers());
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
];
2781 double targetLoad
= (currentLoad
/ RecThreadInfo::numUDPWorkers()) * g_balancingFactor
;
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
);
2788 worker
= (worker
+ 1) % RecThreadInfo::numUDPWorkers();
2789 } while (load
[worker
] > targetLoad
);
2792 return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + worker
;
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
)
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
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)
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");
2811 unsigned int target
= selectWorker(hash
);
2813 ThreadMSG
* tmsg
= new ThreadMSG(); // NOLINT: pointer ownership
2815 tmsg
->wantAnswer
= false;
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;
2822 newTarget
= RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + dns_random(RecThreadInfo::numUDPWorkers());
2823 } while (newTarget
== target
);
2825 if (!trySendingQueryToWorker(newTarget
, tmsg
)) {
2826 t_Counters
.at(rec::Counter::queryPipeFullDrops
)++;
2827 delete tmsg
; // NOLINT: pointer ownership
2830 // coverity[leaked_storage]
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
)
2836 // We close the chain for new entries, since they won't be processed anyway
2837 iter
->key
->closed
= true;
2839 if (iter
->key
->chain
.empty()) {
2842 for (auto i
= iter
->key
->chain
.begin(); i
!= iter
->key
->chain
.end(); ++i
) {
2843 auto packetID
= std::make_shared
<PacketID
>(*resend
);
2846 g_multiTasker
->sendEvent(packetID
, &content
);
2847 t_Counters
.at(rec::Counter::chainResends
)++;
2851 static void handleUDPServerResponse(int fileDesc
, FDMultiplexer::funcparam_t
& var
)
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
);
2859 ssize_t len
= recvfrom(fileDesc
, &packet
.at(0), packet
.size(), 0, reinterpret_cast<sockaddr
*>(&fromaddr
), &addrlen
); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2861 const ssize_t signed_sizeof_sdnsheader
= sizeof(dnsheader
);
2864 // len < 0: error on socket
2865 t_udpclientsocks
->returnSocket(fileDesc
);
2868 auto iter
= g_multiTasker
->getWaiters().find(pid
);
2869 if (iter
!= g_multiTasker
->getWaiters().end()) {
2870 doResends(iter
, pid
, empty
);
2872 g_multiTasker
->sendEvent(pid
, &empty
); // this denotes error (does retry lookup using other NS)
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
)));
2887 // We have at least a full header
2889 dnsheader dnsheader
{};
2890 memcpy(&dnsheader
, &packet
.at(0), sizeof(dnsheader
));
2892 auto pident
= std::make_shared
<PacketID
>();
2893 pident
->remote
= fromaddr
;
2894 pident
->id
= dnsheader
.id
;
2895 pident
->fd
= fileDesc
;
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
)));
2902 if (dnsheader
.qdcount
== 0U || // UPC, Nominum, very old BIND on FormErr, NSD
2903 dnsheader
.qr
== 0U) { // one weird server
2904 pident
->domain
.clear();
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)
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();
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
)));
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
);
2937 if (pident
->domain
.empty() || g_multiTasker
->sendEvent(pident
, &packet
) == 0) {
2938 /* we did not find a match for this response, something is wrong */
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
2946 d_waiter
.key
->nearMisses
++;
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)
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())));
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
);