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

PowerDNS / pdns / 19765062687

28 Nov 2025 01:22PM UTC coverage: 73.135% (+0.03%) from 73.105%
19765062687

Pull #16574

github

web-flow
Merge cc585a1f8 into 8b4e32eff
Pull Request #16574: Improve GitHub error highlighting

38564 of 63428 branches covered (60.8%)

Branch coverage included in aggregate %.

128155 of 164534 relevant lines covered (77.89%)

6255827.11 hits per line

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

62.07
/pdns/misc.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22

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

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

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

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

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

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

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

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

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

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

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

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

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

174
  return len;
1,875✔
175
}
1,881✔
176

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

317
int waitForData(int fileDesc, struct timeval timeout)
318
{
15,891✔
319
  return waitForRWData(fileDesc, true, timeout);
15,891✔
320
}
15,891✔
321

322
int waitForRWData(int fileDesc, bool waitForRead, int seconds, int mseconds, bool* error, bool* disconnected)
323
{
3,534,775✔
324
  struct pollfd pfd{};
3,534,775✔
325
  memset(&pfd, 0, sizeof(pfd));
3,534,775✔
326
  pfd.fd = fileDesc;
3,534,775✔
327

328
  if (waitForRead) {
3,534,775✔
329
    pfd.events = POLLIN;
3,531,638✔
330
  }
3,531,638✔
331
  else {
3,137✔
332
    pfd.events = POLLOUT;
3,137✔
333
  }
3,137✔
334

335
  int ret = poll(&pfd, 1, seconds * 1000 + mseconds);
3,534,775✔
336
  if (ret > 0) {
3,534,777✔
337
    if ((error != nullptr) && (pfd.revents & POLLERR) != 0) {
3,086,497✔
338
      *error = true;
34✔
339
    }
34✔
340
    if ((disconnected != nullptr) && (pfd.revents & POLLHUP) != 0) {
3,086,497✔
341
      *disconnected = true;
34✔
342
    }
34✔
343
  }
3,086,497✔
344

345
  return ret;
3,534,775✔
346
}
3,534,775✔
347

348
int waitForRWData(int fileDesc, bool waitForRead, struct timeval timeout, bool* error, bool* disconnected)
349
{
17,111✔
350
  return waitForRWData(fileDesc, waitForRead, static_cast<int>(timeout.tv_sec), static_cast<int>(timeout.tv_usec / 1000), error, disconnected);
17,111✔
351
}
17,111✔
352

353
// returns -1 in case of error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
354
int waitForMultiData(const set<int>& fds, const int seconds, const int mseconds, int* fdOut) {
2,202✔
355
  set<int> realFDs;
2,202✔
356
  for (const auto& fd : fds) {
4,404✔
357
    if (fd >= 0 && realFDs.count(fd) == 0) {
4,404!
358
      realFDs.insert(fd);
4,380✔
359
    }
4,380✔
360
  }
4,404✔
361

362
  std::vector<struct pollfd> pfds(realFDs.size());
2,202✔
363
  memset(pfds.data(), 0, realFDs.size()*sizeof(struct pollfd));
2,202✔
364
  int ctr = 0;
2,202✔
365
  for (const auto& fd : realFDs) {
4,380✔
366
    pfds[ctr].fd = fd;
4,380✔
367
    pfds[ctr].events = POLLIN;
4,380✔
368
    ctr++;
4,380✔
369
  }
4,380✔
370

371
  int ret;
2,202✔
372
  if(seconds >= 0)
2,202!
373
    ret = poll(pfds.data(), realFDs.size(), seconds * 1000 + mseconds);
2,202✔
374
  else
×
375
    ret = poll(pfds.data(), realFDs.size(), -1);
×
376
  if(ret <= 0)
2,202✔
377
    return ret;
2,196✔
378

379
  set<int> pollinFDs;
6✔
380
  for (const auto& pfd : pfds) {
6✔
381
    if (pfd.revents & POLLIN) {
6!
382
      pollinFDs.insert(pfd.fd);
6✔
383
    }
6✔
384
  }
6✔
385
  set<int>::const_iterator it(pollinFDs.begin());
6✔
386
  advance(it, dns_random(pollinFDs.size()));
6✔
387
  *fdOut = *it;
6✔
388
  return 1;
6✔
389
}
2,202✔
390

391
string humanDuration(time_t passed)
392
{
4✔
393
  ostringstream ret;
4✔
394
  if(passed<60)
4!
395
    ret<<passed<<" seconds";
4✔
396
  else if(passed<3600)
×
397
    ret<<std::setprecision(2)<<passed/60.0<<" minutes";
×
398
  else if(passed<86400)
×
399
    ret<<std::setprecision(3)<<passed/3600.0<<" hours";
×
400
  else if(passed<(86400*30.41))
×
401
    ret<<std::setprecision(3)<<passed/86400.0<<" days";
×
402
  else
×
403
    ret<<std::setprecision(3)<<passed/(86400*30.41)<<" months";
×
404

405
  return ret.str();
4✔
406
}
4✔
407

408
string unquotify(const string &item)
409
{
11✔
410
  if(item.size()<2)
11✔
411
    return item;
2✔
412

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

415
  if(item[0]=='"')
9!
416
    bpos=1;
9✔
417

418
  if(item[epos-1]=='"')
9!
419
    epos-=1;
9✔
420

421
  return item.substr(bpos,epos-bpos);
9✔
422
}
11✔
423

424
void stripLine(string &line)
425
{
×
426
  string::size_type pos=line.find_first_of("\r\n");
×
427
  if(pos!=string::npos) {
×
428
    line.resize(pos);
×
429
  }
×
430
}
×
431

432
string urlEncode(const string &text)
433
{
×
434
  string ret;
×
435
  for(char i : text)
×
436
    if(i==' ')ret.append("%20");
×
437
    else ret.append(1,i);
×
438
  return ret;
×
439
}
×
440

441
static size_t getMaxHostNameSize()
442
{
1,196✔
443
#if defined(HOST_NAME_MAX)
1,196✔
444
  return HOST_NAME_MAX;
1,196✔
445
#endif
×
446

447
#if defined(_SC_HOST_NAME_MAX)
×
448
  auto tmp = sysconf(_SC_HOST_NAME_MAX);
×
449
  if (tmp != -1) {
×
450
    return tmp;
×
451
  }
×
452
#endif
×
453

454
  const size_t maxHostNameSize = 255;
×
455
  return maxHostNameSize;
×
456
}
×
457

458
std::optional<string> getHostname()
459
{
1,196✔
460
  const size_t maxHostNameBufSize = getMaxHostNameSize() + 1;
1,196✔
461
  std::string hostname;
1,196✔
462
  hostname.resize(maxHostNameBufSize, 0);
1,196✔
463

464
  if (gethostname(hostname.data(), maxHostNameBufSize) == -1) {
1,196!
465
    return std::nullopt;
×
466
  }
×
467

468
  hostname.resize(strlen(hostname.c_str()));
1,196✔
469
  return std::make_optional(hostname);
1,196✔
470
}
1,196✔
471

