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

PowerDNS / pdns / 18225652081

03 Oct 2025 02:49PM UTC coverage: 65.825%. Remained the same
18225652081

Pull #16211

github

web-flow
Merge a109c2507 into feeb24672
Pull Request #16211: auth 5.0.x: backport "grow auth source tarballs fatter"

42048 of 92452 branches covered (45.48%)

Branch coverage included in aggregate %.

127983 of 165855 relevant lines covered (77.17%)

5370244.34 hits per line

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

60.2
/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,412,771✔
78
  const char *ptr = static_cast<const char*>(buf);
1,412,771✔
79
  const char *eptr = ptr + count;
1,412,771✔
80

81
  while (ptr != eptr) {
2,825,549✔
82
    auto res = ::write(fileDesc, ptr, eptr - ptr);
1,412,778✔
83
    if (res < 0) {
1,412,778!
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,412,778!
90
      throw std::runtime_error("could not write all bytes, got eof in writen2");
×
91
    }
×
92

93
    ptr += res;
1,412,778✔
94
  }
1,412,778✔
95

96
  return count;
1,412,771✔
97
}
1,412,771✔
98

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

103
  for (;;) {
1,327✔
104
    auto res = read(fileDesc, static_cast<char *>(buffer) + pos, len - pos); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic): it's the API
1,327✔
105
    if (res == 0) {
1,327✔
106
      throw runtime_error("EOF while reading message");
329✔
107
    }
329✔
108
    if (res < 0) {
998!
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);
998✔
116
    if (pos == len) {
998!
117
      break;
998✔
118
    }
998✔
119
  }
998✔
120
  return len;
998✔
121
}
1,327✔
122

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

132
  do {
2,903✔
133
    ssize_t got = read(fd, (char *)buffer + pos, len - pos);
2,903✔
134
    if (got > 0) {
2,903✔
135
      pos += (size_t) got;
1,899✔
136
      if (allowIncomplete) {
1,899!
137
        break;
×
138
      }
×
139
    }
1,899✔
140
    else if (got == 0) {
1,004✔
141
      throw runtime_error("EOF while reading message");
6✔
142
    }
6✔
143
    else {
998✔
144
      if (errno == EAGAIN) {
998!
145
        struct timeval w = ((totalTimeout.tv_sec == 0 && totalTimeout.tv_usec == 0) || idleTimeout <= remainingTime) ? idleTimeout : remainingTime;
998!
146
        int res = waitForData(fd, w.tv_sec, w.tv_usec);
998✔
147
        if (res > 0) {
998!
148
          /* there is data available */
149
        }
998✔
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
      }
998✔
156
      else {
×
157
        unixDie("failed in readn2WithTimeout");
×
158
      }
×
159
    }
998✔
160

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

174
  return len;
1,870✔
175
}
1,876✔
176

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

183
    if (written > 0) {
1,022!
184
      pos += (size_t) written;
1,022✔
185
    }
1,022✔
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
  }
1,022✔
205
  while (pos < len);
1,022!
206

207
  return len;
1,022✔
208
}
1,022✔
209

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

216
  const char* errMsg = nullptr;
1,600✔
217
#ifdef STRERROR_R_CHAR_P
1,600✔
218
  errMsg = strerror_r(errnum, errMsgData.data(), errMsgData.length());
1,600✔
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};
1,600✔
233
  return message;
1,600✔
234
}
1,600✔
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,809✔
299
  time_t now = time(nullptr);
2,809✔
300
  struct tm theTime{};
2,809✔
301
  localtime_r(&now, &theTime);
2,809✔
302
  std::array<char, 30> buffer{};
2,809✔
303
  // YYYY-mm-dd HH:MM:SS TZOFF
304
  size_t ret = strftime(buffer.data(), buffer.size(), "%F %T %z", &theTime);
2,809✔
305
  if (ret == 0) {
2,809!
306
    buffer[0] = '\0';
×
307
  }
×
308
  return {buffer.data()};
2,809✔
309
}
2,809✔
310

311
// returns -1 in case if error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
312
int waitForData(int fileDesc, int seconds, int useconds)
313
{
2,284,124✔
314
  return waitForRWData(fileDesc, true, seconds, useconds);
2,284,124✔
315
}
2,284,124✔
316

317
int waitForRWData(int fileDesc, bool waitForRead, int seconds, int useconds, bool* error, bool* disconnected)
318
{
2,289,435✔
319
  struct pollfd pfd{};
2,289,435✔
320
  memset(&pfd, 0, sizeof(pfd));
2,289,435✔
321
  pfd.fd = fileDesc;
2,289,435✔
322

323
  if (waitForRead) {
2,289,435✔
324
    pfd.events = POLLIN;
2,286,448✔
325
  }
2,286,448✔
326
  else {
2,987✔
327
    pfd.events = POLLOUT;
2,987✔
328
  }
2,987✔
329

330
  int ret = poll(&pfd, 1, seconds * 1000 + useconds/1000);
2,289,435✔
331
  if (ret > 0) {
2,289,435✔
332
    if ((error != nullptr) && (pfd.revents & POLLERR) != 0) {
2,085,182✔
333
      *error = true;
34✔
334
    }
34✔
335
    if ((disconnected != nullptr) && (pfd.revents & POLLHUP) != 0) {
2,085,182✔
336
      *disconnected = true;
34✔
337
    }
34✔
338
  }
2,085,182✔
339

340
  return ret;
2,289,435✔
341
}
2,289,435✔
342

343
// returns -1 in case of error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
344
int waitForMultiData(const set<int>& fds, const int seconds, const int useconds, int* fdOut) {
2,244✔
345
  set<int> realFDs;
2,244✔
346
  for (const auto& fd : fds) {
4,488✔
347
    if (fd >= 0 && realFDs.count(fd) == 0) {
4,488!
348
      realFDs.insert(fd);
4,462✔
349
    }
4,462✔
350
  }
4,488✔
351

352
  std::vector<struct pollfd> pfds(realFDs.size());
2,244✔
353
  memset(pfds.data(), 0, realFDs.size()*sizeof(struct pollfd));
2,244✔
354
  int ctr = 0;
2,244✔
355
  for (const auto& fd : realFDs) {
4,462✔
356
    pfds[ctr].fd = fd;
4,462✔
357
    pfds[ctr].events = POLLIN;
4,462✔
358
    ctr++;
4,462✔
359
  }
4,462✔
360

361
  int ret;
2,244✔
362
  if(seconds >= 0)
2,244!
363
    ret = poll(pfds.data(), realFDs.size(), seconds * 1000 + useconds/1000);
2,244✔
364
  else
×
365
    ret = poll(pfds.data(), realFDs.size(), -1);
×
366
  if(ret <= 0)
2,244✔
367
    return ret;
2,238✔
368

369
  set<int> pollinFDs;
6✔
370
  for (const auto& pfd : pfds) {
6✔
371
    if (pfd.revents & POLLIN) {
6!
372
      pollinFDs.insert(pfd.fd);
6✔
373
    }
6✔
374
  }
6✔
375
  set<int>::const_iterator it(pollinFDs.begin());
6✔
376
  advance(it, dns_random(pollinFDs.size()));
6✔
377
  *fdOut = *it;
6✔
378
  return 1;
6✔
379
}
2,244✔
380

381
// returns -1 in case of error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
382
int waitFor2Data(int fd1, int fd2, int seconds, int useconds, int* fdPtr)
383
{
×
384
  std::array<pollfd,2> pfds{};
×
385
  memset(pfds.data(), 0, pfds.size() * sizeof(struct pollfd));
×
386
  pfds[0].fd = fd1;
×
387
  pfds[1].fd = fd2;
×
388

389
  pfds[0].events= pfds[1].events = POLLIN;
×
390

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

393
  int ret{};
×
394
  if (seconds >= 0) {
×
395
    ret = poll(pfds.data(), nsocks, seconds * 1000 + useconds / 1000);
×
396
  }
×
397
  else {
×
398
    ret = poll(pfds.data(), nsocks, -1);
×
399
  }
×
400
  if (ret <= 0) {
×
401
    return ret;
×
402
  }
×
403

404
  if ((pfds[0].revents & POLLIN) != 0 && (pfds[1].revents & POLLIN) == 0) {
×
405
    *fdPtr = pfds[0].fd;
×
406
  }
×
407
  else if ((pfds[1].revents & POLLIN) != 0 && (pfds[0].revents & POLLIN) == 0) {
×
408
    *fdPtr = pfds[1].fd;
×
409
  }
×
410
  else if(ret == 2) {
×
411
    *fdPtr = pfds.at(dns_random_uint32() % 2).fd;
×
412
  }
×
413
  else {
×
414
    *fdPtr = -1; // should never happen
×
415
  }
×
416

417
  return 1;
×
418
}
×
419

