Coverage Report

Created: 2026-09-13 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pdns/pdns/misc.cc
Line
Count
Source
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
#include "config.h"
24
25
#include <sys/param.h>
26
#include <sys/socket.h>
27
#include <sys/stat.h>
28
#include <fcntl.h>
29
#include <netdb.h>
30
#include <sys/time.h>
31
#include <ctime>
32
#include <sys/resource.h>
33
#include <netinet/in.h>
34
#include <sys/un.h>
35
#include <unistd.h>
36
#include <fstream>
37
#include "misc.hh"
38
#include <vector>
39
#include <string>
40
#include <sstream>
41
#include <cerrno>
42
#include <cstring>
43
#include <iostream>
44
#include <sys/types.h>
45
#include <dirent.h>
46
#include <algorithm>
47
#include <poll.h>
48
#include <iomanip>
49
#include <netinet/tcp.h>
50
#include <optional>
51
#include <cstdlib>
52
#include <cstdio>
53
#include "pdnsexception.hh"
54
#include <boost/algorithm/string.hpp>
55
#include <boost/format.hpp>
56
#include "iputils.hh"
57
#include "dns_random.hh"
58
#include "logger.hh"
59
#include "logging.hh"
60
#include <pwd.h>
61
#include <grp.h>
62
#include <unordered_map>
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
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-reinterpret-cast)
76
77
size_t writen2(int fileDesc, const void* buf, size_t count)
78
0
{
79
0
  const char* ptr = static_cast<const char*>(buf);
80
0
  const char* eptr = ptr + count;
81
82
0
  while (ptr != eptr) {
83
0
    auto res = ::write(fileDesc, ptr, eptr - ptr);
84
0
    if (res < 0) {
85
0
      if (errno == EAGAIN) {
86
0
        throw std::runtime_error("used writen2 on non-blocking socket, got EAGAIN");
87
0
      }
88
0
      unixDie("failed in writen2");
89
0
    }
90
0
    else if (res == 0) {
91
0
      throw std::runtime_error("could not write all bytes, got eof in writen2");
92
0
    }
93
94
0
    ptr += res;
95
0
  }
96
97
0
  return count;
98
0
}
99
100
size_t readn2(int fileDesc, void* buffer, size_t len)
101
0
{
102
0
  size_t pos = 0;
103
104
0
  for (;;) {
105
0
    auto res = read(fileDesc, static_cast<char*>(buffer) + pos, len - pos); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic): it's the API
106
0
    if (res == 0) {
107
0
      throw runtime_error("EOF while reading message");
108
0
    }
109
0
    if (res < 0) {
110
0
      if (errno == EAGAIN) {
111
0
        throw std::runtime_error("used readn2 on non-blocking socket, got EAGAIN");
112
0
      }
113
0
      unixDie("failed in readn2");
114
0
    }
115
116
0
    pos += static_cast<size_t>(res);
117
0
    if (pos == len) {
118
0
      break;
119
0
    }
120
0
  }
121
0
  return len;