472
std::string getCarbonHostName()
473
{
175✔
474
  auto hostname = getHostname();
175✔
475
  if (!hostname.has_value()) {
175!
476
    throw std::runtime_error(stringerror());
×
477
  }
×
478

479
  std::replace(hostname->begin(), hostname->end(), '.', '_');
175✔
480
  return *hostname;
175✔
481
}
175✔
482

483
void cleanSlashes(string &str)
484
{
7,446✔
485
  string out;
7,446✔
486
  bool keepNextSlash = true;
7,446✔
487
  for (const auto& value : str) {
140,717✔
488
    if (value == '/') {
140,717✔
489
      if (keepNextSlash) {
9,330✔
490
        keepNextSlash = false;
9,123✔
491
      }
9,123✔
492
      else {
207✔
493
        continue;
207✔
494
      }
207✔
495
    }
9,330✔
496
    else {
131,387✔
497
      keepNextSlash = true;
131,387✔
498
    }
131,387✔
499
    out.append(1, value);
140,510✔
500
  }
140,510✔
501
  str = std::move(out);
7,446✔
502
}
7,446✔
503

504
bool IpToU32(const string &str, uint32_t *ip)
505
{
×
506
  if(str.empty()) {
×
507
    *ip=0;
×
508
    return true;
×
509
  }
×
510

511
  struct in_addr inp;
×
512
  if(inet_aton(str.c_str(), &inp)) {
×
513
    *ip=inp.s_addr;
×
514
    return true;
×
515
  }
×
516
  return false;
×
517
}
×
518

519
string U32ToIP(uint32_t val)
520
{
×
521
  char tmp[17];
×
522
  snprintf(tmp, sizeof(tmp), "%u.%u.%u.%u",
×
523
           (val >> 24)&0xff,
×
524
           (val >> 16)&0xff,
×
525
           (val >>  8)&0xff,
×
526
           (val      )&0xff);
×
527
  return string(tmp);
×
528
}
×
529

530

531
string makeHexDump(const string& str, const string& sep)
532
{
1,687✔
533
  std::array<char, 5> tmp;
1,687✔
534
  string ret;
1,687✔
535
  ret.reserve(static_cast<size_t>(str.size() * (2 + sep.size())));
1,687✔
536

537
  for (char n : str) {
88,124✔
538
    snprintf(tmp.data(), tmp.size(), "%02x", static_cast<unsigned char>(n));
88,124✔
539
    ret += tmp.data();
88,124✔
540
    ret += sep;
88,124✔
541
  }
88,124✔
542
  return ret;
1,687✔
543
}
1,687✔
544

545
string makeBytesFromHex(const string &in) {
9✔
546
  if (in.size() % 2 != 0) {
9✔
547
    throw std::range_error("odd number of bytes in hex string");
3✔
548
  }
3✔
549
  string ret;
6✔
550
  ret.reserve(in.size() / 2);
6✔
551

552
  for (size_t i = 0; i < in.size(); i += 2) {
36✔
553
    const auto numStr = in.substr(i, 2);
33✔
554
    unsigned int num = 0;
33✔
555
    if (sscanf(numStr.c_str(), "%02x", &num) != 1) {
33✔
556
      throw std::range_error("Invalid value while parsing the hex string '" + in + "'");
3✔
557
    }
3✔
558
    ret.push_back(static_cast<uint8_t>(num));
30✔
559
  }
30✔
560

561
  return ret;
3✔
562
}
6✔
563

564
void normalizeTV(struct timeval& tv)
565
{
238,230✔
566
  if(tv.tv_usec > 1000000) {
238,230✔
567
    ++tv.tv_sec;
4,819✔
568
    tv.tv_usec-=1000000;
4,819✔
569
  }
4,819✔
570
  else if(tv.tv_usec < 0) {
233,411✔
571
    --tv.tv_sec;
47,992✔
572
    tv.tv_usec+=1000000;
47,992✔
573
  }
47,992✔
574
}
238,230✔
575

576
struct timeval operator+(const struct timeval& lhs, const struct timeval& rhs)
577
{
10,969✔
578
  struct timeval ret;
10,969✔
579
  ret.tv_sec=lhs.tv_sec + rhs.tv_sec;
10,969✔
580
  ret.tv_usec=lhs.tv_usec + rhs.tv_usec;
10,969✔
581
  normalizeTV(ret);
10,969✔
582
  return ret;
10,969✔
583
}
10,969✔
584

585
struct timeval operator-(const struct timeval& lhs, const struct timeval& rhs)
586
{
97,058✔
587
  struct timeval ret;
97,058✔
588
  ret.tv_sec=lhs.tv_sec - rhs.tv_sec;
97,058✔
589
  ret.tv_usec=lhs.tv_usec - rhs.tv_usec;
97,058✔
590
  normalizeTV(ret);
97,058✔
591
  return ret;
97,058✔
592
}
97,058✔
593

594
pair<string, string> splitField(const string& inp, char sepa)
595
{
876,393✔
596
  pair<string, string> ret;
876,393✔
597
  string::size_type cpos=inp.find(sepa);
876,393✔
598
  if(cpos==string::npos)
876,393✔
599
    ret.first=inp;
404,521✔
600
  else {
471,872✔
601
    ret.first=inp.substr(0, cpos);
471,872✔
602
    ret.second=inp.substr(cpos+1);
471,872✔
603
  }
471,872✔
604
  return ret;
876,393✔
605
}
876,393✔
606

607
int logFacilityToLOG(unsigned int facility)
608
{
×
609
  switch(facility) {
×
610
  case 0:
×
611
    return LOG_LOCAL0;
×
612
  case 1:
×
613
    return(LOG_LOCAL1);
×
614
  case 2:
×
615
    return(LOG_LOCAL2);
×
616
  case 3:
×
617
    return(LOG_LOCAL3);
×
618
  case 4:
×
619
    return(LOG_LOCAL4);
×
620
  case 5:
×
621
    return(LOG_LOCAL5);
×
622
  case 6:
×
623
    return(LOG_LOCAL6);
×
624
  case 7:
×
625
    return(LOG_LOCAL7);
×
626
  default:
×
627
    return -1;
×
628
  }
×
629
}
×
630

