Coverage Report

Created: 2026-10-16 07:03

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