420

421
string humanDuration(time_t passed)
422
{
4✔
423
  ostringstream ret;
4✔
424
  if(passed<60)
4!
425
    ret<<passed<<" seconds";
4✔
426
  else if(passed<3600)
×
427
    ret<<std::setprecision(2)<<passed/60.0<<" minutes";
×
428
  else if(passed<86400)
×
429
    ret<<std::setprecision(3)<<passed/3600.0<<" hours";
×
430
  else if(passed<(86400*30.41))
×
431
    ret<<std::setprecision(3)<<passed/86400.0<<" days";
×
432
  else
×
433
    ret<<std::setprecision(3)<<passed/(86400*30.41)<<" months";
×
434

435
  return ret.str();
4✔
436
}
4✔
437

438
string unquotify(const string &item)
439
{
11✔
440
  if(item.size()<2)
11✔
441
    return item;
2✔
442

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

445
  if(item[0]=='"')
9!
446
    bpos=1;
9✔
447

448
  if(item[epos-1]=='"')
9!
449
    epos-=1;
9✔
450

451
  return item.substr(bpos,epos-bpos);
9✔
452
}
11✔
453

454
void stripLine(string &line)
455
{
×
456
  string::size_type pos=line.find_first_of("\r\n");
×
457
  if(pos!=string::npos) {
×
458
    line.resize(pos);
×
459
  }
×
460
}
×
461

462
string urlEncode(const string &text)
463
{
×
464
  string ret;
×
465
  for(char i : text)
×
466
    if(i==' ')ret.append("%20");
×
467
    else ret.append(1,i);
×
468
  return ret;
×
469
}
×
470

471
static size_t getMaxHostNameSize()
472
{
349✔
473
#if defined(HOST_NAME_MAX)
349✔
474
  return HOST_NAME_MAX;
349✔
475
#endif
×
476

477
#if defined(_SC_HOST_NAME_MAX)
×
478
  auto tmp = sysconf(_SC_HOST_NAME_MAX);
×
479
  if (tmp != -1) {
×
480
    return tmp;
×
481
  }
×
482
#endif
×
483

484
  const size_t maxHostNameSize = 255;
×
485
  return maxHostNameSize;
×
486
}
×
487

488
std::optional<string> getHostname()
489
{
349✔
490
  const size_t maxHostNameBufSize = getMaxHostNameSize() + 1;
349✔
491
  std::string hostname;
349✔
492
  hostname.resize(maxHostNameBufSize, 0);
349✔
493

494
  if (gethostname(hostname.data(), maxHostNameBufSize) == -1) {
349!
495
    return std::nullopt;
×
496
  }
×
497

498
  hostname.resize(strlen(hostname.c_str()));
349✔
499
  return std::make_optional(hostname);
349✔
500
}
349✔
501

502
std::string getCarbonHostName()
503
{
175✔
504
  auto hostname = getHostname();
175✔
505
  if (!hostname.has_value()) {
175!
506
    throw std::runtime_error(stringerror());
×
507
  }
×
508

509
  std::replace(hostname->begin(), hostname->end(), '.', '_');
175✔
510
  return *hostname;
175✔
511
}
175✔
512

513
string bitFlip(const string &str)
514
{
×
515
  string::size_type pos=0, epos=str.size();
×
516
  string ret;
×
517
  ret.reserve(epos);
×
518
  for(;pos < epos; ++pos)
×
519
    ret.append(1, ~str[pos]);
×
520
  return ret;
×
521
}
×
522

523
void cleanSlashes(string &str)
524
{
7,326✔
525
  string out;
7,326✔
526
  bool keepNextSlash = true;
7,326✔
527
  for (const auto& value : str) {
137,549✔
528
    if (value == '/') {
137,549✔
529
      if (keepNextSlash) {
9,098✔
530
        keepNextSlash = false;
8,891✔
531
      }
8,891✔
532
      else {
207✔
533
        continue;
207✔
534
      }
207✔
535
    }
9,098✔
536
    else {
128,451✔
537
      keepNextSlash = true;
128,451✔
538
    }
128,451✔
539
    out.append(1, value);
137,342✔
540
  }
137,342✔
541
  str = std::move(out);
7,326✔
542
}
7,326✔
543

544
bool IpToU32(const string &str, uint32_t *ip)
545
{
×
546
  if(str.empty()) {
×
547
    *ip=0;
×
548
    return true;
×
549
  }
×
550

551
  struct in_addr inp;
×
552
  if(inet_aton(str.c_str(), &inp)) {
×
553
    *ip=inp.s_addr;
×
554
    return true;
×
555
  }
×
556
  return false;
×
557
}
×
558

559
string U32ToIP(uint32_t val)
560
{
×
561
  char tmp[17];
×
562
  snprintf(tmp, sizeof(tmp), "%u.%u.%u.%u",
×
563
           (val >> 24)&0xff,
×
564
           (val >> 16)&0xff,
×
565
           (val >>  8)&0xff,
×
566
           (val      )&0xff);
×
567
  return string(tmp);
×
568
}
×
569

570

571
string makeHexDump(const string& str, const string& sep)
572
{
1,071✔
573
  std::array<char, 5> tmp;
1,071✔
574
  string ret;
1,071✔
575
  ret.reserve(static_cast<size_t>(str.size() * (2 + sep.size())));
1,071✔
576

577
  for (char n : str) {
84,198✔
578
    snprintf(tmp.data(), tmp.size(), "%02x", static_cast<unsigned char>(n));
84,198✔
579
    ret += tmp.data();
84,198✔
580
    ret += sep;
84,198✔
581
  }
84,198✔
582
  return ret;
1,071✔
583
}
1,071✔
584

585
string makeBytesFromHex(const string &in) {
9✔
586
  if (in.size() % 2 != 0) {
9✔
587
    throw std::range_error("odd number of bytes in hex string");
3✔
588
  }
3✔
589
  string ret;
6✔
590
  ret.reserve(in.size() / 2);
6✔
591

592
  for (size_t i = 0; i < in.size(); i += 2) {
36✔
593
    const auto numStr = in.substr(i, 2);
33✔
594
    unsigned int num = 0;
33✔
595
    if (sscanf(numStr.c_str(), "%02x", &num) != 1) {
33✔
596
      throw std::range_error("Invalid value while parsing the hex string '" + in + "'");
3✔
597
    }
3✔
598
    ret.push_back(static_cast<uint8_t>(num));
30✔
599
  }
30✔
600

601
  return ret;
3✔
602
}
6✔
603

604
void normalizeTV(struct timeval& tv)
605
{
195,836✔
606
  if(tv.tv_usec > 1000000) {
195,836✔
607
    ++tv.tv_sec;
4,710✔
608
    tv.tv_usec-=1000000;
4,710✔
609
  }
4,710✔
610
  else if(tv.tv_usec < 0) {
191,126✔
611
    --tv.tv_sec;
47,288✔
612
    tv.tv_usec+=1000000;
47,288✔
613
  }
47,288✔
614
}
195,836✔
615

616
struct timeval operator+(const struct timeval& lhs, const struct timeval& rhs)
617
{
10,689✔
618
  struct timeval ret;
10,689✔
619
  ret.tv_sec=lhs.tv_sec + rhs.tv_sec;
10,689✔
620
  ret.tv_usec=lhs.tv_usec + rhs.tv_usec;
10,689✔
621
  normalizeTV(ret);
10,689✔
622
  return ret;
10,689✔
623
}
10,689✔
624

625
struct timeval operator-(const struct timeval& lhs, const struct timeval& rhs)
626
{
95,424✔
627
  struct timeval ret;
95,424✔
628
  ret.tv_sec=lhs.tv_sec - rhs.tv_sec;
95,424✔
629
  ret.tv_usec=lhs.tv_usec - rhs.tv_usec;
95,424✔
630
  normalizeTV(ret);
95,424✔
631
  return ret;
95,424✔
632
}
95,424✔
633

634
pair<string, string> splitField(const string& inp, char sepa)
635
{
737,247✔
636
  pair<string, string> ret;
737,247✔
637
  string::size_type cpos=inp.find(sepa);
737,247✔
638
  if(cpos==string::npos)
737,247✔
639
    ret.first=inp;
337,781✔
640
  else {
399,466✔
641
    ret.first=inp.substr(0, cpos);
399,466✔
642
    ret.second=inp.substr(cpos+1);
399,466✔
643
  }
399,466✔
644
  return ret;
737,247✔
645
}
737,247✔
646