122
0
}
123
124
size_t readn2WithTimeout(int fileDesc, void* buffer, size_t len, const struct timeval& idleTimeout, const struct timeval& totalTimeout, bool allowIncomplete)
125
0
{
126
0
  size_t pos = 0;
127
0
  struct timeval start{0, 0};
128
0
  struct timeval remainingTime = totalTimeout;
129
0
  if (totalTimeout.tv_sec != 0 || totalTimeout.tv_usec != 0) {
130
0
    gettimeofday(&start, nullptr);
131
0
  }
132
133
0
  do {
134
0
    ssize_t got = read(fileDesc, static_cast<char*>(buffer) + pos, len - pos);
135
0
    if (got > 0) {
136
0
      pos += (size_t)got;
137
0
      if (allowIncomplete) {
138
0
        break;
139
0
      }
140
0
    }
141
0
    else if (got == 0) {
142
0
      throw runtime_error("EOF while reading message");
143
0
    }
144
0
    else {
145
0
      if (errno == EAGAIN) {
146
0
        struct timeval waitTime = ((totalTimeout.tv_sec == 0 && totalTimeout.tv_usec == 0) || idleTimeout <= remainingTime) ? idleTimeout : remainingTime;
147
0
        int res = waitForData(fileDesc, waitTime);
148
0
        if (res > 0) {
149
          /* there is data available */
150
0
        }
151
0
        else if (res == 0) {
152
0
          throw runtime_error("Timeout while waiting for data to read");
153
0
        }
154
0
        else {
155
0
          throw runtime_error("Error while waiting for data to read");
156
0
        }
157
0
      }
158
0
      else {
159
0
        unixDie("failed in readn2WithTimeout");
160
0
      }
161
0
    }
162
163
0
    if (totalTimeout.tv_sec != 0 || totalTimeout.tv_usec != 0) {
164
0
      struct timeval now{};
165
0
      gettimeofday(&now, nullptr);
166
0
      struct timeval elapsed = now - start;
167
0
      if (remainingTime < elapsed) {
168
0
        throw runtime_error("Timeout while reading data");
169
0
      }
170
0
      start = now;
171
0
      remainingTime = remainingTime - elapsed;
172
0
    }
173
0
  } while (pos < len);
174
175
0
  return len;
176
0
}
177
178
size_t writen2WithTimeout(int fileDesc, const void* buffer, size_t len, const struct timeval& timeout)
179
0
{
180
0
  size_t pos = 0;
181
0
  do {
182
0
    ssize_t written = write(fileDesc, reinterpret_cast<const char*>(buffer) + pos, len - pos);
183
184
0
    if (written > 0) {
185
0
      pos += (size_t)written;
186
0
    }
187
0
    else if (written == 0) {
188
0
      throw runtime_error("EOF while writing message");
189
0
    }
190
0
    else {
191
0
      if (errno == EAGAIN) {
192
0
        int res = waitForRWData(fileDesc, false, timeout);
193
0
        if (res > 0) {
194
          /* there is room available */
195
0
        }
196
0
        else if (res == 0) {
197
0
          throw runtime_error("Timeout while waiting to write data");
198
0
        }
199
0
        else {
200
0
          throw runtime_error("Error while waiting for room to write data");
201
0
        }
202
0
      }
203
0
      else {
204
0
        unixDie("failed in write2WithTimeout");
205
0
      }
206
0
    }
207
0
  } while (pos < len);
208
209
0
  return len;
210
0
}
211
212
auto pdns::getMessageFromErrno(const int errnum) -> std::string
213
0
{
214
0
  const size_t errLen = 2048;
215
0
  std::string errMsgData{};
216
0
  errMsgData.resize(errLen);
217
218
0
  const char* errMsg = nullptr;
219
0
#ifdef STRERROR_R_CHAR_P
220
0
  errMsg = strerror_r(errnum, errMsgData.data(), errMsgData.length());
221
#else
222
  // This can fail, and when it does, it sets errno. We ignore that and
223
  // set our own error message instead.
224
  int res = strerror_r(errnum, errMsgData.data(), errMsgData.length());
225
  errMsg = errMsgData.c_str();
226
  if (res != 0) {
227
    errMsg = "Unknown (the exact error could not be retrieved)";
228
  }
229
#endif
230
231
  // We make a copy here because `strerror_r()` might return a static
232
  // immutable buffer for an error message. The copy shouldn't be
233
  // critical though, we're on the bailout/error-handling path anyways.
234
0
  std::string message{errMsg};
235
0
  return message;
236
0
}
237
238
#if defined(HAVE_LIBCRYPTO)
239
auto pdns::OpenSSL::error(const std::string& errorMessage) -> std::runtime_error
240
0
{
241
0
  unsigned long errorCode = 0;
242
0
  auto fullErrorMessage{errorMessage};
243
#if OPENSSL_VERSION_MAJOR >= 3
244
  const char* filename = nullptr;
245
  const char* functionName = nullptr;
246
  int lineNumber = 0;
247
  while ((errorCode = ERR_get_error_all(&filename, &lineNumber, &functionName, nullptr, nullptr)) != 0) {
248
    fullErrorMessage += std::string(": ") + std::to_string(errorCode);
249
250
    const auto* lib = ERR_lib_error_string(errorCode);
251
    if (lib != nullptr) {
252
      fullErrorMessage += std::string(":") + lib;
253
    }
254
255
    const auto* reason = ERR_reason_error_string(errorCode);
256
    if (reason != nullptr) {
257
      fullErrorMessage += std::string("::") + reason;
258
    }
259
260
    if (filename != nullptr) {
261
      fullErrorMessage += std::string(" - ") + filename;
262
    }
263
    if (lineNumber != 0) {
264
      fullErrorMessage += std::string(":") + std::to_string(lineNumber);
265
    }
266
    if (functionName != nullptr) {
267
      fullErrorMessage += std::string(" - ") + functionName;
268
    }
269
  }
270
#else
271
0
  while ((errorCode = ERR_get_error()) != 0) {
272
0
    fullErrorMessage += std::string(": ") + std::to_string(errorCode);
273
274
0
    const auto* lib = ERR_lib_error_string(errorCode);
275
0
    if (lib != nullptr) {
276
0
      fullErrorMessage += std::string(":") + lib;
277
0
    }
278
279
0
    const auto* func = ERR_func_error_string(errorCode);
280
0
    if (func != nullptr) {
281
0
      fullErrorMessage += std::string(":") + func;
282
0
    }
283
284
0
    const auto* reason = ERR_reason_error_string(errorCode);
285
0
    if (reason != nullptr) {
286
0
      fullErrorMessage += std::string("::") + reason;
287
0
    }
288
0
  }
289
0
#endif
290
0
  return std::runtime_error{fullErrorMessage};
291
0
}
292
293
auto pdns::OpenSSL::error(const std::string& componentName, const std::string& errorMessage) -> std::runtime_error
294
0
{
295
0
  return pdns::OpenSSL::error(componentName + ": " + errorMessage);
296
0
}
297
#endif // HAVE_LIBCRYPTO
298
299
string nowTime()
300
0
{
301
0
  time_t now = time(nullptr);
302
0
  struct tm theTime{};
303
0
  localtime_r(&now, &theTime);
304
0
  std::array<char, 30> buffer{};
305
  // YYYY-mm-dd HH:MM:SS TZOFF
306
0
  size_t ret = strftime(buffer.data(), buffer.size(), "%F %T %z", &theTime);
307
0
  if (ret == 0) {
308
0
    buffer[0] = '\0';
309
0
  }
310
0
  return {buffer.data()};
311
0
}
312
313
// returns -1 in case if error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
314
int waitForData(int fileDesc, int seconds, int mseconds)
315
0
{
316
0
  return waitForRWData(fileDesc, true, seconds, mseconds);
317
0
}
318
319
int waitForData(int fileDesc, struct timeval timeout)
320
0
{
321
0
  return waitForRWData(fileDesc, true, timeout);
322
0
}
323
324
int waitForRWData(int fileDesc, bool waitForRead, int seconds, int mseconds, bool* error, bool* disconnected)
325
0
{
326
0
  struct pollfd pfd{};
327
0
  memset(&pfd, 0, sizeof(pfd));
328
0
  pfd.fd = fileDesc;
329
330
0
  if (waitForRead) {
331
0
    pfd.events = POLLIN;
332
0
  }
333
0
  else {
334
0
    pfd.events = POLLOUT;
335
0
  }
336
337
0
  int ret = poll(&pfd, 1, (seconds * 1000) + mseconds);
338
0
  if (ret > 0) {
339
0
    if ((error != nullptr) && (pfd.revents & POLLERR) != 0) {
340
0
      *error = true;
341
0
    }
342
0
    if ((disconnected != nullptr) && (pfd.revents & POLLHUP) != 0) {
343
0
      *disconnected = true;
344
0
    }
345
0
  }
346
347
0
  return ret;
348
0
}
349
350
int waitForRWData(int fileDesc, bool waitForRead, struct timeval timeout, bool* error, bool* disconnected)
351
0
{
352
0
  return waitForRWData(fileDesc, waitForRead, static_cast<int>(timeout.tv_sec), static_cast<int>(timeout.tv_usec / 1000), error, disconnected);
353
0
}
354
355
// returns -1 in case of error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
356
int waitForMultiData(const set<int>& fds, const int seconds, const int mseconds, int* fdOut)
357
0
{
358
0
  set<int> realFDs;
359
0
  for (const auto& anFd : fds) {
360
0
    if (anFd >= 0 && realFDs.count(anFd) == 0) {
361
0
      realFDs.insert(anFd);
362
0
    }
363
0
  }
364
365
0
  std::vector<struct pollfd> pfds(realFDs.size());
366
0
  memset(pfds.data(), 0, realFDs.size() * sizeof(struct pollfd));
367
0
  int ctr = 0;
368
0
  for (const auto& anFd : realFDs) {
369
0
    pfds[ctr].fd = anFd;
370
0
    pfds[ctr].events = POLLIN;
371
0
    ctr++;
372
0
  }
373
374
0
  int ret{};
375
0
  if (seconds >= 0) {
376
0
    ret = poll(pfds.data(), realFDs.size(), (seconds * 1000) + mseconds);
377
0
  }
378
0
  else {
379
0
    ret = poll(pfds.data(), realFDs.size(), -1);
380
0
  }
381
0
  if (ret <= 0) {
382
0
    return ret;
383
0
  }
384
385
0
  set<int> pollinFDs;
386
0
  for (const auto& pfd : pfds) {
387
0
    if ((pfd.revents & POLLIN) != 0) {
388
0
      pollinFDs.insert(pfd.fd);
389
0
    }
390
0
  }
391
0
  auto iter(pollinFDs.begin());
392
0
  advance(iter, dns_random(pollinFDs.size()));
393
0
  *fdOut = *iter;
394
0
  return 1;
395
0
}
396
397
string humanDuration(time_t passed)
398
0
{
399
0
  ostringstream ret;
400
0
  if (passed < 60) {
401
0
    ret << passed << " seconds";
402
0
  }
403
0
  else if (passed < 3600) {
404
0
    ret << std::setprecision(2) << static_cast<double>(passed) / 60.0 << " minutes";
405
0
  }
406
0
  else if (passed < 86400) {
407
0
    ret << std::setprecision(3) << static_cast<double>(passed) / 3600.0 << " hours";
408
0
  }
409
0
  else if (static_cast<double>(passed) < (86400 * 30.41)) {
410
0
    ret << std::setprecision(3) << static_cast<double>(passed) / 86400.0 << " days";
411
0
  }
412
0
  else {
413
0
    ret << std::setprecision(3) << static_cast<double>(passed) / (86400 * 30.41) << " months";
414
0
  }
415
416
0
  return ret.str();
417
0
}
418
419
string unquotify(const string& item)
420
0
{
421
0
  if (item.size() < 2) {
422
0
    return item;
423
0
  }
424
425
0
  string::size_type bpos = 0;
426
0
  string::size_type epos = item.size();
427
428
0
  if (item[0] == '"') {
429
0
    bpos = 1;
430
0
  }
431
432
0
  if (item[epos - 1] == '"') {
433
0
    epos -= 1;
434
0
  }
435
436
0
  return item.substr(bpos, epos - bpos);
437
0
}
438
439
void stripLine(string& line)
440
0
{
441
0
  string::size_type pos = line.find_first_of("\r\n");
442
0
  if (pos != string::npos) {
443
0
    line.resize(pos);
444
0
  }
445
0
}
446
447
string urlEncode(const string& text)
448
0
{
449
0
  string ret;
450
0
  for (char index : text) {
451
0
    if (index == ' ') {
452
0
      ret.append("%20");
453
0
    }
454
0
    else {
455
0
      ret.append(1, index);
456
0
    }
457
0
  }
458
0
  return ret;
459
0
}
460
461
static size_t getMaxHostNameSize()
462
0
{
463
0
#if defined(HOST_NAME_MAX)
464
0
  return HOST_NAME_MAX;
465
0
#endif
466
467
0
#if defined(_SC_HOST_NAME_MAX)
468
0
  auto tmp = sysconf(_SC_HOST_NAME_MAX);
469
0
  if (tmp != -1) {
470
0
    return tmp;
471
0
  }
472
0
#endif
473
474
0
  const size_t maxHostNameSize = 255;
475
0
  return maxHostNameSize;
476
0
}
477
478
std::optional<string> getHostname()
479
0
{
480
0
  const size_t maxHostNameBufSize = getMaxHostNameSize() + 1;
481
0
  std::string hostname;
482
0
  hostname.resize(maxHostNameBufSize, 0);
483
484
0
  if (gethostname(hostname.data(), maxHostNameBufSize) == -1) {
485
0
    return std::nullopt;
486
0
  }
487
488
0
  hostname.resize(strlen(hostname.c_str()));
489
0
  return std::make_optional(hostname);
490
0
}
491
492
std::string getCarbonHostName()
493
0
{
494
0
  auto hostname = getHostname();
495
0
  if (!hostname.has_value()) {
496
0
    throw std::runtime_error(stringerror());
497
0
  }
498
499
0
  std::replace(hostname->begin(), hostname->end(), '.', '_');
500
0
  return *hostname;
501
0
}
502
503
void cleanSlashes(string& str)
504
0
{
505
0
  string out;
506
0
  bool keepNextSlash = true;
507
0
  for (const auto& value : str) {
508
0
    if (value == '/') {
509
0
      if (keepNextSlash) {
510
0
        keepNextSlash = false;
511
0
      }
512
0
      else {
513
0
        continue;
514
0
      }
515
0
    }
516
0
    else {
517
0
      keepNextSlash = true;
518
0
    }
519
0
    out.append(1, value);
520
0
  }
521
0
  str = std::move(out);
522
0
}
523
524
bool IpToU32(const string& str, uint32_t* ptr)
525
0
{
526
0
  if (str.empty()) {
527
0
    *ptr = 0;
528
0
    return true;
529
0
  }
530
531
0
  struct in_addr inp{};
532
0
  if (inet_aton(str.c_str(), &inp) != 0) {
533
0
    *ptr = inp.s_addr;
534
0
    return true;
535
0
  }
536
0
  return false;
537
0
}
538
539
string U32ToIP(uint32_t val)
540
0
{
541
0
  std::array<char, 17> tmp{};
542
0
  snprintf(tmp.data(), tmp.size(), "%u.%u.%u.%u",
543
0
           (val >> 24) & 0xff,
544
0
           (val >> 16) & 0xff,
545
0
           (val >> 8) & 0xff,
546
0
           (val) & 0xff);
547
0
  return tmp.data();
548
0
}
549
550
string makeHexDump(const string& str, const string& sep)
551
2.29k
{
552
2.29k
  std::array<char, 5> tmp{};
553
2.29k
  string ret;
554
2.29k
  ret.reserve(static_cast<size_t>(str.size() * (2 + sep.size())));
555
556
11.4M
  for (char index : str) {
557
11.4M
    snprintf(tmp.data(), tmp.size(), "%02x", static_cast<unsigned char>(index));
558
11.4M
    ret += tmp.data();
559
11.4M
    ret += sep;
560
11.4M
  }
561
2.29k
  return ret;
562
2.29k
}
563
564
string makeBytesFromHex(const string& str)
565
0
{
566
0
  if (str.size() % 2 != 0) {
567
0
    throw std::range_error("odd number of bytes in hex string");
568
0
  }
569
0
  string ret;
570
0
  ret.reserve(str.size() / 2);
571
572
0
  for (size_t i = 0; i < str.size(); i += 2) {
573
0
    const auto numStr = str.substr(i, 2);
574
0
    unsigned int num = 0;
575
0
    if (sscanf(numStr.c_str(), "%02x", &num) != 1) {
576
0
      throw std::range_error("Invalid value while parsing the hex string '" + str + "'");
577
0
    }
578
0
    ret.push_back(static_cast<char>(num));
579
0
  }
580
581
0
  return ret;
582
0
}
583
584
void normalizeTV(struct timeval& timeval)
585
0
{
586
0
  if (timeval.tv_usec > 1000000) {
587
0
    ++timeval.tv_sec;
588
0
    timeval.tv_usec -= 1000000;
589
0
  }
590
0
  else if (timeval.tv_usec < 0) {
591
0
    --timeval.tv_sec;
592
0
    timeval.tv_usec += 1000000;
593
0
  }
594
0
}
595
596
struct timeval operator+(const struct timeval& lhs, const struct timeval& rhs)
597
0
{
598
0
  struct timeval ret{};
599
0
  ret.tv_sec = lhs.tv_sec + rhs.tv_sec;
600
0
  ret.tv_usec = lhs.tv_usec + rhs.tv_usec;
601
0
  normalizeTV(ret);
602
0
  return ret;
603
0
}
604
605
struct timeval operator-(const struct timeval& lhs, const struct timeval& rhs)
606
0
{
607
0
  struct timeval ret{};
608
0
  ret.tv_sec = lhs.tv_sec - rhs.tv_sec;
609
0
  ret.tv_usec = lhs.tv_usec - rhs.tv_usec;
610
0
  normalizeTV(ret);
611
0
  return ret;
612
0
}
613
614
pair<string, string> splitField(const string& inp, char sepa)
615
24.4k
{
616
24.4k
  pair<string, string> ret;
617
24.4k
  string::size_type cpos = inp.find(sepa);
618
24.4k
  if (cpos == string::npos) {
619
0
    ret.first = inp;
620
0
  }
621
24.4k
  else {
622
24.4k
    ret.first = inp.substr(0, cpos);
623
24.4k
    ret.second = inp.substr(cpos + 1);
624
24.4k
  }
625
24.4k
  return ret;
626
24.4k
}
627
628
int logFacilityToLOG(unsigned int facility)
629
0
{
630
0
  switch (facility) {
631
0
  case 0:
632
0
    return LOG_LOCAL0;
633
0
  case 1:
634
0
    return (LOG_LOCAL1);
635
0
  case 2:
636
0
    return (LOG_LOCAL2);
637
0
  case 3:
638
0
    return (LOG_LOCAL3);
639
0
  case 4:
640
0
    return (LOG_LOCAL4);
641
0
  case 5:
642
0
    return (LOG_LOCAL5);
643
0
  case 6:
644
0
    return (LOG_LOCAL6);
645
0
  case 7:
646
0
    return (LOG_LOCAL7);
647
0
  default:
648
0
    return -1;
649
0
  }
650
0
}
651
652
std::optional<int> logFacilityFromString(std::string facilityStr)
653
0
{
654
0
  static std::unordered_map<std::string, int> const s_facilities = {
655
0
    {"local0", LOG_LOCAL0},
656
0
    {"log_local0", LOG_LOCAL0},
657
0
    {"local1", LOG_LOCAL1},
658
0
    {"log_local1", LOG_LOCAL1},
659
0
    {"local2", LOG_LOCAL2},
660
0
    {"log_local2", LOG_LOCAL2},
661
0
    {"local3", LOG_LOCAL3},
662
0
    {"log_local3", LOG_LOCAL3},
663
0
    {"local4", LOG_LOCAL4},
664
0
    {"log_local4", LOG_LOCAL4},
665
0
    {"local5", LOG_LOCAL5},
666
0
    {"log_local5", LOG_LOCAL5},
667
0
    {"local6", LOG_LOCAL6},
668
0
    {"log_local6", LOG_LOCAL6},
669
0
    {"local7", LOG_LOCAL7},
670
0
    {"log_local7", LOG_LOCAL7},
671
    /* most of these likely make very little sense
672
       for us, but why not? */
673
0
    {"kern", LOG_KERN},
674
0
    {"log_kern", LOG_KERN},
675
0
    {"user", LOG_USER},
676
0
    {"log_user", LOG_USER},
677
0
    {"mail", LOG_MAIL},
678
0
    {"log_mail", LOG_MAIL},
679
0
    {"daemon", LOG_DAEMON},
680
0
    {"log_daemon", LOG_DAEMON},
681
0
    {"auth", LOG_AUTH},
682
0
    {"log_auth", LOG_AUTH},
683
0
    {"syslog", LOG_SYSLOG},
684
0
    {"log_syslog", LOG_SYSLOG},
685
0
    {"lpr", LOG_LPR},
686
0
    {"log_lpr", LOG_LPR},
687
0
    {"news", LOG_NEWS},
688
0
    {"log_news", LOG_NEWS},
689
0
    {"uucp", LOG_UUCP},
690
0
    {"log_uucp", LOG_UUCP},
691
0
    {"cron", LOG_CRON},
692
0
    {"log_cron", LOG_CRON},
693
0
    {"authpriv", LOG_AUTHPRIV},
694
0
    {"log_authpriv", LOG_AUTHPRIV},
695
0
    {"ftp", LOG_FTP},
696
0
    {"log_ftp", LOG_FTP}};
697
698
0
  toLowerInPlace(facilityStr);
699
0
  auto facilityIt = s_facilities.find(facilityStr);
700
0
  if (facilityIt == s_facilities.end()) {
701
0
    return std::nullopt;
702
0
  }
703
704
0
  return facilityIt->second;
705
0
}
706
707
string stripDot(const string& dom)
708
0
{
709
0
  if (dom.empty()) {
710
0
    return dom;
711
0
  }
712
713
0
  if (dom[dom.size() - 1] != '.') {
714
0
    return dom;
715
0
  }
716
717
0
  return dom.substr(0, dom.size() - 1);
718
0
}
719
720
int makeIPv6sockaddr(const std::string& addr, struct sockaddr_in6* ret)
721
127k
{
722
127k
  if (addr.empty()) {
723
28
    return -1;
724
28
  }
725
726
127k
  string ourAddr(addr);
727
127k
  std::optional<uint16_t> port = std::nullopt;
728
729
127k
  if (addr[0] == '[') { // [::]:53 style address
730
1.68k
    string::size_type pos = addr.find(']');
731
1.68k
    if (pos == string::npos) {
732
5
      return -1;
733
5
    }
734
735
1.67k
    ourAddr.assign(addr.c_str() + 1, pos - 1);
736
1.67k
    if (pos + 1 != addr.size()) { // complete after ], no port specified
737
1.47k
      if (pos + 2 > addr.size() || addr[pos + 1] != ':') {
738
10
        return -1;
739
10
      }
740
741
1.46k
      try {
742
1.46k
        auto tmpPort = pdns::checked_stoi<uint16_t>(addr.substr(pos + 2));
743
1.46k
        port = std::make_optional(tmpPort);
744
1.46k
      }
745
1.46k
      catch (const std::logic_error&) {
746
25
        return -1;
747
25
      }
748
1.46k
    }
749
1.67k
  }
750
751
127k
  ret->sin6_scope_id = 0;
752
127k
  ret->sin6_family = AF_INET6;
753
754
127k
  if (inet_pton(AF_INET6, ourAddr.c_str(), (void*)&ret->sin6_addr) != 1) {
755
4.66k
    struct addrinfo hints{};
756
4.66k
    std::memset(&hints, 0, sizeof(struct addrinfo));
757
4.66k
    hints.ai_flags = AI_NUMERICHOST;
758
4.66k
    hints.ai_family = AF_INET6;
759
760
4.66k
    struct addrinfo* res = nullptr;
761
    // getaddrinfo has anomalous return codes, anything nonzero is an error, positive or negative
762
4.66k
    if (getaddrinfo(ourAddr.c_str(), nullptr, &hints, &res) != 0) {
763
333
      return -1;
764
333
    }
765
766
4.32k
    memcpy(ret, res->ai_addr, res->ai_addrlen);
767
4.32k
    freeaddrinfo(res);
768
4.32k
  }
769
770
126k
  if (port.has_value()) {
771
1.38k
    ret->sin6_port = htons(*port);
772
1.38k
  }
773
774
126k
  return 0;
775
127k
}
776
777
int makeIPv4sockaddr(const std::string& arg, struct sockaddr_in* ret)
778
311k
{
779
311k
  std::string str(arg);
780
311k
  if (str.empty()) {
781
28
    return -1;
782
28
  }
783
311k
  struct in_addr inp{};
784
785
311k
  string::size_type pos = str.find(':');
786
311k
  if (pos == string::npos) { // no port specified, not touching the port
787
176k
    if (inet_aton(str.c_str(), &inp) != 0) {
788
175k
      ret->sin_addr.s_addr = inp.s_addr;
789
175k
      return 0;
790
175k
    }
791
56
    return -1;
792
176k
  }
793
134k
  if (*(str.c_str() + pos + 1) == 0) { // trailing :
794
24
    return -1;
795
24
  }
796
797
134k
  auto* eptr = str.data() + str.size();
798
134k
  int port = static_cast<int>(strtol(str.c_str() + pos + 1, &eptr, 10));
799
134k
  if (port < 0 || port > 65535) {
800
60
    return -1;
801
60
  }
802
134k
  if (*eptr != 0) {
803
127k
    return -1;
804
127k
  }
805
806
7.83k
  ret->sin_port = htons(port);
807
7.83k
  if (inet_aton(str.substr(0, pos).c_str(), &inp) != 0) {
808
7.70k
    ret->sin_addr.s_addr = inp.s_addr;
809
7.70k
    return 0;
810
7.70k
  }
811
129
  return -1;
812
7.83k
}
813
814
int makeUNsockaddr(const std::string& path, struct sockaddr_un* ret)
815
0
{
816
0
  if (path.empty()) {
817
0
    return -1;
818
0
  }
819
0
  memset(ret, 0, sizeof(struct sockaddr_un));
820
0
  ret->sun_family = AF_UNIX;
821
0
  if (path.length() >= sizeof(ret->sun_path)) {
822
0
    return -1;
823
0
  }
824
0
  path.copy(&ret->sun_path[0], sizeof(ret->sun_path), 0);
825
0
  return 0;
826
0
}
827
828
//! read a line of text from a FILE* to a std::string, returns false on 'no data'
829
bool stringfgets(FILE* file, std::string& line)
830
0
{
831
0
  std::array<char, 1024> buffer{};
832
0
  line.clear();
833
834
0
  do {
835
0
    if (fgets(buffer.data(), buffer.size(), file) == nullptr) {
836
0
      return !line.empty();
837
0
    }
838
0
    line.append(buffer.data());
839
0
  } while (strchr(buffer.data(), '\n') == nullptr);
840
0
  return true;
841
0
}
842
843
bool readFileIfThere(const char* fname, std::string* line)
844
0
{
845
0
  line->clear();
846
0
  auto filePtr = pdns::UniqueFilePtr(fopen(fname, "r"));
847
0
  if (!filePtr) {
848
0
    return false;
849
0
  }
850
0
  return stringfgets(filePtr.get(), *line);
851
0
}
852
853
Regex::Regex(const string& expr) :
854
0
  d_preg(new regex_t)
