Coverage Report

Created: 2026-01-06 06:20

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