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

PowerDNS / pdns / 18709198868

22 Oct 2025 07:49AM UTC coverage: 73.016% (+0.01%) from 73.006%
18709198868

Pull #16338

github

web-flow
Merge 53c598698 into 8eda540cb
Pull Request #16338: rec: tighten delegation accept

38301 of 63162 branches covered (60.64%)

Branch coverage included in aggregate %.

30 of 35 new or added lines in 2 files covered. (85.71%)

31 existing lines in 8 files now uncovered.

127486 of 163895 relevant lines covered (77.79%)

6129150.01 hits per line

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

65.36
/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
#include "rec-cookiestore.hh"
58

59
static bool g_cookies = false;
60

61
std::string enableOutgoingCookies(bool flag, const string& unsupported)
62
{
185✔
63
  g_cookies = flag;
185✔
64
  if (g_cookies) {
185✔
65
    std::vector<std::string> parts;
3✔
66
    stringtok(parts, unsupported, ", ");
3✔
67
    std::string errors;
3✔
68
    addCookiesUnsupported(parts.begin(), parts.end(), errors);
3✔
69
    return errors;
3✔
70
  }
3✔
71
  return {};
182✔
72
}
185✔
73

74
thread_local TCPOutConnectionManager t_tcp_manager;
75
std::shared_ptr<Logr::Logger> g_slogout;
76
bool g_paddingOutgoing;
77
bool g_ECSHardening;
78

79
static LockGuarded<CookieStore> s_cookiestore;
80

81
uint64_t addCookiesUnsupported(vector<string>::iterator begin, vector<string>::iterator end, string& errors)
82
{
3✔
83
  auto lock = s_cookiestore.lock();
3✔
84
  uint64_t count = 0;
3✔
85
  while (begin != end) {
3!
86
    try {
×
87
      CookieEntry entry;
×
88
      entry.d_address = ComboAddress(*begin, 53);
×
89
      entry.setSupport(CookieEntry::Support::Unsupported, std::numeric_limits<time_t>::max());
×
90
      auto [iter, inserted] = lock->insert(entry);
×
91
      if (!inserted) {
×
92
        lock->replace(iter, entry);
×
93
      }
×
94
      ++count;
×
95
    }
×
96
    catch (const PDNSException& error) {
×
97
      if (!errors.empty()) {
×
98
        errors += ", ";
×
99
      }
×
100
      errors += error.reason;
×
101
    }
×
102
    ++begin;
×
103
  }
×
104
  return count;
3✔
105
}
3✔
106

107
uint64_t clearCookies(vector<string>::iterator begin, vector<string>::iterator end, string& errors)
108
{
6✔
109
  auto lock = s_cookiestore.lock();
6✔
110
  uint64_t count = 0;
6✔
111
  if (begin == end) {
6!
112
    return 0;
×
113
  }
×
114
  if (*begin == "*") {
6!
115
    count = lock->size();
6✔
116
    lock->clear();
6✔
117
  }
6✔
118
  else {
×
119
    while (begin != end) {
×
120
      try {
×
121
        count += lock->erase(ComboAddress(*begin, 53));
×
122
      }
×
123
      catch (const PDNSException& error) {
×
124
        if (!errors.empty()) {
×
125
          errors += ", ";
×
126
        }
×
127
        errors += error.reason;
×
128
      }
×
129
      ++begin;
×
130
    }
×
131
  }
×
132
  return count;
6✔
133
}
6✔
134

135
void pruneCookies(time_t cutoff)
136
{
111✔
137
  auto lock = s_cookiestore.lock();
111✔
138
  lock->prune(cutoff);
111✔
139
}
111✔
140

141
uint64_t dumpCookies(int fileDesc)
142
{
25✔
143
  CookieStore copy;
25✔
144
  {
25✔
145
    auto lock = s_cookiestore.lock();
25✔
146
    copy = *lock;
25✔
147
  }
25✔
148
  return copy.dump(fileDesc);
25✔
149
}
25✔
150

151
void remoteLoggerQueueData(RemoteLoggerInterface& rli, const std::string& data)
152
{
200✔
153
  auto ret = rli.queueData(data);
200✔
154

155
  switch (ret) {
200!
156
  case RemoteLoggerInterface::Result::Queued:
200!
157
    break;
200✔
158
  case RemoteLoggerInterface::Result::PipeFull: {
×
159
    const auto& msg = RemoteLoggerInterface::toErrorString(ret);
×
160
    g_slog->withName(rli.name())->info(Logr::Debug, msg);
×
161
    break;
×
162
  }
×
163
  case RemoteLoggerInterface::Result::TooLarge: {
×
164
    const auto& msg = RemoteLoggerInterface::toErrorString(ret);
×
165
    g_slog->withName(rli.name())->info(Logr::Debug, msg);
×
166
    break;
×
167
  }
×
168
  case RemoteLoggerInterface::Result::OtherError: {
×
169
    const auto& msg = RemoteLoggerInterface::toErrorString(ret);
×
170
    g_slog->withName(rli.name())->info(Logr::Warning, msg);
×
171
    break;
×
172
  }
×
173
  }
200✔
174
}
200✔
175

176
#include "dnstap.hh"
177

178
#ifdef HAVE_FSTRM
179
#include "fstrm_logger.hh"
180

181
static bool isEnabledForQueries(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
182
{
10,664✔
183
  if (fstreamLoggers == nullptr) {
10,667✔
184
    return false;
10,661✔
185
  }
10,661✔
186
  for (auto& logger : *fstreamLoggers) {
2,147,483,651✔
187
    if (logger->logQueries()) {
4✔
188
      return true;
2✔
189
    }
2✔
190
  }
4✔
191
  return false;
2,147,483,649✔
192
}
2,147,483,651✔
193

194
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)
195
{
2✔
196
  if (fstreamLoggers == nullptr)
2!
197
    return;
×
198

199
  struct timespec ts;
2✔
200
  TIMEVAL_TO_TIMESPEC(&queryTime, &ts);
2✔
201
  std::string str;
2✔
202
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
203
  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✔
204
  str = message.getBuffer();
2✔
205

206
  for (auto& logger : *fstreamLoggers) {
2✔
207
    remoteLoggerQueueData(*logger, str);
2✔
208
  }
2✔
209
}
2✔
210

