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

PowerDNS / pdns / 14125261754

28 Mar 2025 08:49AM UTC coverage: 63.407% (-0.02%) from 63.423%
14125261754

push

github

web-flow
Merge pull request #15362 from rgacogne/ddist-do-not-register-xsk-config-check-or-client

dnsdist: Do not register Xsk sockets on configuration check or client mode

41557 of 100218 branches covered (41.47%)

Branch coverage included in aggregate %.

6 of 7 new or added lines in 2 files covered. (85.71%)

41 existing lines in 10 files now uncovered.

128357 of 167755 relevant lines covered (76.51%)

3743391.9 hits per line

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

62.28
/pdns/misc.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

23
#ifdef HAVE_CONFIG_H
24
#include "config.h"
25
#endif
26

27
#include <sys/param.h>
28
#include <sys/socket.h>
29
#include <fcntl.h>
30
#include <netdb.h>
31
#include <sys/time.h>
32
#include <ctime>
33
#include <sys/resource.h>
34
#include <netinet/in.h>
35
#include <sys/un.h>
36
#include <unistd.h>
37
#include <fstream>
38
#include "misc.hh"
39
#include <vector>
40
#include <string>
41
#include <sstream>
42
#include <cerrno>
43
#include <cstring>
44
#include <iostream>
45
#include <sys/types.h>
46
#include <dirent.h>
47
#include <algorithm>
48
#include <poll.h>
49
#include <iomanip>
50
#include <netinet/tcp.h>
51
#include <optional>
52
#include <cstdlib>
53
#include <cstdio>
54
#include "pdnsexception.hh"
55
#include <boost/algorithm/string.hpp>
56
#include <boost/format.hpp>
57
#include "iputils.hh"
58
#include "dnsparser.hh"
59
#include "dns_random.hh"
60
#include <pwd.h>
61
#include <grp.h>
62
#include <climits>
63
#include <unordered_map>
64
#ifdef __FreeBSD__
65
#  include <pthread_np.h>
66
#endif
67
#ifdef __NetBSD__
68
#  include <pthread.h>
69
#  include <sched.h>
70
#endif
71

72
#if defined(HAVE_LIBCRYPTO)
73
#include <openssl/err.h>
74
#endif // HAVE_LIBCRYPTO
75

76
size_t writen2(int fileDesc, const void *buf, size_t count)
77
{
1,407,970✔
78
  const char *ptr = static_cast<const char*>(buf);
1,407,970✔
79
  const char *eptr = ptr + count;
1,407,970✔
80

81
  while (ptr != eptr) {
2,815,941✔
82
    auto res = ::write(fileDesc, ptr, eptr - ptr);
1,407,971✔
83
    if (res < 0) {
1,407,971!
84
      if (errno == EAGAIN) {
×
85
        throw std::runtime_error("used writen2 on non-blocking socket, got EAGAIN");
×
86
      }
×
87
      unixDie("failed in writen2");
×
88
    }
×
89
    else if (res == 0) {
1,407,971!
90
      throw std::runtime_error("could not write all bytes, got eof in writen2");
×
91
    }
×
92

93
    ptr += res;
1,407,971✔
94
  }
1,407,971✔
95

96
  return count;
1,407,970✔
97
}
1,407,970✔
98

99
size_t readn2(int fileDesc, void* buffer, size_t len)
100
{
1,108✔
101
  size_t pos = 0;
1,108✔
102

103
  for (;;) {
1,108✔
104
    auto res = read(fileDesc, static_cast<char *>(buffer) + pos, len - pos); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic): it's the API
1,108✔
105
    if (res == 0) {
1,108✔
106
      throw runtime_error("EOF while reading message");
276✔
107
    }
276✔
108
    if (res < 0) {
832!
109
      if (errno == EAGAIN) {
×
110
        throw std::runtime_error("used readn2 on non-blocking socket, got EAGAIN");
×
111
      }
×
112
      unixDie("failed in readn2");
×
113
    }
×
114

115
    pos += static_cast<size_t>(res);
832✔
116
    if (pos == len) {
832!
117
      break;
832✔
118
    }
832✔
119
  }
832✔
120
  return len;
832✔
121
}
1,108✔
122

123
size_t readn2WithTimeout(int fd, void* buffer, size_t len, const struct timeval& idleTimeout, const struct timeval& totalTimeout, bool allowIncomplete)
124
{
1,697✔
125
  size_t pos = 0;
1,697✔
126
  struct timeval start{0,0};
1,697✔
127
  struct timeval remainingTime = totalTimeout;
1,697✔
128
  if (totalTimeout.tv_sec != 0 || totalTimeout.tv_usec != 0) {
1,697!
129
    gettimeofday(&start, nullptr);
62✔
130
  }
62✔
131

132
  do {
2,636✔
133
    ssize_t got = read(fd, (char *)buffer + pos, len - pos);
2,636✔
134
    if (got > 0) {
2,636✔
135
      pos += (size_t) got;
1,720✔
136
      if (allowIncomplete) {
1,720!
137
        break;
×
138
      }
×
139
    }
1,720✔
140
    else if (got == 0) {
916✔
141
      throw runtime_error("EOF while reading message");
5✔
142
    }
5✔
143
    else {
911✔
144
      if (errno == EAGAIN) {
911!
145
        struct timeval w = ((totalTimeout.tv_sec == 0 && totalTimeout.tv_usec == 0) || idleTimeout <= remainingTime) ? idleTimeout : remainingTime;
911!
146
        int res = waitForData(fd, w.tv_sec, w.tv_usec);
911✔
147
        if (res > 0) {
911!
148
          /* there is data available */
149
        }
911✔
150
        else if (res == 0) {
×
151
          throw runtime_error("Timeout while waiting for data to read");
×
152
        } else {
×
153
          throw runtime_error("Error while waiting for data to read");
×
154
        }
×
155
      }
911✔
156
      else {
×
157
        unixDie("failed in readn2WithTimeout");
×
158
      }
×
159
    }
911✔
160

161
    if (totalTimeout.tv_sec != 0 || totalTimeout.tv_usec != 0) {
2,631!
162
      struct timeval now;
169✔
163
      gettimeofday(&now, nullptr);
169✔
164
      struct timeval elapsed = now - start;
169✔
165
      if (remainingTime < elapsed) {
169!
166
        throw runtime_error("Timeout while reading data");
×
167
      }
×
168
      start = now;
169✔
169
      remainingTime = remainingTime - elapsed;
169✔
170
    }
169✔
171
  }
2,631✔
172
  while (pos < len);
2,631✔
173

174
  return len;
1,692✔
175
}
1,697✔
176

177
size_t writen2WithTimeout(int fd, const void * buffer, size_t len, const struct timeval& timeout)
178
{
910✔
179
  size_t pos = 0;
910✔
180
  do {
910✔
181
    ssize_t written = write(fd, reinterpret_cast<const char *>(buffer) + pos, len - pos);
910✔
182

183
    if (written > 0) {
910!
184
      pos += (size_t) written;
910✔
185
    }
910✔
186
    else if (written == 0)
×
187
      throw runtime_error("EOF while writing message");
×
188
    else {
×
189
      if (errno == EAGAIN) {
×
190
        int res = waitForRWData(fd, false, timeout.tv_sec, timeout.tv_usec);
×
191
        if (res > 0) {
×
192
          /* there is room available */
193
        }
×
194
        else if (res == 0) {
×
195
          throw runtime_error("Timeout while waiting to write data");
×
196
        } else {
×
197
          throw runtime_error("Error while waiting for room to write data");
×
198
        }
×
199
      }
×
200
      else {
×
201
        unixDie("failed in write2WithTimeout");
×
202
      }
×
203
    }
×
204
  }
910✔
205
  while (pos < len);
910!
206

207
  return len;
910✔
208
}
910✔
209

210
auto pdns::getMessageFromErrno(const int errnum) -> std::string
211
{
2,036✔
212
  const size_t errLen = 2048;
2,036✔
213
  std::string errMsgData{};
2,036✔
214
  errMsgData.resize(errLen);
2,036✔
215

216
  const char* errMsg = nullptr;
2,036✔
217
#ifdef STRERROR_R_CHAR_P
2,036✔
218
  errMsg = strerror_r(errnum, errMsgData.data(), errMsgData.length());
2,036✔
219
#else
220
  // This can fail, and when it does, it sets errno. We ignore that and
221
  // set our own error message instead.
222
  int res = strerror_r(errnum, errMsgData.data(), errMsgData.length());
223
  errMsg = errMsgData.c_str();
224
  if (res != 0) {
225
    errMsg = "Unknown (the exact error could not be retrieved)";
226
  }
227
#endif
228

229
  // We make a copy here because `strerror_r()` might return a static
230
  // immutable buffer for an error message. The copy shouldn't be
231
  // critical though, we're on the bailout/error-handling path anyways.
232
  std::string message{errMsg};
2,036✔
233
  return message;
2,036✔
234
}
2,036✔
235

