• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 16615247828

30 Jul 2025 06:33AM UTC coverage: 65.845% (-0.03%) from 65.87%
16615247828

Pull #15942

github

web-flow
Merge 3e4243857 into 4a7b6a621
Pull Request #15942: Optimize reload-zones logic to reduce thread scheduling times

42051 of 92438 branches covered (45.49%)

Branch coverage included in aggregate %.

4 of 4 new or added lines in 1 file covered. (100.0%)

48 existing lines in 12 files now uncovered.

127942 of 165732 relevant lines covered (77.2%)

5529729.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.84
/pdns/recursordist/lwres.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22
#ifdef HAVE_CONFIG_H
23
#include "config.h"
24
#endif
25
#include "utility.hh"
26
#include "lwres.hh"
27
#include <iostream>
28
#include "dnsrecords.hh"
29
#include <cerrno>
30
#include "misc.hh"
31
#include <algorithm>
32
#include <sstream>
33
#include <cstring>
34
#include <string>
35
#include <vector>
36
#include "dns.hh"
37
#include "qtype.hh"
38
#include "pdnsexception.hh"
39
#include "arguments.hh"
40
#include "sstuff.hh"
41
#include "syncres.hh"
42
#include "dnswriter.hh"
43
#include "dnsparser.hh"
44
#include "logger.hh"
45
#include "dns_random.hh"
46
#include <boost/scoped_array.hpp>
47
#include <boost/algorithm/string.hpp>
48
#include "validate-recursor.hh"
49
#include "ednssubnet.hh"
50
#include "query-local-address.hh"
51
#include "tcpiohandler.hh"
52
#include "ednsoptions.hh"
53
#include "ednspadding.hh"
54
#include "rec-protozero.hh"
55
#include "uuid-utils.hh"
56
#include "rec-tcpout.hh"
57

58
thread_local TCPOutConnectionManager t_tcp_manager;
59
std::shared_ptr<Logr::Logger> g_slogout;
60
bool g_paddingOutgoing;
61
bool g_ECSHardening;
62

63
void remoteLoggerQueueData(RemoteLoggerInterface& rli, const std::string& data)
64
{
200✔
65
  auto ret = rli.queueData(data);
200✔
66

67
  switch (ret) {
200!
68
  case RemoteLoggerInterface::Result::Queued:
200!
69
    break;
200✔
70
  case RemoteLoggerInterface::Result::PipeFull: {
×
71
    const auto& msg = RemoteLoggerInterface::toErrorString(ret);
×
72
    SLOG(g_log << Logger::Debug << rli.name() << ": " << msg << std::endl,
×
73
         g_slog->withName(rli.name())->info(Logr::Debug, msg));
×
74
    break;
×
75
  }
×
76
  case RemoteLoggerInterface::Result::TooLarge: {
×
77
    const auto& msg = RemoteLoggerInterface::toErrorString(ret);
×
78
    SLOG(g_log << Logger::Notice << rli.name() << ": " << msg << endl,
×
79
         g_slog->withName(rli.name())->info(Logr::Debug, msg));
×
80
    break;
×
81
  }
×
82
  case RemoteLoggerInterface::Result::OtherError: {
×
83
    const auto& msg = RemoteLoggerInterface::toErrorString(ret);
×
84
    SLOG(g_log << Logger::Warning << rli.name() << ": " << msg << std::endl,
×
85
         g_slog->withName(rli.name())->info(Logr::Warning, msg));
×
86
    break;
×
87
  }
×
88
  }
200✔
89
}
200✔
90

91
#ifdef HAVE_FSTRM
92
#include "dnstap.hh"
93
#include "fstrm_logger.hh"
94