631
std::optional<int> logFacilityFromString(std::string facilityStr)
632
{
×
633
  static std::unordered_map<std::string, int> const s_facilities = {
×
634
    {"local0", LOG_LOCAL0},
×
635
    {"log_local0", LOG_LOCAL0},
×
636
    {"local1", LOG_LOCAL1},
×
637
    {"log_local1", LOG_LOCAL1},
×
638
    {"local2", LOG_LOCAL2},
×
639
    {"log_local2", LOG_LOCAL2},
×
640
    {"local3", LOG_LOCAL3},
×
641
    {"log_local3", LOG_LOCAL3},
×
642
    {"local4", LOG_LOCAL4},
×
643
    {"log_local4", LOG_LOCAL4},
×
644
    {"local5", LOG_LOCAL5},
×
645
    {"log_local5", LOG_LOCAL5},
×
646
    {"local6", LOG_LOCAL6},
×
647
    {"log_local6", LOG_LOCAL6},
×
648
    {"local7", LOG_LOCAL7},
×
649
    {"log_local7", LOG_LOCAL7},
×
650
    /* most of these likely make very little sense
651
       for us, but why not? */
652
    {"kern", LOG_KERN},
×
653
    {"log_kern", LOG_KERN},
×
654
    {"user", LOG_USER},
×
655
    {"log_user", LOG_USER},
×
656
    {"mail", LOG_MAIL},
×
657
    {"log_mail", LOG_MAIL},
×
658
    {"daemon", LOG_DAEMON},
×
659
    {"log_daemon", LOG_DAEMON},
×
660
    {"auth", LOG_AUTH},
×
661
    {"log_auth", LOG_AUTH},
×
662
    {"syslog", LOG_SYSLOG},
×
663
    {"log_syslog", LOG_SYSLOG},
×
664
    {"lpr", LOG_LPR},
×
665
    {"log_lpr", LOG_LPR},
×
666
    {"news", LOG_NEWS},
×
667
    {"log_news", LOG_NEWS},
×
668
    {"uucp", LOG_UUCP},
×
669
    {"log_uucp", LOG_UUCP},
×
670
    {"cron", LOG_CRON},
×
671
    {"log_cron", LOG_CRON},
×
672
    {"authpriv", LOG_AUTHPRIV},
×
673
    {"log_authpriv", LOG_AUTHPRIV},
×
674
    {"ftp", LOG_FTP},
×
675
    {"log_ftp", LOG_FTP}
×
676
  };
×
677

678
  toLowerInPlace(facilityStr);
×
679
  auto facilityIt = s_facilities.find(facilityStr);
×
680
  if (facilityIt == s_facilities.end()) {
×
681
    return std::nullopt;
×
682
  }
×
683

684
  return facilityIt->second;
×
685
}
×
686

687
string stripDot(const string& dom)
688
{
671,593✔
689
  if(dom.empty())
671,593✔
690
    return dom;
3✔
691

692
  if(dom[dom.size()-1]!='.')
671,590✔
693
    return dom;
671,143✔
694

695
  return dom.substr(0,dom.size()-1);
447✔
696
}
671,590✔
697

698
int makeIPv6sockaddr(const std::string& addr, struct sockaddr_in6* ret)
699
{
1,641,721✔
700
  if (addr.empty()) {
1,641,721✔
701
    return -1;
17✔
702
  }
17✔
703

704
  string ourAddr(addr);
1,641,704✔
705
  std::optional<uint16_t> port = std::nullopt;
1,641,704✔
706

707
  if (addr[0] == '[') { // [::]:53 style address
1,641,704✔
708
    string::size_type pos = addr.find(']');
131,582✔
709
    if (pos == string::npos) {
131,582!
710
      return -1;
×
711
    }
×
712

713
    ourAddr.assign(addr.c_str() + 1, pos - 1);
131,582✔
714
    if (pos + 1 != addr.size()) { // complete after ], no port specified
131,582✔
715
      if (pos + 2 > addr.size() || addr[pos + 1] != ':') {
131,558!
716
        return -1;
×
717
      }
×
718

719
      try {
131,558✔
720
        auto tmpPort = pdns::checked_stoi<uint16_t>(addr.substr(pos + 2));
131,558✔
721
        port = std::make_optional(tmpPort);
131,558✔
722
      }
131,558✔
723
      catch (const std::out_of_range&) {
131,558✔
724
        return -1;
24✔
725
      }
24✔
726
    }
131,558✔
727
  }
131,582✔
728

729
  ret->sin6_scope_id = 0;
1,641,680✔
730
  ret->sin6_family = AF_INET6;
1,641,680✔
731

732
  if (inet_pton(AF_INET6, ourAddr.c_str(), (void*)&ret->sin6_addr) != 1) {
1,641,680✔
733
    struct addrinfo hints{};
462✔
734
    std::memset(&hints, 0, sizeof(struct addrinfo));
462✔
735
    hints.ai_flags = AI_NUMERICHOST;
462✔
736
    hints.ai_family = AF_INET6;
462✔
737

738
    struct addrinfo* res = nullptr;
462✔
739
    // getaddrinfo has anomalous return codes, anything nonzero is an error, positive or negative
740
    if (getaddrinfo(ourAddr.c_str(), nullptr, &hints, &res) != 0) {
462!
741
      return -1;
462✔
742
    }
462✔
743

744
    memcpy(ret, res->ai_addr, res->ai_addrlen);
×
745
    freeaddrinfo(res);
×
746
  }
×
747

748
  if (port.has_value()) {
1,641,218✔
749
    ret->sin6_port = htons(*port);
131,534✔
750
  }
131,534✔
751

752
  return 0;
1,641,218✔
753
}
1,641,680✔
754

755
int makeIPv4sockaddr(const std::string& str, struct sockaddr_in* ret)
756
{
2,703,228✔
757
  if(str.empty()) {
2,703,228✔
758
    return -1;
6✔
759
  }
6✔
760
  struct in_addr inp;
2,703,222✔
761

762
  string::size_type pos = str.find(':');
2,703,222✔
763
  if(pos == string::npos) { // no port specified, not touching the port
2,703,222✔
764
    if(inet_aton(str.c_str(), &inp)) {
1,062,816✔
765
      ret->sin_addr.s_addr=inp.s_addr;
1,062,730✔
766
      return 0;
1,062,730✔
767
    }
1,062,730✔
768
    return -1;
86✔
769
  }
1,062,816✔
770
  if(!*(str.c_str() + pos + 1)) // trailing :
1,640,406!
771
    return -1;
×
772

773
  char *eptr = const_cast<char*>(str.c_str()) + str.size();
1,640,406✔
774
  int port = strtol(str.c_str() + pos + 1, &eptr, 10);
1,640,406✔
775
  if (port < 0 || port > 65535)
1,640,406✔
776
    return -1;
24✔
777

778
  if(*eptr)
1,640,382✔
779
    return -1;
1,231,869✔
780

781
  ret->sin_port = htons(port);
408,513✔
782
  if(inet_aton(str.substr(0, pos).c_str(), &inp)) {
408,513!
783
    ret->sin_addr.s_addr=inp.s_addr;
408,513✔
784
    return 0;
408,513✔
785
  }
408,513✔
786
  return -1;
×
787
}
408,513✔
788

789
int makeUNsockaddr(const std::string& path, struct sockaddr_un* ret)
790
{
1,445✔
791
  if (path.empty())
1,445✔
792
    return -1;
6✔
793

794
  memset(ret, 0, sizeof(struct sockaddr_un));
1,439✔
795
  ret->sun_family = AF_UNIX;
1,439✔
796
  if (path.length() >= sizeof(ret->sun_path))
1,439!
797
    return -1;
×
798

799
  path.copy(ret->sun_path, sizeof(ret->sun_path), 0);
1,439✔
800
  return 0;
1,439✔
801
}
1,439✔
802

803
//! read a line of text from a FILE* to a std::string, returns false on 'no data'
804
bool stringfgets(FILE* fp, std::string& line)
805
{
4,342,221✔
806
  char buffer[1024];
4,342,221✔
807
  line.clear();
4,342,221✔
808

809
  do {
4,342,497✔
810
    if(!fgets(buffer, sizeof(buffer), fp))
4,342,497✔
811
      return !line.empty();
4,305✔
812

813
    line.append(buffer);
4,338,192✔
814
  } while(!strchr(buffer, '\n'));
4,338,192✔
815
  return true;
4,337,916✔
816
}
4,342,221✔
817

