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

PowerDNS / pdns / 18937558685

30 Oct 2025 10:27AM UTC coverage: 73.04% (+0.03%) from 73.014%
18937558685

Pull #16395

github

web-flow
Merge 3b9be1959 into 9769584db
Pull Request #16395: dnsdist: Add option to use incoming OpenTelemetry Trace ID

38333 of 63186 branches covered (60.67%)

Branch coverage included in aggregate %.

109 of 120 new or added lines in 5 files covered. (90.83%)

64 existing lines in 15 files now uncovered.

127583 of 163972 relevant lines covered (77.81%)

5996675.21 hits per line

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

57.14
/pdns/iputils.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22
#ifdef HAVE_CONFIG_H
23
#include "config.h"
24
#endif
25

26
#include "iputils.hh"
27

28
#include <fstream>
29
#include <sys/socket.h>
30
#include <boost/format.hpp>
31

32
#ifdef HAVE_GETIFADDRS
33
#include <ifaddrs.h>
34
#endif
35

36
/** these functions provide a very lightweight wrapper to the Berkeley sockets API. Errors -> exceptions! */
37

38
static void RuntimeError(const std::string& error)
39
{
×
40
  throw runtime_error(error);
×
41
}
×
42

43
static void NetworkErr(const std::string& error)
44
{
39✔
45
  throw NetworkError(error);
39✔
46
}
39✔
47

48
int SSocket(int family, int type, int flags)
49
{
1,353✔
50
  int ret = socket(family, type, flags);
1,353✔
51
  if (ret < 0) {
1,353!
52
    RuntimeError("creating socket of type " + std::to_string(family) + ": " + stringerror());
×
53
  }
×
54
  return ret;
1,353✔
55
}
1,353✔
56