855
0
{
856
0
  if (auto ret = regcomp(d_preg.get(), expr.c_str(), REG_ICASE | REG_NOSUB | REG_EXTENDED); ret != 0) {
857
0
    std::array<char, 1024> errorBuffer{};
858
0
    if (regerror(ret, d_preg.get(), errorBuffer.data(), errorBuffer.size()) > 0) {
859
0
      throw PDNSException("Regular expression " + expr + " did not compile: " + errorBuffer.data());
860
0
    }
861
0
    throw PDNSException("Regular expression " + expr + " did not compile");
862
0
  }
863
0
}
864
865
/** call this to find out if 'line' matches your expression */
866
bool Regex::match(const string& line) const
867
0
{
868
0
  return regexec(d_preg.get(), line.c_str(), 0, nullptr, 0) == 0;
869
0
}
870
871
bool Regex::match(const DNSName& name) const
872
0
{
873
0
  return match(name.toStringNoDot());
874
0
}
875
876
// if you end up here because valgrind told you were are doing something wrong
877
// with msgh->msg_controllen, please refer to https://github.com/PowerDNS/pdns/pull/3962
878
// first.
879
// Note that cmsgbuf should be aligned the same as a struct cmsghdr
880
void addCMsgSrcAddr(struct msghdr* msgh, cmsgbuf_aligned* cmsgbuf, const ComboAddress* source, int itfIndex)
881
0
{
882
0
  struct cmsghdr* cmsg = nullptr;
883
884
0
  if (source->sin4.sin_family == AF_INET6) {
885
0
    struct in6_pktinfo* pkt{};
886
887
0
    msgh->msg_control = cmsgbuf;
888
0
#if !defined(__APPLE__)
889
    /* CMSG_SPACE is not a constexpr on macOS */
890
0
    static_assert(CMSG_SPACE(sizeof(*pkt)) <= sizeof(*cmsgbuf), "Buffer is too small for in6_pktinfo");
891
#else /* __APPLE__ */
892
    if (CMSG_SPACE(sizeof(*pkt)) > sizeof(*cmsgbuf)) {
893
      throw std::runtime_error("Buffer is too small for in6_pktinfo");
894
    }
895
#endif /* __APPLE__ */
896
0
    msgh->msg_controllen = CMSG_SPACE(sizeof(*pkt));
897
898
0
    cmsg = CMSG_FIRSTHDR(msgh);
899
0
    cmsg->cmsg_level = IPPROTO_IPV6;
900
0
    cmsg->cmsg_type = IPV6_PKTINFO;
901
0
    cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
902
903
0
    pkt = reinterpret_cast<struct in6_pktinfo*>(CMSG_DATA(cmsg));
904
    // Include the padding to stop valgrind complaining about passing uninitialized data
905
0
    memset(pkt, 0, CMSG_SPACE(sizeof(*pkt)));
906
0
    pkt->ipi6_addr = source->sin6.sin6_addr;
907
0
    pkt->ipi6_ifindex = itfIndex;
908
0
  }
909
0
  else {
910
0
#if defined(IP_PKTINFO)
911
0
    struct in_pktinfo* pkt{};
912
913
0
    msgh->msg_control = cmsgbuf;
914
0
#if !defined(__APPLE__)
915
    /* CMSG_SPACE is not a constexpr on macOS */
916
0
    static_assert(CMSG_SPACE(sizeof(*pkt)) <= sizeof(*cmsgbuf), "Buffer is too small for in_pktinfo");
917
#else /* __APPLE__ */
918
    if (CMSG_SPACE(sizeof(*pkt)) > sizeof(*cmsgbuf)) {
919
      throw std::runtime_error("Buffer is too small for in_pktinfo");
920
    }
921
#endif /* __APPLE__ */
922
0
    msgh->msg_controllen = CMSG_SPACE(sizeof(*pkt));
923
924
0
    cmsg = CMSG_FIRSTHDR(msgh);
925
0
    cmsg->cmsg_level = IPPROTO_IP;
926
0
    cmsg->cmsg_type = IP_PKTINFO;
927
0
    cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
928
929
0
    pkt = reinterpret_cast<struct in_pktinfo*>(CMSG_DATA(cmsg));
930
    // Include the padding to stop valgrind complaining about passing uninitialized data
931
0
    memset(pkt, 0, CMSG_SPACE(sizeof(*pkt)));
932
0
    pkt->ipi_spec_dst = source->sin4.sin_addr;
933
0
    pkt->ipi_ifindex = itfIndex;
934
#elif defined(IP_SENDSRCADDR)
935
    struct in_addr* in;
936
937
    msgh->msg_control = cmsgbuf;
938
#if !defined(__APPLE__)
939
    static_assert(CMSG_SPACE(sizeof(*in)) <= sizeof(*cmsgbuf), "Buffer is too small for in_addr");
940
#else /* __APPLE__ */
941
    if (CMSG_SPACE(sizeof(*in)) > sizeof(*cmsgbuf)) {
942
      throw std::runtime_error("Buffer is too small for in_addr");
943
    }
944
#endif /* __APPLE__ */
945
    msgh->msg_controllen = CMSG_SPACE(sizeof(*in));
946
947
    cmsg = CMSG_FIRSTHDR(msgh);
948
    cmsg->cmsg_level = IPPROTO_IP;
949
    cmsg->cmsg_type = IP_SENDSRCADDR;
950
    cmsg->cmsg_len = CMSG_LEN(sizeof(*in));
951
952
    // Include the padding to stop valgrind complaining about passing uninitialized data
953
    in = (struct in_addr*)CMSG_DATA(cmsg);
954
    memset(in, 0, CMSG_SPACE(sizeof(*in)));
955
    *in = source->sin4.sin_addr;
956
#endif
957
0
  }
958
0
}
959
960
unsigned int getFilenumLimit(bool hardOrSoft)
961
0
{
962
0
  struct rlimit rlim{};
963
0
  if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
964
0
    unixDie("Requesting number of available file descriptors");
965
0
  }
