Coverage Report

Created: 2026-03-07 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pdns/pdns/dnsdistdist/iputils.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
#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
static int doConnect(int sockfd, [[maybe_unused]] bool fastopen, const ComboAddress& remote)
58
0
{
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
0
    return connect(sockfd, reinterpret_cast<const struct sockaddr*>(&remote), remote.getSocklen()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
72
#ifdef CONNECTX_FASTOPEN
73
  }
74
#endif
75
0
}
76
77
int SConnect(int sockfd, bool fastopen, const ComboAddress& remote)
78
0
{
79
0
  int ret = doConnect(sockfd, fastopen, remote);
80
0
  if (ret < 0) {
81
0
    int savederrno = errno;
82
0
    RuntimeError("connecting socket to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
83
0
  }
84
0
  return ret;
85
0
}
86
87
int SConnectWithTimeout(int sockfd, bool fastopen, const ComboAddress& remote, const struct timeval& timeout)
88
0
{
89
0
  int ret = doConnect(sockfd, fastopen, remote);
90
0
  if (ret < 0) {
91
0
    int savederrno = errno;
92
0
    if (savederrno == EINPROGRESS) {
93
0
      if (timeout <= timeval{0, 0}) {
94
0
        return savederrno;
95
0
      }
96
97
      /* we wait until the connection has been established */
98
0
      bool error = false;
99
0
      bool disconnected = false;
100
0
      int res = waitForRWData(sockfd, false, timeout, &error, &disconnected);
101
0
      if (res == 1) {
102
0
        if (error) {
103
0
          savederrno = 0;
104
0
          socklen_t errlen = sizeof(savederrno);
105
0
          if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)&savederrno, &errlen) == 0) {
106
0
            NetworkErr("connecting to " + remote.toStringWithPort() + " failed: " + stringerror(savederrno));
107
0
          }
108
0
          else {
109
0
            NetworkErr("connecting to " + remote.toStringWithPort() + " failed");
110
0
          }
111
0
        }
112
0
        if (disconnected) {
113
0
          NetworkErr(remote.toStringWithPort() + " closed the connection");
114
0
        }
115
0
        return 0;
116
0
      }
117
0
      if (res == 0) {
118
0
        NetworkErr("timeout while connecting to " + remote.toStringWithPort());
119
0
      }
120
0
      else if (res < 0) {
121
0
        savederrno = errno;
122
0
        NetworkErr("waiting to connect to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
123
0
      }
124
0
    }
125
0
    else {
126
0
      NetworkErr("connecting to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
127
0
    }
128
0
  }
129
130
0
  return 0;
131
0
}
132
133
int SBind(int sockfd, const ComboAddress& local)
134
0
{
135
0
  int ret = bind(sockfd, reinterpret_cast<const struct sockaddr*>(&local), local.getSocklen()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
136
0
  if (ret < 0) {
137
0
    int savederrno = errno;
138
0
    RuntimeError("binding socket to " + local.toStringWithPort() + ": " + stringerror(savederrno));
139
0
  }
140
0
  return ret;
141
0
}
142
143
int SAccept(int sockfd, ComboAddress& remote)
144
0
{
145
0
  socklen_t remlen = remote.getSocklen();
146
147
0
  int ret = accept(sockfd, reinterpret_cast<struct sockaddr*>(&remote), &remlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
148
0
  if (ret < 0) {
149
0
    RuntimeError("accepting new connection on socket: " + stringerror());
150
0
  }
151
0
  return ret;
152
0
}
153
154
int SListen(int sockfd, int limit)
155
0
{
156
0
  int ret = listen(sockfd, limit);
157
0
  if (ret < 0) {
158
0
    RuntimeError("setting socket to listen: " + stringerror());
159
0
  }
160
0
  return ret;
161
0
}
162
163
int SSetsockopt(int sockfd, int level, int opname, int value)
164
0
{
165
0
  int ret = setsockopt(sockfd, level, opname, &value, sizeof(value));
166
0
  if (ret < 0) {
167
0
    RuntimeError("setsockopt for level " + std::to_string(level) + " and opname " + std::to_string(opname) + " to " + std::to_string(value) + " failed: " + stringerror());
168
0
  }
169
0
  return ret;
170
0
}
171
172
void setSocketIgnorePMTU([[maybe_unused]] int sockfd, [[maybe_unused]] int family)
173
0
{
174
0
  if (family == AF_INET) { // NOLINT(bugprone-branch-clone)
175
0
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
176
0
#ifdef IP_PMTUDISC_OMIT
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
0
    try {
182
0
      SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_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 /* IP_PMTUDISC_OMIT */
189
190
    /* IP_PMTUDISC_DONT disables Path MTU discovery */
191
0
    SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
192
0
#endif /* defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) */
193
0
  }
194
0
  else {
195
0
#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT)
196
0
#ifdef IPV6_PMTUDISC_OMIT
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
0
    try {
202
0
      SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_OMIT);
203
0
      return;
204
0
    }
205
0
    catch (const std::exception& e) {
206
      /* failed, let's try IP_PMTUDISC_DONT instead */
207
0
    }
208
0
#endif /* IPV6_PMTUDISC_OMIT */
209
210
    /* IPV6_PMTUDISC_DONT disables Path MTU discovery */
211
0
    SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DONT);
212
0
#endif /* defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT) */
213
0
  }
214
0
}
215
216
void setSocketForcePMTU([[maybe_unused]] int sockfd, [[maybe_unused]] int family)
217
0
{
218
0
  if (family == AF_INET) {
219
0
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
220
    /* IP_PMTUDISC_DO enables Path MTU discovery and prevents fragmentation */
221
0
    SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DO);
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
0
  }
227
0
  else {
228
0
#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
229
    /* IPV6_PMTUDISC_DO enables Path MTU discovery and prevents fragmentation */
230
0
    SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DO);
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
0
  }
