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

PowerDNS / pdns / 11049952298

26 Sep 2024 09:59AM UTC coverage: 64.7% (+0.005%) from 64.695%
11049952298

Pull #14594

github

web-flow
Merge b1726e0c0 into 42c3a1e1b
Pull Request #14594: auth udp: use stubDoResolve for ALIAS

37040 of 87934 branches covered (42.12%)

Branch coverage included in aggregate %.

25 of 54 new or added lines in 2 files covered. (46.3%)

53 existing lines in 14 files now uncovered.

124664 of 161993 relevant lines covered (76.96%)

4875762.83 hits per line

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

63.66
/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,386✔
77
  const char *ptr = static_cast<const char*>(buf);
1,406,386✔
78
  const char *eptr = ptr + count;
1,406,386✔
79

80
  while (ptr != eptr) {
2,812,772✔
81
    auto res = ::write(fileDesc, ptr, eptr - ptr);
1,406,386✔
82
    if (res < 0) {
1,406,386!
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,386!
89
      throw std::runtime_error("could not write all bytes, got eof in writen2");
×
90
    }
×
91

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

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

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

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

114
    pos += static_cast<size_t>(res);
739✔
115
    if (pos == len) {
739!
116
      break;
739✔
117
    }
739✔
118
  }
739✔
119
  return len;
739✔
120
}
984✔
121

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

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

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

173
  return len;
1,672✔
174
}
1,675✔
175

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

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

206
  return len;
796✔
207
}
796✔
208

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

350
  return true;
264✔
351
}
276✔
352

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

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

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

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

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

385
  if (waitForRead) {
2,175,857✔
386
    pfd.events = POLLIN;
2,173,359✔
387
  }
2,173,359✔
388
  else {
2,498✔
389
    pfd.events = POLLOUT;
2,498✔
390
  }
2,498✔
391

392
  int ret = poll(&pfd, 1, seconds * 1000 + useconds/1000);
2,175,857✔
393
  if (ret > 0) {
2,175,858✔
394
    if ((error != nullptr) && (pfd.revents & POLLERR) != 0) {
2,076,682✔
395
      *error = true;
23✔
396
    }
23✔
397
    if ((disconnected != nullptr) && (pfd.revents & POLLHUP) != 0) {
2,076,682✔
398
      *disconnected = true;
23✔
399
    }
23✔
400
  }
2,076,682✔
401

402
  return ret;
2,175,857✔
403
}
2,175,857✔
404

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

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

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

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

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

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

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

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

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

479
  return 1;
×
480
}
×
481

482

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

585
void cleanSlashes(string &str)
586
{
4,378✔
587
  string out;
4,378✔
588
  bool keepNextSlash = true;
4,378✔
589
  for (const auto& value : str) {
86,015✔
590
    if (value == '/') {
86,015✔
591
      if (keepNextSlash) {
6,144✔
592
        keepNextSlash = false;
5,950✔
593
      }
5,950✔
594
      else {
194✔
595
        continue;
194✔
596
      }
194✔
597
    }
6,144✔
598
    else {
79,871✔
599
      keepNextSlash = true;
79,871✔
600
    }
79,871✔
601
    out.append(1, value);
85,821✔
602
  }
85,821✔
603
  str = std::move(out);
4,378✔
604
}
4,378✔
605

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

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

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

632

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

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

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

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

662
  return ret;
3✔
663
}
6✔
664

665
void normalizeTV(struct timeval& tv)
666
{
87,071✔
667
  if(tv.tv_usec > 1000000) {
87,071✔
668
    ++tv.tv_sec;
4,057✔
669
    tv.tv_usec-=1000000;
4,057✔
670
  }
4,057✔
671
  else if(tv.tv_usec < 0) {
83,014✔
672
    --tv.tv_sec;
35,459✔
673
    tv.tv_usec+=1000000;
35,459✔
674
  }
35,459✔
675
}
87,071✔
676

677
struct timeval operator+(const struct timeval& lhs, const struct timeval& rhs)
678
{
8,757✔
679
  struct timeval ret;
8,757✔
680
  ret.tv_sec=lhs.tv_sec + rhs.tv_sec;
8,757✔
681
  ret.tv_usec=lhs.tv_usec + rhs.tv_usec;
8,757✔
682
  normalizeTV(ret);
8,757✔
683
  return ret;
8,757✔
684
}
8,757✔
685