95
static bool isEnabledForQueries(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
96
{
11,079✔
97
  if (fstreamLoggers == nullptr) {
11,084✔
98
    return false;
11,080✔
99
  }
11,080✔
100
  for (auto& logger : *fstreamLoggers) {
2,147,483,651✔
101
    if (logger->logQueries()) {
4✔
102
      return true;
2✔
103
    }
2✔
104
  }
4✔
105
  return false;
2,147,483,649✔
106
}
2,147,483,651✔
107

108
static void logFstreamQuery(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers, const struct timeval& queryTime, const ComboAddress& localip, const ComboAddress& address, DnstapMessage::ProtocolType protocol, const boost::optional<const DNSName&>& auth, const vector<uint8_t>& packet)
109
{
2✔
110
  if (fstreamLoggers == nullptr)
2!
111
    return;
×
112

113
  struct timespec ts;
2✔
114
  TIMEVAL_TO_TIMESPEC(&queryTime, &ts);
2✔
115
  std::string str;
2✔
116
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
117
  DnstapMessage message(std::move(str), DnstapMessage::MessageType::resolver_query, SyncRes::s_serverID, &localip, &address, protocol, reinterpret_cast<const char*>(packet.data()), packet.size(), &ts, nullptr, auth);
2✔
118
  str = message.getBuffer();
2✔
119

120
  for (auto& logger : *fstreamLoggers) {
2✔
121
    remoteLoggerQueueData(*logger, str);
2✔
122
  }
2✔
123
}
2✔
124

125
static bool isEnabledForResponses(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
126
{
11,082✔
127
  if (fstreamLoggers == nullptr) {
11,083✔
128
    return false;
11,075✔
129
  }
11,075✔
130
  for (auto& logger : *fstreamLoggers) {
2,147,483,651✔
131
    if (logger->logResponses()) {
4!
132
      return true;
4✔
133
    }
4✔
134
  }
4✔
135
  return false;
2,147,483,647✔
136
}
2,147,483,651✔
137

138
static void logFstreamResponse(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers, const ComboAddress& localip, const ComboAddress& address, DnstapMessage::ProtocolType protocol, const boost::optional<const DNSName&>& auth, const PacketBuffer& packet, const struct timeval& queryTime, const struct timeval& replyTime)
139
{
×
140
  if (fstreamLoggers == nullptr)
×
141
    return;
×
142

143
  struct timespec ts1, ts2;
×
144
  TIMEVAL_TO_TIMESPEC(&queryTime, &ts1);
×
145
  TIMEVAL_TO_TIMESPEC(&replyTime, &ts2);
×
146
  std::string str;
×
147
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
148
  DnstapMessage message(std::move(str), DnstapMessage::MessageType::resolver_response, SyncRes::s_serverID, &localip, &address, protocol, reinterpret_cast<const char*>(packet.data()), packet.size(), &ts1, &ts2, auth);
×
149
  str = message.getBuffer();
×
150

151
  for (auto& logger : *fstreamLoggers) {
×
152
    remoteLoggerQueueData(*logger, str);
×
153
  }
×
154
}
×
155

156
#endif // HAVE_FSTRM
157

158
static void logOutgoingQuery(const std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, const boost::optional<const boost::uuids::uuid&>& initialRequestId, const boost::uuids::uuid& uuid, const ComboAddress& address, const DNSName& domain, int type, uint16_t qid, bool doTCP, bool tls, size_t bytes, const boost::optional<Netmask>& srcmask, const std::string& nsName)
159
{
22✔
160
  if (!outgoingLoggers) {
22!
161
    return;
×
162
  }
×
163

164
  bool log = false;
22✔
165
  for (auto& logger : *outgoingLoggers) {
29✔
166
    if (logger->logQueries()) {
29✔
167
      log = true;
15✔
168
      break;
15✔
169
    }
15✔
170
  }
29✔
171

172
  if (!log) {
22✔
173
    return;
7✔
174
  }
7✔
175

176
  static thread_local std::string buffer;
15✔
177
  buffer.clear();
15✔
178
  pdns::ProtoZero::Message m{buffer};
15✔
179
  m.setType(pdns::ProtoZero::Message::MessageType::DNSOutgoingQueryType);
15✔
180
  m.setMessageIdentity(uuid);
15✔
181
  m.setSocketFamily(address.sin4.sin_family);
15✔
182
  if (!doTCP) {
15!
183
    m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::UDP);
15✔
184
  }
15✔
185
  else if (!tls) {
×
186
    m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::TCP);
×
187
  }
×
188
  else {
×
189
    m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::DoT);
×
190
  }
×
191

192
  m.setTo(address);
15✔
193
  m.setInBytes(bytes);
15✔
194
  m.setTime();
15✔
195
  m.setId(qid);
15✔
196
  m.setQuestion(domain, type, QClass::IN);
15✔
197
  m.setToPort(address.getPort());
15✔
198
  m.setServerIdentity(SyncRes::s_serverID);
15✔
199

200
  if (initialRequestId) {
15!
201
    m.setInitialRequestID(*initialRequestId);
15✔
202
  }