236
0
}
237
238
bool setReusePort(int sockfd)
239
0
{
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
0
  try {
250
0
    SSetsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, 1);
251
0
    return true;
252
0
  }
253
0
  catch (const std::exception& e) {
254
0
    return false;
255
0
  }
256
0
#endif
257
0
  return false;
258
0
}
259
260
bool HarvestTimestamp(struct msghdr* msgh, struct timeval* timeval)
261
0
{
262
  // NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast, cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-type-reinterpret-cast)
263
0
#ifdef SO_TIMESTAMP
264
0
  struct cmsghdr* cmsg{};
265
0
  for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != nullptr; cmsg = CMSG_NXTHDR(msgh, cmsg)) {
266
0
    if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMP || cmsg->cmsg_type == SCM_TIMESTAMP) && CMSG_LEN(sizeof(*timeval)) == cmsg->cmsg_len) {
267
0
      memcpy(timeval, CMSG_DATA(cmsg), sizeof(*timeval));
268
0
      return true;
269
0
    }
270
0
  }
271
0
#endif
272
0
  return false;
273
0
}
274
bool HarvestDestinationAddress(const struct msghdr* msgh, ComboAddress* destination)
275
0
{
276
0
  destination->reset();
277
#ifdef __NetBSD__
278
  struct cmsghdr* cmsg{};
279
#else
280
0
  const struct cmsghdr* cmsg{};
281
0
#endif
282
0
  for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != nullptr; cmsg = CMSG_NXTHDR(const_cast<struct msghdr*>(msgh), const_cast<struct cmsghdr*>(cmsg))) {
283
0
#if defined(IP_PKTINFO)
284
0
    if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
285
0
      const auto* ptr = reinterpret_cast<const struct in_pktinfo*>(CMSG_DATA(cmsg));
286
0
      destination->sin4.sin_addr = ptr->ipi_addr;
287
0
      destination->sin4.sin_family = AF_INET;
288
0
      return true;
289
0
    }
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
0
    if ((cmsg->cmsg_level == IPPROTO_IPV6) && (cmsg->cmsg_type == IPV6_PKTINFO)) {
300
0
      const auto* ptr = reinterpret_cast<const struct in6_pktinfo*>(CMSG_DATA(cmsg));
301
0
      destination->sin6.sin6_addr = ptr->ipi6_addr;
302
0
      destination->sin4.sin_family = AF_INET6;
303
0
      return true;
304
0
    }
