Coverage Report

Created: 2025-08-28 07:00

/src/pdns/pdns/misc.hh
Line
Count
Source (jump to first uncovered line)
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
#pragma once
23
#include <cinttypes>
24
#include <cstring>
25
#include <cstdio>
26
#include <regex.h>
27
#include <climits>
28
#include <type_traits>
29
30
#include <boost/algorithm/string.hpp>
31
32
#include "dns.hh"
33
#include <atomic>
34
#include <sys/time.h>
35
#include <sys/types.h>
36
#include <sys/socket.h>
37
#include <ctime>
38
#include <syslog.h>
39
#include <stdexcept>
40
#include <string>
41
#include <cctype>
42
#include <utility>
43
#include <vector>
44
45
#include "namespaces.hh"
46
47
class DNSName;
48
#if defined(PDNS_AUTH)
49
class ZoneName;
50
#else
51
using ZoneName = DNSName;
52
#endif
53
54
// Do not change to "using TSIGHashEnum ..." until you know CodeQL does not choke on it
55
typedef enum
56
{
57
  TSIG_MD5,
58
  TSIG_SHA1,
59
  TSIG_SHA224,
60
  TSIG_SHA256,
61
  TSIG_SHA384,
62
  TSIG_SHA512,
63
  TSIG_GSS,
64
} TSIGHashEnum;
65
66
namespace pdns
67
{
68
/**
69
 * \brief Retrieves the errno-based error message in a reentrant way.
70
 *
71
 * This internally handles the portability issues around using
72
 * `strerror_r` and returns a `std::string` that owns the error
73
 * message's contents.
74
 *
75
 * \param[in] errnum The errno value.
76
 *
77
 * \return The `std::string` error message.
78
 */
79
auto getMessageFromErrno(int errnum) -> std::string;
80
81
#if defined(HAVE_LIBCRYPTO)
82
namespace OpenSSL
83
{
84
  /**
85
   * \brief Throws a `std::runtime_error` with the current OpenSSL error.
86
   *
87
   * \param[in] errorMessage The message to attach in addition to the OpenSSL error.
88
   */
89
  [[nodiscard]] auto error(const std::string& errorMessage) -> std::runtime_error;
90
91
  /**
92
   * \brief Throws a `std::runtime_error` with a name and the current OpenSSL error.
93
   *
94
   * \param[in] componentName The name of the component to mark the error message with.
95
   * \param[in] errorMessage The message to attach in addition to the OpenSSL error.
96
   */
97
  [[nodiscard]] auto error(const std::string& componentName, const std::string& errorMessage) -> std::runtime_error;
98
}
99
#endif // HAVE_LIBCRYPTO
100
}
101
102
string nowTime();
103
string unquotify(const string &item);
104
string humanDuration(time_t passed);
105
void stripLine(string &line);
106
std::optional<string> getHostname();
107
std::string getCarbonHostName();
108
string urlEncode(const string &text);
109
int waitForData(int fileDesc, int seconds, int mseconds = 0);
110
int waitForData(int fileDesc, struct timeval timeout);
111
int waitForMultiData(const set<int>& fds, const int seconds, const int mseconds, int* fd);
112
int waitForRWData(int fileDesc, bool waitForRead, int seconds, int mseconds, bool* error = nullptr, bool* disconnected = nullptr);
113
int waitForRWData(int fileDesc, bool waitForRead, struct timeval timeout, bool* error = nullptr, bool* disconnected = nullptr);
114
bool getTSIGHashEnum(const DNSName& algoName, TSIGHashEnum& algoEnum);
115
DNSName getTSIGAlgoName(TSIGHashEnum& algoEnum);
116
117
int logFacilityToLOG(unsigned int facility);
118
std::optional<int> logFacilityFromString(std::string facilityStr);
119
120
template<typename Container>
121
void
122
stringtok (Container &container, string const &in,
123
           const char * const delimiters = " \t\n")
124
27.8k
{
125
27.8k
  const string::size_type len = in.length();
126
27.8k
  string::size_type i = 0;
127
128
4.59M
  while (i<len) {
129
    // eat leading whitespace
130
4.59M
    i = in.find_first_not_of (delimiters, i);
131
4.59M
    if (i == string::npos)
132
0
      return;   // nothing left but white space
133
134
    // find the end of the token
135
4.59M
    string::size_type j = in.find_first_of (delimiters, i);
136
137
    // push token
138
4.59M
    if (j == string::npos) {
139
26.9k
      container.push_back (in.substr(i));
140
26.9k
      return;
141
26.9k
    } else
142
4.56M
      container.push_back (in.substr(i, j-i));
143
144
    // set up for next loop
145
4.56M
    i = j + 1;
146
4.56M
  }
147
27.8k
}
148
149
template<typename T> bool rfc1982LessThan(T lhs, T rhs)
150
{
151
  static_assert(std::is_unsigned_v<T>, "rfc1982LessThan only works for unsigned types");
152
  return static_cast<std::make_signed_t<T>>(lhs - rhs) < 0;
153
}
154
155
template<typename T> bool rfc1982LessThanOrEqual(T lhs, T rhs)
156
{
157
  static_assert(std::is_unsigned_v<T>, "rfc1982LessThanOrEqual only works for unsigned types");
158
  return static_cast<std::make_signed_t<T>>(lhs - rhs) <= 0;
159
}
160
161
// fills container with ranges, so {posbegin,posend}
162
template <typename Container>
163
void
164
vstringtok (Container &container, string const &in,
165
           const char * const delimiters = " \t\n")