818
bool readFileIfThere(const char* fname, std::string* line)
819
{
370✔
820
  line->clear();
370✔
821
  auto filePtr = pdns::UniqueFilePtr(fopen(fname, "r"));
370✔
822
  if (!filePtr) {
370!
823
    return false;
×
824
  }
×
825
  return stringfgets(filePtr.get(), *line);
370✔
826
}
370✔
827

828
Regex::Regex(const string& expr):
829
  d_preg(new regex_t)
31✔
830
{
244✔
831
  if (auto ret = regcomp(d_preg.get(), expr.c_str(), REG_ICASE|REG_NOSUB|REG_EXTENDED); ret != 0) {
244!
832
    std::array<char, 1024> errorBuffer{};
×
833
    if (regerror(ret, d_preg.get(), errorBuffer.data(), errorBuffer.size()) > 0) {
×
834
      throw PDNSException("Regular expression " + expr + " did not compile: " + errorBuffer.data());
×
835
    }
×
836
    throw PDNSException("Regular expression " + expr + " did not compile");
×
837
  }
×
838
}
244✔
839

840
/** call this to find out if 'line' matches your expression */
841
bool Regex::match(const string &line) const
842
{
1,493,419✔
843
  return regexec(d_preg.get(), line.c_str(), 0, nullptr, 0) == 0;
1,493,419✔
844
}
1,493,419✔
845

846
bool Regex::match(const DNSName& name) const
847
{
×
848
  return match(name.toStringNoDot());
×
849
}
×
850

851
// if you end up here because valgrind told you were are doing something wrong
852
// with msgh->msg_controllen, please refer to https://github.com/PowerDNS/pdns/pull/3962
853
// first.
854
// Note that cmsgbuf should be aligned the same as a struct cmsghdr
855
void addCMsgSrcAddr(struct msghdr* msgh, cmsgbuf_aligned* cmsgbuf, const ComboAddress* source, int itfIndex)
856
{
28,433✔
857
  struct cmsghdr *cmsg = nullptr;
28,433✔
858

859
  if(source->sin4.sin_family == AF_INET6) {
28,433✔
860
    struct in6_pktinfo *pkt;
1✔
861

862
    msgh->msg_control = cmsgbuf;
1✔
863
#if !defined( __APPLE__ )
1✔
864
    /* CMSG_SPACE is not a constexpr on macOS */
865
    static_assert(CMSG_SPACE(sizeof(*pkt)) <= sizeof(*cmsgbuf), "Buffer is too small for in6_pktinfo");
1✔
866
#else /* __APPLE__ */
867
    if (CMSG_SPACE(sizeof(*pkt)) > sizeof(*cmsgbuf)) {
868
      throw std::runtime_error("Buffer is too small for in6_pktinfo");
869
    }
870
#endif /* __APPLE__ */
871
    msgh->msg_controllen = CMSG_SPACE(sizeof(*pkt));
1✔
872

873
    cmsg = CMSG_FIRSTHDR(msgh);
1!
874
    cmsg->cmsg_level = IPPROTO_IPV6;
1✔
875
    cmsg->cmsg_type = IPV6_PKTINFO;
1✔
876
    cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
1✔
877

878
    pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
1✔
879
    // Include the padding to stop valgrind complaining about passing uninitialized data
880
    memset(pkt, 0, CMSG_SPACE(sizeof(*pkt)));
1✔
881
    pkt->ipi6_addr = source->sin6.sin6_addr;
1✔
882
    pkt->ipi6_ifindex = itfIndex;
1✔
883
  }
1✔
884
  else {
28,432✔
885
#if defined(IP_PKTINFO)
28,432✔
886
    struct in_pktinfo *pkt;
28,432✔
887

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

899
    cmsg = CMSG_FIRSTHDR(msgh);
28,432!
900
    cmsg->cmsg_level = IPPROTO_IP;
28,432✔
901
    cmsg->cmsg_type = IP_PKTINFO;
28,432✔
902
    cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
28,432✔
903

904
    pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
28,432✔
905
    // Include the padding to stop valgrind complaining about passing uninitialized data
906
    memset(pkt, 0, CMSG_SPACE(sizeof(*pkt)));
28,432✔
907
    pkt->ipi_spec_dst = source->sin4.sin_addr;
28,432✔
908
    pkt->ipi_ifindex = itfIndex;
28,432✔
909
#elif defined(IP_SENDSRCADDR)
910
    struct in_addr *in;
911

912
    msgh->msg_control = cmsgbuf;
913
#if !defined( __APPLE__ )
914
    static_assert(CMSG_SPACE(sizeof(*in)) <= sizeof(*cmsgbuf), "Buffer is too small for in_addr");
915
#else /* __APPLE__ */
916
    if (CMSG_SPACE(sizeof(*in)) > sizeof(*cmsgbuf)) {
917
      throw std::runtime_error("Buffer is too small for in_addr");
918
    }
919
#endif /* __APPLE__ */
920
    msgh->msg_controllen = CMSG_SPACE(sizeof(*in));
921

922
    cmsg = CMSG_FIRSTHDR(msgh);
923
    cmsg->cmsg_level = IPPROTO_IP;
924
    cmsg->cmsg_type = IP_SENDSRCADDR;
925
    cmsg->cmsg_len = CMSG_LEN(sizeof(*in));
926

927
    // Include the padding to stop valgrind complaining about passing uninitialized data
928
    in = (struct in_addr *) CMSG_DATA(cmsg);
929
    memset(in, 0, CMSG_SPACE(sizeof(*in)));
930
    *in = source->sin4.sin_addr;
931
#endif
932
  }
28,432✔
933
}
28,433✔
934

935
unsigned int getFilenumLimit(bool hardOrSoft)
936
{
185✔
937
  struct rlimit rlim;
185✔
938
  if(getrlimit(RLIMIT_NOFILE, &rlim) < 0)
185!
939
    unixDie("Requesting number of available file descriptors");
×
940
  return hardOrSoft ? rlim.rlim_max : rlim.rlim_cur;
185!
941
}
185✔
942

943
void setFilenumLimit(unsigned int lim)
944
{
×
945
  struct rlimit rlim;
×
946

947
  if(getrlimit(RLIMIT_NOFILE, &rlim) < 0)
×
948
    unixDie("Requesting number of available file descriptors");
×
949
  rlim.rlim_cur=lim;
×
950
  if(setrlimit(RLIMIT_NOFILE, &rlim) < 0)
×
951
    unixDie("Setting number of available file descriptors");
×
952
}
×
953

954
bool setSocketTimestamps(int fd)
955
{
598✔
956
#ifdef SO_TIMESTAMP
598✔
957
  int on=1;
598✔
958
  return setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, (char*)&on, sizeof(on)) == 0;
598✔
959
#else
960
  return true; // we pretend this happened.
961
#endif
962
}
598✔
963