966
0
  return hardOrSoft ? rlim.rlim_max : rlim.rlim_cur;
967
0
}
968
969
void setFilenumLimit(unsigned int lim)
970
0
{
971
0
  struct rlimit rlim{};
972
973
0
  if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
974
0
    unixDie("Requesting number of available file descriptors");
975
0
  }
976
0
  rlim.rlim_cur = lim;
977
0
  if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
978
0
    unixDie("Setting number of available file descriptors");
979
0
  }
980
0
}
981
982
bool setSocketTimestamps(int fileDesc)
983
0
{
984
0
#ifdef SO_TIMESTAMP
985
0
  int one = 1;
986
0
  return setsockopt(fileDesc, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) == 0;
987
#else
988
  return true; // we pretend this happened.
989
#endif
990
0
}
991
992
bool setTCPNoDelay(int sock)
993
0
{
994
0
  int flag = 1;
995
0
  return setsockopt(sock, /* socket affected */
996
0
                    IPPROTO_TCP, /* set option at TCP level */
997
0
                    TCP_NODELAY, /* name of option */
998
0
                    &flag,
999
0
                    sizeof(flag))
1000
0
    == 0; /* length of option value */
1001
0
}
1002
1003
bool setNonBlocking(int sock)
1004
0
{
1005
0
  int flags = fcntl(sock, F_GETFL, 0);
1006
0
  return flags >= 0 && fcntl(sock, F_SETFL, flags | O_NONBLOCK) >= 0;
1007
0
}
1008
1009
bool setBlocking(int sock)
1010
0
{
1011
0
  int flags = fcntl(sock, F_GETFL, 0);
1012
0
  return flags >= 0 && fcntl(sock, F_SETFL, flags & (~O_NONBLOCK)) >= 0;
1013
0
}
1014
1015
bool setReuseAddr(int sock)
1016
0
{
1017
0
  int tmp = 1;
1018
0
  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &tmp, static_cast<unsigned>(sizeof tmp)) < 0) {
1019
0
    throw PDNSException(string("Setsockopt failed: ") + stringerror());
1020
0
  }
1021
0
  return true;