166
1.52M
{
167
1.52M
  const string::size_type len = in.length();
168
1.52M
  string::size_type i = 0;
169
170
48.4M
  while (i<len) {
171
    // eat leading whitespace
172
47.2M
    i = in.find_first_not_of (delimiters, i);
173
47.2M
    if (i == string::npos)
174
0
      return;   // nothing left but white space
175
176
    // find the end of the token
177
47.2M
    string::size_type j = in.find_first_of (delimiters, i);
178
179
    // push token
180
47.2M
    if (j == string::npos) {
181
317k
      container.emplace_back(i, len);
182
317k
      return;
183
317k
    } else
184
46.9M
      container.emplace_back(i, j);
185
186
    // set up for next loop
187
46.9M
    i = j + 1;
188
46.9M
  }
189
1.52M
}
Unexecuted instantiation: void vstringtok<std::__1::vector<std::__1::pair<unsigned int, unsigned int>, std::__1::allocator<std::__1::pair<unsigned int, unsigned int> > > >(std::__1::vector<std::__1::pair<unsigned int, unsigned int>, std::__1::allocator<std::__1::pair<unsigned int, unsigned int> > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, char const*)
void vstringtok<std::__1::deque<std::__1::pair<unsigned long, unsigned long>, std::__1::allocator<std::__1::pair<unsigned long, unsigned long> > > >(std::__1::deque<std::__1::pair<unsigned long, unsigned long>, std::__1::allocator<std::__1::pair<unsigned long, unsigned long> > >&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, char const*)
Line
Count
Source
166
1.52M
{
167
1.52M
  const string::size_type len = in.length();
168
1.52M
  string::size_type i = 0;
169
170
48.4M
  while (i<len) {
171
    // eat leading whitespace
172
47.2M
    i = in.find_first_not_of (delimiters, i);
173
47.2M
    if (i == string::npos)
174
0
      return;   // nothing left but white space
175
176
    // find the end of the token
177
47.2M
    string::size_type j = in.find_first_of (delimiters, i);
178
179
    // push token
180
47.2M
    if (j == string::npos) {
181
317k
      container.emplace_back(i, len);
182
317k
      return;
183
317k
    } else
184
46.9M
      container.emplace_back(i, j);
185
186
    // set up for next loop
187
46.9M
    i = j + 1;
188
46.9M
  }
189
1.52M
}
190
191
size_t writen2(int fd, const void *buf, size_t count);
192
0
inline size_t writen2(int fd, const std::string &s) { return writen2(fd, s.data(), s.size()); }
193
size_t readn2(int fileDesc, void* buffer, size_t len);
194
size_t readn2WithTimeout(int fd, void* buffer, size_t len, const struct timeval& idleTimeout, const struct timeval& totalTimeout={0,0}, bool allowIncomplete=false);
195
size_t writen2WithTimeout(int fd, const void * buffer, size_t len, const struct timeval& timeout);
196
197
void toLowerInPlace(string& str);
198
const string toLower(const string &upper);
199
const string toLowerCanonic(const string &upper);
200
bool IpToU32(const string &str, uint32_t *ip);
201
string U32ToIP(uint32_t);
202
203
inline string stringerror(int err = errno)
204
0
{
205
0
  return pdns::getMessageFromErrno(err);
206
0
}
207
208
void dropPrivs(int uid, int gid);
209
void cleanSlashes(string &str);
210
211
#if defined(_POSIX_THREAD_CPUTIME) && defined(CLOCK_THREAD_CPUTIME_ID)
212
/** CPUTime measurements */
213
class CPUTime
214
{
215
public:
216
  void start()
217
0
  {
218
0
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &d_start);
219
0
  }
220
  uint64_t ndiff()
221
0
  {
222
0
    struct timespec now;
223
0
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
224
0
    return 1000000000ULL*(now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec);
225
0
  }
226
private:
227
  struct timespec d_start;
