Coverage Report

Created: 2025-07-04 06:59

/src/pdns/pdns/dnsdistdist/iputils.cc
Line
Count
Source (jump to first uncovered line)
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
0
{
40
0
  throw runtime_error(error);
41
0
}
42
43
static void NetworkErr(const std::string& error)
44
0
{
45
0
  throw NetworkError(error);
46
0
}
47
48
int SSocket(int family, int type, int flags)
49
0
{
50
0
  int ret = socket(family, type, flags);
51
0
  if (ret < 0) {
52
0
    RuntimeError("creating socket of type " + std::to_string(family) + ": " + stringerror());
53
0
  }
54
0
  return ret;
55
0
}
56
57
int SConnect(int sockfd, const ComboAddress& remote)
58
0
{
59
0
  int ret = connect(sockfd, reinterpret_cast<const struct sockaddr*>(&remote), remote.getSocklen()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
60
0
  if (ret < 0) {
61
0
    int savederrno = errno;
62
0
    RuntimeError("connecting socket to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
63
0
  }
64
0
  return ret;
65
0
}
66
67
int SConnectWithTimeout(int sockfd, const ComboAddress& remote, const struct timeval& timeout)
68
0
{
69
0
  int ret = connect(sockfd, reinterpret_cast<const struct sockaddr*>(&remote), remote.getSocklen()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
70
0
  if (ret < 0) {
71
0
    int savederrno = errno;
72
0
    if (savederrno == EINPROGRESS) {
73
0
      if (timeout <= timeval{0, 0}) {
74
0
        return savederrno;
75
0
      }
76
77
      /* we wait until the connection has been established */
78
0
      bool error = false;
79
0
      bool disconnected = false;
80
0
      int res = waitForRWData(sockfd, false, static_cast<int>(timeout.tv_sec), static_cast<int>(timeout.tv_usec), &error, &disconnected);
81
0
      if (res == 1) {
82
0
        if (error) {
83
0
          savederrno = 0;
84
0
          socklen_t errlen = sizeof(savederrno);
85
0
          if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)&savederrno, &errlen) == 0) {
86
0
            NetworkErr("connecting to " + remote.toStringWithPort() + " failed: " + stringerror(savederrno));
87
0
          }
88
0
          else {
89
0
            NetworkErr("connecting to " + remote.toStringWithPort() + " failed");
90
0
          }
91
0
        }
92
0
        if (disconnected) {
93
0
          NetworkErr(remote.toStringWithPort() + " closed the connection");
94
0
        }
95
0
        return 0;
96
0
      }
97
0
      if (res == 0) {
98
0
        NetworkErr("timeout while connecting to " + remote.toStringWithPort());
99
0
      }
100
0
      else if (res < 0) {
101
0
        savederrno = errno;
102
0
        NetworkErr("waiting to connect to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
103
0
      }
104
0
    }
105
0
    else {
106
0
      NetworkErr("connecting to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
107
0
    }
108
0
  }
109
110
0
  return 0;
111
0
}
112
113
int SBind(int sockfd, const ComboAddress& local)
114
0
{
115
0
  int ret = bind(sockfd, reinterpret_cast<const struct sockaddr*>(&local), local.getSocklen()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
116
0
  if (ret < 0) {
117
0
    int savederrno = errno;
118
0
    RuntimeError("binding socket to " + local.toStringWithPort() + ": " + stringerror(savederrno));
119
0
  }
120
0
  return ret;
121
0
}
122
123
int SAccept(int sockfd, ComboAddress& remote)
124
0
{
125
0
  socklen_t remlen = remote.getSocklen();
126
127
0
  int ret = accept(sockfd, reinterpret_cast<struct sockaddr*>(&remote), &remlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
128
0
  if (ret < 0) {
129
0
    RuntimeError("accepting new connection on socket: " + stringerror());
130
0
  }
131
0
  return ret;
132
0
}
133
134
int SListen(int sockfd, int limit)
135
0
{
136
0
  int ret = listen(sockfd, limit);
137
0
  if (ret < 0) {
138
0
    RuntimeError("setting socket to listen: " + stringerror());
139
0
  }
140
0
  return ret;
141
0
}
142
143
int SSetsockopt(int sockfd, int level, int opname, int value)
144
0
{
145
0
  int ret = setsockopt(sockfd, level, opname, &value, sizeof(value));
146
0
  if (ret < 0) {
147
0
    RuntimeError("setsockopt for level " + std::to_string(level) + " and opname " + std::to_string(opname) + " to " + std::to_string(value) + " failed: " + stringerror());
148
0
  }
149
0
  return ret;
150
0
}
151
152
void setSocketIgnorePMTU([[maybe_unused]] int sockfd, [[maybe_unused]] int family)
153
0
{
154
0
  if (family == AF_INET) { // NOLINT(bugprone-branch-clone)
155
0
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
156
0
#ifdef IP_PMTUDISC_OMIT
157
    /* Linux 3.15+ has IP_PMTUDISC_OMIT, which discards PMTU information to prevent
158
       poisoning, but still allows fragmentation if the packet size exceeds the
159
       outgoing interface MTU, which is good.
160
    */
161
0
    try {
162
0
      SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_OMIT);
163
0
      return;
164
0
    }
165
0
    catch (const std::exception& e) {
166
      /* failed, let's try IP_PMTUDISC_DONT instead */
167
0
    }
168
0
#endif /* IP_PMTUDISC_OMIT */
169
170
    /* IP_PMTUDISC_DONT disables Path MTU discovery */
171
0
    SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
172
0
#endif /* defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) */
173
0
  }
174
0
  else {
175
0
#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT)
176
0
#ifdef IPV6_PMTUDISC_OMIT
177
    /* Linux 3.15+ has IPV6_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
0
    try {
182
0
      SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_OMIT);
183
0
      return;
184
0
    }
185
0
    catch (const std::exception& e) {
186
      /* failed, let's try IP_PMTUDISC_DONT instead */
187
0
    }
188
0
#endif /* IPV6_PMTUDISC_OMIT */
189
190
    /* IPV6_PMTUDISC_DONT disables Path MTU discovery */
191
0
    SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DONT);
192
0
#endif /* defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT) */
193
0
  }
194
0
}
195
196
void setSocketForcePMTU([[maybe_unused]] int sockfd, [[maybe_unused]] int family)
197
0
{
198
0
  if (family == AF_INET) {
199
0
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
200
    /* IP_PMTUDISC_DO enables Path MTU discovery and prevents fragmentation */
201
0
    SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DO);
202
#elif defined(IP_DONTFRAG)
203
    /* at least this prevents fragmentation */
204
    SSetsockopt(sockfd, IPPROTO_IP, IP_DONTFRAG, 1);
205
#endif /* defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO) */
206
0
  }
207
0
  else {
208
0
#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
209
    /* IPV6_PMTUDISC_DO enables Path MTU discovery and prevents fragmentation */
210
0
    SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DO);
211
#elif defined(IPV6_DONTFRAG)
212
    /* at least this prevents fragmentation */
213
    SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_DONTFRAG, 1);