1022
0
}
1023
1024
void setDscp(int sock, unsigned short family, uint8_t dscp)
1025
0
{
1026
0
  int val = 0;
1027
0
  unsigned int len = 0;
1028
1029
0
  if (dscp == 0 || dscp > 63) {
1030
    // No DSCP marking
1031
0
    return;
1032
0
  }
1033
1034
0
  if (family == AF_INET) {
1035
0
    if (getsockopt(sock, IPPROTO_IP, IP_TOS, &val, &len) < 0) {
1036
0
      throw std::runtime_error(string("Set DSCP failed: ") + stringerror());
1037
0
    }
1038
0
    val = (dscp << 2) | (val & 0x3);
1039
0
    if (setsockopt(sock, IPPROTO_IP, IP_TOS, &val, sizeof(val)) < 0) {
1040
0
      throw std::runtime_error(string("Set DSCP failed: ") + stringerror());
1041
0
    }
1042
0
  }
1043
0
  else if (family == AF_INET6) {
1044
0
    if (getsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, &val, &len) < 0) {
1045
0
      throw std::runtime_error(string("Set DSCP failed: ") + stringerror());
1046
0
    }
1047
0
    val = (dscp << 2) | (val & 0x3);
1048
0
    if (setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, &val, sizeof(val)) < 0) {
1049
0
      throw std::runtime_error(string("Set DSCP failed: ") + stringerror());
1050
0
    }
1051
0
  }
1052
0
}
1053
1054
bool isNonBlocking(int sock)
1055
0
{
1056
0
  int flags = fcntl(sock, F_GETFL, 0);
1057
0
  return (flags & O_NONBLOCK) != 0;
1058
0
}
1059
1060
bool setReceiveSocketErrors([[maybe_unused]] int sock, [[maybe_unused]] int family)
1061
0
{
1062
0
#ifdef __linux__
1063
0
  int tmp = 1;
1064
0
  int ret{};
1065
0
  if (family == AF_INET) {
1066
0
    ret = setsockopt(sock, IPPROTO_IP, IP_RECVERR, &tmp, sizeof(tmp));
1067
0
  }
1068
0
  else {
1069
0
    ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVERR, &tmp, sizeof(tmp));
1070
0
  }
1071
0
  if (ret < 0) {
1072
0
    throw PDNSException(string("Setsockopt failed: ") + stringerror());
1073
0
  }
1074
0
#endif
1075
0
  return true;
1076
0
}
1077
1078
// Closes a socket.
1079
int closesocket(int socket)
1080
0
{
1081
0
  int ret = ::close(socket);
1082
0
  if (ret < 0 && errno == ECONNRESET) { // see ticket 192, odd BSD behaviour
1083
0
    return 0;
1084
0
  }
1085
0
  if (ret < 0) {
1086
0
    int err = errno;
1087
0
    throw PDNSException("Error closing socket: " + stringerror(err));
1088
0
  }
1089
0
  return ret;
1090
0
}
1091
1092
bool setCloseOnExec(int sock)
1093
0
{
1094
0
  int flags = fcntl(sock, F_GETFD, 0);
1095
0
  return flags >= 0 && fcntl(sock, F_SETFD, flags | FD_CLOEXEC) >= 0;
1096
0
}
1097
1098
#ifdef __linux__
1099
#include <linux/rtnetlink.h>
1100
1101
int getMACAddress(const ComboAddress& address, char* dest, size_t destLen)
1102
0
{
1103
0
  struct
1104
0
  {
1105
0
    struct nlmsghdr headermsg;
1106
0
    struct ndmsg neighbormsg;
1107
0
  } request{};
1108
1109
0
  std::array<char, 8192> buffer{};
1110
1111
0
  auto sock = FDWrapper(socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE));
1112
0
  if (sock.getHandle() == -1) {
1113
0
    return errno;
1114
0
  }
1115
1116
0
  memset(&request, 0, sizeof(request));
1117
0
  request.headermsg.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
1118
0
  request.headermsg.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
1119
0
  request.headermsg.nlmsg_type = RTM_GETNEIGH;
1120
0
  request.neighbormsg.ndm_family = address.sin4.sin_family;
1121
1122
0
  while (true) {
1123
0
    ssize_t sent = send(sock.getHandle(), &request, sizeof(request), 0);
1124
0
    if (sent == -1) {
1125
0
      if (errno == EINTR) {
1126
0
        continue;
1127
0
      }
1128
0
      return errno;
1129
0
    }
1130
0
    if (static_cast<size_t>(sent) != sizeof(request)) {
1131
0
      return EIO;
1132
0
    }
1133
0
    break;
1134
0
  }
1135
1136
0
  bool done = false;
1137
0
  bool foundIP = false;
1138
0
  bool foundMAC = false;
1139
0
  do {
1140
0
    ssize_t got = recv(sock.getHandle(), buffer.data(), buffer.size(), 0);
1141
1142
0
    if (got < 0) {
1143
0
      if (errno == EINTR) {
1144
0
        continue;
1145
0
      }
1146
0
      return errno;
1147
0
    }
1148
1149
0
    auto remaining = static_cast<size_t>(got);
1150
0
    for (auto* nlmsgheader = reinterpret_cast<struct nlmsghdr*>(buffer.data());
1151
0
         !done && NLMSG_OK(nlmsgheader, remaining);
1152
0
         nlmsgheader = reinterpret_cast<struct nlmsghdr*>(NLMSG_NEXT(nlmsgheader, remaining))) {
1153
1154
0
      if (nlmsgheader->nlmsg_type == NLMSG_DONE) {
1155
0
        done = true;
1156
0
        break;
1157
0
      }
1158
1159
0
      auto* ndmsg = reinterpret_cast<struct ndmsg*>(NLMSG_DATA(nlmsgheader));
1160
0
      auto* rtatp = reinterpret_cast<struct rtattr*>(reinterpret_cast<char*>(ndmsg) + NLMSG_ALIGN(sizeof(struct ndmsg)));
1161
0
      auto rtattrlen = nlmsgheader->nlmsg_len - NLMSG_LENGTH(sizeof(struct ndmsg));
1162
1163
0
      if (ndmsg->ndm_family != address.sin4.sin_family) {
1164
0
        continue;
1165
0
      }
1166
1167
0
      if (address.sin4.sin_family == AF_INET6 && address.sin6.sin6_scope_id != 0 && static_cast<int32_t>(address.sin6.sin6_scope_id) != ndmsg->ndm_ifindex) {
1168
0
        continue;
1169
0
      }
1170
1171
0
      for (; !done && RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) {
1172
0
        if (rtatp->rta_type == NDA_DST) {
1173
0
          if (ndmsg->ndm_family == AF_INET) {
1174
0
            auto* inp = reinterpret_cast<struct in_addr*>(RTA_DATA(rtatp));
1175
0
            if (inp->s_addr == address.sin4.sin_addr.s_addr) {
1176
0
              foundIP = true;
1177
0
            }
1178
0
          }
1179
0
          else if (ndmsg->ndm_family == AF_INET6) {
1180
0
            auto* inp = reinterpret_cast<struct in6_addr*>(RTA_DATA(rtatp));
1181
0
            if (memcmp(&inp->s6_addr[0], &address.sin6.sin6_addr.s6_addr[0], sizeof(address.sin6.sin6_addr.s6_addr)) == 0) {
1182
0
              foundIP = true;
1183
0
            }
1184
0
          }
1185
0
        }
1186
0
        else if (rtatp->rta_type == NDA_LLADDR) {
1187
0
          if (foundIP) {
1188
0
            size_t addrLen = rtatp->rta_len - sizeof(struct rtattr);
1189
0
            if (addrLen > destLen) {
1190
0
              return ENOBUFS;
1191
0
            }
1192
0
            memcpy(dest, reinterpret_cast<const char*>(rtatp) + sizeof(struct rtattr), addrLen);
1193
0
            foundMAC = true;
1194
0
            done = true;
1195
0
            break;
1196
0
          }
1197
0
        }
1198
0
      }
1199
0
    }
1200
0
  } while (!done);
1201
1202
0
  return foundMAC ? 0 : ENOENT;