211
static bool isEnabledForResponses(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
212
{
10,666✔
213
  if (fstreamLoggers == nullptr) {
10,668✔
214
    return false;
10,660✔
215
  }
10,660✔
216
  for (auto& logger : *fstreamLoggers) {
2,147,483,651✔
217
    if (logger->logResponses()) {
4!
218
      return true;
4✔
219
    }
4✔
220
  }
4✔
221
  return false;
2,147,483,647✔
222
}
2,147,483,651✔
223

224
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)
225
{
×
226
  if (fstreamLoggers == nullptr)
×
227
    return;
×
228

229
  struct timespec ts1, ts2;
×
230
  TIMEVAL_TO_TIMESPEC(&queryTime, &ts1);
×
231
  TIMEVAL_TO_TIMESPEC(&replyTime, &ts2);
×
232
  std::string str;
×
233
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
234
  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);
×
235
  str = message.getBuffer();
×
236

237
  for (auto& logger : *fstreamLoggers) {
×
238
    remoteLoggerQueueData(*logger, str);
×
239
  }
×
240
}
×
241

242
#endif // HAVE_FSTRM
243

244
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)
245
{
22✔
246
  if (!outgoingLoggers) {
22!
247
    return;
×
248
  }
×
249

250
  bool log = false;
22✔
251
  for (auto& logger : *outgoingLoggers) {
29✔
252
    if (logger->logQueries()) {
29✔
253
      log = true;
15✔
254
      break;
15✔
255
    }
15✔
256
  }
29✔
257

258
  if (!log) {
22✔
259
    return;
7✔
260
  }
7✔
261

262
  static thread_local std::string buffer;
15✔
263
  buffer.clear();
15✔
264
  pdns::ProtoZero::Message m{buffer};
15✔
265
  m.setType(pdns::ProtoZero::Message::MessageType::DNSOutgoingQueryType);
15✔
266
  m.setMessageIdentity(uuid);
15✔
267
  m.setSocketFamily(address.sin4.sin_family);
15✔
268
  if (!doTCP) {
15!
269
    m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::UDP);
15✔
270
  }
15✔
271
  else if (!tls) {
×
272
    m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::TCP);
×
273
  }
×
274
  else {
×
275
    m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::DoT);
×
276
  }
×
277

278
  m.setTo(address);
15✔
279
  m.setInBytes(bytes);
15✔
280
  m.setTime();
15✔
281
  m.setId(qid);
15✔
282
  m.setQuestion(domain, type, QClass::IN);
15✔
283
  m.setToPort(address.getPort());
15✔
284
  m.setServerIdentity(SyncRes::s_serverID);
15✔
285

286
  if (initialRequestId) {
15!
287
    m.setInitialRequestID(*initialRequestId);
15✔
288
  }
15✔
289

290
  if (srcmask) {
15✔
291
    m.setEDNSSubnet(*srcmask, 128);
6✔
292
  }
6✔
293

294
  if (!nsName.empty()) {
15!
295
    m.setMeta("nsName", {nsName}, {});
15✔
296
  }
15✔
297
  for (auto& logger : *outgoingLoggers) {
30✔
298
    if (logger->logQueries()) {
30!
299
      remoteLoggerQueueData(*logger, buffer);
30✔
300
    }
30✔
301
  }
30✔
302
}
15✔
303

304
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)
305
{
22✔
306
  if (!outgoingLoggers) {
22!
307
    return;
×
308
  }
×
309

310
  bool log = false;
22✔
311
  for (auto& logger : *outgoingLoggers) {
22!
312
    if (logger->logResponses()) {
22!
313
      log = true;
22✔
314
      break;
22✔
315
    }
22✔
316
  }
22✔
317

318
  if (!log) {
22!
319
    return;
×
320
  }
×
321

322
  static thread_local std::string buffer;
22✔
323
  buffer.clear();
22✔
324
  pdns::ProtoZero::RecMessage m{buffer};
22✔
325
  m.setType(pdns::ProtoZero::Message::MessageType::DNSIncomingResponseType);
22✔
326
  m.setMessageIdentity(uuid);
22✔
327
  m.setSocketFamily(address.sin4.sin_family);
22✔
328
  if (!doTCP) {
22!
329
    m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::UDP);
22✔
330
  }
22✔
331
  else if (!tls) {
×
332
    m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::TCP);
×
333
  }
×
334
  else {
×
335
    m.setSocketProtocol(pdns::ProtoZero::Message::TransportProtocol::DoT);
×
336
  }
×
337
  m.setTo(address);
22✔
338
  m.setInBytes(bytes);
22✔
339
  m.setTime();
22✔
340
  m.setId(qid);
22✔
341
  m.setQuestion(domain, type, QClass::IN);
22✔
342
  m.setToPort(address.getPort());
22✔
343
  m.setServerIdentity(SyncRes::s_serverID);
22✔
344

345
  if (initialRequestId) {
22!
346
    m.setInitialRequestID(*initialRequestId);
22✔
347
  }
22✔
348

349
  if (srcmask) {
22!
350
    m.setEDNSSubnet(*srcmask, 128);
×
351
  }
×
352
  if (!nsName.empty()) {
22!
353
    m.setMeta("nsName", {nsName}, {});
22✔
354
  }
22✔
355

356
  m.startResponse();
