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

PowerDNS / pdns / 14400190872

11 Apr 2025 09:35AM UTC coverage: 63.46% (-0.02%) from 63.483%
14400190872

Pull #15420

github

web-flow
Merge 8143d6a52 into f0eaaf61c
Pull Request #15420: dnsdist: Add Lua bindings for the incoming network interface

41673 of 100404 branches covered (41.51%)

Branch coverage included in aggregate %.

36 of 36 new or added lines in 3 files covered. (100.0%)

58 existing lines in 15 files now uncovered.

128658 of 168002 relevant lines covered (76.58%)

3841118.45 hits per line

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

70.0
/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
{
200✔
64
  auto ret = rli.queueData(data);
200✔
65

66
  switch (ret) {
200!
67
  case RemoteLoggerInterface::Result::Queued:
200!
68
    break;
200✔
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
  }
200✔
88
}
200✔
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,518✔
96
  if (fstreamLoggers == nullptr) {
9,519✔
97
    return false;
9,514✔
98
  }
9,514✔
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,515✔
126
  if (fstreamLoggers == nullptr) {
9,516✔
127
    return false;
9,512✔
128
  }
9,512✔
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
{
22✔
159
  if (!outgoingLoggers) {
22!
160
    return;
×
161
  }
×
162

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

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

175
  static thread_local std::string buffer;
15✔
176
  buffer.clear();
15✔
177
  pdns::ProtoZero::Message m{buffer};
15✔
178
  m.setType(pdns::ProtoZero::Message::MessageType::DNSOutgoingQueryType);
15✔
179
  m.setMessageIdentity(uuid);
15✔
180
  m.setSocketFamily(address.sin4.sin_family);
15✔
181
  if (!doTCP) {
15!
182
    m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::UDP);
15✔
183
  }
15✔
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);
15✔
192
  m.setInBytes(bytes);
15✔
193
  m.setTime();
15✔
194
  m.setId(qid);
15✔
195
  m.setQuestion(domain, type, QClass::IN);
15✔
196
  m.setToPort(address.getPort());
15✔
197
  m.setServerIdentity(SyncRes::s_serverID);
15✔
198

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

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

207
  if (!nsName.empty()) {
15!
208
    m.setMeta("nsName", {nsName}, {});
15✔
209
  }
15✔
210
  for (auto& logger : *outgoingLoggers) {
30✔
211
    if (logger->logQueries()) {
30!
212
      remoteLoggerQueueData(*logger, buffer);
30✔
213
    }
30✔
214
  }
30✔
215
}
15✔
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
{
22✔
219
  if (!outgoingLoggers) {
22!
220
    return;
×
221
  }
×
222

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

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

235
  static thread_local std::string buffer;
22✔
236
  buffer.clear();
22✔
237
  pdns::ProtoZero::RecMessage m{buffer};
22✔
238
  m.setType(pdns::ProtoZero::Message::MessageType::DNSIncomingResponseType);
22✔
239
  m.setMessageIdentity(uuid);
22✔
240
  m.setSocketFamily(address.sin4.sin_family);
22✔
241
  if (!doTCP) {
22!
242
    m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::UDP);
22✔
243
  }
22✔
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);
22✔
251
  m.setInBytes(bytes);
22✔
252
  m.setTime();
22✔
253
  m.setId(qid);
22✔
254
  m.setQuestion(domain, type, QClass::IN);
22✔
255
  m.setToPort(address.getPort());
22✔
256
  m.setServerIdentity(SyncRes::s_serverID);
22✔
257

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

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

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

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

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

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

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

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

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

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

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

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

345
  LWResult::Result ret = asendtcp(packet, connection.d_handler);
49✔
346
  if (ret != LWResult::Result::Success) {
49!
UNCOV
347
    return ret;
×
UNCOV
348
  }
×
349

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

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

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

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

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

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

422
  string ping;
9,520✔
423
  bool weWantEDNSSubnet = false;
9,520✔
424
  uint8_t outgoingECSBits = 0;
9,520✔
425
  ComboAddress outgoingECSAddr;
9,520✔
426
  if (EDNS0Level > 0) {
9,520✔
427
    DNSPacketWriter::optvect_t opts;
9,518✔
428
    if (srcmask) {
9,518✔
429
      EDNSSubnetOpts subnetOpts;
36✔
430
      subnetOpts.setSource(*srcmask);
36✔
431
      outgoingECSBits = srcmask->getBits();
36✔
432
      outgoingECSAddr = srcmask->getNetwork();
36✔
433
      opts.emplace_back(EDNSOptionCode::ECS, subnetOpts.makeOptString());
36✔
434
      weWantEDNSSubnet = true;
36✔
435
    }
36✔
436

437
    if (dnsOverTLS && g_paddingOutgoing) {
9,518!
438
      addPadding(pw, bufsize, opts);
9✔
439
    }
9✔
440

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

448
  DTime dt;
9,520✔
449
  dt.set();
9,520✔
450
  *now = dt.getTimeval();
9,520✔
451

452
  boost::uuids::uuid uuid;
9,520✔
453
  const struct timeval queryTime = *now;
9,520✔
454

455
  if (outgoingLoggers) {
9,520✔
456
    uuid = getUniqueID();
22✔
457
    logOutgoingQuery(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, vpacket.size(), srcmask, nsName);
22✔
458
  }
22✔
459

460
  srcmask = boost::none; // this is also our return value, even if EDNS0Level == 0
9,520✔
461

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

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

476
  if (!doTCP) {
9,520✔
477
    int queryfd;
9,469✔
478

479
    ret = asendto(vpacket.data(), vpacket.size(), 0, address, qid, domain, type, weWantEDNSSubnet, &queryfd, *now);
9,469✔
480

481
    if (ret != LWResult::Result::Success) {
9,469!
482
      return ret;
×
483
    }
×
484

485
    if (queryfd <= -1) {
9,469✔
486
      *chained = true;
764✔
487
    }
764✔
488

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

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

535
  lwr->d_usec = dt.udiff();
9,518✔
536
  *now = dt.getTimeval();
9,518✔
537

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

545
  if (*chained) {
9,466✔
546
    auto msec = lwr->d_usec / 1000;
763✔
547
    if (msec > g_networkTimeoutMsec * 2 / 3) {
763!
548
      auto jitterMsec = dns_random(msec);
×
549
      if (jitterMsec > 0) {
×
550
        mthreadSleep(jitterMsec);
×
551
      }
×
552
    }
×
553
  }
763✔
554

555
  buf.resize(len);
9,466✔
556

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

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

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

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

595
    lwr->d_records.reserve(mdp.d_answers.size());
9,466✔
596
    for (const auto& answer : mdp.d_answers) {
61,453✔
597
      lwr->d_records.push_back(answer);
61,453✔
598
    }
61,453✔
599

600
    EDNSOpts edo;
9,466✔
601
    if (EDNS0Level > 0 && getEDNSOpts(mdp, &edo)) {
9,468✔
602
      lwr->d_haveEDNS = true;
9,463✔
603

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

624
    if (outgoingLoggers) {
9,466✔
625
      logIncomingResponse(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, srcmask, len, lwr->d_rcode, lwr->d_records, queryTime, exportTypes, nsName);
22✔
626
    }
22✔
627

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

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

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

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

653
  t_Counters.at(rec::Counter::serverParseError)++;
×
654

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

660
  return LWResult::Result::PermanentError;
×
661
}
×
662

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

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