305
0
  }
306
0
  return false;
307
  // NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast, cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-type-reinterpret-cast)
308
0
}
309
310
bool IsAnyAddress(const ComboAddress& addr)
311
0
{
312
0
  if (addr.sin4.sin_family == AF_INET) {
313
0
    return addr.sin4.sin_addr.s_addr == 0;
314
0
  }
315
0
  if (addr.sin4.sin_family == AF_INET6) {
316
0
    return memcmp(&addr.sin6.sin6_addr, &in6addr_any, sizeof(addr.sin6.sin6_addr)) == 0;
317
0
  }
318
0
  return false;
319
0
}
320
321
int sendOnNBSocket(int fileDesc, const struct msghdr* msgh)
322
0
{
323
0
  int sendErr = 0;
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
0
  if (sendmsg(fileDesc, msgh, 0) == -1) {
338
0
    sendErr = errno;
339
0
  }
340
0
#endif
341
0
  return sendErr;
342
0
}
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
0
{
349
0
  iov->iov_base = data;
350
0
  iov->iov_len = datalen;
351
352
0
  memset(msgh, 0, sizeof(struct msghdr));
353
354
0
  msgh->msg_control = cbuf;
355
0
  msgh->msg_controllen = cbufsize;
356
0
  msgh->msg_name = addr;
357
0
  msgh->msg_namelen = addr->getSocklen();
358
0
  msgh->msg_iov = iov;
359
0
  msgh->msg_iovlen = 1;
360
0
  msgh->msg_flags = 0;
361
0
}
362
363
// warning: various parts of PowerDNS assume 'truncate' will never throw
364
void ComboAddress::truncate(unsigned int bits) noexcept
365
69
{
366
69
  uint8_t* start{};
367
69
  int len = 4;
368
69
  if (sin4.sin_family == AF_INET) {
369
24
    if (bits >= 32) {
370
11
      return;
371
11
    }
372
13
    start = reinterpret_cast<uint8_t*>(&sin4.sin_addr.s_addr); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
373
13
    len = 4;
374
13
  }
375
45
  else {
376
45
    if (bits >= 128) {
377
10
      return;
378
10
    }
379
35
    start = reinterpret_cast<uint8_t*>(&sin6.sin6_addr.s6_addr); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
380
35
    len = 16;
381
35
  }
382
383
48
  auto tozero = len * 8 - bits; // if set to 22, this will clear 1 byte, as it should
384
385
48
  memset(start + len - tozero / 8, 0, tozero / 8); // blot out the whole bytes on the right NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
386
387
48
  auto bitsleft = tozero % 8; // 2 bits left to clear
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
48
  uint8_t* place = start + len - 1 - tozero / 8; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
392
48
  *place &= (~((1 << bitsleft) - 1));
393
48
}
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
0
{
397
0
  msghdr msgh{};
398
0
  iovec iov{};
399
0
  cmsgbuf_aligned cbuf;
400
401
  /* Set up iov and msgh structures. */
402
0
  memset(&msgh, 0, sizeof(msgh));
403
0
  msgh.msg_control = nullptr;
404
0
  msgh.msg_controllen = 0;
405
0
  if (dest != nullptr) {
406
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-type-const-cast): it's the API
407
0
    msgh.msg_name = reinterpret_cast<void*>(const_cast<ComboAddress*>(dest));
408
0
    msgh.msg_namelen = dest->getSocklen();
409
0
  }
