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

PowerDNS / pdns / 12595591960

03 Jan 2025 09:27AM UTC coverage: 62.774% (+2.5%) from 60.245%
12595591960

Pull #15008

github

web-flow
Merge c2a2749d3 into 788f396a7
Pull Request #15008: Do not follow CNAME records for ANY or CNAME queries

30393 of 78644 branches covered (38.65%)

Branch coverage included in aggregate %.

105822 of 138350 relevant lines covered (76.49%)

4613078.44 hits per line

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

55.91
/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
{
2✔
40
  throw runtime_error(error);
2✔
41
}
2✔
42

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

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

57
int SConnect(int sockfd, const ComboAddress& remote)
58
{
332✔
59
  int ret = connect(sockfd, reinterpret_cast<const struct sockaddr*>(&remote), remote.getSocklen()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
332✔
60
  if (ret < 0) {
332✔
61
    int savederrno = errno;
2✔
62
    RuntimeError("connecting socket to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
2✔
63
  }
2✔
64
  return ret;
332✔
65
}
332✔
66

67
int SConnectWithTimeout(int sockfd, const ComboAddress& remote, const struct timeval& timeout)
68
{
3,717✔
69
  int ret = connect(sockfd, reinterpret_cast<const struct sockaddr*>(&remote), remote.getSocklen()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
3,717✔
70
  if (ret < 0) {
3,717✔
71
    int savederrno = errno;
2,449✔
72
    if (savederrno == EINPROGRESS) {
2,449✔
73
      if (timeout <= timeval{0, 0}) {
2,307✔
74
        return savederrno;
1,992✔
75
      }
1,992✔
76

77
      /* we wait until the connection has been established */
78
      bool error = false;
315✔
79
      bool disconnected = false;
315✔
80
      int res = waitForRWData(sockfd, false, static_cast<int>(timeout.tv_sec), static_cast<int>(timeout.tv_usec), &error, &disconnected);
315✔
81
      if (res == 1) {
316✔
82
        if (error) {
316✔
83
          savederrno = 0;
35✔
84
          socklen_t errlen = sizeof(savederrno);
35✔
85
          if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)&savederrno, &errlen) == 0) {
35!
86
            NetworkErr("connecting to " + remote.toStringWithPort() + " failed: " + stringerror(savederrno));
35✔
87
          }
35✔
88
          else {
×
89
            NetworkErr("connecting to " + remote.toStringWithPort() + " failed");
×
90
          }
×
91
        }
35✔
92
        if (disconnected) {
316!
93
          NetworkErr(remote.toStringWithPort() + " closed the connection");
×
94
        }
×
95
        return 0;
316✔
96
      }
316✔
97
      if (res == 0) {
2,147,483,647!
98
        NetworkErr("timeout while connecting to " + remote.toStringWithPort());
×
99
      }
×
100
      else if (res < 0) {
2,147,483,647!
101
        savederrno = errno;
×
102
        NetworkErr("waiting to connect to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
×
103
      }
×
104
    }
2,147,483,647✔
105
    else {
142✔
106
      NetworkErr("connecting to " + remote.toStringWithPort() + ": " + stringerror(savederrno));
142✔
107
    }
142✔
108
  }
2,449✔
109

110
  return 0;
1,409✔
111
}
3,717✔
112

113
int SBind(int sockfd, const ComboAddress& local)
114
{
735✔
115
  int ret = bind(sockfd, reinterpret_cast<const struct sockaddr*>(&local), local.getSocklen()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
735✔
116
  if (ret < 0) {
735!
117
    int savederrno = errno;
×
118
    RuntimeError("binding socket to " + local.toStringWithPort() + ": " + stringerror(savederrno));
×
119
  }
×
120
  return ret;
735✔
121
}
735✔
122

123
int SAccept(int sockfd, ComboAddress& remote)
124
{
673✔
125
  socklen_t remlen = remote.getSocklen();
673✔
126

127
  int ret = accept(sockfd, reinterpret_cast<struct sockaddr*>(&remote), &remlen); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
673✔
128
  if (ret < 0) {
673!
129
    RuntimeError("accepting new connection on socket: " + stringerror());
×
130
  }
×
131
  return ret;
673✔
132
}
673✔
133

134
int SListen(int sockfd, int limit)
135
{
394✔
136
  int ret = listen(sockfd, limit);
394✔
137
  if (ret < 0) {
394!
138
    RuntimeError("setting socket to listen: " + stringerror());
×
139
  }
×
140
  return ret;
394✔
141
}
394✔
142

143
int SSetsockopt(int sockfd, int level, int opname, int value)
144
{
1,860✔
145
  int ret = setsockopt(sockfd, level, opname, &value, sizeof(value));
1,860✔
146
  if (ret < 0) {
1,860!
147
    RuntimeError("setsockopt for level " + std::to_string(level) + " and opname " + std::to_string(opname) + " to " + std::to_string(value) + " failed: " + stringerror());
×
148
  }
×
149
  return ret;
1,860✔
150
}
1,860✔
151

152
void setSocketIgnorePMTU([[maybe_unused]] int sockfd, [[maybe_unused]] int family)
153
{
610✔
154
  if (family == AF_INET) { // NOLINT(bugprone-branch-clone)
610✔
155
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
607✔
156
#ifdef IP_PMTUDISC_OMIT
607✔
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
    try {
607✔
162
      SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_OMIT);
607✔
163
      return;
607✔
164
    }
607✔
165
    catch (const std::exception& e) {
607✔
166
      /* failed, let's try IP_PMTUDISC_DONT instead */
167
    }
×
168
#endif /* IP_PMTUDISC_OMIT */
×
169

170
    /* IP_PMTUDISC_DONT disables Path MTU discovery */
171
    SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DONT);
×
172
#endif /* defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) */
×
173
  }
×
174
  else {
3✔
175
#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT)
3✔
176
#ifdef IPV6_PMTUDISC_OMIT
3✔
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
    try {
3✔
182
      SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_OMIT);
3✔
183
      return;
3✔
184
    }
3✔
185
    catch (const std::exception& e) {
3✔
186
      /* failed, let's try IP_PMTUDISC_DONT instead */
187
    }
×
188
#endif /* IPV6_PMTUDISC_OMIT */
×
189

190
    /* IPV6_PMTUDISC_DONT disables Path MTU discovery */
191
    SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DONT);