15✔
203

204
  if (srcmask) {
15✔
205
    m.setEDNSSubnet(*srcmask, 128);
6✔
206
  }
6✔
207

208
  if (!nsName.empty()) {
15!
209
    m.setMeta("nsName", {nsName}, {});
15✔
210
  }
15✔
211
  for (auto& logger : *outgoingLoggers) {
30✔
212
    if (logger->logQueries()) {
30!
213
      remoteLoggerQueueData(*logger, buffer);
30✔
214
    }
30✔
215
  }
30✔
216
}
15✔
217

218
static void logIncomingResponse(const std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, const boost::optional<const boost::uuids::uuid&>& initialRequestId, const boost::uuids::uuid& uuid, const ComboAddress& address, const DNSName& domain, int type, uint16_t qid, bool doTCP, bool tls, const boost::optional<Netmask>& srcmask, size_t bytes, int rcode, const std::vector<DNSRecord>& records, const struct timeval& queryTime, const std::set<uint16_t>& exportTypes, const std::string& nsName)
219
{
22✔
220
  if (!outgoingLoggers) {
22!
221
    return;
×
222
  }
×
223

224
  bool log = false;
22✔
225
  for (auto& logger : *outgoingLoggers) {
22!
226
    if (logger->logResponses()) {
22!
227
      log = true;
22✔
228
      break;
22✔
229
    }
22✔
230
  }
22✔
231

232
  if (!log) {
22!
233
    return;
×
234
  }
×
235

236
  static thread_local std::string buffer;
22✔
237
  buffer.clear();
22✔
238
  pdns::ProtoZero::RecMessage m{buffer};
22✔
239
  m.setType(pdns::ProtoZero::Message::MessageType::DNSIncomingResponseType);
22✔
240
  m.setMessageIdentity(uuid);
22✔
241
  m.setSocketFamily(address.sin4.sin_family);
22✔
242
  if (!doTCP) {
22!
243
    m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::UDP);
22✔
244
  }
22✔
245
  else if (!tls) {
×
246
    m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::TCP);
×
247
  }
×
248
  else {
×
249
    m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::DoT);
×
250
  }
×
251
  m.setTo(address);
22✔
252
  m.setInBytes(bytes);
22✔
253
  m.setTime();
22✔
254
  m.setId(qid);
22✔
255
  m.setQuestion(domain, type, QClass::IN);
22✔
256
  m.setToPort(address.getPort());
22✔
257
  m.setServerIdentity(SyncRes::s_serverID);
22✔
258

259
  if (initialRequestId) {
22!
260
    m.setInitialRequestID(*initialRequestId);
22✔
261
  }
22✔
262

263
  if (srcmask) {
22!
264
    m.setEDNSSubnet(*srcmask, 128);
×
265
  }
×
266
  if (!nsName.empty()) {
22!
267
    m.setMeta("nsName", {nsName}, {});
22✔
268
  }
22✔
269

270
  m.startResponse();
22✔
271
  m.setQueryTime(queryTime.tv_sec, queryTime.tv_usec);
22✔
272
  if (rcode == -1) {
22!
273
    m.setNetworkErrorResponseCode();
×
274
  }
×
275
  else {
22✔
276
    m.setResponseCode(rcode);
22✔
277
  }
22✔
278

279
  for (const auto& record : records) {
90✔
280
    m.addRR(record, exportTypes, std::nullopt);
90✔
281
  }
90✔
282
  m.commitResponse();
22✔
283

284
  for (auto& logger : *outgoingLoggers) {
44✔
285
    if (logger->logResponses()) {
44!
286
      remoteLoggerQueueData(*logger, buffer);
44✔
287
    }
44✔
288
  }
44✔
289
}
22✔
290

