Coverage Report

Created: 2026-03-08 06:22

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