686
struct timeval operator-(const struct timeval& lhs, const struct timeval& rhs)
687
{
76,665✔
688
  struct timeval ret;
76,665✔
689
  ret.tv_sec=lhs.tv_sec - rhs.tv_sec;
76,665✔
690
  ret.tv_usec=lhs.tv_usec - rhs.tv_usec;
76,665✔
691
  normalizeTV(ret);
76,665✔
692
  return ret;
76,665✔
693
}
76,665✔
694

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

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

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

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

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

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

749
  string ourAddr(addr);
1,273,442✔
750
  std::optional<uint16_t> port = std::nullopt;
1,273,442✔
751

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

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

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

774
  ret->sin6_scope_id = 0;
1,273,432✔
775
  ret->sin6_family = AF_INET6;
1,273,432✔
776

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

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

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

793
  if (port.has_value()) {
1,273,058✔
794
    ret->sin6_port = htons(*port);
65,932✔
795
  }
65,932✔
796

797
  return 0;
1,273,058✔
798
}
1,273,432✔
799

800
int makeIPv4sockaddr(const std::string& str, struct sockaddr_in* ret)
801
{
1,992,509✔
802
  if(str.empty()) {
1,992,509✔
803
    return -1;
5✔
804
  }
5✔
805
  struct in_addr inp;
1,992,504✔
806

807
  string::size_type pos = str.find(':');
1,992,504✔
808
  if(pos == string::npos) { // no port specified, not touching the port
1,992,504✔
809
    if(inet_aton(str.c_str(), &inp)) {
852,075✔
810
      ret->sin_addr.s_addr=inp.s_addr;
852,015✔
811
      return 0;
852,015✔
812
    }
852,015✔
813
    return -1;
60✔
814
  }
852,075✔
815
  if(!*(str.c_str() + pos + 1)) // trailing :
1,140,429!
816
    return -1;
×
817

818
  char *eptr = const_cast<char*>(str.c_str()) + str.size();
1,140,429✔
819
  int port = strtol(str.c_str() + pos + 1, &eptr, 10);
1,140,429✔
820
  if (port < 0 || port > 65535)
1,140,430✔
821
    return -1;
10✔
822

823
  if(*eptr)
1,140,419✔
824
    return -1;
932,848✔
825

826
  ret->sin_port = htons(port);
207,571✔
827
  if(inet_aton(str.substr(0, pos).c_str(), &inp)) {
207,572✔
828
    ret->sin_addr.s_addr=inp.s_addr;
207,572✔
829
    return 0;
207,572✔
830
  }
207,572✔
831
  return -1;
2,147,483,647✔
832
}
207,571✔
833

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

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

844
  path.copy(ret->sun_path, sizeof(ret->sun_path), 0);
1,106✔
845
  return 0;
1,106✔
846
}
1,106✔
847

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

854
  do {
4,438,776✔
855
    if(!fgets(buffer, sizeof(buffer), fp))
4,438,776✔
856
      return !line.empty();
4,088✔
857

858
    line.append(buffer);
4,434,688✔
859
  } while(!strchr(buffer, '\n'));
4,434,688✔
860
  return true;
4,434,470✔
861
}
4,438,558✔
862

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

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

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