57
static int doConnect(int sockfd, [[maybe_unused]] bool fastopen, const ComboAddress& remote)
58
{
22,576✔
59
#ifdef CONNECTX_FASTOPEN
60
  if (fastopen) {
61
    sa_endpoints_t endpoints{};
62

63
    endpoints.sae_dstaddr = reinterpret_cast<const struct sockaddr*>(&remote); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
64
    endpoints.sae_dstaddrlen = remote.getSocklen();
65

66
    int flags = CONNECT_DATA_IDEMPOTENT | CONNECT_RESUME_ON_READ_WRITE;
67
    return connectx(sockfd, &endpoints, SAE_ASSOCID_ANY, flags, nullptr, 0, nullptr, nullptr);
68
  }
69
  else {
70
#endif
71
    return connect(sockfd, reinterpret_cast<const struct sockaddr*>(&remote), remote.getSocklen()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
22,576✔
72
#ifdef CONNECTX_FASTOPEN
73
  }
74
#endif
75
}
22,576✔
76

77
int SConnect(int sockfd, bool fastopen, const ComboAddress& remote)
78
{
415✔
79
  int ret = doConnect(sockfd, fastopen, remote);
415✔
80
  if (ret < 0) {
415!
81
    int savederrno = errno;
×
82
    RuntimeError("connecting socket to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
×
83
  }
×
84
  return ret;
415✔
85
}
415✔
86

87
int SConnectWithTimeout(int sockfd, bool fastopen, const ComboAddress& remote, const struct timeval& timeout)
88
{
22,161✔
89
  int ret = doConnect(sockfd, fastopen, remote);
22,161✔
90
  if (ret < 0) {
22,161✔
91
    int savederrno = errno;
3,778✔
92
    if (savederrno == EINPROGRESS) {
3,779✔
93
      if (timeout <= timeval{0, 0}) {
3,775✔
94
        return savederrno;
2,555✔
95
      }
2,555✔
96

97
      /* we wait until the connection has been established */
98
      bool error = false;
1,220✔
99
      bool disconnected = false;
1,220✔
100
      int res = waitForRWData(sockfd, false, timeout, &error, &disconnected);
1,220✔
101
      if (res == 1) {
1,221✔
102
        if (error) {
1,221✔
103
          savederrno = 0;
35✔
104
          socklen_t errlen = sizeof(savederrno);
35✔
105
          if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)&savederrno, &errlen) == 0) {
35!
106
            NetworkErr("connecting to " + remote.toStringWithPort() + " failed: " + stringerror(savederrno));
35✔
107
          }
35✔
108
          else {
×
109
            NetworkErr("connecting to " + remote.toStringWithPort() + " failed");
×
110
          }
×
111
        }
35✔
112
        if (disconnected) {
1,221!
113
          NetworkErr(remote.toStringWithPort() + " closed the connection");
×
114
        }
×
115
        return 0;
1,221✔
116
      }
1,221✔
117
      if (res == 0) {
2,147,483,647!
118
        NetworkErr("timeout while connecting to " + remote.toStringWithPort());
×
119
      }
×
120
      else if (res < 0) {
2,147,483,647!
121
        savederrno = errno;
×
122
        NetworkErr("waiting to connect to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
×
123
      }
×
124
    }
2,147,483,647✔
125
    else {
2,147,483,651✔
126
      NetworkErr("connecting to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
2,147,483,651✔
127
    }
2,147,483,651✔
128
  }
3,778✔
129

130
  return 0;
18,385✔
131
}
22,161✔
132

133
int SBind(int sockfd, const ComboAddress& local)
134
{
943✔
135
  int ret = bind(sockfd, reinterpret_cast<const struct sockaddr*>(&local), local.getSocklen()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
943✔
136
  if (ret < 0) {
943!
137
    int savederrno = errno;
×
138
    RuntimeError("binding socket to " + local.toStringWithPort() + ": " + stringerror(savederrno));
×
139
  }
×
140
  return ret;
943✔
141
}
943✔
142

143
int SAccept(int sockfd, ComboAddress& remote)
144
{
849✔
145
  socklen_t remlen = remote.getSocklen();
849✔
146

147
  int ret = accept(sockfd, reinterpret_cast<struct sockaddr*>(&remote), &remlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
849✔
148
  if (ret < 0) {
849!
149
    RuntimeError("accepting new connection on socket: " + stringerror());
×
150
  }
×
151
  return ret;
849✔
152
}
849✔
153

154
int SListen(int sockfd, int limit)
155
{
504✔
156
  int ret = listen(sockfd, limit);
504✔
157
  if (ret < 0) {
504!
158
    RuntimeError("setting socket to listen: " + stringerror());
×
159
  }
×
160
  return ret;
504✔
161
}
504✔
162

163
int SSetsockopt(int sockfd, int level, int opname, int value)
164
{
2,580✔
165
  int ret = setsockopt(sockfd, level, opname, &value, sizeof(value));
2,580✔
166
  if (ret < 0) {
2,580!
167
    RuntimeError("setsockopt for level " + std::to_string(level) + " and opname " + std::to_string(opname) + " to " + std::to_string(value) + " failed: " + stringerror());
×
168
  }
×
169
  return ret;
2,580✔
170
}
2,580✔
171

172
void setSocketIgnorePMTU([[maybe_unused]] int sockfd, [[maybe_unused]] int family)
173
{
990✔
174
  if (family == AF_INET) { // NOLINT(bugprone-branch-clone)
990✔
175
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
916✔
176
#ifdef IP_PMTUDISC_OMIT
916✔
177
    /* Linux 3.15+ has IP_PMTUDISC_OMIT, which discards PMTU information to prevent
178
       poisoning, but still allows fragmentation if the packet size exceeds the
179
       outgoing interface MTU, which is good.
180
    */
181
    try {
916✔
182
      SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_OMIT);
916✔
183
      return;
916✔
184
    }
916✔
185
    catch (const std::exception& e) {
916✔
186
      /* failed, let's try IP_PMTUDISC_DONT instead */
187
    }
×
188
#endif /* IP_PMTUDISC_OMIT */
×
189

190
    /* IP_PMTUDISC_DONT disables Path MTU discovery */
191
    SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
×
192
#endif /* defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) */
×
193
  }
×
194
  else {
74✔
195
#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT)
74✔
196
#ifdef IPV6_PMTUDISC_OMIT
74✔
197
    /* Linux 3.15+ has IPV6_PMTUDISC_OMIT, which discards PMTU information to prevent
198
       poisoning, but still allows fragmentation if the packet size exceeds the
199
       outgoing interface MTU, which is good.
200
    */
201
    try {
74✔
202
      SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_OMIT);
74✔
203
      return;
74✔
204
    }
74✔
205
    catch (const std::exception& e) {
74✔
206
      /* failed, let's try IP_PMTUDISC_DONT instead */
207
    }
×
208
#endif /* IPV6_PMTUDISC_OMIT */
×
209

210
    /* IPV6_PMTUDISC_DONT disables Path MTU discovery */
211
    SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DONT);
×
212
#endif /* defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT) */
×
213
  }
×
214
}
990✔
215