22✔
357
  m.setQueryTime(queryTime.tv_sec, queryTime.tv_usec);
22✔
358
  if (rcode == -1) {
22!
359
    m.setNetworkErrorResponseCode();
×
360
  }
×
361
  else {
22✔
362
    m.setResponseCode(rcode);
22✔
363
  }
22✔
364

365
  for (const auto& record : records) {
90✔
366
    m.addRR(record, exportTypes, std::nullopt);
90✔
367
  }
90✔
368
  m.commitResponse();
22✔
369

370
  for (auto& logger : *outgoingLoggers) {
44✔
371
    if (logger->logResponses()) {
44!
372
      remoteLoggerQueueData(*logger, buffer);
44✔
373
    }
44✔
374
  }
44✔
375
}
22✔
376

377
class BindError
378
{
379
};
380

381
static bool tcpconnect(const OptLog& log, const ComboAddress& remote, const std::optional<ComboAddress> localBind, TCPOutConnectionManager::Connection& connection, bool& dnsOverTLS, const std::string& nsName, std::string& subjectName)
382
{
35✔
383
  dnsOverTLS = SyncRes::s_dot_to_port_853 && remote.getPort() == 853;
35!
384

385
  connection = t_tcp_manager.get({remote, localBind});
35✔
386
  if (connection.d_handler) {
35✔
387
    return false;
9✔
388
  }
9✔
389

390
  const struct timeval timeout{
26✔
391
    g_networkTimeoutMsec / 1000, static_cast<suseconds_t>(g_networkTimeoutMsec) % 1000 * 1000};
26✔
392
  Socket sock(remote.sin4.sin_family, SOCK_STREAM);
26✔
393
  sock.setNonBlocking();
26✔
394
  setTCPNoDelay(sock.getHandle());
26✔
395
  // Bind to the same address the cookie is associated with (RFC 9018 section 3 last paragraph)
396
  ComboAddress localip = localBind ? *localBind : pdns::getQueryLocalAddress(remote.sin4.sin_family, 0);
26✔
397
  if (localBind) {
26✔
398
    VLOG(log, "Connecting TCP to " << remote.toStringWithPortExcept(53) << " with specific local address " << localip.toString() << endl);
2!
399
  }
2✔
400
  else {
24✔
401
    VLOG(log, "Connecting TCP to " << remote.toStringWithPortExcept(53) << " with no specific local address" << endl);
24!
402
  }
24✔
403

404
  try {
26✔
405
    sock.bind(localip);
26✔
406
  }
26✔
407
  catch (const NetworkError& e) {
26✔
408
    if (localBind) {
×
409
      throw BindError();
×
410
    }
×
411
    throw;
×
412
  }
×
413

414
  std::shared_ptr<TLSCtx> tlsCtx{nullptr};
26✔
415
  bool subjectIsAddress = false;
26✔
416
  if (dnsOverTLS) {
26✔
417
    subjectName = nsName;
16✔
418
    std::string subjectAddress;
16✔
419
    std::string configName;
16✔
420
    tlsCtx = TCPOutConnectionManager::getTLSContext(nsName, remote, connection.d_verboseLogging, subjectName, subjectAddress, configName);
16✔
421
    if (tlsCtx == nullptr) {
16!
422
      g_slogout->info(Logr::Error, "DoT requested but not available", "server", Logging::Loggable(remote));
×
423
      dnsOverTLS = false;
×
424
    }
×
425
    else {
16✔
426
      if (subjectName.empty() && !subjectAddress.empty()) {
16!
427
        subjectName = std::move(subjectAddress);
×
428
        subjectIsAddress = true;
×
429
      }
×
430
      if (subjectName.empty()) {
16✔
431
        subjectName = remote.toString();
5✔
432
        subjectIsAddress = true;
5✔
433
      }
5✔
434
    }
16✔
435
    if (dnsOverTLS && connection.d_verboseLogging) {
16!
436
      g_slogout->info(Logr::Debug, "Connecting with DoT", "server", Logging::Loggable(remote),
11✔
437
                      "subjectName", Logging::Loggable(subjectName),
11✔
438
                      "subjectIsAddress", Logging::Loggable(subjectIsAddress),
11✔
439
                      "configName", Logging::Loggable(configName));
11✔
440
    }
11✔
441
  }
16✔
442
  connection.d_handler = std::make_shared<TCPIOHandler>(subjectName, subjectIsAddress, sock.releaseHandle(), timeout, tlsCtx);
26✔
443
  connection.d_local = localBind;
26✔
444
  // Returned state ignored
445
  // This can throw an exception, retry will need to happen at higher level
446
  connection.d_handler->tryConnect(SyncRes::s_tcp_fast_open_connect, remote);
26✔
447
  return true;
26✔
448
}
26✔
449

450
static LWResult::Result tcpsendrecv(const ComboAddress& ip, TCPOutConnectionManager::Connection& connection,
451
                                    ComboAddress& localip, const vector<uint8_t>& vpacket, size_t& len, PacketBuffer& buf,
452
                                    const std::string& nsName, const std::string& subjectName)