291
static bool tcpconnect(const ComboAddress& ip, TCPOutConnectionManager::Connection& connection, bool& dnsOverTLS, const std::string& nsName)
292
{
35✔
293
  dnsOverTLS = SyncRes::s_dot_to_port_853 && ip.getPort() == 853;
35!
294

295
  connection = t_tcp_manager.get(ip);
35✔
296
  if (connection.d_handler) {
35✔
297
    return false;
4✔
298
  }
4✔
299

300
  const struct timeval timeout{
31✔
301
    g_networkTimeoutMsec / 1000, static_cast<suseconds_t>(g_networkTimeoutMsec) % 1000 * 1000};
31✔
302
  Socket s(ip.sin4.sin_family, SOCK_STREAM);
31✔
303
  s.setNonBlocking();
31✔
304
  setTCPNoDelay(s.getHandle());
31✔
305
  ComboAddress localip = pdns::getQueryLocalAddress(ip.sin4.sin_family, 0);
31✔
306
  s.bind(localip);
31✔
307

308
  std::shared_ptr<TLSCtx> tlsCtx{nullptr};
31✔
309
  if (dnsOverTLS) {
31✔
310
    TLSContextParameters tlsParams;
5✔
311
    tlsParams.d_provider = "openssl";
5✔
312
    tlsParams.d_validateCertificates = false;
5✔
313
    // tlsParams.d_caStore
314
    tlsCtx = getTLSContext(tlsParams);
5✔
315
    if (tlsCtx == nullptr) {
5!
316
      SLOG(g_log << Logger::Error << "DoT to " << ip << " requested but not available" << endl,
×
317
           g_slogout->info(Logr::Error, "DoT requested but not available", "server", Logging::Loggable(ip)));
×
318
      dnsOverTLS = false;
×
319
    }
×
320
  }
5✔
321
  connection.d_handler = std::make_shared<TCPIOHandler>(nsName, false, s.releaseHandle(), timeout, tlsCtx);
31✔
322
  // Returned state ignored
323
  // This can throw an exception, retry will need to happen at higher level
324
  connection.d_handler->tryConnect(SyncRes::s_tcp_fast_open_connect, ip);
31✔
325
  return true;
31✔
326
}
35✔
327

328
static LWResult::Result tcpsendrecv(const ComboAddress& ip, TCPOutConnectionManager::Connection& connection,
329
                                    ComboAddress& localip, const vector<uint8_t>& vpacket, size_t& len, PacketBuffer& buf)
330
{
35✔
331
  socklen_t slen = ip.getSocklen();
35✔
332
  uint16_t tlen = htons(vpacket.size());
35✔
333
  const char* lenP = reinterpret_cast<const char*>(&tlen);
35✔
334

335
  len = 0; // in case of error
35✔
336
  localip.sin4.sin_family = ip.sin4.sin_family;
35✔
337
  if (getsockname(connection.d_handler->getDescriptor(), reinterpret_cast<sockaddr*>(&localip), &slen) != 0) {
35!
338
    return LWResult::Result::PermanentError;
×
339
  }
×
340

341
  PacketBuffer packet;
35✔
342
  packet.reserve(2 + vpacket.size());
35✔
343
  packet.insert(packet.end(), lenP, lenP + 2);
35✔
344
  packet.insert(packet.end(), vpacket.begin(), vpacket.end());
35✔
345

346
  LWResult::Result ret = asendtcp(packet, connection.d_handler);
35✔
347
  if (ret != LWResult::Result::Success) {
35✔
348
    return ret;
2✔
349
  }
2✔
350

351
  ret = arecvtcp(packet, 2, connection.d_handler, false);
33✔
352
  if (ret != LWResult::Result::Success) {
33!
353
    return ret;
×
354
  }
×
355

356
  memcpy(&tlen, packet.data(), sizeof(tlen));
33✔
357
  len = ntohs(tlen); // switch to the 'len' shared with the rest of the calling function
33✔
358

359
  // XXX receive into buf directly?
360
  packet.resize(len);
33✔
361
  ret = arecvtcp(packet, len, connection.d_handler, false);
33✔
362
  if (ret != LWResult::Result::Success) {
33!
363
    return ret;
×
364
  }
×
365
  buf.resize(len);
33✔
366
  memcpy(buf.data(), packet.data(), len);
33✔
367
  return LWResult::Result::Success;
33✔
368
}
33✔
369

370
static void addPadding(const DNSPacketWriter& pw, size_t bufsize, DNSPacketWriter::optvect_t& opts)
371
{
9✔
372
  const size_t currentSize = pw.getSizeWithOpts(opts);
9✔
373
  if (currentSize < (bufsize - 4)) {
9!
374
    const size_t remaining = bufsize - (currentSize + 4);
9✔
375
    /* from rfc8647, "4.1.  Recommended Strategy: Block-Length Padding":
376
       Clients SHOULD pad queries to the closest multiple of 128 octets.
377
       Note we are in the client role here.
378
    */
379
    const size_t blockSize = 128;
9✔
380
    const size_t modulo = (currentSize + 4) % blockSize;
9✔
381
    size_t padSize = 0;
9✔
382
    if (modulo > 0) {
9!
383
      padSize = std::min(blockSize - modulo, remaining);
9✔
384
    }
9✔
385
    opts.emplace_back(EDNSOptionCode::PADDING, makeEDNSPaddingOptString(padSize));
9✔
386
  }
9✔
387
}
9✔
388

