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

PowerDNS / pdns / 9418798096

07 Jun 2024 02:38PM UTC coverage: 64.612% (+0.002%) from 64.61%
9418798096

Pull #14057

github

web-flow
Merge 3ef28144e into b1d2ec1c5
Pull Request #14057: Auth: fix a crash and some cleanup in the auth-catalogzone.cc

37074 of 88162 branches covered (42.05%)

Branch coverage included in aggregate %.

23 of 50 new or added lines in 4 files covered. (46.0%)

45 existing lines in 14 files now uncovered.

124373 of 161708 relevant lines covered (76.91%)

5379791.12 hits per line

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

63.35
/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
#ifdef __FreeBSD__
64
#  include <pthread_np.h>
65
#endif
66
#ifdef __NetBSD__
67
#  include <pthread.h>
68
#  include <sched.h>
69
#endif
70

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

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

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

92
    ptr += res;
1,406,444✔
93
  }
1,406,444✔
94

95
  return count;
1,406,399✔
96
}
1,406,399✔
97

98
size_t readn2(int fd, void* buffer, size_t len)
99
{
968✔
100
  size_t pos=0;
968✔
101
  ssize_t res;
968✔
102
  for(;;) {
968✔
103
    res = read(fd, (char*)buffer + pos, len - pos);
968✔
104
    if(res == 0)
968✔
105
      throw runtime_error("EOF while reading message");
241✔
106
    if(res < 0) {
727!
107
      if (errno == EAGAIN)
×
108
        throw std::runtime_error("used readn2 on non-blocking socket, got EAGAIN");
×
109
      else
×
110
        unixDie("failed in readn2");
×
111
    }
×
112

113
    pos+=(size_t)res;
727✔
114
    if(pos == len)
727!
115
      break;
727✔
116
  }
727✔
117
  return len;
727✔
118
}
968✔
119

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

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

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

171
  return len;
1,672✔
172
}
1,675✔
173

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

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

204
  return len;
910✔
205
}
910✔
206

207
auto pdns::getMessageFromErrno(const int errnum) -> std::string
208
{
1,674✔
209
  const size_t errLen = 2048;
1,674✔
210
  std::string errMsgData{};
1,674✔
211
  errMsgData.resize(errLen);
1,674✔
212

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

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

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

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

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

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

269
    const auto* lib = ERR_lib_error_string(errorCode);
270
    if (lib != nullptr) {
271
      fullErrorMessage += std::string(":") + lib;
272
    }
273

274
    const auto* func = ERR_func_error_string(errorCode);
275
    if (func != nullptr) {
276
      fullErrorMessage += std::string(":") + func;
277
    }
278

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

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

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

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

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

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

331
  if(domain.size() <= suffix.size()) {
416✔
332
    return false;
52✔
333
  }
52✔
334

335
  string::size_type dpos = domain.size() - suffix.size() - 1;
364✔
336
  string::size_type spos = 0;
364✔
337

338
  if (domain[dpos++] != '.') {
364✔
339
    return false;
88✔
340
  }
88✔
341

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

348
  return true;
264✔
349
}
276✔
350

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

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

366
    qname->resize(qname->size() - domain.size()-1);
264✔
367
  }
264✔
368
  return true;
280✔
369
}
280✔
370

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

377
int waitForRWData(int fileDesc, bool waitForRead, int seconds, int useconds, bool* error, bool* disconnected)
378
{
2,147,208✔
379
  struct pollfd pfd{};
2,147,208✔
380
  memset(&pfd, 0, sizeof(pfd));
2,147,208✔
381
  pfd.fd = fileDesc;
2,147,208✔
382

383
  if (waitForRead) {
2,147,208✔
384
    pfd.events = POLLIN;
2,144,720✔
385
  }
2,144,720✔
386
  else {
2,488✔
387
    pfd.events = POLLOUT;
2,488✔
388
  }
2,488✔
389

390
  int ret = poll(&pfd, 1, seconds * 1000 + useconds/1000);
2,147,208✔
391
  if (ret > 0) {
2,147,208✔
392
    if ((error != nullptr) && (pfd.revents & POLLERR) != 0) {
2,075,850✔
393
      *error = true;
23✔
394
    }
23✔
395
    if ((disconnected != nullptr) && (pfd.revents & POLLHUP) != 0) {
2,075,850✔
396
      *disconnected = true;
23✔
397
    }
23✔
398
  }
2,075,850✔
399

400
  return ret;
2,147,208✔
401
}
2,147,208✔
402

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

412
  std::vector<struct pollfd> pfds(realFDs.size());
802✔
413
  memset(pfds.data(), 0, realFDs.size()*sizeof(struct pollfd));
802✔
414
  int ctr = 0;
802✔
415
  for (const auto& fd : realFDs) {
1,580✔
416
    pfds[ctr].fd = fd;
1,580✔
417
    pfds[ctr].events = POLLIN;
1,580✔
418
    ctr++;
1,580✔
419
  }
1,580✔
420

421
  int ret;
802✔
422
  if(seconds >= 0)
802!
423
    ret = poll(pfds.data(), realFDs.size(), seconds * 1000 + useconds/1000);
802✔
424
  else
×
425
    ret = poll(pfds.data(), realFDs.size(), -1);
×
426
  if(ret <= 0)
802✔
427
    return ret;
796✔
428

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

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

449
  pfds[0].events= pfds[1].events = POLLIN;
×
450

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

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

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

477
  return 1;
×
478
}
×
479

480

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

495
  return ret.str();
4✔
496
}
4✔
497

498
string unquotify(const string &item)
499
{
10✔
500
  if(item.size()<2)
10✔
501
    return item;
2✔
502

503
  string::size_type bpos=0, epos=item.size();
8✔
504

505
  if(item[0]=='"')
8!
506
    bpos=1;
8✔
507

508
  if(item[epos-1]=='"')
8!
509
    epos-=1;
8✔
510

511
  return item.substr(bpos,epos-bpos);
8✔
512
}
10✔
513

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

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

531
static size_t getMaxHostNameSize()
532
{
309✔
533
#if defined(HOST_NAME_MAX)
309✔
534
  return HOST_NAME_MAX;
309✔
535
#endif
×
536

537
#if defined(_SC_HOST_NAME_MAX)
×
538
  auto tmp = sysconf(_SC_HOST_NAME_MAX);
×
539
  if (tmp != -1) {
×
540
    return tmp;
×
541
  }
×
542
#endif
×
543

544
  const size_t maxHostNameSize = 255;
×
545
  return maxHostNameSize;
×
546
}
×
547