887
  if(source->sin4.sin_family == AF_INET6) {
27,486✔
888
    struct in6_pktinfo *pkt;
1✔
889

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

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

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

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

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

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

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

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

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

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

971
void setFilenumLimit(unsigned int lim)
972
{
×
973
  struct rlimit rlim;
×
974

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

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

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

1002

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

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

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

1027
bool isNonBlocking(int sock)
1028
{
108,111✔
1029
  int flags=fcntl(sock,F_GETFL,0);
108,111✔
1030
  return flags & O_NONBLOCK;
108,111✔
1031
}
108,111✔
1032

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

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

1063
bool setCloseOnExec(int sock)
1064
{
37,202✔
1065
  int flags=fcntl(sock,F_GETFD,0);
37,202✔
1066
  if(flags<0 || fcntl(sock, F_SETFD,flags|FD_CLOEXEC) <0)
37,203!
1067
    return false;
×
1068
  return true;
37,202✔
1069
}
37,202✔
1070

1071
#ifdef __linux__
1072
#include <linux/rtnetlink.h>
1073

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

1081
  std::array<char, 8192> buffer;
2✔
1082

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

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

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

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

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

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

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

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

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

1139
      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!
1140
        continue;
×
1141
      }
×
1142

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

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

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

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

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

1208
      if (parts.size() < 7) {
1,691!
1209
        break;
×
1210
      }
×
1211

1212
      if (str == "udp-rcvbuf-errors") {
1,691!
1213
        return std::stoull(parts.at(5));
×
1214
      }
×
1215
      else if (str == "udp-sndbuf-errors") {
1,691✔
1216
        return std::stoull(parts.at(6));
339✔
1217
      }
339✔
1218
      else if (str == "udp-noport-errors") {
1,352✔
1219
        return std::stoull(parts.at(2));
339✔
1220
      }
339✔
1221
      else if (str == "udp-in-errors") {
1,013✔
1222
        return std::stoull(parts.at(3));
339✔
1223
      }
339✔
1224
      else if (parts.size() >= 8 && str == "udp-in-csum-errors") {
674!
1225
        return std::stoull(parts.at(7));
335✔
1226
      }
335✔
1227
      else {
339✔
1228
        return 0;
339✔
1229
      }
339✔
1230
    }
1,691✔
1231
  }
14,450✔
1232
#endif
×
1233
  return 0;
×
1234
}
1,691✔
1235

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

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

1252
  ifstream ifs("/proc/net/snmp6");
1,690✔
1253
  if (!ifs) {
1,690!
1254
    return 0;
×
1255
  }
×
1256

1257
  std::string line;
1,690✔
1258
  while (getline(ifs, line)) {
129,821✔
1259
    if (!boost::starts_with(line, key->second)) {
129,821✔
1260
      continue;
128,123✔
1261
    }
128,123✔
1262

1263
    std::vector<std::string> parts;
1,698✔
1264
    stringtok(parts, line, " \n\t\r");
1,698✔
1265

1266
    if (parts.size() != 2) {
1,698!
1267
      return 0;
×
1268
    }
×
1269

1270
    return std::stoull(parts.at(1));
1,698✔
1271
  }
1,698✔
1272
#endif
2,147,483,647✔
1273
  return 0;
2,147,483,647✔
1274
}
1,690✔
1275

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

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

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

1294
      return std::stoull(parts.at(20));
207✔
1295
    }
207✔
1296
  }
414✔
1297
#endif
2,147,483,647✔
1298
  return 0;
2,147,483,647✔
1299
}
206✔
1300

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

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

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

1319
      return std::stoull(parts[5]);
335✔
1320
    }
335✔
1321
  }
335✔
UNCOV
1322
#endif
×
UNCOV
1323
  return 0;
×
1324
}
335✔
1325

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

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

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

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

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

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

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

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

1423
  uint64_t size, resident, shared, text, lib, data;
354✔
1424
  ifs >> size >> resident >> shared >> text >> lib >> data;
354✔
1425

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

1437

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

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

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

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

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

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

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

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

1502
  if (pwd == nullptr) {
×
1503
    long long val;
×
1504

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

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

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

1522
  return result;
×
1523
}
×
1524

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

1531
  if (grp == nullptr) {
×
1532
    long long val;
×
1533

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

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

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

1551
  return result;
×
1552
}
×
1553

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

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

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

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

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

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

1604
  string line;
89✔
1605
  while(std::getline(ifs, line)) {
1,153✔
1606
    boost::trim_right_if(line, boost::is_any_of(" \r\n\x1a"));
1,064✔
1607
    boost::trim_left(line); // leading spaces, let's be nice
1,064✔
1608

1609
    string::size_type tpos = line.find_first_of(";#");
1,064✔
1610
    if (tpos != string::npos) {
1,064✔
1611
      line.resize(tpos);
622✔
1612
    }
622✔
1613

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

1628
  return results;
89✔
1629
}
89✔
1630

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

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

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

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

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

1695
  str<<'"';
×
1696

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

1709
  str<<'"';
×
1710

1711
  return str.str();
×
1712
}
×
1713

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

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

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

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

1749
  return res == 0;
1750
#endif /* !HAVE_SODIUM_MEMCMP */
1751
#endif /* !HAVE_CRYPTO_MEMCMP */
1752
}
2,216✔
1753

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

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

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

1780
  return std::nullopt;
484✔
1781
}
487✔
1782

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

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