236
#if defined(HAVE_LIBCRYPTO)
237
auto pdns::OpenSSL::error(const std::string& errorMessage) -> std::runtime_error
238
{
×
239
  unsigned long errorCode = 0;
×
240
  auto fullErrorMessage{errorMessage};
×
241
#if OPENSSL_VERSION_MAJOR >= 3
×
242
  const char* filename = nullptr;
×
243
  const char* functionName = nullptr;
×
244
  int lineNumber = 0;
×
245
  while ((errorCode = ERR_get_error_all(&filename, &lineNumber, &functionName, nullptr, nullptr)) != 0) {
×
246
    fullErrorMessage += std::string(": ") + std::to_string(errorCode);
×
247

248
    const auto* lib = ERR_lib_error_string(errorCode);
×
249
    if (lib != nullptr) {
×
250
      fullErrorMessage += std::string(":") + lib;
×
251
    }
×
252

253
    const auto* reason = ERR_reason_error_string(errorCode);
×
254
    if (reason != nullptr) {
×
255
      fullErrorMessage += std::string("::") + reason;
×
256
    }
×
257

258
    if (filename != nullptr) {
×
259
      fullErrorMessage += std::string(" - ") + filename;
×
260
    }
×
261
    if (lineNumber != 0) {
×
262
      fullErrorMessage += std::string(":") + std::to_string(lineNumber);
×
263
    }
×
264
    if (functionName != nullptr) {
×
265
      fullErrorMessage += std::string(" - ") + functionName;
×
266
    }
×
267
  }
×
268
#else
269
  while ((errorCode = ERR_get_error()) != 0) {
270
    fullErrorMessage += std::string(": ") + std::to_string(errorCode);
271

272
    const auto* lib = ERR_lib_error_string(errorCode);
273
    if (lib != nullptr) {
274
      fullErrorMessage += std::string(":") + lib;
275
    }
276

277
    const auto* func = ERR_func_error_string(errorCode);
278
    if (func != nullptr) {
279
      fullErrorMessage += std::string(":") + func;
280
    }
281

282
    const auto* reason = ERR_reason_error_string(errorCode);
283
    if (reason != nullptr) {
284
      fullErrorMessage += std::string("::") + reason;
285
    }
286
  }
287
#endif
288
  return std::runtime_error{fullErrorMessage};
×
289
}
×
290

291
auto pdns::OpenSSL::error(const std::string& componentName, const std::string& errorMessage) -> std::runtime_error
292
{
×
293
  return pdns::OpenSSL::error(componentName + ": " + errorMessage);
×
294
}
×
295
#endif // HAVE_LIBCRYPTO
296

297
string nowTime()
298
{
2,811✔
299
  time_t now = time(nullptr);
2,811✔
300
  struct tm theTime{};
2,811✔
301
  localtime_r(&now, &theTime);
2,811✔
302
  std::array<char, 30> buffer{};
2,811✔
303
  // YYYY-mm-dd HH:MM:SS TZOFF
304
  size_t ret = strftime(buffer.data(), buffer.size(), "%F %T %z", &theTime);
2,811✔
305
  if (ret == 0) {
2,811!
306
    buffer[0] = '\0';
×
307
  }
×
308
  return {buffer.data()};
2,811✔
309
}
2,811✔
310

311
static bool ciEqual(const string& lhs, const string& rhs)
312
{
432✔
313
  if (lhs.size() != rhs.size()) {
432✔
314
    return false;
408✔
315
  }
408✔
316

317
  string::size_type pos = 0;
24✔
318
  const string::size_type epos = lhs.size();
24✔
319
  for (; pos < epos; ++pos) {
384✔
320
    if (dns_tolower(lhs[pos]) != dns_tolower(rhs[pos])) {
368✔
321
      return false;
8✔
322
    }
8✔
323
  }
368✔
324
  return true;
16✔
325
}
24✔
326

327
/** does domain end on suffix? Is smart about "wwwds9a.nl" "ds9a.nl" not matching */
328
static bool endsOn(const string &domain, const string &suffix)
329
{
432✔
330
  if( suffix.empty() || ciEqual(domain, suffix) ) {
432!
331
    return true;
16✔
332
  }
16✔
333

334
  if(domain.size() <= suffix.size()) {
416✔
335
    return false;
52✔
336
  }
52✔
337

338
  string::size_type dpos = domain.size() - suffix.size() - 1;
364✔
339
  string::size_type spos = 0;
364✔
340

341
  if (domain[dpos++] != '.') {
364✔
342
    return false;
88✔
343
  }
88✔
344

345
  for(; dpos < domain.size(); ++dpos, ++spos) {
3,928✔
346
    if (dns_tolower(domain[dpos]) != dns_tolower(suffix[spos])) {
3,664✔
347
      return false;
12✔
348
    }
12✔
349
  }
3,664✔
350

351
  return true;
264✔
352
}
276✔
353

354
/** strips a domain suffix from a domain, returns true if it stripped */
355
bool stripDomainSuffix(string *qname, const string &domain)
356
{
432✔
357
  if (!endsOn(*qname, domain)) {
432✔
358
    return false;
152✔
359
  }
152✔
360

361
  if (toLower(*qname) == toLower(domain)) {
280✔
362
    *qname="@";
16✔
363
  }
16✔
364
  else {
264✔
365
    if ((*qname)[qname->size() - domain.size() - 1] != '.') {
264!
366
      return false;
×
367
    }
×
368

369
    qname->resize(qname->size() - domain.size()-1);
264✔
370
  }
264✔
371
  return true;
280✔
372
}
280✔
373

374
// returns -1 in case if error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
375
int waitForData(int fileDesc, int seconds, int useconds)
376
{
2,295,116✔
377
  return waitForRWData(fileDesc, true, seconds, useconds);
2,295,116✔
378
}
2,295,116✔
379

380
int waitForRWData(int fileDesc, bool waitForRead, int seconds, int useconds, bool* error, bool* disconnected)
381
{
2,300,196✔
382
  struct pollfd pfd{};
2,300,196✔
383
  memset(&pfd, 0, sizeof(pfd));
2,300,196✔
384
  pfd.fd = fileDesc;
2,300,196✔
385

386
  if (waitForRead) {
2,300,196✔
387
    pfd.events = POLLIN;
2,297,336✔
388
  }
2,297,336✔
389
  else {
2,860✔
390
    pfd.events = POLLOUT;
2,860✔
391
  }
2,860✔
392

393
  int ret = poll(&pfd, 1, seconds * 1000 + useconds/1000);
2,300,196✔
394
  if (ret > 0) {
2,300,196✔
395
    if ((error != nullptr) && (pfd.revents & POLLERR) != 0) {
2,078,333✔
396
      *error = true;
33✔
397
    }
33✔
398
    if ((disconnected != nullptr) && (pfd.revents & POLLHUP) != 0) {
2,078,333✔
399
      *disconnected = true;
33✔
400
    }
33✔
401
  }
2,078,333✔
402

403
  return ret;
2,300,196✔
404
}
2,300,196✔
405

406
// returns -1 in case of error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
407
int waitForMultiData(const set<int>& fds, const int seconds, const int useconds, int* fdOut) {
2,269✔
408
  set<int> realFDs;
2,269✔
409
  for (const auto& fd : fds) {
4,538✔
410
    if (fd >= 0 && realFDs.count(fd) == 0) {
4,538!
411
      realFDs.insert(fd);
4,514✔
412
    }
4,514✔
413
  }
4,538✔
414

415
  std::vector<struct pollfd> pfds(realFDs.size());
2,269✔
416
  memset(pfds.data(), 0, realFDs.size()*sizeof(struct pollfd));
2,269✔
417
  int ctr = 0;
2,269✔
418
  for (const auto& fd : realFDs) {
4,514✔
419
    pfds[ctr].fd = fd;
4,514✔
420
    pfds[ctr].events = POLLIN;
4,514✔
421
    ctr++;
4,514✔
422
  }
4,514✔
423

424
  int ret;
2,269✔
425
  if(seconds >= 0)
2,269!
426
    ret = poll(pfds.data(), realFDs.size(), seconds * 1000 + useconds/1000);
2,269✔
427
  else
×
428
    ret = poll(pfds.data(), realFDs.size(), -1);
×
429
  if(ret <= 0)
2,269✔
430
    return ret;
2,263✔
431

432
  set<int> pollinFDs;
6✔
433
  for (const auto& pfd : pfds) {
6✔
434
    if (pfd.revents & POLLIN) {
6!
435
      pollinFDs.insert(pfd.fd);
6✔
436
    }
6✔
437
  }
6✔
438
  set<int>::const_iterator it(pollinFDs.begin());
6✔
439
  advance(it, dns_random(pollinFDs.size()));
6✔
440
  *fdOut = *it;
6✔
441
  return 1;
6✔
442
}
2,269✔
443

444
// returns -1 in case of error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
445
int waitFor2Data(int fd1, int fd2, int seconds, int useconds, int* fdPtr)
446
{
×
447
  std::array<pollfd,2> pfds{};
×
448
  memset(pfds.data(), 0, pfds.size() * sizeof(struct pollfd));
×
449
  pfds[0].fd = fd1;
×
450
  pfds[1].fd = fd2;
×
451

452
  pfds[0].events= pfds[1].events = POLLIN;
×
453

454
  int nsocks = 1 + static_cast<int>(fd2 >= 0); // fd2 can optionally be -1
×
455

456
  int ret{};
×
457
  if (seconds >= 0) {
×
458
    ret = poll(pfds.data(), nsocks, seconds * 1000 + useconds / 1000);
×
459
  }
×
460
  else {
×
461
    ret = poll(pfds.data(), nsocks, -1);
×
462
  }
×
463
  if (ret <= 0) {
×
464
    return ret;
×
465
  }
×
466

467
  if ((pfds[0].revents & POLLIN) != 0 && (pfds[1].revents & POLLIN) == 0) {
×
468
    *fdPtr = pfds[0].fd;
×
469
  }
×
470
  else if ((pfds[1].revents & POLLIN) != 0 && (pfds[0].revents & POLLIN) == 0) {
×
471
    *fdPtr = pfds[1].fd;
×
472
  }
×
473
  else if(ret == 2) {
×
474
    *fdPtr = pfds.at(dns_random_uint32() % 2).fd;
×
475
  }
×
476
  else {
×
477
    *fdPtr = -1; // should never happen
×
478
  }
×
479

480
  return 1;
×
481
}
×
482

483

484
string humanDuration(time_t passed)
485
{
4✔
486
  ostringstream ret;
4✔
487
  if(passed<60)
4!
488
    ret<<passed<<" seconds";
4✔
489
  else if(passed<3600)
×
490
    ret<<std::setprecision(2)<<passed/60.0<<" minutes";
×
491
  else if(passed<86400)
×
492
    ret<<std::setprecision(3)<<passed/3600.0<<" hours";
×
493
  else if(passed<(86400*30.41))
×
494
    ret<<std::setprecision(3)<<passed/86400.0<<" days";
×
495
  else
×
496
    ret<<std::setprecision(3)<<passed/(86400*30.41)<<" months";
×
497

498
  return ret.str();
4✔
499
}
4✔
500