×
192
#endif /* defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT) */
×
193
  }
×
194
}
610✔
195

196
void setSocketForcePMTU([[maybe_unused]] int sockfd, [[maybe_unused]] int family)
197
{
20✔
198
  if (family == AF_INET) {
20✔
199
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
18✔
200
    /* IP_PMTUDISC_DO enables Path MTU discovery and prevents fragmentation */
201
    SSetsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, IP_PMTUDISC_DO);
18✔
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
  }
18✔
207
  else {
2✔
208
#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
2✔
209
    /* IPV6_PMTUDISC_DO enables Path MTU discovery and prevents fragmentation */
210
    SSetsockopt(sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, IPV6_PMTUDISC_DO);
2✔
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
  }
2✔
216
}
20✔
217

218
bool setReusePort(int sockfd)
219
{
×
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
  try {
×
230
    SSetsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, 1);
×
231
    return true;
×
232
  }
×
233
  catch (const std::exception& e) {
×
234
    return false;
×
235
  }
×
236
#endif
×
237
  return false;
×
238
}
×
239

240
bool HarvestTimestamp(struct msghdr* msgh, struct timeval* timeval)
241
{
4,550✔
242
  // NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast, cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-type-reinterpret-cast)
243
#ifdef SO_TIMESTAMP
4,550✔
244
  struct cmsghdr* cmsg{};
4,550✔
245
  for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != nullptr; cmsg = CMSG_NXTHDR(msgh, cmsg)) {
4,550!
246
    if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMP || cmsg->cmsg_type == SCM_TIMESTAMP) && CMSG_LEN(sizeof(*timeval)) == cmsg->cmsg_len) {
4,550!
247
      memcpy(timeval, CMSG_DATA(cmsg), sizeof(*timeval));
4,550✔
248
      return true;
4,550✔
249
    }
4,550✔
250
  }
4,550✔
251
#endif
×
252
  return false;