389
/** lwr is only filled out in case 1 was returned, and even when returning 1 for 'success', lwr might contain DNS errors
390
    Never throws!
391
 */
392
// NOLINTNEXTLINE(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
393
static LWResult::Result asyncresolve(const ComboAddress& address, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, const ResolveContext& context, const std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, [[maybe_unused]] const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstrmLoggers, const std::set<uint16_t>& exportTypes, LWResult* lwr, bool* chained, TCPOutConnectionManager::Connection& connection)
394
{
11,085✔
395
  size_t len;
11,085✔
396
  size_t bufsize = g_outgoingEDNSBufsize;
11,085✔
397
  PacketBuffer buf;
11,085✔
398
  buf.resize(bufsize);
11,085✔
399
  vector<uint8_t> vpacket;
11,085✔
400
  //  string mapped0x20=dns0x20(domain);
401
  uint16_t qid = dns_random_uint16();
11,085✔
402
  DNSPacketWriter pw(vpacket, domain, type);
11,085✔
403
  bool dnsOverTLS = SyncRes::s_dot_to_port_853 && address.getPort() == 853;
11,085✔
404
  std::string nsName;
11,085✔
405
  if (!context.d_nsName.empty()) {
11,085✔
406
    nsName = context.d_nsName.toStringNoDot();
10,977✔
407
  }
10,977✔
408

409
  pw.getHeader()->rd = sendRDQuery;
11,085✔
410
  pw.getHeader()->id = qid;
11,085✔
411
  /* RFC 6840 section 5.9:
412
   *  This document further specifies that validating resolvers SHOULD set
413
   *  the CD bit on every upstream query.  This is regardless of whether
414
   *  the CD bit was set on the incoming query [...]
415
   *
416
   * sendRDQuery is only true if the qname is part of a forward-zone-recurse (or
417
   * set in the forward-zone-file), so we use this as an indicator for it being
418
   * an "upstream query". To stay true to "dnssec=off means 3.X behaviour", we
419
   * only set +CD on forwarded query in any mode other than dnssec=off.
420
   */
421
  pw.getHeader()->cd = (sendRDQuery && g_dnssecmode != DNSSECMode::Off);
11,085!
422

423
  string ping;
11,085✔
424
  std::optional<EDNSSubnetOpts> subnetOpts = std::nullopt;
11,085✔
425
  if (EDNS0Level > 0) {
11,085✔
426
    DNSPacketWriter::optvect_t opts;
11,084✔
427
    if (srcmask) {
11,084✔
428
      subnetOpts = EDNSSubnetOpts{};
568✔
429
      subnetOpts->setSource(*srcmask);
568✔
430
      opts.emplace_back(EDNSOptionCode::ECS, subnetOpts->makeOptString());
568✔
431
    }
568✔
432

433
    if (dnsOverTLS && g_paddingOutgoing) {
11,084!
434
      addPadding(pw, bufsize, opts);
9✔
435
    }
9✔
436

437
    pw.addOpt(g_outgoingEDNSBufsize, 0, g_dnssecmode == DNSSECMode::Off ? 0 : EDNSOpts::DNSSECOK, opts);
11,084✔
438
    pw.commit();
11,084✔
439
  }
11,084✔
440
  lwr->d_rcode = 0;
11,085✔
441
  lwr->d_haveEDNS = false;
11,085✔
442
  LWResult::Result ret;
11,085✔
443

444
  DTime dt;
11,085✔
445
  dt.set();
11,085✔
446
  *now = dt.getTimeval();
11,085✔
447

448
  boost::uuids::uuid uuid;
11,085✔
449
  const struct timeval queryTime = *now;
11,085✔
450

451
  if (outgoingLoggers) {
11,085✔
452
    uuid = getUniqueID();
22✔
453
    logOutgoingQuery(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, vpacket.size(), srcmask, nsName);
22✔
454
  }
22✔
455

456
  srcmask = boost::none; // this is also our return value, even if EDNS0Level == 0
11,085✔
457

458
  // We only store the localip if needed for fstrm logging
459
  ComboAddress localip;
11,085✔
460
#ifdef HAVE_FSTRM
11,085✔
461
  bool fstrmQEnabled = false;
11,085✔
462
  bool fstrmREnabled = false;
11,085✔
463

464
  if (isEnabledForQueries(fstrmLoggers)) {
11,085✔
465
    fstrmQEnabled = true;
2✔
466
  }
2✔
467
  if (isEnabledForResponses(fstrmLoggers)) {
11,085✔
468
    fstrmREnabled = true;
4✔
469
  }
4✔
470
#endif
11,085✔
471

472
  if (!doTCP) {
11,085✔
473
    int queryfd;
11,050✔
474

475
    ret = asendto(vpacket.data(), vpacket.size(), 0, address, qid, domain, type, subnetOpts, &queryfd, *now);
11,050✔
476

477
    if (ret != LWResult::Result::Success) {
11,050!
478
      return ret;
×
479
    }
×
480

481
    if (queryfd <= -1) {
11,050✔
482
      *chained = true;
1,613✔
483
    }
1,613✔
484

485
#ifdef HAVE_FSTRM
11,050✔
486
    if (!*chained) {
11,050✔
487
      if (fstrmQEnabled || fstrmREnabled) {
9,422✔
488
        localip.sin4.sin_family = address.sin4.sin_family;
4✔
489
        socklen_t slen = address.getSocklen();
4✔
490
        (void)getsockname(queryfd, reinterpret_cast<sockaddr*>(&localip), &slen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast))
4✔
491
      }
4✔
492
      if (fstrmQEnabled) {
9,421✔
493
        logFstreamQuery(fstrmLoggers, queryTime, localip, address, DnstapMessage::ProtocolType::DoUDP, context.d_auth ? context.d_auth : boost::none, vpacket);
2!
494
      }
2✔
495
    }
9,421✔
496
#endif /* HAVE_FSTRM */
11,050✔
497

498
    // sleep until we see an answer to this, interface to mtasker
499
    ret = arecvfrom(buf, 0, address, len, qid, domain, type, queryfd, subnetOpts, *now);
11,050✔
500
  }
