]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/recursordist/pdns_recursor.cc
rec: Switch to `pdns::UniqueFilePtr`
[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 571 catch (const pdns::validation::TooManySEC3IterationsException& e) {
9d4a01ff 572 if (g_logCommonErrors || (g_dnssecLogBogus && resolver.getDNSSECLimitHit())) {
15e973d6
OM
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",
9d4a01ff 575 "policyName", Logging::Loggable(appliedPolicy.getName()), "exception", Logging::Loggable("TooManySEC3IterationsException"), "dnsseclimithit", Logging::Loggable(resolver.getDNSSECLimitHit())));
15e973d6
OM
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);
01807c64 870 auto filep = pdns::UniqueFilePtr(fdopen(traceFd, "a"));
42ae54e3
OM
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,
9d4a01ff 1285 resolver.d_slog->error(Logr::Notice, e.what(), "Sending SERVFAIL during resolve", "dnsseclimithit", Logging::Loggable(true)));
15e973d6
OM
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;
3bc38426 1351 if (comboWriter->d_luaContext->hasPostResolveFFIfunc()) {
e8e12e8c
OM
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));
9d4a01ff
OM
1406 if (resolver.getDNSSECLimitHit()) {
1407 log = log->withValues("dnsseclimithit", Logging::Loggable(true));
1408 }
a1b95336 1409 auto xdnssec = g_xdnssec.getLocal();
e8e12e8c 1410 if (xdnssec->check(comboWriter->d_mdp.d_qname)) {
78a8a91b 1411 log = log->withValues("in-x-dnssec-names", Logging::Loggable(1));
a1b95336
O
1412 x_marker = " [in x-dnssec-names]";
1413 }
2e921ec6 1414 }
8c1cb8aa 1415 if (state == vState::Secure) {
e8e12e8c
OM
1416 if (resolver.doLog()) {
1417 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 1418 log->info(Logr::Info, "Validates Correctly"));
2e921ec6 1419 }
a1b95336 1420
b25cae9a 1421 // Is the query source interested in the value of the ad-bit?
e8e12e8c
OM
1422 if (comboWriter->d_mdp.d_header.ad || DNSSECOK) {
1423 packetWriter.getHeader()->ad = 1;
1424 }
b25cae9a 1425 }
8c1cb8aa 1426 else if (state == vState::Insecure) {
e8e12e8c
OM
1427 if (resolver.doLog()) {
1428 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 1429 log->info(Logr::Info, "Validates as Insecure"));
12ce523e 1430 }
a1b95336 1431
e8e12e8c 1432 packetWriter.getHeader()->ad = 0;
f3fe4ae6 1433 }
fd870915 1434 else if (vStateIsBogus(state)) {
e8e12e8c
OM
1435 if (t_bogusremotes) {
1436 t_bogusremotes->push_back(comboWriter->d_source);
1437 }
1438 if (t_bogusqueryring) {
1439 t_bogusqueryring->push_back({comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype});
1440 }
1441 if (g_dnssecLogBogus || resolver.doLog() || g_dnssecmode == DNSSECMode::ValidateForLog) {
1442 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 1443 log->info(Logr::Notice, "Validates as Bogus"));
b25cae9a 1444 }
a1b95336 1445
b25cae9a 1446 // Does the query or validation mode sending out a SERVFAIL on validation errors?
e8e12e8c
OM
1447 if (!packetWriter.getHeader()->cd && (g_dnssecmode == DNSSECMode::ValidateAll || comboWriter->d_mdp.d_header.ad || DNSSECOK)) {
1448 if (resolver.doLog()) {
1449 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 1450 log->info(Logr::Notice, "Sending out SERVFAIL because recursor or query demands it for Bogus results"));
b25cae9a 1451 }
a1b95336 1452
e8e12e8c
OM
1453 packetWriter.getHeader()->rcode = RCode::ServFail;
1454 goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
8c1cb8aa
OM
1455 }
1456 else {
e8e12e8c
OM
1457 if (resolver.doLog()) {
1458 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 1459 log->info(Logr::Notice, "Sending out SERVFAIL because recursor or query demands it for Bogus results"));
b25cae9a 1460 }
1461 }
1462 }
1463 }
8c1cb8aa 1464 catch (const ImmediateServFailException& e) {
e8e12e8c
OM
1465 if (g_logCommonErrors) {
1466 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,
1467 resolver.d_slog->error(Logr::Notice, e.reason, "Sending SERVFAIL during validation", "exception", Logging::Loggable("ImmediateServFailException")));
1468 }
1469 goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
f3fe4ae6 1470 }
15e973d6 1471 catch (const pdns::validation::TooManySEC3IterationsException& e) {
9d4a01ff 1472 if (g_logCommonErrors || (g_dnssecLogBogus && resolver.getDNSSECLimitHit())) {
15e973d6 1473 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,
9d4a01ff 1474 resolver.d_slog->error(Logr::Notice, e.what(), "Sending SERVFAIL during validation", "exception", Logging::Loggable("TooManySEC3IterationsException"), "dnsseclimithit", Logging::Loggable(resolver.getDNSSECLimitHit())));
15e973d6
OM
1475 }
1476 goto sendit; // NOLINT(cppcoreguidelines-avoid-goto)
1477 }
b3f0ed10 1478 }
1479
e8e12e8c 1480 if (!ret.empty()) {
88da1249 1481 pdns::orderAndShuffle(ret, false);
e8e12e8c
OM
1482 if (auto listToSort = luaconfsLocal->sortlist.getOrderCmp(comboWriter->d_source)) {
1483 stable_sort(ret.begin(), ret.end(), *listToSort);
8c1cb8aa
OM
1484 variableAnswer = true;
1485 }
8e079f3a 1486 }
0afa32d4
RG
1487
1488 bool needCommit = false;
22577619 1489 for (const auto& record : ret) {
e8e12e8c 1490 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 1491 continue;
3e80ebce
KM
1492 }
1493
e8e12e8c 1494 if (!addRecordToPacket(packetWriter, record, minTTL, comboWriter->d_ttlCap, maxanswersize, seenAuthSOA)) {
97c6d7e5
RG
1495 needCommit = false;
1496 break;
1497 }
1498 needCommit = true;
1499
8c1cb8aa 1500 bool udr = false;
d58249ee 1501#ifdef NOD_ENABLED
8c1cb8aa 1502 if (g_udrEnabled) {
e8e12e8c 1503 udr = udrCheckUniqueDNSRecord(nodlogger, comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype, record);
9489e2b5 1504 if (!hasUDR && udr) {
ca2526f5 1505 hasUDR = true;
9489e2b5 1506 }
8c1cb8aa 1507 }
d58249ee 1508#endif /* NOD ENABLED */
41c542ec 1509
babe9430 1510 if (t_protobufServers.servers) {
4c0c4371
OM
1511 // Max size is 64k, but we're conservative here, as other fields are added after the answers have been added
1512 // If a single answer causes a too big protobuf message, it wil be dropped by queueData()
e8b59a65 1513 // But note addRR has code to prevent that
4c0c4371 1514 if (pbMessage.size() < std::numeric_limits<uint16_t>::max() / 2) {
e8e12e8c 1515 pbMessage.addRR(record, luaconfsLocal->protobufExportConfig.exportTypes, udr);
4c0c4371 1516 }
aa7929a3 1517 }
ea634573 1518 }
9489e2b5 1519 if (needCommit) {
e8e12e8c 1520 packetWriter.commit();
9489e2b5
CHB
1521 }
1522#ifdef NOD_ENABLED
1523#ifdef HAVE_FSTRM
1524 if (hasUDR) {
1525 if (isEnabledForUDRs(t_nodFrameStreamServersInfo.servers)) {
22577619
OM
1526 struct timespec timeSpec
1527 {
1528 };
9489e2b5 1529 std::string str;
e8e12e8c
OM
1530 if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
1531 TIMEVAL_TO_TIMESPEC(&comboWriter->d_kernelTimestamp, &timeSpec); // NOLINT
9489e2b5
CHB
1532 }
1533 else {
e8e12e8c 1534 TIMEVAL_TO_TIMESPEC(&comboWriter->d_now, &timeSpec); // NOLINT
9489e2b5 1535 }
94219a42
CHB
1536 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)
1537 str = message.getBuffer();
9489e2b5
CHB
1538 for (auto& logger : *(t_nodFrameStreamServersInfo.servers)) {
1539 if (logger->logUDRs()) {
1540 remoteLoggerQueueData(*logger, str);
1541 }
1542 }
1543 }
1544 }
1545#endif // HAVE_FSTRM
1546#endif // NOD_ENABLED
288f4aa9 1547 }
10321a98 1548 sendit:;
b3f0ed10 1549
e8e12e8c
OM
1550 if (g_useIncomingECS && comboWriter->d_ecsFound && !resolver.wasVariable() && !variableAnswer) {
1551 EDNSSubnetOpts ednsOptions;
1552 ednsOptions.source = comboWriter->d_ednssubnet.source;
1553 ComboAddress sourceAddr;
1554 sourceAddr.reset();
1555 sourceAddr.sin4.sin_family = ednsOptions.source.getNetwork().sin4.sin_family;
1556 ednsOptions.scope = Netmask(sourceAddr, 0);
1557 auto ecsPayload = makeEDNSSubnetOptsString(ednsOptions);
5a7f99b4 1558
f49ae8cf
RG
1559 // if we don't have enough space available let's just not set that scope of zero,
1560 // it will prevent some caching, mostly from dnsdist, but that's fine
e8e12e8c 1561 if (packetWriter.size() < maxanswersize && (maxanswersize - packetWriter.size()) >= (EDNSOptionCodeSize + EDNSOptionLengthSize + ecsPayload.size())) {
94e2a9b0 1562
f49ae8cf
RG
1563 maxanswersize -= EDNSOptionCodeSize + EDNSOptionLengthSize + ecsPayload.size();
1564
e32a8d46 1565 returnedEdnsOptions.emplace_back(EDNSOptionCode::ECS, std::move(ecsPayload));
f49ae8cf 1566 }
5a7f99b4 1567 }
1568
7917914b 1569 if (haveEDNS && addPaddingToResponse) {
e8e12e8c 1570 size_t currentSize = packetWriter.getSizeWithOpts(returnedEdnsOptions);
8ba588d0
RG
1571 /* we don't use maxawnswersize because it accounts for some EDNS options, but
1572 not all of them (for example ECS) */
1573 size_t maxSize = min(static_cast<uint16_t>(edo.d_packetsize >= 512 ? edo.d_packetsize : 512), g_udpTruncationThreshold);
1574
1575 if (currentSize < (maxSize - 4)) {
1576 size_t remaining = maxSize - (currentSize + 4);
53604c2a
RG
1577 /* from rfc8647, "4.1. Recommended Strategy: Block-Length Padding":
1578 If a server receives a query that includes the EDNS(0) "Padding"
1579 option, it MUST pad the corresponding response (see Section 4 of
1580 RFC 7830) and SHOULD pad the corresponding response to a
1581 multiple of 468 octets (see below).
1582 */
926c72f5 1583 const size_t blockSize = 468;
53604c2a
RG
1584 size_t modulo = (currentSize + 4) % blockSize;
1585 size_t padSize = 0;
1586 if (modulo > 0) {
1587 padSize = std::min(blockSize - modulo, remaining);
1588 }
e32a8d46 1589 returnedEdnsOptions.emplace_back(EDNSOptionCode::PADDING, makeEDNSPaddingOptString(padSize));
53604c2a
RG
1590 }
1591 }
1592
97c6d7e5 1593 if (haveEDNS) {
e8e12e8c
OM
1594 auto state = resolver.getValidationState();
1595 if (comboWriter->d_extendedErrorCode || resolver.d_extendedError || (SyncRes::s_addExtendedResolutionDNSErrors && vStateIsBogus(state))) {
1596 EDNSExtendedError::code code = EDNSExtendedError::code::Other;
e95b2a7c 1597 std::string extra;
94e2a9b0 1598
e8e12e8c
OM
1599 if (comboWriter->d_extendedErrorCode) {
1600 code = static_cast<EDNSExtendedError::code>(*comboWriter->d_extendedErrorCode);
1601 extra = std::move(comboWriter->d_extendedErrorExtra);
e95b2a7c 1602 }
e8e12e8c
OM
1603 else if (resolver.d_extendedError) {
1604 code = static_cast<EDNSExtendedError::code>(resolver.d_extendedError->infoCode);
1605 extra = std::move(resolver.d_extendedError->extraText);
3e5667f9 1606 }
e95b2a7c
RG
1607 else {
1608 switch (state) {
1609 case vState::BogusNoValidDNSKEY:
1610 code = EDNSExtendedError::code::DNSKEYMissing;
1611 break;
1612 case vState::BogusInvalidDenial:
1613 code = EDNSExtendedError::code::NSECMissing;
1614 break;
1615 case vState::BogusUnableToGetDSs:
1616 code = EDNSExtendedError::code::DNSSECBogus;
1617 break;
1618 case vState::BogusUnableToGetDNSKEYs:
1619 code = EDNSExtendedError::code::DNSKEYMissing;
1620 break;
1621 case vState::BogusSelfSignedDS:
1622 code = EDNSExtendedError::code::DNSSECBogus;
1623 break;
1624 case vState::BogusNoRRSIG:
1625 code = EDNSExtendedError::code::RRSIGsMissing;
1626 break;
1627 case vState::BogusNoValidRRSIG:
1628 code = EDNSExtendedError::code::DNSSECBogus;
1629 break;
1630 case vState::BogusMissingNegativeIndication:
1631 code = EDNSExtendedError::code::NSECMissing;
1632 break;
1633 case vState::BogusSignatureNotYetValid:
1634 code = EDNSExtendedError::code::SignatureNotYetValid;
1635 break;
1636 case vState::BogusSignatureExpired:
1637 code = EDNSExtendedError::code::SignatureExpired;
1638 break;
1639 case vState::BogusUnsupportedDNSKEYAlgo:
1640 code = EDNSExtendedError::code::UnsupportedDNSKEYAlgorithm;
1641 break;
1642 case vState::BogusUnsupportedDSDigestType:
1643 code = EDNSExtendedError::code::UnsupportedDSDigestType;
1644 break;
1645 case vState::BogusNoZoneKeyBitSet:
1646 code = EDNSExtendedError::code::NoZoneKeyBitSet;
1647 break;
1648 case vState::BogusRevokedDNSKEY:
e95b2a7c
RG
1649 case vState::BogusInvalidDNSKEYProtocol:
1650 code = EDNSExtendedError::code::DNSSECBogus;
1651 break;
1652 default:
1653 throw std::runtime_error("Bogus validation state not handled: " + vStateToString(state));
1654 }
94e2a9b0
RG
1655 }
1656
1657 EDNSExtendedError eee;
1658 eee.infoCode = static_cast<uint16_t>(code);
e95b2a7c
RG
1659 eee.extraText = std::move(extra);
1660
e8e12e8c 1661 if (packetWriter.size() < maxanswersize && (maxanswersize - packetWriter.size()) >= (EDNSOptionCodeSize + EDNSOptionLengthSize + sizeof(eee.infoCode) + eee.extraText.size())) {
e32a8d46 1662 returnedEdnsOptions.emplace_back(EDNSOptionCode::EXTENDEDERROR, makeEDNSExtendedErrorOptString(eee));
e95b2a7c 1663 }
94e2a9b0
RG
1664 }
1665
97c6d7e5
RG
1666 /* we try to add the EDNS OPT RR even for truncated answers,
1667 as rfc6891 states:
1668 "The minimal response MUST be the DNS header, question section, and an
1669 OPT record. This MUST also occur when a truncated response (using
1670 the DNS header's TC bit) is returned."
1671 */
e8e12e8c
OM
1672 packetWriter.addOpt(512, ednsExtRCode, DNSSECOK ? EDNSOpts::DNSSECOK : 0, returnedEdnsOptions);
1673 packetWriter.commit();
97c6d7e5
RG
1674 }
1675
e8e12e8c
OM
1676 t_Counters.at(rec::ResponseStats::responseStats).submitResponse(comboWriter->d_mdp.d_qtype, packet.size(), packetWriter.getHeader()->rcode);
1677 updateResponseStats(res, comboWriter->d_source, packet.size(), &comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype);
ff4d391d
NC
1678#ifdef NOD_ENABLED
1679 bool nod = false;
1680 if (g_nodEnabled) {
e8e12e8c 1681 if (nodCheckNewDomain(nodlogger, comboWriter->d_mdp.d_qname)) {
ff4d391d 1682 nod = true;
9489e2b5
CHB
1683#ifdef HAVE_FSTRM
1684 if (isEnabledForNODs(t_nodFrameStreamServersInfo.servers)) {
22577619
OM
1685 struct timespec timeSpec
1686 {
1687 };
9489e2b5 1688 std::string str;
e8e12e8c
OM
1689 if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
1690 TIMEVAL_TO_TIMESPEC(&comboWriter->d_kernelTimestamp, &timeSpec); // NOLINT
9489e2b5
CHB
1691 }
1692 else {
e8e12e8c 1693 TIMEVAL_TO_TIMESPEC(&comboWriter->d_now, &timeSpec); // NOLINT
9489e2b5 1694 }
94219a42
CHB
1695 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);
1696 str = message.getBuffer();
9489e2b5
CHB
1697
1698 for (auto& logger : *(t_nodFrameStreamServersInfo.servers)) {
1699 if (logger->logNODs()) {
1700 remoteLoggerQueueData(*logger, str);
1701 }
1702 }
1703 }
1704#endif // HAVE_FSTRM
3bd27e4a 1705 }
ff4d391d
NC
1706 }
1707#endif /* NOD_ENABLED */
0638d698 1708
e8e12e8c 1709 if (variableAnswer || resolver.wasVariable()) {
7d3d2f4f 1710 t_Counters.at(rec::Counter::variableResponses)++;
0638d698 1711 }
0fedcaf8 1712
e8e12e8c 1713 if (t_protobufServers.servers && !(luaconfsLocal->protobufExportConfig.taggedOnly && appliedPolicy.getName().empty() && comboWriter->d_policyTags.empty())) {
a44a8d66 1714 // Start constructing embedded DNSResponse object
e8e12e8c 1715 pbMessage.setResponseCode(packetWriter.getHeader()->rcode);
b502d522 1716 if (!appliedPolicy.getName().empty()) {
83b261a7
OM
1717 pbMessage.setAppliedPolicy(appliedPolicy.getName());
1718 pbMessage.setAppliedPolicyType(appliedPolicy.d_type);
ba8fe6b1
OM
1719 pbMessage.setAppliedPolicyTrigger(appliedPolicy.getTrigger());
1720 pbMessage.setAppliedPolicyHit(appliedPolicy.getHit());
b3c20df8 1721 pbMessage.setAppliedPolicyKind(appliedPolicy.d_kind);
0a273054 1722 }
83b261a7 1723 pbMessage.setInBytes(packet.size());
e8e12e8c 1724 pbMessage.setValidationState(resolver.getValidationState());
a89c3250 1725 // See if we want to store the policyTags into the PC
a4d0f523 1726 addPolicyTagsToPBMessageIfNeeded(*comboWriter, pbMessage);
a44a8d66 1727
47a6825e 1728 // Take s snap of the current protobuf buffer state to store in the PC
ac10822e 1729 pbDataForCache = boost::make_optional(RecursorPacketCache::PBData{
83b261a7
OM
1730 pbMessage.getMessageBuf(),
1731 pbMessage.getResponseBuf(),
e8e12e8c 1732 !appliedPolicy.getName().empty() || !comboWriter->d_policyTags.empty()});
47a6825e 1733#ifdef NOD_ENABLED
d78bcadc 1734 // if (g_udrEnabled) ??
83b261a7 1735 pbMessage.clearUDR(pbDataForCache->d_response);
47a6825e 1736#endif
0fedcaf8
O
1737 }
1738
e8e12e8c 1739 const bool intoPC = g_packetCache && !variableAnswer && !resolver.wasVariable();
2d46fe5d 1740 if (intoPC) {
e8e12e8c
OM
1741 minTTL = capPacketCacheTTL(*packetWriter.getHeader(), minTTL, seenAuthSOA);
1742 g_packetCache->insertResponsePacket(comboWriter->d_tag, comboWriter->d_qhash, std::move(comboWriter->d_query), comboWriter->d_mdp.d_qname,
1743 comboWriter->d_mdp.d_qtype, comboWriter->d_mdp.d_qclass,
22577619 1744 string(reinterpret_cast<const char*>(&*packet.begin()), packet.size()), // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
0638d698
O
1745 g_now.tv_sec,
1746 minTTL,
e8e12e8c
OM
1747 dnsQuestion.validationState,
1748 std::move(pbDataForCache), comboWriter->d_tcp);
0638d698 1749 }
b5676bc1
OM
1750
1751 if (g_regressionTestMode) {
1752 t_Counters.updateSnap(g_regressionTestMode);
1753 }
1754
e8e12e8c 1755 if (!comboWriter->d_tcp) {
22577619
OM
1756 struct msghdr msgh
1757 {
1758 };
1759 struct iovec iov
1760 {
1761 };
e8e12e8c 1762 cmsgbuf_aligned cbuf{};
22577619 1763 fillMSGHdr(&msgh, &iov, &cbuf, 0, reinterpret_cast<char*>(&*packet.begin()), packet.size(), &comboWriter->d_remote); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
e8e12e8c 1764 msgh.msg_control = nullptr;
0638d698 1765
e8e12e8c
OM
1766 if (g_fromtosockets.count(comboWriter->d_socket) > 0) {
1767 addCMsgSrcAddr(&msgh, &cbuf, &comboWriter->d_local, 0);
0638d698 1768 }
e8e12e8c
OM
1769 int sendErr = sendOnNBSocket(comboWriter->d_socket, &msgh);
1770 if (sendErr != 0 && g_logCommonErrors) {
1771 SLOG(g_log << Logger::Warning << "Sending UDP reply to client " << comboWriter->getRemote() << " failed with: "
1772 << stringerror(sendErr) << endl,
91092a9f 1773 g_slogudpin->error(Logr::Warning, sendErr, "Sending UDP reply to client failed"));
0638d698 1774 }
0638d698
O
1775 }
1776 else {
e8e12e8c
OM
1777 bool hadError = sendResponseOverTCP(comboWriter, packet);
1778 finishTCPReply(comboWriter, hadError, true);
6b425e61 1779 tcpGuard.setHandled();
0638d698
O
1780 }
1781
e8e12e8c 1782 resolver.d_eventTrace.add(RecEventTrace::AnswerSent);
0638d698 1783
0fedcaf8 1784 // Now do the per query changing part ot the protobuf message
e8e12e8c 1785 if (t_protobufServers.servers && !(luaconfsLocal->protobufExportConfig.taggedOnly && appliedPolicy.getName().empty() && comboWriter->d_policyTags.empty())) {
ac10822e 1786 // Below are the fields that are not stored in the packet cache and will be appended here and on a cache hit
e8e12e8c
OM
1787 if (g_useKernelTimestamp && comboWriter->d_kernelTimestamp.tv_sec != 0) {
1788 pbMessage.setQueryTime(comboWriter->d_kernelTimestamp.tv_sec, comboWriter->d_kernelTimestamp.tv_usec);
c29d820c
RG
1789 }
1790 else {
e8e12e8c 1791 pbMessage.setQueryTime(comboWriter->d_now.tv_sec, comboWriter->d_now.tv_usec);
c29d820c 1792 }
e8e12e8c
OM
1793 pbMessage.setMessageIdentity(comboWriter->d_uuid);
1794 pbMessage.setSocketProtocol(comboWriter->d_tcp ? pdns::ProtoZero::Message::TransportProtocol::TCP : pdns::ProtoZero::Message::TransportProtocol::UDP);
e81063e5
OM
1795
1796 if (!luaconfsLocal->protobufExportConfig.logMappedFrom) {
e8e12e8c
OM
1797 pbMessage.setSocketFamily(comboWriter->d_source.sin4.sin_family);
1798 Netmask requestorNM(comboWriter->d_source, comboWriter->d_source.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
e81063e5
OM
1799 ComboAddress requestor = requestorNM.getMaskedNetwork();
1800 pbMessage.setFrom(requestor);
e8e12e8c 1801 pbMessage.setFromPort(comboWriter->d_source.getPort());
e81063e5
OM
1802 }
1803 else {
e8e12e8c
OM
1804 pbMessage.setSocketFamily(comboWriter->d_mappedSource.sin4.sin_family);
1805 Netmask requestorNM(comboWriter->d_mappedSource, comboWriter->d_mappedSource.sin4.sin_family == AF_INET ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
e81063e5
OM
1806 ComboAddress requestor = requestorNM.getMaskedNetwork();
1807 pbMessage.setFrom(requestor);
e8e12e8c 1808 pbMessage.setFromPort(comboWriter->d_mappedSource.getPort());
e81063e5
OM
1809 }
1810
e8e12e8c
OM
1811 pbMessage.setTo(comboWriter->d_destination);
1812 pbMessage.setId(comboWriter->d_mdp.d_header.id);
83b261a7
OM
1813
1814 pbMessage.setTime();
e8e12e8c
OM
1815 pbMessage.setEDNSSubnet(comboWriter->d_ednssubnet.source, comboWriter->d_ednssubnet.source.isIPv4() ? luaconfsLocal->protobufMaskV4 : luaconfsLocal->protobufMaskV6);
1816 pbMessage.setRequestorId(dnsQuestion.requestorId);
1817 pbMessage.setDeviceId(dnsQuestion.deviceId);
1818 pbMessage.setDeviceName(dnsQuestion.deviceName);
1819 pbMessage.setToPort(comboWriter->d_destination.getPort());
a4d0f523 1820 pbMessage.addPolicyTags(comboWriter->d_gettagPolicyTags);
3211bbaf 1821
e8e12e8c
OM
1822 for (const auto& metaValue : dnsQuestion.meta) {
1823 pbMessage.setMeta(metaValue.first, metaValue.second.stringVal, metaValue.second.intVal);
3211bbaf 1824 }
41c542ec
NC
1825#ifdef NOD_ENABLED
1826 if (g_nodEnabled) {
ca2526f5 1827 if (nod) {
8c1cb8aa 1828 pbMessage.setNewlyObservedDomain(true);
83b261a7 1829 pbMessage.addPolicyTag(g_nod_pbtag);
ca2526f5
NC
1830 }
1831 if (hasUDR) {
83b261a7 1832 pbMessage.addPolicyTag(g_udr_pbtag);
ca2526f5 1833 }
41c542ec
NC
1834 }
1835#endif /* NOD_ENABLED */
e8e12e8c
OM
1836 if (resolver.d_eventTrace.enabled() && (SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_pb) != 0) {
1837 pbMessage.addEvents(resolver.d_eventTrace);
4cdc62f5 1838 }
e8e12e8c 1839 if (comboWriter->d_logResponse) {
83b261a7 1840 protobufLogResponse(pbMessage);
406b722e 1841 }
aa7929a3 1842 }
00b3e94a 1843
e8e12e8c
OM
1844 if (resolver.d_eventTrace.enabled() && (SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_log) != 0) {
1845 SLOG(g_log << Logger::Info << resolver.d_eventTrace.toString() << endl,
1846 resolver.d_slog->info(Logr::Info, resolver.d_eventTrace.toString())); // Maybe we want it to be more fancy?
9c495589 1847 }
3bd27e4a 1848
a71665e5 1849 // Originally this code used a mix of floats, doubles, uint64_t with different units.
02d23ff9 1850 // Now it always uses an integral number of microseconds, except for averages, which use doubles
e8e12e8c 1851 uint64_t spentUsec = uSec(resolver.getNow() - comboWriter->d_now);
3bd27e4a 1852 if (!g_quiet) {
91092a9f 1853 if (!g_slogStructured) {
e8e12e8c
OM
1854 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);
1855 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 1856
e8e12e8c
OM
1857 if (!shouldNotValidate && resolver.isDNSSECValidationRequested()) {
1858 g_log << ", dnssec=" << resolver.getValidationState();
91092a9f 1859 }
e8e12e8c
OM
1860 g_log << " answer-is-variable=" << resolver.wasVariable() << ", into-packetcache=" << intoPC;
1861 g_log << " maxdepth=" << resolver.d_maxdepth;
91092a9f
OM
1862 g_log << endl;
1863 }
1864 else {
e8e12e8c 1865 resolver.d_slog->info(Logr::Info, "Answer", "rd", Logging::Loggable(comboWriter->d_mdp.d_header.rd),
22577619
OM
1866 "answers", Logging::Loggable(ntohs(packetWriter.getHeader()->ancount)),
1867 "additional", Logging::Loggable(ntohs(packetWriter.getHeader()->arcount)),
1868 "outqueries", Logging::Loggable(resolver.d_outqueries),
1869 "netms", Logging::Loggable(resolver.d_totUsec / 1000.0),
1870 "totms", Logging::Loggable(static_cast<double>(spentUsec) / 1000.0),
1871 "throttled", Logging::Loggable(resolver.d_throttledqueries),
1872 "timeouts", Logging::Loggable(resolver.d_timeouts),
1873 "tcpout", Logging::Loggable(resolver.d_tcpoutqueries),
1874 "dotout", Logging::Loggable(resolver.d_dotoutqueries),
1875 "rcode", Logging::Loggable(res),
1876 "validationState", Logging::Loggable(resolver.getValidationState()),
1877 "answer-is-variable", Logging::Loggable(resolver.wasVariable()),
1878 "into-packetcache", Logging::Loggable(intoPC),
1879 "maxdepth", Logging::Loggable(resolver.d_maxdepth));
2c9119cd 1880 }
c75a6a9e 1881 }
b23b8614 1882
e8e12e8c 1883 if (comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Query)) {
b4374c3c 1884 if (resolver.d_outqueries != 0 || resolver.d_throttledqueries != 0 || resolver.d_authzonequeries != 0) {
7b2c4969 1885 g_recCache->incCacheMisses();
0fd8cf21
O
1886 }
1887 else {
7b2c4969 1888 g_recCache->incCacheHits();
0fd8cf21 1889 }
f7b8cffa 1890 }
2c9119cd 1891
7d3d2f4f
OM
1892 t_Counters.at(rec::Histogram::answers)(spentUsec);
1893 t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
fe213470 1894
e8e12e8c 1895 auto newLat = static_cast<double>(spentUsec);
7ab9f171 1896 newLat = min(newLat, g_networkTimeoutMsec * 1000.0); // outliers of several minutes exist..
7d3d2f4f 1897 t_Counters.at(rec::DoubleWAvgCounter::avgLatencyUsec).addToRollingAvg(newLat, g_latencyStatSize);
0a6b1027 1898 // no worries, we do this for packet cache hits elsewhere
19178da9 1899
e8e12e8c
OM
1900 if (spentUsec >= resolver.d_totUsec) {
1901 uint64_t ourtime = spentUsec - resolver.d_totUsec;
7d3d2f4f 1902 t_Counters.at(rec::Histogram::ourtime)(ourtime);
e8e12e8c 1903 newLat = static_cast<double>(ourtime); // usec
7d3d2f4f 1904 t_Counters.at(rec::DoubleWAvgCounter::avgLatencyOursUsec).addToRollingAvg(newLat, g_latencyStatSize);
042da1a1 1905 }
3bd27e4a
RG
1906
1907#ifdef NOD_ENABLED
1908 if (nod) {
e8e12e8c 1909 sendNODLookup(nodlogger, comboWriter->d_mdp.d_qname);
3bd27e4a
RG
1910 }
1911#endif /* NOD_ENABLED */
1912
c6d04bdc 1913 // cout<<dc->d_mdp.d_qname<<"\t"<<MT->getUsec()<<"\t"<<sr.d_outqueries<<endl;
288f4aa9 1914 }
8c1cb8aa 1915 catch (const PDNSException& ae) {
e8e12e8c
OM
1916 SLOG(g_log << Logger::Error << "startDoResolve problem " << makeLoginfo(comboWriter) << ": " << ae.reason << endl,
1917 resolver.d_slog->error(Logr::Error, ae.reason, "startDoResolve problem", "exception", Logging::Loggable("PDNSException")));
288f4aa9 1918 }
8c1cb8aa 1919 catch (const MOADNSException& mde) {
e8e12e8c
OM
1920 SLOG(g_log << Logger::Error << "DNS parser error " << makeLoginfo(comboWriter) << ": " << comboWriter->d_mdp.d_qname << ", " << mde.what() << endl,
1921 resolver.d_slog->error(Logr::Error, mde.what(), "DNS parser error"));
7b1469bb 1922 }
d4f08082 1923 catch (const std::exception& e) {
e8e12e8c 1924 SLOG(g_log << Logger::Error << "STL error " << makeLoginfo(comboWriter) << ": " << e.what(),
c42e8958 1925 resolver.d_slog->error(Logr::Error, e.what(), "Exception in resolver context", "exception", Logging::Loggable("std::exception")));
068c7634
PD
1926
1927 // Luawrapper nests the exception from Lua, so we unnest it here
1928 try {
8c1cb8aa
OM
1929 std::rethrow_if_nested(e);
1930 }
1931 catch (const std::exception& ne) {
91092a9f 1932 SLOG(g_log << ". Extra info: " << ne.what(),
e8e12e8c 1933 resolver.d_slog->error(Logr::Error, ne.what(), "Nested exception in resolver context", Logging::Loggable("std::exception")));
8c1cb8aa
OM
1934 }
1935 catch (...) {
1936 }
91092a9f
OM
1937 if (!g_slogStructured) {
1938 g_log << endl;
1939 }
c154c8a4 1940 }
8c1cb8aa 1941 catch (...) {
e8e12e8c
OM
1942 SLOG(g_log << Logger::Error << "Any other exception in a resolver context " << makeLoginfo(comboWriter) << endl,
1943 resolver.d_slog->info(Logr::Error, "Any other exception in a resolver context"));
288f4aa9 1944 }
3ddb9247 1945
87c4554a
OM
1946 runTaskOnce(g_logCommonErrors);
1947
a7f7ebdf 1948 static const size_t stackSizeThreshold = 9 * ::arg().asNum("stack-size") / 10;
e8e12e8c
OM
1949 if (g_multiTasker->getMaxStackUsage() >= stackSizeThreshold) {
1950 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,
1951 resolver.d_slog->info(Logr::Error, "Reached mthread stack usage of 90%",
22577619
OM
1952 "stackUsage", Logging::Loggable(g_multiTasker->getMaxStackUsage()),
1953 "outqueries", Logging::Loggable(resolver.d_outqueries),
1954 "netms", Logging::Loggable(resolver.d_totUsec / 1000.0),
1955 "throttled", Logging::Loggable(resolver.d_throttledqueries),
1956 "timeouts", Logging::Loggable(resolver.d_timeouts),
1957 "tcpout", Logging::Loggable(resolver.d_tcpoutqueries),
1958 "dotout", Logging::Loggable(resolver.d_dotoutqueries),
1959 "validationState", Logging::Loggable(resolver.getValidationState())));
e8e12e8c
OM
1960 }
1961 t_Counters.at(rec::Counter::maxMThreadStackUsage) = max(g_multiTasker->getMaxStackUsage(), t_Counters.at(rec::Counter::maxMThreadStackUsage));
7d3d2f4f 1962 t_Counters.updateSnap(g_regressionTestMode);
288f4aa9
BH
1963}
1964
bc7a7b24 1965void getQNameAndSubnet(const std::string& question, DNSName* dnsname, uint16_t* qtype, uint16_t* qclass,
fcd32fe8 1966 bool& foundECS, EDNSSubnetOpts* ednssubnet, EDNSOptionViewMap* options)
02b47f43 1967{
5cc8371b 1968 const bool lookForECS = ednssubnet != nullptr;
325a18bf 1969 const dnsheader_aligned dnshead(question.data());
e8e12e8c 1970 const dnsheader* dhPointer = dnshead.get();
02b47f43 1971 size_t questionLen = question.length();
8c1cb8aa 1972 unsigned int consumed = 0;
e8e12e8c 1973 *dnsname = DNSName(question.c_str(), static_cast<int>(questionLen), sizeof(dnsheader), false, qtype, qclass, &consumed);
02b47f43 1974
8c1cb8aa 1975 size_t pos = sizeof(dnsheader) + consumed + 4;
5cc8371b 1976 const size_t headerSize = /* root */ 1 + sizeof(dnsrecordheader);
e8e12e8c 1977 const uint16_t arcount = ntohs(dhPointer->arcount);
5cc8371b 1978
fcd32fe8 1979 for (uint16_t arpos = 0; arpos < arcount && questionLen > (pos + headerSize) && (lookForECS && !foundECS); arpos++) {
5cc8371b 1980 if (question.at(pos) != 0) {
fcd32fe8 1981 /* not an OPT, bye. */
5cc8371b
RG
1982 return;
1983 }
1984
1985 pos += 1;
22577619 1986 const auto* drh = reinterpret_cast<const dnsrecordheader*>(&question.at(pos)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
5cc8371b
RG
1987 pos += sizeof(dnsrecordheader);
1988
1989 if (pos >= questionLen) {
1990 return;
1991 }
1992
02b47f43 1993 /* OPT root label (1) followed by type (2) */
8c1cb8aa 1994 if (lookForECS && ntohs(drh->d_type) == QType::OPT) {
e8e12e8c 1995 if (options == nullptr) {
341d2553 1996 size_t ecsStartPosition = 0;
00b8cadc 1997 size_t ecsLen = 0;
5cc8371b 1998 /* we need to pass the record len */
22577619 1999 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
2000 if (res == 0 && ecsLen > 4) {
2001 EDNSSubnetOpts eso;
8c1cb8aa
OM
2002 if (getEDNSSubnetOptsFromString(&question.at(pos - sizeof(drh->d_clen) + ecsStartPosition + 4), ecsLen - 4, &eso)) {
2003 *ednssubnet = eso;
5cc8371b 2004 foundECS = true;
00b8cadc
RG
2005 }
2006 }
2007 }
2008 else {
5cc8371b 2009 /* we need to pass the record len */
22577619 2010 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 2011 if (res == 0) {
e8e12e8c
OM
2012 const auto& iter = options->find(EDNSOptionCode::ECS);
2013 if (iter != options->end() && !iter->second.values.empty() && iter->second.values.at(0).content != nullptr && iter->second.values.at(0).size > 0) {
00b8cadc 2014 EDNSSubnetOpts eso;
e8e12e8c 2015 if (getEDNSSubnetOptsFromString(iter->second.values.at(0).content, iter->second.values.at(0).size, &eso)) {
8c1cb8aa 2016 *ednssubnet = eso;
5cc8371b 2017 foundECS = true;
00b8cadc
RG
2018 }
2019 }
02b47f43
RG
2020 }
2021 }
2022 }
5cc8371b
RG
2023
2024 pos += ntohs(drh->d_clen);
02b47f43
RG
2025 }
2026}
2027
bc7a7b24 2028bool checkForCacheHit(bool qnameParsed, unsigned int tag, const string& data,
8c1cb8aa
OM
2029 DNSName& qname, uint16_t& qtype, uint16_t& qclass,
2030 const struct timeval& now,
2031 string& response, uint32_t& qhash,
c375521b 2032 RecursorPacketCache::OptPBData& pbData, bool tcp, const ComboAddress& source, const ComboAddress& mappedSource)
d7c99a70 2033{
39e42424 2034 if (!g_packetCache) {
5abc5e10
OM
2035 return false;
2036 }
d7c99a70 2037 bool cacheHit = false;
e8e12e8c
OM
2038 uint32_t age = 0;
2039 vState valState = vState::Indeterminate;
8c1cb8aa 2040
d7c99a70 2041 if (qnameParsed) {
39e42424 2042 cacheHit = g_packetCache->getResponsePacket(tag, data, qname, qtype, qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
8c1cb8aa
OM
2043 }
2044 else {
39e42424 2045 cacheHit = g_packetCache->getResponsePacket(tag, data, qname, &qtype, &qclass, now.tv_sec, &response, &age, &valState, &qhash, &pbData, tcp);
d7c99a70
O
2046 }
2047
2048 if (cacheHit) {
2049 if (vStateIsBogus(valState)) {
2050 if (t_bogusremotes) {
2051 t_bogusremotes->push_back(source);
2052 }
2053 if (t_bogusqueryring) {
e32a8d46 2054 t_bogusqueryring->push_back({qname, qtype});
d7c99a70
O
2055 }
2056 }
2057
c375521b
OM
2058 // This is only to get the proxyMapping suffixMatch stats right i the case of a PC hit
2059 if (t_proxyMapping && source != mappedSource) {
005c00d5
OM
2060 if (const auto* found = t_proxyMapping->lookup(source)) {
2061 if (found->second.suffixMatchNode) {
2062 if (found->second.suffixMatchNode->check(qname)) {
2063 ++found->second.stats.suffixMatches;
c375521b
OM
2064 }
2065 }
2066 }
2067 }
2068
7d3d2f4f
OM
2069 t_Counters.at(rec::Counter::packetCacheHits)++;
2070 t_Counters.at(rec::Counter::syncresqueries)++; // XXX
d7c99a70 2071 if (response.length() >= sizeof(struct dnsheader)) {
100ff9c7
OM
2072 dnsheader_aligned dh_aligned(response.data());
2073 ageDNSPacket(response, age, dh_aligned);
2074 const auto* dhp = dh_aligned.get();
2075 updateResponseStats(dhp->rcode, source, response.length(), nullptr, 0);
2076 t_Counters.at(rec::ResponseStats::responseStats).submitResponse(qtype, response.length(), dhp->rcode);
d7c99a70 2077 }
6ab8d5ea 2078
7d3d2f4f
OM
2079 // we assume 0 usec
2080 t_Counters.at(rec::DoubleWAvgCounter::avgLatencyUsec).addToRollingAvg(0.0, g_latencyStatSize);
2081 t_Counters.at(rec::DoubleWAvgCounter::avgLatencyOursUsec).addToRollingAvg(0.0, g_latencyStatSize);
a71665e5
O
2082#if 0
2083 // XXX changes behaviour compared to old code!
7d3d2f4f
OM
2084 t_Counters.at(rec::Counter::answers)(0);
2085 t_Counters.at(rec::Counter::ourtime)(0);
a71665e5 2086#endif
d7c99a70 2087 }
3211bbaf 2088
d7c99a70
O
2089 return cacheHit;
2090}
2091
35189b7d
KF
2092static void* pleaseWipeCaches(const DNSName& canon, bool subtree, uint16_t qtype)
2093{
2094 auto res = wipeCaches(canon, subtree, qtype);
91092a9f
OM
2095 SLOG(g_log << Logger::Info << "Wiped caches for " << canon << ": " << res.record_count << " records; " << res.negative_record_count << " negative records; " << res.packet_count << " packets" << endl,
2096 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
2097 return nullptr;
2098}
2099
bc7a7b24 2100void requestWipeCaches(const DNSName& canon)
35189b7d
KF
2101{
2102 // send a message to the handler thread asking it
2103 // to wipe all of the caches
e8e12e8c 2104 ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: pointer owner
8c1cb8aa 2105 tmsg->func = [=] { return pleaseWipeCaches(canon, true, 0xffff); };
35189b7d 2106 tmsg->wantAnswer = false;
78199711 2107 if (write(RecThreadInfo::info(0).getPipes().writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) { // NOLINT: correct sizeof
e8e12e8c 2108 delete tmsg; // NOLINT: pointer owner
35189b7d
KF
2109
2110 unixDie("write to thread pipe returned wrong size or error");
2111 }
7db35c6e 2112 // coverity[leaked_storage]
35189b7d
KF
2113}
2114
bc7a7b24 2115bool expectProxyProtocol(const ComboAddress& from)
5216ddcc
RG
2116{
2117 return g_proxyProtocolACL.match(from);
2118}
2119
e81063e5
OM
2120// fromaddr: the address the query is coming from
2121// destaddr: the address the query was received on
2122// source: the address we assume the query is coming from, might be set by proxy protocol
2123// destination: the address we assume the query was sent to, might be set by proxy protocol
2124// mappedSource: the address we assume the query is coming from. Differs from source if table based mapping has been applied
e8e12e8c 2125static 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 2126{
78199711 2127 RecThreadInfo::self().incNumberOfDistributedQueries();
3e4e2d9d 2128 gettimeofday(&g_now, nullptr);
e8e12e8c
OM
2129 if (tval.tv_sec != 0) {
2130 struct timeval diff = g_now - tval;
2131 double delta = (static_cast<double>(diff.tv_sec) * 1000 + static_cast<double>(diff.tv_usec) / 1000.0);
183eb877 2132
8c1cb8aa 2133 if (delta > 1000.0) {
7d3d2f4f 2134 t_Counters.at(rec::Counter::tooOldDrops)++;
c29d820c
RG
2135 return nullptr;
2136 }
b71b60ee 2137 }
2138
7d3d2f4f
OM
2139 ++t_Counters.at(rec::Counter::qcounter);
2140
e8e12e8c 2141 if (fromaddr.sin4.sin_family == AF_INET6) {
7d3d2f4f 2142 t_Counters.at(rec::Counter::ipv6qcounter)++;
e8e12e8c 2143 }
1bc3c142
BH
2144
2145 string response;
325a18bf 2146 const dnsheader_aligned headerdata(question.data());
e8e12e8c 2147 const dnsheader* dnsheader = headerdata.get();
8c1cb8aa 2148 unsigned int ctag = 0;
f57486f1 2149 uint32_t qhash = 0;
12aff2e5 2150 bool needECS = false;
b502d522 2151 std::unordered_set<std::string> policyTags;
634dd69e 2152 std::map<std::string, RecursorLua4::MetaValue> meta;
5fd2577f 2153 LuaContext::LuaObject data;
67e31ebe 2154 string requestorId;
590388d2 2155 string deviceId;
0a6a45c8 2156 string deviceName;
163ed916 2157 string routingTag;
16bbc6e3 2158 bool logQuery = false;
406b722e 2159 bool logResponse = false;
e8e12e8c 2160 boost::uuids::uuid uniqueId{};
02b47f43 2161 auto luaconfsLocal = g_luaconfs.getLocal();
babe9430
OM
2162 const auto pbExport = checkProtobufExport(luaconfsLocal);
2163 const auto outgoingbExport = checkOutgoingProtobufExport(luaconfsLocal);
2164 if (pbExport || outgoingbExport) {
2165 if (pbExport) {
2166 needECS = true;
2167 }
d61aa945 2168 uniqueId = getUniqueID();
02b47f43 2169 }
babe9430
OM
2170 logQuery = t_protobufServers.servers && luaconfsLocal->protobufExportConfig.logQueries;
2171 logResponse = t_protobufServers.servers && luaconfsLocal->protobufExportConfig.logResponses;
b9fa43e0 2172#ifdef HAVE_FSTRM
9489e2b5 2173 checkFrameStreamExport(luaconfsLocal, luaconfsLocal->frameStreamExportConfig, t_frameStreamServersInfo);
12aff2e5 2174#endif
b40562da
RG
2175 EDNSSubnetOpts ednssubnet;
2176 bool ecsFound = false;
2177 bool ecsParsed = false;
37a919d4 2178 std::vector<DNSRecord> records;
e95b2a7c 2179 std::string extendedErrorExtra;
37a919d4 2180 boost::optional<int> rcode = boost::none;
e95b2a7c 2181 boost::optional<uint16_t> extendedErrorCode{boost::none};
70fb28d9
RG
2182 uint32_t ttlCap = std::numeric_limits<uint32_t>::max();
2183 bool variable = false;
37a919d4 2184 bool followCNAMEs = false;
7917914b 2185 bool responsePaddingDisabled = false;
35189b7d 2186 DNSName qname;
1bc3c142 2187 try {
8c1cb8aa
OM
2188 uint16_t qtype = 0;
2189 uint16_t qclass = 0;
2190 bool qnameParsed = false;
8f7473d7 2191#ifdef MALLOC_TRACE
2192 /*
2193 static uint64_t last=0;
2194 if(!last)
2195 g_mtracer->clearAllocators();
2196 cout<<g_mtracer->getAllocs()-last<<" "<<g_mtracer->getNumOut()<<" -- BEGIN TRACE"<<endl;
2197 last=g_mtracer->getAllocs();
2198 cout<<g_mtracer->topAllocatorsString()<<endl;
2199 g_mtracer->clearAllocators();
2200 */
2201#endif
55a1378f 2202
0c0d5c26 2203 // We do not have a SyncRes specific Lua context at this point yet, so ok to use t_pdl
3bc38426 2204 if (needECS || (t_pdl && (t_pdl->hasGettagFunc() || t_pdl->hasGettagFFIFunc())) || dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
b2eacd67 2205 try {
29e6303a 2206 EDNSOptionViewMap ednsOptions;
5cc8371b
RG
2207
2208 ecsFound = false;
2209
2210 getQNameAndSubnet(question, &qname, &qtype, &qclass,
fcd32fe8 2211 ecsFound, &ednssubnet, g_gettagNeedsEDNSOptions ? &ednsOptions : nullptr);
5cc8371b 2212
c15ff3df
RG
2213 qnameParsed = true;
2214 ecsParsed = true;
12aff2e5 2215
8c1cb8aa 2216 if (t_pdl) {
12aff2e5 2217 try {
3bc38426 2218 if (t_pdl->hasGettagFFIFunc()) {
634dd69e 2219 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 2220
25ab0839 2221 eventTrace.add(RecEventTrace::LuaGetTagFFI);
cca6d6a8 2222 ctag = t_pdl->gettag_ffi(params);
0f14442c 2223 eventTrace.add(RecEventTrace::LuaGetTagFFI, ctag, false);
70fb28d9 2224 }
3bc38426 2225 else if (t_pdl->hasGettagFunc()) {
0f14442c 2226 eventTrace.add(RecEventTrace::LuaGetTag);
163ed916 2227 ctag = t_pdl->gettag(source, ednssubnet.source, destination, qname, qtype, &policyTags, data, ednsOptions, false, requestorId, deviceId, deviceName, routingTag, proxyProtocolValues);
0f14442c 2228 eventTrace.add(RecEventTrace::LuaGetTag, ctag, false);
70fb28d9 2229 }
12aff2e5 2230 }
8c1cb8aa 2231 catch (const std::exception& e) {
cca6d6a8 2232 if (g_logCommonErrors) {
91092a9f
OM
2233 SLOG(g_log << Logger::Warning << "Error parsing a query packet qname='" << qname << "' for tag determination, setting tag=0: " << e.what() << endl,
2234 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 2235 }
12aff2e5 2236 }
8ea8c302 2237 }
b2eacd67 2238 }
8c1cb8aa 2239 catch (const std::exception& e) {
cca6d6a8 2240 if (g_logCommonErrors) {
91092a9f 2241 SLOG(g_log << Logger::Warning << "Error parsing a query packet for tag determination, setting tag=0: " << e.what() << endl,
7d0727b1 2242 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 2243 }
12aff2e5 2244 }
12ce523e 2245 }
3ddb9247 2246
ac10822e 2247 RecursorPacketCache::OptPBData pbData{boost::none};
babe9430 2248 if (t_protobufServers.servers) {
845cbf4c 2249 if (logQuery && !(luaconfsLocal->protobufExportConfig.taggedOnly && policyTags.empty())) {
e8e12e8c 2250 protobufLogQuery(luaconfsLocal, uniqueId, source, destination, mappedSource, ednssubnet.source, false, dnsheader->id, question.size(), qname, qtype, qclass, policyTags, requestorId, deviceId, deviceName, meta);
b790ef3d 2251 }
d9d3f9c1 2252 }
02b47f43 2253
7917914b 2254 if (ctag == 0 && !responsePaddingDisabled && g_paddingFrom.match(fromaddr)) {
53604c2a
RG
2255 ctag = g_paddingTag;
2256 }
2c0af54f 2257
e8e12e8c 2258 if (dnsheader->opcode == static_cast<unsigned>(Opcode::Query)) {
35189b7d
KF
2259 /* It might seem like a good idea to skip the packet cache lookup if we know that the answer is not cacheable,
2260 but it means that the hash would not be computed. If some script decides at a later time to mark back the answer
2261 as cacheable we would cache it with a wrong tag, so better safe than sorry. */
2262 eventTrace.add(RecEventTrace::PCacheCheck);
c375521b 2263 bool cacheHit = checkForCacheHit(qnameParsed, ctag, question, qname, qtype, qclass, g_now, response, qhash, pbData, false, source, mappedSource);
35189b7d
KF
2264 eventTrace.add(RecEventTrace::PCacheCheck, cacheHit, false);
2265 if (cacheHit) {
2266 if (!g_quiet) {
91092a9f
OM
2267 SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " question answered from packet cache tag=" << ctag << " from " << source.toStringWithPort() << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << endl,
2268 g_slogudpin->info(Logr::Notice, "Question answered from packet cache", "tag", Logging::Loggable(ctag),
2269 "qname", Logging::Loggable(qname), "qtype", Logging::Loggable(QType(qtype)),
2270 "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
35189b7d 2271 }
22577619
OM
2272 struct msghdr msgh
2273 {
2274 };
2275 struct iovec iov
2276 {
2277 };
e8e12e8c 2278 cmsgbuf_aligned cbuf{};
22577619 2279 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 2280 msgh.msg_control = nullptr;
35189b7d 2281
e8e12e8c 2282 if (g_fromtosockets.count(fileDesc) != 0) {
35189b7d
KF
2283 addCMsgSrcAddr(&msgh, &cbuf, &destaddr, 0);
2284 }
e8e12e8c 2285 int sendErr = sendOnNBSocket(fileDesc, &msgh);
35189b7d 2286 eventTrace.add(RecEventTrace::AnswerSent);
4cdc62f5 2287
e8e12e8c 2288 if (t_protobufServers.servers && logResponse && (!luaconfsLocal->protobufExportConfig.taggedOnly || !pbData || pbData->d_tagged)) {
c837140e 2289 protobufLogResponse(dnsheader, luaconfsLocal, pbData, tval, false, source, destination, mappedSource, ednssubnet, uniqueId, requestorId, deviceId, deviceName, meta, eventTrace, policyTags);
35189b7d 2290 }
0638d698 2291
e8e12e8c 2292 if (eventTrace.enabled() && (SyncRes::s_event_trace_enabled & SyncRes::event_trace_to_log) != 0) {
91092a9f
OM
2293 SLOG(g_log << Logger::Info << eventTrace.toString() << endl,
2294 g_slogudpin->info(Logr::Info, eventTrace.toString())); // Do we want more fancy logging here?
35189b7d 2295 }
e8e12e8c 2296 if (sendErr != 0 && g_logCommonErrors) {
91092a9f 2297 SLOG(g_log << Logger::Warning << "Sending UDP reply to client " << source.toStringWithPort()
7d0727b1 2298 << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << " failed with: "
e8e12e8c 2299 << stringerror(sendErr) << endl,
91092a9f 2300 g_slogudpin->error(Logr::Error, sendErr, "Sending UDP reply to client failed", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
35189b7d 2301 }
22577619
OM
2302 struct timeval now
2303 {
2304 };
35189b7d 2305 Utility::gettimeofday(&now, nullptr);
e8e12e8c 2306 uint64_t spentUsec = uSec(now - tval);
7d3d2f4f
OM
2307 t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
2308 t_Counters.updateSnap(g_regressionTestMode);
e8e12e8c 2309 return nullptr;
a2a81d42 2310 }
1bc3c142 2311 }
3ddb9247 2312 }
7917914b
RG
2313 catch (const std::exception& e) {
2314 if (g_logCommonErrors) {
91092a9f
OM
2315 SLOG(g_log << Logger::Error << "Error processing or aging answer packet: " << e.what() << endl,
2316 g_slogudpin->error(Logr::Error, e.what(), "Error processing or aging answer packet", "exception", Logging::Loggable("std::exception")));
7917914b 2317 }
e8e12e8c 2318 return nullptr;
1bc3c142 2319 }
3ddb9247 2320
7917914b 2321 if (t_pdl) {
e8e12e8c 2322 bool ipf = t_pdl->ipfilter(source, destination, *dnsheader, eventTrace);
0f14442c 2323 if (ipf) {
7917914b 2324 if (!g_quiet) {
e8e12e8c 2325 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 2326 g_slogudpin->info(Logr::Notice, "Dropped question based on policy", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
7917914b 2327 }
7d3d2f4f 2328 t_Counters.at(rec::Counter::policyDrops)++;
e8e12e8c 2329 return nullptr;
4ea94941 2330 }
2331 }
2332
e8e12e8c 2333 if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
8c1cb8aa
OM
2334 if (!isAllowNotifyForZone(qname)) {
2335 if (!g_quiet) {
e8e12e8c 2336 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 2337 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
2338 }
2339
7d3d2f4f 2340 t_Counters.at(rec::Counter::zoneDisallowedNotify)++;
e8e12e8c 2341 return nullptr;
35189b7d
KF
2342 }
2343
2344 if (!g_quiet) {
91092a9f
OM
2345 SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " got NOTIFY for " << qname.toLogString() << " from " << source.toStringWithPort() << (source != fromaddr ? " (via " + fromaddr.toStringWithPort() + ")" : "") << endl,
2346 g_slogudpin->info(Logr::Notice, "Got NOTIFY", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr), "qname", Logging::Loggable(qname)));
35189b7d 2347 }
046b69a5
OM
2348 if (!notifyRPZTracker(qname)) {
2349 // It wasn't an RPZ
2350 requestWipeCaches(qname);
2351 }
35189b7d
KF
2352
2353 // the operation will now be treated as a Query, generating
2354 // a normal response, as the rest of the code does not
2355 // check dh->opcode, but we need to ensure that the response
2356 // to this request does not get put into the packet cache
2357 variable = true;
2358 }
2359
e8e12e8c
OM
2360 if (g_multiTasker->numProcesses() > g_maxMThreads) {
2361 if (!g_quiet) {
2362 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 2363 g_slogudpin->info(Logr::Notice, "Dropped question, over capacity", "source", Logging::Loggable(source), "remote", Logging::Loggable(fromaddr)));
e8e12e8c 2364 }
7d3d2f4f 2365 t_Counters.at(rec::Counter::overCapacityDrops)++;
e8e12e8c
OM
2366 return nullptr;
2367 }
2368
2369 auto comboWriter = std::make_unique<DNSComboWriter>(question, g_now, std::move(policyTags), t_pdl, std::move(data), std::move(records));
2370
2371 comboWriter->setSocket(fileDesc);
2372 comboWriter->d_tag = ctag;
2373 comboWriter->d_qhash = qhash;
2374 comboWriter->setRemote(fromaddr); // the address the query is coming from
2375 comboWriter->setSource(source); // the address we assume the query is coming from, might be set by proxy protocol
2376 comboWriter->setLocal(destaddr); // the address the query was received on
2377 comboWriter->setDestination(destination); // the address we assume the query is sent to, might be set by proxy protocol
2378 comboWriter->setMappedSource(mappedSource); // the address we assume the query is coming from. Differs from source if table-based mapping has been applied
2379 comboWriter->d_tcp = false;
2380 comboWriter->d_ecsFound = ecsFound;
2381 comboWriter->d_ecsParsed = ecsParsed;
2382 comboWriter->d_ednssubnet = ednssubnet;
2383 comboWriter->d_ttlCap = ttlCap;
2384 comboWriter->d_variable = variable;
2385 comboWriter->d_followCNAMERecords = followCNAMEs;
2386 comboWriter->d_rcode = rcode;
2387 comboWriter->d_logResponse = logResponse;
babe9430 2388 if (t_protobufServers.servers || t_outgoingProtobufServers.servers) {
e8e12e8c
OM
2389 comboWriter->d_uuid = uniqueId;
2390 }
4c5a50dc
OM
2391 comboWriter->d_requestorId = std::move(requestorId);
2392 comboWriter->d_deviceId = std::move(deviceId);
2393 comboWriter->d_deviceName = std::move(deviceName);
e8e12e8c
OM
2394 comboWriter->d_kernelTimestamp = tval;
2395 comboWriter->d_proxyProtocolValues = std::move(proxyProtocolValues);
2396 comboWriter->d_routingTag = std::move(routingTag);
2397 comboWriter->d_extendedErrorCode = extendedErrorCode;
2398 comboWriter->d_extendedErrorExtra = std::move(extendedErrorExtra);
2399 comboWriter->d_responsePaddingDisabled = responsePaddingDisabled;
2400 comboWriter->d_meta = std::move(meta);
2401
2402 comboWriter->d_eventTrace = std::move(eventTrace);
2403 g_multiTasker->makeThread(startDoResolve, (void*)comboWriter.release()); // deletes dc
0f14442c 2404
e8e12e8c 2405 return nullptr;
3ddb9247
PD
2406}
2407
e8e12e8c 2408static void handleNewUDPQuestion(int fileDesc, FDMultiplexer::funcparam_t& /* var */) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
5db529f8 2409{
5216ddcc 2410 static const size_t maxIncomingQuerySize = g_proxyProtocolACL.empty() ? 512 : (512 + g_proxyProtocolMaximumSize);
04896b99 2411 static thread_local std::string data;
e81063e5
OM
2412 ComboAddress fromaddr; // the address the query is coming from
2413 ComboAddress source; // the address we assume the query is coming from, might be set by proxy protocol
2414 ComboAddress destination; // the address we assume the query was sent to, might be set by proxy protocol
22577619
OM
2415 struct msghdr msgh
2416 {
2417 };
2418 struct iovec iov
2419 {
2420 };
7bec330a 2421 cmsgbuf_aligned cbuf;
390f1dab 2422 bool firstQuery = true;
5216ddcc 2423 std::vector<ProxyProtocolValue> proxyProtocolValues;
0f14442c 2424 RecEventTrace eventTrace;
b71b60ee 2425
b35191d7 2426 for (size_t queriesCounter = 0; queriesCounter < g_maxUDPQueriesPerRound; queriesCounter++) {
5216ddcc 2427 bool proxyProto = false;
b2b8ccea 2428 proxyProtocolValues.clear();
c0a00acd 2429 data.resize(maxIncomingQuerySize);
8c1cb8aa 2430 fromaddr.sin6.sin6_family = AF_INET6; // this makes sure fromaddr is big enough
e8e12e8c 2431 fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), data.data(), data.size(), &fromaddr);
b71b60ee 2432
199e75c7 2433 if (ssize_t len = recvmsg(fileDesc, &msgh, 0); len >= 0) {
0f14442c 2434 eventTrace.clear();
e8e12e8c 2435 eventTrace.setEnabled(SyncRes::s_event_trace_enabled != 0);
ade7a5e6 2436 eventTrace.add(RecEventTrace::ReqRecv);
390f1dab 2437
c0a00acd 2438 firstQuery = false;
6b8829d5 2439
e8e12e8c 2440 if ((msgh.msg_flags & MSG_TRUNC) != 0) {
7d3d2f4f 2441 t_Counters.at(rec::Counter::truncatedDrops)++;
6b8829d5 2442 if (!g_quiet) {
91092a9f
OM
2443 SLOG(g_log << Logger::Error << "Ignoring truncated query from " << fromaddr.toString() << endl,
2444 g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr)));
6b8829d5
RG
2445 }
2446 return;
2447 }
2448
5216ddcc
RG
2449 data.resize(static_cast<size_t>(len));
2450
2451 if (expectProxyProtocol(fromaddr)) {
e8e12e8c 2452 bool tcp = false;
8c73c703 2453 ssize_t used = parseProxyHeader(data, proxyProto, source, destination, tcp, proxyProtocolValues);
5216ddcc 2454 if (used <= 0) {
7d3d2f4f 2455 ++t_Counters.at(rec::Counter::proxyProtocolInvalidCount);
5216ddcc 2456 if (!g_quiet) {
91092a9f
OM
2457 SLOG(g_log << Logger::Error << "Ignoring invalid proxy protocol (" << std::to_string(len) << ", " << std::to_string(used) << ") query from " << fromaddr.toStringWithPort() << endl,
2458 g_slogudpin->info(Logr::Error, "Ignoring invalid proxy protocol query", "length", Logging::Loggable(len),
7d0727b1 2459 "used", Logging::Loggable(used), "remote", Logging::Loggable(fromaddr)));
5216ddcc
RG
2460 }
2461 return;
2462 }
e8e12e8c 2463 if (static_cast<size_t>(used) > g_proxyProtocolMaximumSize) {
95f851d6 2464 if (g_quiet) {
91092a9f
OM
2465 SLOG(g_log << Logger::Error << "Proxy protocol header in UDP packet from " << fromaddr.toStringWithPort() << " is larger than proxy-protocol-maximum-size (" << used << "), dropping" << endl,
2466 g_slogudpin->info(Logr::Error, "Proxy protocol header in UDP packet is larger than proxy-protocol-maximum-size",
7d0727b1 2467 "used", Logging::Loggable(used), "remote", Logging::Loggable(fromaddr)));
95f851d6 2468 }
7d3d2f4f 2469 ++t_Counters.at(rec::Counter::proxyProtocolInvalidCount);
95f851d6
RG
2470 return;
2471 }
2472
5216ddcc
RG
2473 data.erase(0, used);
2474 }
6b8829d5
RG
2475 else if (len > 512) {
2476 /* we only allow UDP packets larger than 512 for those with a proxy protocol header */
7d3d2f4f 2477 t_Counters.at(rec::Counter::truncatedDrops)++;
8c1cb8aa 2478 if (!g_quiet) {
91092a9f
OM
2479 SLOG(g_log << Logger::Error << "Ignoring truncated query from " << fromaddr.toStringWithPort() << endl,
2480 g_slogudpin->info(Logr::Error, "Ignoring truncated query", "remote", Logging::Loggable(fromaddr)));
6b8829d5
RG
2481 }
2482 return;
5216ddcc 2483 }
390f1dab 2484
5216ddcc 2485 if (data.size() < sizeof(dnsheader)) {
7d3d2f4f 2486 t_Counters.at(rec::Counter::ignoredCount)++;
c0a00acd 2487 if (!g_quiet) {
91092a9f
OM
2488 SLOG(g_log << Logger::Error << "Ignoring too-short (" << std::to_string(data.size()) << ") query from " << fromaddr.toString() << endl,
2489 g_slogudpin->info(Logr::Error, "Ignoring too-short query", "length", Logging::Loggable(data.size()),
7d0727b1 2490 "remote", Logging::Loggable(fromaddr)));
c0a00acd
RG
2491 }
2492 return;
04896b99 2493 }
04896b99 2494
6b8829d5
RG
2495 if (!proxyProto) {
2496 source = fromaddr;
ba892c7f 2497 }
e81063e5
OM
2498 ComboAddress mappedSource = source;
2499 if (t_proxyMapping) {
22577619 2500 if (const auto* iter = t_proxyMapping->lookup(source)) {
e8e12e8c
OM
2501 mappedSource = iter->second.address;
2502 ++iter->second.stats.netmaskMatches;
e81063e5
OM
2503 }
2504 }
8c1cb8aa 2505 if (t_remotes) {
c0a00acd
RG
2506 t_remotes->push_back(fromaddr);
2507 }
81859ba5 2508
e81063e5 2509 if (t_allowFrom && !t_allowFrom->match(&mappedSource)) {
8c1cb8aa 2510 if (!g_quiet) {
e8e12e8c 2511 SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping UDP query from " << mappedSource.toString() << ", address not matched by allow-from" << endl,
91092a9f 2512 g_slogudpin->info(Logr::Error, "Dropping UDP query, address not matched by allow-from", "source", Logging::Loggable(mappedSource)));
c0a00acd 2513 }
3ddb9247 2514
7d3d2f4f 2515 t_Counters.at(rec::Counter::unauthorizedUDP)++;
c0a00acd 2516 return;
5db529f8 2517 }
3bdc4508 2518
c0a00acd 2519 BOOST_STATIC_ASSERT(offsetof(sockaddr_in, sin_port) == offsetof(sockaddr_in6, sin6_port));
e8e12e8c 2520 if (fromaddr.sin4.sin_port == 0) { // also works for IPv6
8c1cb8aa 2521 if (!g_quiet) {
e8e12e8c 2522 SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping UDP query from " << fromaddr.toStringWithPort() << ", can't deal with port 0" << endl,
91092a9f 2523 g_slogudpin->info(Logr::Error, "Dropping UDP query can't deal with port 0", "remote", Logging::Loggable(fromaddr)));
c0a00acd
RG
2524 }
2525
7d3d2f4f 2526 t_Counters.at(rec::Counter::clientParseError)++; // not quite the best place to put it, but needs to go somewhere
c0a00acd 2527 return;
3abcdab2 2528 }
c0a00acd
RG
2529
2530 try {
325a18bf 2531 const dnsheader_aligned headerdata(data.data());
e8e12e8c 2532 const dnsheader* dnsheader = headerdata.get();
c0a00acd 2533
e8e12e8c 2534 if (dnsheader->qr) {
7d3d2f4f 2535 t_Counters.at(rec::Counter::ignoredCount)++;
8c1cb8aa 2536 if (g_logCommonErrors) {
91092a9f 2537 SLOG(g_log << Logger::Error << "Ignoring answer from " << fromaddr.toString() << " on server socket!" << endl,
7d0727b1 2538 g_slogudpin->info(Logr::Error, "Ignoring answer on server socket", "remote", Logging::Loggable(fromaddr)));
c0a00acd
RG
2539 }
2540 }
e8e12e8c 2541 else if (dnsheader->opcode != static_cast<unsigned>(Opcode::Query) && dnsheader->opcode != static_cast<unsigned>(Opcode::Notify)) {
7d3d2f4f 2542 t_Counters.at(rec::Counter::ignoredCount)++;
8c1cb8aa 2543 if (g_logCommonErrors) {
e8e12e8c
OM
2544 SLOG(g_log << Logger::Error << "Ignoring unsupported opcode " << Opcode::to_s(dnsheader->opcode) << " from " << fromaddr.toString() << " on server socket!" << endl,
2545 g_slogudpin->info(Logr::Error, "Ignoring unsupported opcode server socket", "remote", Logging::Loggable(fromaddr), "opcode", Logging::Loggable(Opcode::to_s(dnsheader->opcode))));
c0a00acd 2546 }
a6147cd2 2547 }
e8e12e8c 2548 else if (dnsheader->qdcount == 0U) {
7d3d2f4f 2549 t_Counters.at(rec::Counter::emptyQueriesCount)++;
8c1cb8aa 2550 if (g_logCommonErrors) {
91092a9f
OM
2551 SLOG(g_log << Logger::Error << "Ignoring empty (qdcount == 0) query from " << fromaddr.toString() << " on server socket!" << endl,
2552 g_slogudpin->info(Logr::Error, "Ignoring empty (qdcount == 0) query on server socket!", "remote", Logging::Loggable(fromaddr)));
c0f9be19
RG
2553 }
2554 }
a6147cd2 2555 else {
e8e12e8c 2556 if (dnsheader->opcode == static_cast<unsigned>(Opcode::Notify)) {
e81063e5 2557 if (!t_allowNotifyFrom || !t_allowNotifyFrom->match(&mappedSource)) {
8c1cb8aa 2558 if (!g_quiet) {
e8e12e8c 2559 SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping UDP NOTIFY from " << mappedSource.toString() << ", address not matched by allow-notify-from" << endl,
91092a9f 2560 g_slogudpin->info(Logr::Error, "Dropping UDP NOTIFY from address not matched by allow-notify-from",
7d0727b1 2561 "source", Logging::Loggable(mappedSource)));
35189b7d
KF
2562 }
2563
7d3d2f4f 2564 t_Counters.at(rec::Counter::sourceDisallowedNotify)++;
35189b7d
KF
2565 return;
2566 }
2567 }
2568
e8e12e8c
OM
2569 struct timeval tval = {0, 0};
2570 HarvestTimestamp(&msgh, &tval);
2571 ComboAddress destaddr; // the address the query was sent to to
2572 destaddr.reset(); // this makes sure we ignore this address if not returned by recvmsg above
22577619 2573 const auto* loc = rplookup(g_listenSocketsAddresses, fileDesc);
e8e12e8c 2574 if (HarvestDestinationAddress(&msgh, &destaddr)) {
c0a00acd 2575 // but.. need to get port too
e8e12e8c
OM
2576 if (loc != nullptr) {
2577 destaddr.sin4.sin_port = loc->sin4.sin_port;
c0a00acd 2578 }
a6147cd2 2579 }
2580 else {
e8e12e8c
OM
2581 if (loc != nullptr) {
2582 destaddr = *loc;
c0a00acd
RG
2583 }
2584 else {
e8e12e8c
OM
2585 destaddr.sin4.sin_family = fromaddr.sin4.sin_family;
2586 socklen_t slen = destaddr.getSocklen();
2587 getsockname(fileDesc, reinterpret_cast<sockaddr*>(&destaddr), &slen); // if this fails, we're ok with it // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
c0a00acd
RG
2588 }
2589 }
5216ddcc 2590 if (!proxyProto) {
e8e12e8c 2591 destination = destaddr;
5216ddcc 2592 }
c0a00acd 2593
048607b4 2594 if (RecThreadInfo::weDistributeQueries()) {
6e983628 2595 std::string localdata = data;
42d6b18e 2596 distributeAsyncFunction(data, [localdata = std::move(localdata), fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace]() mutable {
e8e12e8c 2597 return doProcessUDPQuestion(localdata, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace);
0f14442c 2598 });
c0a00acd
RG
2599 }
2600 else {
e8e12e8c 2601 doProcessUDPQuestion(data, fromaddr, destaddr, source, destination, mappedSource, tval, fileDesc, proxyProtocolValues, eventTrace);
a6147cd2 2602 }
2603 }
c0a00acd 2604 }
8c1cb8aa 2605 catch (const MOADNSException& mde) {
7d3d2f4f 2606 t_Counters.at(rec::Counter::clientParseError)++;
8c1cb8aa 2607 if (g_logCommonErrors) {
91092a9f
OM
2608 SLOG(g_log << Logger::Error << "Unable to parse packet from remote UDP client " << fromaddr.toString() << ": " << mde.what() << endl,
2609 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
2610 }
2611 }
8c1cb8aa 2612 catch (const std::runtime_error& e) {
7d3d2f4f 2613 t_Counters.at(rec::Counter::clientParseError)++;
8c1cb8aa 2614 if (g_logCommonErrors) {
91092a9f 2615 SLOG(g_log << Logger::Error << "Unable to parse packet from remote UDP client " << fromaddr.toString() << ": " << e.what() << endl,
7d0727b1 2616 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 2617 }
5db529f8
BH
2618 }
2619 }
c0a00acd
RG
2620 else {
2621 // cerr<<t_id<<" had error: "<<stringerror()<<endl;
8c1cb8aa 2622 if (firstQuery && errno == EAGAIN) {
7d3d2f4f 2623 t_Counters.at(rec::Counter::noPacketError)++;
c0a00acd 2624 }
390f1dab 2625
c0a00acd
RG
2626 break;
2627 }
ac0e821b 2628 }
7d3d2f4f 2629 t_Counters.updateSnap(g_regressionTestMode);
5db529f8
BH
2630}
2631
d61b8a01 2632void makeUDPServerSockets(deferredAdd_t& deferredAdds, Logr::log_t log)
288f4aa9 2633{
8c1cb8aa 2634 int one = 1;
99fad7f4
FM
2635 vector<string> localAddresses;
2636 stringtok(localAddresses, ::arg()["local-address"], " ,");
288f4aa9 2637
99fad7f4 2638 if (localAddresses.empty()) {
3f81d239 2639 throw PDNSException("No local address specified");
99fad7f4 2640 }
3ddb9247 2641
94fc1dc5 2642 const uint16_t defaultLocalPort = ::arg().asNum("local-port");
99fad7f4 2643 for (const auto& localAddress : localAddresses) {
94fc1dc5 2644 ComboAddress address{localAddress, defaultLocalPort};
99fad7f4
FM
2645 const int socketFd = socket(address.sin4.sin_family, SOCK_DGRAM, 0);
2646 if (socketFd < 0) {
8c1cb8aa 2647 throw PDNSException("Making a UDP server socket for resolver: " + stringerror());
d3b4137e 2648 }
99fad7f4 2649 if (!setSocketTimestamps(socketFd)) {
d8d1d955 2650 SLOG(g_log << Logger::Warning << "Unable to enable timestamp reporting for socket" << endl,
6cb5db59 2651 log->info(Logr::Warning, "Unable to enable timestamp reporting for socket"));
d8d1d955 2652 }
99fad7f4
FM
2653 if (IsAnyAddress(address)) {
2654 if (address.sin4.sin_family == AF_INET) {
e8e12e8c 2655 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
2656 g_fromtosockets.insert(socketFd);
2657 }
2658 }
757d3179 2659#ifdef IPV6_RECVPKTINFO
99fad7f4 2660 if (address.sin4.sin_family == AF_INET6) {
e8e12e8c 2661 if (setsockopt(socketFd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)) == 0) {
99fad7f4
FM
2662 g_fromtosockets.insert(socketFd);
2663 }
2664 }
757d3179 2665#endif
99fad7f4 2666 if (address.sin6.sin6_family == AF_INET6 && setsockopt(socketFd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)) < 0) {
a2a81d42 2667 int err = errno;
c42e8958
OM
2668 SLOG(g_log << Logger::Warning << "Failed to set IPv6 socket to IPv6 only, continuing anyhow: " << stringerror(err) << endl,
2669 log->error(Logr::Warning, "Failed to set IPv6 socket to IPv6 only, continuing anyhow"));
0dfa94ab 2670 }
b71b60ee 2671 }
99fad7f4
FM
2672 if (::arg().mustDo("non-local-bind")) {
2673 Utility::setBindAny(AF_INET6, socketFd);
2674 }
fec7dd5a 2675
99fad7f4 2676 setCloseOnExec(socketFd);
a903b39c 2677
f402f388 2678 try {
99fad7f4 2679 setSocketReceiveBuffer(socketFd, 250000);
f402f388
RG
2680 }
2681 catch (const std::exception& e) {
62b191dc 2682 SLOG(g_log << Logger::Error << e.what() << endl,
e5d6a4e1 2683 log->error(Logr::Error, e.what(), "Exception while setting socket buffer size"));
f402f388 2684 }
37d3f960 2685
665821e1
RG
2686 if (g_reusePort) {
2687#if defined(SO_REUSEPORT_LB)
2688 try {
398bb0f1 2689 SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
665821e1
RG
2690 }
2691 catch (const std::exception& e) {
2692 throw PDNSException(std::string("SO_REUSEPORT_LB: ") + e.what());
2693 }
2694#elif defined(SO_REUSEPORT)
2695 try {
99fad7f4 2696 SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT, 1);
665821e1
RG
2697 }
2698 catch (const std::exception& e) {
2699 throw PDNSException(std::string("SO_REUSEPORT: ") + e.what());
2700 }
2332f42d 2701#endif
665821e1 2702 }
90f9fbc0 2703
db63b4b6 2704 try {
99fad7f4 2705 setSocketIgnorePMTU(socketFd, address.sin4.sin_family);
db63b4b6 2706 }
8c1cb8aa 2707 catch (const std::exception& e) {
d8d1d955
OM
2708 SLOG(g_log << Logger::Warning << "Failed to set IP_MTU_DISCOVER on UDP server socket: " << e.what() << endl,
2709 log->error(Logr::Warning, e.what(), "Failed to set IP_MTU_DISCOVER on UDP server socket"));
90f9fbc0
RG
2710 }
2711
99fad7f4 2712 socklen_t socklen = address.getSocklen();
22577619 2713 if (::bind(socketFd, reinterpret_cast<struct sockaddr*>(&address), socklen) < 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
94fc1dc5 2714 throw PDNSException("Resolver binding to server socket on " + address.toStringWithPort() + ": " + stringerror());
99fad7f4 2715 }
3ddb9247 2716
99fad7f4 2717 setNonBlocking(socketFd);
c2136bf0 2718
99fad7f4
FM
2719 deferredAdds.emplace_back(socketFd, handleNewUDPQuestion);
2720 g_listenSocketsAddresses[socketFd] = address; // this is written to only from the startup thread, not from the workers
2721 SLOG(g_log << Logger::Info << "Listening for UDP queries on " << address.toStringWithPort() << endl,
2722 log->info(Logr::Info, "Listening for queries", "proto", Logging::Loggable("UDP"), "address", Logging::Loggable(address)));
f28307ad 2723 }
c836dc19 2724}
caa6eefa 2725
592d7ade 2726static bool trySendingQueryToWorker(unsigned int target, ThreadMSG* tmsg)
00c9b8c1 2727{
4f6e00dc 2728 auto& targetInfo = RecThreadInfo::info(target);
69b39198 2729 if (!targetInfo.isWorker()) {
91092a9f
OM
2730 SLOG(g_log << Logger::Error << "distributeAsyncFunction() tried to assign a query to a non-worker thread" << endl,
2731 g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() tried to assign a query to a non-worker thread"));
ce715f38 2732 _exit(1);
00c9b8c1 2733 }
d77abca1 2734
78199711 2735 const auto& tps = targetInfo.getPipes();
3ddb9247 2736
e8e12e8c 2737 ssize_t written = write(tps.writeQueriesToThread, &tmsg, sizeof(tmsg)); // NOLINT: correct sizeof
cf8cda18 2738 if (written > 0) {
e8e12e8c
OM
2739 if (static_cast<size_t>(written) != sizeof(tmsg)) { // NOLINT: correct sizeof
2740 delete tmsg; // NOLINT: pointer ownership
cf8cda18
RG
2741 unixDie("write to thread pipe returned wrong size or error");
2742 }
2743 }
2744 else {
2745 int error = errno;
cf8cda18 2746 if (error == EAGAIN || error == EWOULDBLOCK) {
592d7ade 2747 return false;
8c1cb8aa 2748 }
e8e12e8c
OM
2749 delete tmsg; // NOLINT: pointer ownership
2750 unixDie("write to thread pipe returned wrong size or error:" + std::to_string(error));
b841314c 2751 }
592d7ade
RG
2752
2753 return true;
2754}
2755
144040be
RG
2756static unsigned int getWorkerLoad(size_t workerIdx)
2757{
78199711 2758 const auto* multiThreader = RecThreadInfo::info(RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + workerIdx).getMT();
e8e12e8c
OM
2759 if (multiThreader != nullptr) {
2760 return multiThreader->numProcesses();
144040be
RG
2761 }
2762 return 0;
2763}
2764
2765static unsigned int selectWorker(unsigned int hash)
2766{
a808ca69 2767 assert(RecThreadInfo::numUDPWorkers() != 0); // NOLINT: assert implementation
b35191d7 2768 if (g_balancingFactor == 0) {
a808ca69 2769 return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + (hash % RecThreadInfo::numUDPWorkers());
144040be
RG
2770 }
2771
2772 /* we start with one, representing the query we are currently handling */
2773 double currentLoad = 1;
a808ca69
OM
2774 std::vector<unsigned int> load(RecThreadInfo::numUDPWorkers());
2775 for (size_t idx = 0; idx < RecThreadInfo::numUDPWorkers(); idx++) {
144040be
RG
2776 load[idx] = getWorkerLoad(idx);
2777 currentLoad += load[idx];
144040be
RG
2778 }
2779
a808ca69 2780 double targetLoad = (currentLoad / RecThreadInfo::numUDPWorkers()) * g_balancingFactor;
144040be 2781
a808ca69 2782 unsigned int worker = hash % RecThreadInfo::numUDPWorkers();
1b9d2d46 2783 /* at least one server has to be at or below the average load */
596bf482 2784 if (load[worker] > targetLoad) {
7d3d2f4f 2785 ++t_Counters.at(rec::Counter::rebalancedQueries);
596bf482 2786 do {
a808ca69 2787 worker = (worker + 1) % RecThreadInfo::numUDPWorkers();
8c1cb8aa 2788 } while (load[worker] > targetLoad);
144040be
RG
2789 }
2790
048607b4 2791 return RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + worker;
144040be
RG
2792}
2793
592d7ade
RG
2794// This function is only called by the distributor threads, when pdns-distributes-queries is set
2795void distributeAsyncFunction(const string& packet, const pipefunc_t& func)
2796{
69b39198 2797 if (!RecThreadInfo::self().isDistributor()) {
91092a9f 2798 SLOG(g_log << Logger::Error << "distributeAsyncFunction() has been called by a worker (" << RecThreadInfo::id() << ")" << endl,
c42e8958 2799 g_slog->withName("runtime")->info(Logr::Error, "distributeAsyncFunction() has been called by a worker")); // tid will be added
ce715f38 2800 _exit(1);
592d7ade
RG
2801 }
2802
e8e12e8c 2803 bool hashOK = false;
22577619 2804 unsigned int hash = hashQuestion(reinterpret_cast<const uint8_t*>(packet.data()), packet.length(), g_disthashseed, hashOK); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
e8e12e8c 2805 if (!hashOK) {
538c11f9 2806 // hashQuestion does detect invalid names, so we might as well punt here instead of in the worker thread
7d3d2f4f 2807 t_Counters.at(rec::Counter::ignoredCount)++;
538c11f9
O
2808 throw MOADNSException("too-short (" + std::to_string(packet.length()) + ") or invalid name");
2809 }
144040be 2810 unsigned int target = selectWorker(hash);
592d7ade 2811
e8e12e8c 2812 ThreadMSG* tmsg = new ThreadMSG(); // NOLINT: pointer ownership
592d7ade
RG
2813 tmsg->func = func;
2814 tmsg->wantAnswer = false;
2815
2816 if (!trySendingQueryToWorker(target, tmsg)) {
2817 /* if this function failed but did not raise an exception, it means that the pipe
2818 was full, let's try another one */
2819 unsigned int newTarget = 0;
2820 do {
a808ca69 2821 newTarget = RecThreadInfo::numHandlers() + RecThreadInfo::numDistributors() + dns_random(RecThreadInfo::numUDPWorkers());
592d7ade
RG
2822 } while (newTarget == target);
2823
2824 if (!trySendingQueryToWorker(newTarget, tmsg)) {
7d3d2f4f 2825 t_Counters.at(rec::Counter::queryPipeFullDrops)++;
e8e12e8c 2826 delete tmsg; // NOLINT: pointer ownership
592d7ade
RG
2827 }
2828 }
7db35c6e 2829 // coverity[leaked_storage]
00c9b8c1 2830}
3427fa8a 2831
34801ab1 2832// resend event to everybody chained onto it
a2eef000 2833static void doResends(MT_t::waiters_t::iterator& iter, const std::shared_ptr<PacketID>& resend, const PacketBuffer& content)
34801ab1 2834{
c647a254 2835 // We close the chain for new entries, since they won't be processed anyway
a2eef000 2836 iter->key->closed = true;
c647a254 2837
e8e12e8c 2838 if (iter->key->chain.empty()) {
34801ab1 2839 return;
e8e12e8c
OM
2840 }
2841 for (auto i = iter->key->chain.begin(); i != iter->key->chain.end(); ++i) {
2842 auto packetID = std::make_shared<PacketID>(*resend);
2843 packetID->fd = -1;
2844 packetID->id = *i;
2845 g_multiTasker->sendEvent(packetID, &content);
7d3d2f4f 2846 t_Counters.at(rec::Counter::chainResends)++;
34801ab1
BH
2847 }
2848}
2849
e8e12e8c 2850static void handleUDPServerResponse(int fileDesc, FDMultiplexer::funcparam_t& var)
09e6702a 2851{
e8e12e8c 2852 auto pid = boost::any_cast<std::shared_ptr<PacketID>>(var);
2a17f6c6 2853 PacketBuffer packet;
fae8fe07 2854 packet.resize(g_outgoingEDNSBufsize);
996c89cc 2855 ComboAddress fromaddr;
8c1cb8aa 2856 socklen_t addrlen = sizeof(fromaddr);
09e6702a 2857
22577619 2858 ssize_t len = recvfrom(fileDesc, &packet.at(0), packet.size(), 0, reinterpret_cast<sockaddr*>(&fromaddr), &addrlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
c1da7976 2859
5f5cd2fe 2860 const ssize_t signed_sizeof_sdnsheader = sizeof(dnsheader);
34801ab1 2861
5f5cd2fe
OM
2862 if (len < 0) {
2863 // len < 0: error on socket
e8e12e8c 2864 t_udpclientsocks->returnSocket(fileDesc);
34801ab1 2865
5f5cd2fe 2866 PacketBuffer empty;
2e8bc0d8
OM
2867 auto iter = g_multiTasker->getWaiters().find(pid);
2868 if (iter != g_multiTasker->getWaiters().end()) {
34801ab1 2869 doResends(iter, pid, empty);
5f5cd2fe 2870 }
e8e12e8c 2871 g_multiTasker->sendEvent(pid, &empty); // this denotes error (does retry lookup using other NS)
5f5cd2fe
OM
2872 return;
2873 }
3ddb9247 2874
5f5cd2fe
OM
2875 if (len < signed_sizeof_sdnsheader) {
2876 // We have received a packet that cannot be a valid DNS packet, as it has no complete header
2877 // Drop it, but continue to wait for other packets
2878 t_Counters.at(rec::Counter::serverParseError)++;
2879 if (g_logCommonErrors) {
2880 SLOG(g_log << Logger::Error << "Unable to parse too short packet from remote UDP server " << fromaddr.toString() << ": packet smaller than DNS header" << endl,
2881 g_slogout->info(Logr::Error, "Unable to parse too short packet from remote UDP server", "from", Logging::Loggable(fromaddr)));
2882 }
998a4334 2883 return;
3ddb9247 2884 }
998a4334 2885
5f5cd2fe 2886 // We have at least a full header
fae8fe07 2887 packet.resize(len);
e8e12e8c
OM
2888 dnsheader dnsheader{};
2889 memcpy(&dnsheader, &packet.at(0), sizeof(dnsheader));
3ddb9247 2890
a2eef000
O
2891 auto pident = std::make_shared<PacketID>();
2892 pident->remote = fromaddr;
e8e12e8c
OM
2893 pident->id = dnsheader.id;
2894 pident->fd = fileDesc;
34801ab1 2895
e8e12e8c 2896 if (!dnsheader.qr && g_logCommonErrors) {
91092a9f
OM
2897 SLOG(g_log << Logger::Notice << "Not taking data from question on outgoing socket from " << fromaddr.toStringWithPort() << endl,
2898 g_slogout->info(Logr::Error, "Not taking data from question on outgoing socket", "from", Logging::Loggable(fromaddr)));
6da3b3ad
PD
2899 }
2900
e8e12e8c
OM
2901 if (dnsheader.qdcount == 0U || // UPC, Nominum, very old BIND on FormErr, NSD
2902 dnsheader.qr == 0U) { // one weird server
a2eef000
O
2903 pident->domain.clear();
2904 pident->type = 0;
6da3b3ad
PD
2905 }
2906 else {
2907 try {
5f5cd2fe 2908 if (len > signed_sizeof_sdnsheader) {
e8e12e8c 2909 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
2910 }
2911 else {
2912 // len == sizeof(dnsheader), only header case
2913 // We will do a full scan search later to see if we can match this reply even without a domain
2914 pident->domain.clear();
2915 pident->type = 0;
2916 }
6da3b3ad 2917 }
8c1cb8aa 2918 catch (std::exception& e) {
5f5cd2fe 2919 // Parse error, continue waiting for other packets
7d3d2f4f 2920 t_Counters.at(rec::Counter::serverParseError)++; // won't be fed to lwres.cc, so we have to increment
91092a9f 2921 SLOG(g_log << Logger::Warning << "Error in packet from remote nameserver " << fromaddr.toStringWithPort() << ": " << e.what() << endl,
9b686b05 2922 g_slogudpin->error(Logr::Warning, e.what(), "Error in packet from remote nameserver", "from", Logging::Loggable(fromaddr)));
6da3b3ad 2923 return;
34801ab1 2924 }
6da3b3ad 2925 }
34801ab1 2926
5f5cd2fe 2927 if (!pident->domain.empty()) {
2e8bc0d8
OM
2928 auto iter = g_multiTasker->getWaiters().find(pident);
2929 if (iter != g_multiTasker->getWaiters().end()) {
5f5cd2fe
OM
2930 doResends(iter, pident, packet);
2931 }
6da3b3ad 2932 }
c1da7976 2933
6da3b3ad 2934retryWithName:
4957a608 2935
e8e12e8c 2936 if (pident->domain.empty() || g_multiTasker->sendEvent(pident, &packet) == 0) {
9ec48f21
RG
2937 /* we did not find a match for this response, something is wrong */
2938
6da3b3ad 2939 // 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 2940 for (const auto& d_waiter : g_multiTasker->getWaiters()) {
e8e12e8c 2941 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
2942 /* we are expecting an answer from that exact source, on that exact port (since we are using connected sockets), for that qname/qtype,
2943 but with a different message ID. That smells like a spoofing attempt. For now we will just increase the counter and will deal with
2944 that later. */
e8e12e8c 2945 d_waiter.key->nearMisses++;
998a4334 2946 }
6da3b3ad
PD
2947
2948 // be a bit paranoid here since we're weakening our matching
e8e12e8c 2949 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 2950 // cerr<<"Empty response, rest matches though, sending to a waiter"<<endl;
e8e12e8c
OM
2951 pident->domain = d_waiter.key->domain;
2952 pident->type = d_waiter.key->type;
2953 goto retryWithName; // note that this only passes on an error, lwres will still reject the packet NOLINT(cppcoreguidelines-avoid-goto)
d4fb76e9 2954 }
09e6702a 2955 }
7d3d2f4f 2956 t_Counters.at(rec::Counter::unexpectedCount)++; // if we made it here, it really is an unexpected answer
8c1cb8aa 2957 if (g_logCommonErrors) {
2e8bc0d8 2958 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 2959 g_slogudpin->info(Logr::Warning, "Discarding unexpected packet", "from", Logging::Loggable(fromaddr),
7d0727b1 2960 "qname", Logging::Loggable(pident->domain),
91092a9f 2961 "qtype", Logging::Loggable(QType(pident->type)),
2e8bc0d8 2962 "waiters", Logging::Loggable(g_multiTasker->getWaiters().size())));
d8f6d49f 2963 }
09e6702a 2964 }
e8e12e8c 2965 else if (fileDesc >= 0) {
9ec48f21 2966 /* we either found a waiter (1) or encountered an issue (-1), it's up to us to clean the socket anyway */
e8e12e8c 2967 t_udpclientsocks->returnSocket(fileDesc);
6da3b3ad 2968 }
09e6702a 2969}