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

PowerDNS / pdns / 12595591960

03 Jan 2025 09:27AM UTC coverage: 62.774% (+2.5%) from 60.245%
12595591960

Pull #15008

github

web-flow
Merge c2a2749d3 into 788f396a7
Pull Request #15008: Do not follow CNAME records for ANY or CNAME queries

30393 of 78644 branches covered (38.65%)

Branch coverage included in aggregate %.

105822 of 138350 relevant lines covered (76.49%)

4613078.44 hits per line

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

70.09
/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

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

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

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

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

107
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)
108
{
2✔
109
  if (fstreamLoggers == nullptr)
2!
110
    return;
×
111

112
  struct timespec ts;
2✔
113
  TIMEVAL_TO_TIMESPEC(&queryTime, &ts);
2✔
114
  std::string str;
2✔
115
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
116
  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✔
117
  str = message.getBuffer();
2✔
118

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

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

137
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)
138
{
×
139
  if (fstreamLoggers == nullptr)
×
140
    return;
×
141

142
  struct timespec ts1, ts2;
×
143
  TIMEVAL_TO_TIMESPEC(&queryTime, &ts1);
×
144
  TIMEVAL_TO_TIMESPEC(&replyTime, &ts2);
×
145
  std::string str;
×
146
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
147
  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);
×
148
  str = message.getBuffer();
×
149

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

155
#endif // HAVE_FSTRM
156