548
std::optional<string> getHostname()
549
{
309✔
550
  const size_t maxHostNameBufSize = getMaxHostNameSize() + 1;
309✔
551
  std::string hostname;
309✔
552
  hostname.resize(maxHostNameBufSize, 0);
309✔
553

554
  if (gethostname(hostname.data(), maxHostNameBufSize) == -1) {
309!
555
    return std::nullopt;
×
556
  }
×
557

558
  hostname.resize(strlen(hostname.c_str()));
309✔
559
  return std::make_optional(hostname);
309✔
560
}
309✔
561

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

569
  boost::replace_all(*hostname, ".", "_");
162✔
570
  return *hostname;
162✔
571
}
162✔
572

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

583
void cleanSlashes(string &str)
584
{
4,380✔
585
  string out;
4,380✔
586
  bool keepNextSlash = true;
4,380✔
587
  for (const auto& value : str) {
85,835✔
588
    if (value == '/') {
85,835✔
589
      if (keepNextSlash) {
6,133✔
590
        keepNextSlash = false;
5,939✔
591
      }
5,939✔
592
      else {
194✔
593
        continue;
194✔
594
      }
194✔
595
    }
6,133✔
596
    else {
79,702✔
597
      keepNextSlash = true;
79,702✔
598
    }
79,702✔
599
    out.append(1, value);
85,641✔
600
  }
85,641✔
601
  str = std::move(out);
4,380✔
602
}
4,380✔
603

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

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

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

630

631
string makeHexDump(const string& str)
632
{
1,240✔
633
  std::array<char, 5> tmp;
1,240✔
634
  string ret;
1,240✔
635
  ret.reserve(static_cast<size_t>(str.size()*2.2));
1,240✔
636

637
  for (char n : str) {
92,451✔
638
    snprintf(tmp.data(), tmp.size(), "%02x ", static_cast<unsigned char>(n));
92,451✔
639
    ret += tmp.data();
92,451✔
640
  }
92,451✔
641
  return ret;
1,240✔
642
}
1,240✔
643

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

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

660
  return ret;
3✔
661
}
6✔
662

663
void normalizeTV(struct timeval& tv)
664
{
81,410✔
665
  if(tv.tv_usec > 1000000) {
81,410✔
666
    ++tv.tv_sec;
4,159✔
667
    tv.tv_usec-=1000000;
4,159✔
668
  }
4,159✔
669
  else if(tv.tv_usec < 0) {
77,251✔
670
    --tv.tv_sec;
34,396✔
671
    tv.tv_usec+=1000000;
34,396✔
672
  }
34,396✔
673
}
81,410✔
674

675
struct timeval operator+(const struct timeval& lhs, const struct timeval& rhs)
676
{
7,795✔
677
  struct timeval ret;
7,795✔
678
  ret.tv_sec=lhs.tv_sec + rhs.tv_sec;
7,795✔
679
  ret.tv_usec=lhs.tv_usec + rhs.tv_usec;
7,795✔
680
  normalizeTV(ret);
7,795✔
681
  return ret;
7,795✔
682
}
7,795✔
683

684
struct timeval operator-(const struct timeval& lhs, const struct timeval& rhs)
685
{
71,984✔
686
  struct timeval ret;
71,984✔
687
  ret.tv_sec=lhs.tv_sec - rhs.tv_sec;
71,984✔
688
  ret.tv_usec=lhs.tv_usec - rhs.tv_usec;
71,984✔
689
  normalizeTV(ret);
71,984✔
690
  return ret;
71,984✔
691
}
71,984✔
692

693
pair<string, string> splitField(const string& inp, char sepa)
694
{
726,705✔
695
  pair<string, string> ret;
726,705✔
696
  string::size_type cpos=inp.find(sepa);
726,705✔
697
  if(cpos==string::npos)
726,705✔
698
    ret.first=inp;
336,187✔
699
  else {
390,518✔
700
    ret.first=inp.substr(0, cpos);
390,518✔
701
    ret.second=inp.substr(cpos+1);
390,518✔
702
  }
390,518✔
703
  return ret;
726,705✔
704
}
726,705✔
705

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

730
string stripDot(const string& dom)
731
{
671,329✔
732
  if(dom.empty())
671,329✔
733
    return dom;
3✔
734

735
  if(dom[dom.size()-1]!='.')
671,326✔
736
    return dom;
671,011✔
737

738
  return dom.substr(0,dom.size()-1);
315✔
739
}
671,326✔
740

741
int makeIPv6sockaddr(const std::string& addr, struct sockaddr_in6* ret)
742
{
1,273,339✔
743
  if (addr.empty()) {
1,273,339✔
744
    return -1;
16✔
745
  }
16✔
746

747
  string ourAddr(addr);
1,273,323✔
748
  std::optional<uint16_t> port = std::nullopt;
1,273,323✔
749

750
  if (addr[0] == '[') { // [::]:53 style address
1,273,323✔
751
    string::size_type pos = addr.find(']');
65,948✔
752
    if (pos == string::npos) {
65,948!
753
      return -1;
×
754
    }
×
755

756
    ourAddr.assign(addr.c_str() + 1, pos - 1);
65,948✔
757
    if (pos + 1 != addr.size()) { // complete after ], no port specified
65,948✔
758
      if (pos + 2 > addr.size() || addr[pos + 1] != ':') {
65,936!
759
        return -1;
×
760
      }
×
761

762
      try {
65,936✔
763
        auto tmpPort = pdns::checked_stoi<uint16_t>(addr.substr(pos + 2));
65,936✔
764
        port = std::make_optional(tmpPort);
65,936✔
765
      }
65,936✔
766
      catch (const std::out_of_range&) {
65,936✔
767
        return -1;
10✔
768
      }
10✔
769
    }
65,936✔
770
  }
65,948✔
771

772
  ret->sin6_scope_id = 0;
1,273,313✔
773
  ret->sin6_family = AF_INET6;
1,273,313✔
774

775
  if (inet_pton(AF_INET6, ourAddr.c_str(), (void*)&ret->sin6_addr) != 1) {
1,273,313✔
776
    struct addrinfo hints{};
372✔
777
    std::memset(&hints, 0, sizeof(struct addrinfo));
372✔
778
    hints.ai_flags = AI_NUMERICHOST;
372✔
779
    hints.ai_family = AF_INET6;
372✔
780

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

787
    memcpy(ret, res->ai_addr, res->ai_addrlen);
×
788
    freeaddrinfo(res);
×
789
  }
×
790

791
  if (port.has_value()) {
1,272,941✔
792
    ret->sin6_port = htons(*port);
65,926✔
793
  }
65,926✔
794

795
  return 0;
1,272,941✔
796
}
1,273,313✔
797