647
int logFacilityToLOG(unsigned int facility)
648
{
×
649
  switch(facility) {
×
650
  case 0:
×
651
    return LOG_LOCAL0;
×
652
  case 1:
×
653
    return(LOG_LOCAL1);
×
654
  case 2:
×
655
    return(LOG_LOCAL2);
×
656
  case 3:
×
657
    return(LOG_LOCAL3);
×
658
  case 4:
×
659
    return(LOG_LOCAL4);
×
660
  case 5:
×
661
    return(LOG_LOCAL5);
×
662
  case 6:
×
663
    return(LOG_LOCAL6);
×
664
  case 7:
×
665
    return(LOG_LOCAL7);
×
666
  default:
×
667
    return -1;
×
668
  }
×
669
}
×
670

671
std::optional<int> logFacilityFromString(std::string facilityStr)
672
{
×
673
  static std::unordered_map<std::string, int> const s_facilities = {
×
674
    {"local0", LOG_LOCAL0},
×
675
    {"log_local0", LOG_LOCAL0},
×
676
    {"local1", LOG_LOCAL1},
×
677
    {"log_local1", LOG_LOCAL1},
×
678
    {"local2", LOG_LOCAL2},
×
679
    {"log_local2", LOG_LOCAL2},
×
680
    {"local3", LOG_LOCAL3},
×
681
    {"log_local3", LOG_LOCAL3},
×
682
    {"local4", LOG_LOCAL4},
×
683
    {"log_local4", LOG_LOCAL4},
×
684
    {"local5", LOG_LOCAL5},
×
685
    {"log_local5", LOG_LOCAL5},
×
686
    {"local6", LOG_LOCAL6},
×
687
    {"log_local6", LOG_LOCAL6},
×
688
    {"local7", LOG_LOCAL7},
×
689
    {"log_local7", LOG_LOCAL7},
×
690
    /* most of these likely make very little sense
691
       for us, but why not? */
692
    {"kern", LOG_KERN},
×
693
    {"log_kern", LOG_KERN},
×
694
    {"user", LOG_USER},
×
695
    {"log_user", LOG_USER},
×
696
    {"mail", LOG_MAIL},
×
697
    {"log_mail", LOG_MAIL},
×
698
    {"daemon", LOG_DAEMON},
×
699
    {"log_daemon", LOG_DAEMON},
×
700
    {"auth", LOG_AUTH},
×
701
    {"log_auth", LOG_AUTH},
×
702
    {"syslog", LOG_SYSLOG},
×
703
    {"log_syslog", LOG_SYSLOG},
×
704
    {"lpr", LOG_LPR},
×
705
    {"log_lpr", LOG_LPR},
×
706
    {"news", LOG_NEWS},
×
707
    {"log_news", LOG_NEWS},
×
708
    {"uucp", LOG_UUCP},
×
709
    {"log_uucp", LOG_UUCP},
×
710
    {"cron", LOG_CRON},
×
711
    {"log_cron", LOG_CRON},
×
712
    {"authpriv", LOG_AUTHPRIV},
×
713
    {"log_authpriv", LOG_AUTHPRIV},
×
714
    {"ftp", LOG_FTP},
×
715
    {"log_ftp", LOG_FTP}
×
716
  };
×
717

718
  toLowerInPlace(facilityStr);
×
719
  auto facilityIt = s_facilities.find(facilityStr);
×
720
  if (facilityIt == s_facilities.end()) {
×
721
    return std::nullopt;
×
722
  }
×
723

724
  return facilityIt->second;
×
725
}
×
726

727
string stripDot(const string& dom)
728
{
671,527✔
729
  if(dom.empty())
671,527✔
730
    return dom;
3✔
731

732
  if(dom[dom.size()-1]!='.')
671,524✔
733
    return dom;
671,077✔
734

735
  return dom.substr(0,dom.size()-1);
447✔
736
}
671,524✔
737

738
int makeIPv6sockaddr(const std::string& addr, struct sockaddr_in6* ret)
739
{
1,277,299✔
740
  if (addr.empty()) {
1,277,299✔
741
    return -1;
16✔
742
  }
16✔
743

744
  string ourAddr(addr);
1,277,283✔
745
  std::optional<uint16_t> port = std::nullopt;
1,277,283✔
746

747
  if (addr[0] == '[') { // [::]:53 style address
1,277,283✔
748
    string::size_type pos = addr.find(']');
67,599✔
749
    if (pos == string::npos) {
67,599!
750
      return -1;
×
751
    }
×
752

753
    ourAddr.assign(addr.c_str() + 1, pos - 1);
67,599✔
754
    if (pos + 1 != addr.size()) { // complete after ], no port specified
67,599✔
755
      if (pos + 2 > addr.size() || addr[pos + 1] != ':') {
67,580!
756
        return -1;
×
757
      }
×
758

759
      try {
67,580✔
760
        auto tmpPort = pdns::checked_stoi<uint16_t>(addr.substr(pos + 2));
67,580✔
761
        port = std::make_optional(tmpPort);
67,580✔
762
      }
67,580✔
763
      catch (const std::out_of_range&) {
67,580✔
764
        return -1;
20✔
765
      }
20✔
766
    }
67,580✔
767
  }
67,599✔
768

769
  ret->sin6_scope_id = 0;
1,277,263✔
770
  ret->sin6_family = AF_INET6;
1,277,263✔
771

772
  if (inet_pton(AF_INET6, ourAddr.c_str(), (void*)&ret->sin6_addr) != 1) {
1,277,263✔
773
    struct addrinfo hints{};
437✔
774
    std::memset(&hints, 0, sizeof(struct addrinfo));
437✔
775
    hints.ai_flags = AI_NUMERICHOST;
437✔
776
    hints.ai_family = AF_INET6;
437✔
777

778
    struct addrinfo* res = nullptr;
437✔
779
    // getaddrinfo has anomalous return codes, anything nonzero is an error, positive or negative
780
    if (getaddrinfo(ourAddr.c_str(), nullptr, &hints, &res) != 0) {
437!
781
      return -1;
437✔
782
    }
437✔
783

784
    memcpy(ret, res->ai_addr, res->ai_addrlen);
×
785
    freeaddrinfo(res);
×
786
  }
×
787

788
  if (port.has_value()) {
1,276,826✔
789
    ret->sin6_port = htons(*port);
67,560✔
790
  }
67,560✔
791

792
  return 0;
1,276,826✔
793
}
1,277,263✔
794

795
int makeIPv4sockaddr(const std::string& str, struct sockaddr_in* ret)
796
{
2,005,214✔
797
  if(str.empty()) {
2,005,214✔
798
    return -1;
5✔
799
  }
5✔
800
  struct in_addr inp;
2,005,209✔
801

802
  string::size_type pos = str.find(':');
2,005,209✔
803
  if(pos == string::npos) { // no port specified, not touching the port
2,005,209✔
804
    if(inet_aton(str.c_str(), &inp)) {
860,246✔
805
      ret->sin_addr.s_addr=inp.s_addr;
860,164✔
806
      return 0;
860,164✔
807
    }
860,164✔
808
    return -1;
82✔
809
  }
860,246✔
810
  if(!*(str.c_str() + pos + 1)) // trailing :
1,144,963!
811
    return -1;
×
812

813
  char *eptr = const_cast<char*>(str.c_str()) + str.size();
1,144,963✔
814
  int port = strtol(str.c_str() + pos + 1, &eptr, 10);
1,144,963✔
815
  if (port < 0 || port > 65535)
1,144,963✔
816
    return -1;
20✔
817

818
  if(*eptr)
1,144,943✔
819
    return -1;
934,839✔
820

821
  ret->sin_port = htons(port);
210,104✔
822
  if(inet_aton(str.substr(0, pos).c_str(), &inp)) {
210,104!
823
    ret->sin_addr.s_addr=inp.s_addr;
210,104✔
824
    return 0;
210,104✔
825
  }
210,104✔
826
  return -1;
×
827
}
210,104✔
828

829
int makeUNsockaddr(const std::string& path, struct sockaddr_un* ret)
830
{
1,351✔
831
  if (path.empty())
1,351✔
832
    return -1;
4✔
833

834
  memset(ret, 0, sizeof(struct sockaddr_un));
1,347✔
835
  ret->sun_family = AF_UNIX;
1,347✔
836
  if (path.length() >= sizeof(ret->sun_path))
1,347!
837
    return -1;
×
838

839
  path.copy(ret->sun_path, sizeof(ret->sun_path), 0);
1,347✔
840
  return 0;
1,347✔
841
}
1,347✔
842