214
#endif /* defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO) */
215
0
  }
216
0
}
217
218
bool setReusePort(int sockfd)
219
0
{
220
#if defined(SO_REUSEPORT_LB)
221
  try {
222
    SSetsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT_LB, 1);
223
    return true;
224
  }
225
  catch (const std::exception& e) {
226
    return false;
227
  }
228
#elif defined(SO_REUSEPORT)
229
0
  try {
230
0
    SSetsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, 1);
231
0
    return true;
232
0
  }
233
0
  catch (const std::exception& e) {
234
0
    return false;
235
0
  }
236
0
#endif
237
0
  return false;
238
0
}
239
240
bool HarvestTimestamp(struct msghdr* msgh, struct timeval* timeval)
241
0
{
242
  // NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast, cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-type-reinterpret-cast)
243
0
#ifdef SO_TIMESTAMP
244
0
  struct cmsghdr* cmsg{};
245
0
  for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != nullptr; cmsg = CMSG_NXTHDR(msgh, cmsg)) {
246
0
    if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMP || cmsg->cmsg_type == SCM_TIMESTAMP) && CMSG_LEN(sizeof(*timeval)) == cmsg->cmsg_len) {
247
0
      memcpy(timeval, CMSG_DATA(cmsg), sizeof(*timeval));
248
0
      return true;
249
0
    }