157
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)
158
{
19✔
159
  if (!outgoingLoggers) {
19!
160
    return;
×
161
  }
×
162

163
  bool log = false;
19✔
164
  for (auto& logger : *outgoingLoggers) {
25✔
165
    if (logger->logQueries()) {
25✔
166
      log = true;
13✔
167
      break;
13✔
168
    }
13✔
169
  }
25✔
170

171
  if (!log) {
19✔
172
    return;
6✔
173
  }
6✔
174

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

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

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

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

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

217
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)
218
{
19✔
219
  if (!outgoingLoggers) {
19!
220
    return;
×
221
  }
×
222

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

231
  if (!log) {
19!
232
    return;
×
233
  }
×
234

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

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

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

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

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

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

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

294
  connection = t_tcp_manager.get(ip);
11✔
295
  if (connection.d_handler) {
11✔
296
    return false;
5✔
297
  }
5✔
298

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

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

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

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

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

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

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

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

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

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

390
/** lwr is only filled out in case 1 was returned, and even when returning 1 for 'success', lwr might contain DNS errors
391
    Never throws!
392
 */
393
// NOLINTNEXTLINE(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
394
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)
395
{
9,019✔
396
  size_t len;
9,019✔
397
  size_t bufsize = g_outgoingEDNSBufsize;
9,019✔
398
  PacketBuffer buf;
9,019✔
399
  buf.resize(bufsize);
9,019✔
400
  vector<uint8_t> vpacket;
9,019✔
401
  //  string mapped0x20=dns0x20(domain);
402
  uint16_t qid = dns_random_uint16();
9,019✔
403
  DNSPacketWriter pw(vpacket, domain, type);
9,019✔
404
  bool dnsOverTLS = SyncRes::s_dot_to_port_853 && address.getPort() == 853;
9,019✔
405
  std::string nsName;
9,019✔
406
  if (!context.d_nsName.empty()) {
9,019✔
407
    nsName = context.d_nsName.toStringNoDot();
8,945✔
408
  }
8,945✔
409

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

424
  string ping;
9,019✔
425
  bool weWantEDNSSubnet = false;
9,019✔
426
  uint8_t outgoingECSBits = 0;
9,019✔
427
  ComboAddress outgoingECSAddr;
9,019✔
428
  if (EDNS0Level > 0) {
9,019✔
429
    DNSPacketWriter::optvect_t opts;
9,018✔
430
    if (srcmask) {
9,018✔
431
      EDNSSubnetOpts eo;
36✔
432
      eo.source = *srcmask;
36✔
433
      outgoingECSBits = srcmask->getBits();
36✔
434
      outgoingECSAddr = srcmask->getNetwork();
36✔
435
      opts.emplace_back(EDNSOptionCode::ECS, makeEDNSSubnetOptsString(eo));
36✔
436
      weWantEDNSSubnet = true;
36✔
437
    }
36✔
438

439
    if (dnsOverTLS && g_paddingOutgoing) {
9,018!
440
      addPadding(pw, bufsize, opts);
9✔
441
    }
9✔
442

443
    pw.addOpt(g_outgoingEDNSBufsize, 0, g_dnssecmode == DNSSECMode::Off ? 0 : EDNSOpts::DNSSECOK, opts);
9,018✔
444
    pw.commit();
9,018✔
445
  }
9,018✔
446
  lwr->d_rcode = 0;
9,019✔
447
  lwr->d_haveEDNS = false;
9,019✔
448
  LWResult::Result ret;
9,019✔
449

450
  DTime dt;
9,019✔
451
  dt.set();
9,019✔
452
  *now = dt.getTimeval();
9,019✔
453

454
  boost::uuids::uuid uuid;
9,019✔
455
  const struct timeval queryTime = *now;
9,019✔
456

457
  if (outgoingLoggers) {
9,019✔
458
    uuid = getUniqueID();
19✔
459
    logOutgoingQuery(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, vpacket.size(), srcmask, nsName);
19✔
460
  }
19✔
461

462
  srcmask = boost::none; // this is also our return value, even if EDNS0Level == 0
9,019✔
463

464
  // We only store the localip if needed for fstrm logging
465
  ComboAddress localip;
9,019✔
466
#ifdef HAVE_FSTRM
9,019✔
467
  bool fstrmQEnabled = false;
9,019✔
468
  bool fstrmREnabled = false;
9,019✔
469

470
  if (isEnabledForQueries(fstrmLoggers)) {
9,019✔
471
    fstrmQEnabled = true;
2✔
472
  }
2✔
473
  if (isEnabledForResponses(fstrmLoggers)) {
9,019✔
474
    fstrmREnabled = true;
4✔
475
  }
4✔
476
#endif
9,019✔
477

478
  if (!doTCP) {
9,019✔
479
    int queryfd;
9,001✔
480

481
    ret = asendto(vpacket.data(), vpacket.size(), 0, address, qid, domain, type, weWantEDNSSubnet, &queryfd, *now);
9,001✔
482

483
    if (ret != LWResult::Result::Success) {
9,001!
484
      return ret;
×
485
    }
×
486

487
    if (queryfd <= -1) {
9,001✔
488
      *chained = true;
1,368✔
489
    }
1,368✔
490

491
#ifdef HAVE_FSTRM
9,001✔
492
    if (!*chained) {
9,001✔
493
      if (fstrmQEnabled || fstrmREnabled) {
7,631✔
494
        localip.sin4.sin_family = address.sin4.sin_family;
4✔
495
        socklen_t slen = address.getSocklen();
4✔
496
        (void)getsockname(queryfd, reinterpret_cast<sockaddr*>(&localip), &slen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast))
4✔
497
      }
4✔
498
      if (fstrmQEnabled) {
7,631✔
499
        logFstreamQuery(fstrmLoggers, queryTime, localip, address, DnstapMessage::ProtocolType::DoUDP, context.d_auth ? context.d_auth : boost::none, vpacket);
2!
500
      }
2✔
501
    }
7,631✔
502
#endif /* HAVE_FSTRM */
9,001✔
503

504
    // sleep until we see an answer to this, interface to mtasker
505
    ret = arecvfrom(buf, 0, address, len, qid, domain, type, queryfd, *now);
9,001✔
506
  }