843
//! read a line of text from a FILE* to a std::string, returns false on 'no data'
844
bool stringfgets(FILE* fp, std::string& line)
845
{
4,341,664✔
846
  char buffer[1024];
4,341,664✔
847
  line.clear();
4,341,664✔
848

849
  do {
4,341,925✔
850
    if(!fgets(buffer, sizeof(buffer), fp))
4,341,925✔
851
      return !line.empty();
4,269✔
852

853
    line.append(buffer);
4,337,656✔
854
  } while(!strchr(buffer, '\n'));
4,337,656✔
855
  return true;
4,337,395✔
856
}
4,341,664✔
857

858
bool readFileIfThere(const char* fname, std::string* line)
859
{
350✔
860
  line->clear();
350✔
861
  auto filePtr = pdns::UniqueFilePtr(fopen(fname, "r"));
350✔
862
  if (!filePtr) {
350!
863
    return false;
×
864
  }
×
865
  return stringfgets(filePtr.get(), *line);
350✔
866
}
350✔
867

868
Regex::Regex(const string &expr)
869
{
51✔
870
  if(regcomp(&d_preg, expr.c_str(), REG_ICASE|REG_NOSUB|REG_EXTENDED))
51!
871
    throw PDNSException("Regular expression did not compile");
×
872
}
51✔
873

874
// if you end up here because valgrind told you were are doing something wrong
875
// with msgh->msg_controllen, please refer to https://github.com/PowerDNS/pdns/pull/3962
876
// first.
877
// Note that cmsgbuf should be aligned the same as a struct cmsghdr
878
void addCMsgSrcAddr(struct msghdr* msgh, cmsgbuf_aligned* cmsgbuf, const ComboAddress* source, int itfIndex)
879
{
28,403✔
880
  struct cmsghdr *cmsg = nullptr;
28,403✔
881

882
  if(source->sin4.sin_family == AF_INET6) {
28,403✔
883
    struct in6_pktinfo *pkt;
1✔
884

885
    msgh->msg_control = cmsgbuf;
1✔
886
#if !defined( __APPLE__ )
1✔
887
    /* CMSG_SPACE is not a constexpr on macOS */
888
    static_assert(CMSG_SPACE(sizeof(*pkt)) <= sizeof(*cmsgbuf), "Buffer is too small for in6_pktinfo");
1✔
889
#else /* __APPLE__ */
890
    if (CMSG_SPACE(sizeof(*pkt)) > sizeof(*cmsgbuf)) {
891
      throw std::runtime_error("Buffer is too small for in6_pktinfo");
892
    }
893
#endif /* __APPLE__ */
894
    msgh->msg_controllen = CMSG_SPACE(sizeof(*pkt));
1✔
895

896
    cmsg = CMSG_FIRSTHDR(msgh);
1!
897
    cmsg->cmsg_level = IPPROTO_IPV6;
1✔
898
    cmsg->cmsg_type = IPV6_PKTINFO;
1✔
899
    cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
1✔
900

901
    pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
1✔
902
    // Include the padding to stop valgrind complaining about passing uninitialized data
903
    memset(pkt, 0, CMSG_SPACE(sizeof(*pkt)));
1✔
904
    pkt->ipi6_addr = source->sin6.sin6_addr;
1✔
905
    pkt->ipi6_ifindex = itfIndex;
1✔
906
  }
1✔
907
  else {
28,402✔
908
#if defined(IP_PKTINFO)
28,402✔
909
    struct in_pktinfo *pkt;
28,402✔
910

911
    msgh->msg_control = cmsgbuf;
28,402✔
912
#if !defined( __APPLE__ )
28,402✔
913
    /* CMSG_SPACE is not a constexpr on macOS */
914
    static_assert(CMSG_SPACE(sizeof(*pkt)) <= sizeof(*cmsgbuf), "Buffer is too small for in_pktinfo");
28,402✔
915
#else /* __APPLE__ */
916
    if (CMSG_SPACE(sizeof(*pkt)) > sizeof(*cmsgbuf)) {
917
      throw std::runtime_error("Buffer is too small for in_pktinfo");
918
    }
919
#endif /* __APPLE__ */
920
    msgh->msg_controllen = CMSG_SPACE(sizeof(*pkt));
28,402✔
921

922
    cmsg = CMSG_FIRSTHDR(msgh);
28,402!
923
    cmsg->cmsg_level = IPPROTO_IP;
28,402✔
924
    cmsg->cmsg_type = IP_PKTINFO;
28,402✔
925
    cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
28,402✔
926

927
    pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
28,402✔
928
    // Include the padding to stop valgrind complaining about passing uninitialized data
929
    memset(pkt, 0, CMSG_SPACE(sizeof(*pkt)));
28,402✔
930
    pkt->ipi_spec_dst = source->sin4.sin_addr;
28,402✔
931
    pkt->ipi_ifindex = itfIndex;
28,402✔
932
#elif defined(IP_SENDSRCADDR)
933
    struct in_addr *in;
934

935
    msgh->msg_control = cmsgbuf;
936
#if !defined( __APPLE__ )
937
    static_assert(CMSG_SPACE(sizeof(*in)) <= sizeof(*cmsgbuf), "Buffer is too small for in_addr");
938
#else /* __APPLE__ */
939
    if (CMSG_SPACE(sizeof(*in)) > sizeof(*cmsgbuf)) {
940
      throw std::runtime_error("Buffer is too small for in_addr");
941
    }
942
#endif /* __APPLE__ */
943
    msgh->msg_controllen = CMSG_SPACE(sizeof(*in));
944

945
    cmsg = CMSG_FIRSTHDR(msgh);
946
    cmsg->cmsg_level = IPPROTO_IP;
947
    cmsg->cmsg_type = IP_SENDSRCADDR;
948
    cmsg->cmsg_len = CMSG_LEN(sizeof(*in));
949

950
    // Include the padding to stop valgrind complaining about passing uninitialized data
951
    in = (struct in_addr *) CMSG_DATA(cmsg);
952
    memset(in, 0, CMSG_SPACE(sizeof(*in)));
953
    *in = source->sin4.sin_addr;
954
#endif
955
  }
28,402✔
956
}
28,403✔
957

958
unsigned int getFilenumLimit(bool hardOrSoft)
959
{
175✔
960
  struct rlimit rlim;
175✔
961
  if(getrlimit(RLIMIT_NOFILE, &rlim) < 0)
175!
962
    unixDie("Requesting number of available file descriptors");
×
963
  return hardOrSoft ? rlim.rlim_max : rlim.rlim_cur;
175!
964
}
175✔
965

966
void setFilenumLimit(unsigned int lim)
967
{
×
968
  struct rlimit rlim;
×
969

970
  if(getrlimit(RLIMIT_NOFILE, &rlim) < 0)
×
971
    unixDie("Requesting number of available file descriptors");
×
972
  rlim.rlim_cur=lim;
×
973
  if(setrlimit(RLIMIT_NOFILE, &rlim) < 0)
×
974
    unixDie("Setting number of available file descriptors");
×
975
}
×
976

977
bool setSocketTimestamps(int fd)
978
{
578✔
979
#ifdef SO_TIMESTAMP
578✔
980
  int on=1;
578✔
981
  return setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, (char*)&on, sizeof(on)) == 0;
578✔
982
#else
983
  return true; // we pretend this happened.
984
#endif
985
}
578✔
986

987
bool setTCPNoDelay(int sock)
988
{
6,853✔
989
  int flag = 1;
6,853✔
990
  return setsockopt(sock,            /* socket affected */
6,853✔
991
                    IPPROTO_TCP,     /* set option at TCP level */
6,853✔
992
                    TCP_NODELAY,     /* name of option */
6,853✔
993
                    (char *) &flag,  /* the cast is historical cruft */
6,853✔
994
                    sizeof(flag)) == 0;    /* length of option value */
6,853✔
995
}
6,853✔
996

997

998
bool setNonBlocking(int sock)
999
{
154,877✔
1000
  int flags=fcntl(sock,F_GETFL,0);
154,877✔
1001
  if(flags<0 || fcntl(sock, F_SETFL,flags|O_NONBLOCK) <0)
154,878✔
1002
    return false;
×
1003
  return true;
154,877✔
1004
}
154,877✔
1005

1006
bool setBlocking(int sock)
1007
{
89,074✔
1008
  int flags=fcntl(sock,F_GETFL,0);
89,074✔
1009
  if(flags<0 || fcntl(sock, F_SETFL,flags&(~O_NONBLOCK)) <0)
89,074!
1010
    return false;
×
1011
  return true;
89,074✔
1012
}
89,074✔
1013

1014
bool setReuseAddr(int sock)
1015
{
31✔
1016
  int tmp = 1;
31✔
1017
  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&tmp, static_cast<unsigned>(sizeof tmp))<0)
31!
1018
    throw PDNSException(string("Setsockopt failed: ")+stringerror());
×
1019
  return true;
31✔
1020
}
31✔
1021