11,050✔
501
  else {
35✔
502
    bool isNew;
35✔
503
    do {
35✔
504
      try {
35✔
505
        // If we get a new (not re-used) TCP connection that does not
506
        // work, we give up. For reused connections, we assume the
507
        // peer has closed it on error, so we retry. At some point we
508
        // *will* get a new connection, so this loop is not endless.
509
        isNew = true; // tcpconnect() might throw for new connections. In that case, we want to break the loop, scanbuild complains here, which is a false positive afaik
35✔
510
        isNew = tcpconnect(address, connection, dnsOverTLS, nsName);
35✔
511
        ret = tcpsendrecv(address, connection, localip, vpacket, len, buf);
35✔
512
#ifdef HAVE_FSTRM
35✔
513
        if (fstrmQEnabled) {
35!
514
          logFstreamQuery(fstrmLoggers, queryTime, localip, address, !dnsOverTLS ? DnstapMessage::ProtocolType::DoTCP : DnstapMessage::ProtocolType::DoT, context.d_auth, vpacket);
×
515
        }
×
516
#endif /* HAVE_FSTRM */
35✔
517
        if (ret == LWResult::Result::Success) {
35✔
518
          break;
33✔
519
        }
33✔
520
        connection.d_handler->close();
2✔
521
      }
2✔
522
      catch (const NetworkError&) {
35✔
523
        ret = LWResult::Result::OSLimitError; // OS limits error
×
524
      }
×
525
      catch (const runtime_error&) {
35✔
526
        ret = LWResult::Result::OSLimitError; // OS limits error (PermanentError is transport related)
×
527
      }
×
528
    } while (!isNew);
35!
529
  }
35✔
530

531
  lwr->d_usec = dt.udiff();
11,085✔
532
  *now = dt.getTimeval();
11,085✔
533

534
  if (ret != LWResult::Result::Success) { // includes 'timeout'
11,085✔
535
    if (outgoingLoggers) {
345!
536
      logIncomingResponse(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, srcmask, 0, -1, {}, queryTime, exportTypes, nsName);
×
537
    }
×
538
    return ret;
345✔
539
  }
345✔
540