501
string unquotify(const string &item)
502
{
11✔
503
  if(item.size()<2)
11✔
504
    return item;
2✔
505

506
  string::size_type bpos=0, epos=item.size();
9✔
507

508
  if(item[0]=='"')
9!
509
    bpos=1;
9✔
510

511
  if(item[epos-1]=='"')
9!
512
    epos-=1;
9✔
513

514
  return item.substr(bpos,epos-bpos);
9✔
515
}
11✔
516

517
void stripLine(string &line)
518
{
×
519
  string::size_type pos=line.find_first_of("\r\n");
×
520
  if(pos!=string::npos) {
×
521
    line.resize(pos);
×
522
  }
×
523
}
×
524

525
string urlEncode(const string &text)
526
{
×
527
  string ret;
×
528
  for(char i : text)
×
529
    if(i==' ')ret.append("%20");
×
530
    else ret.append(1,i);
×
531
  return ret;
×
532
}
×
533

534
static size_t getMaxHostNameSize()
535
{
318✔
536
#if defined(HOST_NAME_MAX)
318✔
537
  return HOST_NAME_MAX;
318✔
538
#endif
×
539

540
#if defined(_SC_HOST_NAME_MAX)
×
541
  auto tmp = sysconf(_SC_HOST_NAME_MAX);
×
542
  if (tmp != -1) {
×
543
    return tmp;
×
544
  }
×
545
#endif
×
546

547
  const size_t maxHostNameSize = 255;
×
548
  return maxHostNameSize;
×
549
}
×
550

551
std::optional<string> getHostname()
552
{
318✔
553
  const size_t maxHostNameBufSize = getMaxHostNameSize() + 1;
318✔
554
  std::string hostname;
318✔
555
  hostname.resize(maxHostNameBufSize, 0);
318✔
556

557
  if (gethostname(hostname.data(), maxHostNameBufSize) == -1) {
318!
558
    return std::nullopt;
×
559
  }
×
560

561
  hostname.resize(strlen(hostname.c_str()));
318✔
562
  return std::make_optional(hostname);
318✔
563
}
318✔
564

565
std::string getCarbonHostName()
566
{
162✔
567
  auto hostname = getHostname();
162✔
568
  if (!hostname.has_value()) {
162!
569
    throw std::runtime_error(stringerror());
×
570
  }
×
571

572
  std::replace(hostname->begin(), hostname->end(), '.', '_');
162✔
573
  return *hostname;
162✔
574
}
162✔
575

576
string bitFlip(const string &str)
577
{
×
578
  string::size_type pos=0, epos=str.size();
×
579
  string ret;
×
580
  ret.reserve(epos);
×
581
  for(;pos < epos; ++pos)
×
582
    ret.append(1, ~str[pos]);
×
583
  return ret;
×
584
}
×
585

586
void cleanSlashes(string &str)
587
{
4,965✔
588
  string out;
4,965✔
589
  bool keepNextSlash = true;
4,965✔
590
  for (const auto& value : str) {
99,463✔
591
    if (value == '/') {
99,463✔
592
      if (keepNextSlash) {
6,898✔
593
        keepNextSlash = false;
6,704✔
594
      }
6,704✔
595
      else {
194✔
596
        continue;
194✔
597
      }
194✔
598
    }
6,898✔
599
    else {
92,565✔
600
      keepNextSlash = true;
92,565✔
601
    }
92,565✔
602
    out.append(1, value);
99,269✔
603
  }
99,269✔
604
  str = std::move(out);
4,965✔
605
}
4,965✔
606

607
bool IpToU32(const string &str, uint32_t *ip)
608
{
×
609
  if(str.empty()) {
×
610
    *ip=0;
×
611
    return true;
×
612
  }
×
613

614
  struct in_addr inp;
×
615
  if(inet_aton(str.c_str(), &inp)) {
×
616
    *ip=inp.s_addr;
×
617
    return true;
×
618
  }
×
619
  return false;
×
620
}
×
621

622
string U32ToIP(uint32_t val)
623
{
×
624
  char tmp[17];
×
625
  snprintf(tmp, sizeof(tmp), "%u.%u.%u.%u",
×
626
           (val >> 24)&0xff,
×
627
           (val >> 16)&0xff,
×
628
           (val >>  8)&0xff,
×
629
           (val      )&0xff);
×
630
  return string(tmp);
×
631
}
×
632

633

634
string makeHexDump(const string& str, const string& sep)
635
{
1,259✔
636
  std::array<char, 5> tmp;
1,259✔
637
  string ret;
1,259✔
638
  ret.reserve(static_cast<size_t>(str.size() * (2 + sep.size())));
1,259✔
639

640
  for (char n : str) {
92,790✔
641
    snprintf(tmp.data(), tmp.size(), "%02x", static_cast<unsigned char>(n));
92,790✔
642
    ret += tmp.data();
92,790✔
643
    ret += sep;
92,790✔
644
  }
92,790✔
645
  return ret;
1,259✔
646
}
1,259✔
647

648
string makeBytesFromHex(const string &in) {
9✔
649
  if (in.size() % 2 != 0) {
9✔
650
    throw std::range_error("odd number of bytes in hex string");
3✔
651
  }
3✔
652
  string ret;
6✔
653
  ret.reserve(in.size() / 2);
6✔
654

655
  for (size_t i = 0; i < in.size(); i += 2) {
36✔
656
    const auto numStr = in.substr(i, 2);
33✔
657
    unsigned int num = 0;
33✔
658
    if (sscanf(numStr.c_str(), "%02x", &num) != 1) {
33✔
659
      throw std::range_error("Invalid value while parsing the hex string '" + in + "'");
3✔
660
    }
3✔
661
    ret.push_back(static_cast<uint8_t>(num));
30✔
662
  }
30✔
663

664
  return ret;
3✔
665
}
6✔
666

667
void normalizeTV(struct timeval& tv)
668
{
98,631✔
669
  if(tv.tv_usec > 1000000) {
98,631✔
670
    ++tv.tv_sec;
5,448✔
671
    tv.tv_usec-=1000000;
5,448✔
672
  }
5,448✔
673
  else if(tv.tv_usec < 0) {
93,183✔
674
    --tv.tv_sec;
44,386✔
675
    tv.tv_usec+=1000000;
44,386✔
676
  }
44,386✔
677
}
98,631✔
678

679
struct timeval operator+(const struct timeval& lhs, const struct timeval& rhs)
680
{
9,676✔
681
  struct timeval ret;
9,676✔
682
  ret.tv_sec=lhs.tv_sec + rhs.tv_sec;
9,676✔
683
  ret.tv_usec=lhs.tv_usec + rhs.tv_usec;
9,676✔
684
  normalizeTV(ret);
9,676✔
685
  return ret;
9,676✔
686
}
9,676✔
687

688
struct timeval operator-(const struct timeval& lhs, const struct timeval& rhs)
689
{
87,226✔
690
  struct timeval ret;
87,226✔
691
  ret.tv_sec=lhs.tv_sec - rhs.tv_sec;
87,226✔
692
  ret.tv_usec=lhs.tv_usec - rhs.tv_usec;
87,226✔
693
  normalizeTV(ret);
87,226✔
694
  return ret;
87,226✔
695
}
87,226✔
696

697
pair<string, string> splitField(const string& inp, char sepa)
698
{
729,363✔
699
  pair<string, string> ret;
729,363✔
700
  string::size_type cpos=inp.find(sepa);
729,363✔
701
  if(cpos==string::npos)
729,363✔
702
    ret.first=inp;
336,312✔
703
  else {
393,051✔
704
    ret.first=inp.substr(0, cpos);
393,051✔
705
    ret.second=inp.substr(cpos+1);
393,051✔
706
  }
393,051✔
707
  return ret;
729,363✔
708
}
729,363✔
709

710
int logFacilityToLOG(unsigned int facility)
711
{
×
712
  switch(facility) {
×
713
  case 0:
×
714
    return LOG_LOCAL0;
×
715
  case 1:
×
716
    return(LOG_LOCAL1);
×
717
  case 2:
×
718
    return(LOG_LOCAL2);
×
719
  case 3:
×
720
    return(LOG_LOCAL3);
×
721
  case 4:
×
722
    return(LOG_LOCAL4);
×
723
  case 5:
×
724
    return(LOG_LOCAL5);
×
725
  case 6:
×
726
    return(LOG_LOCAL6);
×
727
  case 7:
×
728
    return(LOG_LOCAL7);
×
729
  default:
×
730
    return -1;
×
731
  }
×
732
}
×
733