×
253
}
4,550✔
254
bool HarvestDestinationAddress(const struct msghdr* msgh, ComboAddress* destination)
255
{
7,278✔
256
  destination->reset();
7,278✔
257
#ifdef __NetBSD__
258
  struct cmsghdr* cmsg{};
259
#else
260
  const struct cmsghdr* cmsg{};
7,278✔
261
#endif
7,278✔
262
  for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != nullptr; cmsg = CMSG_NXTHDR(const_cast<struct msghdr*>(msgh), const_cast<struct cmsghdr*>(cmsg))) {
11,840✔
263
#if defined(IP_PKTINFO)
4,582✔
264
    if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
4,582!
265
      const auto* ptr = reinterpret_cast<const struct in_pktinfo*>(CMSG_DATA(cmsg));
19✔
266
      destination->sin4.sin_addr = ptr->ipi_addr;
19✔
267
      destination->sin4.sin_family = AF_INET;
19✔
268
      return true;
19✔
269
    }
19✔
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
    if ((cmsg->cmsg_level == IPPROTO_IPV6) && (cmsg->cmsg_type == IPV6_PKTINFO)) {
4,563!
280
      const auto* ptr = reinterpret_cast<const struct in6_pktinfo*>(CMSG_DATA(cmsg));
1✔
281
      destination->sin6.sin6_addr = ptr->ipi6_addr;
1✔
282
      destination->sin4.sin_family = AF_INET6;
1✔
283
      return true;
1✔
284
    }
1✔
285
  }
4,563✔
286
  return false;
7,258✔
287
  // NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast, cppcoreguidelines-pro-bounds-pointer-arithmetic, cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-type-reinterpret-cast)
288
}
7,278✔
289

290
bool IsAnyAddress(const ComboAddress& addr)
291
{
6,128✔
292
  if (addr.sin4.sin_family == AF_INET) {
6,128✔
293
    return addr.sin4.sin_addr.s_addr == 0;
6,112✔
294
  }
6,112✔
295
  if (addr.sin4.sin_family == AF_INET6) {
16!
296
    return memcmp(&addr.sin6.sin6_addr, &in6addr_any, sizeof(addr.sin6.sin6_addr)) == 0;
16✔
297
  }
16✔
298
  return false;
×
299
}
16✔
300

301
int sendOnNBSocket(int fileDesc, const struct msghdr* msgh)
302
{
4,452✔
303
  int sendErr = 0;
4,452✔
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
  if (sendmsg(fileDesc, msgh, 0) == -1) {
4,452!
318
    sendErr = errno;
×
319
  }
×
320
#endif
4,452✔
321
  return sendErr;
4,452✔
322
}
4,452✔
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
{
11,382✔
329
  iov->iov_base = data;
11,382✔
330
  iov->iov_len = datalen;
11,382✔
331

332
  memset(msgh, 0, sizeof(struct msghdr));
11,382✔
333

334
  msgh->msg_control = cbuf;
11,382✔
335
  msgh->msg_controllen = cbufsize;
11,382✔
336
  msgh->msg_name = addr;
11,382✔
337
  msgh->msg_namelen = addr->getSocklen();
11,382✔
338
  msgh->msg_iov = iov;
11,382✔
339
  msgh->msg_iovlen = 1;
11,382✔
340
  msgh->msg_flags = 0;
11,382✔
341
}
11,382✔
342

343
// warning: various parts of PowerDNS assume 'truncate' will never throw
344
void ComboAddress::truncate(unsigned int bits) noexcept
345
{
132,682✔
346
  uint8_t* start{};
132,682✔
347
  int len = 4;
132,682✔
348
  if (sin4.sin_family == AF_INET) {
132,682✔
349
    if (bits >= 32) {
131,933✔
350
      return;
131,245✔
351
    }
131,245✔
352
    start = reinterpret_cast<uint8_t*>(&sin4.sin_addr.s_addr); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
688✔
353
    len = 4;
688✔
354
  }
688✔
355
  else {
749✔
356
    if (bits >= 128) {
749✔
357
      return;
23✔
358
    }
23✔
359
    start = reinterpret_cast<uint8_t*>(&sin6.sin6_addr.s6_addr); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
726✔
360
    len = 16;
726✔
361
  }
726✔
362

363
  auto tozero = len * 8 - bits; // if set to 22, this will clear 1 byte, as it should
1,414✔
364

365
  memset(start + len - tozero / 8, 0, tozero / 8); // blot out the whole bytes on the right NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1,414✔
366

367
  auto bitsleft = tozero % 8; // 2 bits left to clear
1,414✔
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
  uint8_t* place = start + len - 1 - tozero / 8; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1,414✔
372
  *place &= (~((1 << bitsleft) - 1));
1,414✔
373
}
1,414✔
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
{
563✔
377
  msghdr msgh{};
563✔
378
  iovec iov{};
563✔
379
  cmsgbuf_aligned cbuf;
563✔
380

381
  /* Set up iov and msgh structures. */
382
  memset(&msgh, 0, sizeof(msgh));
563✔
383
  msgh.msg_control = nullptr;
563✔
384
  msgh.msg_controllen = 0;
563✔
385
  if (dest != nullptr) {
563!
386
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-type-const-cast): it's the API
387
    msgh.msg_name = reinterpret_cast<void*>(const_cast<ComboAddress*>(dest));
563✔
388
    msgh.msg_namelen = dest->getSocklen();
563✔
389
  }