250
0
  }
251
0
#endif
252
0
  return false;
253
0
}
254
bool HarvestDestinationAddress(const struct msghdr* msgh, ComboAddress* destination)
255
0
{
256
0
  destination->reset();
257
#ifdef __NetBSD__
258
  struct cmsghdr* cmsg{};
259
#else
260
0
  const struct cmsghdr* cmsg{};
261
0
#endif
262
0
  for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != nullptr; cmsg = CMSG_NXTHDR(const_cast<struct msghdr*>(msgh), const_cast<struct cmsghdr*>(cmsg))) {
263
0
#if defined(IP_PKTINFO)
264
0
    if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
265
0
      const auto* ptr = reinterpret_cast<const struct in_pktinfo*>(CMSG_DATA(cmsg));
266
0
      destination->sin4.sin_addr = ptr->ipi_addr;
267
0
      destination->sin4.sin_family = AF_INET;
268
0
      return true;
269
0
    }
270
#elif defined(IP_RECVDSTADDR)
271
    if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) {
272
      const auto* ptr = reinterpret_cast<const struct in_addr*>(CMSG_DATA(cmsg));
273
      destination->sin4.sin_addr = *ptr;
274
      destination->sin4.sin_family = AF_INET;
275
      return true;
276
    }
277
#endif
278
279
0
    if ((cmsg->cmsg_level == IPPROTO_IPV6) && (cmsg->cmsg_type == IPV6_PKTINFO)) {
280
0
      const auto* ptr = reinterpret_cast<const struct in6_pktinfo*>(CMSG_DATA(cmsg));
281
0
      destination->sin6.sin6_addr = ptr->ipi6_addr;
282
0
      destination->sin4.sin_family = AF_INET6;
283
0
      return true;
284
0
    }
285
0
  }
286
0
  return false;
287
  // NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast, cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-type-reinterpret-cast)