798
int makeIPv4sockaddr(const std::string& str, struct sockaddr_in* ret)
799
{
2,024,134✔
800
  if(str.empty()) {
2,024,134✔
801
    return -1;
5✔
802
  }
5✔
803
  struct in_addr inp;
2,024,129✔
804

805
  string::size_type pos = str.find(':');
2,024,129✔
806
  if(pos == string::npos) { // no port specified, not touching the port
2,024,129✔
807
    if(inet_aton(str.c_str(), &inp)) {
852,168✔
808
      ret->sin_addr.s_addr=inp.s_addr;
852,110✔
809
      return 0;
852,110✔
810
    }
852,110✔
811
    return -1;
58✔
812
  }
852,168✔
813
  if(!*(str.c_str() + pos + 1)) // trailing :
1,171,961!
814
    return -1;
×
815

816
  char *eptr = const_cast<char*>(str.c_str()) + str.size();
1,171,961✔
817
  int port = strtol(str.c_str() + pos + 1, &eptr, 10);
1,171,961✔
818
  if (port < 0 || port > 65535)
1,171,961✔
819
    return -1;
10✔
820

821
  if(*eptr)
1,171,951✔
822
    return -1;
932,819✔
823

824
  ret->sin_port = htons(port);
239,132✔
825
  if(inet_aton(str.substr(0, pos).c_str(), &inp)) {
239,132✔
826
    ret->sin_addr.s_addr=inp.s_addr;
239,131✔
827
    return 0;
239,131✔
828
  }
239,131✔
829
  return -1;
1✔
830
}
239,132✔
831

832
int makeUNsockaddr(const std::string& path, struct sockaddr_un* ret)
833
{
1,107✔
834
  if (path.empty())
1,107✔
835
    return -1;
4✔
836

837
  memset(ret, 0, sizeof(struct sockaddr_un));
1,103✔
838
  ret->sun_family = AF_UNIX;
1,103✔
839
  if (path.length() >= sizeof(ret->sun_path))
1,103!
840
    return -1;
×
841

842
  path.copy(ret->sun_path, sizeof(ret->sun_path), 0);
1,103✔
843
  return 0;
1,103✔
844
}
1,103✔
845

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

852
  do {
4,438,334✔
853
    if(!fgets(buffer, sizeof(buffer), fp))
4,438,334✔
854
      return !line.empty();
4,079✔
855

856
    line.append(buffer);
4,434,255✔
857
  } while(!strchr(buffer, '\n'));
4,434,255✔
858
  return true;
4,434,039✔
859
}
4,438,118✔
860

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

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

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

885
  if(source->sin4.sin_family == AF_INET6) {
27,494✔
886
    struct in6_pktinfo *pkt;
1✔
887

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

899
    cmsg = CMSG_FIRSTHDR(msgh);
1✔
900
    cmsg->cmsg_level = IPPROTO_IPV6;
1✔
901
    cmsg->cmsg_type = IPV6_PKTINFO;
1✔
902
    cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
1✔
903

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

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

925
    cmsg = CMSG_FIRSTHDR(msgh);
27,493✔
926
    cmsg->cmsg_level = IPPROTO_IP;
27,493✔
927
    cmsg->cmsg_type = IP_PKTINFO;
27,493✔
928
    cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
27,493✔
929

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

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

948
    cmsg = CMSG_FIRSTHDR(msgh);
949
    cmsg->cmsg_level = IPPROTO_IP;
950
    cmsg->cmsg_type = IP_SENDSRCADDR;
951
    cmsg->cmsg_len = CMSG_LEN(sizeof(*in));
952

953
    // Include the padding to stop valgrind complaining about passing uninitialized data
954
    in = (struct in_addr *) CMSG_DATA(cmsg);
955
    memset(in, 0, CMSG_SPACE(sizeof(*in)));
956
    *in = source->sin4.sin_addr;
957
#endif
958
  }
27,493✔
959
}
27,494✔
960

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

969
void setFilenumLimit(unsigned int lim)
970
{
×
971
  struct rlimit rlim;
×
972

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

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

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

1000

1001
bool setNonBlocking(int sock)
1002
{
141,051✔
1003
  int flags=fcntl(sock,F_GETFL,0);
141,051✔
1004
  if(flags<0 || fcntl(sock, F_SETFL,flags|O_NONBLOCK) <0)
141,051✔
1005
    return false;
×
1006
  return true;
141,051✔
1007
}
141,051✔
1008

1009
bool setBlocking(int sock)
1010
{
86,250✔
1011
  int flags=fcntl(sock,F_GETFL,0);
86,250✔
1012
  if(flags<0 || fcntl(sock, F_SETFL,flags&(~O_NONBLOCK)) <0)
86,250!
1013
    return false;
×
1014
  return true;
86,250✔
1015
}
86,250✔
1016

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

1025
bool isNonBlocking(int sock)
1026
{
107,552✔
1027
  int flags=fcntl(sock,F_GETFL,0);
107,552✔
1028
  return flags & O_NONBLOCK;
107,552✔
1029
}
107,552✔
1030

1031
bool setReceiveSocketErrors([[maybe_unused]] int sock, [[maybe_unused]] int af)
1032
{
7,575✔
1033
#ifdef __linux__
7,575✔
1034
  int tmp = 1, ret;
7,575✔
1035
  if (af == AF_INET) {
7,575✔
1036
    ret = setsockopt(sock, IPPROTO_IP, IP_RECVERR, &tmp, sizeof(tmp));
7,556✔
1037
  } else {
7,556✔
1038
    ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVERR, &tmp, sizeof(tmp));
19✔
1039
  }
19✔
1040
  if (ret < 0) {
7,575!
1041
    throw PDNSException(string("Setsockopt failed: ") + stringerror());
×
1042
  }
×
1043
#endif
7,575✔
1044
  return true;
7,575✔
1045
}
7,575✔
1046