541
  if (*chained) {
10,740✔
542
    auto msec = lwr->d_usec / 1000;
1,367✔
543
    if (msec > g_networkTimeoutMsec * 2 / 3) {
1,367!
UNCOV
544
      auto jitterMsec = dns_random(msec);
×
UNCOV
545
      if (jitterMsec > 0) {
×
UNCOV
546
        mthreadSleep(jitterMsec);
×
UNCOV
547
      }
×
UNCOV
548
    }
×
549
  }
1,367✔
550

551
  buf.resize(len);
10,740✔
552

553
#ifdef HAVE_FSTRM
10,740✔
554
  if (fstrmREnabled && (!*chained || doTCP)) {
10,740!
555
    DnstapMessage::ProtocolType protocol = doTCP ? DnstapMessage::ProtocolType::DoTCP : DnstapMessage::ProtocolType::DoUDP;
×
556
    if (dnsOverTLS) {
×
557
      protocol = DnstapMessage::ProtocolType::DoT;
×
558
    }
×
559
    logFstreamResponse(fstrmLoggers, localip, address, protocol, context.d_auth, buf, queryTime, *now);
×
560
  }
×
561
#endif /* HAVE_FSTRM */
10,740✔
562

563
  lwr->d_records.clear();
10,740✔
564
  try {
10,740✔
565
    lwr->d_tcbit = 0;
10,740✔
566
    MOADNSParser mdp(false, reinterpret_cast<const char*>(buf.data()), buf.size());
10,740✔
567
    lwr->d_aabit = mdp.d_header.aa;
10,740✔
568
    lwr->d_tcbit = mdp.d_header.tc;
10,740✔
569
    lwr->d_rcode = mdp.d_header.rcode;
10,740✔
570

571
    if (mdp.d_header.rcode == RCode::FormErr && mdp.d_qname.empty() && mdp.d_qtype == 0 && mdp.d_qclass == 0) {
10,740!
572
      if (outgoingLoggers) {
×
573
        logIncomingResponse(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, srcmask, len, lwr->d_rcode, lwr->d_records, queryTime, exportTypes, nsName);
×
574
      }
×
575
      lwr->d_validpacket = true;
×
576
      return LWResult::Result::Success; // this is "success", the error is set in lwr->d_rcode
×
577
    }
×
578

579
    if (domain != mdp.d_qname) {
10,740!
580
      if (!mdp.d_qname.empty() && domain.toString().find((char)0) == string::npos /* ugly */) { // embedded nulls are too noisy, plus empty domains are too
×
581
        SLOG(g_log << Logger::Notice << "Packet purporting to come from remote server " << address.toString() << " contained wrong answer: '" << domain << "' != '" << mdp.d_qname << "'" << endl,
×
582
             g_slogout->info(Logr::Notice, "Packet purporting to come from remote server contained wrong answer",
×
583
                             "server", Logging::Loggable(address),
×
584
                             "qname", Logging::Loggable(domain),
×
585
                             "onwire", Logging::Loggable(mdp.d_qname)));
×
586
      }
×
587
      // unexpected count has already been done @ pdns_recursor.cc
588
      goto out;
×
589
    }
×
590

591
    lwr->d_records.reserve(mdp.d_answers.size());
10,740✔
592
    for (const auto& answer : mdp.d_answers) {
68,161✔
593
      lwr->d_records.push_back(answer);
68,161✔
594
    }
68,161✔
595

596
    if (EDNSOpts edo; EDNS0Level > 0 && getEDNSOpts(mdp, &edo)) {
10,740✔
597
      lwr->d_haveEDNS = true;
10,737✔
598

599
      // If we sent out ECS, we can also expect to see a return with or without ECS, the absent case
600
      // is not handled explicitly. If we do see a ECS in the reply, the source part *must* match
601
      // with what we sent out. See https://www.rfc-editor.org/rfc/rfc7871#section-7.3. and section
602
      // 11.2.
603
      // For ECS hardening mode, the case where we sent out an ECS but did not receive a matching
604
      // one is handled in arecvfrom().
605
      if (subnetOpts) {
10,737✔
606
        // THE RFC is not clear about the case of having multiple ECS options. We only look at the first.
607
        if (const auto opt = edo.getFirstOption(EDNSOptionCode::ECS); opt != edo.d_options.end()) {
293✔
608
          EDNSSubnetOpts reso;
52✔
609
          if (EDNSSubnetOpts::getFromString(opt->second, &reso)) {
52!
610
            if (!doTCP && reso.getSource() != subnetOpts->getSource()) {
52!
611
              g_slogout->info(Logr::Notice, "Incoming ECS does not match outgoing",
1✔
612
                              "server", Logging::Loggable(address),
1✔
613
                              "qname", Logging::Loggable(domain),
1✔
614
                              "outgoing", Logging::Loggable(subnetOpts->getSource()),
1✔
615
                              "incoming", Logging::Loggable(reso.getSource()));
1✔
616
              return LWResult::Result::Spoofed;
1✔
617
            }
1✔
618
            /* rfc7871 states that 0 "indicate[s] that the answer is suitable for all addresses in FAMILY",
619
               so we might want to still pass the information along to be able to differentiate between
620
               IPv4 and IPv6. Still I'm pretty sure it doesn't matter in real life, so let's not duplicate
621
               entries in our cache. */
622
            if (reso.getScopePrefixLength() != 0) {
51!
623
              uint8_t bits = std::min(reso.getScopePrefixLength(), subnetOpts->getSourcePrefixLength());
51✔
624
              auto outgoingECSAddr = subnetOpts->getSource().getNetwork();
51✔
625
              outgoingECSAddr.truncate(bits);
51✔
626
              srcmask = Netmask(outgoingECSAddr, bits);
51✔
627
            }
51✔
628
          }
51✔
629
        }
52✔
630
      }
293✔
631
    }
10,737✔
632

633
    if (outgoingLoggers) {
10,739✔
634
      logIncomingResponse(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, srcmask, len, lwr->d_rcode, lwr->d_records, queryTime, exportTypes, nsName);
22✔
635
    }
22✔
636

637
    lwr->d_validpacket = true;
10,739✔
638
    return LWResult::Result::Success;
10,739✔
639
  }