563✔
390
  else {
×
391
    msgh.msg_name = nullptr;
×
392
    msgh.msg_namelen = 0;
×
393
  }
×
394

395
  msgh.msg_flags = 0;
563✔
396

397
  if (local != nullptr && local->sin4.sin_family != 0) {
563!
398
    addCMsgSrcAddr(&msgh, &cbuf, local, static_cast<int>(localItf));
563✔
399
  }
563✔
400

401
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast): it's the API
402
  iov.iov_base = const_cast<void*>(buffer);
563✔
403
  iov.iov_len = len;
563✔
404
  msgh.msg_iov = &iov;
563✔
405
  msgh.msg_iovlen = 1;
563✔
406
  msgh.msg_flags = 0;
563✔
407

408
  size_t sent = 0;
563✔
409
#ifdef MSG_FASTOPEN
563✔
410
  bool firstTry = true;
563✔
411
#endif
563✔
412

413
  do {
563✔
414

415
#ifdef MSG_FASTOPEN
563✔
416
    if ((flags & MSG_FASTOPEN) != 0 && !firstTry) {
563!
417
      flags &= ~MSG_FASTOPEN;
×
418
    }
×
419
#endif /* MSG_FASTOPEN */
563✔
420

421
    ssize_t res = sendmsg(socketDesc, &msgh, flags);
563✔
422

423
    if (res > 0) {
563!
424
      auto written = static_cast<size_t>(res);
563✔
425
      sent += written;
563✔
426

427
      if (sent == len) {
563!
428
        return sent;
563✔
429
      }
563✔
430

431
      /* partial write */
432
#ifdef MSG_FASTOPEN
×
433
      firstTry = false;
×
434
#endif
×
435
      iov.iov_len -= written;
×
436
      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic): it's the API
437
      iov.iov_base = reinterpret_cast<void*>(reinterpret_cast<char*>(iov.iov_base) + written);
×
438
    }
×
439
    else if (res == 0) {
×
440
      return res;
×
441
    }
×
442
    else if (res == -1) {
×
443
      int err = errno;
×
444
      if (err == EINTR) {
×
445
        continue;
×
446
      }
×
447
      if (err == EAGAIN || err == EWOULDBLOCK || err == EINPROGRESS || err == ENOTCONN) {
×
448
        /* EINPROGRESS might happen with non blocking socket,
449
           especially with TCP Fast Open */
450
        return sent;
×
451
      }
×
452
      unixDie("failed in sendMsgWithOptions");
×
453
    }
×
454
  } while (true);
563✔
455

456
  return 0;