1047
// Closes a socket.
1048
int closesocket(int socket)
1049
{
40,353✔
1050
  int ret = ::close(socket);
40,353✔
1051
  if(ret < 0 && errno == ECONNRESET) { // see ticket 192, odd BSD behaviour
40,353!
1052
    return 0;
×
1053
  }
×
1054
  if (ret < 0) {
40,353!
1055
    int err = errno;
×
1056
    throw PDNSException("Error closing socket: " + stringerror(err));
×
1057
  }
×
1058
  return ret;
40,353✔
1059
}
40,353✔
1060

1061
bool setCloseOnExec(int sock)
1062
{
36,845✔
1063
  int flags=fcntl(sock,F_GETFD,0);
36,845✔
1064
  if(flags<0 || fcntl(sock, F_SETFD,flags|FD_CLOEXEC) <0)
36,845!
1065
    return false;
×
1066
  return true;
36,845✔
1067
}
36,845✔
1068

1069
#ifdef __linux__
1070
#include <linux/rtnetlink.h>
1071

1072
int getMACAddress(const ComboAddress& ca, char* dest, size_t destLen)
1073
{
2✔
1074
  struct {
2✔
1075
    struct nlmsghdr headermsg;
2✔
1076
    struct ndmsg neighbormsg;
2✔
1077
  } request;
2✔
1078

1079
  std::array<char, 8192> buffer;
2✔
1080

1081
  auto sock = FDWrapper(socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE));
2✔
1082
  if (sock.getHandle() == -1) {
2!
1083
    return errno;
×
1084
  }
×
1085

1086
  memset(&request, 0, sizeof(request));
2✔
1087
  request.headermsg.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
2✔
1088
  request.headermsg.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
2✔
1089
  request.headermsg.nlmsg_type = RTM_GETNEIGH;
2✔
1090
  request.neighbormsg.ndm_family = ca.sin4.sin_family;
2✔
1091

1092
  while (true) {
2✔
1093
    ssize_t sent = send(sock.getHandle(), &request, sizeof(request), 0);
2✔
1094
    if (sent == -1) {
2!
1095
      if (errno == EINTR) {
×
1096
        continue;
×
1097
      }
×
1098
      return errno;
×
1099
    }
×
1100
    else if (static_cast<size_t>(sent) != sizeof(request)) {
2!
1101
      return EIO;
×
1102
    }
×
1103
    break;
2✔
1104
  }
2✔
1105

1106
  bool done = false;
2✔
1107
  bool foundIP = false;
2✔
1108
  bool foundMAC = false;
2✔
1109
  do {
4✔
1110
    ssize_t got = recv(sock.getHandle(), buffer.data(), buffer.size(), 0);
4✔
1111

1112
    if (got < 0) {
4!
1113
      if (errno == EINTR) {
×
1114
        continue;
×
1115
      }
×
1116
      return errno;
×
1117
    }
×
1118

1119
    size_t remaining = static_cast<size_t>(got);
4✔
1120
    for (struct nlmsghdr* nlmsgheader = reinterpret_cast<struct nlmsghdr*>(buffer.data());
4✔
1121
         done == false && NLMSG_OK (nlmsgheader, remaining);
8!
1122
         nlmsgheader = reinterpret_cast<struct nlmsghdr*>(NLMSG_NEXT(nlmsgheader, remaining))) {
6✔
1123

1124
      if (nlmsgheader->nlmsg_type == NLMSG_DONE) {
6✔
1125
        done = true;
2✔
1126
        break;
2✔
1127
      }
2✔
1128

1129
      auto nd = reinterpret_cast<struct ndmsg*>(NLMSG_DATA(nlmsgheader));
4✔
1130
      auto rtatp = reinterpret_cast<struct rtattr*>(reinterpret_cast<char*>(nd) + NLMSG_ALIGN(sizeof(struct ndmsg)));
4✔
1131
      int rtattrlen = nlmsgheader->nlmsg_len - NLMSG_LENGTH(sizeof(struct ndmsg));
4✔
1132

1133
      if (nd->ndm_family != ca.sin4.sin_family) {
4!
1134
        continue;
×
1135
      }
×
1136

1137
      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!
1138
        continue;
×
1139
      }
×
1140

1141
      for (; done == false && RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) {
20!
1142
        if (rtatp->rta_type == NDA_DST){
16✔
1143
          if (nd->ndm_family == AF_INET) {
4!
1144
            auto inp = reinterpret_cast<struct in_addr*>(RTA_DATA(rtatp));
4✔
1145
            if (inp->s_addr == ca.sin4.sin_addr.s_addr) {
4!
1146
              foundIP = true;
×
1147
            }
×
1148
          }
4✔
1149
          else if (nd->ndm_family == AF_INET6) {
×
1150
            auto inp = reinterpret_cast<struct in6_addr *>(RTA_DATA(rtatp));
×
1151
            if (memcmp(inp->s6_addr, ca.sin6.sin6_addr.s6_addr, sizeof(ca.sin6.sin6_addr.s6_addr)) == 0) {
×
1152
              foundIP = true;
×
1153
            }
×
1154
          }
×
1155
        }
4✔
1156
        else if (rtatp->rta_type == NDA_LLADDR) {
12✔
1157
          if (foundIP) {
4!
1158
            size_t addrLen = rtatp->rta_len - sizeof(struct rtattr);
×
1159
            if (addrLen > destLen) {
×
1160
              return ENOBUFS;
×
1161
            }
×
1162
            memcpy(dest, reinterpret_cast<const char*>(rtatp) + sizeof(struct rtattr), addrLen);
×
1163
            foundMAC = true;
×
1164
            done = true;
×
1165
            break;
×
1166
          }
×
1167
        }
4✔
1168
      }
16✔
1169
    }
4✔
1170
  }
4✔
1171
  while (done == false);
4✔
1172

1173
  return foundMAC ? 0 : ENOENT;
2!
1174
}
2✔
1175
#else
1176
int getMACAddress(const ComboAddress& /* ca */, char* /* dest */, size_t /* len */)
1177
{
1178
  return ENOENT;
1179
}
1180
#endif /* __linux__ */
1181