1022
void setDscp(int sock, unsigned short family, uint8_t dscp)
1023
{
2,929✔
1024
  int val = 0;
2,929✔
1025
  unsigned int len = 0;
2,929✔
1026

1027
  if (dscp == 0 || dscp > 63) {
2,929!
1028
    // No DSCP marking
1029
    return;
2,929✔
1030
  }
2,929✔
1031

1032
  if (family == AF_INET) {
×
1033
    if (getsockopt(sock, IPPROTO_IP, IP_TOS, &val, &len)<0) {
×
1034
      throw std::runtime_error(string("Set DSCP failed: ")+stringerror());
×
1035
    }
×
1036
    val = (dscp<<2) | (val&0x3);
×
1037
    if (setsockopt(sock, IPPROTO_IP, IP_TOS, &val, sizeof(val))<0) {
×
1038
      throw std::runtime_error(string("Set DSCP failed: ")+stringerror());
×
1039
    }
×
1040
  }
×
1041
  else if (family == AF_INET6) {
×
1042
    if (getsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, &val, &len)<0) {
×
1043
      throw std::runtime_error(string("Set DSCP failed: ")+stringerror());
×
1044
    }
×
1045
    val = (dscp<<2) | (val&0x3);
×
1046
    if (setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, &val, sizeof(val))<0) {
×
1047
      throw std::runtime_error(string("Set DSCP failed: ")+stringerror());
×
1048
    }
×
1049
  }
×
1050
}
×
1051

1052
bool isNonBlocking(int sock)
1053
{
111,380✔
1054
  int flags=fcntl(sock,F_GETFL,0);
111,380✔
1055
  return flags & O_NONBLOCK;
111,380✔
1056
}
111,380✔
1057

1058
bool setReceiveSocketErrors([[maybe_unused]] int sock, [[maybe_unused]] int af)
1059
{
9,047✔
1060
#ifdef __linux__
9,047✔
1061
  int tmp = 1, ret;
9,047✔
1062
  if (af == AF_INET) {
9,048✔
1063
    ret = setsockopt(sock, IPPROTO_IP, IP_RECVERR, &tmp, sizeof(tmp));
9,037✔
1064
  } else {
2,147,485,331✔
1065
    ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVERR, &tmp, sizeof(tmp));
2,147,483,655✔
1066
  }
2,147,483,655✔
1067
  if (ret < 0) {
9,047!
1068
    throw PDNSException(string("Setsockopt failed: ") + stringerror());
×
1069
  }
×
1070
#endif
9,047✔
1071
  return true;
9,047✔
1072
}
9,047✔
1073

1074
// Closes a socket.
1075
int closesocket(int socket)
1076
{
49,753✔
1077
  int ret = ::close(socket);
49,753✔
1078
  if(ret < 0 && errno == ECONNRESET) { // see ticket 192, odd BSD behaviour
49,753!
1079
    return 0;
×
1080
  }
×
1081
  if (ret < 0) {
49,753!
1082
    int err = errno;
×
1083
    throw PDNSException("Error closing socket: " + stringerror(err));
×
1084
  }
×
1085
  return ret;
49,753✔
1086
}
49,753✔
1087

1088
bool setCloseOnExec(int sock)
1089
{
46,195✔
1090
  int flags=fcntl(sock,F_GETFD,0);
46,195✔
1091
  if(flags<0 || fcntl(sock, F_SETFD,flags|FD_CLOEXEC) <0)
46,195!
1092
    return false;
×
1093
  return true;
46,195✔
1094
}
46,195✔
1095

1096
#ifdef __linux__
1097
#include <linux/rtnetlink.h>
1098

1099
int getMACAddress(const ComboAddress& ca, char* dest, size_t destLen)
1100
{
2✔
1101
  struct {
2✔
1102
    struct nlmsghdr headermsg;
2✔
1103
    struct ndmsg neighbormsg;
2✔
1104
  } request;
2✔
1105

1106
  std::array<char, 8192> buffer;
2✔
1107

1108
  auto sock = FDWrapper(socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE));
2✔
1109
  if (sock.getHandle() == -1) {
2!
1110
    return errno;
×
1111
  }
×
1112

1113
  memset(&request, 0, sizeof(request));
2✔
1114
  request.headermsg.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
2✔
1115
  request.headermsg.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
2✔
1116
  request.headermsg.nlmsg_type = RTM_GETNEIGH;
2✔
1117
  request.neighbormsg.ndm_family = ca.sin4.sin_family;
2✔
1118

1119
  while (true) {
2✔
1120
    ssize_t sent = send(sock.getHandle(), &request, sizeof(request), 0);
2✔
1121
    if (sent == -1) {
2!
1122
      if (errno == EINTR) {
×
1123
        continue;
×
1124
      }
×
1125
      return errno;
×
1126
    }
×
1127
    else if (static_cast<size_t>(sent) != sizeof(request)) {
2!
1128
      return EIO;
×
1129
    }
×
1130
    break;
2✔
1131
  }
2✔
1132

1133
  bool done = false;
2✔
1134
  bool foundIP = false;
2✔
1135
  bool foundMAC = false;
2✔
1136
  do {
2✔
1137
    ssize_t got = recv(sock.getHandle(), buffer.data(), buffer.size(), 0);
2✔
1138

1139
    if (got < 0) {
2!
1140
      if (errno == EINTR) {
×
1141
        continue;
×
1142
      }
×
1143
      return errno;
×
1144
    }
×
1145

1146
    size_t remaining = static_cast<size_t>(got);
2✔
1147
    for (struct nlmsghdr* nlmsgheader = reinterpret_cast<struct nlmsghdr*>(buffer.data());
2✔
1148
         done == false && NLMSG_OK (nlmsgheader, remaining);
6!
1149
         nlmsgheader = reinterpret_cast<struct nlmsghdr*>(NLMSG_NEXT(nlmsgheader, remaining))) {
6✔
1150

1151
      if (nlmsgheader->nlmsg_type == NLMSG_DONE) {
6✔
1152
        done = true;
2✔
1153
        break;
2✔
1154
      }
2✔
1155

1156
      auto nd = reinterpret_cast<struct ndmsg*>(NLMSG_DATA(nlmsgheader));
4✔
1157
      auto rtatp = reinterpret_cast<struct rtattr*>(reinterpret_cast<char*>(nd) + NLMSG_ALIGN(sizeof(struct ndmsg)));
4✔
1158
      int rtattrlen = nlmsgheader->nlmsg_len - NLMSG_LENGTH(sizeof(struct ndmsg));
4✔
1159

1160
      if (nd->ndm_family != ca.sin4.sin_family) {
4!
1161
        continue;
×
1162
      }
×
1163

1164
      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!
1165
        continue;
×
1166
      }
×
1167

1168
      for (; done == false && RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) {
20!
1169
        if (rtatp->rta_type == NDA_DST){
16✔
1170
          if (nd->ndm_family == AF_INET) {
4!
1171
            auto inp = reinterpret_cast<struct in_addr*>(RTA_DATA(rtatp));
4✔
1172
            if (inp->s_addr == ca.sin4.sin_addr.s_addr) {
4!
1173
              foundIP = true;
×
1174
            }
×
1175
          }
4✔
1176
          else if (nd->ndm_family == AF_INET6) {
×
1177
            auto inp = reinterpret_cast<struct in6_addr *>(RTA_DATA(rtatp));
×
1178
            if (memcmp(inp->s6_addr, ca.sin6.sin6_addr.s6_addr, sizeof(ca.sin6.sin6_addr.s6_addr)) == 0) {
×
1179
              foundIP = true;
×
1180
            }
×
1181
          }
×
1182
        }
4✔
1183
        else if (rtatp->rta_type == NDA_LLADDR) {
12✔
1184
          if (foundIP) {
4!
1185
            size_t addrLen = rtatp->rta_len - sizeof(struct rtattr);
×
1186
            if (addrLen > destLen) {
×
1187
              return ENOBUFS;
×
1188
            }
×
1189
            memcpy(dest, reinterpret_cast<const char*>(rtatp) + sizeof(struct rtattr), addrLen);
×
1190
            foundMAC = true;
×
1191
            done = true;
×
1192
            break;
×
1193
          }
×
1194
        }
4✔
1195
      }
16✔
1196
    }
4✔
1197
  }
2✔
1198
  while (done == false);
2!
1199

1200
  return foundMAC ? 0 : ENOENT;
2!
1201
}
2✔
1202
#else
1203
int getMACAddress(const ComboAddress& /* ca */, char* /* dest */, size_t /* len */)
1204
{
1205
  return ENOENT;
1206
}
1207
#endif /* __linux__ */
1208