×
457
}
563✔
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
{
94✔
467
  int err = 0;
94✔
468
  char buf = '\0';
94✔
469
  size_t buf_size = sizeof(buf);
94✔
470

471
  do {
94✔
472
    ssize_t got = recv(sock, &buf, buf_size, MSG_PEEK);
94✔
473

474
    if (got > 0) {
94!
475
      /* socket is usable, some data is even waiting to be read */
476
      return true;
×
477
    }
×
478
    if (got == 0) {
94✔
479
      /* other end has closed the socket */
480
      return false;
92✔
481
    }
92✔
482
    err = errno;
2✔
483
    if (err == EAGAIN || err == EWOULDBLOCK) {
2!
484
      /* socket is usable, no data waiting */
485
      return true;
2✔
486
    }
2✔
487
    if (err != EINTR) {
×
488
      /* something is wrong, could be ECONNRESET,
489
         ENOTCONN, EPIPE, but anyway this socket is
490
         not usable. */
491
      return false;
×
492
    }
×
493
  } while (err == EINTR);
×
494

495
  return false;
×
496
}
94✔
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
{
2,928✔
506
  if (input[0] == '[') { // case 1
2,928✔
507
    auto both = splitField(input.substr(1), ']');
11✔
508
    return ComboAddress(both.first, both.second.empty() ? port : pdns::checked_stoi<uint16_t>(both.second.substr(1)));
11!
509
  }
11✔
510

511
  string::size_type count = 0;
2,917✔
512
  for (char chr : input) {
45,393✔
513
    if (chr == ':') {
45,393✔
514
      count++;
2,859✔
515
    }
2,859✔
516
    if (count > 1) {
45,393✔
517
      break;
30✔
518
    }
30✔
519
  }
45,393✔
520
  switch (count) {
2,917✔
521
  case 0: // case 2
88✔
522
    return ComboAddress(input, port);
88✔
523
  case 1: { // case 3
2,799✔
524
    string::size_type cpos = input.rfind(':');
2,799✔
525
    pair<std::string, std::string> both;
2,799✔
526
    both.first = input.substr(0, cpos);
2,799✔
527
    both.second = input.substr(cpos + 1);
2,799✔
528

529
    auto newport = pdns::checked_stoi<uint16_t>(both.second);
2,799✔
530
    return ComboAddress(both.first, newport);
2,799✔
531
  }
×
532
  default: // case 4
30✔
533
    return ComboAddress(input, port);
30✔
534
  }
2,917✔
535
}
2,917✔
536

537
void setSocketBuffer(int fileDesc, int optname, uint32_t size)
538
{
448✔
539
  uint32_t psize = 0;
448✔
540
  socklen_t len = sizeof(psize);
448✔
541

542
  if (getsockopt(fileDesc, SOL_SOCKET, optname, &psize, &len) != 0) {
448!
543
    throw std::runtime_error("Unable to retrieve socket buffer size:" + stringerror());
×
544
  }
×
545
  if (psize >= size) {
448✔
546
    return;
294✔
547
  }
294✔
548
  if (setsockopt(fileDesc, SOL_SOCKET, optname, &size, sizeof(size)) != 0) {
154!
549
    throw std::runtime_error("Unable to raise socket buffer size to " + std::to_string(size) + ": " + stringerror());
×
550
  }
×
551
}
154✔
552

553
void setSocketReceiveBuffer(int fileDesc, uint32_t size)
554
{
294✔
555
  setSocketBuffer(fileDesc, SO_RCVBUF, size);
294✔
556
}
294✔
557

558
void setSocketSendBuffer(int fileDesc, uint32_t size)
559
{
154✔
560
  setSocketBuffer(fileDesc, SO_SNDBUF, size);
154✔
561
}
154✔
562

563
#ifdef __linux__
564
static uint32_t raiseSocketBufferToMax(int socket, int optname, const std::string& readMaxFromFile)
565
{
678✔
566
  std::ifstream ifs(readMaxFromFile);
678✔
567
  if (ifs) {
678!
568
    std::string line;
×
569
    if (getline(ifs, line)) {
×
570
      auto max = pdns::checked_stoi<uint32_t>(line);
×
571
      setSocketBuffer(socket, optname, max);
×
572
      return max;
×
573
    }
×
574
  }
×
575
  return 0;
678✔
576
}
678✔
577
#endif
578

579
uint32_t raiseSocketReceiveBufferToMax([[maybe_unused]] int socket)
580
{
339✔
581
#ifdef __linux__
339✔
582
  return raiseSocketBufferToMax(socket, SO_RCVBUF, "/proc/sys/net/core/rmem_max");
339✔
583
#else
584
  return 0;
585
#endif
586
}
339✔
587

588
uint32_t raiseSocketSendBufferToMax([[maybe_unused]] int socket)
589
{
339✔
590
#ifdef __linux__
339✔
591
  return raiseSocketBufferToMax(socket, SO_SNDBUF, "/proc/sys/net/core/wmem_max");
339✔
592
#else
593
  return 0;
594
#endif
595
}
339✔
596