1182
string getMACAddress(const ComboAddress& ca)
1183
{
×
1184
  string ret;
×
1185
  char tmp[6];
×
1186
  if (getMACAddress(ca, tmp, sizeof(tmp)) == 0) {
×
1187
    ret.append(tmp, sizeof(tmp));
×
1188
  }
×
1189
  return ret;
×
1190
}
×
1191

1192
uint64_t udpErrorStats([[maybe_unused]] const std::string& str)
1193
{
1,686✔
1194
#ifdef __linux__
1,686✔
1195
  ifstream ifs("/proc/net/snmp");
1,686✔
1196
  if (!ifs) {
1,686!
1197
    return 0;
×
1198
  }
×
1199

1200
  string line;
1,686✔
1201
  while (getline(ifs, line)) {
14,380!
1202
    if (boost::starts_with(line, "Udp: ") && isdigit(line.at(5))) {
14,380✔
1203
      vector<string> parts;
1,686✔
1204
      stringtok(parts, line, " \n\t\r");
1,686✔
1205

1206
      if (parts.size() < 7) {
1,686!
1207
        break;
×
1208
      }
×
1209

1210
      if (str == "udp-rcvbuf-errors") {
1,686!
1211
        return std::stoull(parts.at(5));
×
1212
      }
×
1213
      else if (str == "udp-sndbuf-errors") {
1,686✔
1214
        return std::stoull(parts.at(6));
338✔
1215
      }
338✔
1216
      else if (str == "udp-noport-errors") {
1,348✔
1217
        return std::stoull(parts.at(2));
338✔
1218
      }
338✔
1219
      else if (str == "udp-in-errors") {
1,010✔
1220
        return std::stoull(parts.at(3));
338✔
1221
      }
338✔
1222
      else if (parts.size() >= 8 && str == "udp-in-csum-errors") {
672!
1223
        return std::stoull(parts.at(7));
334✔
1224
      }
334✔
1225
      else {
338✔
1226
        return 0;
338✔
1227
      }
338✔
1228
    }
1,686✔
1229
  }
14,380✔
1230
#endif
×
1231
  return 0;
×
1232
}
1,686✔
1233

1234
uint64_t udp6ErrorStats([[maybe_unused]] const std::string& str)
1235
{
1,686✔
1236
#ifdef __linux__
1,686✔
1237
  const std::map<std::string, std::string> keys = {
1,686✔
1238
    { "udp6-in-errors", "Udp6InErrors" },
1,686✔
1239
    { "udp6-recvbuf-errors", "Udp6RcvbufErrors" },
1,686✔
1240
    { "udp6-sndbuf-errors", "Udp6SndbufErrors" },
1,686✔
1241
    { "udp6-noport-errors", "Udp6NoPorts" },
1,686✔
1242
    { "udp6-in-csum-errors", "Udp6InCsumErrors" }
1,686✔
1243
  };
1,686✔
1244

1245
  auto key = keys.find(str);
1,686✔
1246
  if (key == keys.end()) {
1,686!
1247
    return 0;
×
1248
  }
×
1249

1250
  ifstream ifs("/proc/net/snmp6");
1,686✔
1251
  if (!ifs) {
1,686!
1252
    return 0;
×
1253
  }
×
1254

1255
  std::string line;
1,686✔
1256
  while (getline(ifs, line)) {
127,764✔
1257
    if (!boost::starts_with(line, key->second)) {
127,764✔
1258
      continue;
126,077✔
1259
    }
126,077✔
1260

1261
    std::vector<std::string> parts;
1,687✔
1262
    stringtok(parts, line, " \n\t\r");
1,687✔
1263

1264
    if (parts.size() != 2) {
1,687!
1265
      return 0;
×
1266
    }
×
1267

1268
    return std::stoull(parts.at(1));
1,687✔
1269
  }
1,687✔
1270
#endif
2,147,483,647✔
1271
  return 0;
2,147,483,647✔
1272
}
1,686✔
1273

1274
uint64_t tcpErrorStats(const std::string& /* str */)
1275
{
207✔
1276
#ifdef __linux__
207✔
1277
  ifstream ifs("/proc/net/netstat");
207✔
1278
  if (!ifs) {
207!
1279
    return 0;
×
1280
  }
×
1281

1282
  string line;
207✔
1283
  vector<string> parts;
207✔
1284
  while (getline(ifs,line)) {
414!
1285
    if (line.size() > 9 && boost::starts_with(line, "TcpExt: ") && isdigit(line.at(8))) {
414!
1286
      stringtok(parts, line, " \n\t\r");
207✔
1287

1288
      if (parts.size() < 21) {
207!
1289
        break;
×
1290
      }
×
1291

1292
      return std::stoull(parts.at(20));
207✔
1293
    }
207✔
1294
  }
414✔
1295
#endif
×
1296
  return 0;
×
1297
}
207✔
1298

1299
uint64_t getCPUIOWait(const std::string& /* str */)
1300
{
334✔
1301
#ifdef __linux__
334✔
1302
  ifstream ifs("/proc/stat");
334✔
1303
  if (!ifs) {
334!
1304
    return 0;
×
1305
  }
×
1306

1307
  string line;
334✔
1308
  vector<string> parts;
334✔
1309
  while (getline(ifs, line)) {
334!
1310
    if (boost::starts_with(line, "cpu ")) {
334!
1311
      stringtok(parts, line, " \n\t\r");
334✔
1312

1313
      if (parts.size() < 6) {
334!
1314
        break;
×
1315
      }
×
1316

1317
      return std::stoull(parts[5]);
334✔
1318
    }
334✔
1319
  }
334✔
UNCOV
1320
#endif
×
UNCOV
1321
  return 0;
×
1322
}
334✔
1323

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

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

1338
      if (parts.size() < 9) {
334!
1339
        break;
×
1340
      }
×
1341

1342
      return std::stoull(parts[8]);
334✔
1343
    }
334✔
1344
  }
334✔
1345
#endif
×
1346
  return 0;
×
1347
}
334✔
1348