734
std::optional<int> logFacilityFromString(std::string facilityStr)
735
{
×
736
  static std::unordered_map<std::string, int> const s_facilities = {
×
737
    {"local0", LOG_LOCAL0},
×
738
    {"log_local0", LOG_LOCAL0},
×
739
    {"local1", LOG_LOCAL1},
×
740
    {"log_local1", LOG_LOCAL1},
×
741
    {"local2", LOG_LOCAL2},
×
742
    {"log_local2", LOG_LOCAL2},
×
743
    {"local3", LOG_LOCAL3},
×
744
    {"log_local3", LOG_LOCAL3},
×
745
    {"local4", LOG_LOCAL4},
×
746
    {"log_local4", LOG_LOCAL4},
×
747
    {"local5", LOG_LOCAL5},
×
748
    {"log_local5", LOG_LOCAL5},
×
749
    {"local6", LOG_LOCAL6},
×
750
    {"log_local6", LOG_LOCAL6},
×
751
    {"local7", LOG_LOCAL7},
×
752
    {"log_local7", LOG_LOCAL7},
×
753
    /* most of these likely make very little sense
754
       for us, but why not? */
755
    {"kern", LOG_KERN},
×
756
    {"log_kern", LOG_KERN},
×
757
    {"user", LOG_USER},
×
758
    {"log_user", LOG_USER},
×
759
    {"mail", LOG_MAIL},
×
760
    {"log_mail", LOG_MAIL},
×
761
    {"daemon", LOG_DAEMON},
×
762
    {"log_daemon", LOG_DAEMON},
×
763
    {"auth", LOG_AUTH},
×
764
    {"log_auth", LOG_AUTH},
×
765
    {"syslog", LOG_SYSLOG},
×
766
    {"log_syslog", LOG_SYSLOG},
×
767
    {"lpr", LOG_LPR},
×
768
    {"log_lpr", LOG_LPR},
×
769
    {"news", LOG_NEWS},
×
770
    {"log_news", LOG_NEWS},
×
771
    {"uucp", LOG_UUCP},
×
772
    {"log_uucp", LOG_UUCP},
×
773
    {"cron", LOG_CRON},
×
774
    {"log_cron", LOG_CRON},
×
775
    {"authpriv", LOG_AUTHPRIV},
×
776
    {"log_authpriv", LOG_AUTHPRIV},
×
777
    {"ftp", LOG_FTP},
×
778
    {"log_ftp", LOG_FTP}
×
779
  };
×
780

781
  toLowerInPlace(facilityStr);
×
782
  auto facilityIt = s_facilities.find(facilityStr);
×
783
  if (facilityIt == s_facilities.end()) {
×
784
    return std::nullopt;
×
785
  }
×
786

787
  return facilityIt->second;
×
788
}
×
789

790
string stripDot(const string& dom)
791
{
671,527✔
792
  if(dom.empty())
671,527✔
793
    return dom;
3✔
794

795
  if(dom[dom.size()-1]!='.')
671,524✔
796
    return dom;
671,077✔
797

798
  return dom.substr(0,dom.size()-1);
447✔
799
}
671,524✔
800

801
int makeIPv6sockaddr(const std::string& addr, struct sockaddr_in6* ret)
802
{
1,273,911✔
803
  if (addr.empty()) {
1,273,911✔
804
    return -1;
16✔
805
  }
16✔
806

807
  string ourAddr(addr);
1,273,895✔
808
  std::optional<uint16_t> port = std::nullopt;
1,273,895✔
809

810
  if (addr[0] == '[') { // [::]:53 style address
1,273,895✔
811
    string::size_type pos = addr.find(']');
65,972✔
812
    if (pos == string::npos) {
65,972!
813
      return -1;
×
814
    }
×
815

816
    ourAddr.assign(addr.c_str() + 1, pos - 1);
65,972✔
817
    if (pos + 1 != addr.size()) { // complete after ], no port specified
65,972✔
818
      if (pos + 2 > addr.size() || addr[pos + 1] != ':') {
65,953!
819
        return -1;
×
820
      }
×
821

822
      try {
65,953✔
823
        auto tmpPort = pdns::checked_stoi<uint16_t>(addr.substr(pos + 2));
65,953✔
824
        port = std::make_optional(tmpPort);
65,953✔
825
      }
65,953✔
826
      catch (const std::out_of_range&) {
65,953✔
827
        return -1;
20✔
828
      }
20✔
829
    }
65,953✔
830
  }
65,972✔
831

832
  ret->sin6_scope_id = 0;
1,273,875✔
833
  ret->sin6_family = AF_INET6;
1,273,875✔
834

835
  if (inet_pton(AF_INET6, ourAddr.c_str(), (void*)&ret->sin6_addr) != 1) {
1,273,875✔
836
    struct addrinfo hints{};
422✔
837
    std::memset(&hints, 0, sizeof(struct addrinfo));
422✔
838
    hints.ai_flags = AI_NUMERICHOST;
422✔
839
    hints.ai_family = AF_INET6;
422✔
840

841
    struct addrinfo* res = nullptr;
422✔
842
    // getaddrinfo has anomalous return codes, anything nonzero is an error, positive or negative
843
    if (getaddrinfo(ourAddr.c_str(), nullptr, &hints, &res) != 0) {
422!
844
      return -1;
422✔
845
    }
422✔
846

847
    memcpy(ret, res->ai_addr, res->ai_addrlen);
×
848
    freeaddrinfo(res);
×
849
  }
×
850

851
  if (port.has_value()) {
1,273,453✔
852
    ret->sin6_port = htons(*port);
65,933✔
853
  }
65,933✔
854

855
  return 0;
1,273,453✔
856
}
1,273,875✔
857

858
int makeIPv4sockaddr(const std::string& str, struct sockaddr_in* ret)
859
{
1,994,068✔
860
  if(str.empty()) {
1,994,068✔
861
    return -1;
5✔
862
  }
5✔
863
  struct in_addr inp;
1,994,063✔
864

865
  string::size_type pos = str.find(':');
1,994,063✔
866
  if(pos == string::npos) { // no port specified, not touching the port
1,994,063✔
867
    if(inet_aton(str.c_str(), &inp)) {
853,000✔
868
      ret->sin_addr.s_addr=inp.s_addr;
852,918✔
869
      return 0;
852,918✔
870
    }
852,918✔
871
    return -1;
82✔
872
  }
853,000✔
873
  if(!*(str.c_str() + pos + 1)) // trailing :
1,141,063!
874
    return -1;
×
875

876
  char *eptr = const_cast<char*>(str.c_str()) + str.size();
1,141,063✔
877
  int port = strtol(str.c_str() + pos + 1, &eptr, 10);
1,141,063✔
878
  if (port < 0 || port > 65535)
1,141,063✔
879
    return -1;
20✔
880

881
  if(*eptr)
1,141,043✔
882
    return -1;
933,035✔
883

884
  ret->sin_port = htons(port);
208,008✔
885
  if(inet_aton(str.substr(0, pos).c_str(), &inp)) {
208,008!
886
    ret->sin_addr.s_addr=inp.s_addr;
208,008✔
887
    return 0;
208,008✔
888
  }
208,008✔
UNCOV
889
  return -1;
×
890
}
208,008✔
891

892
int makeUNsockaddr(const std::string& path, struct sockaddr_un* ret)
893
{
1,284✔
894
  if (path.empty())
1,284✔
895
    return -1;
4✔
896

897
  memset(ret, 0, sizeof(struct sockaddr_un));
1,280✔
898
  ret->sun_family = AF_UNIX;
1,280✔
899
  if (path.length() >= sizeof(ret->sun_path))
1,280!
900
    return -1;
×
901

902
  path.copy(ret->sun_path, sizeof(ret->sun_path), 0);
1,280✔
903
  return 0;
1,280✔
904
}
1,280✔
905

906
//! read a line of text from a FILE* to a std::string, returns false on 'no data'
907
bool stringfgets(FILE* fp, std::string& line)
908
{
4,196,605✔
909
  char buffer[1024];
4,196,605✔
910
  line.clear();
4,196,605✔
911

912
  do {
4,196,828✔
913
    if(!fgets(buffer, sizeof(buffer), fp))
4,196,828✔
914
      return !line.empty();
4,052✔
915

916
    line.append(buffer);
4,192,776✔
917
  } while(!strchr(buffer, '\n'));
4,192,776✔
918
  return true;
4,192,553✔
919
}
4,196,605✔
920

921
bool readFileIfThere(const char* fname, std::string* line)
922
{
314✔
923
  line->clear();
314✔
924
  auto filePtr = pdns::UniqueFilePtr(fopen(fname, "r"));
314✔
925
  if (!filePtr) {
314!
926
    return false;
×
927
  }
×
928
  return stringfgets(filePtr.get(), *line);
314✔
929
}
314✔
930

931
Regex::Regex(const string &expr)
932
{
51✔
933
  if(regcomp(&d_preg, expr.c_str(), REG_ICASE|REG_NOSUB|REG_EXTENDED))
51!
934
    throw PDNSException("Regular expression did not compile");
×
935
}
51✔
936