453
{
35✔
454
  socklen_t slen = ip.getSocklen();
35✔
455
  uint16_t tlen = htons(vpacket.size());
35✔
456
  const char* lenP = reinterpret_cast<const char*>(&tlen);
35✔
457

458
  len = 0; // in case of error
35✔
459
  localip.sin4.sin_family = ip.sin4.sin_family;
35✔
460
  if (getsockname(connection.d_handler->getDescriptor(), reinterpret_cast<sockaddr*>(&localip), &slen) != 0) {
35!
461
    return LWResult::Result::PermanentError;
×
462
  }
×
463

464
  PacketBuffer packet;
35✔
465
  packet.reserve(2 + vpacket.size());
35✔
466
  packet.insert(packet.end(), lenP, lenP + 2);
35✔
467
  packet.insert(packet.end(), vpacket.begin(), vpacket.end());
35✔
468

469
  LWResult::Result ret = asendtcp(packet, connection.d_handler);
35✔
470
  if (ret != LWResult::Result::Success) {
35✔
471
    if (connection.d_handler->isTLS() && connection.d_verboseLogging) {
4!
472
      auto result = connection.d_handler->getVerifyResult();
2✔
473
      g_slogout->info(Logr::Error, "Failed to setup TLS connection",
2✔
474
                      "errorcode", Logging::Loggable(result.first),
2✔
475
                      "remote", Logging::Loggable(ip),
2✔
476
                      "nsname", Logging::Loggable(nsName),
2✔
477
                      "subjectName", Logging::Loggable(subjectName),
2✔
478
                      "tlsmessage", Logging::Loggable(result.second));
2✔
479
    }
2✔
480
    return ret;
4✔
481
  }
4✔
482

483
  ret = arecvtcp(packet, 2, connection.d_handler, false);
31✔
484
  if (ret != LWResult::Result::Success) {
31!
485
    return ret;
×
486
  }
×
487

488
  memcpy(&tlen, packet.data(), sizeof(tlen));
31✔
489
  len = ntohs(tlen); // switch to the 'len' shared with the rest of the calling function
31✔
490

491
  // XXX receive into buf directly?
492
  packet.resize(len);
31✔
493
  ret = arecvtcp(packet, len, connection.d_handler, false);
31✔
494
  if (ret != LWResult::Result::Success) {
31!
495
    return ret;
×
496
  }
×
497
  buf.resize(len);
31✔
498
  memcpy(buf.data(), packet.data(), len);
31✔
499
  return LWResult::Result::Success;
31✔
500
}
31✔
501

502
static void addPadding(const DNSPacketWriter& pw, size_t bufsize, DNSPacketWriter::optvect_t& opts)
503
{
25✔
504
  const size_t currentSize = pw.getSizeWithOpts(opts);
25✔
505
  if (currentSize < (bufsize - 4)) {
25!
506
    const size_t remaining = bufsize - (currentSize + 4);
25✔
507
    /* from rfc8647, "4.1.  Recommended Strategy: Block-Length Padding":
508
       Clients SHOULD pad queries to the closest multiple of 128 octets.
509
       Note we are in the client role here.
510
    */
511
    const size_t blockSize = 128;
25✔
512
    const size_t modulo = (currentSize + 4) % blockSize;
25✔
513
    size_t padSize = 0;
25✔
514
    if (modulo > 0) {
25!
515
      padSize = std::min(blockSize - modulo, remaining);
25✔
516
    }
25✔
517
    opts.emplace_back(EDNSOptionCode::PADDING, makeEDNSPaddingOptString(padSize));
25✔
518
  }
25✔
519
}
25✔
520

521
static void outgoingCookie(const OptLog& log, const ComboAddress& address, const timeval& now, DNSPacketWriter::optvect_t& opts, std::optional<EDNSCookiesOpt>& cookieSentOut, std::optional<ComboAddress>& addressToBindTo)
522
{
52✔
523
  auto lock = s_cookiestore.lock();
52✔
524
  if (auto found = lock->find(address); found != lock->end()) {
52✔
525
    switch (found->getSupport()) {
35!
526
    case CookieEntry::Support::Supported:
21✔
527
    case CookieEntry::Support::Probing:
22✔
528
      cookieSentOut = found->d_cookie;
22✔
529
      addressToBindTo = found->d_localaddress;
22✔
530
      opts.emplace_back(EDNSOptionCode::COOKIE, cookieSentOut->makeOptString());
22✔
531
      found->d_lastused = now.tv_sec;
22✔
532
      VLOG(log, "Sending stored cookie info to " << address.toString() << ": " << found->d_cookie.toDisplayString() << endl);
22!
533
      break;
22✔
534
    case CookieEntry::Support::Unsupported:
13✔
535
      VLOG(log, "Server " << address.toString() << " does not support cookies" << endl);
13!
536
      break;
13✔
537
    }
35✔
538
    return;
35✔
539
  }
35✔
540
  // Server not in table, it's either new or was purged
541
  CookieEntry entry;
17✔
542
  entry.d_address = address;
17✔
543
  entry.d_cookie.makeClientCookie();
17✔
544
  cookieSentOut = entry.d_cookie;
17✔
545
  entry.setSupport(CookieEntry::Support::Probing, now.tv_sec);
17✔
546
  lock->emplace(entry);
17✔
547
  opts.emplace_back(EDNSOptionCode::COOKIE, cookieSentOut->makeOptString());
17✔
548
  VLOG(log, "Sending new client cookie info to " << address.toString() << ": " << entry.d_cookie.toDisplayString() << endl);
17!
549
}
17✔
550

