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