937
// if you end up here because valgrind told you were are doing something wrong
938
// with msgh->msg_controllen, please refer to https://github.com/PowerDNS/pdns/pull/3962
939
// first.
940
// Note that cmsgbuf should be aligned the same as a struct cmsghdr
941
void addCMsgSrcAddr(struct msghdr* msgh, cmsgbuf_aligned* cmsgbuf, const ComboAddress* source, int itfIndex)
942
{
28,335✔
943
  struct cmsghdr *cmsg = nullptr;
28,335✔
944

945
  if(source->sin4.sin_family == AF_INET6) {
28,335✔
946
    struct in6_pktinfo *pkt;
1✔
947

948
    msgh->msg_control = cmsgbuf;
1✔
949
#if !defined( __APPLE__ )
1✔
950
    /* CMSG_SPACE is not a constexpr on macOS */
951
    static_assert(CMSG_SPACE(sizeof(*pkt)) <= sizeof(*cmsgbuf), "Buffer is too small for in6_pktinfo");
1✔
952
#else /* __APPLE__ */
953
    if (CMSG_SPACE(sizeof(*pkt)) > sizeof(*cmsgbuf)) {
954
      throw std::runtime_error("Buffer is too small for in6_pktinfo");
955
    }
956
#endif /* __APPLE__ */
957
    msgh->msg_controllen = CMSG_SPACE(sizeof(*pkt));
1✔
958

959
    cmsg = CMSG_FIRSTHDR(msgh);
1!
960
    cmsg->cmsg_level = IPPROTO_IPV6;
1✔
961
    cmsg->cmsg_type = IPV6_PKTINFO;
1✔
962
    cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
1✔
963

964
    pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
1✔
965
    // Include the padding to stop valgrind complaining about passing uninitialized data
966
    memset(pkt, 0, CMSG_SPACE(sizeof(*pkt)));
1✔
967
    pkt->ipi6_addr = source->sin6.sin6_addr;
1✔
968
    pkt->ipi6_ifindex = itfIndex;
1✔
969
  }
1✔
970
  else {
28,334✔
971
#if defined(IP_PKTINFO)
28,334✔
972
    struct in_pktinfo *pkt;
28,334✔
973

974
    msgh->msg_control = cmsgbuf;
28,334✔
975
#if !defined( __APPLE__ )
28,334✔
976
    /* CMSG_SPACE is not a constexpr on macOS */
977
    static_assert(CMSG_SPACE(sizeof(*pkt)) <= sizeof(*cmsgbuf), "Buffer is too small for in_pktinfo");
28,334✔
978
#else /* __APPLE__ */
979
    if (CMSG_SPACE(sizeof(*pkt)) > sizeof(*cmsgbuf)) {
980
      throw std::runtime_error("Buffer is too small for in_pktinfo");
981
    }
982
#endif /* __APPLE__ */
983
    msgh->msg_controllen = CMSG_SPACE(sizeof(*pkt));
28,334✔
984

985
    cmsg = CMSG_FIRSTHDR(msgh);
28,334!
986
    cmsg->cmsg_level = IPPROTO_IP;
28,334✔
987
    cmsg->cmsg_type = IP_PKTINFO;
28,334✔
988
    cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
28,334✔
989

990
    pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
28,334✔
991
    // Include the padding to stop valgrind complaining about passing uninitialized data
992
    memset(pkt, 0, CMSG_SPACE(sizeof(*pkt)));
28,334✔
993
    pkt->ipi_spec_dst = source->sin4.sin_addr;
28,334✔
994
    pkt->ipi_ifindex = itfIndex;
28,334✔
995
#elif defined(IP_SENDSRCADDR)
996
    struct in_addr *in;
997

998
    msgh->msg_control = cmsgbuf;
999
#if !defined( __APPLE__ )
1000
    static_assert(CMSG_SPACE(sizeof(*in)) <= sizeof(*cmsgbuf), "Buffer is too small for in_addr");
1001
#else /* __APPLE__ */
1002
    if (CMSG_SPACE(sizeof(*in)) > sizeof(*cmsgbuf)) {
1003
      throw std::runtime_error("Buffer is too small for in_addr");
1004
    }
1005
#endif /* __APPLE__ */
1006
    msgh->msg_controllen = CMSG_SPACE(sizeof(*in));
1007

1008
    cmsg = CMSG_FIRSTHDR(msgh);
1009
    cmsg->cmsg_level = IPPROTO_IP;
1010
    cmsg->cmsg_type = IP_SENDSRCADDR;
1011
    cmsg->cmsg_len = CMSG_LEN(sizeof(*in));
1012

1013
    // Include the padding to stop valgrind complaining about passing uninitialized data
1014
    in = (struct in_addr *) CMSG_DATA(cmsg);
1015
    memset(in, 0, CMSG_SPACE(sizeof(*in)));
1016
    *in = source->sin4.sin_addr;
1017
#endif
1018
  }
28,334✔
1019
}
28,335✔
1020

1021
unsigned int getFilenumLimit(bool hardOrSoft)
1022
{
157✔
1023
  struct rlimit rlim;
157✔
1024
  if(getrlimit(RLIMIT_NOFILE, &rlim) < 0)
157!
1025
    unixDie("Requesting number of available file descriptors");
×
1026
  return hardOrSoft ? rlim.rlim_max : rlim.rlim_cur;
157!
1027
}
157✔
1028

1029
void setFilenumLimit(unsigned int lim)
1030
{
×
1031
  struct rlimit rlim;
×
1032

1033
  if(getrlimit(RLIMIT_NOFILE, &rlim) < 0)
×
1034
    unixDie("Requesting number of available file descriptors");
×
1035
  rlim.rlim_cur=lim;
×
1036
  if(setrlimit(RLIMIT_NOFILE, &rlim) < 0)
×
1037
    unixDie("Setting number of available file descriptors");
×
1038
}
×
1039

1040
bool setSocketTimestamps(int fd)
1041
{
512✔
1042
#ifdef SO_TIMESTAMP
512✔
1043
  int on=1;
512✔
1044
  return setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, (char*)&on, sizeof(on)) == 0;
512✔
1045
#else
1046
  return true; // we pretend this happened.
1047
#endif
1048
}
512✔
1049

1050
bool setTCPNoDelay(int sock)
1051
{
5,987✔
1052
  int flag = 1;
5,987✔
1053
  return setsockopt(sock,            /* socket affected */
5,987✔
1054
                    IPPROTO_TCP,     /* set option at TCP level */
5,987✔
1055
                    TCP_NODELAY,     /* name of option */
5,987✔
1056
                    (char *) &flag,  /* the cast is historical cruft */
5,987✔
1057
                    sizeof(flag)) == 0;    /* length of option value */
5,987✔
1058
}
5,987✔
1059

1060

1061
bool setNonBlocking(int sock)
1062
{
145,850✔
1063
  int flags=fcntl(sock,F_GETFL,0);
145,850✔
1064
  if(flags<0 || fcntl(sock, F_SETFL,flags|O_NONBLOCK) <0)
145,850✔
1065
    return false;
×
1066
  return true;
145,850✔
1067
}
145,850✔
1068

1069
bool setBlocking(int sock)
1070
{
87,690✔
1071
  int flags=fcntl(sock,F_GETFL,0);
87,690✔
1072
  if(flags<0 || fcntl(sock, F_SETFL,flags&(~O_NONBLOCK)) <0)
87,690✔
1073
    return false;
×
1074
  return true;
87,690✔
1075
}
87,690✔
1076

1077
bool setReuseAddr(int sock)
1078
{
29✔
1079
  int tmp = 1;
29✔
1080
  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&tmp, static_cast<unsigned>(sizeof tmp))<0)
29!
1081
    throw PDNSException(string("Setsockopt failed: ")+stringerror());
×
1082
  return true;
29✔
1083
}
29✔
1084

1085
bool isNonBlocking(int sock)
1086
{
109,466✔
1087
  int flags=fcntl(sock,F_GETFL,0);
109,466✔
1088
  return flags & O_NONBLOCK;
109,466✔
1089
}
109,466✔
1090

1091
bool setReceiveSocketErrors([[maybe_unused]] int sock, [[maybe_unused]] int af)
1092
{
8,768✔
1093
#ifdef __linux__
8,768✔
1094
  int tmp = 1, ret;
8,768✔
1095
  if (af == AF_INET) {
8,768✔
1096
    ret = setsockopt(sock, IPPROTO_IP, IP_RECVERR, &tmp, sizeof(tmp));
8,753✔
1097
  } else {
8,753✔
1098
    ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVERR, &tmp, sizeof(tmp));
15✔
1099
  }
15✔
1100
  if (ret < 0) {
8,768!
1101
    throw PDNSException(string("Setsockopt failed: ") + stringerror());
×
1102
  }
×
1103
#endif
8,768✔
1104
  return true;
8,768✔
1105
}
8,768✔
1106

1107
// Closes a socket.
1108
int closesocket(int socket)
1109
{
42,342✔
1110
  int ret = ::close(socket);
42,342✔
1111
  if(ret < 0 && errno == ECONNRESET) { // see ticket 192, odd BSD behaviour
42,342!
1112
    return 0;
×
1113
  }
×
1114
  if (ret < 0) {
42,342!
1115
    int err = errno;
×
1116
    throw PDNSException("Error closing socket: " + stringerror(err));
×
1117
  }
×
1118
  return ret;
42,342✔
1119
}
42,342✔
1120

1121
bool setCloseOnExec(int sock)
1122
{
38,123✔
1123
  int flags=fcntl(sock,F_GETFD,0);
38,123✔
1124
  if(flags<0 || fcntl(sock, F_SETFD,flags|FD_CLOEXEC) <0)
38,123!
1125
    return false;
×
1126
  return true;
38,123✔
1127
}
38,123✔
1128

1129
#ifdef __linux__
1130
#include <linux/rtnetlink.h>
1131

1132
int getMACAddress(const ComboAddress& ca, char* dest, size_t destLen)
1133
{
2✔
1134
  struct {
2✔
1135
    struct nlmsghdr headermsg;
2✔
1136
    struct ndmsg neighbormsg;
2✔
1137
  } request;
2✔
1138

1139
  std::array<char, 8192> buffer;
2✔
1140

1141
  auto sock = FDWrapper(socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE));
2✔
1142
  if (sock.getHandle() == -1) {
2!
1143
    return errno;
×
1144
  }
×
1145

1146
  memset(&request, 0, sizeof(request));
2✔
1147
  request.headermsg.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
2✔
1148
  request.headermsg.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
2✔
1149
  request.headermsg.nlmsg_type = RTM_GETNEIGH;
2✔
1150
  request.neighbormsg.ndm_family = ca.sin4.sin_family;
2✔
1151

1152
  while (true) {
2✔
1153
    ssize_t sent = send(sock.getHandle(), &request, sizeof(request), 0);
2✔
1154
    if (sent == -1) {
2!
1155
      if (errno == EINTR) {
×
1156
        continue;
×
1157
      }
×
1158
      return errno;
×
1159
    }
×
1160
    else if (static_cast<size_t>(sent) != sizeof(request)) {
2!
1161
      return EIO;
×
1162
    }
×
1163
    break;
2✔
1164
  }
2✔
1165

1166
  bool done = false;
2✔
1167
  bool foundIP = false;
2✔
1168
  bool foundMAC = false;