597
std::set<std::string> getListOfNetworkInterfaces()
598
{
×
599
  std::set<std::string> result;
×
600
#ifdef HAVE_GETIFADDRS
×
601
  struct ifaddrs* ifaddr{};
×
602
  if (getifaddrs(&ifaddr) == -1) {
×
603
    return result;
×
604
  }
×
605

606
  for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
×
607
    if (ifa->ifa_name == nullptr) {
×
608
      continue;
×
609
    }
×
610
    result.insert(ifa->ifa_name);
×
611
  }
×
612

613
  freeifaddrs(ifaddr);
×
614
#endif
×
615
  return result;
×
616
}
×
617

618
#ifdef HAVE_GETIFADDRS
619
std::vector<ComboAddress> getListOfAddressesOfNetworkInterface(const std::string& itf)
620
{
×
621
  std::vector<ComboAddress> result;
×
622
  struct ifaddrs* ifaddr = nullptr;
×
623
  if (getifaddrs(&ifaddr) == -1) {
×
624
    return result;
×
625
  }
×
626

627
  for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
×
628
    if (ifa->ifa_name == nullptr || strcmp(ifa->ifa_name, itf.c_str()) != 0) {
×
629
      continue;
×
630
    }
×
631
    if (ifa->ifa_addr == nullptr || (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6)) {
×
632
      continue;
×
633
    }
×
634
    ComboAddress addr;
×
635
    try {
×
636
      addr.setSockaddr(ifa->ifa_addr, ifa->ifa_addr->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
×
637
    }
×
638
    catch (...) {
×
639
      continue;
×
640
    }
×
641

642
    result.push_back(addr);
×
643
  }
×
644

645
  freeifaddrs(ifaddr);
×
646
  return result;
×
647
}
×
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
{
×
659
  if (mask == nullptr || len > 16) {
×
660
    throw std::runtime_error("Invalid parameters passed to convertNetmaskToBits");
×
661
  }
×
662

663
  uint8_t result = 0;
×
664
  // for all bytes in the address (4 for IPv4, 16 for IPv6)
665
  for (size_t idx = 0; idx < len; idx++) {
×
666
    uint8_t byte = *(mask + idx); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
×
667
    // count the number of bits set
668
    while (byte > 0) {
×
669
      result += (byte & 1);
×
670
      byte >>= 1;
×
671
    }
×
672
  }
×
673
  return result;
×
674
}
×
675
#endif /* HAVE_GETIFADDRS */
676

677
#ifdef HAVE_GETIFADDRS
678
std::vector<Netmask> getListOfRangesOfNetworkInterface(const std::string& itf)
679
{
×
680
  std::vector<Netmask> result;
×
681
  struct ifaddrs* ifaddr = nullptr;
×
682
  if (getifaddrs(&ifaddr) == -1) {
×
683
    return result;
×
684
  }
×
685

686
  for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
×
687
    if (ifa->ifa_name == nullptr || strcmp(ifa->ifa_name, itf.c_str()) != 0) {
×
688
      continue;
×
689
    }
×
690
    if (ifa->ifa_addr == nullptr || (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6)) {
×
691
      continue;
×
692
    }
×
693
    ComboAddress addr;
×
694
    try {
×
695
      addr.setSockaddr(ifa->ifa_addr, ifa->ifa_addr->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
×
696
    }
×
697
    catch (...) {
×
698
      continue;
×
699
    }
×
700

701
    // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
702
    if (ifa->ifa_addr->sa_family == AF_INET) {
×
703
      const auto* netmask = reinterpret_cast<const struct sockaddr_in*>(ifa->ifa_netmask);
×
704
      uint8_t maskBits = convertNetmaskToBits(reinterpret_cast<const uint8_t*>(&netmask->sin_addr.s_addr), sizeof(netmask->sin_addr.s_addr));
×
705
      result.emplace_back(addr, maskBits);
×
706
    }
×
707
    else if (ifa->ifa_addr->sa_family == AF_INET6) {
×
708
      const auto* netmask = reinterpret_cast<const struct sockaddr_in6*>(ifa->ifa_netmask);
×
709
      uint8_t maskBits = convertNetmaskToBits(reinterpret_cast<const uint8_t*>(&netmask->sin6_addr.s6_addr), sizeof(netmask->sin6_addr.s6_addr));
×
710
      result.emplace_back(addr, maskBits);
×
711
    }
×
712
    // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
713
  }
×
714

715
  freeifaddrs(ifaddr);
×
716
  return result;
×
717
}
×
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
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