551
static std::pair<bool, LWResult::Result> incomingCookie(const OptLog& log, const ComboAddress& address, const ComboAddress& localip, const timeval& now, const std::optional<EDNSCookiesOpt>& cookieSentOut, const EDNSOpts& edo, bool doTCP, LWResult& lwr, bool& cookieFoundInReply)
552
{
52✔
553
  auto lock = s_cookiestore.lock();
52✔
554
  auto found = lock->find(address);
52✔
555

556
  if (found == lock->end()) {
52!
557
    // We received cookie (we might have sent one out) but the server is not in the table?
558
    // This is a case of cannot happen, unless rec_control clear-cookies was called
559
    VLOG(log, "Cookie from " << address.toString() << " not found back in table" << endl);
×
560
    lwr.d_rcode = RCode::FormErr;
×
561
    lwr.d_validpacket = false;
×
562
    return {true, LWResult::Result::Success}; // success - oddly enough
×
563
  }
×
564

565
  // We have stored cookie info, scan for COOKIE option in EDNS
566
  if (const auto opt = edo.getFirstOption(EDNSOptionCode::COOKIE); opt != edo.d_options.end()) {
52✔
567
    if (EDNSCookiesOpt received; received.makeFromString(opt->second)) {
33!
568
      cookieFoundInReply = true;
33✔
569
      VLOG(log, "Received cookie info back from " << address.toString() << ": " << received.toDisplayString() << endl);
33!
570
      if (received.getClient() == cookieSentOut->getClient()) {
33✔
571
        VLOG(log, "Client cookie from " << address.toString() << " matched! Storing with localAddress " << localip.toString() << endl);
31!
572
        ++t_Counters.at(rec::Counter::cookieMatched);
31✔
573
        found->d_localaddress = localip;
31✔
574
        found->d_localaddress.setPort(0);
31✔
575
        found->d_cookie = std::move(received);
31✔
576
        if (found->getSupport() == CookieEntry::Support::Probing) {
31✔
577
          ++t_Counters.at(rec::Counter::cookieProbeSupported);
10✔
578
        }
10✔
579
        found->setSupport(CookieEntry::Support::Supported, now.tv_sec);
31✔
580
        // check extended error code
581
        uint16_t ercode = edo.getCombinedERCode(lwr.d_rcode);
31✔
582
        if (ercode == ERCode::BADCOOKIE) {
31✔
583
          lwr.d_validpacket = true;
7✔
584
          ++t_Counters.at(rec::Counter::cookieRetry);
7✔
585
          VLOG(log, "Server " << localip.toString() << " returned BADCOOKIE " << endl);
7!
586
          return {true, LWResult::Result::BadCookie}; // We did update the entry, retry should succeed
7✔
587
        }
7✔
588
      }
31✔
589
      else {
2✔
590
        if (!doTCP) {
2✔
591
          // Server responded with a wrong client cookie, fall back to TCP, RFC 7873 5.3
592
          VLOG(log, "Server " << localip.toString() << " responded with wrong client cookie, fall back to TCP" << endl);
1!
593
          lwr.d_validpacket = true;
1✔
594
          ++t_Counters.at(rec::Counter::cookieMismatchedOverUDP);
1✔
595
          return {true, LWResult::Result::Spoofed};
1✔
596
        }
1✔
597
        // mismatched cookie when already doing TCP, ignore that
598
        VLOG(log, "Server " << localip.toString() << " responded with wrong client cookie over TCP, ignoring that" << endl);
1!
599
        ++t_Counters.at(rec::Counter::cookieMismatchedOverTCP);
1✔
600
      }
1✔
601
    }
33✔
UNCOV
602
    else {
×
UNCOV
603
      VLOG(log, "Malformed cookie in reply from " << address.toString() << ", dropping as if was a timeout" << endl);
×
604
      // Do something special if we get malformed repeatedly? And/or consider current status?
UNCOV
605
      lwr.d_validpacket = false;
×
UNCOV
606
      ++t_Counters.at(rec::Counter::cookieMalformed);
×
UNCOV
607
      return {true, LWResult::Result::Timeout};
×
UNCOV
608
    }
×
609
  } // COOKIE option found
33✔
610

611
  // The cases where something special needs to be done have been handled above
612
  return {false, LWResult::Result::Success};
44✔
613
}
52✔
614

615
/** lwr is only filled out in case 1 was returned, and even when returning 1 for 'success', lwr might contain DNS errors
616
    Never throws!
617
 */
