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

PowerDNS / pdns / 15920880335

26 Jun 2025 03:30PM UTC coverage: 61.923% (-3.7%) from 65.652%
15920880335

push

github

web-flow
Merge pull request #15669 from miodvallat/serial_keyer

Increase zone serial number after zone key operations

38311 of 91850 branches covered (41.71%)

Branch coverage included in aggregate %.

27 of 29 new or added lines in 1 file covered. (93.1%)

6308 existing lines in 78 files now uncovered.

120482 of 164587 relevant lines covered (73.2%)

5965233.22 hits per line

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

20.17
/pdns/recursordist/rec-tcp.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

23
#include "rec-main.hh"
24

25
#include "arguments.hh"
26
#include "logger.hh"
27
#include "mplexer.hh"
28
#include "uuid-utils.hh"
29

30
// OLD PRE 5.0.0 situation:
31
//
32
// When pdns-distributes-queries is false with reuseport true (the default since 4.9.0), TCP queries
33
// are read and handled by worker threads. If the kernel balancing is OK for TCP sockets (observed
34
// to be good on Debian bullseye, but not good on e.g. MacOS), the TCP handling is no extra burden.
35
// In the case of MacOS all incoming TCP queries are handled by a single worker, while incoming UDP
36
// queries do get distributed round-robin over the worker threads.  Do note the TCP queries might
37
// need to wait until the g_maxUDPQueriesPerRound is reached.
38
//
39
// In the case of pdns-distributes-queries true and reuseport false the queries were read and
40
// initially processed by the distributor thread(s).
41
//
42
// Initial processing consist of parsing, calling gettag and checking if we have a packet cache
43
// hit. If that does not produce a hit, the query is passed to an mthread in the same way as with
44
// UDP queries, but do note that the mthread processing is serviced by the distributor thread. The
45
// final answer will be sent by the same distributor thread that originally picked up the query.
46
//
47
// Changing this, and having incoming TCP queries handled by worker threads is somewhat more complex
48
// than UDP, as the socket must remain available in the distributor thread (for reading more
49
// queries), but the TCP socket must also be passed to a worker thread so it can write its
50
// answer. The in-flight bookkeeping also has to be aware of how a query is handled to do the
51
// accounting properly. I am not sure if changing the current setup is worth all this trouble,
52
// especially since the default is now to not use pdns-distributes-queries, which works well in many
53
// cases.
54
//
55
// NEW SITUATION SINCE 5.0.0:
56
//
57
// The drawback mentioned in https://github.com/PowerDNS/pdns/issues/8394 are not longer true, so an
58
// alternative approach would be to introduce dedicated TCP worker thread(s).
59
//
60
// This approach was implemented in https://github.com/PowerDNS/pdns/pull/13195. The distributor and
61
// worker thread(s) now no longer process TCP queries.
62

63
size_t g_tcpMaxQueriesPerConn;
64
unsigned int g_maxTCPClients;
65
unsigned int g_maxTCPPerClient;
66
int g_tcpTimeout;
67
bool g_anyToTcp;
68

69
uint16_t TCPConnection::s_maxInFlight;
70

71
using tcpClientCounts_t = map<ComboAddress, uint32_t, ComboAddress::addressOnlyLessThan>;
72
static thread_local std::unique_ptr<tcpClientCounts_t> t_tcpClientCounts = std::make_unique<tcpClientCounts_t>();
73

74
static void handleRunningTCPQuestion(int fileDesc, FDMultiplexer::funcparam_t& var);
75

76
#if 0
77
#define TCPLOG(tcpsock, x)                                 \
78
  do {                                                     \
79
    cerr << []() { timeval t; gettimeofday(&t, nullptr); return t.tv_sec % 10  + t.tv_usec/1000000.0; }() << " FD " << (tcpsock) << ' ' << x; \
80
  } while (0)
81
#else
82
// We do not define this as empty since that produces a duplicate case label warning from clang-tidy
83
#define TCPLOG(pid, x) /* NOLINT(cppcoreguidelines-macro-usage) */ \
84
  while (false) {                                                  \
651✔
85
    cerr << x; /* NOLINT(bugprone-macro-parentheses) */            \
×
86
  }
×
87
#endif
88

89
std::atomic<uint32_t> TCPConnection::s_currentConnections;
90

91
TCPConnection::TCPConnection(int fileDesc, const ComboAddress& addr) :
92
  data(2, 0), d_remote(addr), d_fd(fileDesc)
UNCOV
93
{
×
UNCOV
94
  ++s_currentConnections;
×
UNCOV
95
  (*t_tcpClientCounts)[d_remote]++;
×
UNCOV
96
}
×
97

98
TCPConnection::~TCPConnection()
UNCOV
99
{
×
UNCOV
100
  try {
×
UNCOV
101
    if (closesocket(d_fd) < 0) {
×
102
      SLOG(g_log << Logger::Error << "Error closing socket for TCPConnection" << endl,
×
103
           g_slogtcpin->info(Logr::Error, "Error closing socket for TCPConnection"));
×
104
    }
×
UNCOV
105
  }
×
UNCOV
106
  catch (const PDNSException& e) {
×
107
    SLOG(g_log << Logger::Error << "Error closing TCPConnection socket: " << e.reason << endl,
×
108
         g_slogtcpin->error(Logr::Error, e.reason, "Error closing TCPConnection socket", "exception", Logging::Loggable("PDNSException")));
×
109
  }
×
110

UNCOV
111
  if (t_tcpClientCounts && t_tcpClientCounts->count(d_remote) != 0 && (*t_tcpClientCounts)[d_remote]-- == 0) {
×
112
    t_tcpClientCounts->erase(d_remote);
×
113
  }
×
UNCOV
114
  --s_currentConnections;
×
UNCOV
115
}
×
116

117
static void terminateTCPConnection(int fileDesc)
UNCOV
118
{
×
UNCOV
119
  try {
×
UNCOV
120
    t_fdm->removeReadFD(fileDesc);
×
UNCOV
121
  }
×
UNCOV
122
  catch (const FDMultiplexerException& fde) {
×
123
  }
×
UNCOV
124
}
×
125

126
static void sendErrorOverTCP(std::unique_ptr<DNSComboWriter>& comboWriter, int rcode)
127
{
×
128
  std::vector<uint8_t> packet;
×
129
  if (comboWriter->d_mdp.d_header.qdcount == 0U) {
×
130
    /* header-only */
131
    packet.resize(sizeof(dnsheader));
×
132
  }
×
133
  else {
×
134
    DNSPacketWriter packetWriter(packet, comboWriter->d_mdp.d_qname, comboWriter->d_mdp.d_qtype, comboWriter->d_mdp.d_qclass);
×
135
    if (comboWriter->d_mdp.hasEDNS()) {
×
136
      /* we try to add the EDNS OPT RR even for truncated answers,
137
         as rfc6891 states:
138
         "The minimal response MUST be the DNS header, question section, and an
139
         OPT record.  This MUST also occur when a truncated response (using
140
         the DNS header's TC bit) is returned."
141
      */
142
      packetWriter.addOpt(512, 0, 0);
×
143
      packetWriter.commit();
×
144
    }
×
145
  }
×
146

147
  auto& header = reinterpret_cast<dnsheader&>(packet.at(0)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) safe cast
×
148
  header.aa = 0;
×
149
  header.ra = 1;
×
150
  header.qr = 1;
×
151
  header.tc = 0;
×
152
  header.id = comboWriter->d_mdp.d_header.id;
×
153
  header.rd = comboWriter->d_mdp.d_header.rd;
×
154
  header.cd = comboWriter->d_mdp.d_header.cd;
×
155
  header.rcode = rcode;
×
156

157
  sendResponseOverTCP(comboWriter, packet);
×
158
}
×
159

160
void finishTCPReply(std::unique_ptr<DNSComboWriter>& comboWriter, bool hadError, bool updateInFlight)
UNCOV
161
{
×
162
  // update tcp connection status, closing if needed and doing the fd multiplexer accounting
UNCOV
163
  if (updateInFlight && comboWriter->d_tcpConnection->d_requestsInFlight > 0) {
×
UNCOV
164
    comboWriter->d_tcpConnection->d_requestsInFlight--;
×
UNCOV
165
  }
×
166

167
  // In the code below, we try to remove the fd from the set, but
168
  // we don't know if another mthread already did the remove, so we can get a
169
  // "Tried to remove unlisted fd" exception.  Not that an inflight < limit test
170
  // will not work since we do not know if the other mthread got an error or not.
UNCOV
171
  if (hadError) {
×
172
    terminateTCPConnection(comboWriter->d_socket);
×
173
    comboWriter->d_socket = -1;
×
174
    return;
×
175
  }
×
UNCOV
176
  comboWriter->d_tcpConnection->queriesCount++;
×
UNCOV
177
  if ((g_tcpMaxQueriesPerConn > 0 && comboWriter->d_tcpConnection->queriesCount >= g_tcpMaxQueriesPerConn) || (comboWriter->d_tcpConnection->isDropOnIdle() && comboWriter->d_tcpConnection->d_requestsInFlight == 0)) {
×
UNCOV
178
    try {
×
UNCOV
179
      t_fdm->removeReadFD(comboWriter->d_socket);
×
UNCOV
180
    }
×
UNCOV
181
    catch (FDMultiplexerException&) {
×
182
    }
×
UNCOV
183
    comboWriter->d_socket = -1;
×
UNCOV
184
    return;
×
UNCOV
185
  }
×
186

UNCOV
187
  Utility::gettimeofday(&g_now, nullptr); // needs to be updated
×
UNCOV
188
  struct timeval ttd = g_now;
×
189

190
  // If we cross from max to max-1 in flight requests, the fd was not listened to, add it back
UNCOV
191
  if (updateInFlight && comboWriter->d_tcpConnection->d_requestsInFlight == TCPConnection::s_maxInFlight - 1) {
×
192
    // A read error might have happened. If we add the fd back, it will most likely error again.
193
    // This is not a big issue, the next handleTCPClientReadable() will see another read error
194
    // and take action.
UNCOV
195
    ttd.tv_sec += g_tcpTimeout;
×
UNCOV
196
    t_fdm->addReadFD(comboWriter->d_socket, handleRunningTCPQuestion, comboWriter->d_tcpConnection, &ttd);
×
UNCOV
197
    return;
×
UNCOV
198
  }
×
199
  // fd might have been removed by read error code, or a read timeout, so expect an exception
UNCOV
200
  try {
×
UNCOV
201
    t_fdm->setReadTTD(comboWriter->d_socket, ttd, g_tcpTimeout);
×
UNCOV
202
  }
×
UNCOV
203
  catch (const FDMultiplexerException&) {
×
204
    // but if the FD was removed because of a timeout while we were sending a response,
205
    // we need to re-arm it. If it was an error it will error again.
206
    ttd.tv_sec += g_tcpTimeout;
×
207
    t_fdm->addReadFD(comboWriter->d_socket, handleRunningTCPQuestion, comboWriter->d_tcpConnection, &ttd);
×
208
  }
×
UNCOV
209
}
×
210