964
bool setTCPNoDelay(int sock)
965
{
7,029✔
966
  int flag = 1;
7,029✔
967
  return setsockopt(sock,            /* socket affected */
7,029✔
968
                    IPPROTO_TCP,     /* set option at TCP level */
7,029✔
969
                    TCP_NODELAY,     /* name of option */
7,029✔
970
                    (char *) &flag,  /* the cast is historical cruft */
7,029✔
971
                    sizeof(flag)) == 0;    /* length of option value */
7,029✔
972
}
7,029✔
973

974

975
bool setNonBlocking(int sock)
976
{
157,694✔
977
  int flags=fcntl(sock,F_GETFL,0);
157,694✔
978
  if(flags<0 || fcntl(sock, F_SETFL,flags|O_NONBLOCK) <0)
157,697✔
979
    return false;
×
980
  return true;
157,694✔
981
}
157,694✔
982

983
bool setBlocking(int sock)
984
{
89,491✔
985
  int flags=fcntl(sock,F_GETFL,0);
89,491✔
986
  if(flags<0 || fcntl(sock, F_SETFL,flags&(~O_NONBLOCK)) <0)
89,491!
987
    return false;
×
988
  return true;
89,491✔
989
}
89,491✔
990

991
bool setReuseAddr(int sock)
992
{
34✔
993
  int tmp = 1;
34✔
994
  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&tmp, static_cast<unsigned>(sizeof tmp))<0)
34!
995
    throw PDNSException(string("Setsockopt failed: ")+stringerror());
×
996
  return true;
34✔
997
}
34✔
998

999
void setDscp(int sock, unsigned short family, uint8_t dscp)
1000
{
3,070✔
1001
  int val = 0;
3,070✔
1002
  unsigned int len = 0;
3,070✔
1003

1004
  if (dscp == 0 || dscp > 63) {
3,070!
1005
    // No DSCP marking
1006
    return;
3,070✔
1007
  }
3,070✔
1008

1009
  if (family == AF_INET) {
×
1010
    if (getsockopt(sock, IPPROTO_IP, IP_TOS, &val, &len)<0) {
×
1011
      throw std::runtime_error(string("Set DSCP failed: ")+stringerror());
×
1012
    }
×
1013
    val = (dscp<<2) | (val&0x3);
×
1014
    if (setsockopt(sock, IPPROTO_IP, IP_TOS, &val, sizeof(val))<0) {
×
1015
      throw std::runtime_error(string("Set DSCP failed: ")+stringerror());
×
1016
    }
×
1017
  }
×
1018
  else if (family == AF_INET6) {
×
1019
    if (getsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, &val, &len)<0) {
×
1020
      throw std::runtime_error(string("Set DSCP failed: ")+stringerror());
×
1021
    }
×
1022
    val = (dscp<<2) | (val&0x3);
×
1023
    if (setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, &val, sizeof(val))<0) {
×
1024
      throw std::runtime_error(string("Set DSCP failed: ")+stringerror());
×
1025
    }
×
1026
  }
×
1027
}
×
1028

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

1035
bool setReceiveSocketErrors([[maybe_unused]] int sock, [[maybe_unused]] int af)
1036
{
9,360✔
1037
#ifdef __linux__
9,360✔
1038
  int tmp = 1, ret;
9,360✔
1039
  if (af == AF_INET) {
9,362✔
1040
    ret = setsockopt(sock, IPPROTO_IP, IP_RECVERR, &tmp, sizeof(tmp));
9,342✔
1041
  } else {
2,147,485,358✔
1042
    ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVERR, &tmp, sizeof(tmp));
2,147,483,655✔
1043
  }
2,147,483,655✔
1044
  if (ret < 0) {
9,360!
1045
    throw PDNSException(string("Setsockopt failed: ") + stringerror());
×
1046
  }
×
1047
#endif
9,360✔
1048
  return true;
9,360✔
1049
}
9,360✔
1050

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

1065
bool setCloseOnExec(int sock)
1066
{
46,532✔
1067
  int flags=fcntl(sock,F_GETFD,0);
46,532✔
1068
  if(flags<0 || fcntl(sock, F_SETFD,flags|FD_CLOEXEC) <0)
46,532!
1069
    return false;
×
1070
  return true;
46,532✔
1071
}
46,532✔
1072

1073
#ifdef __linux__
1074
#include <linux/rtnetlink.h>
1075

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

1083
  std::array<char, 8192> buffer;
3✔
1084

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

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

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

1110
  bool done = false;
3✔
1111
  bool foundIP = false;
3✔
1112
  bool foundMAC = false;
3✔
1113
  do {
3✔
1114
    ssize_t got = recv(sock.getHandle(), buffer.data(), buffer.size(), 0);
3✔
1115

1116
    if (got < 0) {
3!
1117
      if (errno == EINTR) {
×
1118
        continue;
×
1119
      }
×
1120
      return errno;
×
1121
    }
×
1122

1123
    size_t remaining = static_cast<size_t>(got);
3✔
1124
    for (struct nlmsghdr* nlmsgheader = reinterpret_cast<struct nlmsghdr*>(buffer.data());
3✔
1125
         done == false && NLMSG_OK (nlmsgheader, remaining);
9!
1126
         nlmsgheader = reinterpret_cast<struct nlmsghdr*>(NLMSG_NEXT(nlmsgheader, remaining))) {
9✔
1127

1128
      if (nlmsgheader->nlmsg_type == NLMSG_DONE) {
9✔
1129
        done = true;
3✔
1130
        break;
3✔
1131
      }
3✔
1132

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

1137
      if (nd->ndm_family != ca.sin4.sin_family) {
6!
1138
        continue;
×
1139
      }
×
1140

1141
      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) {
6!
1142
        continue;
×
1143
      }
×
1144

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

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

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

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

1204
  string line;
2,002✔
1205
  while (getline(ifs, line)) {
18,963✔
1206
    if (boost::starts_with(line, "Udp: ") && isdigit(line.at(5))) {
18,963✔
1207
      vector<string> parts;
2,004✔
1208
      stringtok(parts, line, " \n\t\r");
2,004✔
1209

1210
      if (parts.size() < 7) {
2,004!
1211
        break;
×
1212
      }
×
1213

1214
      if (str == "udp-rcvbuf-errors") {
2,004!
1215
        return std::stoull(parts.at(5));
×
1216
      }
×
1217
      else if (str == "udp-sndbuf-errors") {
2,004✔
1218
        return std::stoull(parts.at(6));
401✔
1219
      }
401✔
1220
      else if (str == "udp-noport-errors") {
1,603✔
1221
        return std::stoull(parts.at(2));
401✔
1222
      }
401✔
1223
      else if (str == "udp-in-errors") {
1,202✔
1224
        return std::stoull(parts.at(3));
401✔
1225
      }
401✔
1226
      else if (parts.size() >= 8 && str == "udp-in-csum-errors") {
802✔
1227
        return std::stoull(parts.at(7));
401✔
1228
      }
401✔
1229
      else {
400✔
1230
        return 0;
400✔
1231
      }
400✔
1232
    }
2,004✔
1233
  }
18,963✔
1234
#endif
2,147,483,647✔
1235
  return 0;
2,147,483,647✔
1236
}
2,002✔
1237

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