228
};
229
#endif
230
231
/** The DTime class can be used for timing statistics with microsecond resolution.
232
On 32 bits systems this means that 2147 seconds is the longest time that can be measured. */
233
class DTime
234
{
235
public:
236
  //!< Does not set the timer for you! Saves lots of gettimeofday() calls
237
  DTime() = default;
238
  DTime(const DTime &dt) = default;
239
  DTime & operator=(const DTime &dt) = default;
240
  inline time_t time() const;
241
  inline void set();  //!< Reset the timer
242
  inline int udiff(bool reset = true); //!< Return the number of microseconds since the timer was last set.
243
244
  int udiffNoReset() //!< Return the number of microseconds since the timer was last set.
245
0
  {
246
0
    return udiff(false);
247
0
  }
248
  void setTimeval(const struct timeval& tv)
249
0
  {
250
0
    d_set=tv;
251
0
  }
252
  struct timeval getTimeval() const
253
0
  {
254
0
    return d_set;
255
0
  }
256
private:
257
struct timeval d_set{0, 0};
258
};
259
260
inline time_t DTime::time() const
261
0
{
262
0
  return d_set.tv_sec;
263
0
}
264
265
inline void DTime::set()
266
0
{
267
0
  gettimeofday(&d_set, nullptr);
268
0
}
269
270
inline int DTime::udiff(bool reset)
271
0
{
272
0
  struct timeval now;
273
0
  gettimeofday(&now, nullptr);
274
0
275
0
  int ret=1000000*(now.tv_sec-d_set.tv_sec)+(now.tv_usec-d_set.tv_usec);
276
0
277
0
  if (reset) {
278
0
    d_set = now;
279
0
  }
280
0
281
0
  return ret;
282
0
}
283
284
inline void toLowerInPlace(string& str)
285
0
{
286
0
  const size_t length = str.length();
287
0
  char c;
288
0
  for (size_t i = 0; i < length; ++i) {
289
0
    c = dns_tolower(str[i]);
290
0
    if (c != str[i]) {
291
0
      str[i] = c;
292
0
    }
293
0
  }
294
0
}
295
296
inline const string toLower(const string &upper)
297
0
{
298
0
  string reply(upper);
299
0
300
0
  toLowerInPlace(reply);
301
0
302
0
  return reply;
303
0
}
304
305
inline const string toLowerCanonic(const string &upper)
306
0
{
307
0
  string reply(upper);
308
0
  if (!reply.empty()) {
309
0
    const auto length = reply.length();
310
0
    if (reply[length - 1] == '.') {
311
0
      reply.resize(length - 1);
312
0
    }
313
0
    toLowerInPlace(reply);
314
0
  }
315
0
  return reply;
316
0
}
317
318
// Make s uppercase:
319
inline string toUpper( const string& s )
320
158k
{
321
158k
  string r(s);
322
298M
  for (size_t i = 0; i < s.length(); ++i) {
323
298M
    r[i] = dns_toupper(r[i]);
324
298M
  }
325
158k
  return r;
326
158k
}
327
328
inline double getTime()
329
0
{
330
0
  struct timeval now;
331
0
  gettimeofday(&now,0);
332
0
333
0
  return now.tv_sec+now.tv_usec/1000000.0;
334
0
}
335
336
[[noreturn]] inline void unixDie(const string &why)
337
0
{
338
0
  throw runtime_error(why + ": " + stringerror(errno));
339
0
}
340
341
string makeHexDump(const string& str, const string& sep = " ");
342
//! Convert the hexstring in to a byte string
343
string makeBytesFromHex(const string &in);
344
345
void normalizeTV(struct timeval& tv);
346
struct timeval operator+(const struct timeval& lhs, const struct timeval& rhs);
347
struct timeval operator-(const struct timeval& lhs, const struct timeval& rhs);
348
349
inline float makeFloat(const struct timeval& tv)
350
0
{
351
0
  return tv.tv_sec + tv.tv_usec/1000000.0f;
352
0
}
353
inline uint64_t uSec(const struct timeval& tv)
354
0
{
355
0
  return tv.tv_sec * 1000000 + tv.tv_usec;
356
0
}
357
358
inline bool operator<(const struct timeval& lhs, const struct timeval& rhs)
359
0
{
360
0
  return std::tie(lhs.tv_sec, lhs.tv_usec) < std::tie(rhs.tv_sec, rhs.tv_usec);
361
0
}
362
inline bool operator<=(const struct timeval& lhs, const struct timeval& rhs)
363
0
{
364
0
  return std::tie(lhs.tv_sec, lhs.tv_usec) <= std::tie(rhs.tv_sec, rhs.tv_usec);
365
0
}
366
367
inline bool operator<(const struct timespec& lhs, const struct timespec& rhs)
368
0
{
369
0
  return std::tie(lhs.tv_sec, lhs.tv_nsec) < std::tie(rhs.tv_sec, rhs.tv_nsec);
370
0
}
371
372
373
inline int pdns_ilexicographical_compare_three_way(std::string_view a, std::string_view b)  __attribute__((pure));
374
inline int pdns_ilexicographical_compare_three_way(std::string_view a, std::string_view b)
375
187k
{
376
187k
  const unsigned char *aPtr = (const unsigned char*)a.data(), *bPtr = (const unsigned char*)b.data();
377
187k
  const unsigned char *aEptr = aPtr + a.length(), *bEptr = bPtr + b.length();
378
773k
  while(aPtr != aEptr && bPtr != bEptr) {
379
612k
    if (*aPtr != *bPtr) {
380
329k
      if (int rc = dns_tolower(*aPtr) - dns_tolower(*bPtr); rc != 0) {
381
26.6k
        return rc;
382
26.6k
      }
383
329k
    }
384
585k
    aPtr++;
385
585k
    bPtr++;
386
585k
  }
387
  // At this point, one of the strings has been completely processed.
388
  // Either both have the same length, and they are equal, or one of them
389
  // is larger, and compares as higher.
390
160k
  if (aPtr == aEptr) {
391
160k
    if (bPtr != bEptr) {
392
0
      return -1; // a < b
393
0
    }
394
160k
  }
395
0
  else {
396
0
    return 1; // a > b
397
0
  }
398
160k
  return 0; // a == b
399
160k
}
400
401
inline bool pdns_ilexicographical_compare(const std::string& a, const std::string& b)  __attribute__((pure));
402
inline bool pdns_ilexicographical_compare(const std::string& a, const std::string& b)
403
0
{
404
0
  return pdns_ilexicographical_compare_three_way(a, b) < 0;
405
0
}
406
407
inline bool pdns_iequals(const std::string& a, const std::string& b) __attribute__((pure));
408
inline bool pdns_iequals(const std::string& a, const std::string& b)
409
487k
{
410
487k
  if (a.length() != b.length())
411
300k
    return false;
412
413
187k
  return pdns_ilexicographical_compare_three_way(a, b) == 0;
414
487k
}
415
416
inline bool pdns_iequals_ch(const char a, const char b) __attribute__((pure));
417
inline bool pdns_iequals_ch(const char a, const char b)
418
0
{
419
0
  if ((a != b) && (dns_tolower(a) != dns_tolower(b)))
420
0
    return false;
421
0
422
0
  return true;
423
0
}
424
425
426
typedef unsigned long AtomicCounterInner;
427
typedef std::atomic<AtomicCounterInner> AtomicCounter ;
428
429
// FIXME400 this should probably go?
430
struct CIStringCompare
431
{
432
  bool operator()(const string& a, const string& b) const
433
0
  {
434
0
    return pdns_ilexicographical_compare(a, b);
435
0
  }
436
};
437
438
struct CIStringComparePOSIX
439
{
440
   bool operator() (const std::string& lhs, const std::string& rhs) const
441
0
   {
442
0
      const std::locale &loc = std::locale("POSIX");
443
0
      auto lhsIter = lhs.begin();
444
0
      auto rhsIter = rhs.begin();
445
0
      while (lhsIter != lhs.end()) {
446
0
        if (rhsIter == rhs.end() || std::tolower(*rhsIter,loc) < std::tolower(*lhsIter,loc)) {
447
0
          return false;
448
0
        }
449
0
        if (std::tolower(*lhsIter,loc) < std::tolower(*rhsIter,loc)) {
450
0
          return true;
451
0
        }
452
0
        ++lhsIter;++rhsIter;
453
0
      }
454
0
      return rhsIter != rhs.end();
455
0
   }
456
};
457
458
struct CIStringPairCompare
459
{
460
  bool operator()(const pair<string, uint16_t>& a, const pair<string, uint16_t>& b) const
461
0
  {
462
0
    if(pdns_ilexicographical_compare(a.first, b.first))
463
0
  return true;
464
0
    if(pdns_ilexicographical_compare(b.first, a.first))
465
0
  return false;
466
0
    return a.second < b.second;
467
0
  }
468
};
469
470
inline size_t pdns_ci_find(const string& haystack, const string& needle)
471
0
{
472
0
  string::const_iterator it = std::search(haystack.begin(), haystack.end(),
473
0
    needle.begin(), needle.end(), pdns_iequals_ch);
474
0
  if (it == haystack.end()) {
475
0
    // not found
476
0
    return string::npos;
477
0
  } else {
478
0
    return it - haystack.begin();
479
0
  }
480
0
}
481
482
pair<string, string> splitField(const string& inp, char sepa);
483
484
inline bool isCanonical(std::string_view qname)
485
181k
{
486
181k
  return boost::ends_with(qname, ".");
487
181k
}
488
489
inline DNSName toCanonic(const ZoneName& zone, const string& qname)
490
33.4k
{
491
33.4k
  if(qname.size()==1 && qname[0]=='@')
492
1.60k
    return DNSName(zone);
493
31.8k
  if(isCanonical(qname))
494
2.10k
    return DNSName(qname);
495
29.7k
  return DNSName(qname) += DNSName(zone);
496
31.8k
}
497
498
string stripDot(const string& dom);
499
500
int makeIPv6sockaddr(const std::string& addr, struct sockaddr_in6* ret);
501
int makeIPv4sockaddr(const std::string& str, struct sockaddr_in* ret);
502
int makeUNsockaddr(const std::string& path, struct sockaddr_un* ret);
503
bool stringfgets(FILE* fp, std::string& line);
504
505
template<typename Index>
506
std::pair<typename Index::iterator,bool>
507
replacing_insert(Index& i,const typename Index::value_type& x)
508
{
509
  std::pair<typename Index::iterator,bool> res=i.insert(x);
510
  if(!res.second)res.second=i.replace(res.first,x);
511
  return res;
512
}
513
514
/** very small regex wrapper */
515
class Regex
516
{
517
public:
518
  /** constructor that accepts the expression to regex */
519
  Regex(const string &expr);
520
521
  ~Regex()
522
0
  {
523
0
    regfree(&d_preg);
524
0
  }
525
  /** call this to find out if 'line' matches your expression */
526
  bool match(const string &line) const
527
0
  {
528
0
    return regexec(&d_preg,line.c_str(),0,0,0)==0;
529
0
  }
530
  bool match(const DNSName& name) const
531
0
  {
532
0
    return match(name.toStringNoDot());
533
0
  }
534
535
private:
536
  regex_t d_preg;
537
};
538
539
class SimpleMatch
540
{
541
public:
542
  SimpleMatch(string mask, bool caseFold = false) :
543
    d_mask(std::move(mask)), d_fold(caseFold)
544
0
  {
545
0
  }
546
547
  bool match(string::const_iterator mi, string::const_iterator mend, string::const_iterator vi, string::const_iterator vend) const
548
0
  {
549
0
    for(;;++mi) {
550
0
      if (mi == mend) {
551
0
        return vi == vend;
552
0
      } else if (*mi == '?') {
553
0
        if (vi == vend) return false;
554
0
        ++vi;
555
0
      } else if (*mi == '*') {
556
0
        while(mi != mend && *mi == '*') ++mi;
557
0
        if (mi == mend) return true;
558
0
        while(vi != vend) {
559
0
          if (match(mi,mend,vi,vend)) return true;
560
0
          ++vi;
561
0
        }
562
0
        return false;
563
0
      } else {
564
0
        if ((mi == mend && vi != vend)||
565
0
            (mi != mend && vi == vend)) return false;
566
0
        if (d_fold) {
567
0
          if (dns_tolower(*mi) != dns_tolower(*vi)) return false;
568
0
        } else {
569
0
          if (*mi != *vi) return false;
570
0
        }
571
0
        ++vi;
572
0
      }
573
0
    }
574
0
  }
575
576
0
  bool match(const string& value) const {
577
0
    return match(d_mask.begin(), d_mask.end(), value.begin(), value.end());
578
0
  }
579
580
0
  bool match(const DNSName& name) const {
581
0
    return match(name.toStringNoDot());
582
0
  }
583
584
#if defined(PDNS_AUTH) // [
585
0
  bool match(const ZoneName& name) const {
586
0
    return match(name.toStringNoDot());
587
0
  }
588
#endif // ]
589
590
private:
591
  const string d_mask;
592
  const bool d_fold;
593
};
594
595
union ComboAddress;
596
597
// An aligned type to hold cmsgbufs. See https://man.openbsd.org/CMSG_DATA
598
typedef union { struct cmsghdr hdr; char buf[256]; } cmsgbuf_aligned;
599
600
/* itfIndex is an interface index, as returned by if_nametoindex(). 0 means default. */
601
void addCMsgSrcAddr(struct msghdr* msgh, cmsgbuf_aligned* cbuf, const ComboAddress* source, int itfIndex);
602
603
unsigned int getFilenumLimit(bool hardOrSoft=0);
604
void setFilenumLimit(unsigned int lim);
605
bool readFileIfThere(const char* fname, std::string* line);
606
bool setSocketTimestamps(int fd);
607
608
//! Sets the socket into blocking mode.
609
bool setBlocking( int sock );
610
611
void setDscp(int sock, unsigned short family, uint8_t dscp);
612
613
//! Sets the socket into non-blocking mode.
614
bool setNonBlocking( int sock );
615
bool setTCPNoDelay(int sock);
616
bool setReuseAddr(int sock);
617
bool isNonBlocking(int sock);
618
bool setReceiveSocketErrors(int sock, int af);
619
int closesocket(int socket);
620
bool setCloseOnExec(int sock);
621
622
size_t getPipeBufferSize(int fd);
623
bool setPipeBufferSize(int fd, size_t size);
624
625
uint64_t udpErrorStats(const std::string& str);
626
uint64_t udp6ErrorStats(const std::string& str);
627
uint64_t tcpErrorStats(const std::string& str);
628
uint64_t getRealMemoryUsage(const std::string&);
629
uint64_t getSpecialMemoryUsage(const std::string&);
630
uint64_t getOpenFileDescriptors(const std::string&);
631
uint64_t getCPUTimeUser(const std::string&);
632
uint64_t getCPUTimeSystem(const std::string&);
633
uint64_t getCPUIOWait(const std::string&);
634
uint64_t getCPUSteal(const std::string&);
635
std::string getMACAddress(const ComboAddress& ca);
636
int getMACAddress(const ComboAddress& ca, char* dest, size_t len);
637
638
template<typename T>
639
const T& defTer(const T& a, const T& b)
640
{
641
  return a ? a : b;
642
}
643
644
template<typename P, typename T>
645
T valueOrEmpty(const P val) {
646
  if (!val) return T{};
647
  return T(val);
648
}
649
650
651
// I'm not very OCD, but I appreciate loglines like "processing 1 delta", "processing 2 deltas" :-)
652
template <typename Integer,
653
typename std::enable_if_t<std::is_integral_v<Integer>, bool> = true>
654
const char* addS(Integer siz, const char* singular = "", const char *plural = "s")
655
{
656
  if (siz == 1) {
657
    return singular;
658
  }
659
  return plural;
660
}
661
662
template <typename C,
663
typename std::enable_if_t<std::is_class_v<C>, bool> = true>
664
const char* addS(const C& c, const char* singular = "", const char *plural = "s")
665
{
666
  return addS(c.size(), singular, plural);
667
}
668
669
template<typename C>
670
const typename C::value_type::second_type* rplookup(const C& c, const typename C::value_type::first_type& key)
671
{
672
  auto fnd = c.find(key);
673
  if(fnd == c.end())
674
    return 0;
675
  return &fnd->second;
676
}
677
678
double DiffTime(const struct timespec& first, const struct timespec& second);
679
double DiffTime(const struct timeval& first, const struct timeval& second);
680
uid_t strToUID(const string &str);
681
gid_t strToGID(const string &str);
682
683
namespace pdns
684
{
685
/**
686
 * \brief Does a checked conversion from one integer type to another.
687
 *
688
 * \warning The source type `F` and target type `T` must have the same
689
 * signedness, otherwise a compilation error is thrown.
690
 *
691
 * \exception std::out_of_range Thrown if the source value does not fit
692
 * in the target type.
693
 *
694
 * \param[in] from The source value of type `F`.
695
 *
696
 * \return The target value of type `T`.
697
 */
698
template <typename T, typename F>
699
auto checked_conv(F from) -> T
700
10.9k
{
701
10.9k
  static_assert(std::numeric_limits<F>::is_integer, "checked_conv: The `F` type must be an integer");
702
10.9k
  static_assert(std::numeric_limits<T>::is_integer, "checked_conv: The `T` type must be an integer");
703
10.9k
  static_assert((std::numeric_limits<F>::is_signed && std::numeric_limits<T>::is_signed) || (!std::numeric_limits<F>::is_signed && !std::numeric_limits<T>::is_signed),
704
10.9k
                "checked_conv: The `T` and `F` types must either both be signed or unsigned");
705
706
10.9k
  constexpr auto tMin = std::numeric_limits<T>::min();
707
  if constexpr (std::numeric_limits<F>::min() != tMin) {
708
    if (from < tMin) {
709
      string s = "checked_conv: source value " + std::to_string(from) + " is smaller than target's minimum possible value " + std::to_string(tMin);
710
      throw std::out_of_range(s);
711
    }
712
  }
713
714
10.9k
  constexpr auto tMax = std::numeric_limits<T>::max();
715
10.9k
  if constexpr (std::numeric_limits<F>::max() != tMax) {
716
10.9k
    if (from > tMax) {
717
160
      string s = "checked_conv: source value " + std::to_string(from) + " is larger than target's maximum possible value " + std::to_string(tMax);
718
160
      throw std::out_of_range(s);
719
160
    }
720
10.9k
  }
721
722
10.8k
  return static_cast<T>(from);
723
10.9k
}
Unexecuted instantiation: unsigned char pdns::checked_conv<unsigned char, unsigned long long>(unsigned long long)
unsigned short pdns::checked_conv<unsigned short, unsigned long long>(unsigned long long)
Line
Count
Source
700
2.80k
{
701
2.80k
  static_assert(std::numeric_limits<F>::is_integer, "checked_conv: The `F` type must be an integer");
702
2.80k
  static_assert(std::numeric_limits<T>::is_integer, "checked_conv: The `T` type must be an integer");
703
2.80k
  static_assert((std::numeric_limits<F>::is_signed && std::numeric_limits<T>::is_signed) || (!std::numeric_limits<F>::is_signed && !std::numeric_limits<T>::is_signed),
704
2.80k
                "checked_conv: The `T` and `F` types must either both be signed or unsigned");
705
706
2.80k
  constexpr auto tMin = std::numeric_limits<T>::min();
707
  if constexpr (std::numeric_limits<F>::min() != tMin) {
708
    if (from < tMin) {
709
      string s = "checked_conv: source value " + std::to_string(from) + " is smaller than target's minimum possible value " + std::to_string(tMin);
710
      throw std::out_of_range(s);
711
    }
712
  }
713
714
2.80k
  constexpr auto tMax = std::numeric_limits<T>::max();
715
2.80k
  if constexpr (std::numeric_limits<F>::max() != tMax) {
716
2.80k
    if (from > tMax) {
717
86
      string s = "checked_conv: source value " + std::to_string(from) + " is larger than target's maximum possible value " + std::to_string(tMax);
718
86
      throw std::out_of_range(s);
719
86
    }
720
2.80k
  }
721
722
2.72k
  return static_cast<T>(from);
723
2.80k
}
unsigned int pdns::checked_conv<unsigned int, unsigned long long>(unsigned long long)
Line
Count
Source
700
8.19k
{
701
8.19k
  static_assert(std::numeric_limits<F>::is_integer, "checked_conv: The `F` type must be an integer");
702
8.19k
  static_assert(std::numeric_limits<T>::is_integer, "checked_conv: The `T` type must be an integer");
703
8.19k
  static_assert((std::numeric_limits<F>::is_signed && std::numeric_limits<T>::is_signed) || (!std::numeric_limits<F>::is_signed && !std::numeric_limits<T>::is_signed),
704
8.19k
                "checked_conv: The `T` and `F` types must either both be signed or unsigned");
705
706
8.19k
  constexpr auto tMin = std::numeric_limits<T>::min();
707
  if constexpr (std::numeric_limits<F>::min() != tMin) {
708
    if (from < tMin) {
709
      string s = "checked_conv: source value " + std::to_string(from) + " is smaller than target's minimum possible value " + std::to_string(tMin);
710
      throw std::out_of_range(s);
711
    }
712
  }
713
714
8.19k
  constexpr auto tMax = std::numeric_limits<T>::max();
715
8.19k
  if constexpr (std::numeric_limits<F>::max() != tMax) {
716
8.19k
    if (from > tMax) {
717
74
      string s = "checked_conv: source value " + std::to_string(from) + " is larger than target's maximum possible value " + std::to_string(tMax);
718
74
      throw std::out_of_range(s);
719
74
    }
720
8.19k
  }
721
722
8.11k
  return static_cast<T>(from);
723
8.19k
}
724
725
/**
726
 * \brief Performs a conversion from `std::string&` to integer.
727
 *
728
 * This function internally calls `std::stoll` and `std::stoull` to do
729
 * the conversion from `std::string&` and calls `pdns::checked_conv` to
730
 * do the checked conversion from `long long`/`unsigned long long` to
731
 * `T`.
732
 *
733
 * \warning The target type `T` must be an integer, otherwise a
734
 * compilation error is thrown.
735
 *
736
 * \exception std:stoll Throws what std::stoll throws.
737
 *
738
 * \exception std::stoull Throws what std::stoull throws.
739
 *
740
 * \exception pdns::checked_conv Throws what pdns::checked_conv throws.
741
 *
742
 * \param[in] str The input string to be converted.
743
 *
744
 * \param[in] idx Location to store the index at which processing
745
 * stopped. If the input `str` is empty, `*idx` shall be set to 0.
746
 *
747
 * \param[in] base The numerical base for conversion.
748
 *
749
 * \return `str` converted to integer `T`, or 0 if `str` is empty.
750
 */
751
template <typename T>
752
auto checked_stoi(const std::string& str, size_t* idx = nullptr, int base = 10) -> T
753
11.7k
{
754
11.7k
  static_assert(std::numeric_limits<T>::is_integer, "checked_stoi: The `T` type must be an integer");
755
756
11.7k
  if (str.empty()) {
757
652
    if (idx != nullptr) {
758
0
      *idx = 0;
759
0
    }
760
761
652
    return 0; // compatibility
762
652
  }
763
764
11.1k
  if constexpr (std::is_unsigned_v<T>) {
765
11.1k
    return pdns::checked_conv<T>(std::stoull(str, idx, base));
766
  }
767
  else {
768
    return pdns::checked_conv<T>(std::stoll(str, idx, base));
769
  }
770
11.1k
}
Unexecuted instantiation: unsigned char pdns::checked_stoi<unsigned char>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long*, int)
unsigned short pdns::checked_stoi<unsigned short>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long*, int)
Line
Count
Source
753
3.46k
{
754
3.46k
  static_assert(std::numeric_limits<T>::is_integer, "checked_stoi: The `T` type must be an integer");
755
756
3.46k
  if (str.empty()) {
757
652
    if (idx != nullptr) {
758
0
      *idx = 0;
759
0
    }
760
761
652
    return 0; // compatibility
762
652
  }
763
764
2.81k
  if constexpr (std::is_unsigned_v<T>) {
765
2.81k
    return pdns::checked_conv<T>(std::stoull(str, idx, base));
766
  }
767
  else {
768
    return pdns::checked_conv<T>(std::stoll(str, idx, base));
769
  }
770
2.81k
}
unsigned int pdns::checked_stoi<unsigned int>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long*, int)
Line
Count
Source
753
8.32k
{
754
8.32k
  static_assert(std::numeric_limits<T>::is_integer, "checked_stoi: The `T` type must be an integer");
755
756
8.32k
  if (str.empty()) {
757
0
    if (idx != nullptr) {
758
0
      *idx = 0;
759
0
    }
760
761
0
    return 0; // compatibility
762
0
  }
763
764
8.32k
  if constexpr (std::is_unsigned_v<T>) {
765
8.32k
    return pdns::checked_conv<T>(std::stoull(str, idx, base));
766
  }
767
  else {
768
    return pdns::checked_conv<T>(std::stoll(str, idx, base));
769
  }
770
8.32k
}
771
772
/**
773
 * \brief Performs a conversion from `std::string&` to integer.
774
 *
775
 * This function internally calls `pdns::checked_stoi` and stores its
776
 * result in `out`.
777
 *
778
 * \exception pdns::checked_stoi Throws what pdns::checked_stoi throws.
779
 *
780
 * \param[out] out `str` converted to integer `T`, or 0 if `str` is
781
 * empty.
782
 *
783
 * \param[in] str The input string to be converted.
784
 *
785
 * \param[in] idx Location to store the index at which processing
786
 * stopped. If the input `str` is empty, `*idx` shall be set to 0.
787
 *
788
 * \param[in] base The numerical base for conversion.
789
 *
790
 * \return `str` converted to integer `T`, or 0 if `str` is empty.
791
 */