2✔
1169
  do {
4✔
1170
    ssize_t got = recv(sock.getHandle(), buffer.data(), buffer.size(), 0);
4✔
1171

1172
    if (got < 0) {
4!
1173
      if (errno == EINTR) {
×
1174
        continue;
×
1175
      }
×
1176
      return errno;
×
1177
    }
×
1178

1179
    size_t remaining = static_cast<size_t>(got);
4✔
1180
    for (struct nlmsghdr* nlmsgheader = reinterpret_cast<struct nlmsghdr*>(buffer.data());
4✔
1181
         done == false && NLMSG_OK (nlmsgheader, remaining);
8!
1182
         nlmsgheader = reinterpret_cast<struct nlmsghdr*>(NLMSG_NEXT(nlmsgheader, remaining))) {
6✔
1183

1184
      if (nlmsgheader->nlmsg_type == NLMSG_DONE) {
6✔
1185
        done = true;
2✔
1186
        break;
2✔
1187
      }
2✔
1188

1189
      auto nd = reinterpret_cast<struct ndmsg*>(NLMSG_DATA(nlmsgheader));
4✔
1190
      auto rtatp = reinterpret_cast<struct rtattr*>(reinterpret_cast<char*>(nd) + NLMSG_ALIGN(sizeof(struct ndmsg)));
4✔
1191
      int rtattrlen = nlmsgheader->nlmsg_len - NLMSG_LENGTH(sizeof(struct ndmsg));
4✔
1192

1193
      if (nd->ndm_family != ca.sin4.sin_family) {
4!
1194
        continue;
×
1195
      }
×
1196

1197
      if (ca.sin4.sin_family == AF_INET6 && ca.sin6.sin6_scope_id != 0 && static_cast<int32_t>(ca.sin6.sin6_scope_id) != nd->ndm_ifindex) {
4!
1198
        continue;
×
1199
      }
×
1200

1201
      for (; done == false && RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) {
20!
1202
        if (rtatp->rta_type == NDA_DST){
16✔
1203
          if (nd->ndm_family == AF_INET) {
4!
1204
            auto inp = reinterpret_cast<struct in_addr*>(RTA_DATA(rtatp));
4✔
1205
            if (inp->s_addr == ca.sin4.sin_addr.s_addr) {
4!
1206
              foundIP = true;
×
1207
            }
×
1208
          }
4✔
1209
          else if (nd->ndm_family == AF_INET6) {
×
1210
            auto inp = reinterpret_cast<struct in6_addr *>(RTA_DATA(rtatp));
×
1211
            if (memcmp(inp->s6_addr, ca.sin6.sin6_addr.s6_addr, sizeof(ca.sin6.sin6_addr.s6_addr)) == 0) {
×
1212
              foundIP = true;
×
1213
            }
×
1214
          }
×
1215
        }
4✔
1216
        else if (rtatp->rta_type == NDA_LLADDR) {
12✔
1217
          if (foundIP) {
4!
1218
            size_t addrLen = rtatp->rta_len - sizeof(struct rtattr);
×
1219
            if (addrLen > destLen) {
×
1220
              return ENOBUFS;
×
1221
            }
×
1222
            memcpy(dest, reinterpret_cast<const char*>(rtatp) + sizeof(struct rtattr), addrLen);
×
1223
            foundMAC = true;
×
1224
            done = true;
×
1225
            break;
×
1226
          }
×
1227
        }
4✔
1228
      }
16✔
1229
    }
4✔
1230
  }
4✔
1231
  while (done == false);
4✔
1232

1233
  return foundMAC ? 0 : ENOENT;
2!
1234
}
2✔
1235
#else
1236
int getMACAddress(const ComboAddress& /* ca */, char* /* dest */, size_t /* len */)
1237
{
1238
  return ENOENT;
1239
}
1240
#endif /* __linux__ */
1241

1242
string getMACAddress(const ComboAddress& ca)
1243
{
×
1244
  string ret;
×
1245
  char tmp[6];
×
1246
  if (getMACAddress(ca, tmp, sizeof(tmp)) == 0) {
×
1247
    ret.append(tmp, sizeof(tmp));
×
1248
  }
×
1249
  return ret;
×
1250
}
×
1251

1252
uint64_t udpErrorStats([[maybe_unused]] const std::string& str)
1253
{
1,845✔
1254
#ifdef __linux__
1,845✔
1255
  ifstream ifs("/proc/net/snmp");
1,845✔
1256
  if (!ifs) {
1,845!
1257
    return 0;
×
1258
  }
×
1259

1260
  string line;
1,845✔
1261
  while (getline(ifs, line)) {
15,230!
1262
    if (boost::starts_with(line, "Udp: ") && isdigit(line.at(5))) {
15,230✔
1263
      vector<string> parts;
1,845✔
1264
      stringtok(parts, line, " \n\t\r");
1,845✔
1265

1266
      if (parts.size() < 7) {
1,845!
1267
        break;
×
1268
      }
×
1269

1270
      if (str == "udp-rcvbuf-errors") {
1,845!
1271
        return std::stoull(parts.at(5));
×
1272
      }
×
1273
      else if (str == "udp-sndbuf-errors") {
1,845✔
1274
        return std::stoull(parts.at(6));
369✔
1275
      }
369✔
1276
      else if (str == "udp-noport-errors") {
1,476✔
1277
        return std::stoull(parts.at(2));
369✔
1278
      }
369✔
1279
      else if (str == "udp-in-errors") {
1,107✔
1280
        return std::stoull(parts.at(3));
369✔
1281
      }
369✔
1282
      else if (parts.size() >= 8 && str == "udp-in-csum-errors") {
738!
1283
        return std::stoull(parts.at(7));
369✔
1284
      }
369✔
1285
      else {
369✔
1286
        return 0;
369✔
1287
      }
369✔
1288
    }
1,845✔
1289
  }
15,230✔
1290
#endif
×
1291
  return 0;
×
1292
}
1,845✔
1293

1294
uint64_t udp6ErrorStats([[maybe_unused]] const std::string& str)
1295
{
1,845✔
1296
#ifdef __linux__
1,845✔
1297
  const std::map<std::string, std::string> keys = {
1,845✔
1298
    { "udp6-in-errors", "Udp6InErrors" },
1,845✔
1299
    { "udp6-recvbuf-errors", "Udp6RcvbufErrors" },
1,845✔
1300
    { "udp6-sndbuf-errors", "Udp6SndbufErrors" },
1,845✔
1301
    { "udp6-noport-errors", "Udp6NoPorts" },
1,845✔
1302
    { "udp6-in-csum-errors", "Udp6InCsumErrors" }
1,845✔
1303
  };
1,845✔
1304

1305
  auto key = keys.find(str);
1,845✔
1306
  if (key == keys.end()) {
1,845!
1307
    return 0;
×
1308
  }
×
1309

1310
  ifstream ifs("/proc/net/snmp6");
1,845✔
1311
  if (!ifs) {
1,845!
1312
    return 0;
×
1313
  }
×
1314

1315
  std::string line;
1,845✔
1316
  while (getline(ifs, line)) {
141,657!
1317
    if (!boost::starts_with(line, key->second)) {
141,657✔
1318
      continue;
139,812✔
1319
    }
139,812✔
1320

1321
    std::vector<std::string> parts;
1,845✔
1322
    stringtok(parts, line, " \n\t\r");
1,845✔
1323

1324
    if (parts.size() != 2) {
1,845!
1325
      return 0;
×
1326
    }
×
1327

1328
    return std::stoull(parts.at(1));
1,845✔
1329
  }
1,845✔
UNCOV
1330
#endif
×
UNCOV
1331
  return 0;
×
1332
}
1,845✔
1333

1334
uint64_t tcpErrorStats(const std::string& /* str */)
1335
{
230✔
1336
#ifdef __linux__
230✔
1337
  ifstream ifs("/proc/net/netstat");
230✔
1338
  if (!ifs) {
230!
1339
    return 0;
×
1340
  }
×
1341

1342
  string line;
230✔
1343
  vector<string> parts;
230✔
1344
  while (getline(ifs,line)) {
460!
1345
    if (line.size() > 9 && boost::starts_with(line, "TcpExt: ") && isdigit(line.at(8))) {
460!
1346
      stringtok(parts, line, " \n\t\r");
230✔
1347

1348
      if (parts.size() < 21) {
230!
1349
        break;
×
1350
      }
×
1351

1352
      return std::stoull(parts.at(20));
230✔
1353
    }
230✔
1354
  }
460✔
1355
#endif
×
1356
  return 0;
×
1357
}
230✔
1358

1359
uint64_t getCPUIOWait(const std::string& /* str */)
1360
{
365✔
1361
#ifdef __linux__
365✔
1362
  ifstream ifs("/proc/stat");
365✔
1363
  if (!ifs) {
365!
1364
    return 0;
×
1365
  }
×
1366

1367
  string line;
365✔
1368
  vector<string> parts;
365✔
1369
  while (getline(ifs, line)) {
365!
1370
    if (boost::starts_with(line, "cpu ")) {
365!
1371
      stringtok(parts, line, " \n\t\r");
365✔
1372

1373
      if (parts.size() < 6) {
365!
1374
        break;
×
1375
      }
×
1376

1377
      return std::stoull(parts[5]);
365✔
1378
    }
365✔
1379
  }
365✔
1380
#endif
×
1381
  return 0;
×
1382
}
365✔
1383

1384
uint64_t getCPUSteal(const std::string& /* str */)
1385
{
365✔
1386
#ifdef __linux__
365✔
1387
  ifstream ifs("/proc/stat");
365✔
1388
  if (!ifs) {
365!
1389
    return 0;
×
1390
  }
×
1391

1392
  string line;
365✔
1393
  vector<string> parts;
365✔
1394
  while (getline(ifs, line)) {
365!
1395
    if (boost::starts_with(line, "cpu ")) {
365!
1396
      stringtok(parts, line, " \n\t\r");
365✔
1397

1398
      if (parts.size() < 9) {
365!
1399
        break;
×
1400
      }
×
1401

1402
      return std::stoull(parts[8]);
365✔
1403
    }
365✔
1404
  }
365✔
1405
#endif
×
1406
  return 0;
×
1407
}
365✔
1408