9,001✔
507
  else {
18✔
508
    bool isNew;
18✔
509
    do {
18✔
510
      try {
18✔
511
        // If we get a new (not re-used) TCP connection that does not
512
        // work, we give up. For reused connections, we assume the
513
        // peer has closed it on error, so we retry. At some point we
514
        // *will* get a new connection, so this loop is not endless.
515
        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
18✔
516
        isNew = tcpconnect(address, connection, dnsOverTLS, nsName);
18✔
517
        ret = tcpsendrecv(address, connection, localip, vpacket, len, buf);
18✔
518
#ifdef HAVE_FSTRM
18✔
519
        if (fstrmQEnabled) {
18!
520
          logFstreamQuery(fstrmLoggers, queryTime, localip, address, !dnsOverTLS ? DnstapMessage::ProtocolType::DoTCP : DnstapMessage::ProtocolType::DoT, context.d_auth, vpacket);
×
521
        }
×
522
#endif /* HAVE_FSTRM */
18✔
523
        if (ret == LWResult::Result::Success) {
18✔
524
          break;
11✔
525
        }
11✔
526
        connection.d_handler->close();
7✔
527
      }
7✔
528
      catch (const NetworkError&) {
18✔
529
        ret = LWResult::Result::OSLimitError; // OS limits error
×
530
      }
×
531
      catch (const runtime_error&) {
18✔
532
        ret = LWResult::Result::OSLimitError; // OS limits error (PermanentError is transport related)
×
533
      }
×
534
    } while (!isNew);
18!
535
  }
18✔
536

537
  lwr->d_usec = dt.udiff();
9,012✔
538
  *now = dt.getTimeval();
9,012✔
539

540
  if (ret != LWResult::Result::Success) { // includes 'timeout'
9,012✔
541
    if (outgoingLoggers) {
47!
542
      logIncomingResponse(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, srcmask, 0, -1, {}, queryTime, exportTypes, nsName);
×
543
    }
×
544
    return ret;
47✔
545
  }
47✔
546

547
  if (*chained) {
8,965✔
548
    auto msec = lwr->d_usec / 1000;
1,368✔
549
    if (msec > g_networkTimeoutMsec * 2 / 3) {
1,368!
550
      auto jitterMsec = dns_random(msec);
×
551
      if (jitterMsec > 0) {
×
552
        mthreadSleep(jitterMsec);
×
553
      }
×
554
    }
×
555
  }
1,368✔
556

557
  buf.resize(len);
8,965✔
558

559
#ifdef HAVE_FSTRM
8,965✔
560
  if (fstrmREnabled && (!*chained || doTCP)) {
8,965!
561
    DnstapMessage::ProtocolType protocol = doTCP ? DnstapMessage::ProtocolType::DoTCP : DnstapMessage::ProtocolType::DoUDP;
×
562
    if (dnsOverTLS) {
×
563
      protocol = DnstapMessage::ProtocolType::DoT;
×
564
    }
×
565
    logFstreamResponse(fstrmLoggers, localip, address, protocol, context.d_auth, buf, queryTime, *now);
×
566
  }
×
567
#endif /* HAVE_FSTRM */
8,965✔
568

569
  lwr->d_records.clear();
