Coverage Report

Created: 2026-06-09 07:00

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