1249
  auto key = keys.find(str);
2,005✔
1250
  if (key == keys.end()) {
2,005!
1251
    return 0;
×
1252
  }
×
1253

1254
  ifstream ifs("/proc/net/snmp6");
2,005✔
1255
  if (!ifs) {
2,005!
1256
    return 0;
×
1257
  }
×
1258

1259
  std::string line;
2,005✔
1260
  while (getline(ifs, line)) {
147,871✔
1261
    if (!boost::starts_with(line, key->second)) {
147,870✔
1262
      continue;
145,866✔
1263
    }
145,866✔
1264

1265
    std::vector<std::string> parts;
2,004✔
1266
    stringtok(parts, line, " \n\t\r");
2,004✔
1267

1268
    if (parts.size() != 2) {
2,004!
1269
      return 0;
×
1270
    }
×
1271

1272
    return std::stoull(parts.at(1));
2,004✔
1273
  }
2,004✔
1274
#endif
1✔
1275
  return 0;
1✔
1276
}
2,005✔
1277

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

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

1292
      if (parts.size() < 21) {
250!
1293
        break;
×
1294
      }
×
1295

1296
      return std::stoull(parts.at(20));
250✔
1297
    }
250✔
1298
  }
500✔
1299
#endif
×
1300
  return 0;
×
1301
}
250✔
1302

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

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

1317
      if (parts.size() < 6) {
396!
1318
        break;
×
1319
      }
×
1320

1321
      return std::stoull(parts[5]);
396✔
1322
    }
396✔
1323
  }
397✔
1324
#endif
×
1325
  return 0;
×
1326
}
396✔
1327

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

1336
  string line;
397✔
1337
  vector<string> parts;
397✔
1338
  while (getline(ifs, line)) {
398✔
1339
    if (boost::starts_with(line, "cpu ")) {
397✔
1340
      stringtok(parts, line, " \n\t\r");
396✔
1341

1342
      if (parts.size() < 9) {
396!
1343
        break;
×
1344
      }
×
1345

1346
      return std::stoull(parts[8]);
396✔
1347
    }
396✔
1348
  }
397✔
1349
#endif
1✔
1350
  return 0;
1✔
1351
}
397✔
1352

1353
bool getTSIGHashEnum(const DNSName& algoName, TSIGHashEnum& algoEnum)
1354
{
1,299✔
1355
  if (algoName == g_hmacmd5dnsname_long || algoName == g_hmacmd5dnsname) {
1,299✔
1356
    algoEnum = TSIG_MD5;
1,200✔
1357
  }
1,200✔
1358
  else if (algoName == g_hmacsha1dnsname) {
99✔
1359
    algoEnum = TSIG_SHA1;
76✔
1360
  }
76✔
1361
  else if (algoName == g_hmacsha224dnsname) {
23!
1362
    algoEnum = TSIG_SHA224;
×
1363
  }
×
1364
  else if (algoName == g_hmacsha256dnsname) {
23✔
1365
    algoEnum = TSIG_SHA256;
4✔
1366
  }
4✔
1367
  else if (algoName == g_hmacsha384dnsname) {
19!
1368
    algoEnum = TSIG_SHA384;
×
1369
  }
×
1370
  else if (algoName == g_hmacsha512dnsname) {
19✔
1371
    algoEnum = TSIG_SHA512;
11✔
1372
  }
11✔
1373
  else if (algoName == g_gsstsigdnsname) {
8!
1374
    algoEnum = TSIG_GSS;
×
1375
  }
×
1376
  else {
8✔
1377
     return false;
8✔
1378
  }
8✔
1379
  return true;
1,291✔
1380
}
1,299✔
1381

1382
DNSName getTSIGAlgoName(TSIGHashEnum& algoEnum)
1383
{
135✔
1384
  switch(algoEnum) {
135!
1385
  case TSIG_MD5: return g_hmacmd5dnsname_long;
135!
1386
  case TSIG_SHA1: return g_hmacsha1dnsname;
×
1387
  case TSIG_SHA224: return g_hmacsha224dnsname;
×
1388
  case TSIG_SHA256: return g_hmacsha256dnsname;
×
1389
  case TSIG_SHA384: return g_hmacsha384dnsname;
×
1390
  case TSIG_SHA512: return g_hmacsha512dnsname;
×
1391
  case TSIG_GSS: return g_gsstsigdnsname;
×
1392
  }
135✔
1393
  throw PDNSException("getTSIGAlgoName does not understand given algorithm, please fix!");
×
1394
}
135✔
1395

1396
uint64_t getOpenFileDescriptors(const std::string&)
1397
{
413✔
1398
#ifdef __linux__
413✔
1399
  uint64_t nbFileDescriptors = 0;
413✔
1400
  const auto dirName = "/proc/" + std::to_string(getpid()) + "/fd/";
413✔
1401
  auto directoryError = pdns::visit_directory(dirName, [&nbFileDescriptors]([[maybe_unused]] ino_t inodeNumber, const std::string_view& name) {
26,319✔
1402
    uint32_t num;
26,319✔
1403
    try {
26,319✔
1404
      pdns::checked_stoi_into(num, std::string(name));
26,319✔
1405
      if (std::to_string(num) == name) {
26,319✔
1406
        nbFileDescriptors++;
25,487✔
1407
      }
25,487✔
1408
    } catch (...) {
26,319✔
1409
      // was not a number.
1410
    }
827✔
1411
    return true;
26,319✔
1412
  });
26,319✔
1413
  if (directoryError) {
413!
1414
    return 0U;
×
1415
  }
×
1416
  return nbFileDescriptors;
413✔
1417
#elif defined(__OpenBSD__)
1418
  // FreeBSD also has this in libopenbsd, but I don't know if that's available always
1419
  return getdtablecount();
1420
#else
1421
  return 0U;
1422
#endif
1423
}
413✔
1424

1425
uint64_t getRealMemoryUsage(const std::string&)
1426
{
414✔
1427
#ifdef __linux__
414✔
1428
  ifstream ifs("/proc/self/statm");
414✔
1429
  if(!ifs)
414!
1430
    return 0;
×
1431

1432
  uint64_t size, resident, shared, text, lib, data;
414✔
1433
  ifs >> size >> resident >> shared >> text >> lib >> data;
414✔
1434

1435
  // We used to use "data" here, but it proves unreliable and even is marked "broken"
1436
  // in https://www.kernel.org/doc/html/latest/filesystems/proc.html
1437
  return resident * getpagesize();
414✔
1438
#else
1439
  struct rusage ru;
1440
  if (getrusage(RUSAGE_SELF, &ru) != 0)
1441
    return 0;
1442
  return ru.ru_maxrss * 1024;
1443
#endif
1444
}
414✔
1445

1446