792
template <typename T>
793
auto checked_stoi_into(T& out, const std::string& str, size_t* idx = nullptr, int base = 10)
794
8.32k
{
795
8.32k
  out = checked_stoi<T>(str, idx, base);
796
8.32k
}
auto pdns::checked_stoi_into<unsigned int>(unsigned int&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long*, int)
Line
Count
Source
794
8.32k
{
795
8.32k
  out = checked_stoi<T>(str, idx, base);
796
8.32k
}
Unexecuted instantiation: auto pdns::checked_stoi_into<unsigned short>(unsigned short&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, unsigned long*, int)
797
}
798
799
bool isSettingThreadCPUAffinitySupported();
800
int mapThreadToCPUList(pthread_t tid, const std::set<int>& cpus);
801
802
std::vector<ComboAddress> getResolvers(const std::string& resolvConfPath);
803
804
DNSName reverseNameFromIP(const ComboAddress& ip);
805
806
size_t parseRFC1035CharString(const std::string &in, std::string &val); // from ragel
807
size_t parseSVCBValueListFromParsedRFC1035CharString(const std::string &in, vector<std::string> &val); // from ragel
808
size_t parseSVCBValueList(const std::string &in, vector<std::string> &val);
809
810
std::string makeLuaString(const std::string& in);
811
812
bool constantTimeStringEquals(const std::string& a, const std::string& b);
813
814
// Used in NID and L64 records
815
struct NodeOrLocatorID { uint8_t content[8]; };
816
817
struct FDWrapper
818
{
819
  FDWrapper() = default;
820
0
  FDWrapper(int desc): d_fd(desc) {}
821
  FDWrapper(const FDWrapper&) = delete;
822
  FDWrapper& operator=(const FDWrapper& rhs) = delete;
823
824
825
  ~FDWrapper()
826
0
  {
827
0
    reset();
828
0
  }
829
830
  FDWrapper(FDWrapper&& rhs) noexcept : d_fd(rhs.d_fd)
831
0
  {
832
0
    rhs.d_fd = -1;
833
0
  }
834
835
  FDWrapper& operator=(FDWrapper&& rhs) noexcept
836
0
  {
837
0
    if (d_fd >= 0) {
838
0
      close(d_fd);
839
0
    }
840
0
    d_fd = rhs.d_fd;
841
0
    rhs.d_fd = -1;
842
0
    return *this;
843
0
  }
844
845
  [[nodiscard]] int getHandle() const
846
0
  {
847
0
    return d_fd;
848
0
  }
849
850
  operator int() const
851
0
  {
852
0
    return d_fd;
853
0
  }
854
855
  int reset()
856
0
  {
857
0
    int ret = 0;
858
0
    if (d_fd >= 0) {
859
0
      ret = close(d_fd);
860
0
    }
861
0
    d_fd = -1;
862
0
    return ret;
863
0
  }
864
865
  int release()
866
0
  {
867
0
    auto ret = d_fd;
868
0
    d_fd = -1;
869
0
    return ret;
870
0
  }
871
872
private:
873
  int d_fd{-1};
874
};
875
876
namespace pdns
877
{
878
[[nodiscard]] std::optional<std::string> visit_directory(const std::string& directory, const std::function<bool(ino_t inodeNumber, const std::string_view& name)>& visitor);
879
880
struct FilePtrDeleter
881
{
882
  /* using a deleter instead of decltype(&fclose) has two big advantages:
883
     - the deleter is included in the type and does not have to be passed
884
       when creating a new object (easier to use, less memory usage, in theory
885
       better inlining)
886
     - we avoid the annoying "ignoring attributes on template argument ‘int (*)(FILE*)’"
887
       warning from the compiler, which is there because fclose is tagged as __nonnull((1))
888
  */
889
0
  void operator()(FILE* filePtr) const noexcept {
890
0
    fclose(filePtr);
891
0
  }
892
};
893
894
using UniqueFilePtr = std::unique_ptr<FILE, FilePtrDeleter>;
895
896
UniqueFilePtr openFileForWriting(const std::string& filePath, mode_t permissions, bool mustNotExist = true, bool appendIfExists = false);
897
}