216
void setSocketForcePMTU([[maybe_unused]] int sockfd, [[maybe_unused]] int family)
217
{
37✔
218
  if (family == AF_INET) {
37✔
219
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
35✔
220
    /* IP_PMTUDISC_DO enables Path MTU discovery and prevents fragmentation */
221
    SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DO);
35✔
222
#elif defined(IP_DONTFRAG)
223
    /* at least this prevents fragmentation */
224
    SSetsockopt(sockfd, IPPROTO_IP, IP_DONTFRAG, 1);
225
#endif /* defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO) */
226
  }
35✔
227
  else {
2✔
228
#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
2✔
229
    /* IPV6_PMTUDISC_DO enables Path MTU discovery and prevents fragmentation */
230
    SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DO);
2✔
231
#elif defined(IPV6_DONTFRAG)
232
    /* at least this prevents fragmentation */
233
    SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_DONTFRAG, 1);
234
#endif /* defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO) */
235
  }
2✔
236
}
37✔
237

238
bool setReusePort(int sockfd)
239
{
18✔
240
#if defined(SO_REUSEPORT_LB)
241
  try {
242
    SSetsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
243
    return true;
244
  }
245
  catch (const std::exception& e) {
246
    return false;
247
  }
248
#elif defined(SO_REUSEPORT)
249
  try {
18✔
250
    SSetsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, 1);
18✔
251
    return true;
18✔
252
  }
18✔
253
  catch (const std::exception& e) {
18✔
254
    return false;
×
255
  }
×
256
#endif
×
257
  return false;
×
258
}
18✔
259

260
bool HarvestTimestamp(struct msghdr* msgh, struct timeval* timeval)
261
{
131,876✔
262
  // NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast, cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-type-reinterpret-cast)
263
#ifdef SO_TIMESTAMP
131,876✔
264
  struct cmsghdr* cmsg{};
131,876✔
265
  for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != nullptr; cmsg = CMSG_NXTHDR(msgh, cmsg)) {
131,876!
266
    if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMP || cmsg->cmsg_type == SCM_TIMESTAMP) && CMSG_LEN(sizeof(*timeval)) == cmsg->cmsg_len) {
131,876!
267
      memcpy(timeval, CMSG_DATA(cmsg), sizeof(*timeval));
131,876✔
268
      return true;
131,876✔
269
    }
131,876✔
270
  }
131,876✔
271
#endif
×
272
  return false;
×
273
}
131,876✔
274
bool HarvestDestinationAddress(const struct msghdr* msgh, ComboAddress* destination)
275
{
135,698✔
276
  destination->reset();
135,698✔
277
#ifdef __NetBSD__
278
  struct cmsghdr* cmsg{};
279
#else
280
  const struct cmsghdr* cmsg{};
135,698✔
281
#endif
135,698✔
282
  for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != nullptr; cmsg = CMSG_NXTHDR(const_cast<struct msghdr*>(msgh), const_cast<struct cmsghdr*>(cmsg))) {
267,587✔
283
#if defined(IP_PKTINFO)
159,461✔
284
    if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
159,461!
285
      const auto* ptr = reinterpret_cast<const struct in_pktinfo*>(CMSG_DATA(cmsg));
27,571✔
286
      destination->sin4.sin_addr = ptr->ipi_addr;
27,571✔
287
      destination->sin4.sin_family = AF_INET;
27,571✔
288
      return true;
27,571✔
289
    }