288
0
}
289
290
bool IsAnyAddress(const ComboAddress& addr)
291
0
{
292
0
  if (addr.sin4.sin_family == AF_INET) {
293
0
    return addr.sin4.sin_addr.s_addr == 0;
294
0
  }
295
0
  if (addr.sin4.sin_family == AF_INET6) {
296
0
    return memcmp(&addr.sin6.sin6_addr, &in6addr_any, sizeof(addr.sin6.sin6_addr)) == 0;
297
0
  }
298
0
  return false;
299
0
}
300
301
int sendOnNBSocket(int fileDesc, const struct msghdr* msgh)
302
0
{
303
0
  int sendErr = 0;
304
#ifdef __OpenBSD__
305
  // OpenBSD can and does return EAGAIN on non-blocking datagram sockets
306
  for (int i = 0; i < 10; i++) { // Arbitrary upper bound
307
    if (sendmsg(fileDesc, msgh, 0) != -1) {
308
      sendErr = 0;
309
      break;
310
    }
311
    sendErr = errno;
312
    if (sendErr != EAGAIN) {
313
      break;
314
    }
315
  }
316
#else
317
0
  if (sendmsg(fileDesc, msgh, 0) == -1) {
318
0
    sendErr = errno;
319
0
  }
320
0
#endif
321
0
  return sendErr;
322
0
}
323
324
// be careful: when using this for receive purposes, make sure addr->sin4.sin_family is set appropriately so getSocklen works!
325
// be careful: when using this function for *send* purposes, be sure to set cbufsize to 0!
326
// be careful: if you don't call addCMsgSrcAddr after fillMSGHdr, make sure to set msg_control to NULL
327
void fillMSGHdr(struct msghdr* msgh, struct iovec* iov, cmsgbuf_aligned* cbuf, size_t cbufsize, char* data, size_t datalen, ComboAddress* addr)
328
0
{
329
0
  iov->iov_base = data;
330
0
  iov->iov_len = datalen;
331
332
0
  memset(msgh, 0, sizeof(struct msghdr));
333
334
0
  msgh->msg_control = cbuf;
335
0
  msgh->msg_controllen = cbufsize;
336
0
  msgh->msg_name = addr;
337
0
  msgh->msg_namelen = addr->getSocklen();
338
0
  msgh->msg_iov = iov;
339
0
  msgh->msg_iovlen = 1;
340
0
  msgh->msg_flags = 0;
341
0
}
342
343
// warning: various parts of PowerDNS assume 'truncate' will never throw
344
void ComboAddress::truncate(unsigned int bits) noexcept
345
63
{
346
63
  uint8_t* start{};
347
63
  int len = 4;
348
63
  if (sin4.sin_family == AF_INET) {
349
19
    if (bits >= 32) {
350
10
      return;
351
10
    }
352
9
    start = reinterpret_cast<uint8_t*>(&sin4.sin_addr.s_addr); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
353
9
    len = 4;
354
9
  }
355
44
  else {
356
44
    if (bits >= 128) {
357
8
      return;
358
8
    }
359
36
    start = reinterpret_cast<uint8_t*>(&sin6.sin6_addr.s6_addr); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
360
36
    len = 16;
361
36
  }
362
363
45
  auto tozero = len * 8 - bits; // if set to 22, this will clear 1 byte, as it should
364
365
45
  memset(start + len - tozero / 8, 0, tozero / 8); // blot out the whole bytes on the right NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
366
367
45
  auto bitsleft = tozero % 8; // 2 bits left to clear
368
369
  // a b c d, to truncate to 22 bits, we just zeroed 'd' and need to zero 2 bits from c
370
  // so and by '11111100', which is ~((1<<2)-1)  = ~3
371
45
  uint8_t* place = start + len - 1 - tozero / 8; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
372
45
  *place &= (~((1 << bitsleft) - 1));
373
45
}
374
375
size_t sendMsgWithOptions(int socketDesc, const void* buffer, size_t len, const ComboAddress* dest, const ComboAddress* local, unsigned int localItf, int flags)
376
0
{
377
0
  msghdr msgh{};
378
0
  iovec iov{};
379
0
  cmsgbuf_aligned cbuf;
380
381
  /* Set up iov and msgh structures. */
382
0
  memset(&msgh, 0, sizeof(msgh));
383
0
  msgh.msg_control = nullptr;
384
0
  msgh.msg_controllen = 0;
385
0
  if (dest != nullptr) {
386
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-type-const-cast): it's the API
387
0
    msgh.msg_name = reinterpret_cast<void*>(const_cast<ComboAddress*>(dest));
388
0
    msgh.msg_namelen = dest->getSocklen();
389
0
  }
390
0
  else {
391
0
    msgh.msg_name = nullptr;
392
0
    msgh.msg_namelen = 0;
393
0
  }
394
395
0
  msgh.msg_flags = 0;
396
397
0
  if (local != nullptr && local->sin4.sin_family != 0) {
398
0
    addCMsgSrcAddr(&msgh, &cbuf, local, static_cast<int>(localItf));
399
0
  }
400
401
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast): it's the API
402
0
  iov.iov_base = const_cast<void*>(buffer);
403
0
  iov.iov_len = len;
404
0
  msgh.msg_iov = &iov;
405
0
  msgh.msg_iovlen = 1;
406
0
  msgh.msg_flags = 0;
407
408
0
  size_t sent = 0;
409
0
#ifdef MSG_FASTOPEN
410
0
  bool firstTry = true;