211
/*
212
 * A helper class that by default closes the incoming TCP connection on destruct
213
 * If you want to keep the connection alive, call keep() on the guard object
214
 */
215
class RunningTCPQuestionGuard
216
{
217
public:
218
  RunningTCPQuestionGuard(const RunningTCPQuestionGuard&) = default;
219
  RunningTCPQuestionGuard(RunningTCPQuestionGuard&&) = delete;
220
  RunningTCPQuestionGuard& operator=(const RunningTCPQuestionGuard&) = default;
221
  RunningTCPQuestionGuard& operator=(RunningTCPQuestionGuard&&) = delete;
222
  RunningTCPQuestionGuard(int fileDesc) :
UNCOV
223
    d_fd(fileDesc) {}
×
224
  ~RunningTCPQuestionGuard()
UNCOV
225
  {
×
UNCOV
226
    if (d_fd != -1) {
×
UNCOV
227
      terminateTCPConnection(d_fd);
×
UNCOV
228
      d_fd = -1;
×
UNCOV
229
    }
×
UNCOV
230
  }
×
231
  void keep()
UNCOV
232
  {
×
UNCOV
233
    d_fd = -1;
×
UNCOV
234
  }
×
235
  bool handleTCPReadResult(int /* fd */, ssize_t bytes)
UNCOV
236
  {
×
UNCOV
237
    if (bytes == 0) {
×
238
      /* EOF */
UNCOV
239
      return false;
×
UNCOV
240
    }
×
UNCOV
241
    if (bytes < 0) {
×
UNCOV
242
      if (errno != EAGAIN && errno != EWOULDBLOCK) {
×
243
        return false;
×
244
      }
×
UNCOV
245
    }
×
UNCOV
246
    keep();
×
UNCOV
247
    return true;
×
UNCOV
248
  }
×
249

250
private:
251
  int d_fd{-1};
252
};
253

254
static void handleNotify(std::unique_ptr<DNSComboWriter>& comboWriter, const DNSName& qname)
UNCOV
255
{
×
UNCOV
256
  if (!t_allowNotifyFrom || !t_allowNotifyFrom->match(comboWriter->d_mappedSource)) {
×
257
    if (!g_quiet) {
×
258
      SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping TCP NOTIFY from " << comboWriter->d_mappedSource.toString() << ", address not matched by allow-notify-from" << endl,
×
259
           g_slogtcpin->info(Logr::Error, "Dropping TCP NOTIFY, address not matched by allow-notify-from", "source", Logging::Loggable(comboWriter->d_mappedSource)));
×
260
    }
×
261

262
    t_Counters.at(rec::Counter::sourceDisallowedNotify)++;
×
263
    return;
×
264
  }
×
265

UNCOV
266
  if (!isAllowNotifyForZone(qname)) {
×
267
    if (!g_quiet) {
×
268
      SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping TCP NOTIFY from " << comboWriter->d_mappedSource.toString() << ", for " << qname.toLogString() << ", zone not matched by allow-notify-for" << endl,
×
269
           g_slogtcpin->info(Logr::Error, "Dropping TCP NOTIFY,  zone not matched by allow-notify-for", "source", Logging::Loggable(comboWriter->d_mappedSource), "zone", Logging::Loggable(qname)));
×
270
    }
×
271

272
    t_Counters.at(rec::Counter::zoneDisallowedNotify)++;
×
273
    return;
×
274
  }
×
UNCOV
275
}
×
276

277
static void doProtobufLogQuery(bool logQuery, LocalStateHolder<LuaConfigItems>& luaconfsLocal, const std::unique_ptr<DNSComboWriter>& comboWriter, const DNSName& qname, QType qtype, QClass qclass, const dnsheader* dnsheader, const shared_ptr<TCPConnection>& conn, const boost::optional<uint32_t>& ednsVersion)
UNCOV
278
{
×
UNCOV
279
  try {
×
UNCOV
280
    if (logQuery && !(luaconfsLocal->protobufExportConfig.taggedOnly && comboWriter->d_policyTags.empty())) {
×
UNCOV
281
      protobufLogQuery(luaconfsLocal, comboWriter->d_uuid, comboWriter->d_source, comboWriter->d_destination, comboWriter->d_mappedSource, comboWriter->d_ednssubnet.getSource(), true, conn->qlen, qname, qtype, qclass, comboWriter->d_policyTags, comboWriter->d_requestorId, comboWriter->d_deviceId, comboWriter->d_deviceName, comboWriter->d_meta, ednsVersion, *dnsheader);
×
UNCOV
282
    }
×
UNCOV
283
  }
×
UNCOV
284
  catch (const std::exception& e) {
×
285
    if (g_logCommonErrors) {
×
286
      SLOG(g_log << Logger::Warning << "Error parsing a TCP query packet for edns subnet: " << e.what() << endl,
×
287
           g_slogtcpin->error(Logr::Warning, e.what(), "Error parsing a TCP query packet for edns subnet", "exception", Logging::Loggable("std::exception"), "remote", Logging::Loggable(conn->d_remote)));
×
288
    }
×
289
  }
×
UNCOV
290
}
×
291