618
// NOLINTNEXTLINE(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791
619
static LWResult::Result asyncresolve(const OptLog& log, 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)
620
{
10,670✔
621
  size_t len;
10,670✔
622
  size_t bufsize = g_outgoingEDNSBufsize;
10,670✔
623
  PacketBuffer buf;
10,670✔
624
  buf.resize(bufsize);
10,670✔
625
  vector<uint8_t> vpacket;
10,670✔
626
  //  string mapped0x20=dns0x20(domain);
627
  uint16_t qid = dns_random_uint16();
10,670✔
628
  DNSPacketWriter pw(vpacket, domain, type);
10,670✔
629
  bool dnsOverTLS = SyncRes::s_dot_to_port_853 && address.getPort() == 853;
10,670✔
630
  std::string nsName;
10,670✔
631
  if (!context.d_nsName.empty()) {
10,670✔
632
    nsName = context.d_nsName.toStringNoDot();
10,541✔
633
  }
10,541✔
634

635
  pw.getHeader()->rd = sendRDQuery;
10,670✔
636
  pw.getHeader()->id = qid;
10,670✔
637
  /* RFC 6840 section 5.9:
638
   *  This document further specifies that validating resolvers SHOULD set
639
   *  the CD bit on every upstream query.  This is regardless of whether
640
   *  the CD bit was set on the incoming query [...]
641
   *
642
   * sendRDQuery is only true if the qname is part of a forward-zone-recurse (or
643
   * set in the forward-zone-file), so we use this as an indicator for it being
644
   * an "upstream query". To stay true to "dnssec=off means 3.X behaviour", we
645
   * only set +CD on forwarded query in any mode other than dnssec=off.
646
   */
647
  pw.getHeader()->cd = (sendRDQuery && g_dnssecmode != DNSSECMode::Off);
10,670✔
648

649
  std::optional<EDNSSubnetOpts> subnetOpts = std::nullopt;
10,670✔
650
  std::optional<ComboAddress> addressToBindTo;
10,670✔
651
  std::optional<EDNSCookiesOpt> cookieSentOut;
10,670✔
652

653
  if (EDNS0Level > 0) {
10,670✔
654
    DNSPacketWriter::optvect_t opts;
10,666✔
655
    if (srcmask) {
10,666✔
656
      subnetOpts = EDNSSubnetOpts{};
567✔
657
      subnetOpts->setSource(*srcmask);
567✔
658
      opts.emplace_back(EDNSOptionCode::ECS, subnetOpts->makeOptString());
567✔
659
    }
567✔
660

661
    if (g_cookies) {
10,666✔
662
      outgoingCookie(log, address, *now, opts, cookieSentOut, addressToBindTo);
52✔
663
    }
52✔
664

665
    if (dnsOverTLS && g_paddingOutgoing) {
10,666!
666
      addPadding(pw, bufsize, opts);
25✔
667
    }
25✔
668

669
    pw.addOpt(g_outgoingEDNSBufsize, 0, g_dnssecmode == DNSSECMode::Off ? 0 : EDNSOpts::DNSSECOK, opts);
10,666✔
670
    pw.commit();
10,666✔
671
  }
10,666✔
672
  lwr->d_rcode = 0;
10,670✔
673
  lwr->d_haveEDNS = false;
10,670✔
674
  LWResult::Result ret;
10,670✔
675

676
  DTime dt;
10,670✔
677
  dt.set();
10,670✔
678
  *now = dt.getTimeval();
10,670✔
679

680
  boost::uuids::uuid uuid;
10,670✔
681
  const struct timeval queryTime = *now;
10,670✔
682

683
  if (outgoingLoggers) {
10,670✔
684
    uuid = getUniqueID();
22✔
685
    logOutgoingQuery(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, vpacket.size(), srcmask, nsName);
22✔
686
  }
22✔
687

688
  srcmask = boost::none; // this is also our return value, even if EDNS0Level == 0
10,670✔
689

690
  // We only store the localip if needed for fstrm logging or cookie support
691
  ComboAddress localip;
10,670✔
692
  bool fstrmQEnabled = false;
10,670✔
693
  bool fstrmREnabled = false;
10,670✔
694

695
#ifdef HAVE_FSTRM
10,670✔
696
  if (isEnabledForQueries(fstrmLoggers)) {
10,670✔
697
    fstrmQEnabled = true;
2✔
698
  }
2✔
699
  if (isEnabledForResponses(fstrmLoggers)) {
10,670✔
700
    fstrmREnabled = true;
4✔
701
  }
4✔
702
#endif
10,670✔
703

704
  if (!doTCP) {
10,670✔
705
    int queryfd;
10,626✔
706
    try {
10,626✔
707
      ret = asendto(vpacket.data(), vpacket.size(), 0, address, addressToBindTo, qid, domain, type, subnetOpts, &queryfd, *now);
10,626✔
708
    }
10,626✔
709
    catch (const PDNSException& e) {
10,626✔
710
      if (addressToBindTo) {
×
711
        // Cookie info already has been added to packet, so we must retry from a higher level: SyncRes::asyncresolveWrapper
712
        auto lock = s_cookiestore.lock();
×
713
        lock->erase(address);
×
714
        return LWResult::Result::BindError;
×
715
      }
×
716
      throw;
×
717
    }
×
718
    if (ret != LWResult::Result::Success) {
10,632!
719
      return ret;
×
720
    }
×
721

722
    if (queryfd <= -1) {
10,632✔
723
      *chained = true;
1,562✔
724
    }
1,562✔
725

726
    if (!*chained) {
10,632✔
727
      if (cookieSentOut || fstrmQEnabled || fstrmREnabled) {
9,060✔
728
        localip.sin4.sin_family = address.sin4.sin_family;
41✔
729
        socklen_t slen = address.getSocklen();
41✔
730
        (void)getsockname(queryfd, reinterpret_cast<sockaddr*>(&localip), &slen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast))
41✔
731
      }
41✔
732
#ifdef HAVE_FSTRM
9,055✔
733
      if (fstrmQEnabled) {
9,055✔
734
        logFstreamQuery(fstrmLoggers, queryTime, localip, address, DnstapMessage::ProtocolType::DoUDP, context.d_auth ? context.d_auth : boost::none, vpacket);
2!
735
      }
2✔
736
#endif
9,055✔
737
    }
9,055✔
738

739
    // sleep until we see an answer to this, interface to mtasker
740
    ret = arecvfrom(buf, 0, address, len, qid, domain, type, queryfd, subnetOpts, *now);
10,632✔
741
  }
10,632✔
742
  else {
44✔
743
    bool isNew{};
44✔
744
    do {
44✔
745
      try {
44✔
746
        // If we get a new (not re-used) TCP connection that does not
747
        // work, we give up. For reused connections, we assume the
748
        // peer has closed it on error, so we retry. At some point we
749
        // *will* get a new connection, so this loop is not endless.
750
        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
44✔
751
        std::string subjectName;
44✔
752
        isNew = tcpconnect(log, address, addressToBindTo, connection, dnsOverTLS, nsName, subjectName);
44✔
753
        ret = tcpsendrecv(address, connection, localip, vpacket, len, buf, nsName, subjectName);
44✔
754
#ifdef HAVE_FSTRM
44✔
755
        if (fstrmQEnabled) {
44!
756
          logFstreamQuery(fstrmLoggers, queryTime, localip, address, !dnsOverTLS ? DnstapMessage::ProtocolType::DoTCP : DnstapMessage::ProtocolType::DoT, context.d_auth, vpacket);
×
757
        }
×
758
#endif /* HAVE_FSTRM */
44✔
759
        if (ret == LWResult::Result::Success) {
44✔
760
          break;
31✔
761
        }
31✔
762
        connection.d_handler->close();
13✔
763
      }
13✔
764
      catch (const BindError&) {
44✔
765
        // Cookie info already has been added to packet, so we must retry from a higher level
766
        auto lock = s_cookiestore.lock();
×
767
        lock->erase(address);
×
768
        VLOG(log, "BindError remote: " << address.toString() << " localAddress: " << (addressToBindTo ? addressToBindTo->toString() : "none") << endl);
×
769
        return LWResult::Result::BindError;
×
770
      }
×
771
      catch (const NetworkError& nwe) {
44✔
772
        VLOG(log, "NetworkException: " << address.toString() << ": " << nwe.what() << endl);
×
773
        ret = LWResult::Result::OSLimitError; // OS limits error
×
774
      }
×
775
      catch (const runtime_error& rte) {
44✔
776
        VLOG(log, "runtime_error: " << address.toString() << ": " << rte.what() << endl);
×
777
        ret = LWResult::Result::OSLimitError; // OS limits error (PermanentError is transport related)
×
778
      }
×
779
    } while (!isNew);
44!
780
  }