27,571✔
290
#elif defined(IP_RECVDSTADDR)
291
    if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) {
292
      const auto* ptr = reinterpret_cast<const struct in_addr*>(CMSG_DATA(cmsg));
293
      destination->sin4.sin_addr = *ptr;
294
      destination->sin4.sin_family = AF_INET;
295
      return true;
296
    }
297
#endif
298

299
    if ((cmsg->cmsg_level == IPPROTO_IPV6) && (cmsg->cmsg_type == IPV6_PKTINFO)) {
131,890!
300
      const auto* ptr = reinterpret_cast<const struct in6_pktinfo*>(CMSG_DATA(cmsg));
1✔
301
      destination->sin6.sin6_addr = ptr->ipi6_addr;
1✔
302
      destination->sin4.sin_family = AF_INET6;
1✔
303
      return true;
1✔
304
    }
1✔
305
  }
131,890✔
306
  return false;
108,126✔
307
  // NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast, cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-type-reinterpret-cast)
308
}
135,698✔
309

310
bool IsAnyAddress(const ComboAddress& addr)
311
{
8,448✔
312
  if (addr.sin4.sin_family == AF_INET) {
8,448✔
313
    return addr.sin4.sin_addr.s_addr == 0;
8,366✔
314
  }
8,366✔
315
  if (addr.sin4.sin_family == AF_INET6) {
82!
316
    return memcmp(&addr.sin6.sin6_addr, &in6addr_any, sizeof(addr.sin6.sin6_addr)) == 0;
82✔
317
  }
82✔
318
  return false;
×
319
}
82✔
320

321
int sendOnNBSocket(int fileDesc, const struct msghdr* msgh)
322
{
131,545✔
323
  int sendErr = 0;
131,545✔
324
#ifdef __OpenBSD__
325
  // OpenBSD can and does return EAGAIN on non-blocking datagram sockets
326
  for (int i = 0; i < 10; i++) { // Arbitrary upper bound
327
    if (sendmsg(fileDesc, msgh, 0) != -1) {
328
      sendErr = 0;
329
      break;
330
    }
331
    sendErr = errno;
332
    if (sendErr != EAGAIN) {
333
      break;
334
    }
335
  }
336
#else
337
  if (sendmsg(fileDesc, msgh, 0) == -1) {
131,545!
338
    sendErr = errno;
×
339
  }
×
340
#endif
131,545✔
341
  return sendErr;
131,545✔
342
}
131,545✔
343

344
// be careful: when using this for receive purposes, make sure addr->sin4.sin_family is set appropriately so getSocklen works!
345
// be careful: when using this function for *send* purposes, be sure to set cbufsize to 0!
346
// be careful: if you don't call addCMsgSrcAddr after fillMSGHdr, make sure to set msg_control to NULL
347
void fillMSGHdr(struct msghdr* msgh, struct iovec* iov, cmsgbuf_aligned* cbuf, size_t cbufsize, char* data, size_t datalen, ComboAddress* addr)
348
{
266,725✔
349
  iov->iov_base = data;
266,725✔
350
  iov->iov_len = datalen;
266,725✔
351

352
  memset(msgh, 0, sizeof(struct msghdr));
266,725✔
353

354
  msgh->msg_control = cbuf;
266,725✔
355
  msgh->msg_controllen = cbufsize;
266,725✔
356
  msgh->msg_name = addr;
266,725✔
357
  msgh->msg_namelen = addr->getSocklen();
266,725✔
358
  msgh->msg_iov = iov;
266,725✔
359
  msgh->msg_iovlen = 1;
266,725✔
360
  msgh->msg_flags = 0;
266,725✔
361
}
266,725✔
362