1409
bool getTSIGHashEnum(const DNSName& algoName, TSIGHashEnum& algoEnum)
1410
{
1,179✔
1411
  if (algoName == DNSName("hmac-md5.sig-alg.reg.int") || algoName == DNSName("hmac-md5"))
1,179✔
1412
    algoEnum = TSIG_MD5;
1,092✔
1413
  else if (algoName == DNSName("hmac-sha1"))
87✔
1414
    algoEnum = TSIG_SHA1;
64✔
1415
  else if (algoName == DNSName("hmac-sha224"))
23!
1416
    algoEnum = TSIG_SHA224;
×
1417
  else if (algoName == DNSName("hmac-sha256"))
23✔
1418
    algoEnum = TSIG_SHA256;
4✔
1419
  else if (algoName == DNSName("hmac-sha384"))
19!
1420
    algoEnum = TSIG_SHA384;
×
1421
  else if (algoName == DNSName("hmac-sha512"))
19✔
1422
    algoEnum = TSIG_SHA512;
11✔
1423
  else if (algoName == DNSName("gss-tsig"))
8!
1424
    algoEnum = TSIG_GSS;
×
1425
  else {
8✔
1426
     return false;
8✔
1427
  }
8✔
1428
  return true;
1,171✔
1429
}
1,179✔
1430

1431
DNSName getTSIGAlgoName(TSIGHashEnum& algoEnum)
1432
{
120✔
1433
  switch(algoEnum) {
120!
1434
  case TSIG_MD5: return DNSName("hmac-md5.sig-alg.reg.int.");
120!
1435
  case TSIG_SHA1: return DNSName("hmac-sha1.");
×
1436
  case TSIG_SHA224: return DNSName("hmac-sha224.");
×
1437
  case TSIG_SHA256: return DNSName("hmac-sha256.");
×
1438
  case TSIG_SHA384: return DNSName("hmac-sha384.");
×
1439
  case TSIG_SHA512: return DNSName("hmac-sha512.");
×
1440
  case TSIG_GSS: return DNSName("gss-tsig.");
×
1441
  }
120✔
1442
  throw PDNSException("getTSIGAlgoName does not understand given algorithm, please fix!");
×
1443
}
120✔
1444

1445
uint64_t getOpenFileDescriptors(const std::string&)
1446
{
382✔
1447
#ifdef __linux__
382✔
1448
  uint64_t nbFileDescriptors = 0;
382✔
1449
  const auto dirName = "/proc/" + std::to_string(getpid()) + "/fd/";
382✔
1450
  auto directoryError = pdns::visit_directory(dirName, [&nbFileDescriptors]([[maybe_unused]] ino_t inodeNumber, const std::string_view& name) {
23,929✔
1451
    uint32_t num;
23,929✔
1452
    try {
23,929✔
1453
      pdns::checked_stoi_into(num, std::string(name));
23,929✔
1454
      if (std::to_string(num) == name) {
23,929✔
1455
        nbFileDescriptors++;
23,174✔
1456
      }
23,174✔
1457
    } catch (...) {
23,929✔
1458
      // was not a number.
1459
    }
764✔
1460
    return true;
23,938✔
1461
  });
23,929✔
1462
  if (directoryError) {
382!
1463
    return 0U;
×
1464
  }
×
1465
  return nbFileDescriptors;
382✔
1466
#elif defined(__OpenBSD__)
1467
  // FreeBSD also has this in libopenbsd, but I don't know if that's available always
1468
  return getdtablecount();
1469
#else
1470
  return 0U;
1471
#endif
1472
}
382✔
1473

1474
uint64_t getRealMemoryUsage(const std::string&)
1475
{
382✔
1476
#ifdef __linux__
382✔
1477
  ifstream ifs("/proc/self/statm");
382✔
1478
  if(!ifs)
382!
1479
    return 0;
×
1480

1481
  uint64_t size, resident, shared, text, lib, data;
382✔
1482
  ifs >> size >> resident >> shared >> text >> lib >> data;
382✔
1483

1484
  // We used to use "data" here, but it proves unreliable and even is marked "broken"
1485
  // in https://www.kernel.org/doc/html/latest/filesystems/proc.html
1486
  return resident * getpagesize();
382✔
1487
#else
1488
  struct rusage ru;
1489
  if (getrusage(RUSAGE_SELF, &ru) != 0)
1490
    return 0;
1491
  return ru.ru_maxrss * 1024;
1492
#endif
1493
}
382✔
1494

1495

1496
uint64_t getSpecialMemoryUsage(const std::string&)
1497
{
17✔
1498
#ifdef __linux__
17✔
1499
  ifstream ifs("/proc/self/smaps");
17✔
1500
  if(!ifs)
17!
1501
    return 0;
×
1502
  string line;
17✔
1503
  uint64_t bytes=0;
17✔
1504
  string header("Private_Dirty:");
17✔
1505
  while(getline(ifs, line)) {
243,932✔
1506
    if(boost::starts_with(line, header)) {
243,915✔
1507
      bytes += std::stoull(line.substr(header.length() + 1))*1024;
9,800✔
1508
    }
9,800✔
1509
  }
243,915✔
1510
  return bytes;
17✔
1511
#else
1512
  return 0;
1513
#endif
1514
}
17✔
1515

1516
uint64_t getCPUTimeUser(const std::string&)
1517
{
243✔
1518
  struct rusage ru;
243✔
1519
  getrusage(RUSAGE_SELF, &ru);
243✔
1520
  return (ru.ru_utime.tv_sec*1000ULL + ru.ru_utime.tv_usec/1000);
243✔
1521
}
243✔
1522

1523
uint64_t getCPUTimeSystem(const std::string&)
1524
{
243✔
1525
  struct rusage ru;
243✔
1526
  getrusage(RUSAGE_SELF, &ru);
243✔
1527
  return (ru.ru_stime.tv_sec*1000ULL + ru.ru_stime.tv_usec/1000);
243✔
1528
}
243✔
1529

1530
double DiffTime(const struct timespec& first, const struct timespec& second)
1531
{
122✔
1532
  auto seconds = second.tv_sec - first.tv_sec;
122✔
1533
  auto nseconds = second.tv_nsec - first.tv_nsec;
122✔
1534

1535
  if (nseconds < 0) {
122✔
1536
    seconds -= 1;
110✔
1537
    nseconds += 1000000000;
110✔
1538
  }
110✔
1539
  return static_cast<double>(seconds) + static_cast<double>(nseconds) / 1000000000.0;
122✔
1540
}
122✔
1541

1542
double DiffTime(const struct timeval& first, const struct timeval& second)
1543
{
×
1544
  int seconds=second.tv_sec - first.tv_sec;
×
1545
  int useconds=second.tv_usec - first.tv_usec;
×
1546

1547
  if(useconds < 0) {
×
1548
    seconds-=1;
×
1549
    useconds+=1000000;
×
1550
  }
×
1551
  return seconds + useconds/1000000.0;
×
1552
}
×
1553

1554
uid_t strToUID(const string &str)
1555
{
×
1556
  uid_t result = 0;
×
1557
  const char * cstr = str.c_str();
×
1558
  struct passwd * pwd = getpwnam(cstr);
×
1559

1560
  if (pwd == nullptr) {
×
1561
    long long val;
×
1562

1563
    try {
×
1564
      val = stoll(str);
×
1565
    }
×
1566
    catch(std::exception& e) {
×
1567
      throw runtime_error((boost::format("Error: Unable to parse user ID %s") % cstr).str() );
×
1568
    }
×
1569

1570
    if (val < std::numeric_limits<uid_t>::min() || val > std::numeric_limits<uid_t>::max()) {
×
1571
      throw runtime_error((boost::format("Error: Unable to parse user ID %s") % cstr).str() );
×
1572
    }
×
1573

1574
    result = static_cast<uid_t>(val);
×
1575
  }
×
1576
  else {
×
1577
    result = pwd->pw_uid;
×
1578
  }
×
1579

1580
  return result;
×
1581
}
×
1582

1583
gid_t strToGID(const string &str)
1584
{
×
1585
  gid_t result = 0;
×
1586
  const char * cstr = str.c_str();
×
1587
  struct group * grp = getgrnam(cstr);
×
1588

1589
  if (grp == nullptr) {
×
1590
    long long val;
×
1591

1592
    try {
×
1593
      val = stoll(str);
×
1594
    }
×
1595
    catch(std::exception& e) {
×
1596
      throw runtime_error((boost::format("Error: Unable to parse group ID %s") % cstr).str() );
×
1597
    }
×
1598

1599
    if (val < std::numeric_limits<gid_t>::min() || val > std::numeric_limits<gid_t>::max()) {
×
1600
      throw runtime_error((boost::format("Error: Unable to parse group ID %s") % cstr).str() );
×
1601
    }
×
1602

1603
    result = static_cast<gid_t>(val);
×
1604
  }
×
1605
  else {
×
1606
    result = grp->gr_gid;
×
1607
  }
×
1608

1609
  return result;
×
1610
}
×
1611

1612
bool isSettingThreadCPUAffinitySupported()
1613
{
×
1614
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
1615
  return true;
1616
#else
1617
  return false;
1618
#endif
1619
}
×
1620

1621
int mapThreadToCPUList([[maybe_unused]] pthread_t tid, [[maybe_unused]] const std::set<int>& cpus)
1622
{
×
1623
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
1624
#  ifdef __NetBSD__
1625
  cpuset_t *cpuset;
1626
  cpuset = cpuset_create();
1627
  for (const auto cpuID : cpus) {
1628
    cpuset_set(cpuID, cpuset);
1629
  }
1630

1631
  return pthread_setaffinity_np(tid,
1632
                                cpuset_size(cpuset),
1633
                                cpuset);
1634
#  else
1635
#    ifdef __FreeBSD__
1636
#      define cpu_set_t cpuset_t
1637
#    endif
1638
  cpu_set_t cpuset;
1639
  CPU_ZERO(&cpuset);
1640
  for (const auto cpuID : cpus) {
×
1641
    CPU_SET(cpuID, &cpuset);
×
1642
  }
1643

1644
  return pthread_setaffinity_np(tid,
1645
                                sizeof(cpuset),
1646
                                &cpuset);
1647
#  endif
1648
#else
1649
  return ENOSYS;
1650
#endif /* HAVE_PTHREAD_SETAFFINITY_NP */
1651
}
×
1652