292
static void doProcessTCPQuestion(std::unique_ptr<DNSComboWriter>& comboWriter, shared_ptr<TCPConnection>& conn, RunningTCPQuestionGuard& tcpGuard, int fileDesc)
UNCOV
293
{
×
UNCOV
294
  RecThreadInfo::self().incNumberOfDistributedQueries();
×
UNCOV
295
  struct timeval start{};
×
UNCOV
296
  Utility::gettimeofday(&start, nullptr);
×
297

UNCOV
298
  DNSName qname;
×
UNCOV
299
  uint16_t qtype = 0;
×
UNCOV
300
  uint16_t qclass = 0;
×
UNCOV
301
  bool needEDNSParse = false;
×
UNCOV
302
  string requestorId;
×
UNCOV
303
  string deviceId;
×
UNCOV
304
  string deviceName;
×
UNCOV
305
  bool logQuery = false;
×
UNCOV
306
  bool qnameParsed = false;
×
UNCOV
307
  boost::optional<uint32_t> ednsVersion;
×
308

UNCOV
309
  comboWriter->d_eventTrace.setEnabled(SyncRes::s_event_trace_enabled != 0);
×
310
  // eventTrace uses monotonic time, while OpenTelemetry uses absolute time. setEnabled()
311
  // established the reference point, get an absolute TS as close as possible to the
312
  // eventTrace start of trace time.
UNCOV
313
  auto traceTS = pdns::trace::timestamp();
×
UNCOV
314
  comboWriter->d_eventTrace.add(RecEventTrace::ReqRecv);
×
UNCOV
315
  if (SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
×
316
    comboWriter->d_otTrace.clear();
×
317
    comboWriter->d_otTrace.start_time_unix_nano = traceTS;
×
318
    comboWriter->d_otTrace.name = "RecRequest";
×
319
  }
×
UNCOV
320
  auto luaconfsLocal = g_luaconfs.getLocal();
×
UNCOV
321
  if (checkProtobufExport(luaconfsLocal)) {
×
UNCOV
322
    needEDNSParse = true;
×
UNCOV
323
  }
×
UNCOV
324
  logQuery = t_protobufServers.servers && luaconfsLocal->protobufExportConfig.logQueries;
×
UNCOV
325
  comboWriter->d_logResponse = t_protobufServers.servers && luaconfsLocal->protobufExportConfig.logResponses;
×
326

UNCOV
327
  if (needEDNSParse || (t_pdl && (t_pdl->hasGettagFFIFunc() || t_pdl->hasGettagFunc())) || comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Notify)) {
×
328

UNCOV
329
    try {
×
UNCOV
330
      EDNSOptionViewMap ednsOptions;
×
UNCOV
331
      comboWriter->d_ecsParsed = true;
×
UNCOV
332
      comboWriter->d_ecsFound = false;
×
UNCOV
333
      getQNameAndSubnet(conn->data, &qname, &qtype, &qclass,
×
UNCOV
334
                        comboWriter->d_ecsFound, &comboWriter->d_ednssubnet,
×
UNCOV
335
                        (g_gettagNeedsEDNSOptions || SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) ? &ednsOptions : nullptr, ednsVersion);
×
UNCOV
336
      qnameParsed = true;
×
337

UNCOV
338
      if (SyncRes::eventTraceEnabled(SyncRes::event_trace_to_ot)) {
×
339
        pdns::trace::extractOTraceIDs(ednsOptions, comboWriter->d_otTrace);
×
340
      }
×
UNCOV
341
      if (t_pdl) {
×
UNCOV
342
        try {
×
UNCOV
343
          if (t_pdl->hasGettagFFIFunc()) {
×
UNCOV
344
            RecursorLua4::FFIParams params(qname, qtype, comboWriter->d_local, comboWriter->d_remote, comboWriter->d_destination, comboWriter->d_source, comboWriter->d_ednssubnet.getSource(), comboWriter->d_data, comboWriter->d_gettagPolicyTags, comboWriter->d_records, ednsOptions, comboWriter->d_proxyProtocolValues, requestorId, deviceId, deviceName, comboWriter->d_routingTag, comboWriter->d_rcode, comboWriter->d_ttlCap, comboWriter->d_variable, true, logQuery, comboWriter->d_logResponse, comboWriter->d_followCNAMERecords, comboWriter->d_extendedErrorCode, comboWriter->d_extendedErrorExtra, comboWriter->d_responsePaddingDisabled, comboWriter->d_meta);
×
UNCOV
345
            auto match = comboWriter->d_eventTrace.add(RecEventTrace::LuaGetTagFFI);
×
UNCOV
346
            comboWriter->d_tag = t_pdl->gettag_ffi(params);
×
UNCOV
347
            comboWriter->d_eventTrace.add(RecEventTrace::LuaGetTagFFI, comboWriter->d_tag, false, match);
×
UNCOV
348
          }
×
UNCOV
349
          else if (t_pdl->hasGettagFunc()) {
×
UNCOV
350
            auto match = comboWriter->d_eventTrace.add(RecEventTrace::LuaGetTag);
×
UNCOV
351
            comboWriter->d_tag = t_pdl->gettag(comboWriter->d_source, comboWriter->d_ednssubnet.getSource(), comboWriter->d_destination, qname, qtype, &comboWriter->d_gettagPolicyTags, comboWriter->d_data, ednsOptions, true, requestorId, deviceId, deviceName, comboWriter->d_routingTag, comboWriter->d_proxyProtocolValues);
×
UNCOV
352
            comboWriter->d_eventTrace.add(RecEventTrace::LuaGetTag, comboWriter->d_tag, false, match);
×
UNCOV
353
          }
×
354
          // Copy d_gettagPolicyTags to d_policyTags, so other Lua hooks see them and can add their
355
          // own. Before storing into the packetcache, the tags in d_gettagPolicyTags will be
356
          // cleared by addPolicyTagsToPBMessageIfNeeded() so they do *not* end up in the PC. When an
357
          // Protobuf message is constructed, one part comes from the PC (including the tags
358
          // set by non-gettag hooks), and the tags in d_gettagPolicyTags will be added by the code
359
          // constructing the PB message.
UNCOV
360
          comboWriter->d_policyTags = comboWriter->d_gettagPolicyTags;
×
UNCOV
361
        }
×
UNCOV
362
        catch (const MOADNSException& moadnsexception) {
×
363
          if (g_logCommonErrors) {
×
364
            g_slogtcpin->error(moadnsexception.what(), "Error parsing a query packet for tag determination", "qname", Logging::Loggable(qname), "excepion", Logging::Loggable("MOADNSException"));
×
365
          }
×
366
        }
×
UNCOV
367
        catch (const std::exception& stdException) {
×
368
          g_rateLimitedLogger.log(g_slogtcpin, "Error parsing a query packet for tag determination", stdException, "qname", Logging::Loggable(qname), "remote", Logging::Loggable(conn->d_remote));
×
369
        }
×
UNCOV
370
      }
×
UNCOV
371
    }
×
UNCOV
372
    catch (const std::exception& stdException) {
×
373
      g_rateLimitedLogger.log(g_slogudpin, "Error parsing a query packet for tag determination, setting tag=0", stdException, "remote", Logging::Loggable(conn->d_remote));
×
374
    }
×
UNCOV
375
  }
×
376

UNCOV
377
  if (comboWriter->d_tag == 0 && !comboWriter->d_responsePaddingDisabled && g_paddingFrom.match(comboWriter->d_remote)) {
×
378
    comboWriter->d_tag = g_paddingTag;
×
379
  }
×
380

UNCOV
381
  const dnsheader_aligned headerdata(conn->data.data());
×
UNCOV
382
  const struct dnsheader* dnsheader = headerdata.get();
×
383

UNCOV
384
  if (t_protobufServers.servers || t_outgoingProtobufServers.servers) {
×
UNCOV
385
    comboWriter->d_requestorId = std::move(requestorId);
×
UNCOV
386
    comboWriter->d_deviceId = std::move(deviceId);
×
UNCOV
387
    comboWriter->d_deviceName = std::move(deviceName);
×
UNCOV
388
    comboWriter->d_uuid = getUniqueID();
×
UNCOV
389
  }
×
390

UNCOV
391
  if (t_protobufServers.servers) {
×
UNCOV
392
    doProtobufLogQuery(logQuery, luaconfsLocal, comboWriter, qname, qtype, qclass, dnsheader, conn, ednsVersion);
×
UNCOV
393
  }
×
394

UNCOV
395
  if (t_pdl) {
×
UNCOV
396
    bool ipf = t_pdl->ipfilter(comboWriter->d_source, comboWriter->d_destination, *dnsheader, comboWriter->d_eventTrace);
×
UNCOV
397
    if (ipf) {
×
UNCOV
398
      if (!g_quiet) {
×
UNCOV
399
        SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " [" << g_multiTasker->getTid() << "/" << g_multiTasker->numProcesses() << "] DROPPED TCP question from " << comboWriter->d_source.toStringWithPort() << (comboWriter->d_source != comboWriter->d_remote ? " (via " + comboWriter->d_remote.toStringWithPort() + ")" : "") << " based on policy" << endl,
×
UNCOV
400
             g_slogtcpin->info(Logr::Info, "Dropped TCP question based on policy", "remote", Logging::Loggable(conn->d_remote), "source", Logging::Loggable(comboWriter->d_source)));
×
UNCOV
401
      }
×
UNCOV
402
      t_Counters.at(rec::Counter::policyDrops)++;
×
UNCOV
403
      return;
×
UNCOV
404
    }
×
UNCOV
405
  }
×
406

UNCOV
407
  if (comboWriter->d_mdp.d_header.qr) {
×
408
    t_Counters.at(rec::Counter::ignoredCount)++;
×
409
    if (g_logCommonErrors) {
×
410
      SLOG(g_log << Logger::Error << "Ignoring answer from TCP client " << comboWriter->getRemote() << " on server socket!" << endl,
×
411
           g_slogtcpin->info(Logr::Error, "Ignoring answer from TCP client on server socket", "remote", Logging::Loggable(comboWriter->getRemote())));
×
412
    }
×
413
    return;
×
414
  }
×
UNCOV
415
  if (comboWriter->d_mdp.d_header.opcode != static_cast<unsigned>(Opcode::Query) && comboWriter->d_mdp.d_header.opcode != static_cast<unsigned>(Opcode::Notify)) {
×
416
    t_Counters.at(rec::Counter::ignoredCount)++;
×
417
    if (g_logCommonErrors) {
×
418
      SLOG(g_log << Logger::Error << "Ignoring unsupported opcode " << Opcode::to_s(comboWriter->d_mdp.d_header.opcode) << " from TCP client " << comboWriter->getRemote() << " on server socket!" << endl,
×
419
           g_slogtcpin->info(Logr::Error, "Ignoring unsupported opcode from TCP client", "remote", Logging::Loggable(comboWriter->getRemote()), "opcode", Logging::Loggable(Opcode::to_s(comboWriter->d_mdp.d_header.opcode))));
×
420
    }
×
421
    sendErrorOverTCP(comboWriter, RCode::NotImp);
×
422
    tcpGuard.keep();
×
423
    return;
×
424
  }
×
UNCOV
425
  if (dnsheader->qdcount == 0U) {
×
426
    t_Counters.at(rec::Counter::emptyQueriesCount)++;
×
427
    if (g_logCommonErrors) {
×
428
      SLOG(g_log << Logger::Error << "Ignoring empty (qdcount == 0) query from " << comboWriter->getRemote() << " on server socket!" << endl,
×
429
           g_slogtcpin->info(Logr::Error, "Ignoring empty (qdcount == 0) query on server socket", "remote", Logging::Loggable(comboWriter->getRemote())));
×
430
    }
×
431
    sendErrorOverTCP(comboWriter, RCode::NotImp);
×
432
    tcpGuard.keep();
×
433
    return;
×
434
  }
×
UNCOV
435
  {
×
436
    // We have read a proper query
UNCOV
437
    ++t_Counters.at(rec::Counter::qcounter);
×
UNCOV
438
    ++t_Counters.at(rec::Counter::tcpqcounter);
×
UNCOV
439
    if (comboWriter->d_source.sin4.sin_family == AF_INET6) {
×
UNCOV
440
      ++t_Counters.at(rec::Counter::ipv6qcounter);
×
UNCOV
441
    }
×
442

UNCOV
443
    if (comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Notify)) {
×
UNCOV
444
      handleNotify(comboWriter, qname);
×
UNCOV
445
    }
×
446