363
// warning: various parts of PowerDNS assume 'truncate' will never throw
364
void ComboAddress::truncate(unsigned int bits) noexcept
365
{
135,250✔
366
  uint8_t* start{};
135,250✔
367
  int len = 4;
135,250✔
368
  if (sin4.sin_family == AF_INET) {
135,250✔
369
    if (bits >= 32) {
134,459✔
370
      return;
131,773✔
371
    }
131,773✔
372
    start = reinterpret_cast<uint8_t*>(&sin4.sin_addr.s_addr); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
2,686✔
373
    len = 4;
2,686✔
374
  }
2,686✔
375
  else {
791✔
376
    if (bits >= 128) {
791✔
377
      return;
53✔
378
    }
53✔
379
    start = reinterpret_cast<uint8_t*>(&sin6.sin6_addr.s6_addr); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
738✔
380
    len = 16;
738✔
381
  }
738✔
382

383
  auto tozero = len * 8 - bits; // if set to 22, this will clear 1 byte, as it should
3,424✔
384

385
  memset(start + len - tozero / 8, 0, tozero / 8); // blot out the whole bytes on the right NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
3,424✔
386

387
  auto bitsleft = tozero % 8; // 2 bits left to clear
3,424✔
388

389
  // a b c d, to truncate to 22 bits, we just zeroed 'd' and need to zero 2 bits from c
390
  // so and by '11111100', which is ~((1<<2)-1)  = ~3
391
  uint8_t* place = start + len - 1 - tozero / 8; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
3,424✔
392
  *place &= (~((1 << bitsleft) - 1));
3,424✔
393
}
3,424✔
394

395
size_t sendMsgWithOptions(int socketDesc, const void* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags)
396
{
891✔
397
  msghdr msgh{};
891✔
398
  iovec iov{};
891✔
399
  cmsgbuf_aligned cbuf;
891✔
400

401
  /* Set up iov and msgh structures. */
402
  memset(&msgh, 0, sizeof(msgh));
891✔
403
  msgh.msg_control = nullptr;
891✔
404
  msgh.msg_controllen = 0;
891✔
405
  if (dest != nullptr) {
891!
406
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-type-const-cast): it's the API
407
    msgh.msg_name = reinterpret_cast<void*>(const_cast<ComboAddress*>(dest));
891✔
408
    msgh.msg_namelen = dest->getSocklen();
891✔
409
  }
891✔
410
  else {
×
411
    msgh.msg_name = nullptr;
×
412
    msgh.msg_namelen = 0;
×
413
  }
×
414

415
  msgh.msg_flags = 0;
891✔
416

417
  if (local != nullptr && local->sin4.sin_family != 0) {
891!
418
    addCMsgSrcAddr(&msgh, &cbuf, local, static_cast<int>(localItf));
891✔
419
  }
891✔
420

421
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast): it's the API
422
  iov.iov_base = const_cast<void*>(buffer);
891✔
423
  iov.iov_len = len;
891✔
424
  msgh.msg_iov = &iov;
891✔
425
  msgh.msg_iovlen = 1;
891✔
426
  msgh.msg_flags = 0;
891✔
427

428
  size_t sent = 0;
891✔
429
#ifdef MSG_FASTOPEN
891✔
430
  bool firstTry = true;
891✔
431
#endif
891✔
432

433
  do {
891✔
434

435
#ifdef MSG_FASTOPEN
891✔
436
    if ((flags & MSG_FASTOPEN) != 0 && !firstTry) {
891!
437
      flags &= ~MSG_FASTOPEN;
×
438
    }
×
439
#endif /* MSG_FASTOPEN */
891✔
440

441
    ssize_t res = sendmsg(socketDesc, &msgh, flags);
891✔
442

443
    if (res > 0) {
891!
444
      auto written = static_cast<size_t>(res);
891✔
445
      sent += written;
891✔
446

447
      if (sent == len) {
891!
448
        return sent;
891✔
449
      }
891✔
450

451
      /* partial write */
452
#ifdef MSG_FASTOPEN
×
453
      firstTry = false;
×
454
#endif
×
455
      iov.iov_len -= written;
×
456
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic): it's the API
457
      iov.iov_base = reinterpret_cast<void*>(reinterpret_cast<char*>(iov.iov_base) + written);
×
458
    }