411
0
#endif
412
413
0
  do {
414
415
0
#ifdef MSG_FASTOPEN
416
0
    if ((flags & MSG_FASTOPEN) != 0 && !firstTry) {
417
0
      flags &= ~MSG_FASTOPEN;
418
0
    }
419
0
#endif /* MSG_FASTOPEN */
420
421
0
    ssize_t res = sendmsg(socketDesc, &msgh, flags);
422
423
0
    if (res > 0) {
424
0
      auto written = static_cast<size_t>(res);
425
0
      sent += written;
426
427
0
      if (sent == len) {
428
0
        return sent;
429
0
      }
430
431
      /* partial write */
432
0
#ifdef MSG_FASTOPEN
433
0
      firstTry = false;
434
0
#endif
435
0
      iov.iov_len -= written;
436
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic): it's the API
437
0
      iov.iov_base = reinterpret_cast<void*>(reinterpret_cast<char*>(iov.iov_base) + written);
438
0
    }
439
0
    else if (res == 0) {
440
0
      return res;
441
0
    }
442
0
    else if (res == -1) {
443
0
      int err = errno;
444
0
      if (err == EINTR) {
445
0
        continue;
446
0
      }
447
0
      if (err == EAGAIN || err == EWOULDBLOCK || err == EINPROGRESS || err == ENOTCONN) {
448
        /* EINPROGRESS might happen with non blocking socket,
449
           especially with TCP Fast Open */
450
0
        return sent;
451
0
      }
452
0
      unixDie("failed in sendMsgWithOptions");
453
0
    }
454
0
  } while (true);
455
456
0
  return 0;