1653
std::vector<ComboAddress> getResolvers(const std::string& resolvConfPath)
1654
{
89✔
1655
  std::vector<ComboAddress> results;
89✔
1656

1657
  ifstream ifs(resolvConfPath);
89✔
1658
  if (!ifs) {
89!
1659
    return results;
×
1660
  }
×
1661

1662
  string line;
89✔
1663
  while(std::getline(ifs, line)) {
1,153✔
1664
    boost::trim_right_if(line, boost::is_any_of(" \r\n\x1a"));
1,064✔
1665
    boost::trim_left(line); // leading spaces, let's be nice
1,064✔
1666

1667
    string::size_type tpos = line.find_first_of(";#");
1,064✔
1668
    if (tpos != string::npos) {
1,064✔
1669
      line.resize(tpos);
622✔
1670
    }
622✔
1671

1672
    if (boost::starts_with(line, "nameserver ") || boost::starts_with(line, "nameserver\t")) {
1,064✔
1673
      vector<string> parts;
90✔
1674
      stringtok(parts, line, " \t,"); // be REALLY nice
90✔
1675
      for (auto iter = parts.begin() + 1; iter != parts.end(); ++iter) {
180✔
1676
        try {
90✔
1677
          results.emplace_back(*iter, 53);
90✔
1678
        }
90✔
1679
        catch(...)
90✔
1680
        {
90✔
1681
        }
×
1682
      }
90✔
1683
    }
90✔
1684
  }
1,064✔
1685

1686
  return results;
89✔
1687
}
89✔
1688

1689
size_t getPipeBufferSize([[maybe_unused]] int fd)
1690
{
9,982✔
1691
#ifdef F_GETPIPE_SZ
9,982✔
1692
  int res = fcntl(fd, F_GETPIPE_SZ);
9,982✔
1693
  if (res == -1) {
9,982!
1694
    return 0;
×
1695
  }
×
1696
  return res;
9,982✔
1697
#else
1698
  errno = ENOSYS;
1699
  return 0;
1700
#endif /* F_GETPIPE_SZ */
1701
}
9,982✔
1702

1703
bool setPipeBufferSize([[maybe_unused]] int fd, [[maybe_unused]] size_t size)
1704
{
9,982✔
1705
#ifdef F_SETPIPE_SZ
9,982✔
1706
  if (size > static_cast<size_t>(std::numeric_limits<int>::max())) {
9,982!
1707
    errno = EINVAL;
×
1708
    return false;
×
1709
  }
×
1710
  int newSize = static_cast<int>(size);
9,982✔
1711
  int res = fcntl(fd, F_SETPIPE_SZ, newSize);
9,982✔
1712
  if (res == -1) {
9,982✔
1713
    return false;
4,081✔
1714
  }
4,081✔
1715
  return true;
5,901✔
1716
#else
1717
  errno = ENOSYS;
1718
  return false;
1719
#endif /* F_SETPIPE_SZ */
1720
}
9,982✔
1721

1722
DNSName reverseNameFromIP(const ComboAddress& ip)
1723
{
9✔
1724
  if (ip.isIPv4()) {
9✔
1725
    std::string result("in-addr.arpa.");
3✔
1726
    auto ptr = reinterpret_cast<const uint8_t*>(&ip.sin4.sin_addr.s_addr);
3✔
1727
    for (size_t idx = 0; idx < sizeof(ip.sin4.sin_addr.s_addr); idx++) {
15✔
1728
      result = std::to_string(ptr[idx]) + "." + result;
12✔
1729
    }
12✔
1730
    return DNSName(result);
3✔
1731
  }
3✔
1732
  else if (ip.isIPv6()) {
6!
1733
    std::string result("ip6.arpa.");
6✔
1734
    auto ptr = reinterpret_cast<const uint8_t*>(&ip.sin6.sin6_addr.s6_addr[0]);
6✔
1735
    for (size_t idx = 0; idx < sizeof(ip.sin6.sin6_addr.s6_addr); idx++) {
102✔
1736
      std::stringstream stream;
96✔
1737
      stream << std::hex << (ptr[idx] & 0x0F);
96✔
1738
      stream << '.';
96✔
1739
      stream << std::hex << (((ptr[idx]) >> 4) & 0x0F);
96✔
1740
      stream << '.';
96✔
1741
      result = stream.str() + result;
96✔
1742
    }
96✔
1743
    return DNSName(result);
6✔
1744
  }
6✔
1745

1746
  throw std::runtime_error("Calling reverseNameFromIP() for an address which is neither an IPv4 nor an IPv6");
×
1747
}
9✔
1748

1749
std::string makeLuaString(const std::string& in)
1750
{
×
1751
  ostringstream str;
×
1752

1753
  str<<'"';
×
1754

1755
  char item[5];
×
1756
  for (unsigned char n : in) {
×
1757
    if (islower(n) || isupper(n)) {
×
1758
      item[0] = n;
×
1759
      item[1] = 0;
×
1760
    }
×
1761
    else {
×
1762
      snprintf(item, sizeof(item), "\\%03d", n);
×
1763
    }
×
1764
    str << item;
×
1765
  }
×
1766

1767
  str<<'"';
×
1768

1769
  return str.str();
×
1770
}
×
1771

1772
size_t parseSVCBValueList(const std::string &in, vector<std::string> &val) {
638✔
1773
  std::string parsed;
638✔
1774
  auto ret = parseRFC1035CharString(in, parsed);
638✔
1775
  parseSVCBValueListFromParsedRFC1035CharString(parsed, val);
638✔
1776
  return ret;
638✔
1777
};
638✔
1778

1779
#ifdef HAVE_CRYPTO_MEMCMP
1780
#include <openssl/crypto.h>
1781
#else /* HAVE_CRYPTO_MEMCMP */
1782
#ifdef HAVE_SODIUM_MEMCMP
1783
#include <sodium.h>
1784
#endif /* HAVE_SODIUM_MEMCMP */
1785
#endif /* HAVE_CRYPTO_MEMCMP */
1786

1787
bool constantTimeStringEquals(const std::string& a, const std::string& b)
1788
{
2,200✔
1789
  if (a.size() != b.size()) {
2,200✔
1790
    return false;
5✔
1791
  }
5✔
1792
  const size_t size = a.size();
2,195✔
1793
#ifdef HAVE_CRYPTO_MEMCMP
2,195✔
1794
  return CRYPTO_memcmp(a.c_str(), b.c_str(), size) == 0;
2,195✔
1795
#else /* HAVE_CRYPTO_MEMCMP */
1796
#ifdef HAVE_SODIUM_MEMCMP
1797
  return sodium_memcmp(a.c_str(), b.c_str(), size) == 0;
1798
#else /* HAVE_SODIUM_MEMCMP */
1799
  const volatile unsigned char *_a = (const volatile unsigned char *) a.c_str();
1800
  const volatile unsigned char *_b = (const volatile unsigned char *) b.c_str();
1801
  unsigned char res = 0;
1802

1803
  for (size_t idx = 0; idx < size; idx++) {
1804
    res |= _a[idx] ^ _b[idx];
1805
  }
1806

1807
  return res == 0;
1808
#endif /* !HAVE_SODIUM_MEMCMP */
1809
#endif /* !HAVE_CRYPTO_MEMCMP */
1810
}
2,200✔
1811

1812
namespace pdns
1813
{
1814
struct CloseDirDeleter
1815
{
1816
  void operator()(DIR* dir) const noexcept {
418✔
1817
    closedir(dir);
418✔
1818
  }
418✔
1819
};
1820

1821
std::optional<std::string> visit_directory(const std::string& directory, const std::function<bool(ino_t inodeNumber, const std::string_view& name)>& visitor)
1822
{
518✔
1823
  auto dirHandle = std::unique_ptr<DIR, CloseDirDeleter>(opendir(directory.c_str()));
518✔
1824
  if (!dirHandle) {
518✔
1825
    auto err = errno;
3✔
1826
    return std::string("Error opening directory '" + directory + "': " + stringerror(err));
3✔
1827
  }
3✔
1828

1829
  bool keepGoing = true;
515✔
1830
  struct dirent* ent = nullptr;
515✔
1831
  // NOLINTNEXTLINE(concurrency-mt-unsafe): readdir is thread-safe nowadays and readdir_r is deprecated
1832
  while (keepGoing && (ent = readdir(dirHandle.get())) != nullptr) {
29,844✔
1833
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay: dirent API
1834
    auto name = std::string_view(ent->d_name, strlen(ent->d_name));
29,324✔
1835
    keepGoing = visitor(ent->d_ino, name);
29,324✔
1836
  }
29,324✔
1837

1838
  return std::nullopt;
515✔
1839
}
518✔
1840

1841
UniqueFilePtr openFileForWriting(const std::string& filePath, mode_t permissions, bool mustNotExist, bool appendIfExists)
1842
{
4✔
1843
  int flags = O_WRONLY | O_CREAT;
4✔
1844
  if (mustNotExist) {
4!
1845
    flags |= O_EXCL;
×
1846
  }
×
1847
  else if (appendIfExists) {
4!
1848
    flags |= O_APPEND;
4✔
1849
  }
4✔
1850
  int fileDesc = open(filePath.c_str(), flags, permissions);
4✔
1851
  if (fileDesc == -1) {
4!
1852
    return {};
×
1853
  }
×
1854
  auto filePtr = pdns::UniqueFilePtr(fdopen(fileDesc, appendIfExists ? "a" : "w"));
4!
1855
  if (!filePtr) {
4!
1856
    auto error = errno;
×
1857
    close(fileDesc);
×
1858
    errno = error;
×
1859
    return {};
×
1860
  }
×
1861
  return filePtr;
4✔
1862
}
4✔
1863

1864
}
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

© 2026 Coveralls, Inc