×
459
    else if (res == 0) {
×
460
      return res;
×
461
    }
×
462
    else if (res == -1) {
×
463
      int err = errno;
×
464
      if (err == EINTR) {
×
465
        continue;
×
466
      }
×
467
      if (err == EAGAIN || err == EWOULDBLOCK || err == EINPROGRESS || err == ENOTCONN) {
×
468
        /* EINPROGRESS might happen with non blocking socket,
469
           especially with TCP Fast Open */
470
        return sent;
×
471
      }
×
472
      unixDie("failed in sendMsgWithOptions");
×
473
    }
×
474
  } while (true);
891✔
475

476
  return 0;
×
477
}
891✔
478

479
template class NetmaskTree<bool, Netmask>;
480

481
/* requires a non-blocking socket.
482
   On Linux, we could use MSG_DONTWAIT on a blocking socket
483
   but this is not portable.
484
*/
485
bool isTCPSocketUsable(int sock)
486
{
112,052✔
487
  int err = 0;
112,052✔
488
  char buf = '\0';
112,052✔
489
  size_t buf_size = sizeof(buf);
112,052✔
490

491
  do {
112,052✔
492
    ssize_t got = recv(sock, &buf, buf_size, MSG_PEEK);
112,052✔
493

494
    if (got > 0) {
112,052!
495
      /* socket is usable, some data is even waiting to be read */
496
      return true;
×
497
    }
×
498
    if (got == 0) {
112,052✔
499
      /* other end has closed the socket */
500
      return false;
93✔
501
    }
93✔
502
    err = errno;
111,959✔
503
    if (err == EAGAIN || err == EWOULDBLOCK) {
111,959!
504
      /* socket is usable, no data waiting */
505
      return true;
111,959✔
506
    }
111,959✔
UNCOV
507
    if (err != EINTR) {
×
508
      /* something is wrong, could be ECONNRESET,
509
         ENOTCONN, EPIPE, but anyway this socket is
510
         not usable. */
511
      return false;
×
512
    }
×
UNCOV
513
  } while (err == EINTR);
×
514

UNCOV
515
  return false;
×
516
}
112,052✔
517
/* mission in life: parse four cases
518
   1) [2002::1]:53
519
   2) 1.2.3.4
520
   3) 1.2.3.4:5300
521
   4) 2001::1 no port allowed
522
*/
523

524
ComboAddress parseIPAndPort(const std::string& input, uint16_t port)
525
{
2,929✔
526
  if (input[0] == '[') { // case 1
2,929✔
527
    auto both = splitField(input.substr(1), ']');
12✔
528
    return ComboAddress(both.first, both.second.empty() ? port : pdns::checked_stoi<uint16_t>(both.second.substr(1)));
12!
529
  }
12✔
530

531
  string::size_type count = 0;
2,917✔
532
  for (char chr : input) {
45,480✔
533
    if (chr == ':') {
45,480✔
534
      count++;
2,866✔
535
    }
2,866✔
536
    if (count > 1) {
45,480✔
537
      break;
30✔
538
    }
30✔
539
  }
45,480✔
540
  switch (count) {
2,917✔
541
  case 0: // case 2
81✔
542
    return ComboAddress(input, port);
81✔
543
  case 1: { // case 3
2,806✔
544
    string::size_type cpos = input.rfind(':');
2,806✔
545
    pair<std::string, std::string> both;
2,806✔
546
    both.first = input.substr(0, cpos);
2,806✔
547
    both.second = input.substr(cpos + 1);
2,806✔
548

549
    auto newport = pdns::checked_stoi<uint16_t>(both.second);
2,806✔
550
    return ComboAddress(both.first, newport);
2,806✔
551
  }
×
552
  default: // case 4
30✔
553
    return ComboAddress(input, port);
30✔
554
  }
2,917✔
555
}
2,917✔
556