1209
string getMACAddress(const ComboAddress& ca)
1210
{
×
1211
  string ret;
×
1212
  char tmp[6];
×
1213
  if (getMACAddress(ca, tmp, sizeof(tmp)) == 0) {
×
1214
    ret.append(tmp, sizeof(tmp));
×
1215
  }
×
1216
  return ret;
×
1217
}
×
1218

1219
uint64_t udpErrorStats([[maybe_unused]] const std::string& str)
1220
{
1,963✔
1221
#ifdef __linux__
1,963✔
1222
  ifstream ifs("/proc/net/snmp");
1,963✔
1223
  if (!ifs) {
1,963!
1224
    return 0;
×
1225
  }
×
1226

1227
  string line;
1,963✔
1228
  while (getline(ifs, line)) {
18,570✔
1229
    if (boost::starts_with(line, "Udp: ") && isdigit(line.at(5))) {
18,570✔
1230
      vector<string> parts;
1,965✔
1231
      stringtok(parts, line, " \n\t\r");
1,965✔
1232

1233
      if (parts.size() < 7) {
1,965!
1234
        break;
×
1235
      }
×
1236

1237
      if (str == "udp-rcvbuf-errors") {
1,965!
1238
        return std::stoull(parts.at(5));
×
1239
      }
×
1240
      else if (str == "udp-sndbuf-errors") {
1,965✔
1241
        return std::stoull(parts.at(6));
393✔
1242
      }
393✔
1243
      else if (str == "udp-noport-errors") {
1,572✔
1244
        return std::stoull(parts.at(2));
393✔
1245
      }
393✔
1246
      else if (str == "udp-in-errors") {
1,179✔
1247
        return std::stoull(parts.at(3));
393✔
1248
      }
393✔
1249
      else if (parts.size() >= 8 && str == "udp-in-csum-errors") {
786!
1250
        return std::stoull(parts.at(7));
393✔
1251
      }
393✔
1252
      else {
393✔
1253
        return 0;
393✔
1254
      }
393✔
1255
    }
1,965✔
1256
  }
18,570✔
1257
#endif
2,147,483,647✔
1258
  return 0;
2,147,483,647✔
1259
}
1,963✔
1260

1261
uint64_t udp6ErrorStats([[maybe_unused]] const std::string& str)
1262
{
1,965✔
1263
#ifdef __linux__
1,965✔
1264
  const std::map<std::string, std::string> keys = {
1,965✔
1265
    { "udp6-in-errors", "Udp6InErrors" },
1,965✔
1266
    { "udp6-recvbuf-errors", "Udp6RcvbufErrors" },
1,965✔
1267
    { "udp6-sndbuf-errors", "Udp6SndbufErrors" },
1,965✔
1268
    { "udp6-noport-errors", "Udp6NoPorts" },
1,965✔
1269
    { "udp6-in-csum-errors", "Udp6InCsumErrors" }
1,965✔
1270
  };
1,965✔
1271

1272
  auto key = keys.find(str);
1,965✔
1273
  if (key == keys.end()) {
1,965!
1274
    return 0;
×
1275
  }
×
1276

1277
  ifstream ifs("/proc/net/snmp6");
1,965✔
1278
  if (!ifs) {
1,965!
1279
    return 0;
×
1280
  }
×
1281

1282
  std::string line;
1,965✔
1283
  while (getline(ifs, line)) {
144,952!
1284
    if (!boost::starts_with(line, key->second)) {
144,952✔
1285
      continue;
142,987✔
1286
    }
142,987✔
1287

1288
    std::vector<std::string> parts;
1,965✔
1289
    stringtok(parts, line, " \n\t\r");
1,965✔
1290

1291
    if (parts.size() != 2) {
1,965!
1292
      return 0;
×
1293
    }
×
1294

1295
    return std::stoull(parts.at(1));
1,965✔
1296
  }
1,965✔
1297
#endif
×
1298
  return 0;
×
1299
}
1,965✔
1300

1301
uint64_t tcpErrorStats(const std::string& /* str */)
1302
{
246✔
1303
#ifdef __linux__
246✔
1304
  ifstream ifs("/proc/net/netstat");
246✔
1305
  if (!ifs) {
246!
1306
    return 0;
×
1307
  }
×
1308

1309
  string line;
246✔
1310
  vector<string> parts;
246✔
1311
  while (getline(ifs,line)) {
493✔
1312
    if (line.size() > 9 && boost::starts_with(line, "TcpExt: ") && isdigit(line.at(8))) {
492!
1313
      stringtok(parts, line, " \n\t\r");
245✔
1314

1315
      if (parts.size() < 21) {
245!
1316
        break;
×
1317
      }
×
1318

1319
      return std::stoull(parts.at(20));
245✔
1320
    }
245✔
1321
  }
492✔
1322
#endif
1✔
1323
  return 0;
1✔
1324
}
246✔
1325

1326
uint64_t getCPUIOWait(const std::string& /* str */)
1327
{
389✔
1328
#ifdef __linux__
389✔
1329
  ifstream ifs("/proc/stat");
389✔
1330
  if (!ifs) {
389!
1331
    return 0;
×
1332
  }
×
1333

1334
  string line;
389✔
1335
  vector<string> parts;
389✔
1336
  while (getline(ifs, line)) {
389!
1337
    if (boost::starts_with(line, "cpu ")) {
389!
1338
      stringtok(parts, line, " \n\t\r");
389✔
1339

1340
      if (parts.size() < 6) {
389!
1341
        break;
×
1342
      }
×
1343

1344
      return std::stoull(parts[5]);
389✔
1345
    }
389✔
1346
  }
389✔
1347
#endif
×
1348
  return 0;
×
1349
}
389✔
1350

1351
uint64_t getCPUSteal(const std::string& /* str */)
1352
{
389✔
1353
#ifdef __linux__
389✔
1354
  ifstream ifs("/proc/stat");
389✔
1355
  if (!ifs) {
389!
1356
    return 0;
×
1357
  }
×
1358

1359
  string line;
389✔
1360
  vector<string> parts;
389✔
1361
  while (getline(ifs, line)) {
389!
1362
    if (boost::starts_with(line, "cpu ")) {
389!
1363
      stringtok(parts, line, " \n\t\r");
389✔
1364

1365
      if (parts.size() < 9) {
389!
1366
        break;
×
1367
      }
×
1368

1369
      return std::stoull(parts[8]);
389✔
1370
    }
389✔
1371
  }
389✔
1372
#endif
×
1373
  return 0;
×
1374
}
389✔
1375

1376
bool getTSIGHashEnum(const DNSName& algoName, TSIGHashEnum& algoEnum)
1377
{
1,299✔
1378
  if (algoName == DNSName("hmac-md5.sig-alg.reg.int") || algoName == DNSName("hmac-md5"))
1,299✔
1379
    algoEnum = TSIG_MD5;
1,200✔
1380
  else if (algoName == DNSName("hmac-sha1"))
99✔
1381
    algoEnum = TSIG_SHA1;
76✔
1382
  else if (algoName == DNSName("hmac-sha224"))
23!
1383
    algoEnum = TSIG_SHA224;
×
1384
  else if (algoName == DNSName("hmac-sha256"))
23✔
1385
    algoEnum = TSIG_SHA256;
4✔
1386
  else if (algoName == DNSName("hmac-sha384"))
19!
1387
    algoEnum = TSIG_SHA384;
×
1388
  else if (algoName == DNSName("hmac-sha512"))
19✔
1389
    algoEnum = TSIG_SHA512;
11✔
1390
  else if (algoName == DNSName("gss-tsig"))
8!
1391
    algoEnum = TSIG_GSS;
×
1392
  else {
8✔
1393
     return false;
8✔
1394
  }
8✔
1395
  return true;
1,291✔
1396
}
1,299✔
1397

1398
DNSName getTSIGAlgoName(TSIGHashEnum& algoEnum)
1399
{
135✔
1400
  switch(algoEnum) {
135!
1401
  case TSIG_MD5: return DNSName("hmac-md5.sig-alg.reg.int.");
135!
1402
  case TSIG_SHA1: return DNSName("hmac-sha1.");
×
1403
  case TSIG_SHA224: return DNSName("hmac-sha224.");
×
1404
  case TSIG_SHA256: return DNSName("hmac-sha256.");
×
1405
  case TSIG_SHA384: return DNSName("hmac-sha384.");
×
1406
  case TSIG_SHA512: return DNSName("hmac-sha512.");
×
1407
  case TSIG_GSS: return DNSName("gss-tsig.");
×
1408
  }
135✔
1409
  throw PDNSException("getTSIGAlgoName does not understand given algorithm, please fix!");
×
1410
}
135✔
1411