1349
bool getTSIGHashEnum(const DNSName& algoName, TSIGHashEnum& algoEnum)
1350
{
1,179✔
1351
  if (algoName == DNSName("hmac-md5.sig-alg.reg.int") || algoName == DNSName("hmac-md5"))
1,179✔
1352
    algoEnum = TSIG_MD5;
1,092✔
1353
  else if (algoName == DNSName("hmac-sha1"))
87✔
1354
    algoEnum = TSIG_SHA1;
64✔
1355
  else if (algoName == DNSName("hmac-sha224"))
23!
1356
    algoEnum = TSIG_SHA224;
×
1357
  else if (algoName == DNSName("hmac-sha256"))
23✔
1358
    algoEnum = TSIG_SHA256;
4✔
1359
  else if (algoName == DNSName("hmac-sha384"))
19!
1360
    algoEnum = TSIG_SHA384;
×
1361
  else if (algoName == DNSName("hmac-sha512"))
19✔
1362
    algoEnum = TSIG_SHA512;
11✔
1363
  else if (algoName == DNSName("gss-tsig"))
8!
1364
    algoEnum = TSIG_GSS;
×
1365
  else {
8✔
1366
     return false;
8✔
1367
  }
8✔
1368
  return true;
1,171✔
1369
}
1,179✔
1370

1371
DNSName getTSIGAlgoName(TSIGHashEnum& algoEnum)
1372
{
120✔
1373
  switch(algoEnum) {
120!
1374
  case TSIG_MD5: return DNSName("hmac-md5.sig-alg.reg.int.");
120!
1375
  case TSIG_SHA1: return DNSName("hmac-sha1.");
×
1376
  case TSIG_SHA224: return DNSName("hmac-sha224.");
×
1377
  case TSIG_SHA256: return DNSName("hmac-sha256.");
×
1378
  case TSIG_SHA384: return DNSName("hmac-sha384.");
×
1379
  case TSIG_SHA512: return DNSName("hmac-sha512.");
×
1380
  case TSIG_GSS: return DNSName("gss-tsig.");
×
1381
  }
120✔
1382
  throw PDNSException("getTSIGAlgoName does not understand given algorithm, please fix!");
×
1383
}
120✔
1384

1385
uint64_t getOpenFileDescriptors(const std::string&)
1386
{
351✔
1387
#ifdef __linux__
351✔
1388
  uint64_t nbFileDescriptors = 0;
351✔
1389
  const auto dirName = "/proc/" + std::to_string(getpid()) + "/fd/";
351✔
1390
  auto directoryError = pdns::visit_directory(dirName, [&nbFileDescriptors]([[maybe_unused]] ino_t inodeNumber, const std::string_view& name) {
22,252✔
1391
    uint32_t num;
22,252✔
1392
    try {
22,252✔
1393
      pdns::checked_stoi_into(num, std::string(name));
22,252✔
1394
      if (std::to_string(num) == name) {
22,252✔
1395
        nbFileDescriptors++;
21,542✔
1396
      }
21,542✔
1397
    } catch (...) {
22,252✔
1398
      // was not a number.
1399
    }
702✔
1400
    return true;
22,252✔
1401
  });
22,252✔
1402
  if (directoryError) {
351!
1403
    return 0U;
×
1404
  }
×
1405
  return nbFileDescriptors;
351✔
1406
#elif defined(__OpenBSD__)
1407
  // FreeBSD also has this in libopenbsd, but I don't know if that's available always
1408
  return getdtablecount();
1409
#else
1410
  return 0U;
1411
#endif
1412
}
351✔
1413

1414
uint64_t getRealMemoryUsage(const std::string&)
1415
{
351✔
1416
#ifdef __linux__
351✔
1417
  ifstream ifs("/proc/self/statm");
351✔
1418
  if(!ifs)
351!
1419
    return 0;
×
1420

1421
  uint64_t size, resident, shared, text, lib, data;
351✔
1422
  ifs >> size >> resident >> shared >> text >> lib >> data;
351✔
1423

1424
  // We used to use "data" here, but it proves unreliable and even is marked "broken"
1425
  // in https://www.kernel.org/doc/html/latest/filesystems/proc.html
1426
  return resident * getpagesize();
351✔
1427
#else
1428
  struct rusage ru;
1429
  if (getrusage(RUSAGE_SELF, &ru) != 0)
1430
    return 0;
1431
  return ru.ru_maxrss * 1024;
1432
#endif
1433
}
351✔
1434

1435

1436
uint64_t getSpecialMemoryUsage(const std::string&)
1437
{
15✔
1438
#ifdef __linux__
15✔
1439
  ifstream ifs("/proc/self/smaps");
15✔
1440
  if(!ifs)
15!
1441
    return 0;
×
1442
  string line;
15✔
1443
  uint64_t bytes=0;
15✔
1444
  string header("Private_Dirty:");
15✔
1445
  while(getline(ifs, line)) {
195,581✔
1446
    if(boost::starts_with(line, header)) {
195,566✔
1447
      bytes += std::stoull(line.substr(header.length() + 1))*1024;
8,165✔
1448
    }
8,165✔
1449
  }
195,566✔
1450
  return bytes;
15✔
1451
#else
1452
  return 0;
1453
#endif
1454
}
15✔
1455

1456
uint64_t getCPUTimeUser(const std::string&)
1457
{
220✔
1458
  struct rusage ru;
220✔
1459
  getrusage(RUSAGE_SELF, &ru);
220✔
1460
  return (ru.ru_utime.tv_sec*1000ULL + ru.ru_utime.tv_usec/1000);
220✔
1461
}
220✔
1462

1463
uint64_t getCPUTimeSystem(const std::string&)
1464
{
220✔
1465
  struct rusage ru;
220✔
1466
  getrusage(RUSAGE_SELF, &ru);
220✔
1467
  return (ru.ru_stime.tv_sec*1000ULL + ru.ru_stime.tv_usec/1000);
220✔
1468
}
220✔
1469

1470
double DiffTime(const struct timespec& first, const struct timespec& second)
1471
{
122✔
1472
  auto seconds = second.tv_sec - first.tv_sec;
122✔
1473
  auto nseconds = second.tv_nsec - first.tv_nsec;
122✔
1474

1475
  if (nseconds < 0) {
122✔
1476
    seconds -= 1;
110✔
1477
    nseconds += 1000000000;
110✔
1478
  }
110✔
1479
  return static_cast<double>(seconds) + static_cast<double>(nseconds) / 1000000000.0;
122✔
1480
}
122✔
1481