1447
uint64_t getSpecialMemoryUsage(const std::string&)
1448
{
17✔
1449
#ifdef __linux__
17✔
1450
  ifstream ifs("/proc/self/smaps");
17✔
1451
  if(!ifs)
17!
1452
    return 0;
×
1453
  string line;
17✔
1454
  uint64_t bytes=0;
17✔
1455
  string header("Private_Dirty:");
17✔
1456
  while(getline(ifs, line)) {
229,420✔
1457
    if(boost::starts_with(line, header)) {
229,403✔
1458
      bytes += std::stoull(line.substr(header.length() + 1))*1024;
9,245✔
1459
    }
9,245✔
1460
  }
229,403✔
1461
  return bytes;
17✔
1462
#else
1463
  return 0;
1464
#endif
1465
}
17✔
1466

1467
uint64_t getCPUTimeUser(const std::string&)
1468
{
261✔
1469
  struct rusage ru;
261✔
1470
  getrusage(RUSAGE_SELF, &ru);
261✔
1471
  return (ru.ru_utime.tv_sec*1000ULL + ru.ru_utime.tv_usec/1000);
261✔
1472
}
261✔
1473

1474
uint64_t getCPUTimeSystem(const std::string&)
1475
{
263✔
1476
  struct rusage ru;
263✔
1477
  getrusage(RUSAGE_SELF, &ru);
263✔
1478
  return (ru.ru_stime.tv_sec*1000ULL + ru.ru_stime.tv_usec/1000);
263✔
1479
}
263✔
1480

1481
double DiffTime(const struct timespec& first, const struct timespec& second)
1482
{
126✔
1483
  auto seconds = second.tv_sec - first.tv_sec;
126✔
1484
  auto nseconds = second.tv_nsec - first.tv_nsec;
126✔
1485

1486
  if (nseconds < 0) {
126✔
1487
    seconds -= 1;
110✔
1488
    nseconds += 1000000000;
110✔
1489
  }
110✔
1490
  return static_cast<double>(seconds) + static_cast<double>(nseconds) / 1000000000.0;
126✔
1491
}
126✔
1492

1493
double DiffTime(const struct timeval& first, const struct timeval& second)
1494
{
×
1495
  int seconds=second.tv_sec - first.tv_sec;
×
1496
  int useconds=second.tv_usec - first.tv_usec;
×
1497

1498
  if(useconds < 0) {
×
1499
    seconds-=1;
×
1500
    useconds+=1000000;
×
1501
  }
×
1502
  return seconds + useconds/1000000.0;
×
1503
}
×
1504

1505
uid_t strToUID(const string &str)
1506
{
×
1507
  uid_t result = 0;
×
1508
  const char * cstr = str.c_str();
×
1509
  struct passwd * pwd = getpwnam(cstr);
×
1510

1511
  if (pwd == nullptr) {
×
1512
    long long val;
×
1513

1514
    try {
×
1515
      val = stoll(str);
×
1516
    }
×
1517
    catch(std::exception& e) {
×
1518
      throw runtime_error((boost::format("Error: Unable to parse user ID %s") % cstr).str() );
×
1519
    }
×
1520

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

1525
    result = static_cast<uid_t>(val);
×
1526
  }
×
1527
  else {
×
1528
    result = pwd->pw_uid;
×
1529
  }
×
1530

1531
  return result;
×
1532
}
×
1533

1534
gid_t strToGID(const string &str)
1535
{
×
1536
  gid_t result = 0;
×
1537
  const char * cstr = str.c_str();
×
1538
  struct group * grp = getgrnam(cstr);
×
1539

1540
  if (grp == nullptr) {
×
1541
    long long val;
×
1542

1543
    try {
×
1544
      val = stoll(str);
×
1545
    }
×
1546
    catch(std::exception& e) {
×
1547
      throw runtime_error((boost::format("Error: Unable to parse group ID %s") % cstr).str() );
×
1548
    }
×
1549

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

1554
    result = static_cast<gid_t>(val);
×
1555
  }
×
1556
  else {
×
1557
    result = grp->gr_gid;
×
1558
  }
×
1559

1560
  return result;
×
1561
}
×
1562

1563
bool isSettingThreadCPUAffinitySupported()
1564
{
×
1565
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
1566
  return true;
1567
#else
1568
  return false;
1569
#endif
1570
}
×
1571

1572
int mapThreadToCPUList([[maybe_unused]] pthread_t tid, [[maybe_unused]] const std::set<int>& cpus)
1573
{
×
1574
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
1575
#  ifdef __NetBSD__
1576
  cpuset_t *cpuset;
1577
  cpuset = cpuset_create();
1578
  for (const auto cpuID : cpus) {
1579
    cpuset_set(cpuID, cpuset);
1580
  }
1581

1582
  return pthread_setaffinity_np(tid,
1583
                                cpuset_size(cpuset),
1584
                                cpuset);
1585
#  else
1586
#    ifdef __FreeBSD__
1587
#      define cpu_set_t cpuset_t
1588
#    endif
1589
  cpu_set_t cpuset;
1590
  CPU_ZERO(&cpuset);
1591
  for (const auto cpuID : cpus) {
×
1592
    CPU_SET(cpuID, &cpuset);
×
1593
  }
1594

1595
  return pthread_setaffinity_np(tid,
1596
                                sizeof(cpuset),
1597
                                &cpuset);
1598
#  endif
1599
#else
1600
  return ENOSYS;
1601
#endif /* HAVE_PTHREAD_SETAFFINITY_NP */
1602
}
×
1603

1604
std::vector<ComboAddress> getResolvers(const std::string& resolvConfPath)
1605
{
90✔
1606
  std::vector<ComboAddress> results;
90✔
1607

1608
  ifstream ifs(resolvConfPath);
90✔
1609
  if (!ifs) {
90!
1610
    return results;
×
1611
  }
×
1612

1613
  string line;
90✔
1614
  while(std::getline(ifs, line)) {
1,166✔
1615
    boost::trim_right_if(line, boost::is_any_of(" \r\n\x1a"));
1,076✔
1616
    boost::trim_left(line); // leading spaces, let's be nice
1,076✔
1617

1618
    string::size_type tpos = line.find_first_of(";#");
1,076✔
1619
    if (tpos != string::npos) {
1,076✔
1620
      line.resize(tpos);
629✔
1621
    }
629✔
1622

1623
    if (boost::starts_with(line, "nameserver ") || boost::starts_with(line, "nameserver\t")) {
1,076✔
1624
      vector<string> parts;
91✔
1625
      stringtok(parts, line, " \t,"); // be REALLY nice
91✔
1626
      for (auto iter = parts.begin() + 1; iter != parts.end(); ++iter) {
182✔
1627
        try {
91✔
1628
          results.emplace_back(*iter, 53);
91✔
1629
        }
91✔
1630
        catch(...)
91✔
1631
        {
91✔
1632
        }
×
1633
      }
91✔
1634
    }
91✔
1635
  }
1,076✔
1636

1637
  return results;
90✔
1638
}
90✔
1639

1640
size_t getPipeBufferSize([[maybe_unused]] int fd)
1641
{
11,916✔
1642
#ifdef F_GETPIPE_SZ
11,916✔
1643
  int res = fcntl(fd, F_GETPIPE_SZ);
11,916✔
1644
  if (res == -1) {
11,916!
1645
    return 0;
×
1646
  }
×
1647
  return res;
11,916✔
1648
#else
1649
  errno = ENOSYS;
1650
  return 0;
1651
#endif /* F_GETPIPE_SZ */
1652
}
11,916✔
1653