UNCOV
447
    string response;
×
UNCOV
448
    RecursorPacketCache::OptPBData pbData{boost::none};
×
449

UNCOV
450
    if (comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Query)) {
×
451
      /* It might seem like a good idea to skip the packet cache lookup if we know that the answer is not cacheable,
452
         but it means that the hash would not be computed. If some script decides at a later time to mark back the answer
453
         as cacheable we would cache it with a wrong tag, so better safe than sorry. */
UNCOV
454
      auto match = comboWriter->d_eventTrace.add(RecEventTrace::PCacheCheck);
×
UNCOV
455
      bool cacheHit = checkForCacheHit(qnameParsed, comboWriter->d_tag, conn->data, qname, qtype, qclass, g_now, response, comboWriter->d_qhash, pbData, true, comboWriter->d_source, comboWriter->d_mappedSource);
×
UNCOV
456
      comboWriter->d_eventTrace.add(RecEventTrace::PCacheCheck, cacheHit, false, match);
×
457

UNCOV
458
      if (cacheHit) {
×
UNCOV
459
        if (!g_quiet) {
×
UNCOV
460
          SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " TCP question answered from packet cache tag=" << comboWriter->d_tag << " from " << comboWriter->d_source.toStringWithPort() << (comboWriter->d_source != comboWriter->d_remote ? " (via " + comboWriter->d_remote.toStringWithPort() + ")" : "") << endl,
×
UNCOV
461
               g_slogtcpin->info(Logr::Notice, "TCP question answered from packet cache", "tag", Logging::Loggable(comboWriter->d_tag),
×
UNCOV
462
                                 "qname", Logging::Loggable(qname), "qtype", Logging::Loggable(QType(qtype)),
×
UNCOV
463
                                 "source", Logging::Loggable(comboWriter->d_source), "remote", Logging::Loggable(comboWriter->d_remote)));
×
UNCOV
464
        }
×
465

UNCOV
466
        bool hadError = sendResponseOverTCP(comboWriter, response);
×
UNCOV
467
        finishTCPReply(comboWriter, hadError, false);
×
UNCOV
468
        struct timeval now{};
×
UNCOV
469
        Utility::gettimeofday(&now, nullptr);
×
UNCOV
470
        uint64_t spentUsec = uSec(now - start);
×
UNCOV
471
        t_Counters.at(rec::Histogram::cumulativeAnswers)(spentUsec);
×
UNCOV
472
        comboWriter->d_eventTrace.add(RecEventTrace::AnswerSent);
×
473

UNCOV
474
        if (t_protobufServers.servers && comboWriter->d_logResponse && (!luaconfsLocal->protobufExportConfig.taggedOnly || (pbData && pbData->d_tagged))) {
×
UNCOV
475
          struct timeval tval{
×
UNCOV
476
            0, 0};
×
UNCOV
477
          protobufLogResponse(qname, qtype, dnsheader, luaconfsLocal, pbData, tval, true, comboWriter->d_source, comboWriter->d_destination, comboWriter->d_mappedSource, comboWriter->d_ednssubnet, comboWriter->d_uuid, comboWriter->d_requestorId, comboWriter->d_deviceId, comboWriter->d_deviceName, comboWriter->d_meta, comboWriter->d_eventTrace, comboWriter->d_otTrace, comboWriter->d_policyTags);
×
UNCOV
478
        }
×
479

UNCOV
480
        if (comboWriter->d_eventTrace.enabled() && SyncRes::eventTraceEnabled(SyncRes::event_trace_to_log)) {
×
481
          SLOG(g_log << Logger::Info << comboWriter->d_eventTrace.toString() << endl,
×
482
               g_slogtcpin->info(Logr::Info, comboWriter->d_eventTrace.toString())); // More fancy?
×
483
        }
×
UNCOV
484
        tcpGuard.keep();
×
UNCOV
485
        t_Counters.updateSnap(g_regressionTestMode);
×
UNCOV
486
        return;
×
UNCOV
487
      } // cache hit
×
UNCOV
488
    } // query opcode
×
489

UNCOV
490
    if (comboWriter->d_mdp.d_header.opcode == static_cast<unsigned>(Opcode::Notify)) {
×
UNCOV
491
      if (!g_quiet) {
×
UNCOV
492
        SLOG(g_log << Logger::Notice << RecThreadInfo::id() << " got NOTIFY for " << qname.toLogString() << " from " << comboWriter->d_source.toStringWithPort() << (comboWriter->d_source != comboWriter->d_remote ? " (via " + comboWriter->d_remote.toStringWithPort() + ")" : "") << endl,
×
UNCOV
493
             g_slogtcpin->info(Logr::Notice, "Got NOTIFY", "qname", Logging::Loggable(qname), "source", Logging::Loggable(comboWriter->d_source), "remote", Logging::Loggable(comboWriter->d_remote)));
×
UNCOV
494
      }
×
495

UNCOV
496
      requestWipeCaches(qname);
×
497

498
      // the operation will now be treated as a Query, generating
499
      // a normal response, as the rest of the code does not
500
      // check dh->opcode, but we need to ensure that the response
501
      // to this request does not get put into the packet cache
UNCOV
502
      comboWriter->d_variable = true;
×
UNCOV
503
    }
×
504

505
    // setup for startDoResolve() in an mthread
UNCOV
506
    ++conn->d_requestsInFlight;
×
UNCOV
507
    if (conn->d_requestsInFlight >= TCPConnection::s_maxInFlight) {
×
UNCOV
508
      t_fdm->removeReadFD(fileDesc); // should no longer awake ourselves when there is data to read
×
UNCOV
509
    }
×
UNCOV
510
    else {
×
UNCOV
511
      Utility::gettimeofday(&g_now, nullptr); // needed?
×
UNCOV
512
      struct timeval ttd = g_now;
×
UNCOV
513
      t_fdm->setReadTTD(fileDesc, ttd, g_tcpTimeout);
×
UNCOV
514
    }
×
UNCOV
515
    tcpGuard.keep();
×
UNCOV
516
    g_multiTasker->makeThread(startDoResolve, comboWriter.release()); // deletes dc
×
UNCOV
517
  } // good query
×
UNCOV
518
}
×
519

520
static void handleRunningTCPQuestion(int fileDesc, FDMultiplexer::funcparam_t& var) // NOLINT(readability-function-cognitive-complexity)
UNCOV
521
{
×
UNCOV
522
  auto conn = boost::any_cast<shared_ptr<TCPConnection>>(var);
×
523

UNCOV
524
  RunningTCPQuestionGuard tcpGuard{fileDesc};
×
525

UNCOV
526
  if (conn->state == TCPConnection::PROXYPROTOCOLHEADER) {
×
UNCOV
527
    ssize_t bytes = recv(conn->getFD(), &conn->data.at(conn->proxyProtocolGot), conn->proxyProtocolNeed, 0);
×
UNCOV
528
    if (bytes <= 0) {
×
UNCOV
529
      tcpGuard.handleTCPReadResult(fileDesc, bytes);
×
UNCOV
530
      return;
×
UNCOV
531
    }
×
532

UNCOV
533
    conn->proxyProtocolGot += bytes;
×
UNCOV
534
    conn->data.resize(conn->proxyProtocolGot);
×
UNCOV
535
    ssize_t remaining = isProxyHeaderComplete(conn->data);
×
UNCOV
536
    if (remaining == 0) {
×
UNCOV
537
      if (g_logCommonErrors) {
×
UNCOV
538
        SLOG(g_log << Logger::Error << "Unable to consume proxy protocol header in packet from TCP client " << conn->d_remote.toStringWithPort() << endl,
×
UNCOV
539
             g_slogtcpin->info(Logr::Error, "Unable to consume proxy protocol header in packet from TCP client", "remote", Logging::Loggable(conn->d_remote)));
×
UNCOV
540
      }
×
UNCOV
541
      ++t_Counters.at(rec::Counter::proxyProtocolInvalidCount);
×
UNCOV
542
      return;
×
UNCOV
543
    }
×
UNCOV
544
    if (remaining < 0) {
×
UNCOV
545
      conn->proxyProtocolNeed = -remaining;
×
UNCOV
546
      conn->data.resize(conn->proxyProtocolGot + conn->proxyProtocolNeed);
×
UNCOV
547
      tcpGuard.keep();
×
UNCOV
548
      return;
×
UNCOV
549
    }
×
UNCOV
550
    {
×
551
      /* proxy header received */
552
      /* we ignore the TCP field for now, but we could properly set whether
553
         the connection was received over UDP or TCP if needed */
UNCOV
554
      bool tcp = false;
×
UNCOV
555
      bool proxy = false;
×
UNCOV
556
      size_t used = parseProxyHeader(conn->data, proxy, conn->d_source, conn->d_destination, tcp, conn->proxyProtocolValues);
×
UNCOV
557
      if (used <= 0) {
×
558
        if (g_logCommonErrors) {
×
559
          SLOG(g_log << Logger::Error << "Unable to parse proxy protocol header in packet from TCP client " << conn->d_remote.toStringWithPort() << endl,
×
560
               g_slogtcpin->info(Logr::Error, "Unable to parse proxy protocol header in packet from TCP client", "remote", Logging::Loggable(conn->d_remote)));
×
561
        }
×
562
        ++t_Counters.at(rec::Counter::proxyProtocolInvalidCount);
×
563
        return;
×
564
      }
×
UNCOV
565
      if (static_cast<size_t>(used) > g_proxyProtocolMaximumSize) {
×
UNCOV
566
        if (g_logCommonErrors) {
×
UNCOV
567
          SLOG(g_log << Logger::Error << "Proxy protocol header in packet from TCP client " << conn->d_remote.toStringWithPort() << " is larger than proxy-protocol-maximum-size (" << used << "), dropping" << endl,
×
UNCOV
568
               g_slogtcpin->info(Logr::Error, "Proxy protocol header in packet from TCP client is larger than proxy-protocol-maximum-size", "remote", Logging::Loggable(conn->d_remote), "size", Logging::Loggable(used)));
×
UNCOV
569
        }
×
UNCOV
570
        ++t_Counters.at(rec::Counter::proxyProtocolInvalidCount);
×
UNCOV
571
        return;
×
UNCOV
572
      }
×
573

574
      /* Now that we have retrieved the address of the client, as advertised by the proxy
575
         via the proxy protocol header, check that it is allowed by our ACL */
576
      /* note that if the proxy header used a 'LOCAL' command, the original source and destination are untouched so everything should be fine */
UNCOV
577
      conn->d_mappedSource = conn->d_source;
×
UNCOV
578
      if (t_proxyMapping) {
×
579
        if (const auto* iter = t_proxyMapping->lookup(conn->d_source)) {
×
580
          conn->d_mappedSource = iter->second.address;
×
581
          ++iter->second.stats.netmaskMatches;
×
582
        }
×
583
      }
×
UNCOV
584
      if (t_remotes) {
×
UNCOV
585
        t_remotes->push_back(conn->d_source);
×
UNCOV
586
      }
×
UNCOV
587
      if (t_allowFrom && !t_allowFrom->match(&conn->d_mappedSource)) {
×
UNCOV
588
        if (!g_quiet) {
×
UNCOV
589
          SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping TCP query from " << conn->d_mappedSource.toString() << ", address not matched by allow-from" << endl,
×
UNCOV
590
               g_slogtcpin->info(Logr::Error, "Dropping TCP query, address not matched by allow-from", "remote", Logging::Loggable(conn->d_remote)));
×
UNCOV
591
        }
×
592

UNCOV
593
        ++t_Counters.at(rec::Counter::unauthorizedTCP);
×
UNCOV
594
        return;
×
UNCOV
595
      }
×
596

UNCOV
597
      conn->data.resize(2);
×
UNCOV
598
      conn->state = TCPConnection::BYTE0;
×
UNCOV
599
    }
×
UNCOV
600
  }
×
601

UNCOV
602
  if (conn->state == TCPConnection::BYTE0) {
×
UNCOV
603
    ssize_t bytes = recv(conn->getFD(), conn->data.data(), 2, 0);
×
UNCOV
604
    if (bytes == 1) {
×
UNCOV
605
      conn->state = TCPConnection::BYTE1;
×
UNCOV
606
    }
×
UNCOV
607
    if (bytes == 2) {
×
UNCOV
608
      conn->qlen = (((unsigned char)conn->data[0]) << 8) + (unsigned char)conn->data[1];
×
UNCOV
609
      conn->data.resize(conn->qlen);
×
UNCOV
610
      conn->bytesread = 0;
×
UNCOV
611
      conn->state = TCPConnection::GETQUESTION;
×
UNCOV
612
    }
×
UNCOV
613
    if (bytes <= 0) {
×
UNCOV
614
      tcpGuard.handleTCPReadResult(fileDesc, bytes);
×
UNCOV
615
      return;
×
UNCOV
616
    }
×
UNCOV
617
  }
×
618

UNCOV
619
  if (conn->state == TCPConnection::BYTE1) {
×
UNCOV
620
    ssize_t bytes = recv(conn->getFD(), &conn->data[1], 1, 0);
×
UNCOV
621
    if (bytes == 1) {
×
UNCOV
622
      conn->state = TCPConnection::GETQUESTION;
×
UNCOV
623
      conn->qlen = (((unsigned char)conn->data[0]) << 8) + (unsigned char)conn->data[1];
×
UNCOV
624
      conn->data.resize(conn->qlen);
×
UNCOV
625
      conn->bytesread = 0;
×
UNCOV
626
    }
×
UNCOV
627
    if (bytes <= 0) {
×
UNCOV
628
      if (!tcpGuard.handleTCPReadResult(fileDesc, bytes)) {
×
629
        if (g_logCommonErrors) {
×
630
          SLOG(g_log << Logger::Error << "TCP client " << conn->d_remote.toStringWithPort() << " disconnected after first byte" << endl,
×
631
               g_slogtcpin->info(Logr::Error, "TCP client disconnected after first byte", "remote", Logging::Loggable(conn->d_remote)));
×
632
        }
×
633
      }
×
UNCOV
634
      return;
×
UNCOV
635
    }
×
UNCOV
636
  }
×
637

UNCOV
638
  if (conn->state == TCPConnection::GETQUESTION) {
×
UNCOV
639
    ssize_t bytes = recv(conn->getFD(), &conn->data[conn->bytesread], conn->qlen - conn->bytesread, 0);
×
UNCOV
640
    if (bytes <= 0) {
×
UNCOV
641
      if (!tcpGuard.handleTCPReadResult(fileDesc, bytes)) {
×
UNCOV
642
        if (g_logCommonErrors) {
×
UNCOV
643
          SLOG(g_log << Logger::Error << "TCP client " << conn->d_remote.toStringWithPort() << " disconnected while reading question body" << endl,
×
UNCOV
644
               g_slogtcpin->info(Logr::Error, "TCP client disconnected while reading question body", "remote", Logging::Loggable(conn->d_remote)));
×
UNCOV
645
        }
×
UNCOV
646
      }
×
UNCOV
647
      return;
×
UNCOV
648
    }
×
UNCOV
649
    if (bytes > std::numeric_limits<std::uint16_t>::max()) {
×
650
      if (g_logCommonErrors) {
×
651
        SLOG(g_log << Logger::Error << "TCP client " << conn->d_remote.toStringWithPort() << " sent an invalid question size while reading question body" << endl,
×
652
             g_slogtcpin->info(Logr::Error, "TCP client sent an invalid question size while reading question body", "remote", Logging::Loggable(conn->d_remote)));
×
653
      }
×
654
      return;
×
655
    }
×
UNCOV
656
    conn->bytesread += (uint16_t)bytes;
×
UNCOV
657
    if (conn->bytesread == conn->qlen) {
×
UNCOV
658
      conn->state = TCPConnection::BYTE0;
×
UNCOV
659
      std::unique_ptr<DNSComboWriter> comboWriter;
×
UNCOV
660
      try {
×
UNCOV
661
        comboWriter = std::make_unique<DNSComboWriter>(conn->data, g_now, t_pdl);
×
UNCOV
662
      }
×
UNCOV
663
      catch (const MOADNSException& mde) {
×
664
        t_Counters.at(rec::Counter::clientParseError)++;
×
665
        if (g_logCommonErrors) {
×
666
          SLOG(g_log << Logger::Error << "Unable to parse packet from TCP client " << conn->d_remote.toStringWithPort() << endl,
×
667
               g_slogtcpin->info(Logr::Error, "Unable to parse packet from TCP client", "remte", Logging::Loggable(conn->d_remote)));
×
668
        }
×
669
        return;
×
670
      }
×
671

UNCOV
672
      comboWriter->d_tcpConnection = conn; // carry the torch
×
UNCOV
673
      comboWriter->setSocket(conn->getFD()); // this is the only time a copy is made of the actual fd
×
UNCOV
674
      comboWriter->d_tcp = true;
×
UNCOV
675
      comboWriter->setRemote(conn->d_remote); // the address the query was received from
×
UNCOV
676
      comboWriter->setSource(conn->d_source); // the address we assume the query is coming from, might be set by proxy protocol
×
UNCOV
677
      ComboAddress dest;
×
UNCOV
678
      dest.reset();
×
UNCOV
679
      dest.sin4.sin_family = conn->d_remote.sin4.sin_family;
×
UNCOV
680
      socklen_t len = dest.getSocklen();
×
UNCOV
681
      getsockname(conn->getFD(), reinterpret_cast<sockaddr*>(&dest), &len); // if this fails, we're ok with it NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
×
UNCOV
682
      comboWriter->setLocal(dest); // the address we received the query on
×
UNCOV
683
      comboWriter->setDestination(conn->d_destination); // the address we assume the query is received on, might be set by proxy protocol
×
UNCOV
684
      comboWriter->setMappedSource(conn->d_mappedSource); // the address we assume the query is coming from after table based mapping
×
685
      /* we can't move this if we want to be able to access the values in
686
         all queries sent over this connection */
UNCOV
687
      comboWriter->d_proxyProtocolValues = conn->proxyProtocolValues;
×
688

UNCOV
689
      doProcessTCPQuestion(comboWriter, conn, tcpGuard, fileDesc);
×
UNCOV
690
    } // reading query
×
UNCOV
691
  }
×
692
  // more to come
UNCOV
693
  tcpGuard.keep();
×
UNCOV
694
}
×
695

696
//! Handle new incoming TCP connection
697
void handleNewTCPQuestion(int fileDesc, [[maybe_unused]] FDMultiplexer::funcparam_t& var)
UNCOV
698
{
×
UNCOV
699
  ComboAddress addr;
×
UNCOV
700
  socklen_t addrlen = sizeof(addr);
×
UNCOV
701
  int newsock = accept(fileDesc, reinterpret_cast<struct sockaddr*>(&addr), &addrlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
×
UNCOV
702
  if (newsock < 0) {
×
703
    return;
×
704
  }
×
UNCOV
705
  auto closeSock = [newsock](rec::Counter cnt, const string& msg) {
×
706
    try {
×
707
      closesocket(newsock);
×
708
      t_Counters.at(cnt)++;
×
709
      // We want this bump to percolate up without too much delay
710
      t_Counters.updateSnap(false);
×
711
    }
×
712
    catch (const PDNSException& e) {
×
713
      g_slogtcpin->error(Logr::Error, e.reason, msg, "exception", Logging::Loggable("PDNSException"));
×
714
    }
×
715
  };
×
716

UNCOV
717
  if (TCPConnection::getCurrentConnections() >= g_maxTCPClients) {
×
718
    closeSock(rec::Counter::tcpOverflow, "Error closing TCP socket after an overflow drop");
×
719
    return;
×
720
  }
×
UNCOV
721
  if (g_multiTasker->numProcesses() >= g_maxMThreads) {
×
722
    closeSock(rec::Counter::overCapacityDrops, "Error closing TCP socket after an over capacity drop");
×
723
    return;
×
724
  }
×
725

UNCOV
726
  ComboAddress destaddr;
×
UNCOV
727
  socklen_t len = sizeof(destaddr);
×
UNCOV
728
  getsockname(newsock, reinterpret_cast<sockaddr*>(&destaddr), &len); // if this fails, we're ok with it NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
×
UNCOV
729
  bool fromProxyProtocolSource = expectProxyProtocol(addr, destaddr);
×
UNCOV
730
  if (!fromProxyProtocolSource && t_remotes) {
×
UNCOV
731
    t_remotes->push_back(addr);
×
UNCOV
732
  }
×
UNCOV
733
  ComboAddress mappedSource = addr;
×
UNCOV
734
  if (!fromProxyProtocolSource && t_proxyMapping) {
×
UNCOV
735
    if (const auto* iter = t_proxyMapping->lookup(addr)) {
×
UNCOV
736
      mappedSource = iter->second.address;
×
UNCOV
737
      ++iter->second.stats.netmaskMatches;
×
UNCOV
738
    }
×
UNCOV
739
  }
×
UNCOV
740
  if (!fromProxyProtocolSource && t_allowFrom && !t_allowFrom->match(&mappedSource)) {
×
741
    if (!g_quiet) {
×
742
      SLOG(g_log << Logger::Error << "[" << g_multiTasker->getTid() << "] dropping TCP query from " << mappedSource.toString() << ", address neither matched by allow-from nor proxy-protocol-from" << endl,
×
743
           g_slogtcpin->info(Logr::Error, "dropping TCP query address neither matched by allow-from nor proxy-protocol-from", "source", Logging::Loggable(mappedSource)));
×
744
    }
×
745
    closeSock(rec::Counter::unauthorizedTCP, "Error closing TCP socket after an ACL drop");
×
746
    return;
×
747
  }
×
748

UNCOV
749
  if (g_maxTCPPerClient > 0 && t_tcpClientCounts->count(addr) > 0 && (*t_tcpClientCounts)[addr] >= g_maxTCPPerClient) {
×
750
    closeSock(rec::Counter::tcpClientOverflow, "Error closing TCP socket after a client overflow drop");
×
751
    return;
×
752
  }
×
753

UNCOV
754
  setNonBlocking(newsock);
×
UNCOV
755
  setTCPNoDelay(newsock);
×
UNCOV
756
  std::shared_ptr<TCPConnection> tcpConn = std::make_shared<TCPConnection>(newsock, addr);
×
UNCOV
757
  tcpConn->d_source = addr;
×
UNCOV
758
  tcpConn->d_destination = destaddr;
×
UNCOV
759
  tcpConn->d_mappedSource = mappedSource;
×
760

UNCOV
761
  if (fromProxyProtocolSource) {
×
UNCOV
762
    tcpConn->proxyProtocolNeed = s_proxyProtocolMinimumHeaderSize;
×
UNCOV
763
    tcpConn->data.resize(tcpConn->proxyProtocolNeed);
×
UNCOV
764
    tcpConn->state = TCPConnection::PROXYPROTOCOLHEADER;
×
UNCOV
765
  }
×
UNCOV
766
  else {
×
UNCOV
767
    tcpConn->state = TCPConnection::BYTE0;
×
UNCOV
768
  }
×
769

UNCOV
770
  timeval ttd{};
×
UNCOV
771
  Utility::gettimeofday(&ttd, nullptr);
×
UNCOV
772
  ttd.tv_sec += g_tcpTimeout;
×
773

UNCOV
774
  t_fdm->addReadFD(tcpConn->getFD(), handleRunningTCPQuestion, tcpConn, &ttd);
×
UNCOV
775
}
×
776

777
static void TCPIOHandlerIO(int fileDesc, FDMultiplexer::funcparam_t& var);
778

779
static void TCPIOHandlerStateChange(IOState oldstate, IOState newstate, std::shared_ptr<PacketID>& pid)
780
{
84✔
781
  TCPLOG(pid->tcpsock, "State transation " << int(oldstate) << "->" << int(newstate) << endl);
84✔
782

783
  pid->lowState = newstate;
84✔
784

785
  // handle state transitions
786
  switch (oldstate) {
84!
787
  case IOState::NeedRead:
21✔
788

789
    switch (newstate) {
21!
790
    case IOState::NeedWrite:
×
791
      TCPLOG(pid->tcpsock, "NeedRead -> NeedWrite: flip FD" << endl);
×
792
      t_fdm->alterFDToWrite(pid->tcpsock, TCPIOHandlerIO, pid);
×
793
      break;
×
UNCOV
794
    case IOState::NeedRead:
×
UNCOV
795
      break;
×
796
    case IOState::Done:
21!
797
      TCPLOG(pid->tcpsock, "Done -> removeReadFD" << endl);
21✔
798
      t_fdm->removeReadFD(pid->tcpsock);
21✔
799
      break;
21✔
800
    case IOState::Async:
×
801
      throw std::runtime_error("TLS async mode not supported");
×
802
      break;
×
803
    }
21✔
804
    break;
21✔
805

806
  case IOState::NeedWrite:
21✔
807

808
    switch (newstate) {
21!
UNCOV
809
    case IOState::NeedRead:
×
UNCOV
810
      TCPLOG(pid->tcpsock, "NeedWrite -> NeedRead: flip FD" << endl);
×
UNCOV
811
      t_fdm->alterFDToRead(pid->tcpsock, TCPIOHandlerIO, pid);
×
UNCOV
812
      break;
×
813
    case IOState::NeedWrite:
×
814
      break;
×
815
    case IOState::Done:
21!
816
      TCPLOG(pid->tcpsock, "Done -> removeWriteFD" << endl);
21✔
817
      t_fdm->removeWriteFD(pid->tcpsock);
21✔
818
      break;
21✔
819
    case IOState::Async:
×
820
      throw std::runtime_error("TLS async mode not supported");
×
821
      break;
×
822
    }
21✔
823
    break;
21✔
824

825
  case IOState::Done:
42✔
826
    switch (newstate) {
42!
827
    case IOState::NeedRead:
21✔
828
      TCPLOG(pid->tcpsock, "NeedRead: addReadFD" << endl);
21✔
829
      t_fdm->addReadFD(pid->tcpsock, TCPIOHandlerIO, pid);
21✔
830
      break;
21✔
831
    case IOState::NeedWrite:
21✔
832
      TCPLOG(pid->tcpsock, "NeedWrite: addWriteFD" << endl);
21✔
833
      t_fdm->addWriteFD(pid->tcpsock, TCPIOHandlerIO, pid);
21✔
834
      break;
21✔
835
    case IOState::Done:
×
836
      break;
×
837
    case IOState::Async:
×
838
      throw std::runtime_error("TLS async mode not supported");
×
839
      break;
×
840
    }
42✔
841
    break;
42✔
842

843
  case IOState::Async:
42!
844
    throw std::runtime_error("TLS async mode not supported");
×
845
    break;
×
846
  }
84✔
847
}
84✔
848

849
static void TCPIOHandlerIO(int fileDesc, FDMultiplexer::funcparam_t& var)
850
{
42✔
851
  auto pid = boost::any_cast<std::shared_ptr<PacketID>>(var);
42✔
852
  assert(pid->tcphandler); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay): def off assert triggers it
42✔
853
  assert(fileDesc == pid->tcphandler->getDescriptor()); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay) idem
×
854
  IOState newstate = IOState::Done;
×
855

856
  TCPLOG(pid->tcpsock, "TCPIOHandlerIO: lowState " << int(pid->lowState) << endl);
42✔
857

858
  // In the code below, we want to update the state of the fd before calling sendEvent
859
  // a sendEvent might close the fd, and some poll multiplexers do not like to manipulate a closed fd
860

861
  switch (pid->highState) {
42!
862
  case TCPAction::DoingRead:
21✔
863
    TCPLOG(pid->tcpsock, "highState: Reading" << endl);
21✔
864
    // In arecvtcp, the buffer was resized already so inWanted bytes will fit
865
    // try reading
866
    try {
21✔
867
      newstate = pid->tcphandler->tryRead(pid->inMSG, pid->inPos, pid->inWanted);
21✔
868
      switch (newstate) {
21!
869
      case IOState::Done:
21!
870
      case IOState::NeedRead:
21!
871
        TCPLOG(pid->tcpsock, "tryRead: Done or NeedRead " << int(newstate) << ' ' << pid->inPos << '/' << pid->inWanted << endl);
21✔
872
        TCPLOG(pid->tcpsock, "TCPIOHandlerIO " << pid->inWanted << ' ' << pid->inIncompleteOkay << endl);
21✔
873
        if (pid->inPos == pid->inWanted || (pid->inIncompleteOkay && pid->inPos > 0)) {
21!
874
          pid->inMSG.resize(pid->inPos); // old content (if there) + new bytes read, only relevant for the inIncompleteOkay case
21✔
875
          newstate = IOState::Done;
21✔
876
          TCPIOHandlerStateChange(pid->lowState, newstate, pid);
21✔
877
          g_multiTasker->sendEvent(pid, &pid->inMSG);
21✔
878
          return;
21✔
879
        }
21✔
UNCOV
880
        break;
×
UNCOV
881
      case IOState::NeedWrite:
×
882
        break;
×
883
      case IOState::Async:
×
884
        throw std::runtime_error("TLS async mode not supported");
×
885
        break;
×
886
      }
21✔
887
    }
21✔
888
    catch (const std::exception& e) {
21✔
889
      newstate = IOState::Done;
×
890
      TCPLOG(pid->tcpsock, "read exception..." << e.what() << endl);
×
891
      PacketBuffer empty;
×
892
      TCPIOHandlerStateChange(pid->lowState, newstate, pid);
×
893
      g_multiTasker->sendEvent(pid, &empty); // this conveys error status
×
894
      return;
×
895
    }
×
UNCOV
896
    break;
×
897

898
  case TCPAction::DoingWrite:
21✔
899
    TCPLOG(pid->tcpsock, "highState: Writing" << endl);
21✔
900
    try {
21✔
901
      TCPLOG(pid->tcpsock, "tryWrite: " << pid->outPos << '/' << pid->outMSG.size() << ' ' << " -> ");
21✔
902
      newstate = pid->tcphandler->tryWrite(pid->outMSG, pid->outPos, pid->outMSG.size());
21✔
903
      TCPLOG(pid->tcpsock, pid->outPos << '/' << pid->outMSG.size() << endl);
21✔
904
      switch (newstate) {
21!
905
      case IOState::Done: {
21!
906
        TCPLOG(pid->tcpsock, "tryWrite: Done" << endl);
21✔
907
        TCPIOHandlerStateChange(pid->lowState, newstate, pid);
21✔
908
        g_multiTasker->sendEvent(pid, &pid->outMSG); // send back what we sent to convey everything is ok
21✔
909
        return;
21✔
910
      }
×
UNCOV
911
      case IOState::NeedRead:
×
UNCOV
912
        TCPLOG(pid->tcpsock, "tryWrite: NeedRead" << endl);
×
UNCOV
913
        break;
×
914
      case IOState::NeedWrite:
×
915
        TCPLOG(pid->tcpsock, "tryWrite: NeedWrite" << endl);
×
916
        break;
×
917
      case IOState::Async:
×
918
        throw std::runtime_error("TLS async mode not supported");
×
919
        break;
×
920
      }
21✔
921
    }
21✔
922
    catch (const std::exception& e) {
21✔
923
      newstate = IOState::Done;
×
924
      TCPLOG(pid->tcpsock, "write exception..." << e.what() << endl);
×
925
      PacketBuffer sent;
×
926
      TCPIOHandlerStateChange(pid->lowState, newstate, pid);
×
927
      g_multiTasker->sendEvent(pid, &sent); // we convey error status by sending empty string
×
928
      return;
×
929
    }
×
UNCOV
930
    break;
×
931
  }
42✔
932

933
  // Cases that did not end up doing a sendEvent
UNCOV
934
  TCPIOHandlerStateChange(pid->lowState, newstate, pid);
×
UNCOV
935
}
×
936

937
void checkFastOpenSysctl([[maybe_unused]] bool active, [[maybe_unused]] Logr::log_t log)
938
{
×
939
#ifdef __linux__
×
940
  string line;
×
941
  if (readFileIfThere("/proc/sys/net/ipv4/tcp_fastopen", &line)) {
×
942
    int flag = std::stoi(line);
×
943
    if (active && !(flag & 1)) {
×
944
      SLOG(g_log << Logger::Error << "tcp-fast-open-connect enabled but net.ipv4.tcp_fastopen does not allow it" << endl,
×
945
           log->info(Logr::Error, "tcp-fast-open-connect enabled but net.ipv4.tcp_fastopen does not allow it"));
×
946
    }
×
947
    if (!active && !(flag & 2)) {
×
948
      SLOG(g_log << Logger::Error << "tcp-fast-open enabled but net.ipv4.tcp_fastopen does not allow it" << endl,
×
949
           log->info(Logr::Error, "tcp-fast-open enabled but net.ipv4.tcp_fastopen does not allow it"));
×
950
    }
×
951
  }
×
952
  else {
×
953
    SLOG(g_log << Logger::Notice << "Cannot determine if kernel settings allow fast-open" << endl,
×
954
         log->info(Logr::Notice, "Cannot determine if kernel settings allow fast-open"));
×
955
  }
×
956
#else
957
  SLOG(g_log << Logger::Notice << "Cannot determine if kernel settings allow fast-open" << endl,
958
       log->info(Logr::Notice, "Cannot determine if kernel settings allow fast-open"));
959
#endif
960
}
×
961

962
void checkTFOconnect(Logr::log_t log)
963
{
×
964
  try {
×
965
    Socket socket(AF_INET, SOCK_STREAM);
×
966
    socket.setNonBlocking();
×
967
    socket.setFastOpenConnect();
×
968
  }
×
969
  catch (const NetworkError& e) {
×
970
    SLOG(g_log << Logger::Error << "tcp-fast-open-connect enabled but returned error: " << e.what() << endl,
×
971
         log->error(Logr::Error, e.what(), "tcp-fast-open-connect enabled but returned error"));
×
972
  }
×
973
}
×
974

975
LWResult::Result asendtcp(const PacketBuffer& data, shared_ptr<TCPIOHandler>& handler)
976
{
21✔
977
  TCPLOG(handler->getDescriptor(), "asendtcp called " << data.size() << endl);
21✔
978

979
  auto pident = std::make_shared<PacketID>();
21✔
980
  pident->tcphandler = handler;
21✔
981
  pident->tcpsock = handler->getDescriptor();
21✔
982
  pident->outMSG = data;
21✔
983
  pident->highState = TCPAction::DoingWrite;
21✔
984

985
  IOState state = IOState::Done;
21✔
986
  try {
21✔
987
    TCPLOG(pident->tcpsock, "Initial tryWrite: " << pident->outPos << '/' << pident->outMSG.size() << ' ' << " -> ");
21✔
988
    state = handler->tryWrite(pident->outMSG, pident->outPos, pident->outMSG.size());
21✔
989
    TCPLOG(pident->tcpsock, pident->outPos << '/' << pident->outMSG.size() << endl);
21✔
990

991
    if (state == IOState::Done) {
21!
UNCOV
992
      TCPLOG(pident->tcpsock, "asendtcp success A" << endl);
×
UNCOV
993
      return LWResult::Result::Success;
×
UNCOV
994
    }
×
995
  }
21✔
996
  catch (const std::exception& e) {
21✔
997
    TCPLOG(pident->tcpsock, "tryWrite() exception..." << e.what() << endl);
×
998
    return LWResult::Result::PermanentError;
×
999
  }
×
1000

1001
  // Will set pident->lowState
1002
  TCPIOHandlerStateChange(IOState::Done, state, pident);
21✔
1003

1004
  PacketBuffer packet;
21✔
1005
  int ret = g_multiTasker->waitEvent(pident, &packet, g_networkTimeoutMsec);
21✔
1006
  TCPLOG(pident->tcpsock, "asendtcp waitEvent returned " << ret << ' ' << packet.size() << '/' << data.size() << ' ');
21✔
1007
  if (ret == 0) {
21!
1008
    TCPLOG(pident->tcpsock, "timeout" << endl);
×
1009
    TCPIOHandlerStateChange(pident->lowState, IOState::Done, pident);
×
1010
    return LWResult::Result::Timeout;
×
1011
  }
×
1012
  if (ret == -1) { // error
21!
1013
    TCPLOG(pident->tcpsock, "PermanentError" << endl);
×
1014
    TCPIOHandlerStateChange(pident->lowState, IOState::Done, pident);
×
1015
    return LWResult::Result::PermanentError;
×
1016
  }
×
1017
  if (packet.size() != data.size()) { // main loop tells us what it sent out, or empty in case of an error
21!
1018
    // fd housekeeping done by TCPIOHandlerIO
1019
    TCPLOG(pident->tcpsock, "PermanentError size mismatch" << endl);
×
1020
    return LWResult::Result::PermanentError;
×
1021
  }
×
1022

1023
  TCPLOG(pident->tcpsock, "asendtcp success" << endl);
21✔
1024
  return LWResult::Result::Success;
21✔
1025
}
21✔
1026