1482
double DiffTime(const struct timeval& first, const struct timeval& second)
1483
{
×
1484
  int seconds=second.tv_sec - first.tv_sec;
×
1485
  int useconds=second.tv_usec - first.tv_usec;
×
1486

1487
  if(useconds < 0) {
×
1488
    seconds-=1;
×
1489
    useconds+=1000000;
×
1490
  }
×
1491
  return seconds + useconds/1000000.0;
×
1492
}
×
1493

1494
uid_t strToUID(const string &str)
1495
{
×
1496
  uid_t result = 0;
×
1497
  const char * cstr = str.c_str();
×
1498
  struct passwd * pwd = getpwnam(cstr);
×
1499

1500
  if (pwd == nullptr) {
×
1501
    long long val;
×
1502

1503
    try {
×
1504
      val = stoll(str);
×
1505
    }
×
1506
    catch(std::exception& e) {
×
1507
      throw runtime_error((boost::format("Error: Unable to parse user ID %s") % cstr).str() );
×
1508
    }
×
1509

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

1514
    result = static_cast<uid_t>(val);
×
1515
  }
×
1516
  else {
×
1517
    result = pwd->pw_uid;
×
1518
  }
×
1519

1520
  return result;
×
1521
}
×
1522

1523
gid_t strToGID(const string &str)
1524
{
×
1525
  gid_t result = 0;
×
1526
  const char * cstr = str.c_str();
×
1527
  struct group * grp = getgrnam(cstr);
×
1528

1529
  if (grp == nullptr) {
×
1530
    long long val;
×
1531

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

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

1543
    result = static_cast<gid_t>(val);
×
1544
  }
×
1545
  else {
×
1546
    result = grp->gr_gid;
×
1547
  }
×
1548

1549
  return result;
×
1550
}
×
1551

1552
bool isSettingThreadCPUAffinitySupported()
1553
{
×
1554
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
1555
  return true;
1556
#else
1557
  return false;
1558
#endif
1559
}
×
1560

1561
int mapThreadToCPUList([[maybe_unused]] pthread_t tid, [[maybe_unused]] const std::set<int>& cpus)
1562
{
×
1563
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
1564
#  ifdef __NetBSD__
1565
  cpuset_t *cpuset;
1566
  cpuset = cpuset_create();
1567
  for (const auto cpuID : cpus) {
1568
    cpuset_set(cpuID, cpuset);
1569
  }
1570

1571
  return pthread_setaffinity_np(tid,
1572
                                cpuset_size(cpuset),
1573
                                cpuset);
1574
#  else
1575
#    ifdef __FreeBSD__
1576
#      define cpu_set_t cpuset_t
1577
#    endif
1578
  cpu_set_t cpuset;
1579
  CPU_ZERO(&cpuset);
1580
  for (const auto cpuID : cpus) {
×
1581
    CPU_SET(cpuID, &cpuset);
1582
  }
1583

1584
  return pthread_setaffinity_np(tid,
1585
                                sizeof(cpuset),
1586
                                &cpuset);
1587
#  endif
1588
#else
1589
  return ENOSYS;
1590
#endif /* HAVE_PTHREAD_SETAFFINITY_NP */
1591
}
×
1592

1593
std::vector<ComboAddress> getResolvers(const std::string& resolvConfPath)
1594
{
89✔
1595
  std::vector<ComboAddress> results;
89✔
1596

1597
  ifstream ifs(resolvConfPath);
89✔
1598
  if (!ifs) {
89!
1599
    return results;
×
1600
  }
×
1601

1602
  string line;
89✔
1603
  while(std::getline(ifs, line)) {
361✔
1604
    boost::trim_right_if(line, boost::is_any_of(" \r\n\x1a"));
272✔
1605
    boost::trim_left(line); // leading spaces, let's be nice
272✔
1606

1607
    string::size_type tpos = line.find_first_of(";#");
272✔
1608
    if (tpos != string::npos) {
272✔
1609
      line.resize(tpos);
6✔
1610
    }
6✔
1611

1612
    if (boost::starts_with(line, "nameserver ") || boost::starts_with(line, "nameserver\t")) {
272✔
1613
      vector<string> parts;
90✔
1614
      stringtok(parts, line, " \t,"); // be REALLY nice
90✔
1615
      for (auto iter = parts.begin() + 1; iter != parts.end(); ++iter) {
180✔
1616
        try {
90✔
1617
          results.emplace_back(*iter, 53);
90✔
1618
        }
90✔
1619
        catch(...)
90✔
1620
        {
90✔
1621
        }
×
1622
      }
90✔
1623
    }
90✔
1624
  }
272✔
1625

1626
  return results;
89✔
1627
}
89✔
1628

1629
size_t getPipeBufferSize([[maybe_unused]] int fd)
1630
{
9,254✔
1631
#ifdef F_GETPIPE_SZ
9,254✔
1632
  int res = fcntl(fd, F_GETPIPE_SZ);
9,254✔
1633
  if (res == -1) {
9,254!
1634
    return 0;
×
1635
  }
×
1636
  return res;
9,254✔
1637
#else
1638
  errno = ENOSYS;
1639
  return 0;
1640
#endif /* F_GETPIPE_SZ */
1641
}
9,254✔
1642

1643
bool setPipeBufferSize([[maybe_unused]] int fd, [[maybe_unused]] size_t size)
1644
{
9,254✔
1645
#ifdef F_SETPIPE_SZ
9,254✔
1646
  if (size > static_cast<size_t>(std::numeric_limits<int>::max())) {
9,254!
1647
    errno = EINVAL;
×
1648
    return false;
×
1649
  }
×
1650
  int newSize = static_cast<int>(size);
9,254✔
1651
  int res = fcntl(fd, F_SETPIPE_SZ, newSize);
9,254✔
1652
  if (res == -1) {
9,254✔
1653
    return false;
3,733✔
1654
  }
3,733✔
1655
  return true;
5,521✔
1656
#else
1657
  errno = ENOSYS;
1658
  return false;
1659
#endif /* F_SETPIPE_SZ */
1660
}
9,254✔
1661