557
void setSocketBuffer(int fileDesc, int optname, uint32_t size)
558
{
1,442✔
559
  uint32_t psize = 0;
1,442✔
560
  socklen_t len = sizeof(psize);
1,442✔
561

562
  if (getsockopt(fileDesc, SOL_SOCKET, optname, &psize, &len) != 0) {
1,442!
563
    throw std::runtime_error("Unable to retrieve socket buffer size:" + stringerror());
×
564
  }
×
565
  if (psize >= size) {
1,442✔
566
    return;
1,240✔
567
  }
1,240✔
568
  if (setsockopt(fileDesc, SOL_SOCKET, optname, &size, sizeof(size)) != 0) {
202!
569
    throw std::runtime_error("Unable to raise socket buffer size to " + std::to_string(size) + ": " + stringerror());
×
570
  }
×
571
}
202✔
572

573
void setSocketReceiveBuffer(int fileDesc, uint32_t size)
574
{
372✔
575
  setSocketBuffer(fileDesc, SO_RCVBUF, size);
372✔
576
}
372✔
577

578
void setSocketSendBuffer(int fileDesc, uint32_t size)
579
{
202✔
580
  setSocketBuffer(fileDesc, SO_SNDBUF, size);
202✔
581
}
202✔
582

583
#ifdef __linux__
584
static uint32_t raiseSocketBufferToMax(int socket, int optname, const std::string& readMaxFromFile)
585
{
868✔
586
  std::ifstream ifs(readMaxFromFile);
868✔
587
  if (ifs) {
868!
588
    std::string line;
868✔
589
    if (getline(ifs, line)) {
868!
590
      auto max = pdns::checked_stoi<uint32_t>(line);
868✔
591
      setSocketBuffer(socket, optname, max);
868✔
592
      return max;
868✔
593
    }
868✔
594
  }
868✔
595
  return 0;
×
596
}
868✔
597
#endif
598

599
uint32_t raiseSocketReceiveBufferToMax([[maybe_unused]] int socket)
600
{
434✔
601
#ifdef __linux__
434✔
602
  return raiseSocketBufferToMax(socket, SO_RCVBUF, "/proc/sys/net/core/rmem_max");
434✔
603
#else
604
  return 0;
605
#endif
606
}
434✔
607

608
uint32_t raiseSocketSendBufferToMax([[maybe_unused]] int socket)
609
{
434✔
610
#ifdef __linux__
434✔
611
  return raiseSocketBufferToMax(socket, SO_SNDBUF, "/proc/sys/net/core/wmem_max");
434✔
612
#else
613
  return 0;
614
#endif
615
}
434✔
616

617
std::set<std::string> getListOfNetworkInterfaces()
618
{
×
619
  std::set<std::string> result;
×
620
#ifdef HAVE_GETIFADDRS
×
621
  struct ifaddrs* ifaddr{};
×
622
  if (getifaddrs(&ifaddr) == -1) {
×
623
    return result;
×
624
  }
×
625

626
  for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
×
627
    if (ifa->ifa_name == nullptr) {
×
628
      continue;
×
629
    }
×
630
    result.insert(ifa->ifa_name);
×
631
  }
×
632

633
  freeifaddrs(ifaddr);
×
634
#endif
×
635
  return result;
×
636
}
×
637