410
0
  else {
411
0
    msgh.msg_name = nullptr;
412
0
    msgh.msg_namelen = 0;
413
0
  }
414
415
0
  msgh.msg_flags = 0;
416
417
0
  if (local != nullptr && local->sin4.sin_family != 0) {
418
0
    addCMsgSrcAddr(&msgh, &cbuf, local, static_cast<int>(localItf));
419
0
  }
420
421
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast): it's the API
422
0
  iov.iov_base = const_cast<void*>(buffer);
423
0
  iov.iov_len = len;
424
0
  msgh.msg_iov = &iov;
425
0
  msgh.msg_iovlen = 1;
426
0
  msgh.msg_flags = 0;
427
428
0
  size_t sent = 0;
429
0
#ifdef MSG_FASTOPEN
430
0
  bool firstTry = true;
431
0
#endif
432
433
0
  do {
434
435
0
#ifdef MSG_FASTOPEN
436
0
    if ((flags & MSG_FASTOPEN) != 0 && !firstTry) {
437
0
      flags &= ~MSG_FASTOPEN;
438
0
    }
439
0
#endif /* MSG_FASTOPEN */
440
441
0
    ssize_t res = sendmsg(socketDesc, &msgh, flags);
442
443
0
    if (res > 0) {
444
0
      auto written = static_cast<size_t>(res);
445
0
      sent += written;
446
447
0
      if (sent == len) {
448
0
        return sent;
449
0
      }
450
451
      /* partial write */
452
0
#ifdef MSG_FASTOPEN
453
0
      firstTry = false;
454
0
#endif
455
0
      iov.iov_len -= written;
456
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic): it's the API
457
0
      iov.iov_base = reinterpret_cast<void*>(reinterpret_cast<char*>(iov.iov_base) + written);
458
0
    }
459
0
    else if (res == 0) {
460
0
      return res;
461
0
    }
462
0
    else if (res == -1) {
463
0
      int err = errno;
464
0
      if (err == EINTR) {
465
0
        continue;
466
0
      }
467
0
      if (err == EAGAIN || err == EWOULDBLOCK || err == EINPROGRESS || err == ENOTCONN) {
468
        /* EINPROGRESS might happen with non blocking socket,
469
           especially with TCP Fast Open */
470
0
        return sent;
471
0
      }
472
0
      unixDie("failed in sendMsgWithOptions");
473
0
    }
474
0
  } while (true);
475
476
0
  return 0;