457
0
}
458
459
template class NetmaskTree<bool, Netmask>;
460
461
/* requires a non-blocking socket.
462
   On Linux, we could use MSG_DONTWAIT on a blocking socket
463
   but this is not portable.
464
*/
465
bool isTCPSocketUsable(int sock)
466
0
{
467
0
  int err = 0;
468
0
  char buf = '\0';
469
0
  size_t buf_size = sizeof(buf);
470
471
0
  do {
472
0
    ssize_t got = recv(sock, &buf, buf_size, MSG_PEEK);
473
474
0
    if (got > 0) {
475
      /* socket is usable, some data is even waiting to be read */
476
0
      return true;
477
0
    }
478
0
    if (got == 0) {
479
      /* other end has closed the socket */
480
0
      return false;
481
0
    }
482
0
    err = errno;
483
0
    if (err == EAGAIN || err == EWOULDBLOCK) {
484
      /* socket is usable, no data waiting */
485
0
      return true;
486
0
    }
487
0
    if (err != EINTR) {
488
      /* something is wrong, could be ECONNRESET,
489
         ENOTCONN, EPIPE, but anyway this socket is
490
         not usable. */
491
0
      return false;
492
0
    }
493
0
  } while (err == EINTR);
494
495
0
  return false;
496
0
}
497
/* mission in life: parse four cases
498
   1) [2002::1]:53
499
   2) 1.2.3.4
500
   3) 1.2.3.4:5300
501
   4) 2001::1 no port allowed
502
*/
503
504
ComboAddress parseIPAndPort(const std::string& input, uint16_t port)
505
0
{
506
0
  if (input[0] == '[') { // case 1
507
0
    auto both = splitField(input.substr(1), ']');
508
0
    return ComboAddress(both.first, both.second.empty() ? port : pdns::checked_stoi<uint16_t>(both.second.substr(1)));
509
0
  }
510
511
0
  string::size_type count = 0;
512
0
  for (char chr : input) {
513
0
    if (chr == ':') {
514
0
      count++;
515
0
    }
516
0
    if (count > 1) {
517
0
      break;
518
0
    }
519
0
  }
520
0
  switch (count) {
521
0
  case 0: // case 2
522
0
    return ComboAddress(input, port);
523
0
  case 1: { // case 3
524
0
    string::size_type cpos = input.rfind(':');
525
0
    pair<std::string, std::string> both;
526
0
    both.first = input.substr(0, cpos);
527
0
    both.second = input.substr(cpos + 1);
528
529
0
    auto newport = pdns::checked_stoi<uint16_t>(both.second);
530
0
    return ComboAddress(both.first, newport);
531
0
  }
532
0
  default: // case 4
533
0
    return ComboAddress(input, port);
534
0
  }
535
0
}
536
537
void setSocketBuffer(int fileDesc, int optname, uint32_t size)
538
0
{
539
0
  uint32_t psize = 0;
540
0
  socklen_t len = sizeof(psize);
541
542
0
  if (getsockopt(fileDesc, SOL_SOCKET, optname, &psize, &len) != 0) {
543
0
    throw std::runtime_error("Unable to retrieve socket buffer size:" + stringerror());
544
0
  }
545
0
  if (psize >= size) {
546
0
    return;
547
0
  }
548
0
  if (setsockopt(fileDesc, SOL_SOCKET, optname, &size, sizeof(size)) != 0) {
549
0
    throw std::runtime_error("Unable to raise socket buffer size to " + std::to_string(size) + ": " + stringerror());
550
0
  }
551
0
}
552
553
void setSocketReceiveBuffer(int fileDesc, uint32_t size)
554
0
{
555
0
  setSocketBuffer(fileDesc, SO_RCVBUF, size);
556
0
}
557
558
void setSocketSendBuffer(int fileDesc, uint32_t size)
559
0
{
560
0
  setSocketBuffer(fileDesc, SO_SNDBUF, size);
561
0
}
562
563
#ifdef __linux__
564
static uint32_t raiseSocketBufferToMax(int socket, int optname, const std::string& readMaxFromFile)
565
0
{
566
0
  std::ifstream ifs(readMaxFromFile);
567
0
  if (ifs) {
568
0
    std::string line;
569
0
    if (getline(ifs, line)) {
570
0
      auto max = pdns::checked_stoi<uint32_t>(line);
571
0
      setSocketBuffer(socket, optname, max);
572
0
      return max;
573
0
    }
574
0
  }
575
0
  return 0;
576
0
}
577
#endif
578
579
uint32_t raiseSocketReceiveBufferToMax([[maybe_unused]] int socket)
580
0
{
581
0
#ifdef __linux__
582
0
  return raiseSocketBufferToMax(socket, SO_RCVBUF, "/proc/sys/net/core/rmem_max");
583
#else
584
  return 0;
585
#endif
586
0
}
587
588
uint32_t raiseSocketSendBufferToMax([[maybe_unused]] int socket)
589
0
{
590
0
#ifdef __linux__
591
0
  return raiseSocketBufferToMax(socket, SO_SNDBUF, "/proc/sys/net/core/wmem_max");
592
#else
593
  return 0;
594
#endif
595
0
}
596
597
std::set<std::string> getListOfNetworkInterfaces()
598
0
{
599
0
  std::set<std::string> result;
600
0
#ifdef HAVE_GETIFADDRS
601
0
  struct ifaddrs* ifaddr{};
602
0
  if (getifaddrs(&ifaddr) == -1) {
603
0
    return result;
604
0
  }
605
606
0
  for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
607
0
    if (ifa->ifa_name == nullptr) {
608
0
      continue;
609
0
    }
610
0
    result.insert(ifa->ifa_name);
611
0
  }
612
613
0
  freeifaddrs(ifaddr);
614
0
#endif
615
0
  return result;