1203
0
}
1204
#else
1205
int getMACAddress(const ComboAddress& /* ca */, char* /* dest */, size_t /* len */)
1206
{
1207
  return ENOENT;
1208
}
1209
#endif /* __linux__ */
1210
1211
string getMACAddress(const ComboAddress& address)
1212
0
{
1213
0
  string ret;
1214
0
  std::array<char, 6> tmp{};
1215
0
  if (getMACAddress(address, tmp.data(), tmp.size()) == 0) {
1216
0
    ret.append(tmp.data(), tmp.size());
1217
0
  }
1218
0
  return ret;
1219
0
}
1220
1221
uint64_t udpErrorStats([[maybe_unused]] const std::string& str)
1222
0
{
1223
0
#ifdef __linux__
1224
0
  ifstream ifs("/proc/net/snmp");
1225
0
  if (!ifs) {
1226
0
    return 0;
1227
0
  }
1228
1229
0
  string line;
1230
0
  while (getline(ifs, line)) {
1231
0
    if (boost::starts_with(line, "Udp: ") && isdigit(line.at(5)) != 0) {
1232
0
      vector<string> parts;
1233
0
      stringtok(parts, line, " \n\t\r");
1234
1235
0
      if (parts.size() < 7) {
1236
0
        break;
1237
0
      }
1238
1239
0
      if (str == "udp-rcvbuf-errors") {
1240
0
        return std::stoull(parts.at(5));
1241
0
      }
1242
0
      if (str == "udp-sndbuf-errors") {
1243
0
        return std::stoull(parts.at(6));
1244
0
      }
1245
0
      if (str == "udp-noport-errors") {
1246
0
        return std::stoull(parts.at(2));
1247
0
      }
1248
0
      if (str == "udp-in-errors") {
1249
0
        return std::stoull(parts.at(3));
1250
0
      }
1251
0
      if (parts.size() >= 8 && str == "udp-in-csum-errors") {
1252
0
        return std::stoull(parts.at(7));
1253
0
      }
1254
0
      return 0;
1255
0
    }
1256
0
  }
1257
0
#endif
1258
0
  return 0;
1259
0
}
1260
1261
uint64_t udp6ErrorStats([[maybe_unused]] const std::string& str)
1262
0
{
1263
0
#ifdef __linux__
1264
0
  const std::map<std::string, std::string> keys = {
1265
0
    {"udp6-in-errors", "Udp6InErrors"},
1266
0
    {"udp6-recvbuf-errors", "Udp6RcvbufErrors"},
1267
0
    {"udp6-sndbuf-errors", "Udp6SndbufErrors"},
1268
0
    {"udp6-noport-errors", "Udp6NoPorts"},
1269
0
    {"udp6-in-csum-errors", "Udp6InCsumErrors"}};
1270
1271
0
  auto key = keys.find(str);
1272
0
  if (key == keys.end()) {
1273
0
    return 0;
1274
0
  }
1275
1276
0
  ifstream ifs("/proc/net/snmp6");
1277
0
  if (!ifs) {
1278
0
    return 0;
1279
0
  }
1280
1281
0
  std::string line;
1282
0
  while (getline(ifs, line)) {
1283
0
    if (!boost::starts_with(line, key->second)) {
1284
0
      continue;
1285
0
    }
1286
1287
0
    std::vector<std::string> parts;
1288
0
    stringtok(parts, line, " \n\t\r");
1289
1290
0
    if (parts.size() != 2) {
1291
0
      return 0;
1292
0
    }
1293
1294
0
    return std::stoull(parts.at(1));
1295
0
  }
1296
0
#endif
1297
0
  return 0;
1298
0
}
1299
1300
uint64_t tcpErrorStats(const std::string& label)
1301
0
{
1302
0
#ifdef __linux__
1303
0
  ifstream ifs("/proc/net/netstat");
1304
0
  if (!ifs) {
1305
0
    return 0U;
1306
0
  }
1307
1308
0
  string line;
1309
0
  std::vector<string> parts;
1310
0
  std::optional<size_t> position{std::nullopt};
1311
0
  while (getline(ifs, line)) {
1312
0
    if (line.size() <= 9 || !boost::starts_with(line, "TcpExt: ")) {
1313
0
      continue;
1314
0
    }
1315
1316
0
    if (!position.has_value() && isdigit(line.at(8)) == 0) {
1317
      // this is the line with the labels, let's try to find ours
1318
0
      parts.clear();
1319
0
      stringtok(parts, line, " \n\t\r");
1320
0
      size_t currentPosition = 0;
1321
0
      for (const auto& tentativeLabel : parts) {
1322
0
        if (tentativeLabel == label) {
1323
0
          position = currentPosition;
1324
0
          break;
1325
0
        }
1326
0
        ++currentPosition;
1327
0
      }
1328
0
      if (!position.has_value()) {
1329
        // label not found, bye
1330
0
        return 0U;
1331
0
      }
1332
0
      continue;
1333
0
    }
1334
1335
0
    if (position.has_value() && isdigit(line.at(8)) != 0) {
1336
      // this is the line with the values
1337
0
      parts.clear();
1338
0
      stringtok(parts, line, " \n\t\r");
1339
0
      if (parts.size() < *position) {
1340
0
        return 0U;
1341
0
      }
1342
0
      return std::stoull(parts.at(*position));
1343
0
    }
1344
0
  }
1345
0
#endif
1346
0
  return 0U;
1347
0
}
1348
1349
uint64_t getCPUIOWait(const std::string& /* str */)
1350
0
{
1351
0
#ifdef __linux__
1352
0
  ifstream ifs("/proc/stat");
1353
0
  if (!ifs) {
1354
0
    return 0;
1355
0
  }
1356
1357
0
  string line;
1358
0
  vector<string> parts;
1359
0
  while (getline(ifs, line)) {
1360
0
    if (boost::starts_with(line, "cpu ")) {
1361
0
      stringtok(parts, line, " \n\t\r");
1362
1363
0
      if (parts.size() < 6) {
1364
0
        break;
1365
0
      }
1366
1367
0
      return std::stoull(parts[5]);
1368
0
    }
1369
0
  }
1370
0
#endif
1371
0
  return 0;
1372
0
}
1373
1374
uint64_t getCPUSteal(const std::string& /* str */)
1375
0
{
1376
0
#ifdef __linux__
1377
0
  ifstream ifs("/proc/stat");
1378
0
  if (!ifs) {
1379
0
    return 0;
1380
0
  }
1381
1382
0
  string line;
1383
0
  vector<string> parts;
1384
0
  while (getline(ifs, line)) {
1385
0
    if (boost::starts_with(line, "cpu ")) {
1386
0
      stringtok(parts, line, " \n\t\r");
1387
1388
0
      if (parts.size() < 9) {
1389
0
        break;
1390
0
      }
1391
1392
0
      return std::stoull(parts[8]);
1393
0
    }
1394
0
  }
1395
0
#endif
1396
0
  return 0;
1397
0
}
1398
1399
bool getTSIGHashEnum(const DNSName& algoName, TSIGHashEnum& algoEnum)
1400
0
{
1401
0
  if (algoName == g_hmacmd5dnsname_long || algoName == g_hmacmd5dnsname) {
1402
0
    algoEnum = TSIG_MD5;
1403
0
  }
1404
0
  else if (algoName == g_hmacsha1dnsname) {
1405
0
    algoEnum = TSIG_SHA1;
1406
0
  }
1407
0
  else if (algoName == g_hmacsha224dnsname) {
1408
0
    algoEnum = TSIG_SHA224;
1409
0
  }
1410
0
  else if (algoName == g_hmacsha256dnsname) {
1411
0
    algoEnum = TSIG_SHA256;
1412
0
  }
1413
0
  else if (algoName == g_hmacsha384dnsname) {
1414
0
    algoEnum = TSIG_SHA384;
1415
0
  }
1416
0
  else if (algoName == g_hmacsha512dnsname) {
1417
0
    algoEnum = TSIG_SHA512;
1418
0
  }
1419
0
  else if (algoName == g_gsstsigdnsname) {
1420
0
    algoEnum = TSIG_GSS;
1421
0
  }
1422
0
  else {
1423
0
    return false;
1424
0
  }
1425
0
  return true;
1426
0
}
1427
1428
DNSName getTSIGAlgoName(TSIGHashEnum& algoEnum)
1429
0
{
1430
0
  switch (algoEnum) {
1431
0
  case TSIG_MD5:
1432
0
    return g_hmacmd5dnsname_long;
1433
0
  case TSIG_SHA1:
1434
0
    return g_hmacsha1dnsname;
1435
0
  case TSIG_SHA224:
1436
0
    return g_hmacsha224dnsname;
1437
0
  case TSIG_SHA256:
1438
0
    return g_hmacsha256dnsname;
1439
0
  case TSIG_SHA384:
1440
0
    return g_hmacsha384dnsname;
1441
0
  case TSIG_SHA512:
1442
0
    return g_hmacsha512dnsname;
1443
0
  case TSIG_GSS:
1444
0
    return g_gsstsigdnsname;
1445
0
  }
1446
0
  throw PDNSException("getTSIGAlgoName does not understand given algorithm, please fix!");
1447
0
}
1448
1449
uint64_t getOpenFileDescriptors(const std::string& /* unused */)
1450
0
{
1451
0
#ifdef __linux__
1452
0
  const auto* const dirName = "/proc/self/fd";
1453
0
  struct stat status; // NOLINT(cppcoreguidelines-pro-type-member-init)
1454
0
  auto ret = stat(dirName, &status);
1455
0
  if (ret != 0) {
1456
0
    return 0;
1457
0
  }
1458
0
  if (status.st_size != 0) { // Until linux 6.1, this would return 0
1459
0
    return status.st_size;
1460
0
  }
1461
1462
  // This can lead to performance issues with *many* open fds
1463
0
  uint64_t nbFileDescriptors = 0;
1464
0
  auto directoryError = pdns::visit_directory(dirName, [&nbFileDescriptors]([[maybe_unused]] ino_t inodeNumber, const std::string_view& name) {
1465
0
    uint32_t num{};
1466
0
    try {
1467
0
      pdns::checked_stoi_into(num, std::string(name));
1468
0
      if (std::to_string(num) == name) {
1469
0
        nbFileDescriptors++;
1470
0
      }
1471
0
    }
1472
0
    catch (...) {
1473
      // was not a number.
1474
0
      ;
1475
0
    }
1476
0
    return true;
1477
0
  });
1478
0
  if (directoryError) {
1479
0
    return 0U;
1480
0
  }
1481
0
  return nbFileDescriptors;
1482
#elif defined(__OpenBSD__)
1483
  // FreeBSD also has this in libopenbsd, but I don't know if that's available always
1484
  return getdtablecount();
1485
#else
1486
  return 0U;
1487
#endif
1488
0
}
1489
1490
uint64_t getRealMemoryUsage(const std::string& /* unused */)
1491
0
{
1492
0
#ifdef __linux__
1493
0
  ifstream ifs("/proc/self/statm");
1494
0
  if (!ifs) {
1495
0
    return 0;
1496
0
  }
1497
0
  uint64_t size{};
1498
0
  uint64_t resident{};
1499
0
  uint64_t shared{};
1500
0
  uint64_t text{};
1501
0
  uint64_t lib{};
1502
0
  uint64_t data{};
1503
0
  ifs >> size >> resident >> shared >> text >> lib >> data;
1504
1505
  // We used to use "data" here, but it proves unreliable and even is marked "broken"
1506
  // in https://www.kernel.org/doc/html/latest/filesystems/proc.html
1507
0
  return resident * getpagesize();
1508
#else
1509
  struct rusage rusage{};
1510
  if (getrusage(RUSAGE_SELF, &rusage) != 0) {
1511
    return 0;
1512
  }
1513
  return rusage.ru_maxrss * 1024;
1514
#endif
1515
0
}
1516
1517
uint64_t getSpecialMemoryUsage(const std::string& /* unused */)
1518
0
{
1519
0
#ifdef __linux__
1520
0
  ifstream ifs("/proc/self/smaps");
1521
0
  if (!ifs) {
1522
0
    return 0;
1523
0
  }
1524
0
  string line;
1525
0
  uint64_t bytes = 0;
1526
0
  string header("Private_Dirty:");
1527
0
  while (getline(ifs, line)) {
1528
0
    if (boost::starts_with(line, header)) {
1529
0
      bytes += std::stoull(line.substr(header.length() + 1)) * 1024;
1530
0
    }
1531
0
  }
1532
0
  return bytes;
1533
#else
1534
  return 0;
1535
#endif
1536
0
}
1537
1538
uint64_t getCPUTimeUser(const std::string& /* unused */)
1539
0
{
1540
0
  struct rusage rusagew{};
1541
0
  getrusage(RUSAGE_SELF, &rusagew);
1542
0
  return (rusagew.ru_utime.tv_sec * 1000ULL) + (rusagew.ru_utime.tv_usec / 1000);
1543
0
}
1544
1545
uint64_t getCPUTimeSystem(const std::string& /* unused */)
1546
0
{
1547
0
  struct rusage rusage{};
1548
0
  getrusage(RUSAGE_SELF, &rusage);
1549
0
  return (rusage.ru_stime.tv_sec * 1000ULL) + (rusage.ru_stime.tv_usec / 1000);
1550
0
}
1551
1552
double DiffTime(const struct timespec& first, const struct timespec& second)
1553
0
{
1554
0
  auto seconds = second.tv_sec - first.tv_sec;
1555
0
  auto nseconds = second.tv_nsec - first.tv_nsec;
1556
1557
0
  if (nseconds < 0) {
1558
0
    seconds -= 1;
1559
0
    nseconds += 1000000000;
1560
0
  }
1561
0
  return static_cast<double>(seconds) + (static_cast<double>(nseconds) / 1000000000.0);
1562
0
}
1563
1564
double DiffTime(const struct timeval& first, const struct timeval& second)
1565
0
{
1566
0
  auto seconds = second.tv_sec - first.tv_sec;
1567
0
  auto useconds = second.tv_usec - first.tv_usec;
1568
1569
0
  if (useconds < 0) {
1570
0
    seconds -= 1;
1571
0
    useconds += 1000000;
1572
0
  }
1573
0
  return static_cast<double>(seconds) + (static_cast<double>(useconds) / 1000000.0);
1574
0
}
1575
1576
uid_t strToUID(const string& str)
1577
0
{
1578
0
  uid_t result = 0;
1579
0
  const char* cstr = str.c_str();
1580
0
  auto bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
1581
0
  if (bufsize == -1) {
1582
0
    throw runtime_error("cannot retrieve buffer size for passwd entry");
1583
0
  }
1584
0
  std::string buffer;
1585
0
  buffer.resize(bufsize);
1586
0
  struct passwd pwd{};
1587
0
  struct passwd* pwdPtr = nullptr;
1588
0
  if (getpwnam_r(cstr, &pwd, buffer.data(), buffer.size(), &pwdPtr) != 0) {
1589
0
    throw runtime_error("cannot retrieve uid");
1590
0
  }
1591
1592
0
  if (pwdPtr == nullptr) {
1593
0
    long long val{};
1594
1595
0
    try {
1596
0
      val = stoll(str);
1597
0
    }
1598
0
    catch (std::exception& e) {
1599
0
      throw runtime_error((boost::format("Error: Unable to parse user ID %s") % cstr).str());
1600
0
    }
1601
1602
0
    if (val < std::numeric_limits<uid_t>::min() || val > std::numeric_limits<uid_t>::max()) {
1603
0
      throw runtime_error((boost::format("Error: Unable to parse user ID %s") % cstr).str());
1604
0
    }
1605
1606
0
    result = static_cast<uid_t>(val);
1607
0
  }
1608
0
  else {
1609
0
    result = pwdPtr->pw_uid;
1610
0
  }
1611
1612
0
  return result;
1613
0
}
1614
1615
gid_t strToGID(const string& str)
1616
0
{
1617
0
  gid_t result = 0;
1618
0
  const char* cstr = str.c_str();
1619
0
  auto bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
1620
0
  if (bufsize == -1) {
1621
0
    throw runtime_error("cannot retrieve buffer size for group entry");
1622
0
  }
1623
0
  std::string buffer;
1624
0
  buffer.resize(bufsize);
1625
0
  struct group grp{};
1626
0
  struct group* grpPtr = nullptr;
1627
0
  if (getgrnam_r(cstr, &grp, buffer.data(), buffer.size(), &grpPtr) != 0) {
1628
0
    throw runtime_error("cannot retrieve gid");
1629
0
  }
1630
1631
0
  if (grpPtr == nullptr) {
1632
0
    long long val{};
1633
1634
0
    try {
1635
0
      val = stoll(str);
1636
0
    }
1637
0
    catch (std::exception& e) {
1638
0
      throw runtime_error((boost::format("Error: Unable to parse group ID %s") % cstr).str());
1639
0
    }
1640
1641
0
    if (val < std::numeric_limits<gid_t>::min() || val > std::numeric_limits<gid_t>::max()) {
1642
0
      throw runtime_error((boost::format("Error: Unable to parse group ID %s") % cstr).str());
1643
0
    }
1644
1645
0
    result = static_cast<gid_t>(val);
1646
0
  }
1647
0
  else {
1648
0
    result = grpPtr->gr_gid;
1649
0
  }
1650
1651
0
  return result;
1652
0
}
1653
1654
bool isSettingThreadCPUAffinitySupported()
1655
0
{
1656
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
1657
  return true;
1658
#else
1659
0
  return false;
1660
0
#endif
1661
0
}
1662
1663
int mapThreadToCPUList([[maybe_unused]] pthread_t tid, [[maybe_unused]] const std::set<int>& cpus)
1664
0
{
1665
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
1666
#ifdef __NetBSD__
1667
  cpuset_t* cpuset;
1668
  cpuset = cpuset_create();
1669
  for (const auto cpuID : cpus) {
1670
    cpuset_set(cpuID, cpuset);
1671
  }
1672
1673
  return pthread_setaffinity_np(tid,
1674
                                cpuset_size(cpuset),
1675
                                cpuset);
1676
#else
1677
#ifdef __FreeBSD__
1678
#define cpu_set_t cpuset_t
1679
#endif
1680
  cpu_set_t cpuset;
1681
  CPU_ZERO(&cpuset);
1682
  for (const auto cpuID : cpus) {
1683
    CPU_SET(cpuID, &cpuset);
1684
  }
1685
1686
  return pthread_setaffinity_np(tid,
1687
                                sizeof(cpuset),
1688
                                &cpuset);
1689
#endif
1690
#else
1691
0
  return ENOSYS;
1692
0
#endif /* HAVE_PTHREAD_SETAFFINITY_NP */
1693
0
}
1694
1695
std::vector<ComboAddress> getResolvers(const std::string& resolvConfPath)
1696
0
{
1697
  // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive in some boost versions
1698
0
  std::vector<ComboAddress> results;
1699
1700
0
  std::ifstream ifs(resolvConfPath);
1701
0
  if (!ifs) {
1702
0
    return results;
1703
0
  }
1704
1705
0
  string line;
1706
0
  while (std::getline(ifs, line)) {
1707
0
    boost::trim_right_if(line, boost::is_any_of(" \r\n\x1a"));
1708
0
    boost::trim_left(line); // leading spaces, let's be nice
1709
1710
0
    string::size_type tpos = line.find_first_of(";#");
1711
0
    if (tpos != string::npos) {
1712
0
      line.resize(tpos);
1713
0
    }
1714
1715
0
    if (boost::starts_with(line, "nameserver ") || boost::starts_with(line, "nameserver\t")) {
1716
0
      vector<string> parts;
1717
0
      stringtok(parts, line, " \t,"); // be REALLY nice
1718
0
      for (auto iter = parts.begin() + 1; iter != parts.end(); ++iter) {
1719
0
        try {
1720
0
          results.emplace_back(*iter, 53);
1721
0
        }
1722
0
        catch (...) {
1723
0
          ; // ignored
1724
0
        }
1725
0
      }
1726
0
    }
1727
0
  }
1728
1729
0
  return results;
1730
  // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
1731
0
}
1732
1733
size_t getPipeBufferSize([[maybe_unused]] int fileDesc)
1734
0
{
1735
0
#ifdef F_GETPIPE_SZ
1736
0
  int res = fcntl(fileDesc, F_GETPIPE_SZ);
1737
0
  if (res == -1) {
1738
0
    return 0;
1739
0
  }
1740
0
  return res;
1741
#else
1742
  errno = ENOSYS;
1743
  return 0;
1744
#endif /* F_GETPIPE_SZ */
1745
0
}
1746
1747
bool setPipeBufferSize([[maybe_unused]] int fileDesc, [[maybe_unused]] size_t size)
1748
0
{
1749
0
#ifdef F_SETPIPE_SZ
1750
0
  if (size > static_cast<size_t>(std::numeric_limits<int>::max())) {
1751
0
    errno = EINVAL;
1752
0
    return false;
1753
0
  }
1754
0
  int newSize = static_cast<int>(size);
1755
0
  int res = fcntl(fileDesc, F_SETPIPE_SZ, newSize);
1756
0
  return res != -1;
1757
#else
1758
  errno = ENOSYS;
1759
  return false;
1760
#endif /* F_SETPIPE_SZ */
1761
0
}
1762
1763
DNSName reverseNameFromIP(const ComboAddress& address)
1764
0
{
1765
0
  if (address.isIPv4()) {
1766
0
    std::string result("in-addr.arpa.");
1767
0
    const auto* ptr = reinterpret_cast<const uint8_t*>(&address.sin4.sin_addr.s_addr);
1768
0
    for (size_t idx = 0; idx < sizeof(address.sin4.sin_addr.s_addr); idx++) {
1769
0
      auto tmp{result};
1770
0
      result = std::to_string(ptr[idx]);
1771
0
      result += ".";
1772
0
      result += tmp;
1773
0
    }
1774
0
    return DNSName(result);
1775
0
  }
1776
0
  if (address.isIPv6()) {
1777
0
    std::string result("ip6.arpa.");
1778
0
    const auto* ptr = &address.sin6.sin6_addr.s6_addr[0];
1779
0
    for (size_t idx = 0; idx < sizeof(address.sin6.sin6_addr.s6_addr); idx++) {
1780
0
      std::stringstream stream;
1781
0
      stream << std::hex << (ptr[idx] & 0x0F);
1782
0
      stream << '.';
1783
0
      stream << std::hex << (((ptr[idx]) >> 4) & 0x0F);
1784
0
      stream << '.';
1785
0
      auto tmp{result};
1786
0
      result = stream.str();
1787
0
      result += tmp;
1788
0
    }
1789
0
    return DNSName(result);
1790
0
  }
1791
1792
0
  throw std::runtime_error("Calling reverseNameFromIP() for an address which is neither an IPv4 nor an IPv6");
1793
0
}
1794
1795
std::string makeLuaString(const std::string& input)
1796
0
{
1797
0
  ostringstream str;
1798
1799
0
  str << '"';
1800
1801
0
  std::array<char, 5> item{};
1802
0
  for (unsigned char character : input) {
1803
0
    if ((islower(character) != 0) || (isupper(character) != 0)) {
1804
0
      item[0] = static_cast<char>(character);
1805
0
      item[1] = 0;
1806
0
    }
1807
0
    else {
1808
0
      snprintf(item.data(), item.size(), "\\%03d", character);
1809
0
    }
1810
0
    str << item.data();
1811
0
  }
1812
1813
0
  str << '"';
1814
1815
0
  return str.str();
1816
0
}
1817
1818
size_t parseSVCBValueList(const std::string& str, vector<std::string>& val)
1819
1.16k
{
1820
1.16k
  std::string parsed;
1821
1.16k
  auto ret = parseRFC1035CharString(str, parsed);
1822
1.16k
  parseSVCBValueListFromParsedRFC1035CharString(parsed, val);
1823
1.16k
  return ret;
1824
1.16k
};
1825
1826
#ifdef HAVE_CRYPTO_MEMCMP
1827
#include <openssl/crypto.h>
1828
#else /* HAVE_CRYPTO_MEMCMP */
1829
#ifdef HAVE_SODIUM_MEMCMP
1830
#include <sodium.h>
1831
#endif /* HAVE_SODIUM_MEMCMP */
1832
#endif /* HAVE_CRYPTO_MEMCMP */
1833
1834
bool constantTimeStringEquals(const std::string& lhs, const std::string& rhs)
1835
0
{
1836
0
  if (lhs.size() != rhs.size()) {
1837
0
    return false;
1838
0
  }
1839
0
  const size_t size = lhs.size();
1840
0
#ifdef HAVE_CRYPTO_MEMCMP
1841
0
  return CRYPTO_memcmp(lhs.c_str(), rhs.c_str(), size) == 0;
1842
#else /* HAVE_CRYPTO_MEMCMP */
1843
#ifdef HAVE_SODIUM_MEMCMP
1844
  return sodium_memcmp(lhs.c_str(), rhs.c_str(), size) == 0;
1845
#else /* HAVE_SODIUM_MEMCMP */
1846
  const volatile unsigned char* _a = (const volatile unsigned char*)lhs.c_str();
1847
  const volatile unsigned char* _b = (const volatile unsigned char*)rhs.c_str();
1848
  unsigned char res = 0;
1849
1850
  for (size_t idx = 0; idx < size; idx++) {
1851
    res |= _a[idx] ^ _b[idx];
1852
  }
1853
1854
  return res == 0;
1855
#endif /* !HAVE_SODIUM_MEMCMP */
1856
#endif /* !HAVE_CRYPTO_MEMCMP */
1857
0
}
1858
1859
namespace pdns
1860
{
1861
struct CloseDirDeleter
1862
{
1863
  void operator()(DIR* dir) const noexcept
1864
0
  {
1865
0
    closedir(dir);
1866
0
  }
1867
};
1868
1869
std::optional<std::string> visit_directory(const std::string& directory, const std::function<bool(ino_t inodeNumber, const std::string_view& name)>& visitor)
1870
0
{
1871
0
  auto dirHandle = std::unique_ptr<DIR, CloseDirDeleter>(opendir(directory.c_str()));
1872
0
  if (!dirHandle) {
1873
0
    auto err = errno;
1874
0
    return std::string("Error opening directory '" + directory + "': " + stringerror(err));
1875
0
  }
1876
1877
0
  bool keepGoing = true;
1878
0
  struct dirent* ent = nullptr;
1879
  // NOLINTNEXTLINE(concurrency-mt-unsafe): readdir is thread-safe nowadays and readdir_r is deprecated
1880
0
  while (keepGoing && (ent = readdir(dirHandle.get())) != nullptr) {
1881
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay: dirent API
1882
0
    auto name = std::string_view(ent->d_name, strlen(ent->d_name));
1883
0
    keepGoing = visitor(ent->d_ino, name);
1884
0
  }
1885
1886
0
  return std::nullopt;
1887
0
}
1888
1889
#ifndef DNSDIST
1890
std::vector<std::string> list_directory(const std::string& directory, const std::string& suffix, Logr::log_t d_log)
1891
0
{
1892
0
  std::vector<std::string> results;
1893
1894
0
  auto directoryError = pdns::visit_directory(directory,
1895
0
                                              [&directory, &suffix, &results, d_log]([[maybe_unused]] ino_t inodeNumber, const std::string_view& name) {
1896
0
                                                if (boost::starts_with(name, ".")) {
1897
0
                                                  return true; // skip any dots
1898
0
                                                }
1899
0
                                                if (boost::ends_with(name, suffix)) {
1900
                                                  // build name
1901
0
                                                  string fullName = directory + "/" + std::string(name);
1902
                                                  // ensure it's a readable file
1903
0
                                                  struct stat statInfo{};
1904
0
                                                  if (stat(fullName.c_str(), &statInfo) != 0) {
1905
0
                                                    int err = errno;
1906
0
                                                    string msg = "Unable to stat file '" + fullName + "': " + stringerror(err);
1907
0
                                                    SLOG(g_log << Logger::Error << msg << std::endl,
1908
0
                                                         d_log->error(Logr::Error, err, "Unable to stat file", "name", Logging::Loggable(fullName)));
1909
0
                                                    throw PDNSException(std::move(msg));
1910
0
                                                  }
1911
0
                                                  if (!S_ISREG(statInfo.st_mode)) {
1912
0
                                                    string msg = "File '" + fullName + "' is not a regular file";
1913
0
                                                    SLOG(g_log << Logger::Error << msg << std::endl,
1914
0
                                                         d_log->info(Logr::Error, "File is not a regular file", "name", Logging::Loggable(fullName)));
1915
0
                                                    throw PDNSException(std::move(msg));
1916
0
                                                  }
1917
0
                                                  results.emplace_back(fullName);
1918
0
                                                }
1919
0
                                                return true;
1920
0
                                              });
1921
1922
0
  if (directoryError) {
1923
0
    int err = errno;
1924
0
    string msg = directory + " is not accessible: " + stringerror(err);
1925
0
    SLOG(g_log << Logger::Error << msg << std::endl,
1926
0
         d_log->error(Logr::Error, err, "Directory is not accessible", "name", Logging::Loggable(directory)));
1927
0
    throw PDNSException(std::move(msg));
1928
0
  }
1929
1930
0
  return results;
1931
0
}
1932
#endif
1933
1934
UniqueFilePtr openFileForWriting(const std::string& filePath, mode_t permissions, bool mustNotExist, bool appendIfExists)
1935
0
{
1936
0
  int flags = O_WRONLY | O_CREAT;
1937
0
  if (mustNotExist) {
1938
0
    flags |= O_EXCL;
1939
0
  }
1940
0
  else if (appendIfExists) {
1941
0
    flags |= O_APPEND;
1942
0
  }
1943
0
  int fileDesc = open(filePath.c_str(), flags, permissions);
1944
0
  if (fileDesc == -1) {
1945
0
    return {};
1946
0
  }
1947
0
  auto filePtr = pdns::UniqueFilePtr(fdopen(fileDesc, appendIfExists ? "a" : "w"));
1948
0
  if (!filePtr) {
1949
0
    auto error = errno;
1950
0
    close(fileDesc);
1951
0
    errno = error;
1952
0
    return {};
1953
0
  }
1954
0
  return filePtr;
1955
0
}
1956
1957
}
1958
1959
const char* timestamp(time_t arg, timebuf_t& buf)
1960
0
{
1961
0
  const std::string s_timestampFormat = "%Y-%m-%dT%T";
1962
0
  struct tm tmval{};
1963
0
  size_t len = strftime(buf.data(), buf.size(), s_timestampFormat.c_str(), localtime_r(&arg, &tmval));
1964
0
  if (len == 0) {
1965
0
    int ret = snprintf(buf.data(), buf.size(), "%lld", static_cast<long long>(arg));
1966
0
    if (ret < 0 || static_cast<size_t>(ret) >= buf.size()) {
1967
0
      buf[0] = '\0';
1968
0
    }
1969
0
  }
1970
0
  return buf.data();
1971
0
}
1972
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-reinterpret-cast)