1412
uint64_t getOpenFileDescriptors(const std::string&)
1413
{
405✔
1414
#ifdef __linux__
405✔
1415
  uint64_t nbFileDescriptors = 0;
405✔
1416
  const auto dirName = "/proc/" + std::to_string(getpid()) + "/fd/";
405✔
1417
  auto directoryError = pdns::visit_directory(dirName, [&nbFileDescriptors]([[maybe_unused]] ino_t inodeNumber, const std::string_view& name) {
25,901✔
1418
    uint32_t num;
25,901✔
1419
    try {
25,901✔
1420
      pdns::checked_stoi_into(num, std::string(name));
25,901✔
1421
      if (std::to_string(num) == name) {
25,901✔
1422
        nbFileDescriptors++;
25,076✔
1423
      }
25,076✔
1424
    } catch (...) {
25,901✔
1425
      // was not a number.
1426
    }
812✔
1427
    return true;
25,901✔
1428
  });
25,901✔
1429
  if (directoryError) {
405!
1430
    return 0U;
×
1431
  }
×
1432
  return nbFileDescriptors;
405✔
1433
#elif defined(__OpenBSD__)
1434
  // FreeBSD also has this in libopenbsd, but I don't know if that's available always
1435
  return getdtablecount();
1436
#else
1437
  return 0U;
1438
#endif
1439
}
405✔
1440

1441
uint64_t getRealMemoryUsage(const std::string&)
1442
{
406✔
1443
#ifdef __linux__
406✔
1444
  ifstream ifs("/proc/self/statm");
406✔
1445
  if(!ifs)
406!
1446
    return 0;
×
1447

1448
  uint64_t size, resident, shared, text, lib, data;
406✔
1449
  ifs >> size >> resident >> shared >> text >> lib >> data;
406✔
1450

1451
  // We used to use "data" here, but it proves unreliable and even is marked "broken"
1452
  // in https://www.kernel.org/doc/html/latest/filesystems/proc.html
1453
  return resident * getpagesize();
406✔
1454
#else
1455
  struct rusage ru;
1456
  if (getrusage(RUSAGE_SELF, &ru) != 0)
1457
    return 0;
1458
  return ru.ru_maxrss * 1024;
1459
#endif
1460
}
406✔
1461

1462

1463
uint64_t getSpecialMemoryUsage(const std::string&)
1464
{
17✔
1465
#ifdef __linux__
17✔
1466
  ifstream ifs("/proc/self/smaps");
17✔
1467
  if(!ifs)
17!
1468
    return 0;
×
1469
  string line;
17✔
1470
  uint64_t bytes=0;
17✔
1471
  string header("Private_Dirty:");
17✔
1472
  while(getline(ifs, line)) {
229,923✔
1473
    if(boost::starts_with(line, header)) {
229,906✔
1474
      bytes += std::stoull(line.substr(header.length() + 1))*1024;
9,231✔
1475
    }
9,231✔
1476
  }
229,906✔
1477
  return bytes;
17✔
1478
#else
1479
  return 0;
1480
#endif
1481
}
17✔
1482

1483
uint64_t getCPUTimeUser(const std::string&)
1484
{
259✔
1485
  struct rusage ru;
259✔
1486
  getrusage(RUSAGE_SELF, &ru);
259✔
1487
  return (ru.ru_utime.tv_sec*1000ULL + ru.ru_utime.tv_usec/1000);
259✔
1488
}
259✔
1489

1490
uint64_t getCPUTimeSystem(const std::string&)
1491
{
259✔
1492
  struct rusage ru;
259✔
1493
  getrusage(RUSAGE_SELF, &ru);
259✔
1494
  return (ru.ru_stime.tv_sec*1000ULL + ru.ru_stime.tv_usec/1000);
259✔
1495
}
259✔
1496

1497
double DiffTime(const struct timespec& first, const struct timespec& second)
1498
{
122✔
1499
  auto seconds = second.tv_sec - first.tv_sec;
122✔
1500
  auto nseconds = second.tv_nsec - first.tv_nsec;
122✔
1501

1502
  if (nseconds < 0) {
122✔
1503
    seconds -= 1;
110✔
1504
    nseconds += 1000000000;
110✔
1505
  }
110✔
1506
  return static_cast<double>(seconds) + static_cast<double>(nseconds) / 1000000000.0;
122✔
1507
}
122✔
1508

1509
double DiffTime(const struct timeval& first, const struct timeval& second)
1510
{
×
1511
  int seconds=second.tv_sec - first.tv_sec;
×
1512
  int useconds=second.tv_usec - first.tv_usec;
×
1513

1514
  if(useconds < 0) {
×
1515
    seconds-=1;
×
1516
    useconds+=1000000;
×
1517
  }
×
1518
  return seconds + useconds/1000000.0;
×
1519
}
×
1520

1521
uid_t strToUID(const string &str)
1522
{
×
1523
  uid_t result = 0;
×
1524
  const char * cstr = str.c_str();
×
1525
  struct passwd * pwd = getpwnam(cstr);
×
1526

1527
  if (pwd == nullptr) {
×
1528
    long long val;
×
1529

1530
    try {
×
1531
      val = stoll(str);
×
1532
    }
×
1533
    catch(std::exception& e) {
×
1534
      throw runtime_error((boost::format("Error: Unable to parse user ID %s") % cstr).str() );
×
1535
    }
×
1536

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

1541
    result = static_cast<uid_t>(val);
×
1542
  }
×
1543
  else {
×
1544
    result = pwd->pw_uid;
×
1545
  }
×
1546

1547
  return result;
×
1548
}
×
1549

1550
gid_t strToGID(const string &str)
1551
{
×
1552
  gid_t result = 0;
×
1553
  const char * cstr = str.c_str();
×
1554
  struct group * grp = getgrnam(cstr);
×
1555

1556
  if (grp == nullptr) {
×
1557
    long long val;
×
1558

1559
    try {
×
1560
      val = stoll(str);
×
1561
    }
×
1562
    catch(std::exception& e) {
×
1563
      throw runtime_error((boost::format("Error: Unable to parse group ID %s") % cstr).str() );
×
1564
    }
×
1565

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

1570
    result = static_cast<gid_t>(val);
×
1571
  }
×
1572
  else {
×
1573
    result = grp->gr_gid;
×
1574
  }
×
1575

1576
  return result;
×
1577
}
×
1578

1579
bool isSettingThreadCPUAffinitySupported()
1580
{
×
1581
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
1582
  return true;
1583
#else
1584
  return false;
1585
#endif
1586
}
×
1587

1588
int mapThreadToCPUList([[maybe_unused]] pthread_t tid, [[maybe_unused]] const std::set<int>& cpus)
1589
{
×
1590
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
1591
#  ifdef __NetBSD__
1592
  cpuset_t *cpuset;
1593
  cpuset = cpuset_create();
1594
  for (const auto cpuID : cpus) {
1595
    cpuset_set(cpuID, cpuset);
1596
  }
1597

1598
  return pthread_setaffinity_np(tid,
1599
                                cpuset_size(cpuset),
1600
                                cpuset);
1601
#  else
1602
#    ifdef __FreeBSD__
1603
#      define cpu_set_t cpuset_t
1604
#    endif
1605
  cpu_set_t cpuset;
1606
  CPU_ZERO(&cpuset);
1607
  for (const auto cpuID : cpus) {
×
1608
    CPU_SET(cpuID, &cpuset);
×
1609
  }
1610

1611
  return pthread_setaffinity_np(tid,
1612
                                sizeof(cpuset),
1613
                                &cpuset);
1614
#  endif
1615
#else
1616
  return ENOSYS;
1617
#endif /* HAVE_PTHREAD_SETAFFINITY_NP */
1618
}
×
1619

1620
std::vector<ComboAddress> getResolvers(const std::string& resolvConfPath)
1621
{
90✔
1622
  std::vector<ComboAddress> results;
90✔
1623

1624
  ifstream ifs(resolvConfPath);
90✔
1625
  if (!ifs) {
90!
1626
    return results;
×
1627
  }
×
1628

1629
  string line;
90✔
1630
  while(std::getline(ifs, line)) {
1,166✔
1631
    boost::trim_right_if(line, boost::is_any_of(" \r\n\x1a"));
1,076✔
1632
    boost::trim_left(line); // leading spaces, let's be nice
1,076✔
1633

1634
    string::size_type tpos = line.find_first_of(";#");
1,076✔
1635
    if (tpos != string::npos) {
1,076✔
1636
      line.resize(tpos);
629✔
1637
    }
629✔
1638

1639
    if (boost::starts_with(line, "nameserver ") || boost::starts_with(line, "nameserver\t")) {
1,076✔
1640
      vector<string> parts;
91✔
1641
      stringtok(parts, line, " \t,"); // be REALLY nice
91✔
1642
      for (auto iter = parts.begin() + 1; iter != parts.end(); ++iter) {
182✔
1643
        try {
91✔
1644
          results.emplace_back(*iter, 53);
91✔
1645
        }
91✔
1646
        catch(...)
91✔
1647
        {
91✔
1648
        }
×
1649
      }
91✔
1650
    }
91✔
1651
  }
1,076✔
1652

1653
  return results;
90✔
1654
}
90✔
1655