616
0
}
617
618
#ifdef HAVE_GETIFADDRS
619
std::vector<ComboAddress> getListOfAddressesOfNetworkInterface(const std::string& itf)
620
0
{
621
0
  std::vector<ComboAddress> result;
622
0
  struct ifaddrs* ifaddr = nullptr;
623
0
  if (getifaddrs(&ifaddr) == -1) {
624
0
    return result;
625
0
  }
626
627
0
  for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
628
0
    if (ifa->ifa_name == nullptr || strcmp(ifa->ifa_name, itf.c_str()) != 0) {
629
0
      continue;
630
0
    }
631
0
    if (ifa->ifa_addr == nullptr || (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6)) {
632
0
      continue;
633
0
    }
634
0
    ComboAddress addr;
635
0
    try {
636
0
      addr.setSockaddr(ifa->ifa_addr, ifa->ifa_addr->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
637
0
    }
638
0
    catch (...) {
639
0
      continue;
640
0
    }
641
642
0
    result.push_back(addr);
643
0
  }
644
645
0
  freeifaddrs(ifaddr);
646
0
  return result;
647
0
}
648
#else
649
std::vector<ComboAddress> getListOfAddressesOfNetworkInterface(const std::string& /* itf */)
650
{
651
  std::vector<ComboAddress> result;
652
  return result;
653
}
654
#endif // HAVE_GETIFADDRS
655
656
#ifdef HAVE_GETIFADDRS
657
static uint8_t convertNetmaskToBits(const uint8_t* mask, socklen_t len)
658
0
{
659
0
  if (mask == nullptr || len > 16) {
660
0
    throw std::runtime_error("Invalid parameters passed to convertNetmaskToBits");
661
0
  }
662
663
0
  uint8_t result = 0;
664
  // for all bytes in the address (4 for IPv4, 16 for IPv6)
665
0
  for (size_t idx = 0; idx < len; idx++) {
666
0
    uint8_t byte = *(mask + idx); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
667
    // count the number of bits set
668
0
    while (byte > 0) {
669
0
      result += (byte & 1);
670
0
      byte >>= 1;
671
0
    }
672
0
  }
673
0
  return result;
674
0
}
675
#endif /* HAVE_GETIFADDRS */
676
677
#ifdef HAVE_GETIFADDRS
678
std::vector<Netmask> getListOfRangesOfNetworkInterface(const std::string& itf)
679
0
{
680
0
  std::vector<Netmask> result;
681
0
  struct ifaddrs* ifaddr = nullptr;
682
0
  if (getifaddrs(&ifaddr) == -1) {
683
0
    return result;
684
0
  }
685
686
0
  for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
687
0
    if (ifa->ifa_name == nullptr || strcmp(ifa->ifa_name, itf.c_str()) != 0) {
688
0
      continue;
689
0
    }
690
0
    if (ifa->ifa_addr == nullptr || (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6)) {
691
0
      continue;
692
0
    }
693
0
    ComboAddress addr;
694
0
    try {
695
0
      addr.setSockaddr(ifa->ifa_addr, ifa->ifa_addr->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
696
0
    }
697
0
    catch (...) {
698
0
      continue;
699
0
    }
700
701
    // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
702
0
    if (ifa->ifa_addr->sa_family == AF_INET) {
703
0
      const auto* netmask = reinterpret_cast<const struct sockaddr_in*>(ifa->ifa_netmask);
704
0
      uint8_t maskBits = convertNetmaskToBits(reinterpret_cast<const uint8_t*>(&netmask->sin_addr.s_addr), sizeof(netmask->sin_addr.s_addr));
705
0
      result.emplace_back(addr, maskBits);
706
0
    }
707
0
    else if (ifa->ifa_addr->sa_family == AF_INET6) {
708
0
      const auto* netmask = reinterpret_cast<const struct sockaddr_in6*>(ifa->ifa_netmask);
709
0
      uint8_t maskBits = convertNetmaskToBits(reinterpret_cast<const uint8_t*>(&netmask->sin6_addr.s6_addr), sizeof(netmask->sin6_addr.s6_addr));
710
0
      result.emplace_back(addr, maskBits);
711
0
    }
712
    // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
713
0
  }
714
715
0
  freeifaddrs(ifaddr);
716
0
  return result;
717
0
}
718
#else
719
std::vector<Netmask> getListOfRangesOfNetworkInterface(const std::string& /* itf */)
720
{
721
  std::vector<Netmask> result;
722
  return result;
723
}
724
#endif // HAVE_GETIFADDRS