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
;
74 bool g_logCommonErrors
;
75 bool g_reusePort
{false};
76 bool g_gettagNeedsEDNSOptions
{false};
77 bool g_useKernelTimestamp
;
78 std::atomic
<uint32_t> g_maxCacheEntries
, g_maxPacketCacheEntries
;
79 #ifdef HAVE_BOOST_CONTAINER_FLAT_SET_HPP
80 boost::container::flat_set
<uint16_t> g_avoidUdpSourcePorts
;
82 std::set
<uint16_t> g_avoidUdpSourcePorts
;
84 uint16_t g_minUdpSourcePort
;
85 uint16_t g_maxUdpSourcePort
;
86 double g_balancingFactor
;
88 bool g_lowercaseOutgoing
;
89 unsigned int g_networkTimeoutMsec
;
90 uint16_t g_outgoingEDNSBufsize
;
92 // Used in Syncres to counts DNSSEC stats for names in a different "universe"
93 GlobalStateHolder
<SuffixMatchNode
> g_xdnssec
;
94 // Used in the Syncres to not throttle certain servers
95 GlobalStateHolder
<SuffixMatchNode
> g_dontThrottleNames
;
96 GlobalStateHolder
<NetmaskGroup
> g_dontThrottleNetmasks
;
97 GlobalStateHolder
<SuffixMatchNode
> g_DoTToAuthNames
;
98 uint64_t g_latencyStatSize
;
100 LWResult::Result
UDPClientSocks::getSocket(const ComboAddress
& toaddr
, int* fileDesc
)
102 *fileDesc
= makeClientSocket(toaddr
.sin4
.sin_family
);
103 if (*fileDesc
< 0) { // temporary error - receive exception otherwise
104 return LWResult::Result::OSLimitError
;
107 if (connect(*fileDesc
, reinterpret_cast<const struct sockaddr
*>(&toaddr
), toaddr
.getSocklen()) < 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast))
110 closesocket(*fileDesc
);
112 catch (const PDNSException
& e
) {
113 SLOG(g_log
<< Logger::Error
<< "Error closing UDP socket after connect() failed: " << e
.reason
<< endl
,
114 g_slogout
->error(Logr::Error
, e
.reason
, "Error closing UDP socket after connect() failed", "exception", Logging::Loggable("PDNSException")));
117 if (err
== ENETUNREACH
) { // Seth "My Interfaces Are Like A Yo Yo" Arnold special
118 return LWResult::Result::OSLimitError
;
121 return LWResult::Result::PermanentError
;
125 return LWResult::Result::Success
;
128 // return a socket to the pool, or simply erase it
129 void UDPClientSocks::returnSocket(int fileDesc
)
132 t_fdm
->removeReadFD(fileDesc
);
134 catch (const FDMultiplexerException
& e
) {
135 // we sometimes return a socket that has not yet been assigned to t_fdm
139 closesocket(fileDesc
);
141 catch (const PDNSException
& e
) {
142 SLOG(g_log
<< Logger::Error
<< "Error closing returned UDP socket: " << e
.reason
<< endl
,
143 g_slogout
->error(Logr::Error
, e
.reason
, "Error closing returned UDP socket", "exception", Logging::Loggable("PDNSException")));
149 // returns -1 for errors which might go away, throws for ones that won't
150 int UDPClientSocks::makeClientSocket(int family
)
152 int ret
= socket(family
, SOCK_DGRAM
, 0); // turns out that setting CLO_EXEC and NONBLOCK from here is not a performance win on Linux (oddly enough)
154 if (ret
< 0 && errno
== EMFILE
) { // this is not a catastrophic error
159 throw PDNSException("Making a socket for resolver (family = " + std::to_string(family
) + "): " + stringerror(err
));
162 // The loop below runs the body with [tries-1 tries-2 ... 1]. Last iteration with tries == 1 is special: it uses a kernel
163 // allocated UDP port.
164 #if !defined(__OpenBSD__)
167 int tries
= 2; // hit the reliable kernel random case for OpenBSD immediately (because it will match tries==1 below), using sysctl net.inet.udp.baddynamic to exclude ports
170 while (--tries
!= 0) {
173 if (tries
== 1) { // last iteration: fall back to kernel 'random'
178 port
= g_minUdpSourcePort
+ dns_random(g_maxUdpSourcePort
- g_minUdpSourcePort
+ 1);
179 } while (g_avoidUdpSourcePorts
.count(port
) != 0);
182 sin
= pdns::getQueryLocalAddress(family
, port
); // does htons for us
183 if (::bind(ret
, reinterpret_cast<struct sockaddr
*>(&sin
), sin
.getSocklen()) >= 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
192 throw PDNSException("Resolver binding to local query client socket on " + sin
.toString() + ": " + stringerror(err
));
196 setReceiveSocketErrors(ret
, family
);
206 static void handleGenUDPQueryResponse(int fileDesc
, FDMultiplexer::funcparam_t
& var
)
208 auto pident
= boost::any_cast
<std::shared_ptr
<PacketID
>>(var
);
211 ComboAddress fromaddr
;
212 socklen_t addrlen
= sizeof(fromaddr
);
214 ssize_t ret
= recvfrom(fileDesc
, resp
.data(), resp
.size(), 0, reinterpret_cast<sockaddr
*>(&fromaddr
), &addrlen
); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
215 if (fromaddr
!= pident
->remote
) {
216 SLOG(g_log
<< Logger::Notice
<< "Response received from the wrong remote host (" << fromaddr
.toStringWithPort() << " instead of " << pident
->remote
.toStringWithPort() << "), discarding" << endl
,
217 g_slog
->withName("lua")->info(Logr::Notice
, "Response received from the wrong remote host. discarding", "method", Logging::Loggable("GenUDPQueryResponse"), "fromaddr", Logging::Loggable(fromaddr
), "expected", Logging::Loggable(pident
->remote
)));
220 t_fdm
->removeReadFD(fileDesc
);
223 g_multiTasker
->sendEvent(pident
, &resp
);
227 g_multiTasker
->sendEvent(pident
, &empty
);
228 // cerr<<"Had some kind of error: "<<ret<<", "<<stringerror()<<endl;
232 PacketBuffer
GenUDPQueryResponse(const ComboAddress
& dest
, const string
& query
)
234 Socket
socket(dest
.sin4
.sin_family
, SOCK_DGRAM
);
235 socket
.setNonBlocking();
236 ComboAddress local
= pdns::getQueryLocalAddress(dest
.sin4
.sin_family
, 0);
239 socket
.connect(dest
);
242 std::shared_ptr
<PacketID
> pident
= std::make_shared
<PacketID
>();
243 pident
->fd
= socket
.getHandle();
244 pident
->remote
= dest
;
246 t_fdm
->addReadFD(socket
.getHandle(), handleGenUDPQueryResponse
, pident
);
249 int ret
= g_multiTasker
->waitEvent(pident
, &data
, g_networkTimeoutMsec
);
251 if (ret
== 0 || ret
== -1) { // timeout
252 t_fdm
->removeReadFD(socket
.getHandle());
254 else if (data
.empty()) { // error, EOF or other
255 // we could special case this
261 static void handleUDPServerResponse(int fileDesc
, FDMultiplexer::funcparam_t
& var
);
263 thread_local
std::unique_ptr
<UDPClientSocks
> t_udpclientsocks
;
265 /* these two functions are used by LWRes */
266 LWResult::Result
asendto(const char* data
, size_t len
, int /* flags */,
267 const ComboAddress
& toAddress
, uint16_t qid
, const DNSName
& domain
, uint16_t qtype
, bool ecs
, int* fileDesc
)
270 auto pident
= std::make_shared
<PacketID
>();
271 pident
->domain
= domain
;
272 pident
->remote
= toAddress
;
273 pident
->type
= qtype
;
275 // We cannot merge ECS-enabled queries based on the ECS source only, as the scope
276 // of the response might be narrower, so instead we do not chain ECS-enabled queries
279 // See if there is an existing outstanding request we can chain on to, using partial equivalence
280 // function looking for the same query (qname and qtype) to the same host, but with a different
282 auto chain
= g_multiTasker
->d_waiters
.equal_range(pident
, PacketIDBirthdayCompare());
284 for (; chain
.first
!= chain
.second
; chain
.first
++) {
285 // Line below detected an issue with the two ways of ordering PacketIDs (birthday and non-birthday)
286 assert(chain
.first
->key
->domain
== pident
->domain
); // NOLINT
287 // don't chain onto existing chained waiter or a chain already processed
288 if (chain
.first
->key
->fd
> -1 && !chain
.first
->key
->closed
) {
289 chain
.first
->key
->chain
.insert(qid
); // we can chain
290 *fileDesc
= -1; // gets used in waitEvent / sendEvent later on
291 return LWResult::Result::Success
;
296 auto ret
= t_udpclientsocks
->getSocket(toAddress
, fileDesc
);
297 if (ret
!= LWResult::Result::Success
) {
301 pident
->fd
= *fileDesc
;
304 t_fdm
->addReadFD(*fileDesc
, handleUDPServerResponse
, pident
);
305 ssize_t sent
= send(*fileDesc
, data
, len
, 0);
310 t_udpclientsocks
->returnSocket(*fileDesc
);
311 errno
= tmp
; // this is for logging purposes only
312 return LWResult::Result::PermanentError
;
315 return LWResult::Result::Success
;
318 LWResult::Result
arecvfrom(PacketBuffer
& packet
, int /* flags */, const ComboAddress
& fromAddr
, size_t& len
,
319 uint16_t qid
, const DNSName
& domain
, uint16_t qtype
, int fileDesc
, const struct timeval
& now
)
321 static const unsigned int nearMissLimit
= ::arg().asNum("spoof-nearmiss-max");
323 auto pident
= std::make_shared
<PacketID
>();
324 pident
->fd
= fileDesc
;
326 pident
->domain
= domain
;
327 pident
->type
= qtype
;
328 pident
->remote
= fromAddr
;
330 int ret
= g_multiTasker
->waitEvent(pident
, &packet
, g_networkTimeoutMsec
, &now
);
333 /* -1 means error, 0 means timeout, 1 means a result from handleUDPServerResponse() which might still be an error */
335 /* handleUDPServerResponse() will close the socket for us no matter what */
336 if (packet
.empty()) { // means "error"
337 return LWResult::Result::PermanentError
;
342 if (nearMissLimit
> 0 && pident
->nearMisses
> nearMissLimit
) {
343 /* we have received more than nearMissLimit answers on the right IP and port, from the right source (we are using connected sockets),
344 for the correct qname and qtype, but with an unexpected message ID. That looks like a spoofing attempt. */
345 SLOG(g_log
<< Logger::Error
<< "Too many (" << pident
->nearMisses
<< " > " << nearMissLimit
<< ") answers with a wrong message ID for '" << domain
<< "' from " << fromAddr
.toString() << ", assuming spoof attempt." << endl
,
346 g_slogudpin
->info(Logr::Error
, "Too many answers with a wrong message ID, assuming spoofing attempt",
347 "nearmisses", Logging::Loggable(pident
->nearMisses
),
348 "nearmisslimit", Logging::Loggable(nearMissLimit
),
349 "qname", Logging::Loggable(domain
),
350 "from", Logging::Loggable(fromAddr
)));
351 t_Counters
.at(rec::Counter::spoofCount
)++;
352 return LWResult::Result::Spoofed
;
355 return LWResult::Result::Success
;
357 /* getting there means error or timeout, it's up to us to close the socket */
359 t_udpclientsocks
->returnSocket(fileDesc
);
362 return ret
== 0 ? LWResult::Result::Timeout
: LWResult::Result::PermanentError
;
365 // the idea is, only do things that depend on the *response* here. Incoming accounting is on incoming.
366 static void updateResponseStats(int res
, const ComboAddress
& remote
, unsigned int packetsize
, const DNSName
* query
, uint16_t qtype
)
368 if (packetsize
> 1000 && t_largeanswerremotes
) {
369 t_largeanswerremotes
->push_back(remote
);
372 case RCode::ServFail
:
373 if (t_servfailremotes
) {
374 t_servfailremotes
->push_back(remote
);
375 if (query
!= nullptr && t_servfailqueryring
) { // packet cache
376 t_servfailqueryring
->push_back({*query
, qtype
});
379 ++t_Counters
.at(rec::Counter::servFails
);
381 case RCode::NXDomain
:
382 ++t_Counters
.at(rec::Counter::nxDomains
);
385 t_Counters
.at(rec::Counter::noErrors
)++;
390 static string
makeLoginfo(const std::unique_ptr
<DNSComboWriter
>& comboWriter
)
392 return "(" + comboWriter
->d_mdp
.d_qname
.toLogString() + "/" + DNSRecordContent::NumberToType(comboWriter
->d_mdp
.d_qtype
) + " from " + (comboWriter
->getRemote()) + ")";
395 return "Exception making error message for exception";
399 * Chases the CNAME provided by the PolicyCustom RPZ policy.
401 * @param spoofed: The DNSRecord that was created by the policy, should already be added to ret
402 * @param qtype: The QType of the original query
403 * @param sr: A SyncRes
404 * @param res: An integer that will contain the RCODE of the lookup we do
405 * @param ret: A vector of DNSRecords where the result of the CNAME chase should be appended to
407 static void handleRPZCustom(const DNSRecord
& spoofed
, const QType
& qtype
, SyncRes
& resolver
, int& res
, vector
<DNSRecord
>& ret
)
409 if (spoofed
.d_type
== QType::CNAME
) {
410 bool oldWantsRPZ
= resolver
.getWantsRPZ();
411 resolver
.setWantsRPZ(false);
412 vector
<DNSRecord
> ans
;
413 res
= resolver
.beginResolve(DNSName(spoofed
.getContent()->getZoneRepresentation()), qtype
, QClass::IN
, ans
);
414 for (const auto& rec
: ans
) {
415 if (rec
.d_place
== DNSResourceRecord::ANSWER
) {
419 // Reset the RPZ state of the SyncRes
420 resolver
.setWantsRPZ(oldWantsRPZ
);
424 static bool addRecordToPacket(DNSPacketWriter
& packetWritewr
, const DNSRecord
& rec
, uint32_t& minTTL
, uint32_t ttlCap
, const uint16_t maxAnswerSize
, bool& seenAuthSOA
)
426 packetWritewr
.startRecord(rec
.d_name
, rec
.d_type
, (rec
.d_ttl
> ttlCap
? ttlCap
: rec
.d_ttl
), rec
.d_class
, rec
.d_place
);
428 if (rec
.d_type
== QType::SOA
&& rec
.d_place
== DNSResourceRecord::AUTHORITY
) {
432 if (rec
.d_type
!= QType::OPT
) { // their TTL ain't real
433 minTTL
= min(minTTL
, rec
.d_ttl
);
436 rec
.getContent()->toPacket(packetWritewr
);
437 if (packetWritewr
.size() > static_cast<size_t>(maxAnswerSize
)) {
438 packetWritewr
.rollback();
439 if (rec
.d_place
!= DNSResourceRecord::ADDITIONAL
) {
440 packetWritewr
.getHeader()->tc
= 1;
441 packetWritewr
.truncate();
450 * A helper class that handles the TCP in-flight bookkeeping on
451 * destruct. This class ise used by startDoResolve() to not forget
452 * that. You can also signal that the TCP connection must be closed
453 * once the in-flight connections drop to zero.
455 class RunningResolveGuard
458 RunningResolveGuard(const RunningResolveGuard
&) = default;
459 RunningResolveGuard(RunningResolveGuard
&&) = delete;
460 RunningResolveGuard
& operator=(const RunningResolveGuard
&) = delete;
461 RunningResolveGuard
& operator=(RunningResolveGuard
&&) = delete;
462 RunningResolveGuard(std::unique_ptr
<DNSComboWriter
>& comboWriter
) :
465 if (d_dc
->d_tcp
&& !d_dc
->d_tcpConnection
) {
466 throw std::runtime_error("incoming TCP case without TCP connection");
469 ~RunningResolveGuard()
471 if (!d_handled
&& d_dc
->d_tcp
) {
473 finishTCPReply(d_dc
, false, true);
475 catch (const FDMultiplexerException
&) {
486 d_dc
->d_tcpConnection
->setDropOnIdle();
491 std::unique_ptr
<DNSComboWriter
>& d_dc
;
492 bool d_handled
{false};
495 enum class PolicyResult
: uint8_t
502 static PolicyResult
handlePolicyHit(const DNSFilterEngine::Policy
& appliedPolicy
, const std::unique_ptr
<DNSComboWriter
>& comboWriter
, SyncRes
& resolver
, int& res
, vector
<DNSRecord
>& ret
, DNSPacketWriter
& packetWriter
, RunningResolveGuard
& tcpGuard
)
504 /* don't account truncate actions for TCP queries, since they are not applied */
505 if (appliedPolicy
.d_kind
!= DNSFilterEngine::PolicyKind::Truncate
|| !comboWriter
->d_tcp
) {
506 ++t_Counters
.at(rec::PolicyHistogram::policy
).at(appliedPolicy
.d_kind
);
507 ++t_Counters
.at(rec::PolicyNameHits::policyName
).counts
[appliedPolicy
.getName()];
510 if (resolver
.doLog() && appliedPolicy
.d_type
!= DNSFilterEngine::PolicyType::None
) {
511 SLOG(g_log
<< Logger::Warning
<< comboWriter
->d_mdp
.d_qname
<< "|" << QType(comboWriter
->d_mdp
.d_qtype
) << appliedPolicy
.getLogString() << endl
,
512 appliedPolicy
.info(Logr::Warning
, resolver
.d_slog
));
515 if (appliedPolicy
.d_zoneData
&& appliedPolicy
.d_zoneData
->d_extendedErrorCode
) {
516 comboWriter
->d_extendedErrorCode
= *appliedPolicy
.d_zoneData
->d_extendedErrorCode
;
517 comboWriter
->d_extendedErrorExtra
= appliedPolicy
.d_zoneData
->d_extendedErrorExtra
;
520 switch (appliedPolicy
.d_kind
) {
522 case DNSFilterEngine::PolicyKind::NoAction
:
523 return PolicyResult::NoAction
;
525 case DNSFilterEngine::PolicyKind::Drop
:
526 tcpGuard
.setDropOnIdle();
527 ++t_Counters
.at(rec::Counter::policyDrops
);
528 return PolicyResult::Drop
;
530 case DNSFilterEngine::PolicyKind::NXDOMAIN
:
532 appliedPolicy
.addSOAtoRPZResult(ret
);
533 res
= RCode::NXDomain
;
534 return PolicyResult::HaveAnswer
;
536 case DNSFilterEngine::PolicyKind::NODATA
:
538 appliedPolicy
.addSOAtoRPZResult(ret
);
539 res
= RCode::NoError
;
540 return PolicyResult::HaveAnswer
;
542 case DNSFilterEngine::PolicyKind::Truncate
:
543 if (!comboWriter
->d_tcp
) {
545 appliedPolicy
.addSOAtoRPZResult(ret
);
546 res
= RCode::NoError
;
547 packetWriter
.getHeader()->tc
= 1;
548 return PolicyResult::HaveAnswer
;
550 return PolicyResult::NoAction
;
552 case DNSFilterEngine::PolicyKind::Custom
:
553 res
= RCode::NoError
;
555 auto spoofed
= appliedPolicy
.getCustomRecords(comboWriter
->d_mdp
.d_qname
, comboWriter
->d_mdp
.d_qtype
);
556 for (auto& record
: spoofed
) {
557 ret
.push_back(record
);
559 handleRPZCustom(record
, QType(comboWriter
->d_mdp
.d_qtype
), resolver
, res
, ret
);
561 catch (const ImmediateServFailException
& e
) {
562 if (g_logCommonErrors
) {
563 SLOG(g_log
<< Logger::Notice
<< "Sending SERVFAIL to " << comboWriter
->getRemote() << " during resolve of the custom filter policy '" << appliedPolicy
.getName() << "' while resolving '" << comboWriter
->d_mdp
.d_qname
<< "' because: " << e
.reason
<< endl
,
564 resolver
.d_slog
->error(Logr::Notice
, e
.reason
, "Sending SERVFAIL during resolve of the custom filter policy",
565 "policyName", Logging::Loggable(appliedPolicy
.getName()), "exception", Logging::Loggable("ImmediateServFailException")));
567 res
= RCode::ServFail
;
570 catch (const PolicyHitException
& e
) {
571 if (g_logCommonErrors
) {
572 SLOG(g_log
<< Logger::Notice
<< "Sending SERVFAIL to " << comboWriter
->getRemote() << " during resolve of the custom filter policy '" << appliedPolicy
.getName() << "' while resolving '" << comboWriter
->d_mdp
.d_qname
<< "' because another RPZ policy was hit" << endl
,
573 resolver
.d_slog
->info(Logr::Notice
, "Sending SERVFAIL during resolve of the custom filter policy because another RPZ policy was hit",
574 "policyName", Logging::Loggable(appliedPolicy
.getName()), "exception", Logging::Loggable("PolicyHitException")));
576 res
= RCode::ServFail
;
581 appliedPolicy
.addSOAtoRPZResult(ret
);
582 return PolicyResult::HaveAnswer
;
586 return PolicyResult::NoAction
;
590 static bool nodCheckNewDomain(Logr::log_t nodlogger
, const DNSName
& dname
)
593 // First check the (sub)domain isn't ignored for NOD purposes
594 if (!g_nodDomainWL
.check(dname
)) {
595 // Now check the NODDB (note this is probabilistic so can have FNs/FPs)
596 if (t_nodDBp
&& t_nodDBp
->isNewDomain(dname
)) {
598 // This should probably log to a dedicated log file
599 SLOG(g_log
<< Logger::Notice
<< "Newly observed domain nod=" << dname
<< endl
,
600 nodlogger
->info(Logr::Notice
, "New domain observed"));
602 t_Counters
.at(rec::Counter::nodCount
)++;
609 static void sendNODLookup(Logr::log_t nodlogger
, const DNSName
& dname
)
611 if (!(g_nodLookupDomain
.isRoot())) {
612 // Send a DNS A query to <domain>.g_nodLookupDomain
615 qname
= dname
+ g_nodLookupDomain
;
617 catch (const std::range_error
& e
) {
618 if (g_logCommonErrors
) {
619 nodlogger
->v(10)->error(Logr::Error
, "DNSName too long", "Unable to send NOD lookup");
621 ++t_Counters
.at(rec::Counter::nodLookupsDroppedOversize
);
624 nodlogger
->v(10)->info(Logr::Debug
, "Sending NOD lookup", "nodqname", Logging::Loggable(qname
));
625 vector
<DNSRecord
> dummy
;
626 directResolve(qname
, QType::A
, QClass::IN
, dummy
, nullptr, false, nodlogger
);
630 static bool udrCheckUniqueDNSRecord(Logr::log_t nodlogger
, const DNSName
& dname
, uint16_t qtype
, const DNSRecord
& record
)
633 if (record
.d_place
== DNSResourceRecord::ANSWER
|| record
.d_place
== DNSResourceRecord::ADDITIONAL
) {
634 // Create a string that represent a triplet of (qname, qtype and RR[type, name, content])
635 std::stringstream strStream
;
636 strStream
<< dname
.toDNSStringLC() << ":" << qtype
<< ":" << qtype
<< ":" << record
.d_type
<< ":" << record
.d_name
.toDNSStringLC() << ":" << record
.getContent()->getZoneRepresentation();
637 if (t_udrDBp
&& t_udrDBp
->isUniqueResponse(strStream
.str())) {
639 // This should also probably log to a dedicated file.
640 SLOG(g_log
<< Logger::Notice
<< "Unique response observed: qname=" << dname
<< " qtype=" << QType(qtype
) << " rrtype=" << QType(record
.d_type
) << " rrname=" << record
.d_name
<< " rrcontent=" << record
.getContent()->getZoneRepresentation() << endl
,
641 nodlogger
->info(Logr::Notice
, "New response observed",
642 "qtype", Logging::Loggable(QType(qtype
)),
643 "rrtype", Logging::Loggable(QType(record
.d_type
)),
644 "rrname", Logging::Loggable(record
.d_name
),
645 "rrcontent", Logging::Loggable(record
.getContent()->getZoneRepresentation())););
647 t_Counters
.at(rec::Counter::udrCount
)++;
653 #endif /* NOD_ENABLED */
655 static bool dns64Candidate(uint16_t requestedType
, int rcode
, const std::vector
<DNSRecord
>& records
);
657 int followCNAMERecords(vector
<DNSRecord
>& ret
, const QType qtype
, int rcode
)
659 vector
<DNSRecord
> resolved
;
661 for (const DNSRecord
& record
: ret
) {
662 if (record
.d_type
== QType::CNAME
) {
663 auto rec
= getRR
<CNAMERecordContent
>(record
);
665 target
= rec
->getTarget();
671 if (target
.empty()) {
675 auto log
= g_slog
->withName("lua")->withValues("method", Logging::Loggable("followCNAMERecords"));
676 rcode
= directResolve(target
, qtype
, QClass::IN
, resolved
, t_pdl
, log
);
678 if (g_dns64Prefix
&& qtype
== QType::AAAA
&& dns64Candidate(qtype
, rcode
, resolved
)) {
679 rcode
= getFakeAAAARecords(target
, *g_dns64Prefix
, resolved
);
682 for (DNSRecord
& record
: resolved
) {
683 if (record
.d_place
== DNSResourceRecord::ANSWER
) {
684 ret
.push_back(std::move(record
));
690 int getFakeAAAARecords(const DNSName
& qname
, ComboAddress prefix
, vector
<DNSRecord
>& ret
)
692 auto log
= g_slog
->withName("dns64")->withValues("method", Logging::Loggable("getAAAA"));
693 /* we pass a separate vector of records because we will be resolving the initial qname
694 again, possibly encountering the same CNAME(s), and we don't want to trigger the CNAME
696 vector
<DNSRecord
> newRecords
;
697 int rcode
= directResolve(qname
, QType::A
, QClass::IN
, newRecords
, t_pdl
, log
);
699 ret
.reserve(ret
.size() + newRecords
.size());
700 for (auto& record
: newRecords
) {
701 ret
.push_back(std::move(record
));
704 // Remove double CNAME records
705 std::set
<DNSName
> seenCNAMEs
;
706 ret
.erase(std::remove_if(
709 [&seenCNAMEs
](DNSRecord
& record
) {
710 if (record
.d_type
== QType::CNAME
) {
711 auto target
= getRR
<CNAMERecordContent
>(record
);
712 if (target
== nullptr) {
715 if (seenCNAMEs
.count(target
->getTarget()) > 0) {
716 // We've had this CNAME before, remove it
719 seenCNAMEs
.insert(target
->getTarget());
726 for (DNSRecord
& record
: ret
) {
727 if (record
.d_type
== QType::A
&& record
.d_place
== DNSResourceRecord::ANSWER
) {
728 if (auto rec
= getRR
<ARecordContent
>(record
)) {
729 ComboAddress
ipv4(rec
->getCA());
730 memcpy(&prefix
.sin6
.sin6_addr
.s6_addr
[12], &ipv4
.sin4
.sin_addr
.s_addr
, sizeof(ipv4
.sin4
.sin_addr
.s_addr
));
731 record
.setContent(std::make_shared
<AAAARecordContent
>(prefix
));
732 record
.d_type
= QType::AAAA
;
739 // We've seen an A in the ANSWER section, so there is no need to keep any
740 // SOA in the AUTHORITY section as this is not a NODATA response.
741 ret
.erase(std::remove_if(
744 [](DNSRecord
& record
) {
745 return (record
.d_type
== QType::SOA
&& record
.d_place
== DNSResourceRecord::AUTHORITY
);
749 t_Counters
.at(rec::Counter::dns64prefixanswers
)++;
753 int getFakePTRRecords(const DNSName
& qname
, vector
<DNSRecord
>& ret
)
755 /* qname has a reverse ordered IPv6 address, need to extract the underlying IPv4 address from it
756 and turn it into an IPv4 in-addr.arpa query */
758 vector
<string
> parts
= qname
.getRawLabels();
760 if (parts
.size() < 8) {
765 for (size_t octet
= 0; octet
< 4; ++octet
) {
766 newquery
+= std::to_string(stoll(parts
[octet
* 2], nullptr, 16) + 16 * stoll(parts
[octet
* 2 + 1], nullptr, 16));
767 newquery
.append(1, '.');
769 newquery
+= "in-addr.arpa.";
772 record
.d_name
= qname
;
773 record
.d_type
= QType::CNAME
;
774 record
.setContent(std::make_shared
<CNAMERecordContent
>(newquery
));
775 ret
.push_back(record
);
777 auto log
= g_slog
->withName("dns64")->withValues("method", Logging::Loggable("getPTR"));
778 int rcode
= directResolve(DNSName(newquery
), QType::PTR
, QClass::IN
, ret
, t_pdl
, log
);
780 t_Counters
.at(rec::Counter::dns64prefixanswers
)++;
784 static bool answerIsNOData(uint16_t requestedType
, int rcode
, const std::vector
<DNSRecord
>& records
)
786 if (rcode
!= RCode::NoError
) {
789 for (const auto& rec
: records
) {
790 if (rec
.d_place
!= DNSResourceRecord::ANSWER
) {
791 /* no records in the answer section */
794 if (rec
.d_type
== requestedType
) {
795 /* we have a record, of the right type, in the right section */
802 // RFC 6147 section 5.1 all rcodes except NXDomain should be candidate for dns64
803 // for NoError, check if it is NoData
804 static bool dns64Candidate(uint16_t requestedType
, int rcode
, const std::vector
<DNSRecord
>& records
)
806 if (rcode
== RCode::NoError
) {
807 return answerIsNOData(requestedType
, rcode
, records
);
809 return rcode
!= RCode::NXDomain
;
812 bool isAllowNotifyForZone(DNSName qname
)
814 if (t_allowNotifyFor
->empty()) {
818 notifyset_t::const_iterator ret
;
820 ret
= t_allowNotifyFor
->find(qname
);
821 if (ret
!= t_allowNotifyFor
->end()) {
824 } while (qname
.chopOff());
828 #if defined(HAVE_FSTRM) && defined(NOD_ENABLED)
830 #include "fstrm_logger.hh"
832 static bool isEnabledForNODs(const std::shared_ptr
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>>& fstreamLoggers
)
834 if (fstreamLoggers
== nullptr) {
837 for (auto& logger
: *fstreamLoggers
) {
838 if (logger
->logNODs()) {
844 static bool isEnabledForUDRs(const std::shared_ptr
<std::vector
<std::unique_ptr
<FrameStreamLogger
>>>& fstreamLoggers
)
846 if (fstreamLoggers
== nullptr) {
849 for (auto& logger
: *fstreamLoggers
) {
850 if (logger
->logUDRs()) {
858 static void dumpTrace(const string
& trace
, const timeval
& timev
)
864 Utility::gettimeofday(&now
);
865 int traceFd
= dup(t_tracefd
);
868 SLOG(g_log
<< Logger::Error
<< "Could not dup trace file: " << stringerror(err
) << endl
,
869 g_slog
->withName("trace")->error(Logr::Error
, err
, "Could not dup trace file"));
872 setNonBlocking(traceFd
);
873 auto filep
= std::unique_ptr
<FILE, decltype(&fclose
)>(fdopen(traceFd
, "a"), &fclose
);
876 SLOG(g_log
<< Logger::Error
<< "Could not write to trace file: " << stringerror(err
) << endl
,
877 g_slog
->withName("trace")->error(Logr::Error
, err
, "Could not write to trace file"));
881 std::array
<char, 64> timebuf
{};
882 isoDateTimeMillis(timev
, timebuf
.data(), timebuf
.size());
883 fprintf(filep
.get(), " us === START OF TRACE %s ===\n", timebuf
.data());
884 fprintf(filep
.get(), "%s", trace
.c_str());
885 isoDateTimeMillis(now
, timebuf
.data(), timebuf
.size());
886 fprintf(filep
.get(), "=== END OF TRACE %s ===\n", timebuf
.data());
887 if (ferror(filep
.get()) != 0) {
889 SLOG(g_log
<< Logger::Error
<< "Problems writing to trace file: " << stringerror(err
) << endl
,
890 g_slog
->withName("trace")->error(Logr::Error
, err
, "Problems writing to trace file"));
892 // fclose by unique_ptr does implicit flush
895 static uint32_t capPacketCacheTTL(const struct dnsheader
& hdr
, uint32_t ttl
, bool seenAuthSOA
)
897 if (hdr
.rcode
== RCode::NXDomain
|| (hdr
.rcode
== RCode::NoError
&& hdr
.ancount
== 0 && seenAuthSOA
)) {
898 ttl
= std::min(ttl
, SyncRes::s_packetcachenegativettl
);
900 else if ((hdr
.rcode
!= RCode::NoError
&& hdr
.rcode
!= RCode::NXDomain
) || (hdr
.ancount
== 0 && hdr
.nscount
== 0)) {
901 ttl
= min(ttl
, SyncRes::s_packetcacheservfailttl
);
904 ttl
= std::min(ttl
, SyncRes::s_packetcachettl
);
909 static void addPolicyTagsToPBMessageIfNeeded(DNSComboWriter
& comboWriter
, pdns::ProtoZero::RecMessage
& pbMessage
)
911 if (!comboWriter
.d_gettagPolicyTags
.empty()) {
912 for (const auto& tag
: comboWriter
.d_gettagPolicyTags
) {
913 comboWriter
.d_policyTags
.erase(tag
);
916 if (!comboWriter
.d_policyTags
.empty()) {
917 pbMessage
.addPolicyTags(comboWriter
.d_policyTags
);
921 void startDoResolve(void* arg
) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
923 auto comboWriter
= std::unique_ptr
<DNSComboWriter
>(static_cast<DNSComboWriter
*>(arg
));
924 SyncRes
resolver(comboWriter
->d_now
);
927 t_queryring
->push_back({comboWriter
->d_mdp
.d_qname
, comboWriter
->d_mdp
.d_qtype
});
930 uint16_t maxanswersize
= comboWriter
->d_tcp
? 65535 : min(static_cast<uint16_t>(512), g_udpTruncationThreshold
);
932 std::vector
<pair
<uint16_t, string
>> ednsOpts
;
933 bool variableAnswer
= comboWriter
->d_variable
;
934 bool haveEDNS
= false;
935 bool paddingAllowed
= false;
936 bool addPaddingToResponse
= false;
939 std::shared_ptr
<Logr::Logger
> nodlogger
{nullptr};
940 if (g_udrEnabled
|| g_nodEnabled
) {
941 nodlogger
= g_slog
->withName("nod")->v(1)->withValues("qname", Logging::Loggable(comboWriter
->d_mdp
.d_qname
));
943 #endif /* NOD_ENABLED */
944 DNSPacketWriter::optvect_t returnedEdnsOptions
; // Here we stuff all the options for the return packet
945 uint8_t ednsExtRCode
= 0;
946 if (getEDNSOpts(comboWriter
->d_mdp
, &edo
)) {
948 if (edo
.d_version
!= 0) {
949 ednsExtRCode
= ERCode::BADVERS
;
952 if (!comboWriter
->d_tcp
) {
954 "Values lower than 512 MUST be treated as equal to 512."
956 maxanswersize
= min(static_cast<uint16_t>(edo
.d_packetsize
>= 512 ? edo
.d_packetsize
: 512), g_udpTruncationThreshold
);
958 ednsOpts
= edo
.d_options
;
959 maxanswersize
-= 11; // EDNS header size
961 if (!comboWriter
->d_responsePaddingDisabled
&& g_paddingFrom
.match(comboWriter
->d_remote
)) {
962 paddingAllowed
= true;
963 if (g_paddingMode
== PaddingMode::Always
) {
964 addPaddingToResponse
= true;
968 for (const auto& option
: edo
.d_options
) {
969 if (option
.first
== EDNSOptionCode::ECS
&& g_useIncomingECS
&& !comboWriter
->d_ecsParsed
) {
970 comboWriter
->d_ecsFound
= getEDNSSubnetOptsFromString(option
.second
, &comboWriter
->d_ednssubnet
);
972 else if (option
.first
== EDNSOptionCode::NSID
) {
973 const static string mode_server_id
= ::arg()["server-id"];
974 if (mode_server_id
!= "disabled" && !mode_server_id
.empty() && maxanswersize
> (EDNSOptionCodeSize
+ EDNSOptionLengthSize
+ mode_server_id
.size())) {
975 returnedEdnsOptions
.emplace_back(EDNSOptionCode::NSID
, mode_server_id
);
976 variableAnswer
= true; // Can't packetcache an answer with NSID
977 maxanswersize
-= EDNSOptionCodeSize
+ EDNSOptionLengthSize
+ mode_server_id
.size();
980 else if (paddingAllowed
&& !addPaddingToResponse
&& g_paddingMode
== PaddingMode::PaddedQueries
&& option
.first
== EDNSOptionCode::PADDING
) {
981 addPaddingToResponse
= true;
986 /* the lookup will be done _before_ knowing whether the query actually
987 has a padding option, so we need to use the separate tag even when the
988 query does not have padding, as long as it is from an allowed source */
989 if (paddingAllowed
&& comboWriter
->d_tag
== 0) {
990 comboWriter
->d_tag
= g_paddingTag
;
993 /* perhaps there was no EDNS or no ECS but by now we looked */
994 comboWriter
->d_ecsParsed
= true;
995 vector
<DNSRecord
> ret
;
996 vector
<uint8_t> packet
;
998 auto luaconfsLocal
= g_luaconfs
.getLocal();
999 // Used to tell syncres later on if we should apply NSDNAME and NSIP RPZ triggers for this query
1000 bool wantsRPZ(true);
1001 RecursorPacketCache::OptPBData pbDataForCache
;
1002 pdns::ProtoZero::RecMessage pbMessage
;
1003 if (checkProtobufExport(luaconfsLocal
)) {
1004 pbMessage
.reserve(128, 128); // It's a bit of a guess...
1005 pbMessage
.setResponse(comboWriter
->d_mdp
.d_qname
, comboWriter
->d_mdp
.d_qtype
, comboWriter
->d_mdp
.d_qclass
);
1006 pbMessage
.setServerIdentity(SyncRes::s_serverID
);
1008 // RRSets added below
1010 checkOutgoingProtobufExport(luaconfsLocal
); // to pick up changed configs
1012 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->frameStreamExportConfig
, t_frameStreamServersInfo
);
1013 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->nodFrameStreamExportConfig
, t_nodFrameStreamServersInfo
);
1016 DNSPacketWriter
packetWriter(packet
, comboWriter
->d_mdp
.d_qname
, comboWriter
->d_mdp
.d_qtype
, comboWriter
->d_mdp
.d_qclass
, comboWriter
->d_mdp
.d_header
.opcode
);
1018 packetWriter
.getHeader()->aa
= 0;
1019 packetWriter
.getHeader()->ra
= 1;
1020 packetWriter
.getHeader()->qr
= 1;
1021 packetWriter
.getHeader()->tc
= 0;
1022 packetWriter
.getHeader()->id
= comboWriter
->d_mdp
.d_header
.id
;
1023 packetWriter
.getHeader()->rd
= comboWriter
->d_mdp
.d_header
.rd
;
1024 packetWriter
.getHeader()->cd
= comboWriter
->d_mdp
.d_header
.cd
;
1026 /* This is the lowest TTL seen in the records of the response,
1027 so we can't cache it for longer than this value.
1028 If we have a TTL cap, this value can't be larger than the
1029 cap no matter what. */
1030 uint32_t minTTL
= comboWriter
->d_ttlCap
;
1031 bool seenAuthSOA
= false;
1033 resolver
.d_eventTrace
= std::move(comboWriter
->d_eventTrace
);
1034 resolver
.setId(g_multiTasker
->getTid());
1036 bool DNSSECOK
= false;
1037 if (comboWriter
->d_luaContext
) {
1038 resolver
.setLuaEngine(comboWriter
->d_luaContext
);
1040 if (g_dnssecmode
!= DNSSECMode::Off
) {
1041 resolver
.setDoDNSSEC(true);
1043 // Does the requestor want DNSSEC records?
1044 if ((edo
.d_extFlags
& EDNSOpts::DNSSECOK
) != 0) {
1046 t_Counters
.at(rec::Counter::dnssecQueries
)++;
1048 if (comboWriter
->d_mdp
.d_header
.cd
) {
1049 /* Per rfc6840 section 5.9, "When processing a request with
1050 the Checking Disabled (CD) bit set, a resolver SHOULD attempt
1051 to return all response data, even data that has failed DNSSEC
1053 ++t_Counters
.at(rec::Counter::dnssecCheckDisabledQueries
);
1055 if (comboWriter
->d_mdp
.d_header
.ad
) {
1056 /* Per rfc6840 section 5.7, "the AD bit in a query as a signal
1057 indicating that the requester understands and is interested in the
1058 value of the AD bit in the response. This allows a requester to
1059 indicate that it understands the AD bit without also requesting
1060 DNSSEC data via the DO bit. */
1061 ++t_Counters
.at(rec::Counter::dnssecAuthenticDataQueries
);
1065 // Ignore the client-set CD flag
1066 packetWriter
.getHeader()->cd
= 0;
1068 resolver
.setDNSSECValidationRequested(g_dnssecmode
== DNSSECMode::ValidateAll
|| g_dnssecmode
== DNSSECMode::ValidateForLog
|| ((comboWriter
->d_mdp
.d_header
.ad
|| DNSSECOK
) && g_dnssecmode
== DNSSECMode::Process
));
1070 resolver
.setInitialRequestId(comboWriter
->d_uuid
);
1071 resolver
.setOutgoingProtobufServers(t_outgoingProtobufServers
.servers
);
1073 resolver
.setFrameStreamServers(t_frameStreamServersInfo
.servers
);
1076 bool useMapped
= true;
1077 // If proxy by table is active and had a match, we only want to use the mapped address if it also has a domain match
1078 // (if a domain suffix match table is present in the config)
1079 if (t_proxyMapping
&& comboWriter
->d_source
!= comboWriter
->d_mappedSource
) {
1080 if (const auto* iter
= t_proxyMapping
->lookup(comboWriter
->d_source
)) {
1081 if (iter
->second
.suffixMatchNode
) {
1082 if (!iter
->second
.suffixMatchNode
->check(comboWriter
->d_mdp
.d_qname
)) {
1083 // No match in domains, use original source
1087 ++iter
->second
.stats
.suffixMatches
;
1090 // No suffix match node defined, use mapped address
1092 // lookup failing cannot happen as dc->d_source != dc->d_mappedSource
1094 resolver
.setQuerySource(useMapped
? comboWriter
->d_mappedSource
: comboWriter
->d_source
, g_useIncomingECS
&& !comboWriter
->d_ednssubnet
.source
.empty() ? boost::optional
<const EDNSSubnetOpts
&>(comboWriter
->d_ednssubnet
) : boost::none
);
1096 resolver
.setQueryReceivedOverTCP(comboWriter
->d_tcp
);
1098 bool tracedQuery
= false; // we could consider letting Lua know about this too
1099 bool shouldNotValidate
= false;
1101 /* preresolve expects res (dq.rcode) to be set to RCode::NoError by default */
1102 int res
= RCode::NoError
;
1104 DNSFilterEngine::Policy appliedPolicy
;
1105 RecursorLua4::DNSQuestion
dnsQuestion(comboWriter
->d_source
, comboWriter
->d_destination
, comboWriter
->d_mdp
.d_qname
, comboWriter
->d_mdp
.d_qtype
, comboWriter
->d_tcp
, variableAnswer
, wantsRPZ
, comboWriter
->d_logResponse
, addPaddingToResponse
, (g_useKernelTimestamp
&& comboWriter
->d_kernelTimestamp
.tv_sec
!= 0) ? comboWriter
->d_kernelTimestamp
: comboWriter
->d_now
);
1106 dnsQuestion
.ednsFlags
= &edo
.d_extFlags
;
1107 dnsQuestion
.ednsOptions
= &ednsOpts
;
1108 dnsQuestion
.tag
= comboWriter
->d_tag
;
1109 dnsQuestion
.discardedPolicies
= &resolver
.d_discardedPolicies
;
1110 dnsQuestion
.policyTags
= &comboWriter
->d_policyTags
;
1111 dnsQuestion
.appliedPolicy
= &appliedPolicy
;
1112 dnsQuestion
.currentRecords
= &ret
;
1113 dnsQuestion
.dh
= &comboWriter
->d_mdp
.d_header
;
1114 dnsQuestion
.data
= comboWriter
->d_data
;
1115 dnsQuestion
.requestorId
= comboWriter
->d_requestorId
;
1116 dnsQuestion
.deviceId
= comboWriter
->d_deviceId
;
1117 dnsQuestion
.deviceName
= comboWriter
->d_deviceName
;
1118 dnsQuestion
.proxyProtocolValues
= &comboWriter
->d_proxyProtocolValues
;
1119 dnsQuestion
.extendedErrorCode
= &comboWriter
->d_extendedErrorCode
;
1120 dnsQuestion
.extendedErrorExtra
= &comboWriter
->d_extendedErrorExtra
;
1121 dnsQuestion
.meta
= std::move(comboWriter
->d_meta
);
1122 dnsQuestion
.fromAuthIP
= &resolver
.d_fromAuthIP
;
1124 resolver
.d_slog
= resolver
.d_slog
->withValues("qname", Logging::Loggable(comboWriter
->d_mdp
.d_qname
),
1125 "qtype", Logging::Loggable(QType(comboWriter
->d_mdp
.d_qtype
)),
1126 "remote", Logging::Loggable(comboWriter
->getRemote()),
1127 "proto", Logging::Loggable(comboWriter
->d_tcp
? "tcp" : "udp"),
1128 "ecs", Logging::Loggable(comboWriter
->d_ednssubnet
.source
.empty() ? "" : comboWriter
->d_ednssubnet
.source
.toString()),
1129 "mtid", Logging::Loggable(g_multiTasker
->getTid()));
1130 RunningResolveGuard
tcpGuard(comboWriter
);
1132 if (ednsExtRCode
!= 0 || comboWriter
->d_mdp
.d_header
.opcode
== static_cast<unsigned>(Opcode::Notify
)) {
1133 goto sendit
; // NOLINT(cppcoreguidelines-avoid-goto)
1136 if (comboWriter
->d_mdp
.d_qtype
== QType::ANY
&& !comboWriter
->d_tcp
&& g_anyToTcp
) {
1137 packetWriter
.getHeader()->tc
= 1;
1139 variableAnswer
= true;
1140 goto sendit
; // NOLINT(cppcoreguidelines-avoid-goto)
1143 if (t_traceRegex
&& t_traceRegex
->match(comboWriter
->d_mdp
.d_qname
.toString())) {
1144 resolver
.setLogMode(SyncRes::Store
);
1148 if (!g_quiet
|| tracedQuery
) {
1149 if (!g_slogStructured
) {
1150 g_log
<< Logger::Warning
<< RecThreadInfo::id() << " [" << g_multiTasker
->getTid() << "/" << g_multiTasker
->numProcesses() << "] " << (comboWriter
->d_tcp
? "TCP " : "") << "question for '" << comboWriter
->d_mdp
.d_qname
<< "|"
1151 << QType(comboWriter
->d_mdp
.d_qtype
) << "' from " << comboWriter
->getRemote();
1152 if (!comboWriter
->d_ednssubnet
.source
.empty()) {
1153 g_log
<< " (ecs " << comboWriter
->d_ednssubnet
.source
.toString() << ")";
1158 resolver
.d_slog
->info(Logr::Info
, "Question");
1162 if (!comboWriter
->d_mdp
.d_header
.rd
) {
1163 resolver
.setCacheOnly();
1166 if (comboWriter
->d_luaContext
) {
1167 comboWriter
->d_luaContext
->prerpz(dnsQuestion
, res
, resolver
.d_eventTrace
);
1170 // Check if the client has a policy attached to it
1171 if (wantsRPZ
&& !appliedPolicy
.wasHit()) {
1173 if (luaconfsLocal
->dfe
.getClientPolicy(comboWriter
->d_source
, resolver
.d_discardedPolicies
, appliedPolicy
)) {
1174 mergePolicyTags(comboWriter
->d_policyTags
, appliedPolicy
.getTags());
1178 /* If we already have an answer generated from gettag_ffi, let's see if the filtering policies
1179 should be applied to it */
1180 if (comboWriter
->d_rcode
!= boost::none
) {
1182 bool policyOverride
= false;
1183 /* Unless we already matched on the client IP, time to check the qname.
1184 We normally check it in beginResolve() but it will be bypassed since we already have an answer */
1185 if (wantsRPZ
&& appliedPolicy
.policyOverridesGettag()) {
1186 if (appliedPolicy
.d_type
!= DNSFilterEngine::PolicyType::None
) {
1187 // Client IP already matched
1190 // no match on the client IP, check the qname
1191 if (luaconfsLocal
->dfe
.getQueryPolicy(comboWriter
->d_mdp
.d_qname
, resolver
.d_discardedPolicies
, appliedPolicy
)) {
1193 mergePolicyTags(comboWriter
->d_policyTags
, appliedPolicy
.getTags());
1197 if (appliedPolicy
.wasHit()) {
1198 policyOverride
= true;
1202 if (!policyOverride
) {
1203 /* No RPZ or gettag overrides it anyway */
1204 ret
= std::move(comboWriter
->d_records
);
1205 res
= *comboWriter
->d_rcode
;
1206 if (res
== RCode::NoError
&& comboWriter
->d_followCNAMERecords
) {
1207 res
= followCNAMERecords(ret
, QType(comboWriter
->d_mdp
.d_qtype
), res
);
1209 goto haveAnswer
; // NOLINT(cppcoreguidelines-avoid-goto)
1213 // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve
1214 if (!comboWriter
->d_luaContext
|| !comboWriter
->d_luaContext
->preresolve(dnsQuestion
, res
, resolver
.d_eventTrace
)) {
1216 if (!g_dns64PrefixReverse
.empty() && dnsQuestion
.qtype
== QType::PTR
&& dnsQuestion
.qname
.isPartOf(g_dns64PrefixReverse
)) {
1217 res
= getFakePTRRecords(dnsQuestion
.qname
, ret
);
1218 goto haveAnswer
; // NOLINT(cppcoreguidelines-avoid-goto)
1221 resolver
.setWantsRPZ(wantsRPZ
);
1223 if (wantsRPZ
&& appliedPolicy
.d_kind
!= DNSFilterEngine::PolicyKind::NoAction
) {
1225 if (comboWriter
->d_luaContext
&& comboWriter
->d_luaContext
->policyHitEventFilter(comboWriter
->d_source
, comboWriter
->d_mdp
.d_qname
, QType(comboWriter
->d_mdp
.d_qtype
), comboWriter
->d_tcp
, appliedPolicy
, comboWriter
->d_policyTags
, resolver
.d_discardedPolicies
)) {
1226 /* reset to no match */
1227 appliedPolicy
= DNSFilterEngine::Policy();
1230 auto policyResult
= handlePolicyHit(appliedPolicy
, comboWriter
, resolver
, res
, ret
, packetWriter
, tcpGuard
);
1231 if (policyResult
== PolicyResult::HaveAnswer
) {
1232 if (g_dns64Prefix
&& dnsQuestion
.qtype
== QType::AAAA
&& dns64Candidate(comboWriter
->d_mdp
.d_qtype
, res
, ret
)) {
1233 res
= getFakeAAAARecords(dnsQuestion
.qname
, *g_dns64Prefix
, ret
);
1234 shouldNotValidate
= true;
1236 goto haveAnswer
; // NOLINT(cppcoreguidelines-avoid-goto)
1238 else if (policyResult
== PolicyResult::Drop
) {
1244 // Query did not get handled for Client IP or QNAME Policy reasons, now actually go out to find an answer
1246 resolver
.d_appliedPolicy
= appliedPolicy
;
1247 resolver
.d_policyTags
= std::move(comboWriter
->d_policyTags
);
1249 if (!comboWriter
->d_routingTag
.empty()) {
1250 resolver
.d_routingTag
= comboWriter
->d_routingTag
;
1253 ret
.clear(); // policy might have filled it with custom records but we decided not to use them
1254 res
= resolver
.beginResolve(comboWriter
->d_mdp
.d_qname
, QType(comboWriter
->d_mdp
.d_qtype
), comboWriter
->d_mdp
.d_qclass
, ret
);
1255 shouldNotValidate
= resolver
.wasOutOfBand();
1257 catch (const ImmediateQueryDropException
& e
) {
1258 // XXX We need to export a protobuf message (and do a NOD lookup) if requested!
1259 t_Counters
.at(rec::Counter::policyDrops
)++;
1260 SLOG(g_log
<< Logger::Debug
<< "Dropping query because of a filtering policy " << makeLoginfo(comboWriter
) << endl
,
1261 resolver
.d_slog
->info(Logr::Debug
, "Dropping query because of a filtering policy"));
1264 catch (const ImmediateServFailException
& e
) {
1265 if (g_logCommonErrors
) {
1266 SLOG(g_log
<< Logger::Notice
<< "Sending SERVFAIL to " << comboWriter
->getRemote() << " during resolve of '" << comboWriter
->d_mdp
.d_qname
<< "' because: " << e
.reason
<< endl
,
1267 resolver
.d_slog
->error(Logr::Notice
, e
.reason
, "Sending SERVFAIL during resolve"));
1269 res
= RCode::ServFail
;
1271 catch (const SendTruncatedAnswerException
& e
) {
1273 resolver
.d_appliedPolicy
.addSOAtoRPZResult(ret
);
1274 res
= RCode::NoError
;
1275 packetWriter
.getHeader()->tc
= 1;
1277 catch (const PolicyHitException
& e
) {
1280 dnsQuestion
.validationState
= resolver
.getValidationState();
1281 appliedPolicy
= resolver
.d_appliedPolicy
;
1282 comboWriter
->d_policyTags
= std::move(resolver
.d_policyTags
);
1284 if (appliedPolicy
.d_type
!= DNSFilterEngine::PolicyType::None
&& appliedPolicy
.d_zoneData
&& appliedPolicy
.d_zoneData
->d_extendedErrorCode
) {
1285 comboWriter
->d_extendedErrorCode
= *appliedPolicy
.d_zoneData
->d_extendedErrorCode
;
1286 comboWriter
->d_extendedErrorExtra
= appliedPolicy
.d_zoneData
->d_extendedErrorExtra
;
1289 // During lookup, an NSDNAME or NSIP trigger was hit in RPZ
1290 if (res
== -2) { // XXX This block should be macro'd, it is repeated post-resolve.
1291 if (appliedPolicy
.d_kind
== DNSFilterEngine::PolicyKind::NoAction
) {
1292 throw PDNSException("NoAction policy returned while a NSDNAME or NSIP trigger was hit");
1294 auto policyResult
= handlePolicyHit(appliedPolicy
, comboWriter
, resolver
, res
, ret
, packetWriter
, tcpGuard
);
1295 if (policyResult
== PolicyResult::HaveAnswer
) {
1296 goto haveAnswer
; // NOLINT(cppcoreguidelines-avoid-goto)
1298 else if (policyResult
== PolicyResult::Drop
) {
1303 bool luaHookHandled
= false;
1304 if (comboWriter
->d_luaContext
) {
1305 PolicyResult policyResult
= PolicyResult::NoAction
;
1306 if (answerIsNOData(comboWriter
->d_mdp
.d_qtype
, res
, ret
)) {
1307 if (comboWriter
->d_luaContext
->nodata(dnsQuestion
, res
, resolver
.d_eventTrace
)) {
1308 luaHookHandled
= true;
1309 shouldNotValidate
= true;
1310 policyResult
= handlePolicyHit(appliedPolicy
, comboWriter
, resolver
, res
, ret
, packetWriter
, tcpGuard
);
1313 else if (res
== RCode::NXDomain
&& comboWriter
->d_luaContext
->nxdomain(dnsQuestion
, res
, resolver
.d_eventTrace
)) {
1314 luaHookHandled
= true;
1315 shouldNotValidate
= true;
1316 policyResult
= handlePolicyHit(appliedPolicy
, comboWriter
, resolver
, res
, ret
, packetWriter
, tcpGuard
);
1318 if (policyResult
== PolicyResult::HaveAnswer
) {
1319 goto haveAnswer
; // NOLINT(cppcoreguidelines-avoid-goto)
1321 else if (policyResult
== PolicyResult::Drop
) {
1324 } // dc->d_luaContext
1326 if (!luaHookHandled
&& g_dns64Prefix
&& comboWriter
->d_mdp
.d_qtype
== QType::AAAA
&& (shouldNotValidate
|| !resolver
.isDNSSECValidationRequested() || !vStateIsBogus(dnsQuestion
.validationState
)) && dns64Candidate(comboWriter
->d_mdp
.d_qtype
, res
, ret
)) {
1327 res
= getFakeAAAARecords(dnsQuestion
.qname
, *g_dns64Prefix
, ret
);
1328 shouldNotValidate
= true;
1331 if (comboWriter
->d_luaContext
) {
1332 PolicyResult policyResult
= PolicyResult::NoAction
;
1333 if (comboWriter
->d_luaContext
->d_postresolve_ffi
) {
1334 RecursorLua4::PostResolveFFIHandle
handle(dnsQuestion
);
1335 resolver
.d_eventTrace
.add(RecEventTrace::LuaPostResolveFFI
);
1336 bool prResult
= comboWriter
->d_luaContext
->postresolve_ffi(handle
);
1337 resolver
.d_eventTrace
.add(RecEventTrace::LuaPostResolveFFI
, prResult
, false);
1339 shouldNotValidate
= true;
1340 policyResult
= handlePolicyHit(appliedPolicy
, comboWriter
, resolver
, res
, ret
, packetWriter
, tcpGuard
);
1343 else if (comboWriter
->d_luaContext
->postresolve(dnsQuestion
, res
, resolver
.d_eventTrace
)) {
1344 shouldNotValidate
= true;
1345 policyResult
= handlePolicyHit(appliedPolicy
, comboWriter
, resolver
, res
, ret
, packetWriter
, tcpGuard
);
1347 if (policyResult
== PolicyResult::HaveAnswer
) {
1348 goto haveAnswer
; // NOLINT(cppcoreguidelines-avoid-goto)
1350 else if (policyResult
== PolicyResult::Drop
) {
1353 } // dc->d_luaContext
1355 else if (comboWriter
->d_luaContext
) {
1356 // preresolve returned true
1357 shouldNotValidate
= true;
1358 auto policyResult
= handlePolicyHit(appliedPolicy
, comboWriter
, resolver
, res
, ret
, packetWriter
, tcpGuard
);
1359 // haveAnswer case redundant
1360 if (policyResult
== PolicyResult::Drop
) {
1366 if (tracedQuery
|| res
== -1 || res
== RCode::ServFail
|| packetWriter
.getHeader()->rcode
== static_cast<unsigned>(RCode::ServFail
)) {
1367 dumpTrace(resolver
.getTrace(), resolver
.d_fixednow
);
1371 packetWriter
.getHeader()->rcode
= RCode::ServFail
;
1372 // no commit here, because no record
1373 ++t_Counters
.at(rec::Counter::servFails
);
1376 packetWriter
.getHeader()->rcode
= res
;
1378 // Does the validation mode or query demand validation?
1379 if (!shouldNotValidate
&& resolver
.isDNSSECValidationRequested()) {
1381 auto state
= resolver
.getValidationState();
1384 std::shared_ptr
<Logr::Logger
> log
;
1385 if (resolver
.doLog() || vStateIsBogus(state
)) {
1386 // Only create logging object if needed below, beware if you change the logging logic!
1387 log
= resolver
.d_slog
->withValues("vstate", Logging::Loggable(state
));
1388 auto xdnssec
= g_xdnssec
.getLocal();
1389 if (xdnssec
->check(comboWriter
->d_mdp
.d_qname
)) {
1390 log
= log
->withValues("in-x-dnssec-names", Logging::Loggable(1));
1391 x_marker
= " [in x-dnssec-names]";
1394 if (state
== vState::Secure
) {
1395 if (resolver
.doLog()) {
1396 SLOG(g_log
<< Logger::Warning
<< "Answer to " << comboWriter
->d_mdp
.d_qname
<< "|" << QType(comboWriter
->d_mdp
.d_qtype
) << x_marker
<< " for " << comboWriter
->getRemote() << " validates correctly" << endl
,
1397 log
->info(Logr::Info
, "Validates Correctly"));
1400 // Is the query source interested in the value of the ad-bit?
1401 if (comboWriter
->d_mdp
.d_header
.ad
|| DNSSECOK
) {
1402 packetWriter
.getHeader()->ad
= 1;
1405 else if (state
== vState::Insecure
) {
1406 if (resolver
.doLog()) {
1407 SLOG(g_log
<< Logger::Warning
<< "Answer to " << comboWriter
->d_mdp
.d_qname
<< "|" << QType(comboWriter
->d_mdp
.d_qtype
) << x_marker
<< " for " << comboWriter
->getRemote() << " validates as Insecure" << endl
,
1408 log
->info(Logr::Info
, "Validates as Insecure"));
1411 packetWriter
.getHeader()->ad
= 0;
1413 else if (vStateIsBogus(state
)) {
1414 if (t_bogusremotes
) {
1415 t_bogusremotes
->push_back(comboWriter
->d_source
);
1417 if (t_bogusqueryring
) {
1418 t_bogusqueryring
->push_back({comboWriter
->d_mdp
.d_qname
, comboWriter
->d_mdp
.d_qtype
});
1420 if (g_dnssecLogBogus
|| resolver
.doLog() || g_dnssecmode
== DNSSECMode::ValidateForLog
) {
1421 SLOG(g_log
<< Logger::Warning
<< "Answer to " << comboWriter
->d_mdp
.d_qname
<< "|" << QType(comboWriter
->d_mdp
.d_qtype
) << x_marker
<< " for " << comboWriter
->getRemote() << " validates as " << vStateToString(state
) << endl
,
1422 log
->info(Logr::Notice
, "Validates as Bogus"));
1425 // Does the query or validation mode sending out a SERVFAIL on validation errors?
1426 if (!packetWriter
.getHeader()->cd
&& (g_dnssecmode
== DNSSECMode::ValidateAll
|| comboWriter
->d_mdp
.d_header
.ad
|| DNSSECOK
)) {
1427 if (resolver
.doLog()) {
1428 SLOG(g_log
<< Logger::Warning
<< "Sending out SERVFAIL for " << comboWriter
->d_mdp
.d_qname
<< "|" << QType(comboWriter
->d_mdp
.d_qtype
) << " because recursor or query demands it for Bogus results" << endl
,
1429 log
->info(Logr::Notice
, "Sending out SERVFAIL because recursor or query demands it for Bogus results"));
1432 packetWriter
.getHeader()->rcode
= RCode::ServFail
;
1433 goto sendit
; // NOLINT(cppcoreguidelines-avoid-goto)
1436 if (resolver
.doLog()) {
1437 SLOG(g_log
<< Logger::Warning
<< "Not sending out SERVFAIL for " << comboWriter
->d_mdp
.d_qname
<< "|" << QType(comboWriter
->d_mdp
.d_qtype
) << x_marker
<< " Bogus validation since neither config nor query demands this" << endl
,
1438 log
->info(Logr::Notice
, "Sending out SERVFAIL because recursor or query demands it for Bogus results"));
1443 catch (const ImmediateServFailException
& e
) {
1444 if (g_logCommonErrors
) {
1445 SLOG(g_log
<< Logger::Notice
<< "Sending SERVFAIL to " << comboWriter
->getRemote() << " during validation of '" << comboWriter
->d_mdp
.d_qname
<< "|" << QType(comboWriter
->d_mdp
.d_qtype
) << "' because: " << e
.reason
<< endl
,
1446 resolver
.d_slog
->error(Logr::Notice
, e
.reason
, "Sending SERVFAIL during validation", "exception", Logging::Loggable("ImmediateServFailException")));
1448 goto sendit
; // NOLINT(cppcoreguidelines-avoid-goto)
1453 pdns::orderAndShuffle(ret
, false);
1454 if (auto listToSort
= luaconfsLocal
->sortlist
.getOrderCmp(comboWriter
->d_source
)) {
1455 stable_sort(ret
.begin(), ret
.end(), *listToSort
);
1456 variableAnswer
= true;
1460 bool needCommit
= false;
1461 for (const auto& record
: ret
) {
1462 if (!DNSSECOK
&& (record
.d_type
== QType::NSEC3
|| ((record
.d_type
== QType::RRSIG
|| record
.d_type
== QType::NSEC
) && ((comboWriter
->d_mdp
.d_qtype
!= record
.d_type
&& comboWriter
->d_mdp
.d_qtype
!= QType::ANY
) || (record
.d_place
!= DNSResourceRecord::ANSWER
&& record
.d_place
!= DNSResourceRecord::ADDITIONAL
))))) {
1466 if (!addRecordToPacket(packetWriter
, record
, minTTL
, comboWriter
->d_ttlCap
, maxanswersize
, seenAuthSOA
)) {
1475 udr
= udrCheckUniqueDNSRecord(nodlogger
, comboWriter
->d_mdp
.d_qname
, comboWriter
->d_mdp
.d_qtype
, record
);
1476 if (!hasUDR
&& udr
) {
1480 #endif /* NOD ENABLED */
1482 if (t_protobufServers
.servers
) {
1483 // Max size is 64k, but we're conservative here, as other fields are added after the answers have been added
1484 // If a single answer causes a too big protobuf message, it wil be dropped by queueData()
1485 // But note addRR has code to prevent that
1486 if (pbMessage
.size() < std::numeric_limits
<uint16_t>::max() / 2) {
1487 pbMessage
.addRR(record
, luaconfsLocal
->protobufExportConfig
.exportTypes
, udr
);
1492 packetWriter
.commit();
1497 if (isEnabledForUDRs(t_nodFrameStreamServersInfo
.servers
)) {
1498 struct timespec timeSpec
1502 if (g_useKernelTimestamp
&& comboWriter
->d_kernelTimestamp
.tv_sec
!= 0) {
1503 TIMEVAL_TO_TIMESPEC(&comboWriter
->d_kernelTimestamp
, &timeSpec
); // NOLINT
1506 TIMEVAL_TO_TIMESPEC(&comboWriter
->d_now
, &timeSpec
); // NOLINT
1508 DnstapMessage
message(str
, DnstapMessage::MessageType::resolver_response
, SyncRes::s_serverID
, &comboWriter
->d_source
, &comboWriter
->d_destination
, comboWriter
->d_tcp
? DnstapMessage::ProtocolType::DoTCP
: DnstapMessage::ProtocolType::DoUDP
, reinterpret_cast<const char*>(&*packet
.begin()), packet
.size(), &timeSpec
, nullptr, comboWriter
->d_mdp
.d_qname
); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
1510 for (auto& logger
: *(t_nodFrameStreamServersInfo
.servers
)) {
1511 if (logger
->logUDRs()) {
1512 remoteLoggerQueueData(*logger
, str
);
1517 #endif // HAVE_FSTRM
1518 #endif // NOD_ENABLED
1522 if (g_useIncomingECS
&& comboWriter
->d_ecsFound
&& !resolver
.wasVariable() && !variableAnswer
) {
1523 EDNSSubnetOpts ednsOptions
;
1524 ednsOptions
.source
= comboWriter
->d_ednssubnet
.source
;
1525 ComboAddress sourceAddr
;
1527 sourceAddr
.sin4
.sin_family
= ednsOptions
.source
.getNetwork().sin4
.sin_family
;
1528 ednsOptions
.scope
= Netmask(sourceAddr
, 0);
1529 auto ecsPayload
= makeEDNSSubnetOptsString(ednsOptions
);
1531 // if we don't have enough space available let's just not set that scope of zero,
1532 // it will prevent some caching, mostly from dnsdist, but that's fine
1533 if (packetWriter
.size() < maxanswersize
&& (maxanswersize
- packetWriter
.size()) >= (EDNSOptionCodeSize
+ EDNSOptionLengthSize
+ ecsPayload
.size())) {
1535 maxanswersize
-= EDNSOptionCodeSize
+ EDNSOptionLengthSize
+ ecsPayload
.size();
1537 returnedEdnsOptions
.emplace_back(EDNSOptionCode::ECS
, std::move(ecsPayload
));
1541 if (haveEDNS
&& addPaddingToResponse
) {
1542 size_t currentSize
= packetWriter
.getSizeWithOpts(returnedEdnsOptions
);
1543 /* we don't use maxawnswersize because it accounts for some EDNS options, but
1544 not all of them (for example ECS) */
1545 size_t maxSize
= min(static_cast<uint16_t>(edo
.d_packetsize
>= 512 ? edo
.d_packetsize
: 512), g_udpTruncationThreshold
);
1547 if (currentSize
< (maxSize
- 4)) {
1548 size_t remaining
= maxSize
- (currentSize
+ 4);
1549 /* from rfc8647, "4.1. Recommended Strategy: Block-Length Padding":
1550 If a server receives a query that includes the EDNS(0) "Padding"
1551 option, it MUST pad the corresponding response (see Section 4 of
1552 RFC 7830) and SHOULD pad the corresponding response to a
1553 multiple of 468 octets (see below).
1555 const size_t blockSize
= 468;
1556 size_t modulo
= (currentSize
+ 4) % blockSize
;
1559 padSize
= std::min(blockSize
- modulo
, remaining
);
1561 returnedEdnsOptions
.emplace_back(EDNSOptionCode::PADDING
, makeEDNSPaddingOptString(padSize
));
1566 auto state
= resolver
.getValidationState();
1567 if (comboWriter
->d_extendedErrorCode
|| resolver
.d_extendedError
|| (SyncRes::s_addExtendedResolutionDNSErrors
&& vStateIsBogus(state
))) {
1568 EDNSExtendedError::code code
= EDNSExtendedError::code::Other
;
1571 if (comboWriter
->d_extendedErrorCode
) {
1572 code
= static_cast<EDNSExtendedError::code
>(*comboWriter
->d_extendedErrorCode
);
1573 extra
= std::move(comboWriter
->d_extendedErrorExtra
);
1575 else if (resolver
.d_extendedError
) {
1576 code
= static_cast<EDNSExtendedError::code
>(resolver
.d_extendedError
->infoCode
);
1577 extra
= std::move(resolver
.d_extendedError
->extraText
);
1581 case vState::BogusNoValidDNSKEY
:
1582 code
= EDNSExtendedError::code::DNSKEYMissing
;
1584 case vState::BogusInvalidDenial
:
1585 code
= EDNSExtendedError::code::NSECMissing
;
1587 case vState::BogusUnableToGetDSs
:
1588 code
= EDNSExtendedError::code::DNSSECBogus
;
1590 case vState::BogusUnableToGetDNSKEYs
:
1591 code
= EDNSExtendedError::code::DNSKEYMissing
;
1593 case vState::BogusSelfSignedDS
:
1594 code
= EDNSExtendedError::code::DNSSECBogus
;
1596 case vState::BogusNoRRSIG
:
1597 code
= EDNSExtendedError::code::RRSIGsMissing
;
1599 case vState::BogusNoValidRRSIG
:
1600 code
= EDNSExtendedError::code::DNSSECBogus
;
1602 case vState::BogusMissingNegativeIndication
:
1603 code
= EDNSExtendedError::code::NSECMissing
;
1605 case vState::BogusSignatureNotYetValid
:
1606 code
= EDNSExtendedError::code::SignatureNotYetValid
;
1608 case vState::BogusSignatureExpired
:
1609 code
= EDNSExtendedError::code::SignatureExpired
;
1611 case vState::BogusUnsupportedDNSKEYAlgo
:
1612 code
= EDNSExtendedError::code::UnsupportedDNSKEYAlgorithm
;
1614 case vState::BogusUnsupportedDSDigestType
:
1615 code
= EDNSExtendedError::code::UnsupportedDSDigestType
;
1617 case vState::BogusNoZoneKeyBitSet
:
1618 code
= EDNSExtendedError::code::NoZoneKeyBitSet
;
1620 case vState::BogusRevokedDNSKEY
:
1621 case vState::BogusInvalidDNSKEYProtocol
:
1622 code
= EDNSExtendedError::code::DNSSECBogus
;
1625 throw std::runtime_error("Bogus validation state not handled: " + vStateToString(state
));
1629 EDNSExtendedError eee
;
1630 eee
.infoCode
= static_cast<uint16_t>(code
);
1631 eee
.extraText
= std::move(extra
);
1633 if (packetWriter
.size() < maxanswersize
&& (maxanswersize
- packetWriter
.size()) >= (EDNSOptionCodeSize
+ EDNSOptionLengthSize
+ sizeof(eee
.infoCode
) + eee
.extraText
.size())) {
1634 returnedEdnsOptions
.emplace_back(EDNSOptionCode::EXTENDEDERROR
, makeEDNSExtendedErrorOptString(eee
));
1638 /* we try to add the EDNS OPT RR even for truncated answers,
1640 "The minimal response MUST be the DNS header, question section, and an
1641 OPT record. This MUST also occur when a truncated response (using
1642 the DNS header's TC bit) is returned."
1644 packetWriter
.addOpt(512, ednsExtRCode
, DNSSECOK
? EDNSOpts::DNSSECOK
: 0, returnedEdnsOptions
);
1645 packetWriter
.commit();
1648 t_Counters
.at(rec::ResponseStats::responseStats
).submitResponse(comboWriter
->d_mdp
.d_qtype
, packet
.size(), packetWriter
.getHeader()->rcode
);
1649 updateResponseStats(res
, comboWriter
->d_source
, packet
.size(), &comboWriter
->d_mdp
.d_qname
, comboWriter
->d_mdp
.d_qtype
);
1653 if (nodCheckNewDomain(nodlogger
, comboWriter
->d_mdp
.d_qname
)) {
1656 if (isEnabledForNODs(t_nodFrameStreamServersInfo
.servers
)) {
1657 struct timespec timeSpec
1661 if (g_useKernelTimestamp
&& comboWriter
->d_kernelTimestamp
.tv_sec
!= 0) {
1662 TIMEVAL_TO_TIMESPEC(&comboWriter
->d_kernelTimestamp
, &timeSpec
); // NOLINT
1665 TIMEVAL_TO_TIMESPEC(&comboWriter
->d_now
, &timeSpec
); // NOLINT
1667 DnstapMessage
message(str
, DnstapMessage::MessageType::client_query
, SyncRes::s_serverID
, &comboWriter
->d_source
, &comboWriter
->d_destination
, comboWriter
->d_tcp
? DnstapMessage::ProtocolType::DoTCP
: DnstapMessage::ProtocolType::DoUDP
, nullptr, 0, &timeSpec
, nullptr, comboWriter
->d_mdp
.d_qname
);
1669 for (auto& logger
: *(t_nodFrameStreamServersInfo
.servers
)) {
1670 if (logger
->logNODs()) {
1671 remoteLoggerQueueData(*logger
, str
);
1675 #endif // HAVE_FSTRM
1678 #endif /* NOD_ENABLED */
1680 if (variableAnswer
|| resolver
.wasVariable()) {
1681 t_Counters
.at(rec::Counter::variableResponses
)++;
1684 if (t_protobufServers
.servers
&& !(luaconfsLocal
->protobufExportConfig
.taggedOnly
&& appliedPolicy
.getName().empty() && comboWriter
->d_policyTags
.empty())) {
1685 // Start constructing embedded DNSResponse object
1686 pbMessage
.setResponseCode(packetWriter
.getHeader()->rcode
);
1687 if (!appliedPolicy
.getName().empty()) {
1688 pbMessage
.setAppliedPolicy(appliedPolicy
.getName());
1689 pbMessage
.setAppliedPolicyType(appliedPolicy
.d_type
);
1690 pbMessage
.setAppliedPolicyTrigger(appliedPolicy
.d_trigger
);
1691 pbMessage
.setAppliedPolicyHit(appliedPolicy
.d_hit
);
1692 pbMessage
.setAppliedPolicyKind(appliedPolicy
.d_kind
);
1694 pbMessage
.setInBytes(packet
.size());
1695 pbMessage
.setValidationState(resolver
.getValidationState());
1696 // See if we want to store the policyTags into th PC
1697 addPolicyTagsToPBMessageIfNeeded(*comboWriter
, pbMessage
);
1699 // Take s snap of the current protobuf buffer state to store in the PC
1700 pbDataForCache
= boost::make_optional(RecursorPacketCache::PBData
{
1701 pbMessage
.getMessageBuf(),
1702 pbMessage
.getResponseBuf(),
1703 !appliedPolicy
.getName().empty() || !comboWriter
->d_policyTags
.empty()});
1705 // if (g_udrEnabled) ??
1706 pbMessage
.clearUDR(pbDataForCache
->d_response
);
1710 const bool intoPC
= g_packetCache
&& !variableAnswer
&& !resolver
.wasVariable();
1712 minTTL
= capPacketCacheTTL(*packetWriter
.getHeader(), minTTL
, seenAuthSOA
);
1713 g_packetCache
->insertResponsePacket(comboWriter
->d_tag
, comboWriter
->d_qhash
, std::move(comboWriter
->d_query
), comboWriter
->d_mdp
.d_qname
,
1714 comboWriter
->d_mdp
.d_qtype
, comboWriter
->d_mdp
.d_qclass
,
1715 string(reinterpret_cast<const char*>(&*packet
.begin()), packet
.size()), // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
1718 dnsQuestion
.validationState
,
1719 std::move(pbDataForCache
), comboWriter
->d_tcp
);
1722 if (g_regressionTestMode
) {
1723 t_Counters
.updateSnap(g_regressionTestMode
);
1726 if (!comboWriter
->d_tcp
) {
1733 cmsgbuf_aligned cbuf
{};
1734 fillMSGHdr(&msgh
, &iov
, &cbuf
, 0, reinterpret_cast<char*>(&*packet
.begin()), packet
.size(), &comboWriter
->d_remote
); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
1735 msgh
.msg_control
= nullptr;
1737 if (g_fromtosockets
.count(comboWriter
->d_socket
) > 0) {
1738 addCMsgSrcAddr(&msgh
, &cbuf
, &comboWriter
->d_local
, 0);
1740 int sendErr
= sendOnNBSocket(comboWriter
->d_socket
, &msgh
);
1741 if (sendErr
!= 0 && g_logCommonErrors
) {
1742 SLOG(g_log
<< Logger::Warning
<< "Sending UDP reply to client " << comboWriter
->getRemote() << " failed with: "
1743 << stringerror(sendErr
) << endl
,
1744 g_slogudpin
->error(Logr::Warning
, sendErr
, "Sending UDP reply to client failed"));
1748 bool hadError
= sendResponseOverTCP(comboWriter
, packet
);
1749 finishTCPReply(comboWriter
, hadError
, true);
1750 tcpGuard
.setHandled();
1753 resolver
.d_eventTrace
.add(RecEventTrace::AnswerSent
);
1755 // Now do the per query changing part ot the protobuf message
1756 if (t_protobufServers
.servers
&& !(luaconfsLocal
->protobufExportConfig
.taggedOnly
&& appliedPolicy
.getName().empty() && comboWriter
->d_policyTags
.empty())) {
1757 // Below are the fields that are not stored in the packet cache and will be appended here and on a cache hit
1758 if (g_useKernelTimestamp
&& comboWriter
->d_kernelTimestamp
.tv_sec
!= 0) {
1759 pbMessage
.setQueryTime(comboWriter
->d_kernelTimestamp
.tv_sec
, comboWriter
->d_kernelTimestamp
.tv_usec
);
1762 pbMessage
.setQueryTime(comboWriter
->d_now
.tv_sec
, comboWriter
->d_now
.tv_usec
);
1764 pbMessage
.setMessageIdentity(comboWriter
->d_uuid
);
1765 pbMessage
.setSocketProtocol(comboWriter
->d_tcp
? pdns::ProtoZero::Message::TransportProtocol::TCP
: pdns::ProtoZero::Message::TransportProtocol::UDP
);
1767 if (!luaconfsLocal
->protobufExportConfig
.logMappedFrom
) {
1768 pbMessage
.setSocketFamily(comboWriter
->d_source
.sin4
.sin_family
);
1769 Netmask
requestorNM(comboWriter
->d_source
, comboWriter
->d_source
.sin4
.sin_family
== AF_INET
? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
1770 ComboAddress requestor
= requestorNM
.getMaskedNetwork();
1771 pbMessage
.setFrom(requestor
);
1772 pbMessage
.setFromPort(comboWriter
->d_source
.getPort());
1775 pbMessage
.setSocketFamily(comboWriter
->d_mappedSource
.sin4
.sin_family
);
1776 Netmask
requestorNM(comboWriter
->d_mappedSource
, comboWriter
->d_mappedSource
.sin4
.sin_family
== AF_INET
? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
1777 ComboAddress requestor
= requestorNM
.getMaskedNetwork();
1778 pbMessage
.setFrom(requestor
);
1779 pbMessage
.setFromPort(comboWriter
->d_mappedSource
.getPort());
1782 pbMessage
.setTo(comboWriter
->d_destination
);
1783 pbMessage
.setId(comboWriter
->d_mdp
.d_header
.id
);
1785 pbMessage
.setTime();
1786 pbMessage
.setEDNSSubnet(comboWriter
->d_ednssubnet
.source
, comboWriter
->d_ednssubnet
.source
.isIPv4() ? luaconfsLocal
->protobufMaskV4
: luaconfsLocal
->protobufMaskV6
);
1787 pbMessage
.setRequestorId(dnsQuestion
.requestorId
);
1788 pbMessage
.setDeviceId(dnsQuestion
.deviceId
);
1789 pbMessage
.setDeviceName(dnsQuestion
.deviceName
);
1790 pbMessage
.setToPort(comboWriter
->d_destination
.getPort());
1791 pbMessage
.addPolicyTags(comboWriter
->d_gettagPolicyTags
);
1793 for (const auto& metaValue
: dnsQuestion
.meta
) {
1794 pbMessage
.setMeta(metaValue
.first
, metaValue
.second
.stringVal
, metaValue
.second
.intVal
);
1799 pbMessage
.setNewlyObservedDomain(true);
1800 pbMessage
.addPolicyTag(g_nod_pbtag
);
1803 pbMessage
.addPolicyTag(g_udr_pbtag
);
1806 #endif /* NOD_ENABLED */
1807 if (resolver
.d_eventTrace
.enabled() && (SyncRes::s_event_trace_enabled
& SyncRes::event_trace_to_pb
) != 0) {
1808 pbMessage
.addEvents(resolver
.d_eventTrace
);
1810 if (comboWriter
->d_logResponse
) {
1811 protobufLogResponse(pbMessage
);
1815 if (resolver
.d_eventTrace
.enabled() && (SyncRes::s_event_trace_enabled
& SyncRes::event_trace_to_log
) != 0) {
1816 SLOG(g_log
<< Logger::Info
<< resolver
.d_eventTrace
.toString() << endl
,
1817 resolver
.d_slog
->info(Logr::Info
, resolver
.d_eventTrace
.toString())); // Maybe we want it to be more fancy?
1820 // Originally this code used a mix of floats, doubles, uint64_t with different units.
1821 // Now it always uses an integral number of microseconds, except for averages, which use doubles
1822 uint64_t spentUsec
= uSec(resolver
.getNow() - comboWriter
->d_now
);
1824 if (!g_slogStructured
) {
1825 g_log
<< Logger::Error
<< RecThreadInfo::id() << " [" << g_multiTasker
->getTid() << "/" << g_multiTasker
->numProcesses() << "] answer to " << (comboWriter
->d_mdp
.d_header
.rd
? "" : "non-rd ") << "question '" << comboWriter
->d_mdp
.d_qname
<< "|" << DNSRecordContent::NumberToType(comboWriter
->d_mdp
.d_qtype
);
1826 g_log
<< "': " << ntohs(packetWriter
.getHeader()->ancount
) << " answers, " << ntohs(packetWriter
.getHeader()->arcount
) << " additional, took " << resolver
.d_outqueries
<< " packets, " << resolver
.d_totUsec
/ 1000.0 << " netw ms, " << static_cast<double>(spentUsec
) / 1000.0 << " tot ms, " << resolver
.d_throttledqueries
<< " throttled, " << resolver
.d_timeouts
<< " timeouts, " << resolver
.d_tcpoutqueries
<< "/" << resolver
.d_dotoutqueries
<< " tcp/dot connections, rcode=" << res
;
1828 if (!shouldNotValidate
&& resolver
.isDNSSECValidationRequested()) {
1829 g_log
<< ", dnssec=" << resolver
.getValidationState();
1831 g_log
<< " answer-is-variable=" << resolver
.wasVariable() << ", into-packetcache=" << intoPC
;
1832 g_log
<< " maxdepth=" << resolver
.d_maxdepth
;
1836 resolver
.d_slog
->info(Logr::Info
, "Answer", "rd", Logging::Loggable(comboWriter
->d_mdp
.d_header
.rd
),
1837 "answers", Logging::Loggable(ntohs(packetWriter
.getHeader()->ancount
)),
1838 "additional", Logging::Loggable(ntohs(packetWriter
.getHeader()->arcount
)),
1839 "outqueries", Logging::Loggable(resolver
.d_outqueries
),
1840 "netms", Logging::Loggable(resolver
.d_totUsec
/ 1000.0),
1841 "totms", Logging::Loggable(static_cast<double>(spentUsec
) / 1000.0),
1842 "throttled", Logging::Loggable(resolver
.d_throttledqueries
),
1843 "timeouts", Logging::Loggable(resolver
.d_timeouts
),
1844 "tcpout", Logging::Loggable(resolver
.d_tcpoutqueries
),
1845 "dotout", Logging::Loggable(resolver
.d_dotoutqueries
),
1846 "rcode", Logging::Loggable(res
),
1847 "validationState", Logging::Loggable(resolver
.getValidationState()),
1848 "answer-is-variable", Logging::Loggable(resolver
.wasVariable()),
1849 "into-packetcache", Logging::Loggable(intoPC
),
1850 "maxdepth", Logging::Loggable(resolver
.d_maxdepth
));
1854 if (comboWriter
->d_mdp
.d_header
.opcode
== static_cast<unsigned>(Opcode::Query
)) {
1855 if (resolver
.d_outqueries
!= 0 || resolver
.d_authzonequeries
!= 0) {
1856 g_recCache
->cacheMisses
++;
1859 g_recCache
->cacheHits
++;
1863 t_Counters
.at(rec::Histogram::answers
)(spentUsec
);
1864 t_Counters
.at(rec::Histogram::cumulativeAnswers
)(spentUsec
);
1866 auto newLat
= static_cast<double>(spentUsec
);
1867 newLat
= min(newLat
, g_networkTimeoutMsec
* 1000.0); // outliers of several minutes exist..
1868 t_Counters
.at(rec::DoubleWAvgCounter::avgLatencyUsec
).addToRollingAvg(newLat
, g_latencyStatSize
);
1869 // no worries, we do this for packet cache hits elsewhere
1871 if (spentUsec
>= resolver
.d_totUsec
) {
1872 uint64_t ourtime
= spentUsec
- resolver
.d_totUsec
;
1873 t_Counters
.at(rec::Histogram::ourtime
)(ourtime
);
1874 newLat
= static_cast<double>(ourtime
); // usec
1875 t_Counters
.at(rec::DoubleWAvgCounter::avgLatencyOursUsec
).addToRollingAvg(newLat
, g_latencyStatSize
);
1880 sendNODLookup(nodlogger
, comboWriter
->d_mdp
.d_qname
);
1882 #endif /* NOD_ENABLED */
1884 // cout<<dc->d_mdp.d_qname<<"\t"<<MT->getUsec()<<"\t"<<sr.d_outqueries<<endl;
1886 catch (const PDNSException
& ae
) {
1887 SLOG(g_log
<< Logger::Error
<< "startDoResolve problem " << makeLoginfo(comboWriter
) << ": " << ae
.reason
<< endl
,
1888 resolver
.d_slog
->error(Logr::Error
, ae
.reason
, "startDoResolve problem", "exception", Logging::Loggable("PDNSException")));
1890 catch (const MOADNSException
& mde
) {
1891 SLOG(g_log
<< Logger::Error
<< "DNS parser error " << makeLoginfo(comboWriter
) << ": " << comboWriter
->d_mdp
.d_qname
<< ", " << mde
.what() << endl
,
1892 resolver
.d_slog
->error(Logr::Error
, mde
.what(), "DNS parser error"));
1894 catch (const std::exception
& e
) {
1895 SLOG(g_log
<< Logger::Error
<< "STL error " << makeLoginfo(comboWriter
) << ": " << e
.what(),
1896 resolver
.d_slog
->error(Logr::Error
, e
.what(), "Exception in resolver context ", "exception", Logging::Loggable("std::exception")));
1898 // Luawrapper nests the exception from Lua, so we unnest it here
1900 std::rethrow_if_nested(e
);
1902 catch (const std::exception
& ne
) {
1903 SLOG(g_log
<< ". Extra info: " << ne
.what(),
1904 resolver
.d_slog
->error(Logr::Error
, ne
.what(), "Nested exception in resolver context", Logging::Loggable("std::exception")));
1908 if (!g_slogStructured
) {
1913 SLOG(g_log
<< Logger::Error
<< "Any other exception in a resolver context " << makeLoginfo(comboWriter
) << endl
,
1914 resolver
.d_slog
->info(Logr::Error
, "Any other exception in a resolver context"));
1917 runTaskOnce(g_logCommonErrors
);
1919 static const size_t stackSizeThreshold
= 9 * ::arg().asNum("stack-size") / 10;
1920 if (g_multiTasker
->getMaxStackUsage() >= stackSizeThreshold
) {
1921 SLOG(g_log
<< Logger::Error
<< "Reached mthread stack usage of 90%: " << g_multiTasker
->getMaxStackUsage() << " " << makeLoginfo(comboWriter
) << " after " << resolver
.d_outqueries
<< " out queries, " << resolver
.d_tcpoutqueries
<< " TCP out queries, " << resolver
.d_dotoutqueries
<< " DoT out queries" << endl
,
1922 resolver
.d_slog
->info(Logr::Error
, "Reached mthread stack usage of 90%",
1923 "stackUsage", Logging::Loggable(g_multiTasker
->getMaxStackUsage()),
1924 "outqueries", Logging::Loggable(resolver
.d_outqueries
),
1925 "netms", Logging::Loggable(resolver
.d_totUsec
/ 1000.0),
1926 "throttled", Logging::Loggable(resolver
.d_throttledqueries
),
1927 "timeouts", Logging::Loggable(resolver
.d_timeouts
),
1928 "tcpout", Logging::Loggable(resolver
.d_tcpoutqueries
),
1929 "dotout", Logging::Loggable(resolver
.d_dotoutqueries
),
1930 "validationState", Logging::Loggable(resolver
.getValidationState())));
1932 t_Counters
.at(rec::Counter::maxMThreadStackUsage
) = max(g_multiTasker
->getMaxStackUsage(), t_Counters
.at(rec::Counter::maxMThreadStackUsage
));
1933 t_Counters
.updateSnap(g_regressionTestMode
);
1936 void getQNameAndSubnet(const std::string
& question
, DNSName
* dnsname
, uint16_t* qtype
, uint16_t* qclass
,
1937 bool& foundECS
, EDNSSubnetOpts
* ednssubnet
, EDNSOptionViewMap
* options
)
1939 const bool lookForECS
= ednssubnet
!= nullptr;
1940 const dnsheader_aligned
dnshead(question
.data());
1941 const dnsheader
* dhPointer
= dnshead
.get();
1942 size_t questionLen
= question
.length();
1943 unsigned int consumed
= 0;
1944 *dnsname
= DNSName(question
.c_str(), static_cast<int>(questionLen
), sizeof(dnsheader
), false, qtype
, qclass
, &consumed
);
1946 size_t pos
= sizeof(dnsheader
) + consumed
+ 4;
1947 const size_t headerSize
= /* root */ 1 + sizeof(dnsrecordheader
);
1948 const uint16_t arcount
= ntohs(dhPointer
->arcount
);
1950 for (uint16_t arpos
= 0; arpos
< arcount
&& questionLen
> (pos
+ headerSize
) && (lookForECS
&& !foundECS
); arpos
++) {
1951 if (question
.at(pos
) != 0) {
1952 /* not an OPT, bye. */
1957 const auto* drh
= reinterpret_cast<const dnsrecordheader
*>(&question
.at(pos
)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
1958 pos
+= sizeof(dnsrecordheader
);
1960 if (pos
>= questionLen
) {
1964 /* OPT root label (1) followed by type (2) */
1965 if (lookForECS
&& ntohs(drh
->d_type
) == QType::OPT
) {
1966 if (options
== nullptr) {
1967 size_t ecsStartPosition
= 0;
1969 /* we need to pass the record len */
1970 int res
= getEDNSOption(reinterpret_cast<const char*>(&question
.at(pos
- sizeof(drh
->d_clen
))), questionLen
- pos
+ sizeof(drh
->d_clen
), EDNSOptionCode::ECS
, &ecsStartPosition
, &ecsLen
); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
1971 if (res
== 0 && ecsLen
> 4) {
1973 if (getEDNSSubnetOptsFromString(&question
.at(pos
- sizeof(drh
->d_clen
) + ecsStartPosition
+ 4), ecsLen
- 4, &eso
)) {
1980 /* we need to pass the record len */
1981 int res
= getEDNSOptions(reinterpret_cast<const char*>(&question
.at(pos
- sizeof(drh
->d_clen
))), questionLen
- pos
+ (sizeof(drh
->d_clen
)), *options
); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
1983 const auto& iter
= options
->find(EDNSOptionCode::ECS
);
1984 if (iter
!= options
->end() && !iter
->second
.values
.empty() && iter
->second
.values
.at(0).content
!= nullptr && iter
->second
.values
.at(0).size
> 0) {
1986 if (getEDNSSubnetOptsFromString(iter
->second
.values
.at(0).content
, iter
->second
.values
.at(0).size
, &eso
)) {
1995 pos
+= ntohs(drh
->d_clen
);
1999 bool checkForCacheHit(bool qnameParsed
, unsigned int tag
, const string
& data
,
2000 DNSName
& qname
, uint16_t& qtype
, uint16_t& qclass
,
2001 const struct timeval
& now
,
2002 string
& response
, uint32_t& qhash
,
2003 RecursorPacketCache::OptPBData
& pbData
, bool tcp
, const ComboAddress
& source
, const ComboAddress
& mappedSource
)
2005 if (!g_packetCache
) {
2008 bool cacheHit
= false;
2010 vState valState
= vState::Indeterminate
;
2013 cacheHit
= g_packetCache
->getResponsePacket(tag
, data
, qname
, qtype
, qclass
, now
.tv_sec
, &response
, &age
, &valState
, &qhash
, &pbData
, tcp
);
2016 cacheHit
= g_packetCache
->getResponsePacket(tag
, data
, qname
, &qtype
, &qclass
, now
.tv_sec
, &response
, &age
, &valState
, &qhash
, &pbData
, tcp
);
2020 if (vStateIsBogus(valState
)) {
2021 if (t_bogusremotes
) {
2022 t_bogusremotes
->push_back(source
);
2024 if (t_bogusqueryring
) {
2025 t_bogusqueryring
->push_back({qname
, qtype
});
2029 // This is only to get the proxyMapping suffixMatch stats right i the case of a PC hit
2030 if (t_proxyMapping
&& source
!= mappedSource
) {
2031 if (const auto* found
= t_proxyMapping
->lookup(source
)) {
2032 if (found
->second
.suffixMatchNode
) {
2033 if (found
->second
.suffixMatchNode
->check(qname
)) {
2034 ++found
->second
.stats
.suffixMatches
;
2040 t_Counters
.at(rec::Counter::packetCacheHits
)++;
2041 t_Counters
.at(rec::Counter::syncresqueries
)++; // XXX
2042 if (response
.length() >= sizeof(struct dnsheader
)) {
2043 dnsheader_aligned
dh_aligned(response
.data());
2044 ageDNSPacket(response
, age
, dh_aligned
);
2045 const auto* dhp
= dh_aligned
.get();
2046 updateResponseStats(dhp
->rcode
, source
, response
.length(), nullptr, 0);
2047 t_Counters
.at(rec::ResponseStats::responseStats
).submitResponse(qtype
, response
.length(), dhp
->rcode
);
2051 t_Counters
.at(rec::DoubleWAvgCounter::avgLatencyUsec
).addToRollingAvg(0.0, g_latencyStatSize
);
2052 t_Counters
.at(rec::DoubleWAvgCounter::avgLatencyOursUsec
).addToRollingAvg(0.0, g_latencyStatSize
);
2054 // XXX changes behaviour compared to old code!
2055 t_Counters
.at(rec::Counter::answers
)(0);
2056 t_Counters
.at(rec::Counter::ourtime
)(0);
2063 static void* pleaseWipeCaches(const DNSName
& canon
, bool subtree
, uint16_t qtype
)
2065 auto res
= wipeCaches(canon
, subtree
, qtype
);
2066 SLOG(g_log
<< Logger::Info
<< "Wiped caches for " << canon
<< ": " << res
.record_count
<< " records; " << res
.negative_record_count
<< " negative records; " << res
.packet_count
<< " packets" << endl
,
2067 g_slog
->withName("runtime")->info(Logr::Info
, "Wiped cache", "qname", Logging::Loggable(canon
), "records", Logging::Loggable(res
.record_count
), "negrecords", Logging::Loggable(res
.negative_record_count
), "packets", Logging::Loggable(res
.packet_count
)));
2071 void requestWipeCaches(const DNSName
& canon
)
2073 // send a message to the handler thread asking it
2074 // to wipe all of the caches
2075 ThreadMSG
* tmsg
= new ThreadMSG(); // NOLINT: pointer owner
2076 tmsg
->func
= [=] { return pleaseWipeCaches(canon
, true, 0xffff); };
2077 tmsg
->wantAnswer
= false;
2078 if (write(RecThreadInfo::info(0).pipes
.writeToThread
, &tmsg
, sizeof(tmsg
)) != sizeof(tmsg
)) { // NOLINT: correct sizeof
2079 delete tmsg
; // NOLINT: pointer owner
2081 unixDie("write to thread pipe returned wrong size or error");
2083 // coverity[leaked_storage]
2086 bool expectProxyProtocol(const ComboAddress
& from
)
2088 return g_proxyProtocolACL
.match(from
);
2091 // fromaddr: the address the query is coming from
2092 // destaddr: the address the query was received on
2093 // source: the address we assume the query is coming from, might be set by proxy protocol
2094 // destination: the address we assume the query was sent to, might be set by proxy protocol
2095 // mappedSource: the address we assume the query is coming from. Differs from source if table based mapping has been applied
2096 static string
* doProcessUDPQuestion(const std::string
& question
, const ComboAddress
& fromaddr
, const ComboAddress
& destaddr
, ComboAddress source
, ComboAddress destination
, const ComboAddress
& mappedSource
, struct timeval tval
, int fileDesc
, std::vector
<ProxyProtocolValue
>& proxyProtocolValues
, RecEventTrace
& eventTrace
) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
2098 ++(RecThreadInfo::self().numberOfDistributedQueries
);
2099 gettimeofday(&g_now
, nullptr);
2100 if (tval
.tv_sec
!= 0) {
2101 struct timeval diff
= g_now
- tval
;
2102 double delta
= (static_cast<double>(diff
.tv_sec
) * 1000 + static_cast<double>(diff
.tv_usec
) / 1000.0);
2104 if (delta
> 1000.0) {
2105 t_Counters
.at(rec::Counter::tooOldDrops
)++;
2110 ++t_Counters
.at(rec::Counter::qcounter
);
2112 if (fromaddr
.sin4
.sin_family
== AF_INET6
) {
2113 t_Counters
.at(rec::Counter::ipv6qcounter
)++;
2117 const dnsheader_aligned
headerdata(question
.data());
2118 const dnsheader
* dnsheader
= headerdata
.get();
2119 unsigned int ctag
= 0;
2121 bool needECS
= false;
2122 std::unordered_set
<std::string
> policyTags
;
2123 std::map
<std::string
, RecursorLua4::MetaValue
> meta
;
2124 LuaContext::LuaObject data
;
2129 bool logQuery
= false;
2130 bool logResponse
= false;
2131 boost::uuids::uuid uniqueId
{};
2132 auto luaconfsLocal
= g_luaconfs
.getLocal();
2133 const auto pbExport
= checkProtobufExport(luaconfsLocal
);
2134 const auto outgoingbExport
= checkOutgoingProtobufExport(luaconfsLocal
);
2135 if (pbExport
|| outgoingbExport
) {
2139 uniqueId
= getUniqueID();
2141 logQuery
= t_protobufServers
.servers
&& luaconfsLocal
->protobufExportConfig
.logQueries
;
2142 logResponse
= t_protobufServers
.servers
&& luaconfsLocal
->protobufExportConfig
.logResponses
;
2144 checkFrameStreamExport(luaconfsLocal
, luaconfsLocal
->frameStreamExportConfig
, t_frameStreamServersInfo
);
2146 EDNSSubnetOpts ednssubnet
;
2147 bool ecsFound
= false;
2148 bool ecsParsed
= false;
2149 std::vector
<DNSRecord
> records
;
2150 std::string extendedErrorExtra
;
2151 boost::optional
<int> rcode
= boost::none
;
2152 boost::optional
<uint16_t> extendedErrorCode
{boost::none
};
2153 uint32_t ttlCap
= std::numeric_limits
<uint32_t>::max();
2154 bool variable
= false;
2155 bool followCNAMEs
= false;
2156 bool responsePaddingDisabled
= false;
2160 uint16_t qclass
= 0;
2161 bool qnameParsed
= false;
2164 static uint64_t last=0;
2166 g_mtracer->clearAllocators();
2167 cout<<g_mtracer->getAllocs()-last<<" "<<g_mtracer->getNumOut()<<" -- BEGIN TRACE"<<endl;
2168 last=g_mtracer->getAllocs();
2169 cout<<g_mtracer->topAllocatorsString()<<endl;
2170 g_mtracer->clearAllocators();
2174 // We do not have a SyncRes specific Lua context at this point yet, so ok to use t_pdl
2175 if (needECS
|| (t_pdl
&& (t_pdl
->d_gettag
|| t_pdl
->d_gettag_ffi
)) || dnsheader
->opcode
== static_cast<unsigned>(Opcode::Notify
)) {
2177 EDNSOptionViewMap ednsOptions
;
2181 getQNameAndSubnet(question
, &qname
, &qtype
, &qclass
,
2182 ecsFound
, &ednssubnet
, g_gettagNeedsEDNSOptions
? &ednsOptions
: nullptr);
2189 if (t_pdl
->d_gettag_ffi
) {
2190 RecursorLua4::FFIParams
params(qname
, qtype
, destination
, source
, ednssubnet
.source
, data
, policyTags
, records
, ednsOptions
, proxyProtocolValues
, requestorId
, deviceId
, deviceName
, routingTag
, rcode
, ttlCap
, variable
, false, logQuery
, logResponse
, followCNAMEs
, extendedErrorCode
, extendedErrorExtra
, responsePaddingDisabled
, meta
);
2192 eventTrace
.add(RecEventTrace::LuaGetTagFFI
);
2193 ctag
= t_pdl
->gettag_ffi(params
);
2194 eventTrace
.add(RecEventTrace::LuaGetTagFFI
, ctag
, false);
2196 else if (t_pdl
->d_gettag
) {
2197 eventTrace
.add(RecEventTrace::LuaGetTag
);
2198 ctag
= t_pdl
->gettag(source
, ednssubnet
.source
, destination
, qname
, qtype
, &policyTags
, data
, ednsOptions
, false, requestorId
, deviceId
, deviceName
, routingTag
, proxyProtocolValues
);
2199 eventTrace
.add(RecEventTrace::LuaGetTag
, ctag
, false);
2202 catch (const std::exception
& e
) {
2203 if (g_logCommonErrors
) {
2204 SLOG(g_log
<< Logger::Warning
<< "Error parsing a query packet qname='" << qname
<< "' for tag determination, setting tag=0: " << e
.what() << endl
,
2205 g_slogudpin
->error(Logr::Warning
, e
.what(), "Error parsing a query packet for tag determination, setting tag=0", "qname", Logging::Loggable(qname
), "remote", Logging::Loggable(fromaddr
), "exception", Logging::Loggable("std;:exception")));
2210 catch (const std::exception
& e
) {
2211 if (g_logCommonErrors
) {
2212 SLOG(g_log
<< Logger::Warning
<< "Error parsing a query packet for tag determination, setting tag=0: " << e
.what() << endl
,
2213 g_slogudpin
->error(Logr::Warning
, e
.what(), "Error parsing a query packet for tag determination, setting tag=0", "remote", Logging::Loggable(fromaddr
), "exception", Logging::Loggable("std;:exception")));
2218 RecursorPacketCache::OptPBData pbData
{boost::none
};
2219 if (t_protobufServers
.servers
) {
2220 if (logQuery
&& !(luaconfsLocal
->protobufExportConfig
.taggedOnly
&& policyTags
.empty())) {
2221 protobufLogQuery(luaconfsLocal
, uniqueId
, source
, destination
, mappedSource
, ednssubnet
.source
, false, dnsheader
->id
, question
.size(), qname
, qtype
, qclass
, policyTags
, requestorId
, deviceId
, deviceName
, meta
);
2225 if (ctag
== 0 && !responsePaddingDisabled
&& g_paddingFrom
.match(fromaddr
)) {
2226 ctag
= g_paddingTag
;
2229 if (dnsheader
->opcode
== static_cast<unsigned>(Opcode::Query
)) {
2230 /* It might seem like a good idea to skip the packet cache lookup if we know that the answer is not cacheable,
2231 but it means that the hash would not be computed. If some script decides at a later time to mark back the answer
2232 as cacheable we would cache it with a wrong tag, so better safe than sorry. */
2233 eventTrace
.add(RecEventTrace::PCacheCheck
);
2234 bool cacheHit
= checkForCacheHit(qnameParsed
, ctag
, question
, qname
, qtype
, qclass
, g_now
, response
, qhash
, pbData
, false, source
, mappedSource
);
2235 eventTrace
.add(RecEventTrace::PCacheCheck
, cacheHit
, false);
2238 SLOG(g_log
<< Logger::Notice
<< RecThreadInfo::id() << " question answered from packet cache tag=" << ctag
<< " from " << source
.toStringWithPort() << (source
!= fromaddr
? " (via " + fromaddr
.toStringWithPort() + ")" : "") << endl
,
2239 g_slogudpin
->info(Logr::Notice
, "Question answered from packet cache", "tag", Logging::Loggable(ctag
),
2240 "qname", Logging::Loggable(qname
), "qtype", Logging::Loggable(QType(qtype
)),
2241 "source", Logging::Loggable(source
), "remote", Logging::Loggable(fromaddr
)));
2249 cmsgbuf_aligned cbuf
{};
2250 fillMSGHdr(&msgh
, &iov
, &cbuf
, 0, reinterpret_cast<char*>(response
.data()), response
.length(), const_cast<ComboAddress
*>(&fromaddr
)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-type-const-cast)
2251 msgh
.msg_control
= nullptr;
2253 if (g_fromtosockets
.count(fileDesc
) != 0) {
2254 addCMsgSrcAddr(&msgh
, &cbuf
, &destaddr
, 0);
2256 int sendErr
= sendOnNBSocket(fileDesc
, &msgh
);
2257 eventTrace
.add(RecEventTrace::AnswerSent
);
2259 if (t_protobufServers
.servers
&& logResponse
&& (!luaconfsLocal
->protobufExportConfig
.taggedOnly
|| !pbData
|| pbData
->d_tagged
)) {
2260 protobufLogResponse(dnsheader
, luaconfsLocal
, pbData
, tval
, false, source
, destination
, mappedSource
, ednssubnet
, uniqueId
, requestorId
, deviceId
, deviceName
, meta
, eventTrace
, policyTags
);
2263 if (eventTrace
.enabled() && (SyncRes::s_event_trace_enabled
& SyncRes::event_trace_to_log
) != 0) {
2264 SLOG(g_log
<< Logger::Info
<< eventTrace
.toString() << endl
,
2265 g_slogudpin
->info(Logr::Info
, eventTrace
.toString())); // Do we want more fancy logging here?
2267 if (sendErr
!= 0 && g_logCommonErrors
) {
2268 SLOG(g_log
<< Logger::Warning
<< "Sending UDP reply to client " << source
.toStringWithPort()
2269 << (source
!= fromaddr
? " (via " + fromaddr
.toStringWithPort() + ")" : "") << " failed with: "
2270 << stringerror(sendErr
) << endl
,
2271 g_slogudpin
->error(Logr::Error
, sendErr
, "Sending UDP reply to client failed", "source", Logging::Loggable(source
), "remote", Logging::Loggable(fromaddr
)));
2276 Utility::gettimeofday(&now
, nullptr);
2277 uint64_t spentUsec
= uSec(now
- tval
);
2278 t_Counters
.at(rec::Histogram::cumulativeAnswers
)(spentUsec
);
2279 t_Counters
.updateSnap(g_regressionTestMode
);
2284 catch (const std::exception
& e
) {
2285 if (g_logCommonErrors
) {
2286 SLOG(g_log
<< Logger::Error
<< "Error processing or aging answer packet: " << e
.what() << endl
,
2287 g_slogudpin
->error(Logr::Error
, e
.what(), "Error processing or aging answer packet", "exception", Logging::Loggable("std::exception")));
2293 bool ipf
= t_pdl
->ipfilter(source
, destination
, *dnsheader
, eventTrace
);
2296 SLOG(g_log
<< Logger::Notice
<< RecThreadInfo::id() << " [" << g_multiTasker
->getTid() << "/" << g_multiTasker
->numProcesses() << "] DROPPED question from " << source
.toStringWithPort() << (source
!= fromaddr
? " (via " + fromaddr
.toStringWithPort() + ")" : "") << " based on policy" << endl
,
2297 g_slogudpin
->info(Logr::Notice
, "Dropped question based on policy", "source", Logging::Loggable(source
), "remote", Logging::Loggable(fromaddr
)));
2299 t_Counters
.at(rec::Counter::policyDrops
)++;
2304 if (dnsheader
->opcode
== static_cast<unsigned>(Opcode::Notify
)) {
2305 if (!isAllowNotifyForZone(qname
)) {
2307 SLOG(g_log
<< Logger::Error
<< "[" << g_multiTasker
->getTid() << "] dropping UDP NOTIFY from " << source
.toStringWithPort() << (source
!= fromaddr
? " (via " + fromaddr
.toStringWithPort() + ")" : "") << ", for " << qname
.toLogString() << ", zone not matched by allow-notify-for" << endl
,
2308 g_slogudpin
->info(Logr::Notice
, "Dropping UDP NOTIFY, zone not matched by allow-notify-for", "source", Logging::Loggable(source
), "remote", Logging::Loggable(fromaddr
)));
2311 t_Counters
.at(rec::Counter::zoneDisallowedNotify
)++;
2316 SLOG(g_log
<< Logger::Notice
<< RecThreadInfo::id() << " got NOTIFY for " << qname
.toLogString() << " from " << source
.toStringWithPort() << (source
!= fromaddr
? " (via " + fromaddr
.toStringWithPort() + ")" : "") << endl
,
2317 g_slogudpin
->info(Logr::Notice
, "Got NOTIFY", "source", Logging::Loggable(source
), "remote", Logging::Loggable(fromaddr
), "qname", Logging::Loggable(qname
)));
2320 requestWipeCaches(qname
);
2322 // the operation will now be treated as a Query, generating
2323 // a normal response, as the rest of the code does not
2324 // check dh->opcode, but we need to ensure that the response
2325 // to this request does not get put into the packet cache
2329 if (g_multiTasker
->numProcesses() > g_maxMThreads
) {
2331 SLOG(g_log
<< Logger::Notice
<< RecThreadInfo::id() << " [" << g_multiTasker
->getTid() << "/" << g_multiTasker
->numProcesses() << "] DROPPED question from " << source
.toStringWithPort() << (source
!= fromaddr
? " (via " + fromaddr
.toStringWithPort() + ")" : "") << ", over capacity" << endl
,
2332 g_slogudpin
->info(Logr::Notice
, "Dropped question, over capacity", "source", Logging::Loggable(source
), "remote", Logging::Loggable(fromaddr
)));
2334 t_Counters
.at(rec::Counter::overCapacityDrops
)++;
2338 auto comboWriter
= std::make_unique
<DNSComboWriter
>(question
, g_now
, std::move(policyTags
), t_pdl
, std::move(data
), std::move(records
));
2340 comboWriter
->setSocket(fileDesc
);
2341 comboWriter
->d_tag
= ctag
;
2342 comboWriter
->d_qhash
= qhash
;
2343 comboWriter
->setRemote(fromaddr
); // the address the query is coming from
2344 comboWriter
->setSource(source
); // the address we assume the query is coming from, might be set by proxy protocol
2345 comboWriter
->setLocal(destaddr
); // the address the query was received on
2346 comboWriter
->setDestination(destination
); // the address we assume the query is sent to, might be set by proxy protocol
2347 comboWriter
->setMappedSource(mappedSource
); // the address we assume the query is coming from. Differs from source if table-based mapping has been applied
2348 comboWriter
->d_tcp
= false;
2349 comboWriter
->d_ecsFound
= ecsFound
;
2350 comboWriter
->d_ecsParsed
= ecsParsed
;
2351 comboWriter
->d_ednssubnet
= ednssubnet
;
2352 comboWriter
->d_ttlCap
= ttlCap
;
2353 comboWriter
->d_variable
= variable
;
2354 comboWriter
->d_followCNAMERecords
= followCNAMEs
;
2355 comboWriter
->d_rcode
= rcode
;
2356 comboWriter
->d_logResponse
= logResponse
;
2357 if (t_protobufServers
.servers
|| t_outgoingProtobufServers
.servers
) {
2358 comboWriter
->d_uuid
= uniqueId
;
2360 comboWriter
->d_requestorId
= requestorId
;
2361 comboWriter
->d_deviceId
= deviceId
;
2362 comboWriter
->d_deviceName
= deviceName
;
2363 comboWriter
->d_kernelTimestamp
= tval
;
2364 comboWriter
->d_proxyProtocolValues
= std::move(proxyProtocolValues
);
2365 comboWriter
->d_routingTag
= std::move(routingTag
);
2366 comboWriter
->d_extendedErrorCode
= extendedErrorCode
;
2367 comboWriter
->d_extendedErrorExtra
= std::move(extendedErrorExtra
);
2368 comboWriter
->d_responsePaddingDisabled
= responsePaddingDisabled
;
2369 comboWriter
->d_meta
= std::move(meta
);
2371 comboWriter
->d_eventTrace
= std::move(eventTrace
);
2372 g_multiTasker
->makeThread(startDoResolve
, (void*)comboWriter
.release()); // deletes dc
2377 static void handleNewUDPQuestion(int fileDesc
, FDMultiplexer::funcparam_t
& /* var */) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
2380 static const size_t maxIncomingQuerySize
= g_proxyProtocolACL
.empty() ? 512 : (512 + g_proxyProtocolMaximumSize
);
2381 static thread_local
std::string data
;
2382 ComboAddress fromaddr
; // the address the query is coming from
2383 ComboAddress source
; // the address we assume the query is coming from, might be set by proxy protocol
2384 ComboAddress destination
; // the address we assume the query was sent to, might be set by proxy protocol
2391 cmsgbuf_aligned cbuf
;
2392 bool firstQuery
= true;
2393 std::vector
<ProxyProtocolValue
> proxyProtocolValues
;
2394 RecEventTrace eventTrace
;
2396 for (size_t queriesCounter
= 0; queriesCounter
< g_maxUDPQueriesPerRound
; queriesCounter
++) {
2397 bool proxyProto
= false;
2398 proxyProtocolValues
.clear();
2399 data
.resize(maxIncomingQuerySize
);
2400 fromaddr
.sin6
.sin6_family
= AF_INET6
; // this makes sure fromaddr is big enough
2401 fillMSGHdr(&msgh
, &iov
, &cbuf
, sizeof(cbuf
), data
.data(), data
.size(), &fromaddr
);
2403 if ((len
= recvmsg(fileDesc
, &msgh
, 0)) >= 0) {
2405 eventTrace
.setEnabled(SyncRes::s_event_trace_enabled
!= 0);
2406 eventTrace
.add(RecEventTrace::ReqRecv
);
2410 if ((msgh
.msg_flags
& MSG_TRUNC
) != 0) {
2411 t_Counters
.at(rec::Counter::truncatedDrops
)++;
2413 SLOG(g_log
<< Logger::Error
<< "Ignoring truncated query from " << fromaddr
.toString() << endl
,
2414 g_slogudpin
->info(Logr::Error
, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr
)));
2419 data
.resize(static_cast<size_t>(len
));
2421 if (expectProxyProtocol(fromaddr
)) {
2423 ssize_t used
= parseProxyHeader(data
, proxyProto
, source
, destination
, tcp
, proxyProtocolValues
);
2425 ++t_Counters
.at(rec::Counter::proxyProtocolInvalidCount
);
2427 SLOG(g_log
<< Logger::Error
<< "Ignoring invalid proxy protocol (" << std::to_string(len
) << ", " << std::to_string(used
) << ") query from " << fromaddr
.toStringWithPort() << endl
,
2428 g_slogudpin
->info(Logr::Error
, "Ignoring invalid proxy protocol query", "length", Logging::Loggable(len
),
2429 "used", Logging::Loggable(used
), "remote", Logging::Loggable(fromaddr
)));
2433 if (static_cast<size_t>(used
) > g_proxyProtocolMaximumSize
) {
2435 SLOG(g_log
<< Logger::Error
<< "Proxy protocol header in UDP packet from " << fromaddr
.toStringWithPort() << " is larger than proxy-protocol-maximum-size (" << used
<< "), dropping" << endl
,
2436 g_slogudpin
->info(Logr::Error
, "Proxy protocol header in UDP packet is larger than proxy-protocol-maximum-size",
2437 "used", Logging::Loggable(used
), "remote", Logging::Loggable(fromaddr
)));
2439 ++t_Counters
.at(rec::Counter::proxyProtocolInvalidCount
);
2443 data
.erase(0, used
);
2445 else if (len
> 512) {
2446 /* we only allow UDP packets larger than 512 for those with a proxy protocol header */
2447 t_Counters
.at(rec::Counter::truncatedDrops
)++;
2449 SLOG(g_log
<< Logger::Error
<< "Ignoring truncated query from " << fromaddr
.toStringWithPort() << endl
,
2450 g_slogudpin
->info(Logr::Error
, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr
)));
2455 if (data
.size() < sizeof(dnsheader
)) {
2456 t_Counters
.at(rec::Counter::ignoredCount
)++;
2458 SLOG(g_log
<< Logger::Error
<< "Ignoring too-short (" << std::to_string(data
.size()) << ") query from " << fromaddr
.toString() << endl
,
2459 g_slogudpin
->info(Logr::Error
, "Ignoring too-short query", "length", Logging::Loggable(data
.size()),
2460 "remote", Logging::Loggable(fromaddr
)));
2468 ComboAddress mappedSource
= source
;
2469 if (t_proxyMapping
) {
2470 if (const auto* iter
= t_proxyMapping
->lookup(source
)) {
2471 mappedSource
= iter
->second
.address
;
2472 ++iter
->second
.stats
.netmaskMatches
;
2476 t_remotes
->push_back(fromaddr
);
2479 if (t_allowFrom
&& !t_allowFrom
->match(&mappedSource
)) {
2481 SLOG(g_log
<< Logger::Error
<< "[" << g_multiTasker
->getTid() << "] dropping UDP query from " << mappedSource
.toString() << ", address not matched by allow-from" << endl
,
2482 g_slogudpin
->info(Logr::Error
, "Dropping UDP query, address not matched by allow-from", "source", Logging::Loggable(mappedSource
)));
2485 t_Counters
.at(rec::Counter::unauthorizedUDP
)++;
2489 BOOST_STATIC_ASSERT(offsetof(sockaddr_in
, sin_port
) == offsetof(sockaddr_in6
, sin6_port
));
2490 if (fromaddr
.sin4
.sin_port
== 0) { // also works for IPv6
2492 SLOG(g_log
<< Logger::Error
<< "[" << g_multiTasker
->getTid() << "] dropping UDP query from " << fromaddr
.toStringWithPort() << ", can't deal with port 0" << endl
,
2493 g_slogudpin
->info(Logr::Error
, "Dropping UDP query can't deal with port 0", "remote", Logging::Loggable(fromaddr
)));
2496 t_Counters
.at(rec::Counter::clientParseError
)++; // not quite the best place to put it, but needs to go somewhere
2501 const dnsheader_aligned
headerdata(data
.data());
2502 const dnsheader
* dnsheader
= headerdata
.get();
2504 if (dnsheader
->qr
) {
2505 t_Counters
.at(rec::Counter::ignoredCount
)++;
2506 if (g_logCommonErrors
) {
2507 SLOG(g_log
<< Logger::Error
<< "Ignoring answer from " << fromaddr
.toString() << " on server socket!" << endl
,
2508 g_slogudpin
->info(Logr::Error
, "Ignoring answer on server socket", "remote", Logging::Loggable(fromaddr
)));
2511 else if (dnsheader
->opcode
!= static_cast<unsigned>(Opcode::Query
) && dnsheader
->opcode
!= static_cast<unsigned>(Opcode::Notify
)) {
2512 t_Counters
.at(rec::Counter::ignoredCount
)++;
2513 if (g_logCommonErrors
) {
2514 SLOG(g_log
<< Logger::Error
<< "Ignoring unsupported opcode " << Opcode::to_s(dnsheader
->opcode
) << " from " << fromaddr
.toString() << " on server socket!" << endl
,
2515 g_slogudpin
->info(Logr::Error
, "Ignoring unsupported opcode server socket", "remote", Logging::Loggable(fromaddr
), "opcode", Logging::Loggable(Opcode::to_s(dnsheader
->opcode
))));
2518 else if (dnsheader
->qdcount
== 0U) {
2519 t_Counters
.at(rec::Counter::emptyQueriesCount
)++;
2520 if (g_logCommonErrors
) {
2521 SLOG(g_log
<< Logger::Error
<< "Ignoring empty (qdcount == 0) query from " << fromaddr
.toString() << " on server socket!" << endl
,
2522 g_slogudpin
->info(Logr::Error
, "Ignoring empty (qdcount == 0) query on server socket!", "remote", Logging::Loggable(fromaddr
)));
2526 if (dnsheader
->opcode
== static_cast<unsigned>(Opcode::Notify
)) {
2527 if (!t_allowNotifyFrom
|| !t_allowNotifyFrom
->match(&mappedSource
)) {
2529 SLOG(g_log
<< Logger::Error
<< "[" << g_multiTasker
->getTid() << "] dropping UDP NOTIFY from " << mappedSource
.toString() << ", address not matched by allow-notify-from" << endl
,
2530 g_slogudpin
->info(Logr::Error
, "Dropping UDP NOTIFY from address not matched by allow-notify-from",
2531 "source", Logging::Loggable(mappedSource
)));
2534 t_Counters
.at(rec::Counter::sourceDisallowedNotify
)++;
2539 struct timeval tval
= {0, 0};
2540 HarvestTimestamp(&msgh
, &tval
);
2541 ComboAddress destaddr
; // the address the query was sent to to
2542 destaddr
.reset(); // this makes sure we ignore this address if not returned by recvmsg above
2543 const auto* loc
= rplookup(g_listenSocketsAddresses
, fileDesc
);
2544 if (HarvestDestinationAddress(&msgh
, &destaddr
)) {
2545 // but.. need to get port too
2546 if (loc
!= nullptr) {
2547 destaddr
.sin4
.sin_port
= loc
->sin4
.sin_port
;
2551 if (loc
!= nullptr) {
2555 destaddr
.sin4
.sin_family
= fromaddr
.sin4
.sin_family
;
2556 socklen_t slen
= destaddr
.getSocklen();
2557 getsockname(fileDesc
, reinterpret_cast<sockaddr
*>(&destaddr
), &slen
); // if this fails, we're ok with it // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2561 destination
= destaddr
;
2564 if (RecThreadInfo::weDistributeQueries()) {
2565 std::string localdata
= data
;
2566 distributeAsyncFunction(data
, [localdata
, fromaddr
, destaddr
, source
, destination
, mappedSource
, tval
, fileDesc
, proxyProtocolValues
, eventTrace
]() mutable {
2567 return doProcessUDPQuestion(localdata
, fromaddr
, destaddr
, source
, destination
, mappedSource
, tval
, fileDesc
, proxyProtocolValues
, eventTrace
);
2571 doProcessUDPQuestion(data
, fromaddr
, destaddr
, source
, destination
, mappedSource
, tval
, fileDesc
, proxyProtocolValues
, eventTrace
);
2575 catch (const MOADNSException
& mde
) {
2576 t_Counters
.at(rec::Counter::clientParseError
)++;
2577 if (g_logCommonErrors
) {
2578 SLOG(g_log
<< Logger::Error
<< "Unable to parse packet from remote UDP client " << fromaddr
.toString() << ": " << mde
.what() << endl
,
2579 g_slogudpin
->error(Logr::Error
, mde
.what(), "Unable to parse packet from remote UDP client", "remote", Logging::Loggable(fromaddr
), "exception", Logging::Loggable("MOADNSException")));
2582 catch (const std::runtime_error
& e
) {
2583 t_Counters
.at(rec::Counter::clientParseError
)++;
2584 if (g_logCommonErrors
) {
2585 SLOG(g_log
<< Logger::Error
<< "Unable to parse packet from remote UDP client " << fromaddr
.toString() << ": " << e
.what() << endl
,
2586 g_slogudpin
->error(Logr::Error
, e
.what(), "Unable to parse packet from remote UDP client", "remote", Logging::Loggable(fromaddr
), "exception", Logging::Loggable("std::runtime_error")));
2591 // cerr<<t_id<<" had error: "<<stringerror()<<endl;
2592 if (firstQuery
&& errno
== EAGAIN
) {
2593 t_Counters
.at(rec::Counter::noPacketError
)++;
2599 t_Counters
.updateSnap(g_regressionTestMode
);
2602 void makeUDPServerSockets(deferredAdd_t
& deferredAdds
, Logr::log_t log
)
2605 vector
<string
> localAddresses
;
2606 stringtok(localAddresses
, ::arg()["local-address"], " ,");
2608 if (localAddresses
.empty()) {
2609 throw PDNSException("No local address specified");
2612 const uint16_t defaultLocalPort
= ::arg().asNum("local-port");
2613 for (const auto& localAddress
: localAddresses
) {
2614 ComboAddress address
{localAddress
, defaultLocalPort
};
2615 const int socketFd
= socket(address
.sin4
.sin_family
, SOCK_DGRAM
, 0);
2617 throw PDNSException("Making a UDP server socket for resolver: " + stringerror());
2619 if (!setSocketTimestamps(socketFd
)) {
2620 SLOG(g_log
<< Logger::Warning
<< "Unable to enable timestamp reporting for socket" << endl
,
2621 log
->info(Logr::Warning
, "Unable to enable timestamp reporting for socket"));
2623 if (IsAnyAddress(address
)) {
2624 if (address
.sin4
.sin_family
== AF_INET
) {
2625 if (setsockopt(socketFd
, IPPROTO_IP
, GEN_IP_PKTINFO
, &one
, sizeof(one
)) == 0) { // linux supports this, so why not - might fail on other systems
2626 g_fromtosockets
.insert(socketFd
);
2629 #ifdef IPV6_RECVPKTINFO
2630 if (address
.sin4
.sin_family
== AF_INET6
) {
2631 if (setsockopt(socketFd
, IPPROTO_IPV6
, IPV6_RECVPKTINFO
, &one
, sizeof(one
)) == 0) {
2632 g_fromtosockets
.insert(socketFd
);
2636 if (address
.sin6
.sin6_family
== AF_INET6
&& setsockopt(socketFd
, IPPROTO_IPV6
, IPV6_V6ONLY
, &one
, sizeof(one
)) < 0) {
2638 SLOG(g_log
<< Logger::Error
<< "Failed to set IPv6 socket to IPv6 only, continuing anyhow: " << stringerror(err
) << endl
,
2639 log
->error(Logr::Error
, "Failed to set IPv6 socket to IPv6 only, continuing anyhow"));
2642 if (::arg().mustDo("non-local-bind")) {
2643 Utility::setBindAny(AF_INET6
, socketFd
);
2646 setCloseOnExec(socketFd
);
2649 setSocketReceiveBuffer(socketFd
, 250000);
2651 catch (const std::exception
& e
) {
2652 SLOG(g_log
<< Logger::Error
<< e
.what() << endl
,
2653 log
->error(Logr::Error
, e
.what(), "Exception while setting socket buffer size"));
2657 #if defined(SO_REUSEPORT_LB)
2659 SSetsockopt(socketFd
, SOL_SOCKET
, SO_REUSEPORT_LB
, 1);
2661 catch (const std::exception
& e
) {
2662 throw PDNSException(std::string("SO_REUSEPORT_LB: ") + e
.what());
2664 #elif defined(SO_REUSEPORT)
2666 SSetsockopt(socketFd
, SOL_SOCKET
, SO_REUSEPORT
, 1);
2668 catch (const std::exception
& e
) {
2669 throw PDNSException(std::string("SO_REUSEPORT: ") + e
.what());
2675 setSocketIgnorePMTU(socketFd
, address
.sin4
.sin_family
);
2677 catch (const std::exception
& e
) {
2678 SLOG(g_log
<< Logger::Warning
<< "Failed to set IP_MTU_DISCOVER on UDP server socket: " << e
.what() << endl
,
2679 log
->error(Logr::Warning
, e
.what(), "Failed to set IP_MTU_DISCOVER on UDP server socket"));
2682 socklen_t socklen
= address
.getSocklen();
2683 if (::bind(socketFd
, reinterpret_cast<struct sockaddr
*>(&address
), socklen
) < 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2684 throw PDNSException("Resolver binding to server socket on " + address
.toStringWithPort() + ": " + stringerror());
2687 setNonBlocking(socketFd
);
2689 deferredAdds
.emplace_back(socketFd
, handleNewUDPQuestion
);
2690 g_listenSocketsAddresses
[socketFd
] = address
; // this is written to only from the startup thread, not from the workers
2691 SLOG(g_log
<< Logger::Info
<< "Listening for UDP queries on " << address
.toStringWithPort() << endl
,
2692 log
->info(Logr::Info
, "Listening for queries", "proto", Logging::Loggable("UDP"), "address", Logging::Loggable(address
)));
2696 static bool trySendingQueryToWorker(unsigned int target
, ThreadMSG
* tmsg
)
2698 auto& targetInfo
= RecThreadInfo::info(target
);
2699 if (!targetInfo
.isWorker()) {
2700 SLOG(g_log
<< Logger::Error
<< "distributeAsyncFunction() tried to assign a query to a non-worker thread" << endl
,
2701 g_slog
->withName("runtime")->info(Logr::Error
, "distributeAsyncFunction() tried to assign a query to a non-worker thread"));
2705 const auto& tps
= targetInfo
.pipes
;
2707 ssize_t written
= write(tps
.writeQueriesToThread
, &tmsg
, sizeof(tmsg
)); // NOLINT: correct sizeof
2709 if (static_cast<size_t>(written
) != sizeof(tmsg
)) { // NOLINT: correct sizeof
2710 delete tmsg
; // NOLINT: pointer ownership
2711 unixDie("write to thread pipe returned wrong size or error");
2716 if (error
== EAGAIN
|| error
== EWOULDBLOCK
) {
2719 delete tmsg
; // NOLINT: pointer ownership
2720 unixDie("write to thread pipe returned wrong size or error:" + std::to_string(error
));
2726 static unsigned int getWorkerLoad(size_t workerIdx
)
2728 const auto* multiThreader
= RecThreadInfo::info(RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + workerIdx
).mt
;
2729 if (multiThreader
!= nullptr) {
2730 return multiThreader
->numProcesses();
2735 static unsigned int selectWorker(unsigned int hash
)
2737 assert(RecThreadInfo::numWorkers() != 0); // NOLINT: assert implementation
2738 if (g_balancingFactor
== 0) {
2739 return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + (hash
% RecThreadInfo::numWorkers());
2742 /* we start with one, representing the query we are currently handling */
2743 double currentLoad
= 1;
2744 std::vector
<unsigned int> load(RecThreadInfo::numWorkers());
2745 for (size_t idx
= 0; idx
< RecThreadInfo::numWorkers(); idx
++) {
2746 load
[idx
] = getWorkerLoad(idx
);
2747 currentLoad
+= load
[idx
];
2750 double targetLoad
= (currentLoad
/ RecThreadInfo::numWorkers()) * g_balancingFactor
;
2752 unsigned int worker
= hash
% RecThreadInfo::numWorkers();
2753 /* at least one server has to be at or below the average load */
2754 if (load
[worker
] > targetLoad
) {
2755 ++t_Counters
.at(rec::Counter::rebalancedQueries
);
2757 worker
= (worker
+ 1) % RecThreadInfo::numWorkers();
2758 } while (load
[worker
] > targetLoad
);
2761 return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + worker
;
2764 // This function is only called by the distributor threads, when pdns-distributes-queries is set
2765 void distributeAsyncFunction(const string
& packet
, const pipefunc_t
& func
)
2767 if (!RecThreadInfo::self().isDistributor()) {
2768 SLOG(g_log
<< Logger::Error
<< "distributeAsyncFunction() has been called by a worker (" << RecThreadInfo::id() << ")" << endl
,
2769 g_slog
->info(Logr::Error
, "distributeAsyncFunction() has been called by a worker")); // tid will be added
2773 bool hashOK
= false;
2774 unsigned int hash
= hashQuestion(reinterpret_cast<const uint8_t*>(packet
.data()), packet
.length(), g_disthashseed
, hashOK
); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2776 // hashQuestion does detect invalid names, so we might as well punt here instead of in the worker thread
2777 t_Counters
.at(rec::Counter::ignoredCount
)++;
2778 throw MOADNSException("too-short (" + std::to_string(packet
.length()) + ") or invalid name");
2780 unsigned int target
= selectWorker(hash
);
2782 ThreadMSG
* tmsg
= new ThreadMSG(); // NOLINT: pointer ownership
2784 tmsg
->wantAnswer
= false;
2786 if (!trySendingQueryToWorker(target
, tmsg
)) {
2787 /* if this function failed but did not raise an exception, it means that the pipe
2788 was full, let's try another one */
2789 unsigned int newTarget
= 0;
2791 newTarget
= RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + dns_random(RecThreadInfo::numWorkers());
2792 } while (newTarget
== target
);
2794 if (!trySendingQueryToWorker(newTarget
, tmsg
)) {
2795 t_Counters
.at(rec::Counter::queryPipeFullDrops
)++;
2796 delete tmsg
; // NOLINT: pointer ownership
2799 // coverity[leaked_storage]
2802 // resend event to everybody chained onto it
2803 static void doResends(MT_t::waiters_t::iterator
& iter
, const std::shared_ptr
<PacketID
>& resend
, const PacketBuffer
& content
)
2805 // We close the chain for new entries, since they won't be processed anyway
2806 iter
->key
->closed
= true;
2808 if (iter
->key
->chain
.empty()) {
2811 for (auto i
= iter
->key
->chain
.begin(); i
!= iter
->key
->chain
.end(); ++i
) {
2812 auto packetID
= std::make_shared
<PacketID
>(*resend
);
2815 g_multiTasker
->sendEvent(packetID
, &content
);
2816 t_Counters
.at(rec::Counter::chainResends
)++;
2820 static void handleUDPServerResponse(int fileDesc
, FDMultiplexer::funcparam_t
& var
)
2822 auto pid
= boost::any_cast
<std::shared_ptr
<PacketID
>>(var
);
2823 PacketBuffer packet
;
2824 packet
.resize(g_outgoingEDNSBufsize
);
2825 ComboAddress fromaddr
;
2826 socklen_t addrlen
= sizeof(fromaddr
);
2828 ssize_t len
= recvfrom(fileDesc
, &packet
.at(0), packet
.size(), 0, reinterpret_cast<sockaddr
*>(&fromaddr
), &addrlen
); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2830 const ssize_t signed_sizeof_sdnsheader
= sizeof(dnsheader
);
2833 // len < 0: error on socket
2834 t_udpclientsocks
->returnSocket(fileDesc
);
2837 MT_t::waiters_t::iterator iter
= g_multiTasker
->d_waiters
.find(pid
);
2838 if (iter
!= g_multiTasker
->d_waiters
.end()) {
2839 doResends(iter
, pid
, empty
);
2841 g_multiTasker
->sendEvent(pid
, &empty
); // this denotes error (does retry lookup using other NS)
2845 if (len
< signed_sizeof_sdnsheader
) {
2846 // We have received a packet that cannot be a valid DNS packet, as it has no complete header
2847 // Drop it, but continue to wait for other packets
2848 t_Counters
.at(rec::Counter::serverParseError
)++;
2849 if (g_logCommonErrors
) {
2850 SLOG(g_log
<< Logger::Error
<< "Unable to parse too short packet from remote UDP server " << fromaddr
.toString() << ": packet smaller than DNS header" << endl
,
2851 g_slogout
->info(Logr::Error
, "Unable to parse too short packet from remote UDP server", "from", Logging::Loggable(fromaddr
)));
2856 // We have at least a full header
2858 dnsheader dnsheader
{};
2859 memcpy(&dnsheader
, &packet
.at(0), sizeof(dnsheader
));
2861 auto pident
= std::make_shared
<PacketID
>();
2862 pident
->remote
= fromaddr
;
2863 pident
->id
= dnsheader
.id
;
2864 pident
->fd
= fileDesc
;
2866 if (!dnsheader
.qr
&& g_logCommonErrors
) {
2867 SLOG(g_log
<< Logger::Notice
<< "Not taking data from question on outgoing socket from " << fromaddr
.toStringWithPort() << endl
,
2868 g_slogout
->info(Logr::Error
, "Not taking data from question on outgoing socket", "from", Logging::Loggable(fromaddr
)));
2871 if (dnsheader
.qdcount
== 0U || // UPC, Nominum, very old BIND on FormErr, NSD
2872 dnsheader
.qr
== 0U) { // one weird server
2873 pident
->domain
.clear();
2878 if (len
> signed_sizeof_sdnsheader
) {
2879 pident
->domain
= DNSName(reinterpret_cast<const char*>(packet
.data()), static_cast<int>(len
), static_cast<int>(sizeof(dnsheader
)), false, &pident
->type
); // don't copy this from above - we need to do the actual read // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2882 // len == sizeof(dnsheader), only header case
2883 // We will do a full scan search later to see if we can match this reply even without a domain
2884 pident
->domain
.clear();
2888 catch (std::exception
& e
) {
2889 // Parse error, continue waiting for other packets
2890 t_Counters
.at(rec::Counter::serverParseError
)++; // won't be fed to lwres.cc, so we have to increment
2891 SLOG(g_log
<< Logger::Warning
<< "Error in packet from remote nameserver " << fromaddr
.toStringWithPort() << ": " << e
.what() << endl
,
2892 g_slogudpin
->error(Logr::Warning
, e
.what(), "Error in packet from remote nameserver", "from", Logging::Loggable(fromaddr
)));
2897 if (!pident
->domain
.empty()) {
2898 MT_t::waiters_t::iterator iter
= g_multiTasker
->d_waiters
.find(pident
);
2899 if (iter
!= g_multiTasker
->d_waiters
.end()) {
2900 doResends(iter
, pident
, packet
);
2906 if (pident
->domain
.empty() || g_multiTasker
->sendEvent(pident
, &packet
) == 0) {
2907 /* we did not find a match for this response, something is wrong */
2909 // we do a full scan for outstanding queries on unexpected answers. not too bad since we only accept them on the right port number, which is hard enough to guess
2910 for (const auto& d_waiter
: g_multiTasker
->d_waiters
) {
2911 if (pident
->fd
== d_waiter
.key
->fd
&& d_waiter
.key
->remote
== pident
->remote
&& d_waiter
.key
->type
== pident
->type
&& pident
->domain
== d_waiter
.key
->domain
) {
2912 /* we are expecting an answer from that exact source, on that exact port (since we are using connected sockets), for that qname/qtype,
2913 but with a different message ID. That smells like a spoofing attempt. For now we will just increase the counter and will deal with
2915 d_waiter
.key
->nearMisses
++;
2918 // be a bit paranoid here since we're weakening our matching
2919 if (pident
->domain
.empty() && !d_waiter
.key
->domain
.empty() && pident
->type
== 0 && d_waiter
.key
->type
!= 0 && pident
->id
== d_waiter
.key
->id
&& d_waiter
.key
->remote
== pident
->remote
) {
2920 // cerr<<"Empty response, rest matches though, sending to a waiter"<<endl;
2921 pident
->domain
= d_waiter
.key
->domain
;
2922 pident
->type
= d_waiter
.key
->type
;
2923 goto retryWithName
; // note that this only passes on an error, lwres will still reject the packet NOLINT(cppcoreguidelines-avoid-goto)
2926 t_Counters
.at(rec::Counter::unexpectedCount
)++; // if we made it here, it really is an unexpected answer
2927 if (g_logCommonErrors
) {
2928 SLOG(g_log
<< Logger::Warning
<< "Discarding unexpected packet from " << fromaddr
.toStringWithPort() << ": " << (pident
->domain
.empty() ? "<empty>" : pident
->domain
.toString()) << ", " << pident
->type
<< ", " << g_multiTasker
->d_waiters
.size() << " waiters" << endl
,
2929 g_slogudpin
->info(Logr::Warning
, "Discarding unexpected packet", "from", Logging::Loggable(fromaddr
),
2930 "qname", Logging::Loggable(pident
->domain
),
2931 "qtype", Logging::Loggable(QType(pident
->type
)),
2932 "waiters", Logging::Loggable(g_multiTasker
->d_waiters
.size())));
2935 else if (fileDesc
>= 0) {
2936 /* we either found a waiter (1) or encountered an issue (-1), it's up to us to clean the socket anyway */
2937 t_udpclientsocks
->returnSocket(fileDesc
);