1656
size_t getPipeBufferSize([[maybe_unused]] int fd)
1657
{
11,108✔
1658
#ifdef F_GETPIPE_SZ
11,108✔
1659
  int res = fcntl(fd, F_GETPIPE_SZ);
11,108✔
1660
  if (res == -1) {
11,108!
1661
    return 0;
×
1662
  }
×
1663
  return res;
11,108✔
1664
#else
1665
  errno = ENOSYS;
1666
  return 0;
1667
#endif /* F_GETPIPE_SZ */
1668
}
11,108✔
1669

1670
bool setPipeBufferSize([[maybe_unused]] int fd, [[maybe_unused]] size_t size)
1671
{
11,108✔
1672
#ifdef F_SETPIPE_SZ
11,108✔
1673
  if (size > static_cast<size_t>(std::numeric_limits<int>::max())) {
11,108!
1674
    errno = EINVAL;
×
1675
    return false;
×
1676
  }
×
1677
  int newSize = static_cast<int>(size);
11,108✔
1678
  int res = fcntl(fd, F_SETPIPE_SZ, newSize);
11,108✔
1679
  if (res == -1) {
11,108✔
1680
    return false;
5,754✔
1681
  }
5,754✔
1682
  return true;
5,354✔
1683
#else
1684
  errno = ENOSYS;
1685
  return false;
1686
#endif /* F_SETPIPE_SZ */
1687
}
11,108✔
1688

1689
DNSName reverseNameFromIP(const ComboAddress& ip)
1690
{
9✔
1691
  if (ip.isIPv4()) {
9✔
1692
    std::string result("in-addr.arpa.");
3✔
1693
    auto ptr = reinterpret_cast<const uint8_t*>(&ip.sin4.sin_addr.s_addr);
3✔
1694
    for (size_t idx = 0; idx < sizeof(ip.sin4.sin_addr.s_addr); idx++) {
15✔
1695
      result = std::to_string(ptr[idx]) + "." + result;
12✔
1696
    }
12✔
1697
    return DNSName(result);
3✔
1698
  }
3✔
1699
  else if (ip.isIPv6()) {
6!
1700
    std::string result("ip6.arpa.");
6✔
1701
    auto ptr = reinterpret_cast<const uint8_t*>(&ip.sin6.sin6_addr.s6_addr[0]);
6✔
1702
    for (size_t idx = 0; idx < sizeof(ip.sin6.sin6_addr.s6_addr); idx++) {
102✔
1703
      std::stringstream stream;
96✔
1704
      stream << std::hex << (ptr[idx] & 0x0F);
96✔
1705
      stream << '.';
96✔
1706
      stream << std::hex << (((ptr[idx]) >> 4) & 0x0F);
96✔
1707
      stream << '.';
96✔
1708
      result = stream.str() + result;
96✔
1709
    }
96✔
1710
    return DNSName(result);
6✔
1711
  }
6✔
1712

1713
  throw std::runtime_error("Calling reverseNameFromIP() for an address which is neither an IPv4 nor an IPv6");
×
1714
}
9✔
1715

1716
std::string makeLuaString(const std::string& in)
1717
{
×
1718
  ostringstream str;
×
1719

1720
  str<<'"';
×
1721

1722
  char item[5];
×
1723
  for (unsigned char n : in) {
×
1724
    if (islower(n) || isupper(n)) {
×
1725
      item[0] = n;
×
1726
      item[1] = 0;
×
1727
    }
×
1728
    else {
×
1729
      snprintf(item, sizeof(item), "\\%03d", n);
×
1730
    }
×
1731
    str << item;
×
1732
  }
×
1733

1734
  str<<'"';
×
1735

1736
  return str.str();
×
1737
}
×
1738

1739
size_t parseSVCBValueList(const std::string &in, vector<std::string> &val) {
733✔
1740
  std::string parsed;
733✔
1741
  auto ret = parseRFC1035CharString(in, parsed);
733✔
1742
  parseSVCBValueListFromParsedRFC1035CharString(parsed, val);
733✔
1743
  return ret;
733✔
1744
};
733✔
1745

1746
#ifdef HAVE_CRYPTO_MEMCMP
1747
#include <openssl/crypto.h>
1748
#else /* HAVE_CRYPTO_MEMCMP */
1749
#ifdef HAVE_SODIUM_MEMCMP
1750
#include <sodium.h>
1751
#endif /* HAVE_SODIUM_MEMCMP */
1752
#endif /* HAVE_CRYPTO_MEMCMP */
1753

1754
bool constantTimeStringEquals(const std::string& a, const std::string& b)
1755
{
2,308✔
1756
  if (a.size() != b.size()) {
2,308✔
1757
    return false;
5✔
1758
  }
5✔
1759
  const size_t size = a.size();
2,303✔
1760
#ifdef HAVE_CRYPTO_MEMCMP
2,303✔
1761
  return CRYPTO_memcmp(a.c_str(), b.c_str(), size) == 0;
2,303✔
1762
#else /* HAVE_CRYPTO_MEMCMP */
1763
#ifdef HAVE_SODIUM_MEMCMP
1764
  return sodium_memcmp(a.c_str(), b.c_str(), size) == 0;
1765
#else /* HAVE_SODIUM_MEMCMP */
1766
  const volatile unsigned char *_a = (const volatile unsigned char *) a.c_str();
1767
  const volatile unsigned char *_b = (const volatile unsigned char *) b.c_str();
1768
  unsigned char res = 0;
1769

1770
  for (size_t idx = 0; idx < size; idx++) {
1771
    res |= _a[idx] ^ _b[idx];
1772
  }
1773

1774
  return res == 0;
1775
#endif /* !HAVE_SODIUM_MEMCMP */
1776
#endif /* !HAVE_CRYPTO_MEMCMP */
1777
}
2,308✔
1778

1779
namespace pdns
1780
{
1781
struct CloseDirDeleter
1782
{
1783
  void operator()(DIR* dir) const noexcept {
442✔
1784
    closedir(dir);
442✔
1785
  }
442✔
1786
};
1787

1788
std::optional<std::string> visit_directory(const std::string& directory, const std::function<bool(ino_t inodeNumber, const std::string_view& name)>& visitor)
1789
{
554✔
1790
  auto dirHandle = std::unique_ptr<DIR, CloseDirDeleter>(opendir(directory.c_str()));
554✔
1791
  if (!dirHandle) {
554✔
1792
    auto err = errno;
113✔
1793
    return std::string("Error opening directory '" + directory + "': " + stringerror(err));
113✔
1794
  }
113✔
1795

1796
  bool keepGoing = true;
441✔
1797
  struct dirent* ent = nullptr;
441✔
1798
  // NOLINTNEXTLINE(concurrency-mt-unsafe): readdir is thread-safe nowadays and readdir_r is deprecated
1799
  while (keepGoing && (ent = readdir(dirHandle.get())) != nullptr) {
30,562✔
1800
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay: dirent API
1801
    auto name = std::string_view(ent->d_name, strlen(ent->d_name));
30,121✔
1802
    keepGoing = visitor(ent->d_ino, name);
30,121✔
1803
  }
30,121✔
1804

1805
  return std::nullopt;
441✔
1806
}
554✔
1807

1808
UniqueFilePtr openFileForWriting(const std::string& filePath, mode_t permissions, bool mustNotExist, bool appendIfExists)
1809
{
4✔
1810
  int flags = O_WRONLY | O_CREAT;
4✔
1811
  if (mustNotExist) {
4!
1812
    flags |= O_EXCL;
×
1813
  }
×
1814
  else if (appendIfExists) {
4!
1815
    flags |= O_APPEND;
4✔
1816
  }
4✔
1817
  int fileDesc = open(filePath.c_str(), flags, permissions);
4✔
1818
  if (fileDesc == -1) {
4!
1819
    return {};
×
1820
  }
×
1821
  auto filePtr = pdns::UniqueFilePtr(fdopen(fileDesc, appendIfExists ? "a" : "w"));
4!
1822
  if (!filePtr) {
4!
1823
    auto error = errno;
×
1824
    close(fileDesc);
×
1825
    errno = error;
×
1826
    return {};
×
1827
  }
×
1828
  return filePtr;
4✔
1829
}
4✔
1830

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