477
0
}
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
0
{
487
0
  int err = 0;
488
0
  char buf = '\0';
489
0
  size_t buf_size = sizeof(buf);
490
491
0
  do {
492
0
    ssize_t got = recv(sock, &buf, buf_size, MSG_PEEK);
493
494
0
    if (got > 0) {
495
      /* socket is usable, some data is even waiting to be read */
496
0
      return true;
497
0
    }
498
0
    if (got == 0) {
499
      /* other end has closed the socket */
500
0
      return false;
501
0
    }
502
0
    err = errno;
503
0
    if (err == EAGAIN || err == EWOULDBLOCK) {
504
      /* socket is usable, no data waiting */
505
0
      return true;
506
0
    }
507
0
    if (err != EINTR) {
508
      /* something is wrong, could be ECONNRESET,
509
         ENOTCONN, EPIPE, but anyway this socket is
510
         not usable. */
511
0
      return false;
512
0
    }
513
0
  } while (err == EINTR);
514
515
0
  return false;
516
0
}
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
0
{
526
0
  if (input[0] == '[') { // case 1
527
0
    auto both = splitField(input.substr(1), ']');
528
0
    return ComboAddress(both.first, both.second.empty() ? port : pdns::checked_stoi<uint16_t>(both.second.substr(1)));
529
0
  }
530
531
0
  string::size_type count = 0;
532
0
  for (char chr : input) {
533
0
    if (chr == ':') {
534
0
      count++;
535
0
    }
536
0
    if (count > 1) {
537
0
      break;
538
0
    }
539
0
  }
540
0
  switch (count) {
541
0
  case 0: // case 2
542
0
    return ComboAddress(input, port);
543
0
  case 1: { // case 3
544
0
    string::size_type cpos = input.rfind(':');
545
0
    pair<std::string, std::string> both;
546
0
    both.first = input.substr(0, cpos);
547
0
    both.second = input.substr(cpos + 1);
548
549
0
    auto newport = pdns::checked_stoi<uint16_t>(both.second);
550
0
    return ComboAddress(both.first, newport);
551
0
  }
552
0
  default: // case 4
553
0
    return ComboAddress(input, port);
554
0
  }
555
0
}
556
557
void setSocketBuffer(int fileDesc, int optname, uint32_t size)
558
0
{
559
0
  uint32_t psize = 0;
560
0
  socklen_t len = sizeof(psize);
561
562
0
  if (getsockopt(fileDesc, SOL_SOCKET, optname, &psize, &len) != 0) {
563
0
    throw std::runtime_error("Unable to retrieve socket buffer size:" + stringerror());
564
0
  }
565
0
  if (psize >= size) {
566
0
    return;
567
0
  }
568
0
  if (setsockopt(fileDesc, SOL_SOCKET, optname, &size, sizeof(size)) != 0) {
569
0
    throw std::runtime_error("Unable to raise socket buffer size to " + std::to_string(size) + ": " + stringerror());
570
0
  }
571
0
}
572
573
void setSocketReceiveBuffer(int fileDesc, uint32_t size)
574
0
{
575
0
  setSocketBuffer(fileDesc, SO_RCVBUF, size);
576
0
}
577
578
void setSocketSendBuffer(int fileDesc, uint32_t size)
579
0
{
580
0
  setSocketBuffer(fileDesc, SO_SNDBUF, size);
581
0
}
582
583
#ifdef __linux__
584
static uint32_t raiseSocketBufferToMax(int socket, int optname, const std::string& readMaxFromFile)
585
0
{
586
0
  std::ifstream ifs(readMaxFromFile);
587
0
  if (ifs) {
588
0
    std::string line;
589
0
    if (getline(ifs, line)) {
590
0
      auto max = pdns::checked_stoi<uint32_t>(line);
591
0
      setSocketBuffer(socket, optname, max);
592
0
      return max;
593
0
    }
594
0
  }
595
0
  return 0;
596
0
}
597
#endif
598
599
uint32_t raiseSocketReceiveBufferToMax([[maybe_unused]] int socket)
600
0
{
601
0
#ifdef __linux__
602
0
  return raiseSocketBufferToMax(socket, SO_RCVBUF, "/proc/sys/net/core/rmem_max");
603
#else
604
  return 0;
605
#endif
606
0
}
607
608
uint32_t raiseSocketSendBufferToMax([[maybe_unused]] int socket)
609
0
{
610
0
#ifdef __linux__
611
0
  return raiseSocketBufferToMax(socket, SO_SNDBUF, "/proc/sys/net/core/wmem_max");
612
#else
613
  return 0;
614
#endif
615
0
}
616
617
std::set<std::string> getListOfNetworkInterfaces()
618
0
{
619
0
  std::set<std::string> result;
620
0
#ifdef HAVE_GETIFADDRS
621
0
  struct ifaddrs* ifaddr{};
622
0
  if (getifaddrs(&ifaddr) == -1) {
623
0
    return result;
624
0
  }
625
626
0
  for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
627
0
    if (ifa->ifa_name == nullptr) {
628
0
      continue;
629
0
    }
630
0
    result.insert(ifa->ifa_name);
631
0
  }
632
633
0
  freeifaddrs(ifaddr);
634
0
#endif
635
0
  return result;