10,740✔
640
  catch (const std::exception& mde) {
10,740✔
641
    if (::arg().mustDo("log-common-errors")) {
×
642
      SLOG(g_log << Logger::Notice << "Unable to parse packet from remote server " << address.toString() << ": " << mde.what() << endl,
×
643
           g_slogout->error(Logr::Notice, mde.what(), "Unable to parse packet from remote server", "server", Logging::Loggable(address),
×
644
                            "exception", Logging::Loggable("std::exception")));
×
645
    }
×
646

647
    lwr->d_rcode = RCode::FormErr;
×
648
    lwr->d_validpacket = false;
×
649
    t_Counters.at(rec::Counter::serverParseError)++;
×
650

651
    if (outgoingLoggers) {
×
652
      logIncomingResponse(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, srcmask, len, lwr->d_rcode, lwr->d_records, queryTime, exportTypes, nsName);
×
653
    }
×
654

655
    return LWResult::Result::Success; // success - oddly enough
×
656
  }
×
657
  catch (...) {
10,740✔
658
    SLOG(g_log << Logger::Notice << "Unknown error parsing packet from remote server" << endl,
×
659
         g_slogout->info(Logr::Notice, "Unknown error parsing packet from remote server", "server", Logging::Loggable(address)));
×
660
  }
×
661

662
  t_Counters.at(rec::Counter::serverParseError)++;
×
663

664
out:
×
665
  if (!lwr->d_rcode) {
×
666
    lwr->d_rcode = RCode::ServFail;
×
667
  }
×
668

669
  return LWResult::Result::PermanentError;
×
670
}
×
671

672
LWResult::Result asyncresolve(const ComboAddress& address, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, const ResolveContext& context, const std::shared_ptr<std::vector<std::unique_ptr<RemoteLogger>>>& outgoingLoggers, const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstrmLoggers, const std::set<uint16_t>& exportTypes, LWResult* lwr, bool* chained)
673
{
11,085✔
674
  TCPOutConnectionManager::Connection connection;
11,085✔
675
  auto ret = asyncresolve(address, domain, type, doTCP, sendRDQuery, EDNS0Level, now, srcmask, context, outgoingLoggers, fstrmLoggers, exportTypes, lwr, chained, connection);
11,085✔
676

677
  if (doTCP) {
11,085✔
678
    if (connection.d_handler && lwr->d_validpacket) {
35!
679
      t_tcp_manager.store(*now, address, std::move(connection));
33✔
680
    }
33✔
681
  }
35✔
682
  return ret;
11,085✔
683
}
11,085✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc