Coverage Report

Created: 2026-10-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pdns/pdns/dnsdistdist/dnsdist-udp.cc
Line
Count
Source
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 "dnsdist.hh"
24
#include "dnsdist-udp.hh"
25
#include "dnsdist-dnscrypt.hh"
26
#include "dnsdist-metrics.hh"
27
#include "dnsparser.hh"
28
#include "dnsdist-dnsparser.hh"
29
#include "dnsdist-ecs.hh"
30
#include "dnsdist-proxy-protocol.hh"
31
#include "threadname.hh"
32
#include "dolog.hh"
33
#include "dnsdist-configuration.hh"
34
#include "xsk.hh"
35
36
namespace dnsdist::udp
37
{
38
static std::string contextToStr(Context context)
39
0
{
40
0
  if (context == Context::Frontend) {
41
0
    return "frontend";
42
0
  }
43
0
  if (context == Context::Backend) {
44
0
    return "backend";
45
0
  }
46
47
0
  return "";
48
0
}
49
50
void setUDPSocketBufferSizes(int socketDesc, const Logr::Logger& logger, Context context, const ComboAddress& addr)
51
0
{
52
0
  const auto& immutableConfig = dnsdist::configuration::getImmutableConfiguration();
53
0
  if (immutableConfig.d_socketUDPSendBuffer > 0) {
54
0
    try {
55
0
      setSocketSendBuffer(socketDesc, immutableConfig.d_socketUDPSendBuffer);
56
0
    }
57
0
    catch (const std::exception& e) {
58
0
      if (context == Context::Frontend) {
59
0
        SLOG(warnlog(e.what()),
60
0
             logger.error(Logr::Warning, e.what(), "Failed to raise send buffer size on UDP socket", "frontend.address", Logging::Loggable(addr)));
61
0
      }
62
0
    }
63
0
  }
64
0
  else {
65
0
    try {
66
0
      auto result = raiseSocketSendBufferToMax(socketDesc);
67
0
      if (result > 0 && context == Context::Frontend) {
68
0
        SLOG(infolog("Raised send buffer to %u for %s address '%s'", result, contextToStr(context), addr.toStringWithPort()),
69
0
             logger.info(Logr::Info, "Raised send buffer size", "frontend.address", Logging::Loggable(addr), "network.send_buffer_size", Logging::Loggable(result)));
70
0
      }
71
0
    }
72
0
    catch (const std::exception& e) {
73
0
      if (context == Context::Frontend) {
74
0
        SLOG(warnlog(e.what()),
75
0
             logger.error(Logr::Warning, e.what(), "Failed to raise send buffer size on UDP socket", "frontend.address", Logging::Loggable(addr)));
76
0
      }
77
0
    }
78
0
  }
79
80
0
  if (immutableConfig.d_socketUDPRecvBuffer > 0) {
81
0
    try {
82
0
      setSocketReceiveBuffer(socketDesc, immutableConfig.d_socketUDPRecvBuffer);
83
0
    }
84
0
    catch (const std::exception& e) {
85
0
      if (context == Context::Frontend) {
86
0
        SLOG(warnlog(e.what()),
87
0
             logger.error(Logr::Warning, e.what(), "Failed to raise receive buffer size on UDP socket", "frontend.address", Logging::Loggable(addr)));
88
0
      }
89
0
    }
90
0
  }
91
0
  else {
92
0
    try {
93
0
      auto result = raiseSocketReceiveBufferToMax(socketDesc);
94
0
      if (result > 0 && context == Context::Frontend) {
95
0
        SLOG(infolog("Raised receive buffer to %u for address '%s'", result, addr.toStringWithPort()),
96
0
             logger.info(Logr::Info, "Raised receive buffer size", "frontend.address", Logging::Loggable(addr), "buffer_size", Logging::Loggable(result)));
97
0
      }
98
0
    }
99
0
    catch (const std::exception& e) {
100
0
      if (context == Context::Frontend) {
101
0
        SLOG(warnlog(e.what()),
102
0
             logger.error(Logr::Warning, e.what(), "Failed to raise receive buffer size on UDP socket", "frontend.address", Logging::Loggable(addr)));
103
0
      }
104
0
    }
105
0
  }
106
0
}
107
108
void sendfromto(int sock, const PacketBuffer& buffer, const ComboAddress& from, const ComboAddress& dest)
109
0
{
110
0
  const int flags = 0;
111
0
  if (from.sin4.sin_family == 0) {
112
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
113
0
    auto ret = sendto(sock, buffer.data(), buffer.size(), flags, reinterpret_cast<const struct sockaddr*>(&dest), dest.getSocklen());
114
0
    if (ret == -1) {
115
0
      int error = errno;
116
0
      VERBOSESLOG(infolog("Error sending UDP response to %s: %s", dest.toStringWithPort(), stringerror(error)),
117
0
                  dnsdist::logging::getTopLogger("sendfromto")->error(Logr::Info, error, "Error sending UDP response", "client.address", Logging::Loggable(dest)));
118
0
    }
119
0
    return;
120
0
  }
121
122
0
  auto ret = sendMsgWithOptions(sock, buffer.data(), buffer.size(), &dest, &from, 0, 0);
123
0
  if (!ret.has_value()) {
124
0
    VERBOSESLOG(infolog("Error sending UDP response from %s to %s: %s", from.toStringWithPort(), dest.toStringWithPort(), stringerror(ret.error())),
125
0
                dnsdist::logging::getTopLogger("sendfromto")->error(Logr::Info, ret.error(), "Error sending UDP response", "source.address", Logging::Loggable(from), "client.address", Logging::Loggable(dest)));
126
0
  }
127
0
}
128
129
void truncateTC(PacketBuffer& packet, size_t maximumSize, unsigned int qnameWireLength, bool addEDNSToSelfGeneratedResponses)
130
0
{
131
0
  try {
132
0
    bool hadEDNS = false;
133
0
    uint16_t payloadSize = 0;
134
0
    uint16_t zValue = 0;
135
136
0
    if (addEDNSToSelfGeneratedResponses) {
137
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
138
0
      hadEDNS = getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(packet.data()), packet.size(), &payloadSize, &zValue);
139
0
    }
140
141
0
    packet.resize(static_cast<uint16_t>(sizeof(dnsheader) + qnameWireLength + DNS_TYPE_SIZE + DNS_CLASS_SIZE));
142
0
    dnsdist::PacketMangling::editDNSHeaderFromPacket(packet, [](dnsheader& header) {
143
0
      header.ancount = 0;
144
0
      header.arcount = 0;
145
0
      header.nscount = 0;
146
0
      return true;
147
0
    });
148
149
0
    if (hadEDNS) {
150
0
      addEDNS(packet, maximumSize, (zValue & EDNS_HEADER_FLAG_DO) != 0, payloadSize, 0);
151
0
    }
152
0
  }
153
0
  catch (...) {
154
0
    ++dnsdist::metrics::g_stats.truncFail;
155
0
  }
156
0
}
157
158
void handleResponseTC4UDPClient(DNSQuestion& dnsQuestion, uint16_t udpPayloadSize, PacketBuffer& response)
159
0
{
160
0
  if (udpPayloadSize != 0 && response.size() > udpPayloadSize) {
161
0
    VERBOSESLOG(infolog("Got a response of size %d while the initial UDP payload size was %d, truncating", response.size(), udpPayloadSize),
162
0
                dnsQuestion.getLogger()->withName("udp-response")->info(Logr::Info, "Got a UDP response larger than the initial UDP payload size, truncating", "dns.response.size", Logging::Loggable(response.size()), "dns.query.udp_payload_size", Logging::Loggable(udpPayloadSize)));
163
164
0
    truncateTC(dnsQuestion.getMutableData(), dnsQuestion.getMaximumSize(), dnsQuestion.ids.qname.wirelength(), dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses);
165
0
    dnsdist::PacketMangling::editDNSHeaderFromPacket(dnsQuestion.getMutableData(), [](dnsheader& header) {
166
0
      header.tc = true;
167
0
      return true;
168
0
    });
169
0
  }
170
0
  else if (dnsdist::configuration::getCurrentRuntimeConfiguration().d_truncateTC && dnsQuestion.getHeader()->tc) {
171
0
    truncateTC(response, dnsQuestion.getMaximumSize(), dnsQuestion.ids.qname.wirelength(), dnsdist::configuration::getCurrentRuntimeConfiguration().d_addEDNSToSelfGeneratedResponses);
172
0
  }
173
0
}
174
175
void handleResponseForUDPClient(InternalQueryState& ids, PacketBuffer& response, const std::shared_ptr<DownstreamState>& backend, bool isAsync, bool selfGenerated)
176
0
{
177
0
  DNSResponse dnsResponse(ids, response, backend);
178
179
0
  handleResponseTC4UDPClient(dnsResponse, ids.udpPayloadSize, response);
180
181
  /* when the answer is encrypted in place, we need to get a copy
182
     of the original header before encryption to fill the ring buffer */
183
0
  dnsheader cleartextDH{};
184
0
  memcpy(&cleartextDH, dnsResponse.getHeader().get(), sizeof(cleartextDH));
185
186
0
  if (!isAsync) {
187
0
    if (!processResponse(response, dnsResponse)) {
188
0
      return;
189
0
    }
190
191
0
    if (dnsResponse.isAsynchronous()) {
192
0
      return;
193
0
    }
194
0
  }
195
196
0
  ++dnsdist::metrics::g_stats.responses;
197
0
  if (ids.cs != nullptr) {
198
0
    ++ids.cs->responses;
199
0
  }
200
201
0
  if (!ids.isXSK()) {
202
0
    sendUDPResponse(ids.cs->udpFD, response, dnsResponse.ids.delayMsec, ids.hopLocal, ids.hopRemote);
203
0
  }
204
205
0
  if (!selfGenerated) {
206
0
    auto latencyUs = ids.queryRealTime.udiff();
207
0
    if (!ids.isXSK()) {
208
0
      VERBOSESLOG(infolog("Got answer from %s, relayed to %s (UDP), took %d us", backend->d_config.remote.toStringWithPort(), ids.origRemote.toStringWithPort(), latencyUs),
209
0
                  dnsResponse.getLogger()->withName("udp-response")->info(Logr::Info, "Got answer from backend, relayed to client"));
210
0
    }
211
0
    else {
212
0
      VERBOSESLOG(infolog("Got answer from %s, relayed to %s (UDP via XSK), took %d us", backend->d_config.remote.toStringWithPort(), ids.origRemote.toStringWithPort(), latencyUs),
213
0
                  dnsResponse.getLogger()->withName("udp-xsk-response")->info(Logr::Info, "Got answer from backend, relayed to client"));
214
0
    }
215
0
    handleResponseSent(ids, latencyUs, dnsResponse.ids.origRemote, backend->d_config.remote, response.size(), cleartextDH, backend->getProtocol(), true);
216
0
  }
217
0
  else {
218
0
    handleResponseSent(ids, 0., dnsResponse.ids.origRemote, ComboAddress(), response.size(), cleartextDH, dnsdist::Protocol::DoUDP, false);
219
0
  }
220
0
}
221
222
#if !defined(DISABLE_RECVMMSG) && defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
223
void queueResponse(const PacketBuffer& response, const ComboAddress& dest, const ComboAddress& remote, struct mmsghdr& outMsg, struct iovec* iov, cmsgbuf_aligned* cbuf)
224
0
{
225
0
  outMsg.msg_len = 0;
226
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast,cppcoreguidelines-pro-type-reinterpret-cast): API
227
0
  fillMSGHdr(&outMsg.msg_hdr, iov, nullptr, 0, const_cast<char*>(reinterpret_cast<const char*>(&response.at(0))), response.size(), const_cast<ComboAddress*>(&remote));
228
229
0
  if (dest.sin4.sin_family == 0) {
230
0
    outMsg.msg_hdr.msg_control = nullptr;
231
0
  }
232
0
  else {
233
0
    addCMsgSrcAddr(&outMsg.msg_hdr, cbuf, &dest, 0);
234
0
  }
235
0
}
236
#endif
237
238
bool isUDPQueryAcceptable(ClientState& clientState, const struct msghdr* msgh, const ComboAddress& remote, ComboAddress& dest, bool& expectProxyProtocol)
239
0
{
240
0
  if ((msgh->msg_flags & MSG_TRUNC) != 0) {
241
    /* message was too large for our buffer */
242
0
    VERBOSESLOG(infolog("Dropping message too large for our buffer"),
243
0
                dnsdist::logging::getTopLogger("udp-query")->info(Logr::Info, "Dropping query from client that is too large for our buffer", "client.address", Logging::Loggable(remote), "destination.address", Logging::Loggable(dest), "frontend.address", Logging::Loggable(clientState.local)));
244
0
    ++dnsdist::metrics::g_stats.nonCompliantQueries;
245
0
    ++clientState.nonCompliantQueries;
246
0
    return false;
247
0
  }
248
249
0
  expectProxyProtocol = clientState.d_enableProxyProtocol && expectProxyProtocolFrom(remote);
250
0
  if (!dnsdist::configuration::getCurrentRuntimeConfiguration().d_ACL.match(remote) && !expectProxyProtocol) {
251
0
    VERBOSESLOG(infolog("Query from %s dropped because of ACL", remote.toStringWithPort()),
252
0
                dnsdist::logging::getTopLogger("udp-query")->info(Logr::Info, "Query dropped because of ACL", "source.address", Logging::Loggable(dest)));
253
0
    ++dnsdist::metrics::g_stats.aclDrops;
254
0
    return false;
255
0
  }
256
257
0
  if (HarvestDestinationAddress(msgh, &dest)) {
258
    /* so it turns out that sometimes the kernel lies to us:
259
       the address is set to 0.0.0.0:0 which makes our sendfromto() use
260
       the wrong address. In that case it's better to let the kernel
261
       do the work by itself and use sendto() instead.
262
       This is indicated by setting the family to 0 which is acted upon
263
       in sendUDPResponse() and DelayedPacket::().
264
    */
265
0
    static const ComboAddress bogusV4("0.0.0.0:0");
266
0
    static const ComboAddress bogusV6("[::]:0");
267
0
    if ((dest.sin4.sin_family == AF_INET && dest == bogusV4) || (dest.sin4.sin_family == AF_INET6 && dest == bogusV6)) {
268
0
      dest.sin4.sin_family = 0;
269
0
    }
270
0
    else {
271
      /* we don't get the port, only the address */
272
0
      dest.sin4.sin_port = clientState.local.sin4.sin_port;
273
0
    }
274
0
  }
275
0
  else {
276
0
    dest.sin4.sin_family = 0;
277
0
  }
278
279
0
  ++clientState.queries;
280
0
  ++dnsdist::metrics::g_stats.queries;
281
282
0
  return true;
283
0
}
284
285
void processUDPQuery(ClientState& clientState, const struct msghdr* msgh, const ComboAddress& remote, ComboAddress& dest, PacketBuffer& query, std::vector<mmsghdr>* responsesVect, unsigned int* queuedResponses, struct iovec* respIOV, cmsgbuf_aligned* respCBuf)
286
0
{
287
0
  assert(responsesVect == nullptr || (queuedResponses != nullptr && respIOV != nullptr && respCBuf != nullptr));
288
0
  uint16_t queryId = 0;
289
0
  InternalQueryState ids;
290
291
0
  auto closer = ids.getCloser(__func__, SpanID{}); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
292
293
0
  ids.cs = &clientState;
294
0
  ids.origRemote = remote;
295
0
  ids.hopRemote = remote;
296
0
  ids.protocol = dnsdist::Protocol::DoUDP;
297
298
0
  try {
299
0
    bool expectProxyProtocol = false;
300
0
    if (!isUDPQueryAcceptable(clientState, msgh, remote, dest, expectProxyProtocol)) {
301
0
      return;
302
0
    }
303
    /* dest might have been updated, if we managed to harvest the destination address */
304
0
    if (dest.sin4.sin_family != 0) {
305
0
      ids.origDest = dest;
306
0
      ids.hopLocal = dest;
307
0
    }
308
0
    else {
309
      /* if we have not been able to harvest the destination address,
310
         we do NOT want to update dest or hopLocal, to let the kernel
311
         pick the less terrible option, but we want to update origDest
312
         which is used by rules and actions to at least the correct
313
         address family */
314
0
      ids.origDest = clientState.local;
315
0
      ids.hopLocal.sin4.sin_family = 0;
316
0
    }
317
318
0
    std::vector<ProxyProtocolValue> proxyProtocolValues;
319
0
    if (expectProxyProtocol && !handleProxyProtocol(remote, false, dnsdist::configuration::getCurrentRuntimeConfiguration().d_ACL, query, ids.origRemote, ids.origDest, proxyProtocolValues)) {
320
0
      return;
321
0
    }
322
323
0
    ids.queryRealTime.start();
324
325
0
    auto dnsCryptResponse = dnsdist::dnscrypt::checkDNSCryptQuery(clientState, query, ids.dnsCryptQuery, ids.queryRealTime.d_start.tv_sec, false);
326
0
    if (dnsCryptResponse) {
327
0
      sendUDPResponse(clientState.udpFD, query, 0, dest, remote);
328
0
      return;
329
0
    }
330
331
0
    {
332
      /* this pointer will be invalidated the second the buffer is resized, don't hold onto it! */
333
0
      const dnsheader_aligned dnsHeader(query.data());
334
0
      queryId = ntohs(dnsHeader->id);
335
336
0
      if (!checkQueryHeaders(*dnsHeader, clientState)) {
337
0
        return;
338
0
      }
339
340
0
      if (dnsHeader->qdcount == 0) {
341
0
        dnsdist::PacketMangling::editDNSHeaderFromPacket(query, [](dnsheader& header) {
342
0
          header.rcode = RCode::NotImp;
343
0
          header.qr = true;
344
0
          return true;
345
0
        });
346
347
0
        sendUDPResponse(clientState.udpFD, query, 0, dest, remote);
348
0
        return;
349
0
      }
350
0
    }
351
352
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
353
0
    ids.qname = DNSName(reinterpret_cast<const char*>(query.data()), query.size(), sizeof(dnsheader), false, &ids.qtype, &ids.qclass);
354
0
    if (ids.dnsCryptQuery) {
355
0
      ids.protocol = dnsdist::Protocol::DNSCryptUDP;
356
0
    }
357
0
    DNSQuestion dnsQuestion(ids, query);
358
0
    const uint16_t* flags = getFlagsFromDNSHeader(dnsQuestion.getHeader().get());
359
0
    ids.origFlags = *flags;
360
361
0
    if (!proxyProtocolValues.empty()) {
362
0
      dnsQuestion.proxyProtocolValues = make_unique<std::vector<ProxyProtocolValue>>(std::move(proxyProtocolValues));
363
0
    }
364
365
    // save UDP payload size from origin query
366
0
    uint16_t udpPayloadSize = 0;
367
0
    uint16_t zValue = 0;
368
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
369
0
    getEDNSUDPPayloadSizeAndZ(reinterpret_cast<const char*>(query.data()), query.size(), &udpPayloadSize, &zValue);
370
0
    if (!ids.dnssecOK) {
371
0
      ids.dnssecOK = (zValue & EDNS_HEADER_FLAG_DO) != 0;
372
0
    }
373
0
    if (udpPayloadSize < 512) {
374
0
      udpPayloadSize = 512;
375
0
    }
376
377
0
    std::shared_ptr<DownstreamState> backend{nullptr};
378
0
    auto result = processQuery(dnsQuestion, backend);
379
380
0
    if (result == ProcessQueryResult::Drop || result == ProcessQueryResult::Asynchronous) {
381
0
      return;
382
0
    }
383
384
    // the buffer might have been invalidated by now (resized)
385
0
    const auto dnsHeader = dnsQuestion.getHeader();
386
0
    if (result == ProcessQueryResult::SendAnswer) {
387
      /* ensure payload size is not exceeded */
388
0
      handleResponseTC4UDPClient(dnsQuestion, udpPayloadSize, query);
389
0
#ifndef DISABLE_RECVMMSG
390
0
#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
391
0
      if (dnsQuestion.ids.delayMsec == 0 && responsesVect != nullptr) {
392
0
        queueResponse(query, dest, remote, (*responsesVect)[*queuedResponses], respIOV, respCBuf);
393
0
        (*queuedResponses)++;
394
0
        handleResponseSent(std::move(dnsQuestion.ids.qname), dnsQuestion.ids.qtype, 0., remote, ComboAddress(), query.size(), *dnsHeader, dnsdist::Protocol::DoUDP, dnsdist::Protocol::DoUDP, false);
395
0
        return;
396
0
      }
397
0
#endif /* defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE) */
398
0
#endif /* DISABLE_RECVMMSG */
399
      /* we use dest, always, because we don't want to use the listening address to send a response since it could be 0.0.0.0 */
400
0
      sendUDPResponse(clientState.udpFD, query, dnsQuestion.ids.delayMsec, dest, remote);
401
0
      handleResponseSent(std::move(dnsQuestion.ids.qname), dnsQuestion.ids.qtype, 0., remote, ComboAddress(), query.size(), *dnsHeader, dnsdist::Protocol::DoUDP, dnsdist::Protocol::DoUDP, false);
402
0
      return;
403
0
    }
404
405
0
    if (result != ProcessQueryResult::PassToBackend || backend == nullptr) {
406
0
      return;
407
0
    }
408
409
0
    if (backend->isTCPOnly()) {
410
0
      std::string proxyProtocolPayload;
411
      /* we need to do this _before_ creating the cross protocol query because
412
         after that the buffer will have been moved */
413
0
      if (backend->d_config.useProxyProtocol) {
414
0
        proxyProtocolPayload = getProxyProtocolPayload(dnsQuestion);
415
0
      }
416
417
0
      ids.origID = dnsHeader->id;
418
0
      auto cpq = std::make_unique<UDPCrossProtocolQuery>(std::move(query), std::move(ids), backend);
419
0
      cpq->query.d_proxyProtocolPayload = std::move(proxyProtocolPayload);
420
421
0
      backend->passCrossProtocolQuery(std::move(cpq));
422
0
      return;
423
0
    }
424
425
0
    assignOutgoingUDPQueryToBackend(backend, dnsHeader->id, dnsQuestion, query);
426
0
  }
427
0
  catch (const std::exception& e) {
428
0
    VERBOSESLOG(infolog("Got an error in UDP question thread while parsing a query from %s, id %d: %s", ids.origRemote.toStringWithPort(), queryId, e.what()),
429
0
                dnsdist::logging::getTopLogger("udp-frontend")->error(Logr::Info, e.what(), "Got an error in UDP question thread while parsing a query", "source.address", Logging::Loggable(ids.origRemote), "dns.question.id", Logging::Loggable(queryId)));
430
0
  }
431
0
}
432
433
size_t getInitialUDPPacketBufferSize(bool expectProxyProtocol)
434
0
{
435
0
  static_assert(dnsdist::configuration::s_udpIncomingBufferSize <= dnsdist::udp::s_initialUDPPacketBufferSize, "The incoming buffer size should not be larger than s_initialUDPPacketBufferSize");
436
437
0
  const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
438
0
  if (!expectProxyProtocol || runtimeConfig.d_proxyProtocolACL.empty()) {
439
0
    return s_initialUDPPacketBufferSize;
440
0
  }
441
442
0
  return s_initialUDPPacketBufferSize + runtimeConfig.d_proxyProtocolMaximumSize;
443
0
}
444
445
size_t getMaximumIncomingPacketSize(const ClientState& clientState)
446
0
{
447
0
  if (clientState.dnscryptCtx) {
448
0
    return getInitialUDPPacketBufferSize(clientState.d_enableProxyProtocol);
449
0
  }
450
451
0
  const auto& runtimeConfig = dnsdist::configuration::getCurrentRuntimeConfiguration();
452
0
  if (!clientState.d_enableProxyProtocol || runtimeConfig.d_proxyProtocolACL.empty()) {
453
0
    return dnsdist::configuration::s_udpIncomingBufferSize;
454
0
  }
455
456
0
  return dnsdist::configuration::s_udpIncomingBufferSize + runtimeConfig.d_proxyProtocolMaximumSize;
457
0
}
458
459
static void processUDPResponseFromBackend(const Logr::Logger& logger, std::shared_ptr<DownstreamState>& backend, PacketBuffer& response, ssize_t got, size_t initialBufferSize, int sockDesc)
460
0
{
461
0
  uint16_t queryId = 0;
462
0
  try {
463
0
    if (got < 0 || static_cast<size_t>(got) < sizeof(dnsheader) || static_cast<size_t>(got) >= (initialBufferSize + 1)) {
464
0
      return;
465
0
    }
466
467
0
    response.resize(static_cast<size_t>(got));
468
0
    const dnsheader_aligned dnsHeader(response.data());
469
0
    queryId = dnsHeader->id;
470
471
0
    auto ids = backend->getState(queryId);
472
0
    if (!ids) {
473
0
      return;
474
0
    }
475
476
0
    if (!ids->isXSK() && sockDesc != ids->backendFD) {
477
0
      backend->restoreState(queryId, std::move(*ids));
478
0
      return;
479
0
    }
480
481
0
    dnsdist::configuration::refreshLocalRuntimeConfiguration();
482
0
    if (processResponderPacket(backend, response, std::move(*ids)) && ids->isXSK() && ids->cs->xskInfoResponder) {
483
#ifdef HAVE_XSK
484
      auto& xskInfo = ids->cs->xskInfoResponder;
485
      auto xskPacket = xskInfo->getEmptyFrame();
486
      if (!xskPacket) {
487
        return;
488
      }
489
      xskPacket->setHeader(ids->xskPacketHeader);
490
      if (!xskPacket->setPayload(response)) {
491
      }
492
      if (ids->delayMsec > 0) {
493
        xskPacket->addDelay(ids->delayMsec);
494
      }
495
      xskPacket->updatePacket();
496
      xskInfo->pushToSendQueue(*xskPacket);
497
      xskInfo->notifyXskSocket();
498
#endif /* HAVE_XSK */
499
0
    }
500
0
  }
501
0
  catch (const std::exception& e) {
502
0
    VERBOSESLOG(infolog("Got an error in UDP responder thread while parsing a response from %s, id %d: %s", backend->d_config.remote.toStringWithPort(), queryId, e.what()),
503
0
                logger.error(Logr::Info, e.what(), "Got an error in UDP responder thread while parsing a response", "dns.response.id", Logging::Loggable(queryId)));
504
0
  }
505
0
}
506
507
#ifndef DISABLE_RECVMMSG
508
#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
509
static void MultipleMessagesUDPClientThread(ClientState* clientState)
510
0
{
511
0
  struct MMReceiver
512
0
  {
513
0
    PacketBuffer packet;
514
0
    ComboAddress remote;
515
0
    ComboAddress dest;
516
0
    iovec iov{};
517
    /* used by HarvestDestinationAddress */
518
0
    cmsgbuf_aligned cbuf{};
519
0
  };
520
0
  const size_t vectSize = dnsdist::configuration::getImmutableConfiguration().d_udpVectorSize;
521
522
0
  if (vectSize > std::numeric_limits<uint16_t>::max()) {
523
0
    throw std::runtime_error("The value of setUDPMultipleMessagesVectorSize is too high, the maximum value is " + std::to_string(std::numeric_limits<uint16_t>::max()));
524
0
  }
525
526
0
  auto recvData = std::vector<MMReceiver>(vectSize);
527
0
  auto msgVec = std::vector<mmsghdr>(vectSize);
528
0
  auto outMsgVec = std::vector<mmsghdr>(vectSize);
529
530
  /* the actual buffer is larger because:
531
     - we may have to add EDNS and/or ECS
532
     - we use it for self-generated responses (from rule or cache)
533
     but we only accept incoming payloads up to that size
534
  */
535
0
  const size_t initialBufferSize = getInitialUDPPacketBufferSize(clientState->d_enableProxyProtocol);
536
0
  const size_t maxIncomingPacketSize = getMaximumIncomingPacketSize(*clientState);
537
538
  /* initialize the structures needed to receive our messages */
539
0
  for (size_t idx = 0; idx < vectSize; idx++) {
540
0
    auto& slot = recvData[idx];
541
0
    slot.remote.sin4.sin_family = clientState->local.sin4.sin_family;
542
0
    slot.packet.resize(initialBufferSize);
543
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
544
0
    fillMSGHdr(&msgVec[idx].msg_hdr, &slot.iov, &slot.cbuf, sizeof(decltype(slot.cbuf)), reinterpret_cast<char*>(slot.packet.data()), maxIncomingPacketSize, &slot.remote);
545
0
  }
546
547
0
  int msgsGot = static_cast<int>(vectSize);
548
  /* go now */
549
0
  for (;;) {
550
551
    /* reset the IO vector, since it's also used to send the vector of responses
552
       to avoid having to copy the data around.
553
       No need to reset the parts that have not been used, though. */
554
0
    for (int idx = 0; idx < msgsGot; idx++) {
555
0
      auto& slot = recvData[idx];
556
      /* only resize if the buffer is actually smaller than expected */
557
0
      if (slot.packet.size() < initialBufferSize) {
558
0
        slot.packet.resize(initialBufferSize);
559
0
      }
560
      /* but we need to set the IOv pointer and size anyway,
561
         because if the buffer has been resized at any point
562
         during the processing (to add EDNS options, for example)
563
         the pointer might now be invalid */
564
0
      slot.iov.iov_base = &slot.packet.at(0);
565
0
      slot.iov.iov_len = slot.packet.size();
566
0
    }
567
568
    /* block until we have at least one message ready, but return
569
       as many as possible to save the syscall costs */
570
0
    msgsGot = recvmmsg(clientState->udpFD, msgVec.data(), vectSize, MSG_WAITFORONE, nullptr);
571
0
    if (msgsGot <= 0) {
572
0
      int savederrno = errno;
573
0
      VERBOSESLOG(infolog("Getting UDP messages via recvmmsg() failed with: %s", stringerror(savederrno)),
574
0
                  dnsdist::logging::getTopLogger("udp-recvmmsg-frontend")->error(Logr::Info, savederrno, "Getting UDP messages via recvmmsg failed", "frontend.address", Logging::Loggable(clientState->local)));
575
0
      msgsGot = 0;
576
0
      continue;
577
0
    }
578
579
0
    unsigned int msgsToSend = 0;
580
581
    /* process the received messages */
582
0
    for (int msgIdx = 0; msgIdx < msgsGot; msgIdx++) {
583
0
      auto& msg = msgVec[msgIdx];
584
0
      const struct msghdr* msgh = &msg.msg_hdr;
585
0
      unsigned int got = msg.msg_len;
586
0
      const ComboAddress& remote = recvData[msgIdx].remote;
587
588
0
      if (static_cast<size_t>(got) < sizeof(struct dnsheader)) {
589
0
        ++dnsdist::metrics::g_stats.nonCompliantQueries;
590
0
        ++clientState->nonCompliantQueries;
591
0
        continue;
592
0
      }
593
594
0
      if ((msgh->msg_flags & MSG_TRUNC) != 0) {
595
        /* message was too large for our buffer */
596
0
        ++dnsdist::metrics::g_stats.nonCompliantQueries;
597
0
        ++clientState->nonCompliantQueries;
598
0
        continue;
599
0
      }
600
601
0
      auto& data = recvData[msgIdx];
602
0
      data.packet.resize(got);
603
0
      dnsdist::configuration::refreshLocalRuntimeConfiguration();
604
0
      processUDPQuery(*clientState, msgh, remote, data.dest, data.packet, &outMsgVec, &msgsToSend, &data.iov, &data.cbuf);
605
0
    }
606
607
    /* immediate (not delayed or sent to a backend) responses (mostly from a rule, dynamic block
608
       or the cache) can be sent in batch too */
609
610
0
    if (msgsToSend > 0 && msgsToSend <= static_cast<unsigned int>(msgsGot)) {
611
0
      int sent = sendmmsg(clientState->udpFD, outMsgVec.data(), msgsToSend, 0);
612
613
0
      if (sent < 0 || static_cast<unsigned int>(sent) != msgsToSend) {
614
0
        int savederrno = errno;
615
0
        VERBOSESLOG(infolog("Error sending responses with sendmmsg() (%d on %u): %s", sent, msgsToSend, stringerror(savederrno)),
616
0
                    dnsdist::logging::getTopLogger("udp-sendmmsg-frontend")->error(Logr::Info, savederrno, "Error sending responses with sendmmsg()", "address", Logging::Loggable(clientState->local), "dnsdist.sendmmsg.messages_sent", Logging::Loggable(sent), "dnsdist.sendmmsg.messages_to_send", Logging::Loggable(msgsToSend)));
617
0
      }
618
0
    }
619
0
  }
620
0
}
621
622
static void MultipleMessagesUDPResponseFromBackendThread(std::shared_ptr<DownstreamState> dss)
623
0
{
624
0
  auto responderLogger = dnsdist::logging::getTopLogger("udp-response")->withValues("backend.address", Logging::Loggable(dss->d_config.remote));
625
626
0
  try {
627
0
    setThreadName("dnsdist/respond");
628
0
    const size_t initialBufferSize = getInitialUDPPacketBufferSize(false);
629
0
    std::vector<int> sockets;
630
0
    sockets.reserve(dss->sockets.size());
631
632
0
    struct MMReceiver
633
0
    {
634
0
      PacketBuffer packet;
635
0
      ComboAddress remote;
636
0
      iovec iov{};
637
      /* used by HarvestDestinationAddress */
638
0
      cmsgbuf_aligned cbuf{};
639
0
    };
640
0
    const size_t vectSize = dnsdist::configuration::getImmutableConfiguration().d_udpVectorSize;
641
642
0
    if (vectSize > std::numeric_limits<uint16_t>::max()) {
643
0
      throw std::runtime_error("The value of setUDPMultipleMessagesVectorSize is too high, the maximum value is " + std::to_string(std::numeric_limits<uint16_t>::max()));
644
0
    }
645
646
0
    auto recvData = std::vector<MMReceiver>(vectSize);
647
0
    auto msgVec = std::vector<mmsghdr>(vectSize);
648
649
    /* initialize the structures needed to receive our messages */
650
0
    for (size_t idx = 0; idx < vectSize; idx++) {
651
0
      auto& slot = recvData[idx];
652
0
      slot.packet.resize(initialBufferSize + 1);
653
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
654
0
      fillMSGHdr(&msgVec[idx].msg_hdr, &slot.iov, &slot.cbuf, sizeof(decltype(slot.cbuf)), reinterpret_cast<char*>(slot.packet.data()), initialBufferSize + 1, &slot.remote);
655
0
    }
656
657
0
    int msgsGot = static_cast<int>(vectSize);
658
    /* go now */
659
0
    for (;;) {
660
0
      if (dss->isStopped()) {
661
0
        break;
662
0
      }
663
664
0
      if (!dss->connected) {
665
        /* the sockets are not connected yet, likely because we detected a problem,
666
           tried to reconnect and it failed. We will try to reconnect after the next
667
           successful health-check (unless reconnectOnUp is false), or when trying
668
           to send in the UDP listener thread, but until then we simply need to wait. */
669
0
        dss->waitUntilConnected();
670
0
        continue;
671
0
      }
672
673
0
      dss->pickSocketsReadyForReceiving(sockets);
674
675
      /* check a second time here because we might have waited quite a bit
676
         since the first check */
677
0
      if (dss->isStopped()) {
678
0
        break;
679
0
      }
680
681
0
      for (const auto& sockDesc : sockets) {
682
        /* reset the IO vector.
683
           No need to reset the parts that have not been used, though. */
684
0
        for (int idx = 0; idx < msgsGot; idx++) {
685
0
          auto& slot = recvData[idx];
686
          /* only resize if the buffer is actually smaller than expected */
687
0
          if (slot.packet.size() < (initialBufferSize + 1)) {
688
0
            slot.packet.resize((initialBufferSize + 1));
689
0
          }
690
          /* but we need to set the IOv pointer and size anyway,
691
             because if the buffer has been resized at any point
692
             during the processing (to add EDNS options, for example)
693
             the pointer might now be invalid */
694
0
          slot.iov.iov_base = &slot.packet.at(0);
695
0
          slot.iov.iov_len = slot.packet.size();
696
0
        }
697
698
        /* block until we have at least one message ready, but return
699
           as many as possible to save the syscall costs */
700
0
        msgsGot = recvmmsg(sockDesc, msgVec.data(), vectSize, MSG_WAITFORONE, nullptr);
701
0
        if (msgsGot <= 0) {
702
0
          int savederrno = errno;
703
0
          if (dss->isStopped()) {
704
0
            break;
705
0
          }
706
0
          VERBOSESLOG(infolog("Getting UDP messages from backend via recvmmsg() failed with: %s", stringerror(savederrno)),
707
0
                      responderLogger->error(Logr::Info, savederrno, "Getting UDP messages via recvmmsg failed"));
708
0
          msgsGot = 0;
709
0
          continue;
710
0
        }
711
712
        /* process the received messages */
713
0
        for (int msgIdx = 0; msgIdx < msgsGot; msgIdx++) {
714
0
          try {
715
0
            auto& msg = msgVec.at(msgIdx);
716
0
            unsigned int got = msg.msg_len;
717
0
            const struct msghdr* msgh = &msg.msg_hdr;
718
0
            if ((msgh->msg_flags & MSG_TRUNC) != 0) {
719
0
              continue;
720
0
            }
721
722
0
            processUDPResponseFromBackend(*responderLogger, dss, recvData.at(msgIdx).packet, got, initialBufferSize, sockDesc);
723
0
          }
724
0
          catch (const std::exception& e) {
725
0
            VERBOSESLOG(infolog("Got an error in UDP responder thread while parsing a response from %s: %s", dss->d_config.remote.toStringWithPort(), e.what()),
726
0
                        responderLogger->error(Logr::Info, e.what(), "Got an error in UDP responder thread while parsing a response"));
727
0
          }
728
0
        }
729
0
      }
730
0
    }
731
0
  }
732
0
  catch (const std::exception& e) {
733
0
    SLOG(errlog("UDP responder thread died because of exception: %s", e.what()),
734
0
         responderLogger->error(Logr::Error, e.what(), "UDP responder thread died because of an exception"));
735
0
  }
736
0
  catch (...) {
737
0
    SLOG(errlog("UDP responder thread died because of an exception: %s", "unknown"),
738
0
         responderLogger->info(Logr::Error, "UDP responder thread died because of an unknown exception"));
739
0
  }
740
0
}
741
#endif /* defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE) */
742
#endif /* DISABLE_RECVMMSG */
743
744
// listens to incoming queries, sends out to downstream servers, noting the intended return path
745
void udpClientThread(std::vector<ClientState*> states)
746
0
{
747
0
  try {
748
0
    setThreadName("dnsdist/udpClie");
749
0
#ifndef DISABLE_RECVMMSG
750
0
#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
751
0
    if (dnsdist::configuration::getImmutableConfiguration().d_udpVectorSize > 1) {
752
0
      MultipleMessagesUDPClientThread(states.at(0));
753
0
    }
754
0
    else
755
0
#endif /* defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE) */
756
0
#endif /* DISABLE_RECVMMSG */
757
0
    {
758
      /* the actual buffer is larger because:
759
         - we may have to add EDNS and/or ECS
760
         - we use it for self-generated responses (from rule or cache)
761
         but we only accept incoming payloads up to that size
762
      */
763
0
      struct UDPStateParam
764
0
      {
765
0
        ClientState* cs{nullptr};
766
0
        size_t maxIncomingPacketSize{0};
767
0
        int socket{-1};
768
0
      };
769
0
      const size_t initialBufferSize = getInitialUDPPacketBufferSize(true);
770
0
      PacketBuffer packet(initialBufferSize);
771
772
0
      msghdr msgh{};
773
0
      iovec iov{};
774
0
      ComboAddress remote;
775
0
      ComboAddress dest;
776
777
0
      auto handleOnePacket = [&packet, &iov, &msgh, &remote, &dest, initialBufferSize](const UDPStateParam& param) {
778
0
        packet.resize(initialBufferSize);
779
0
        iov.iov_base = &packet.at(0);
780
0
        iov.iov_len = packet.size();
781
782
0
        ssize_t got = recvmsg(param.socket, &msgh, 0);
783
784
0
        if (got < 0 || static_cast<size_t>(got) < sizeof(struct dnsheader)) {
785
0
          ++dnsdist::metrics::g_stats.nonCompliantQueries;
786
0
          ++param.cs->nonCompliantQueries;
787
0
          return;
788
0
        }
789
790
0
        packet.resize(static_cast<size_t>(got));
791
792
0
        dnsdist::configuration::refreshLocalRuntimeConfiguration();
793
0
        processUDPQuery(*param.cs, &msgh, remote, dest, packet, nullptr, nullptr, nullptr, nullptr);
794
0
      };
795
796
0
      std::vector<UDPStateParam> params;
797
0
      for (auto& state : states) {
798
0
        const size_t maxIncomingPacketSize = getMaximumIncomingPacketSize(*state);
799
0
        params.emplace_back(UDPStateParam{state, maxIncomingPacketSize, state->udpFD});
800
0
      }
801
802
0
      if (params.size() == 1) {
803
0
        const auto& param = params.at(0);
804
0
        remote.sin4.sin_family = param.cs->local.sin4.sin_family;
805
        /* used by HarvestDestinationAddress */
806
0
        cmsgbuf_aligned cbuf;
807
        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
808
0
        fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), reinterpret_cast<char*>(&packet.at(0)), param.maxIncomingPacketSize, &remote);
809
0
        while (true) {
810
0
          try {
811
0
            handleOnePacket(param);
812
0
          }
813
0
          catch (const std::bad_alloc& e) {
814
            /* most exceptions are handled by handleOnePacket(), but we might be out of memory (std::bad_alloc)
815
               in which case we DO NOT want to log (as it would trigger another memory allocation attempt
816
               that might throw as well) but wait a bit (one millisecond) and then try to recover */
817
0
            usleep(1000);
818
0
          }
819
0
        }
820
0
      }
821
0
      else {
822
0
        auto callback = [&remote, &msgh, &iov, &packet, &handleOnePacket, initialBufferSize](int socket, FDMultiplexer::funcparam_t& funcparam) {
823
0
          (void)socket;
824
0
          const auto* param = boost::any_cast<const UDPStateParam*>(funcparam);
825
0
          try {
826
0
            remote.sin4.sin_family = param->cs->local.sin4.sin_family;
827
0
            packet.resize(initialBufferSize);
828
            /* used by HarvestDestinationAddress */
829
0
            cmsgbuf_aligned cbuf;
830
            // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
831
0
            fillMSGHdr(&msgh, &iov, &cbuf, sizeof(cbuf), reinterpret_cast<char*>(&packet.at(0)), param->maxIncomingPacketSize, &remote);
832
0
            handleOnePacket(*param);
833
0
          }
834
0
          catch (const std::bad_alloc& e) {
835
            /* most exceptions are handled by handleOnePacket(), but we might be out of memory (std::bad_alloc)
836
               in which case we DO NOT want to log (as it would trigger another memory allocation attempt
837
               that might throw as well) but wait a bit (one millisecond) and then try to recover */
838
0
            usleep(1000);
839
0
          }
840
0
        };
841
0
        auto mplexer = std::unique_ptr<FDMultiplexer>(FDMultiplexer::getMultiplexerSilent(params.size()));
842
0
        for (const auto& param : params) {
843
0
          mplexer->addReadFD(param.socket, callback, &param);
844
0
        }
845
846
0
        timeval now{};
847
0
        while (true) {
848
0
          mplexer->run(&now, -1);
849
0
        }
850
0
      }
851
0
    }
852
0
  }
853
0
  catch (const std::exception& e) {
854
0
    SLOG(errlog("UDP client thread died because of exception: %s", e.what()),
855
0
         dnsdist::logging::getTopLogger("udp-frontend")->error(Logr::Error, e.what(), "UDP client thread died because of exception"));
856
0
  }
857
0
  catch (...) {
858
0
    SLOG(errlog("UDP client thread died because of an exception: unknown"),
859
0
         dnsdist::logging::getTopLogger("udp-frontend")->info(Logr::Error, "UDP client thread died because of an unknown exception"));
860
0
  }
861
0
}
862
863
std::unique_ptr<CrossProtocolQuery> getUDPCrossProtocolQueryFromDQ(DNSQuestion& dnsQuestion)
864
0
{
865
0
  dnsQuestion.ids.origID = dnsQuestion.getHeader()->id;
866
0
  return std::make_unique<dnsdist::udp::UDPCrossProtocolQuery>(std::move(dnsQuestion.getMutableData()), std::move(dnsQuestion.ids), nullptr);
867
0
}
868
869
// listens on a dedicated socket, lobs answers from downstream servers to original requestors
870
void responderThread(std::shared_ptr<DownstreamState> dss)
871
0
{
872
0
#ifndef DISABLE_RECVMMSG
873
0
#if defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE)
874
0
  if (dnsdist::configuration::getImmutableConfiguration().d_udpVectorSize > 1) {
875
0
    MultipleMessagesUDPResponseFromBackendThread(std::move(dss));
876
0
    return;
877
0
  }
878
0
#endif /* defined(HAVE_RECVMMSG) && defined(HAVE_SENDMMSG) && defined(MSG_WAITFORONE) */
879
0
#endif /* DISABLE_RECVMMSG */
880
881
0
  auto responderLogger = dnsdist::logging::getTopLogger("udp-response")->withValues("backend.address", Logging::Loggable(dss->d_config.remote));
882
883
0
  try {
884
0
    setThreadName("dnsdist/respond");
885
0
    const size_t initialBufferSize = dnsdist::udp::getInitialUDPPacketBufferSize(false);
886
    /* allocate one more byte so we can detect truncation */
887
0
    PacketBuffer response(initialBufferSize + 1);
888
0
    std::vector<int> sockets;
889
0
    sockets.reserve(dss->sockets.size());
890
891
0
    for (;;) {
892
0
      try {
893
0
        if (dss->isStopped()) {
894
0
          break;
895
0
        }
896
897
0
        if (!dss->connected) {
898
          /* the sockets are not connected yet, likely because we detected a problem,
899
             tried to reconnect and it failed. We will try to reconnect after the next
900
             successful health-check (unless reconnectOnUp is false), or when trying
901
             to send in the UDP listener thread, but until then we simply need to wait. */
902
0
          dss->waitUntilConnected();
903
0
          continue;
904
0
        }
905
906
0
        dss->pickSocketsReadyForReceiving(sockets);
907
908
        /* check a second time here because we might have waited quite a bit
909
           since the first check */
910
0
        if (dss->isStopped()) {
911
0
          break;
912
0
        }
913
914
0
        for (const auto& sockDesc : sockets) {
915
          /* allocate one more byte so we can detect truncation */
916
          // NOLINTNEXTLINE(bugprone-use-after-move): resizing a vector has no preconditions so it is valid to do so after moving it
917
0
          response.resize(initialBufferSize + 1);
918
0
          ssize_t got = recv(sockDesc, response.data(), response.size(), 0);
919
920
0
          if (got == 0 && dss->isStopped()) {
921
0
            break;
922
0
          }
923
924
0
          processUDPResponseFromBackend(*responderLogger, dss, response, got, initialBufferSize, sockDesc);
925
0
        }
926
0
      }
927
0
      catch (const std::exception& e) {
928
0
        VERBOSESLOG(infolog("Got an error in UDP responder thread while parsing a response from %s: %s", dss->d_config.remote.toStringWithPort(), e.what()),
929
0
                    responderLogger->error(Logr::Info, e.what(), "Got an error in UDP responder thread while parsing a response"));
930
0
      }
931
0
    }
932
0
  }
933
0
  catch (const std::exception& e) {
934
0
    SLOG(errlog("UDP responder thread died because of exception: %s", e.what()),
935
0
         responderLogger->error(Logr::Error, e.what(), "UDP responder thread died because of an exception"));
936
0
  }
937
0
  catch (...) {
938
0
    SLOG(errlog("UDP responder thread died because of an exception: %s", "unknown"),
939
0
         responderLogger->info(Logr::Error, "UDP responder thread died because of an unknown exception"));
940
0
  }
941
0
}
942
943
} // namespace dnsdist::udp