636
0
}
637
638
#ifdef HAVE_GETIFADDRS
639
std::vector<ComboAddress> getListOfAddressesOfNetworkInterface(const std::string& itf)
640
0
{
641
0
  std::vector<ComboAddress> result;
642
0
  struct ifaddrs* ifaddr = nullptr;
643
0
  if (getifaddrs(&ifaddr) == -1) {
644
0
    return result;
645
0
  }
646
647
0
  for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
648
0
    if (ifa->ifa_name == nullptr || strcmp(ifa->ifa_name, itf.c_str()) != 0) {
649
0
      continue;
650
0
    }
651
0
    if (ifa->ifa_addr == nullptr || (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6)) {
652
0
      continue;
653
0
    }
654
0
    ComboAddress addr;
655
0
    try {
656
0
      addr.setSockaddr(ifa->ifa_addr, ifa->ifa_addr->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
657
0
    }
658
0
    catch (...) {
659
0
      continue;
660
0
    }
661
662
0
    result.push_back(addr);
663
0
  }
664
665
0
  freeifaddrs(ifaddr);
666
0
  return result;
667
0
}
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
0
{
679
0
  if (mask == nullptr || len > 16) {
680
0
    throw std::runtime_error("Invalid parameters passed to convertNetmaskToBits");
681
0
  }
682
683
0
  uint8_t result = 0;
684
  // for all bytes in the address (4 for IPv4, 16 for IPv6)
685
0
  for (size_t idx = 0; idx < len; idx++) {
686
0
    uint8_t byte = *(mask + idx); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
687
    // count the number of bits set
688
0
    while (byte > 0) {
689
0
      result += (byte & 1);
690
0
      byte >>= 1;
691
0
    }
692
0
  }
693
0
  return result;
694
0
}
695
#endif /* HAVE_GETIFADDRS */
696
697
#ifdef HAVE_GETIFADDRS
698
std::vector<Netmask> getListOfRangesOfNetworkInterface(const std::string& itf)
699
0
{
700
0
  std::vector<Netmask> result;
701
0
  struct ifaddrs* ifaddr = nullptr;
702
0
  if (getifaddrs(&ifaddr) == -1) {
703
0
    return result;
704
0
  }
705
706
0
  for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
707
0
    if (ifa->ifa_name == nullptr || strcmp(ifa->ifa_name, itf.c_str()) != 0) {
708
0
      continue;
709
0
    }
710
0
    if (ifa->ifa_addr == nullptr || (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6)) {
711
0
      continue;
712
0
    }
713
0
    ComboAddress addr;
714
0
    try {
715
0
      addr.setSockaddr(ifa->ifa_addr, ifa->ifa_addr->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
716
0
    }
717
0
    catch (...) {
718
0
      continue;
719
0
    }
720
721
    // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
722
0
    if (ifa->ifa_addr->sa_family == AF_INET) {
723
0
      const auto* netmask = reinterpret_cast<const struct sockaddr_in*>(ifa->ifa_netmask);
724
0
      uint8_t maskBits = convertNetmaskToBits(reinterpret_cast<const uint8_t*>(&netmask->sin_addr.s_addr), sizeof(netmask->sin_addr.s_addr));
725
0
      result.emplace_back(addr, maskBits);
726
0
    }
727
0
    else if (ifa->ifa_addr->sa_family == AF_INET6) {
728
0
      const auto* netmask = reinterpret_cast<const struct sockaddr_in6*>(ifa->ifa_netmask);
729
0
      uint8_t maskBits = convertNetmaskToBits(reinterpret_cast<const uint8_t*>(&netmask->sin6_addr.s6_addr), sizeof(netmask->sin6_addr.s6_addr));
730
0
      result.emplace_back(addr, maskBits);
731
0
    }
732
    // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
733
0
  }
734
735
0
  freeifaddrs(ifaddr);
736
0
  return result;
737
0
}
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