44✔
781

782
  lwr->d_usec = dt.udiff();
10,667✔
783
  *now = dt.getTimeval();
10,667✔
784

785
  if (ret != LWResult::Result::Success) { // includes 'timeout'
10,667✔
786
    if (outgoingLoggers) {
342!
787
      logIncomingResponse(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, srcmask, 0, -1, {}, queryTime, exportTypes, nsName);
×
788
    }
×
789
    return ret;
342✔
790
  }
342✔
791

792
  if (*chained) {
10,325✔
793
    auto msec = lwr->d_usec / 1000;
1,326✔
794
    if (msec > g_networkTimeoutMsec * 2 / 3) {
1,326!
795
      auto jitterMsec = dns_random(msec);
×
796
      if (jitterMsec > 0) {
×
797
        mthreadSleep(jitterMsec);
×
798
      }
×
799
    }
×
800
  }
1,326✔
801

802
  buf.resize(len);
10,325✔
803

804
#ifdef HAVE_FSTRM
10,325✔
805
  if (fstrmREnabled && (!*chained || doTCP)) {
10,325!
806
    DnstapMessage::ProtocolType protocol = doTCP ? DnstapMessage::ProtocolType::DoTCP : DnstapMessage::ProtocolType::DoUDP;
×
807
    if (dnsOverTLS) {
×
808
      protocol = DnstapMessage::ProtocolType::DoT;
×
809
    }
×
810
    logFstreamResponse(fstrmLoggers, localip, address, protocol, context.d_auth, buf, queryTime, *now);
×
811
  }
×
812
#endif /* HAVE_FSTRM */
10,325✔
813

814
  lwr->d_records.clear();