1654
bool setPipeBufferSize([[maybe_unused]] int fd, [[maybe_unused]] size_t size)
1655
{
11,916✔
1656
#ifdef F_SETPIPE_SZ
11,916✔
1657
  if (size > static_cast<size_t>(std::numeric_limits<int>::max())) {
11,916!
1658
    errno = EINVAL;
×
1659
    return false;
×
1660
  }
×
1661
  int newSize = static_cast<int>(size);
11,916✔
1662
  int res = fcntl(fd, F_SETPIPE_SZ, newSize);
11,916✔
1663
  if (res == -1) {
11,916✔
1664
    return false;
5,497✔
1665
  }
5,497✔
1666
  return true;
6,419✔
1667
#else
1668
  errno = ENOSYS;
1669
  return false;
1670
#endif /* F_SETPIPE_SZ */
1671
}
11,916✔
1672

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

1697
  throw std::runtime_error("Calling reverseNameFromIP() for an address which is neither an IPv4 nor an IPv6");
×
1698
}
9✔
1699

1700
std::string makeLuaString(const std::string& in)
1701
{
×
1702
  ostringstream str;
×
1703

1704
  str<<'"';
×
1705

1706
  char item[5];
×
1707
  for (unsigned char n : in) {
×
1708
    if (islower(n) || isupper(n)) {
×
1709
      item[0] = n;
×
1710
      item[1] = 0;
×
1711
    }
×
1712
    else {
×
1713
      snprintf(item, sizeof(item), "\\%03d", n);
×
1714
    }
×
1715
    str << item;
×
1716
  }
×
1717

1718
  str<<'"';
×
1719

1720
  return str.str();
×
1721
}
×
1722

1723
size_t parseSVCBValueList(const std::string &in, vector<std::string> &val) {
739✔
1724
  std::string parsed;
739✔
1725
  auto ret = parseRFC1035CharString(in, parsed);
739✔
1726
  parseSVCBValueListFromParsedRFC1035CharString(parsed, val);
739✔
1727
  return ret;
739✔
1728
};
739✔
1729

1730
#ifdef HAVE_CRYPTO_MEMCMP
1731
#include <openssl/crypto.h>
1732
#else /* HAVE_CRYPTO_MEMCMP */
1733
#ifdef HAVE_SODIUM_MEMCMP
1734
#include <sodium.h>
1735
#endif /* HAVE_SODIUM_MEMCMP */
1736
#endif /* HAVE_CRYPTO_MEMCMP */
1737

1738
bool constantTimeStringEquals(const std::string& a, const std::string& b)
1739
{
2,371✔
1740
  if (a.size() != b.size()) {
2,371✔
1741
    return false;
5✔
1742
  }
5✔
1743
  const size_t size = a.size();
2,366✔
1744
#ifdef HAVE_CRYPTO_MEMCMP
2,366✔
1745
  return CRYPTO_memcmp(a.c_str(), b.c_str(), size) == 0;
2,366✔
1746
#else /* HAVE_CRYPTO_MEMCMP */
1747
#ifdef HAVE_SODIUM_MEMCMP
1748
  return sodium_memcmp(a.c_str(), b.c_str(), size) == 0;
1749
#else /* HAVE_SODIUM_MEMCMP */
1750
  const volatile unsigned char *_a = (const volatile unsigned char *) a.c_str();
1751
  const volatile unsigned char *_b = (const volatile unsigned char *) b.c_str();
1752
  unsigned char res = 0;
1753

1754
  for (size_t idx = 0; idx < size; idx++) {
1755
    res |= _a[idx] ^ _b[idx];
1756
  }
1757

1758
  return res == 0;
1759
#endif /* !HAVE_SODIUM_MEMCMP */
1760
#endif /* !HAVE_CRYPTO_MEMCMP */
1761
}
2,371✔
1762

1763
namespace pdns
1764
{
1765
struct CloseDirDeleter
1766
{
1767
  void operator()(DIR* dir) const noexcept {
554✔
1768
    closedir(dir);
554✔
1769
  }
554✔
1770
};
1771

1772
std::optional<std::string> visit_directory(const std::string& directory, const std::function<bool(ino_t inodeNumber, const std::string_view& name)>& visitor)
1773
{
667✔
1774
  auto dirHandle = std::unique_ptr<DIR, CloseDirDeleter>(opendir(directory.c_str()));
667✔
1775
  if (!dirHandle) {
667✔
1776
    auto err = errno;
114✔
1777
    return std::string("Error opening directory '" + directory + "': " + stringerror(err));
114✔
1778
  }
114✔
1779

1780
  bool keepGoing = true;
553✔
1781
  struct dirent* ent = nullptr;
553✔
1782
  // NOLINTNEXTLINE(concurrency-mt-unsafe): readdir is thread-safe nowadays and readdir_r is deprecated
1783
  while (keepGoing && (ent = readdir(dirHandle.get())) != nullptr) {
31,516✔
1784
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay: dirent API
1785
    auto name = std::string_view(ent->d_name, strlen(ent->d_name));
30,963✔
1786
    keepGoing = visitor(ent->d_ino, name);
30,963✔
1787
  }
30,963✔
1788

1789
  return std::nullopt;
553✔
1790
}
667✔
1791

1792
UniqueFilePtr openFileForWriting(const std::string& filePath, mode_t permissions, bool mustNotExist, bool appendIfExists)
1793
{
4✔
1794
  int flags = O_WRONLY | O_CREAT;
4✔
1795
  if (mustNotExist) {
4!
1796
    flags |= O_EXCL;
×
1797
  }
×
1798
  else if (appendIfExists) {
4!
1799
    flags |= O_APPEND;
4✔
1800
  }
4✔
1801
  int fileDesc = open(filePath.c_str(), flags, permissions);
4✔
1802
  if (fileDesc == -1) {
4!
1803
    return {};
×
1804
  }
×
1805
  auto filePtr = pdns::UniqueFilePtr(fdopen(fileDesc, appendIfExists ? "a" : "w"));
4!
1806
  if (!filePtr) {
4!
1807
    auto error = errno;
×
1808
    close(fileDesc);
×
1809
    errno = error;
×
1810
    return {};
×
1811
  }
×
1812
  return filePtr;
4✔
1813
}
4✔
1814

1815
}
1816

1817
const char* timestamp(time_t arg, timebuf_t& buf)
1818
{
87✔
1819
  const std::string s_timestampFormat = "%Y-%m-%dT%T";
87✔
1820
  struct tm tmval{};
87✔
1821
  size_t len = strftime(buf.data(), buf.size(), s_timestampFormat.c_str(), localtime_r(&arg, &tmval));
87✔
1822
  if (len == 0) {
87!
1823
    int ret = snprintf(buf.data(), buf.size(), "%lld", static_cast<long long>(arg));
×
1824
    if (ret < 0 || static_cast<size_t>(ret) >= buf.size()) {
×
1825
      buf[0] = '\0';
×
1826
    }
×
1827
  }
×
1828
  return buf.data();
87✔
1829
}
87✔
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