8,965✔
570
  try {
8,965✔
571
    lwr->d_tcbit = 0;
8,965✔
572
    MOADNSParser mdp(false, reinterpret_cast<const char*>(buf.data()), buf.size());
8,965✔
573
    lwr->d_aabit = mdp.d_header.aa;
8,965✔
574
    lwr->d_tcbit = mdp.d_header.tc;
8,965✔
575
    lwr->d_rcode = mdp.d_header.rcode;
8,965✔
576

577
    if (mdp.d_header.rcode == RCode::FormErr && mdp.d_qname.empty() && mdp.d_qtype == 0 && mdp.d_qclass == 0) {
8,965!
578
      if (outgoingLoggers) {
×
579
        logIncomingResponse(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, srcmask, len, lwr->d_rcode, lwr->d_records, queryTime, exportTypes, nsName);
×
580
      }
×
581
      lwr->d_validpacket = true;
×
582
      return LWResult::Result::Success; // this is "success", the error is set in lwr->d_rcode
×
583
    }
×
584

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

597
    lwr->d_records.reserve(mdp.d_answers.size());
8,965✔
598
    for (const auto& answer : mdp.d_answers) {
52,992✔
599
      lwr->d_records.push_back(answer);
52,992✔
600
    }
52,992✔
601

602
    EDNSOpts edo;
8,965✔
603
    if (EDNS0Level > 0 && getEDNSOpts(mdp, &edo)) {
8,972✔
604
      lwr->d_haveEDNS = true;
8,969✔
605

606
      if (weWantEDNSSubnet) {
8,969✔
607
        for (const auto& opt : edo.d_options) {
36✔
608
          if (opt.first == EDNSOptionCode::ECS) {
30!
609
            EDNSSubnetOpts reso;
30✔
610
            if (getEDNSSubnetOptsFromString(opt.second, &reso)) {
30!
611
              /* rfc7871 states that 0 "indicate[s] that the answer is suitable for all addresses in FAMILY",
612
                 so we might want to still pass the information along to be able to differentiate between
613
                 IPv4 and IPv6. Still I'm pretty sure it doesn't matter in real life, so let's not duplicate
614
                 entries in our cache. */
615
              if (reso.scope.getBits()) {
30!
616
                uint8_t bits = std::min(reso.scope.getBits(), outgoingECSBits);
30✔
617
                outgoingECSAddr.truncate(bits);
30✔
618
                srcmask = Netmask(outgoingECSAddr, bits);
30✔
619
              }
30✔
620
            }
30✔
621
          }
30✔
622
        }
30✔
623
      }
36✔
624
    }
8,969✔
625

626
    if (outgoingLoggers) {
8,965✔
627
      logIncomingResponse(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, srcmask, len, lwr->d_rcode, lwr->d_records, queryTime, exportTypes, nsName);
19✔
628
    }
19✔
629

630
    lwr->d_validpacket = true;
8,965✔
631
    return LWResult::Result::Success;
8,965✔
632
  }
8,965✔
633
  catch (const std::exception& mde) {
8,965✔
634
    if (::arg().mustDo("log-common-errors")) {
×
635
      SLOG(g_log << Logger::Notice << "Unable to parse packet from remote server " << address.toString() << ": " << mde.what() << endl,
×
636
           g_slogout->error(Logr::Notice, mde.what(), "Unable to parse packet from remote server", "server", Logging::Loggable(address),
×
637
                            "exception", Logging::Loggable("std::exception")));
×
638
    }
×
639

640
    lwr->d_rcode = RCode::FormErr;
×
641
    lwr->d_validpacket = false;
×
642
    t_Counters.at(rec::Counter::serverParseError)++;
×
643

644
    if (outgoingLoggers) {
×
645
      logIncomingResponse(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, srcmask, len, lwr->d_rcode, lwr->d_records, queryTime, exportTypes, nsName);
×
646
    }
×
647

648
    return LWResult::Result::Success; // success - oddly enough
×
649
  }
×
650
  catch (...) {
8,965✔
651
    SLOG(g_log << Logger::Notice << "Unknown error parsing packet from remote server" << endl,
×
652
         g_slogout->info(Logr::Notice, "Unknown error parsing packet from remote server", "server", Logging::Loggable(address)));
×
653
  }
×
654

655
  t_Counters.at(rec::Counter::serverParseError)++;
×
656

657
out:
×
658
  if (!lwr->d_rcode) {
×
659
    lwr->d_rcode = RCode::ServFail;
×
660
  }
×
661

662
  return LWResult::Result::PermanentError;
×
663
}
×
664

665
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)
666
{
9,019✔
667
  TCPOutConnectionManager::Connection connection;
9,019✔
668
  auto ret = asyncresolve(address, domain, type, doTCP, sendRDQuery, EDNS0Level, now, srcmask, context, outgoingLoggers, fstrmLoggers, exportTypes, lwr, chained, connection);
9,019✔
669

670
  if (doTCP) {
9,019✔
671
    if (connection.d_handler && lwr->d_validpacket) {
11!
672
      t_tcp_manager.store(*now, address, std::move(connection));
11✔
673
    }
11✔
674
  }
11✔
675
  return ret;
9,019✔
676
}
9,019✔
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