10,325✔
815
  try {
10,325✔
816
    lwr->d_tcbit = 0;
10,325✔
817
    MOADNSParser mdp(false, reinterpret_cast<const char*>(buf.data()), buf.size());
10,325✔
818
    lwr->d_aabit = mdp.d_header.aa;
10,325✔
819
    lwr->d_tcbit = mdp.d_header.tc;
10,325✔
820
    lwr->d_rcode = mdp.d_header.rcode;
10,325✔
821

822
    if (mdp.d_header.rcode == RCode::FormErr && mdp.d_qname.empty() && mdp.d_qtype == 0 && mdp.d_qclass == 0) {
10,325!
823
      if (outgoingLoggers) {
×
824
        logIncomingResponse(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, srcmask, len, lwr->d_rcode, lwr->d_records, queryTime, exportTypes, nsName);
×
825
      }
×
826
      lwr->d_validpacket = true;
×
827
      return LWResult::Result::Success; // this is "success", the error is set in lwr->d_rcode
×
828
    }
×
829

830
    if (domain != mdp.d_qname) {
10,325!
831
      if (!mdp.d_qname.empty() && domain.toString().find((char)0) == string::npos /* ugly */) { // embedded nulls are too noisy, plus empty domains are too
×
832
        g_slogout->info(Logr::Notice, "Packet purporting to come from remote server contained wrong answer",
×
833
                        "server", Logging::Loggable(address),
×
834
                        "qname", Logging::Loggable(domain),
×
835
                        "onwire", Logging::Loggable(mdp.d_qname));
×
836
      }
×
837
      // unexpected count has already been done @ pdns_recursor.cc
838
      goto out;
×
839
    }
×
840

841
    lwr->d_records.reserve(mdp.d_answers.size());
10,325✔
842
    for (const auto& answer : mdp.d_answers) {
66,704✔
843
      lwr->d_records.push_back(answer);
66,704✔
844
    }
66,704✔
845

846
    bool cookieFoundInReply = false;
10,325✔
847
    if (EDNSOpts edo; EDNS0Level > 0 && getEDNSOpts(mdp, &edo)) {
10,328✔
848
      lwr->d_haveEDNS = true;
10,324✔
849

850
      // If we sent out ECS, we can also expect to see a return with or without ECS, the absent case
851
      // is not handled explicitly. If we do see a ECS in the reply, the source part *must* match
852
      // with what we sent out. See https://www.rfc-editor.org/rfc/rfc7871#section-7.3. and section
853
      // 11.2.
854
      // For ECS hardening mode, the case where we sent out an ECS but did not receive a matching
855
      // one is handled in arecvfrom().
856
      if (subnetOpts) {
10,324✔
857
        // THE RFC is not clear about the case of having multiple ECS options. We only look at the first.
858
        if (const auto opt = edo.getFirstOption(EDNSOptionCode::ECS); opt != edo.d_options.end()) {
299✔
859
          EDNSSubnetOpts reso;
52✔
860
          if (EDNSSubnetOpts::getFromString(opt->second, &reso)) {
52!
861
            if (!doTCP && reso.getSource() != subnetOpts->getSource()) {
52!
862
              g_slogout->info(Logr::Notice, "Incoming ECS does not match outgoing",
1✔
863
                              "server", Logging::Loggable(address),
1✔
864
                              "qname", Logging::Loggable(domain),
1✔
865
                              "outgoing", Logging::Loggable(subnetOpts->getSource()),
1✔
866
                              "incoming", Logging::Loggable(reso.getSource()));
1✔
867
              return LWResult::Result::Spoofed;
1✔
868
            }
1✔
869
            /* rfc7871 states that 0 "indicate[s] that the answer is suitable for all addresses in FAMILY",
870
               so we might want to still pass the information along to be able to differentiate between
871
               IPv4 and IPv6. Still I'm pretty sure it doesn't matter in real life, so let's not duplicate
872
               entries in our cache. */
873
            if (reso.getScopePrefixLength() != 0) {
51!
874
              uint8_t bits = std::min(reso.getScopePrefixLength(), subnetOpts->getSourcePrefixLength());
51✔
875
              auto outgoingECSAddr = subnetOpts->getSource().getNetwork();
51✔
876
              outgoingECSAddr.truncate(bits);
51✔
877
              srcmask = Netmask(outgoingECSAddr, bits);
51✔
878
            }
51✔
879
          }
51✔
880
        }
52✔
881
      }
299✔
882
      if (g_cookies && !*chained) {
10,323!
883
        auto [done, result] = incomingCookie(log, address, localip, *now, cookieSentOut, edo, doTCP, *lwr, cookieFoundInReply);
52✔
884
        if (done) {
52✔
885
          return result;
8✔
886
        }
8✔
887
      }
52✔
888
    }
10,323✔
889

890
    // Case: we sent out a cookie but did not get one back
891
    if (cookieSentOut && !cookieFoundInReply && !*chained) {
10,316!
892
      ++t_Counters.at(rec::Counter::cookieNotInReply);
6✔
893
      auto lock = s_cookiestore.lock();
6✔
894
      auto found = lock->find(address);
6✔
895
      if (found != lock->end()) {
6!
896
        switch (found->getSupport()) {
6!
897
        case CookieEntry::Support::Probing:
6!
898
          VLOG(log, "No cookie in reply from " << address.toString() << ", was probing, setting support to Unsupported" << endl);
6!
899
          found->setSupport(CookieEntry::Support::Unsupported, now->tv_sec);
6✔
900
          ++t_Counters.at(rec::Counter::cookieProbeUnsupported);
6✔
901
          break;
6✔
902
        case CookieEntry::Support::Unsupported:
×
903
          // We could have detected the server does not support cookies in the meantime
904
          VLOG(log, "No cookie in reply from " << address.toString() << ", cookie state is Unsupported, fine" << endl);
×
905
          break;
×
906
        case CookieEntry::Support::Supported:
×
907
          // RFC says: ignore replies not containing any cookie info, equivalent to timeout
908
          VLOG(log, "No cookie in reply from " << address.toString() << ", cookie state is Supported, dropping packet as if it timed out)" << endl);
×
909
          return LWResult::Result::Timeout;
×
910
          break;
×
911
        }
6✔
912
      }
6✔
913
      else {
×
914
        VLOG(log, "No cookie in reply from " << address.toString() << ", cookie state is Unknown, dropping packet as if it timed out" << endl);
×
915
        return LWResult::Result::Timeout;
×
916
      }
×
917
    }
6✔
918

919
    if (outgoingLoggers) {
10,316✔
920
      logIncomingResponse(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, srcmask, len, lwr->d_rcode, lwr->d_records, queryTime, exportTypes, nsName);
22✔
921
    }
22✔
922

923
    lwr->d_validpacket = true;
10,316✔
924
    return LWResult::Result::Success;
10,316✔
925
  }
10,316✔
926
  catch (const std::exception& mde) {
10,325✔
927
    if (::arg().mustDo("log-common-errors")) {
×
928
      g_slogout->error(Logr::Notice, mde.what(), "Unable to parse packet from remote server", "server", Logging::Loggable(address),
×
929
                       "exception", Logging::Loggable("std::exception"));
×
930
    }
×
931

932
    lwr->d_rcode = RCode::FormErr;
×
933
    lwr->d_validpacket = false;
×
934
    t_Counters.at(rec::Counter::serverParseError)++;
×
935

936
    if (outgoingLoggers) {
×
937
      logIncomingResponse(outgoingLoggers, context.d_initialRequestId, uuid, address, domain, type, qid, doTCP, dnsOverTLS, srcmask, len, lwr->d_rcode, lwr->d_records, queryTime, exportTypes, nsName);
×
938
    }
×
939

940
    return LWResult::Result::Success; // success - oddly enough
×
941
  }
×
942
  catch (...) {
10,325✔
943
    g_slogout->info(Logr::Notice, "Unknown error parsing packet from remote server", "server", Logging::Loggable(address));
×
944
  }
×
945

946
  t_Counters.at(rec::Counter::serverParseError)++;
×
947

948
out:
×
949
  if (!lwr->d_rcode) {
×
950
    lwr->d_rcode = RCode::ServFail;
×
951
  }
×
952

953
  return LWResult::Result::PermanentError;
×
954
}
×
955

956
LWResult::Result asyncresolve(const OptLog& log, 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)
957
{
10,670✔
958
  TCPOutConnectionManager::Connection connection;
10,670✔
959
  auto ret = asyncresolve(log, address, domain, type, doTCP, sendRDQuery, EDNS0Level, now, srcmask, context, outgoingLoggers, fstrmLoggers, exportTypes, lwr, chained, connection);
10,670✔
960

961
  if (doTCP) {
10,670✔
962
    if (connection.d_handler && lwr->d_validpacket) {
35!
963
      t_tcp_manager.store(*now, std::make_pair(address, connection.d_local), std::move(connection));
31✔
964
    }
31✔
965
  }
35✔
966
  return ret;
10,670✔
967
}
10,670✔
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