1027
LWResult::Result arecvtcp(PacketBuffer& data, const size_t len, shared_ptr<TCPIOHandler>& handler, const bool incompleteOkay)
1028
{
42✔
1029
  TCPLOG(handler->getDescriptor(), "arecvtcp called " << len << ' ' << data.size() << endl);
42✔
1030
  data.resize(len);
42✔
1031

1032
  // We might have data already available from the TLS layer, try to get that into the buffer
1033
  size_t pos = 0;
42✔
1034
  IOState state = IOState::Done;
42✔
1035
  try {
42✔
1036
    TCPLOG(handler->getDescriptor(), "calling tryRead() " << len << endl);
42✔
1037
    state = handler->tryRead(data, pos, len);
42✔
1038
    TCPLOG(handler->getDescriptor(), "arcvtcp tryRead() returned " << int(state) << ' ' << pos << '/' << len << endl);
42✔
1039
    switch (state) {
42!
1040
    case IOState::Done:
21✔
1041
    case IOState::NeedRead:
42✔
1042
      if (pos == len || (incompleteOkay && pos > 0)) {
42!
1043
        data.resize(pos);
21✔
1044
        TCPLOG(handler->getDescriptor(), "acecvtcp success A" << endl);
21✔
1045
        return LWResult::Result::Success;
21✔
1046
      }
21✔
1047
      break;
21✔
1048
    case IOState::NeedWrite:
21!
1049
      break;
×
1050
    case IOState::Async:
×
1051
      throw std::runtime_error("TLS async mode not supported");
×
1052
      break;
×
1053
    }
42✔
1054
  }
42✔
1055
  catch (const std::exception& e) {
42✔
1056
    TCPLOG(handler->getDescriptor(), "tryRead() exception..." << e.what() << endl);
×
1057
    return LWResult::Result::PermanentError;
×
1058
  }
×
1059

1060
  auto pident = std::make_shared<PacketID>();
21✔
1061
  pident->tcphandler = handler;
21✔
1062
  pident->tcpsock = handler->getDescriptor();
21✔
1063
  // We might have a partial result
1064
  pident->inMSG = std::move(data);
21✔
1065
  pident->inPos = pos;
21✔
1066
  pident->inWanted = len;
21✔
1067
  pident->inIncompleteOkay = incompleteOkay;
21✔
1068
  pident->highState = TCPAction::DoingRead;
21✔
1069

1070
  data.clear();
21✔
1071

1072
  // Will set pident->lowState
1073
  TCPIOHandlerStateChange(IOState::Done, state, pident);
21✔
1074

1075
  int ret = g_multiTasker->waitEvent(pident, &data, authWaitTimeMSec(g_multiTasker));
21✔
1076
  TCPLOG(pident->tcpsock, "arecvtcp " << ret << ' ' << data.size() << ' ');
21✔
1077
  if (ret == 0) {
21!
1078
    TCPLOG(pident->tcpsock, "timeout" << endl);
×
1079
    TCPIOHandlerStateChange(pident->lowState, IOState::Done, pident);
×
1080
    return LWResult::Result::Timeout;
×
1081
  }
×
1082
  if (ret == -1) {
21!
1083
    TCPLOG(pident->tcpsock, "PermanentError" << endl);
×
1084
    TCPIOHandlerStateChange(pident->lowState, IOState::Done, pident);
×
1085
    return LWResult::Result::PermanentError;
×
1086
  }
×
1087
  if (data.empty()) { // error, EOF or other
21!
1088
    // fd housekeeping done by TCPIOHandlerIO
1089
    TCPLOG(pident->tcpsock, "EOF" << endl);
×
1090
    return LWResult::Result::PermanentError;
×
1091
  }
×
1092

1093
  TCPLOG(pident->tcpsock, "arecvtcp success" << endl);
21✔
1094
  return LWResult::Result::Success;
21✔
1095
}
21✔
1096

1097
// The two last arguments to makeTCPServerSockets are used for logging purposes only
1098
unsigned int makeTCPServerSockets(deferredAdd_t& deferredAdds, std::set<int>& tcpSockets, Logr::log_t log, bool doLog, unsigned int instances)
1099
{
15✔
1100
  vector<string> localAddresses;
15✔
1101
  vector<string> logVec;
15✔
1102
  stringtok(localAddresses, ::arg()["local-address"], " ,");
15✔
1103

1104
  if (localAddresses.empty()) {
15!
1105
    throw PDNSException("No local address specified");
×
1106
  }
×
1107

1108
#ifdef TCP_DEFER_ACCEPT
15✔
1109
  auto first = true;
15✔
1110
#endif
15✔
1111
  const uint16_t defaultLocalPort = ::arg().asNum("local-port");
15✔
1112
  for (const auto& localAddress : localAddresses) {
15✔
1113
    ComboAddress address{localAddress, defaultLocalPort};
15✔
1114
    const int socketFd = socket(address.sin6.sin6_family, SOCK_STREAM, 0);
15✔
1115
    if (socketFd < 0) {
15!
1116
      throw PDNSException("Making a TCP server socket for resolver: " + stringerror());
×
1117
    }
×
1118
    logVec.emplace_back(address.toStringWithPort());
15✔
1119
    setCloseOnExec(socketFd);
15✔
1120

1121
    int tmp = 1;
15✔
1122
    if (setsockopt(socketFd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof tmp) < 0) {
15!
1123
      int err = errno;
×
1124
      SLOG(g_log << Logger::Error << "Setsockopt failed for TCP listening socket" << endl,
×
1125
           log->error(Logr::Critical, err, "Setsockopt failed for TCP listening socket"));
×
1126
      _exit(1);
×
1127
    }
×
1128
    if (address.sin6.sin6_family == AF_INET6 && setsockopt(socketFd, IPPROTO_IPV6, IPV6_V6ONLY, &tmp, sizeof(tmp)) < 0) {
15!
1129
      int err = errno;
×
1130
      SLOG(g_log << Logger::Error << "Failed to set IPv6 socket to IPv6 only, continuing anyhow: " << stringerror(err) << endl,
×
1131
           log->error(Logr::Warning, err, "Failed to set IPv6 socket to IPv6 only, continuing anyhow"));
×
1132
    }
×
1133

1134
#ifdef TCP_DEFER_ACCEPT
15✔
1135
    if (setsockopt(socketFd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &tmp, sizeof tmp) >= 0) {
15!
1136
      if (first) {
15!
1137
        SLOG(g_log << Logger::Info << "Enabled TCP data-ready filter for (slight) DoS protection" << endl,
15✔
1138
             log->info(Logr::Info, "Enabled TCP data-ready filter for (slight) DoS protection"));
15✔
1139
      }
15✔
1140
    }
15✔
1141
#endif
15✔
1142

1143
    if (::arg().mustDo("non-local-bind")) {
15!
1144
      Utility::setBindAny(AF_INET, socketFd);
×
1145
    }
×
1146

1147
    if (g_reusePort) {
15!
1148
#if defined(SO_REUSEPORT_LB)
1149
      try {
1150
        SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
1151
      }
1152
      catch (const std::exception& e) {
1153
        throw PDNSException(std::string("SO_REUSEPORT_LB: ") + e.what());
1154
      }
1155
#elif defined(SO_REUSEPORT)
UNCOV
1156
      try {
×
UNCOV
1157
        SSetsockopt(socketFd, SOL_SOCKET, SO_REUSEPORT, 1);
×
UNCOV
1158
      }
×
UNCOV
1159
      catch (const std::exception& e) {
×
1160
        throw PDNSException(std::string("SO_REUSEPORT: ") + e.what());
×
1161
      }
×
UNCOV
1162
#endif
×
UNCOV
1163
    }
×
1164

1165
    if (SyncRes::s_tcp_fast_open > 0) {
15!
1166
      checkFastOpenSysctl(false, log);
×
1167
#ifdef TCP_FASTOPEN
×
1168
      if (setsockopt(socketFd, IPPROTO_TCP, TCP_FASTOPEN, &SyncRes::s_tcp_fast_open, sizeof SyncRes::s_tcp_fast_open) < 0) {
×
1169
        int err = errno;
×
1170
        SLOG(g_log << Logger::Error << "Failed to enable TCP Fast Open for listening socket: " << stringerror(err) << endl,
×
1171
             log->error(Logr::Error, err, "Failed to enable TCP Fast Open for listening socket"));
×
1172
      }
×
1173
#else
1174
      SLOG(g_log << Logger::Warning << "TCP Fast Open configured but not supported for listening socket" << endl,
1175
           log->info(Logr::Warning, "TCP Fast Open configured but not supported for listening socket"));
1176
#endif
1177
    }
×
1178

1179
    socklen_t socklen = address.sin4.sin_family == AF_INET ? sizeof(address.sin4) : sizeof(address.sin6);
15!
1180
    if (::bind(socketFd, reinterpret_cast<struct sockaddr*>(&address), socklen) < 0) { // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
15!
1181
      throw PDNSException("Binding TCP server socket for " + address.toStringWithPort() + ": " + stringerror());
×
1182
    }
×
1183

1184
    setNonBlocking(socketFd);
15✔
1185
    try {
15✔
1186
      setSocketSendBuffer(socketFd, 65000);
15✔
1187
    }
15✔
1188
    catch (const std::exception& e) {
15✔
1189
      SLOG(g_log << Logger::Error << e.what() << endl,
×
1190
           log->error(Logr::Error, e.what(), "Exception while setting socket send buffer"));
×
1191
    }
×
1192

1193
    listen(socketFd, 128);
15✔
1194
    deferredAdds.emplace_back(socketFd, handleNewTCPQuestion);
15✔
1195
    tcpSockets.insert(socketFd);
15✔
1196

1197
    // we don't need to update g_listenSocketsAddresses since it doesn't work for TCP/IP:
1198
    //  - fd is not that which we know here, but returned from accept()
1199

1200
#ifdef TCP_DEFER_ACCEPT
15✔
1201
    first = false;
15✔
1202
#endif
15✔
1203
  }
15✔
1204
  if (doLog) {
15!
1205
    log->info(Logr::Info, "Listening for queries", "protocol", Logging::Loggable("TCP"), "addresses", Logging::IterLoggable(logVec.cbegin(), logVec.cend()), "socketInstances", Logging::Loggable(instances), "reuseport", Logging::Loggable(g_reusePort));
15✔
1206
  }
15✔
1207
  return localAddresses.size();
15✔
1208
}
15✔
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