1662
DNSName reverseNameFromIP(const ComboAddress& ip)
1663
{
9✔
1664
  if (ip.isIPv4()) {
9✔
1665
    std::string result("in-addr.arpa.");
3✔
1666
    auto ptr = reinterpret_cast<const uint8_t*>(&ip.sin4.sin_addr.s_addr);
3✔
1667
    for (size_t idx = 0; idx < sizeof(ip.sin4.sin_addr.s_addr); idx++) {
15✔
1668
      result = std::to_string(ptr[idx]) + "." + result;
12✔
1669
    }
12✔
1670
    return DNSName(result);
3✔
1671
  }
3✔
1672
  else if (ip.isIPv6()) {
6!
1673
    std::string result("ip6.arpa.");
6✔
1674
    auto ptr = reinterpret_cast<const uint8_t*>(&ip.sin6.sin6_addr.s6_addr[0]);
6✔
1675
    for (size_t idx = 0; idx < sizeof(ip.sin6.sin6_addr.s6_addr); idx++) {
102✔
1676
      std::stringstream stream;
96✔
1677
      stream << std::hex << (ptr[idx] & 0x0F);
96✔
1678
      stream << '.';
96✔
1679
      stream << std::hex << (((ptr[idx]) >> 4) & 0x0F);
96✔
1680
      stream << '.';
96✔
1681
      result = stream.str() + result;
96✔
1682
    }
96✔
1683
    return DNSName(result);
6✔
1684
  }
6✔
1685

1686
  throw std::runtime_error("Calling reverseNameFromIP() for an address which is neither an IPv4 nor an IPv6");
×
1687
}
9✔
1688

1689
std::string makeLuaString(const std::string& in)
1690
{
×
1691
  ostringstream str;
×
1692

1693
  str<<'"';
×
1694

1695
  char item[5];
×
1696
  for (unsigned char n : in) {
×
1697
    if (islower(n) || isupper(n)) {
×
1698
      item[0] = n;
×
1699
      item[1] = 0;
×
1700
    }
×
1701
    else {
×
1702
      snprintf(item, sizeof(item), "\\%03d", n);
×
1703
    }
×
1704
    str << item;
×
1705
  }
×
1706

1707
  str<<'"';
×
1708

1709
  return str.str();
×
1710
}
×
1711

1712
size_t parseSVCBValueList(const std::string &in, vector<std::string> &val) {
546✔
1713
  std::string parsed;
546✔
1714
  auto ret = parseRFC1035CharString(in, parsed);
546✔
1715
  parseSVCBValueListFromParsedRFC1035CharString(parsed, val);
546✔
1716
  return ret;
546✔
1717
};
546✔
1718

1719
#ifdef HAVE_CRYPTO_MEMCMP
1720
#include <openssl/crypto.h>
1721
#else /* HAVE_CRYPTO_MEMCMP */
1722
#ifdef HAVE_SODIUM_MEMCMP
1723
#include <sodium.h>
1724
#endif /* HAVE_SODIUM_MEMCMP */
1725
#endif /* HAVE_CRYPTO_MEMCMP */
1726

1727
bool constantTimeStringEquals(const std::string& a, const std::string& b)
1728
{
2,211✔
1729
  if (a.size() != b.size()) {
2,211✔
1730
    return false;
5✔
1731
  }
5✔
1732
  const size_t size = a.size();
2,206✔
1733
#ifdef HAVE_CRYPTO_MEMCMP
2,206✔
1734
  return CRYPTO_memcmp(a.c_str(), b.c_str(), size) == 0;
2,206✔
1735
#else /* HAVE_CRYPTO_MEMCMP */
1736
#ifdef HAVE_SODIUM_MEMCMP
1737
  return sodium_memcmp(a.c_str(), b.c_str(), size) == 0;
1738
#else /* HAVE_SODIUM_MEMCMP */
1739
  const volatile unsigned char *_a = (const volatile unsigned char *) a.c_str();
1740
  const volatile unsigned char *_b = (const volatile unsigned char *) b.c_str();
1741
  unsigned char res = 0;
1742

1743
  for (size_t idx = 0; idx < size; idx++) {
1744
    res |= _a[idx] ^ _b[idx];
1745
  }
1746

1747
  return res == 0;
1748
#endif /* !HAVE_SODIUM_MEMCMP */
1749
#endif /* !HAVE_CRYPTO_MEMCMP */
1750
}
2,211✔
1751

1752
namespace pdns
1753
{
1754
struct CloseDirDeleter
1755
{
1756
  void operator()(DIR* dir) const noexcept {
386✔
1757
    closedir(dir);
386✔
1758
  }
386✔
1759
};
1760

1761
std::optional<std::string> visit_directory(const std::string& directory, const std::function<bool(ino_t inodeNumber, const std::string_view& name)>& visitor)
1762
{
486✔
1763
  auto dirHandle = std::unique_ptr<DIR, CloseDirDeleter>(opendir(directory.c_str()));
486✔
1764
  if (!dirHandle) {
486✔
1765
    auto err = errno;
3✔
1766
    return std::string("Error opening directory '" + directory + "': " + stringerror(err));
3✔
1767
  }
3✔
1768

1769
  bool keepGoing = true;
483✔
1770
  struct dirent* ent = nullptr;
483✔
1771
  // NOLINTNEXTLINE(concurrency-mt-unsafe): readdir is thread-safe nowadays and readdir_r is deprecated
1772
  while (keepGoing && (ent = readdir(dirHandle.get())) != nullptr) {
27,033✔
1773
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay: dirent API
1774
    auto name = std::string_view(ent->d_name, strlen(ent->d_name));
26,550✔
1775
    keepGoing = visitor(ent->d_ino, name);
26,550✔
1776
  }
26,550✔
1777

1778
  return std::nullopt;
483✔
1779
}
486✔
1780

1781
UniqueFilePtr openFileForWriting(const std::string& filePath, mode_t permissions, bool mustNotExist, bool appendIfExists)
1782
{
×
1783
  int flags = O_WRONLY | O_CREAT;
×
1784
  if (mustNotExist) {
×
1785
    flags |= O_EXCL;
×
1786
  }
×
1787
  else if (appendIfExists) {
×
1788
    flags |= O_APPEND;
×
1789
  }
×
1790
  int fileDesc = open(filePath.c_str(), flags, permissions);
×
1791
  if (fileDesc == -1) {
×
1792
    return {};
×
1793
  }
×
1794
  auto filePtr = pdns::UniqueFilePtr(fdopen(fileDesc, appendIfExists ? "a" : "w"));
×
1795
  if (!filePtr) {
×
1796
    auto error = errno;
×
1797
    close(fileDesc);
×
1798
    errno = error;
×
1799
    return {};
×
1800
  }
×
1801
  return filePtr;
×
1802
}
×
1803

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