638
#ifdef HAVE_GETIFADDRS
639
std::vector<ComboAddress> getListOfAddressesOfNetworkInterface(const std::string& itf)
640
{
×
641
  std::vector<ComboAddress> result;
×
642
  struct ifaddrs* ifaddr = nullptr;
×
643
  if (getifaddrs(&ifaddr) == -1) {
×
644
    return result;
×
645
  }
×
646

647
  for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
×
648
    if (ifa->ifa_name == nullptr || strcmp(ifa->ifa_name, itf.c_str()) != 0) {
×
649
      continue;
×
650
    }
×
651
    if (ifa->ifa_addr == nullptr || (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6)) {
×
652
      continue;
×
653
    }
×
654
    ComboAddress addr;
×
655
    try {
×
656
      addr.setSockaddr(ifa->ifa_addr, ifa->ifa_addr->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
×
657
    }
×
658
    catch (...) {
×
659
      continue;
×
660
    }
×
661

662
    result.push_back(addr);
×
663
  }
×
664

665
  freeifaddrs(ifaddr);
×
666
  return result;
×
667
}
×
668
#else
669
std::vector<ComboAddress> getListOfAddressesOfNetworkInterface(const std::string& /* itf */)
670
{
671
  std::vector<ComboAddress> result;
672
  return result;
673
}
674
#endif // HAVE_GETIFADDRS
675

676
#ifdef HAVE_GETIFADDRS
677
static uint8_t convertNetmaskToBits(const uint8_t* mask, socklen_t len)
678
{
×
679
  if (mask == nullptr || len > 16) {
×
680
    throw std::runtime_error("Invalid parameters passed to convertNetmaskToBits");
×
681
  }
×
682

683
  uint8_t result = 0;
×
684
  // for all bytes in the address (4 for IPv4, 16 for IPv6)
685
  for (size_t idx = 0; idx < len; idx++) {
×
686
    uint8_t byte = *(mask + idx); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
×
687
    // count the number of bits set
688
    while (byte > 0) {
×
689
      result += (byte & 1);
×
690
      byte >>= 1;
×
691
    }
×
692
  }
×
693
  return result;
×
694
}
×
695
#endif /* HAVE_GETIFADDRS */
696

697
#ifdef HAVE_GETIFADDRS
698
std::vector<Netmask> getListOfRangesOfNetworkInterface(const std::string& itf)
699
{
×
700
  std::vector<Netmask> result;
×
701
  struct ifaddrs* ifaddr = nullptr;
×
702
  if (getifaddrs(&ifaddr) == -1) {
×
703
    return result;
×
704
  }
×
705

706
  for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
×
707
    if (ifa->ifa_name == nullptr || strcmp(ifa->ifa_name, itf.c_str()) != 0) {
×
708
      continue;
×
709
    }
×
710
    if (ifa->ifa_addr == nullptr || (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6)) {
×
711
      continue;
×
712
    }
×
713
    ComboAddress addr;
×
714
    try {
×
715
      addr.setSockaddr(ifa->ifa_addr, ifa->ifa_addr->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
×
716
    }
×
717
    catch (...) {
×
718
      continue;
×
719
    }
×
720

721
    // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
722
    if (ifa->ifa_addr->sa_family == AF_INET) {
×
723
      const auto* netmask = reinterpret_cast<const struct sockaddr_in*>(ifa->ifa_netmask);
×
724
      uint8_t maskBits = convertNetmaskToBits(reinterpret_cast<const uint8_t*>(&netmask->sin_addr.s_addr), sizeof(netmask->sin_addr.s_addr));
×
725
      result.emplace_back(addr, maskBits);
×
726
    }
×
727
    else if (ifa->ifa_addr->sa_family == AF_INET6) {
×
728
      const auto* netmask = reinterpret_cast<const struct sockaddr_in6*>(ifa->ifa_netmask);
×
729
      uint8_t maskBits = convertNetmaskToBits(reinterpret_cast<const uint8_t*>(&netmask->sin6_addr.s6_addr), sizeof(netmask->sin6_addr.s6_addr));
×
730
      result.emplace_back(addr, maskBits);
×
731
    }
×
732
    // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
733
  }
×
734

735
  freeifaddrs(ifaddr);
×
736
  return result;
×
737
}
×
738
#else
739
std::vector<Netmask> getListOfRangesOfNetworkInterface(const std::string& /* itf */)
740
{
741
  std::vector<Netmask> result;
742
  return result;
743
}
744
#endif // HAVE_GETIFADDRS
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