• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 12247231368

10 Dec 2024 12:57AM UTC coverage: 66.418% (+1.6%) from 64.777%
12247231368

Pull #14948

github

web-flow
Merge 432a0b93c into d0a62cc2d
Pull Request #14948: use boost algorithms

26100 of 54734 branches covered (47.69%)

Branch coverage included in aggregate %.

87 of 101 new or added lines in 28 files covered. (86.14%)

2076 existing lines in 52 files now uncovered.

85923 of 113929 relevant lines covered (75.42%)

4735022.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.05
/pdns/misc.hh
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 <boost/range/algorithm/search.hpp>
24
#include <cinttypes>
25
#include <cstring>
26
#include <cstdio>
27
#include <regex.h>
28
#include <climits>
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 <cctype>
43
#include <vector>
44

45
#include "namespaces.hh"
46

47
class DNSName;
48

49
// Do not change to "using TSIGHashEnum ..." until you know CodeQL does not choke on it
50
typedef enum
51
{
52
  TSIG_MD5,
53
  TSIG_SHA1,
54
  TSIG_SHA224,
55
  TSIG_SHA256,
56
  TSIG_SHA384,
57
  TSIG_SHA512,
58
  TSIG_GSS,
59
} TSIGHashEnum;
60

61
namespace pdns
62
{
63
/**
64
 * \brief Retrieves the errno-based error message in a reentrant way.
65
 *
66
 * This internally handles the portability issues around using
67
 * `strerror_r` and returns a `std::string` that owns the error
68
 * message's contents.
69
 *
70
 * \param[in] errnum The errno value.
71
 *
72
 * \return The `std::string` error message.
73
 */
74
auto getMessageFromErrno(int errnum) -> std::string;
75

76
#if defined(HAVE_LIBCRYPTO)
77
namespace OpenSSL
78
{
79
  /**
80
   * \brief Throws a `std::runtime_error` with the current OpenSSL error.
81
   *
82
   * \param[in] errorMessage The message to attach in addition to the OpenSSL error.
83
   */
84
  [[nodiscard]] auto error(const std::string& errorMessage) -> std::runtime_error;
85

86
  /**
87
   * \brief Throws a `std::runtime_error` with a name and the current OpenSSL error.
88
   *
89
   * \param[in] componentName The name of the component to mark the error message with.
90
   * \param[in] errorMessage The message to attach in addition to the OpenSSL error.
91
   */
92
  [[nodiscard]] auto error(const std::string& componentName, const std::string& errorMessage) -> std::runtime_error;
93
}
94
#endif // HAVE_LIBCRYPTO
95
}
96

97
string nowTime();
98
string unquotify(const string &item);
99
string humanDuration(time_t passed);
100
bool stripDomainSuffix(string *qname, const string &domain);
101
void stripLine(string &line);
102
std::optional<string> getHostname();
103
std::string getCarbonHostName();
104
string urlEncode(const string &text);
105
int waitForData(int fileDesc, int seconds, int useconds = 0);
106
int waitFor2Data(int fd1, int fd2, int seconds, int useconds, int* fd);
107
int waitForMultiData(const set<int>& fds, const int seconds, const int useconds, int* fd);
108
int waitForRWData(int fileDesc, bool waitForRead, int seconds, int useconds, bool* error = nullptr, bool* disconnected = nullptr);
109
bool getTSIGHashEnum(const DNSName& algoName, TSIGHashEnum& algoEnum);
110
DNSName getTSIGAlgoName(TSIGHashEnum& algoEnum);
111

112
int logFacilityToLOG(unsigned int facility);
113

114
template<typename Container>
115
void
116
stringtok (Container &container, string const &in,
117
           const char * const delimiters = " \t\n")
118
{
1,311,889✔
119
  const string::size_type len = in.length();
1,311,889✔
120
  string::size_type i = 0;
1,311,889✔
121

122
  while (i<len) {
3,611,965✔
123
    // eat leading whitespace
124
    i = in.find_first_not_of (delimiters, i);
3,575,747✔
125
    if (i == string::npos)
3,575,747!
126
      return;   // nothing left but white space
2✔
127

128
    // find the end of the token
129
    string::size_type j = in.find_first_of (delimiters, i);
3,575,745✔
130

131
    // push token
132
    if (j == string::npos) {
3,575,745✔
133
      container.push_back (in.substr(i));
1,275,669✔
134
      return;
1,275,669✔
135
    } else
1,275,669✔
136
      container.push_back (in.substr(i, j-i));
2,300,076✔
137

138
    // set up for next loop
139
    i = j + 1;
2,300,076✔
140
  }
2,300,076✔
141
}
1,311,889✔
142

143
template<typename T> bool rfc1982LessThan(T a, T b)
144
{
164✔
145
  static_assert(std::is_unsigned_v<T>, "rfc1982LessThan only works for unsigned types");
164✔
146
  return std::make_signed_t<T>(a - b) < 0;
164✔
147
}
164✔
148

149
// fills container with ranges, so {posbegin,posend}
150
template <typename Container>
151
void
152
vstringtok (Container &container, string const &in,
153
           const char * const delimiters = " \t\n")
154
{
4,416,155✔
155
  const string::size_type len = in.length();
4,416,155✔
156
  string::size_type i = 0;
4,416,155✔
157

158
  while (i<len) {
18,722,665!
159
    // eat leading whitespace
160
    i = in.find_first_not_of (delimiters, i);
18,715,566✔
161
    if (i == string::npos)
18,715,566!
162
      return;   // nothing left but white space
×
163

164
    // find the end of the token
165
    string::size_type j = in.find_first_of (delimiters, i);
18,715,566✔
166

167
    // push token
168
    if (j == string::npos) {
18,715,566✔
169
      container.emplace_back(i, len);
4,409,056✔
170
      return;
4,409,056✔
171
    } else
4,409,056✔
172
      container.emplace_back(i, j);
14,306,510✔
173

174
    // set up for next loop
175
    i = j + 1;
14,306,510✔
176
  }
14,306,510✔
177
}
4,416,155✔
178

179
size_t writen2(int fd, const void *buf, size_t count);
180
inline size_t writen2(int fd, const std::string &s) { return writen2(fd, s.data(), s.size()); }
78✔
181
size_t readn2(int fileDesc, void* buffer, size_t len);
182
size_t readn2WithTimeout(int fd, void* buffer, size_t len, const struct timeval& idleTimeout, const struct timeval& totalTimeout={0,0}, bool allowIncomplete=false);
183
size_t writen2WithTimeout(int fd, const void * buffer, size_t len, const struct timeval& timeout);
184

185
void toLowerInPlace(string& str);
186
const string toLower(const string &upper);
187
const string toLowerCanonic(const string &upper);
188
bool IpToU32(const string &str, uint32_t *ip);
189
string U32ToIP(uint32_t);
190

191
inline string stringerror(int err = errno)
192
{
1,696✔
193
  return pdns::getMessageFromErrno(err);
1,696✔
194
}
1,696✔
195

196
string bitFlip(const string &str);
197

198
void dropPrivs(int uid, int gid);
199
void cleanSlashes(string &str);
200

201
#if defined(_POSIX_THREAD_CPUTIME) && defined(CLOCK_THREAD_CPUTIME_ID)
202
/** CPUTime measurements */
203
class CPUTime
204
{
205
public:
206
  void start()
207
  {
×
208
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &d_start);
×
209
  }
×
210
  uint64_t ndiff()
211
  {
×
212
    struct timespec now;
×
213
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
×
214
    return 1000000000ULL*(now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec);
×
215
  }
×
216
private:
217
  struct timespec d_start;
218
};
219
#endif
220

221
/** The DTime class can be used for timing statistics with microsecond resolution.
222
On 32 bits systems this means that 2147 seconds is the longest time that can be measured. */
223
class DTime
224
{
225
public:
226
  //!< Does not set the timer for you! Saves lots of gettimeofday() calls
227
  DTime() = default;
2,312,023✔
228
  DTime(const DTime &dt) = default;
229
  DTime & operator=(const DTime &dt) = default;
230
  inline time_t time() const;
231
  inline void set();  //!< Reset the timer
232
  inline int udiff(bool reset = true); //!< Return the number of microseconds since the timer was last set.
233

234
  int udiffNoReset() //!< Return the number of microseconds since the timer was last set.
235
  {
207,365✔
236
    return udiff(false);
207,365✔
237
  }
207,365✔
238
  void setTimeval(const struct timeval& tv)
239
  {
97,016✔
240
    d_set=tv;
97,016✔
241
  }
97,016✔
242
  struct timeval getTimeval() const
243
  {
18,685✔
244
    return d_set;
18,685✔
245
  }
18,685✔
246
private:
247
struct timeval d_set{0, 0};
248
};
249

250
inline time_t DTime::time() const
251
{
×
252
  return d_set.tv_sec;
×
253
}
×
254

255
inline void DTime::set()
256
{
13,701✔
257
  gettimeofday(&d_set, nullptr);
13,701✔
258
}
13,701✔
259

260
inline int DTime::udiff(bool reset)
261
{
489,577✔
262
  struct timeval now;
489,577✔
263
  gettimeofday(&now, nullptr);
489,577✔
264

265
  int ret=1000000*(now.tv_sec-d_set.tv_sec)+(now.tv_usec-d_set.tv_usec);
489,577✔
266

267
  if (reset) {
489,584✔
268
    d_set = now;
282,218✔
269
  }
282,218✔
270

271
  return ret;
489,577✔
272
}
489,577✔
273

274
inline void toLowerInPlace(string& str)
275
{
7,755,454✔
276
  const size_t length = str.length();
7,755,454✔
277
  char c;
7,755,454✔
278
  for (size_t i = 0; i < length; ++i) {
150,494,263✔
279
    c = dns_tolower(str[i]);
142,738,809✔
280
    if (c != str[i]) {
142,738,809✔
281
      str[i] = c;
968,200✔
282
    }
968,200✔
283
  }
142,738,809✔
284
}
7,755,454✔
285

286
inline const string toLower(const string &upper)
287
{
2,632,006✔
288
  string reply(upper);
2,632,006✔
289

290
  toLowerInPlace(reply);
2,632,006✔
291

292
  return reply;
2,632,006✔
293
}
2,632,006✔
294

295
inline const string toLowerCanonic(const string &upper)
296
{
213✔
297
  string reply(upper);
213✔
298
  if(!upper.empty()) {
213!
299
    unsigned int i, limit= ( unsigned int ) reply.length();
213✔
300
    unsigned char c;
213✔
301
    for(i = 0; i < limit ; i++) {
9,941✔
302
      c = dns_tolower(upper[i]);
9,728✔
303
      if(c != upper[i])
9,728!
304
        reply[i] = c;
×
305
    }
9,728✔
306
    if(upper[i-1]=='.')
213!
307
      reply.resize(i-1);
213✔
308
  }
213✔
309

310
  return reply;
213✔
311
}
213✔
312

313

314

315
// Make s uppercase:
316
inline string toUpper( const string& s )
317
{
6,751,670✔
318
        string r(s);
6,751,670✔
319
        for( unsigned int i = 0; i < s.length(); i++ ) {
24,451,259✔
320
          r[i] = dns_toupper(r[i]);
17,699,589✔
321
        }
17,699,589✔
322
        return r;
6,751,670✔
323
}
6,751,670✔
324

325
inline double getTime()
326
{
×
327
  struct timeval now;
×
328
  gettimeofday(&now,0);
×
329

×
330
  return now.tv_sec+now.tv_usec/1000000.0;
×
331
}
×
332

333
[[noreturn]] inline void unixDie(const string &why)
UNCOV
334
{
×
UNCOV
335
  throw runtime_error(why + ": " + stringerror(errno));
×
UNCOV
336
}
×
337

338
string makeHexDump(const string& str);
339
//! Convert the hexstring in to a byte string
340
string makeBytesFromHex(const string &in);
341

342
void normalizeTV(struct timeval& tv);
343
struct timeval operator+(const struct timeval& lhs, const struct timeval& rhs);
344
struct timeval operator-(const struct timeval& lhs, const struct timeval& rhs);
345

346
inline float makeFloat(const struct timeval& tv)
347
{
49,721✔
348
  return tv.tv_sec + tv.tv_usec/1000000.0f;
49,721✔
349
}
49,721✔
350
inline uint64_t uSec(const struct timeval& tv)
351
{
18,963✔
352
  return tv.tv_sec * 1000000 + tv.tv_usec;
18,963✔
353
}
18,963✔
354

355
inline bool operator<(const struct timeval& lhs, const struct timeval& rhs)
356
{
458,210✔
357
  return std::tie(lhs.tv_sec, lhs.tv_usec) < std::tie(rhs.tv_sec, rhs.tv_usec);
458,210✔
358
}
458,210✔
359
inline bool operator<=(const struct timeval& lhs, const struct timeval& rhs)
360
{
1,179✔
361
  return std::tie(lhs.tv_sec, lhs.tv_usec) <= std::tie(rhs.tv_sec, rhs.tv_usec);
1,179✔
362
}
1,179✔
363

364
inline bool operator<(const struct timespec& lhs, const struct timespec& rhs)
UNCOV
365
{
×
UNCOV
366
  return std::tie(lhs.tv_sec, lhs.tv_nsec) < std::tie(rhs.tv_sec, rhs.tv_nsec);
×
UNCOV
367
}
×
368

369

370
inline bool pdns_ilexicographical_compare(const std::string& a, const std::string& b)  __attribute__((pure));
371
inline bool pdns_ilexicographical_compare(const std::string& a, const std::string& b)
372
{
1,345✔
373
  const unsigned char *aPtr = (const unsigned char*)a.c_str(), *bPtr = (const unsigned char*)b.c_str();
1,345✔
374
  const unsigned char *aEptr = aPtr + a.length(), *bEptr = bPtr + b.length();
1,345✔
375
  while(aPtr != aEptr && bPtr != bEptr) {
14,545✔
376
    if ((*aPtr != *bPtr) && (dns_tolower(*aPtr) - dns_tolower(*bPtr)))
14,369✔
377
      return (dns_tolower(*aPtr) - dns_tolower(*bPtr)) < 0;
1,169✔
378
    aPtr++;
13,200✔
379
    bPtr++;
13,200✔
380
  }
13,200✔
381
  if(aPtr == aEptr && bPtr == bEptr) // strings are equal (in length)
176✔
382
    return false;
149✔
383
  return aPtr == aEptr; // true if first string was shorter
27✔
384
}
176✔
385

386
inline bool pdns_iequals(const std::string& a, const std::string& b) __attribute__((pure));
387
inline bool pdns_iequals(const std::string& a, const std::string& b)
388
{
13,935,162✔
389
  if (a.length() != b.length())
13,935,162✔
390
    return false;
9,359,319✔
391

392
  const char *aPtr = a.c_str(), *bPtr = b.c_str();
4,575,843✔
393
  const char *aEptr = aPtr + a.length();
4,575,843✔
394
  while(aPtr != aEptr) {
15,015,586✔
395
    if((*aPtr != *bPtr) && (dns_tolower(*aPtr) != dns_tolower(*bPtr)))
10,564,396✔
396
      return false;
124,653✔
397
    aPtr++;
10,439,743✔
398
    bPtr++;
10,439,743✔
399
  }
10,439,743✔
400
  return true;
4,451,190✔
401
}
4,575,843✔
402

403
inline bool pdns_iequals_ch(const char a, const char b) __attribute__((pure));
404
inline bool pdns_iequals_ch(const char a, const char b)
405
{
1,443,291✔
406
  if ((a != b) && (dns_tolower(a) != dns_tolower(b)))
1,443,291✔
407
    return false;
1,422,501✔
408

409
  return true;
20,790✔
410
}
1,443,291✔
411

412

413
typedef unsigned long AtomicCounterInner;
414
typedef std::atomic<AtomicCounterInner> AtomicCounter ;
415

416
// FIXME400 this should probably go?
417
struct CIStringCompare
418
{
419
  bool operator()(const string& a, const string& b) const
420
  {
1,177✔
421
    return pdns_ilexicographical_compare(a, b);
1,177✔
422
  }
1,177✔
423
};
424

425
struct CIStringComparePOSIX
426
{
427
   bool operator() (const std::string& lhs, const std::string& rhs) const
428
   {
16,696✔
429
      const std::locale &loc = std::locale("POSIX");
16,696✔
430
      auto lhsIter = lhs.begin();
16,696✔
431
      auto rhsIter = rhs.begin();
16,696✔
432
      while (lhsIter != lhs.end()) {
537,251!
433
        if (rhsIter == rhs.end() || std::tolower(*rhsIter,loc) < std::tolower(*lhsIter,loc)) {
537,251!
434
          return false;
6,489✔
435
        }
6,489✔
436
        if (std::tolower(*lhsIter,loc) < std::tolower(*rhsIter,loc)) {
530,762✔
437
          return true;
10,207✔
438
        }
10,207✔
439
        ++lhsIter;++rhsIter;
520,555✔
440
      }
520,555✔
441
      return rhsIter != rhs.end();
×
442
   }
16,696✔
443
};
444

445
struct CIStringPairCompare
446
{
447
  bool operator()(const pair<string, uint16_t>& a, const pair<string, uint16_t>& b) const
448
  {
93✔
449
    if(pdns_ilexicographical_compare(a.first, b.first))
93✔
450
        return true;
45✔
451
    if(pdns_ilexicographical_compare(b.first, a.first))
48✔
452
        return false;
9✔
453
    return a.second < b.second;
39✔
454
  }
48✔
455
};
456

457
inline size_t pdns_ci_find(const string& haystack, const string& needle)
458
{
81,016✔
459
  string::const_iterator it = boost::range::search(haystack,
81,016✔
460
                                                   needle, pdns_iequals_ch);
81,016✔
461
  if (it == haystack.end()) {
81,016✔
462
    // not found
463
    return string::npos;
81,013✔
464
  } else {
81,013✔
465
    return it - haystack.begin();
3✔
466
  }
3✔
467
}
81,016✔
468

469
pair<string, string> splitField(const string& inp, char sepa);
470

471
inline bool isCanonical(const string& qname)
472
{
4,418,322✔
473
  if(qname.empty())
4,418,322✔
474
    return false;
10✔
475
  return qname[qname.size()-1]=='.';
4,418,312✔
476
}
4,418,322✔
477

478
inline DNSName toCanonic(const DNSName& zone, const string& qname)
479
{
37,077✔
480
  if(qname.size()==1 && qname[0]=='@')
37,077!
481
    return zone;
×
482
  if(isCanonical(qname))
37,077✔
483
    return DNSName(qname);
27,023✔
484
  return DNSName(qname) += zone;
10,054✔
485
}
37,077✔
486

487
string stripDot(const string& dom);
488

489
int makeIPv6sockaddr(const std::string& addr, struct sockaddr_in6* ret);
490
int makeIPv4sockaddr(const std::string& str, struct sockaddr_in* ret);
491
int makeUNsockaddr(const std::string& path, struct sockaddr_un* ret);
492
bool stringfgets(FILE* fp, std::string& line);
493

494
template<typename Index>
495
std::pair<typename Index::iterator,bool>
496
replacing_insert(Index& i,const typename Index::value_type& x)
497
{
2,817✔
498
  std::pair<typename Index::iterator,bool> res=i.insert(x);
2,817✔
499
  if(!res.second)res.second=i.replace(res.first,x);
2,817✔
500
  return res;
2,817✔
501
}
2,817✔
502

503
/** very small regex wrapper */
504
class Regex
505
{
506
public:
507
  /** constructor that accepts the expression to regex */
508
  Regex(const string &expr);
509

510
  ~Regex()
511
  {
20✔
512
    regfree(&d_preg);
20✔
513
  }
20✔
514
  /** call this to find out if 'line' matches your expression */
515
  bool match(const string &line) const
516
  {
2,929✔
517
    return regexec(&d_preg,line.c_str(),0,0,0)==0;
2,929✔
518
  }
2,929✔
519
  bool match(const DNSName& name) const
520
  {
×
521
    return match(name.toStringNoDot());
×
522
  }
×
523

524
private:
525
  regex_t d_preg;
526
};
527

528
class SimpleMatch
529
{
530
public:
531
  SimpleMatch(const string &mask, bool caseFold = false): d_mask(mask), d_fold(caseFold)
532
  {
96✔
533
  }
96✔
534

535
  bool match(string::const_iterator mi, string::const_iterator mend, string::const_iterator vi, string::const_iterator vend) const
536
  {
13,308✔
537
    for(;;++mi) {
21,182✔
538
      if (mi == mend) {
21,182✔
539
        return vi == vend;
30✔
540
      } else if (*mi == '?') {
21,152✔
541
        if (vi == vend) return false;
12✔
542
        ++vi;
9✔
543
      } else if (*mi == '*') {
21,140✔
544
        while(mi != mend && *mi == '*') ++mi;
1,158✔
545
        if (mi == mend) return true;
579✔
546
        while(vi != vend) {
12,696✔
547
          if (match(mi,mend,vi,vend)) return true;
12,156✔
548
          ++vi;
12,141✔
549
        }
12,141✔
550
        return false;
540✔
551
      } else {
20,561✔
552
        if ((mi == mend && vi != vend)||
20,561!
553
            (mi != mend && vi == vend)) return false;
20,561!
554
        if (d_fold) {
20,555✔
555
          if (dns_tolower(*mi) != dns_tolower(*vi)) return false;
20,465✔
556
        } else {
20,555✔
557
          if (*mi != *vi) return false;
90✔
558
        }
90✔
559
        ++vi;
7,865✔
560
      }
7,865✔
561
    }
21,182✔
562
  }
13,308✔
563

564
  bool match(const string& value) const {
1,152✔
565
    return match(d_mask.begin(), d_mask.end(), value.begin(), value.end());
1,152✔
566
  }
1,152✔
567

568
  bool match(const DNSName& name) const {
1,095✔
569
    return match(name.toStringNoDot());
1,095✔
570
  }
1,095✔
571

572
private:
573
  const string d_mask;
574
  const bool d_fold;
575
};
576

577
union ComboAddress;
578

579
// An aligned type to hold cmsgbufs. See https://man.openbsd.org/CMSG_DATA
580
typedef union { struct cmsghdr hdr; char buf[256]; } cmsgbuf_aligned;
581

582
/* itfIndex is an interface index, as returned by if_nametoindex(). 0 means default. */
583
void addCMsgSrcAddr(struct msghdr* msgh, cmsgbuf_aligned* cbuf, const ComboAddress* source, int itfIndex);
584

585
unsigned int getFilenumLimit(bool hardOrSoft=0);
586
void setFilenumLimit(unsigned int lim);
587
bool readFileIfThere(const char* fname, std::string* line);
588
bool setSocketTimestamps(int fd);
589

590
//! Sets the socket into blocking mode.
591
bool setBlocking( int sock );
592

593
//! Sets the socket into non-blocking mode.
594
bool setNonBlocking( int sock );
595
bool setTCPNoDelay(int sock);
596
bool setReuseAddr(int sock);
597
bool isNonBlocking(int sock);
598
bool setReceiveSocketErrors(int sock, int af);
599
int closesocket(int socket);
600
bool setCloseOnExec(int sock);
601

602
size_t getPipeBufferSize(int fd);
603
bool setPipeBufferSize(int fd, size_t size);
604

605
uint64_t udpErrorStats(const std::string& str);
606
uint64_t udp6ErrorStats(const std::string& str);
607
uint64_t tcpErrorStats(const std::string& str);
608
uint64_t getRealMemoryUsage(const std::string&);
609
uint64_t getSpecialMemoryUsage(const std::string&);
610
uint64_t getOpenFileDescriptors(const std::string&);
611
uint64_t getCPUTimeUser(const std::string&);
612
uint64_t getCPUTimeSystem(const std::string&);
613
uint64_t getCPUIOWait(const std::string&);
614
uint64_t getCPUSteal(const std::string&);
615
std::string getMACAddress(const ComboAddress& ca);
616
int getMACAddress(const ComboAddress& ca, char* dest, size_t len);
617

618
template<typename T>
619
const T& defTer(const T& a, const T& b)
620
{
9✔
621
  return a ? a : b;
9!
622
}
9✔
623

624
template<typename P, typename T>
625
T valueOrEmpty(const P val) {
92✔
626
  if (!val) return T{};
92!
627
  return T(val);
92✔
628
}
92✔
629

630

631
// I'm not very OCD, but I appreciate loglines like "processing 1 delta", "processing 2 deltas" :-)
632
template <typename Integer,
633
typename std::enable_if_t<std::is_integral_v<Integer>, bool> = true>
634
const char* addS(Integer siz, const char* singular = "", const char *plural = "s")
635
{
94✔
636
  if (siz == 1) {
94!
637
    return singular;
29✔
638
  }
29✔
639
  return plural;
65✔
640
}
94✔
641

642
template <typename C,
643
typename std::enable_if_t<std::is_class_v<C>, bool> = true>
644
const char* addS(const C& c, const char* singular = "", const char *plural = "s")
645
{
646
  return addS(c.size(), singular, plural);
647
}
648

649
template<typename C>
650
const typename C::value_type::second_type* rplookup(const C& c, const typename C::value_type::first_type& key)
651
{
4,626✔
652
  auto fnd = c.find(key);
4,626✔
653
  if(fnd == c.end())
4,626!
654
    return 0;
69✔
655
  return &fnd->second;
4,557✔
656
}
4,626✔
657

658
double DiffTime(const struct timespec& first, const struct timespec& second);
659
double DiffTime(const struct timeval& first, const struct timeval& second);
660
uid_t strToUID(const string &str);
661
gid_t strToGID(const string &str);
662

663
namespace pdns
664
{
665
/**
666
 * \brief Does a checked conversion from one integer type to another.
667
 *
668
 * \warning The source type `F` and target type `T` must have the same
669
 * signedness, otherwise a compilation error is thrown.
670
 *
671
 * \exception std::out_of_range Thrown if the source value does not fit
672
 * in the target type.
673
 *
674
 * \param[in] from The source value of type `F`.
675
 *
676
 * \return The target value of type `T`.
677
 */
678
template <typename T, typename F>
679
auto checked_conv(F from) -> T
680
{
5,176,293✔
681
  static_assert(std::numeric_limits<F>::is_integer, "checked_conv: The `F` type must be an integer");
5,176,293✔
682
  static_assert(std::numeric_limits<T>::is_integer, "checked_conv: The `T` type must be an integer");
5,176,293✔
683
  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),
5,176,293✔
684
                "checked_conv: The `T` and `F` types must either both be signed or unsigned");
5,176,293✔
685

686
  constexpr auto tMin = std::numeric_limits<T>::min();
5,176,293✔
687
  if constexpr (std::numeric_limits<F>::min() != tMin) {
5,176,293✔
688
    if (from < tMin) {
1,749,812!
689
      string s = "checked_conv: source value " + std::to_string(from) + " is smaller than target's minimum possible value " + std::to_string(tMin);
602,399✔
690
      throw std::out_of_range(s);
602,399✔
691
    }
602,399✔
692
  }
1,749,812✔
693

694
  constexpr auto tMax = std::numeric_limits<T>::max();
1,749,812✔
695
  if constexpr (std::numeric_limits<F>::max() != tMax) {
5,176,293✔
696
    if (from > tMax) {
5,175,505!
697
      string s = "checked_conv: source value " + std::to_string(from) + " is larger than target's maximum possible value " + std::to_string(tMax);
12✔
698
      throw std::out_of_range(s);
12✔
699
    }
12✔
700
  }
5,175,505✔
701

702
  return static_cast<T>(from);
5,175,491✔
703
}
1,749,812✔
704

705
/**
706
 * \brief Performs a conversion from `std::string&` to integer.
707
 *
708
 * This function internally calls `std::stoll` and `std::stoull` to do
709
 * the conversion from `std::string&` and calls `pdns::checked_conv` to
710
 * do the checked conversion from `long long`/`unsigned long long` to
711
 * `T`.
712
 *
713
 * \warning The target type `T` must be an integer, otherwise a
714
 * compilation error is thrown.
715
 *
716
 * \exception std:stoll Throws what std::stoll throws.
717
 *
718
 * \exception std::stoull Throws what std::stoull throws.
719
 *
720
 * \exception pdns::checked_conv Throws what pdns::checked_conv throws.
721
 *
722
 * \param[in] str The input string to be converted.
723
 *
724
 * \param[in] idx Location to store the index at which processing
725
 * stopped. If the input `str` is empty, `*idx` shall be set to 0.
726
 *
727
 * \param[in] base The numerical base for conversion.
728
 *
729
 * \return `str` converted to integer `T`, or 0 if `str` is empty.
730
 */
731
template <typename T>
732
auto checked_stoi(const std::string& str, size_t* idx = nullptr, int base = 10) -> T
733
{
5,192,629✔
734
  static_assert(std::numeric_limits<T>::is_integer, "checked_stoi: The `T` type must be an integer");
5,192,629✔
735

736
  if (str.empty()) {
5,192,629!
737
    if (idx != nullptr) {
16,042!
738
      *idx = 0;
×
739
    }
×
740

741
    return 0; // compatibility
16,042✔
742
  }
16,042✔
743

744
  if constexpr (std::is_unsigned_v<T>) {
5,176,587✔
745
    return pdns::checked_conv<T>(std::stoull(str, idx, base));
1,750,320✔
746
  }
1,750,320✔
747
  else {
1,750,320✔
748
    return pdns::checked_conv<T>(std::stoll(str, idx, base));
1,750,320✔
749
  }
1,750,320✔
750
}
5,176,587✔
751

752
/**
753
 * \brief Performs a conversion from `std::string&` to integer.
754
 *
755
 * This function internally calls `pdns::checked_stoi` and stores its
756
 * result in `out`.
757
 *
758
 * \exception pdns::checked_stoi Throws what pdns::checked_stoi throws.
759
 *
760
 * \param[out] out `str` converted to integer `T`, or 0 if `str` is
761
 * empty.
762
 *
763
 * \param[in] str The input string to be converted.
764
 *
765
 * \param[in] idx Location to store the index at which processing
766
 * stopped. If the input `str` is empty, `*idx` shall be set to 0.
767
 *
768
 * \param[in] base The numerical base for conversion.
769
 *
770
 * \return `str` converted to integer `T`, or 0 if `str` is empty.
771
 */
772
template <typename T>
773
auto checked_stoi_into(T& out, const std::string& str, size_t* idx = nullptr, int base = 10)
774
{
2,647,433✔
775
  out = checked_stoi<T>(str, idx, base);
2,647,433✔
776
}
2,647,433✔
777
}
778

779
bool isSettingThreadCPUAffinitySupported();
780
int mapThreadToCPUList(pthread_t tid, const std::set<int>& cpus);
781

782
std::vector<ComboAddress> getResolvers(const std::string& resolvConfPath);
783

784
DNSName reverseNameFromIP(const ComboAddress& ip);
785

786
size_t parseRFC1035CharString(const std::string &in, std::string &val); // from ragel
787
size_t parseSVCBValueListFromParsedRFC1035CharString(const std::string &in, vector<std::string> &val); // from ragel
788
size_t parseSVCBValueList(const std::string &in, vector<std::string> &val);
789

790
std::string makeLuaString(const std::string& in);
791

792
bool constantTimeStringEquals(const std::string& a, const std::string& b);
793

794
// Used in NID and L64 records
795
struct NodeOrLocatorID { uint8_t content[8]; };
796

797
struct FDWrapper
798
{
799
  FDWrapper() = default;
4✔
800
  FDWrapper(int desc): d_fd(desc) {}
2,333✔
801
  FDWrapper(const FDWrapper&) = delete;
802
  FDWrapper& operator=(const FDWrapper& rhs) = delete;
803

804

805
  ~FDWrapper()
806
  {
4,101✔
807
    reset();
4,101✔
808
  }
4,101✔
809

810
  FDWrapper(FDWrapper&& rhs) noexcept : d_fd(rhs.d_fd)
811
  {
3,606✔
812
    rhs.d_fd = -1;
3,606✔
813
  }
3,606✔
814

815
  FDWrapper& operator=(FDWrapper&& rhs) noexcept
816
  {
4✔
817
    if (d_fd >= 0) {
4✔
818
      close(d_fd);
2✔
819
    }
2✔
820
    d_fd = rhs.d_fd;
4✔
821
    rhs.d_fd = -1;
4✔
822
    return *this;
4✔
823
  }
4✔
824

825
  [[nodiscard]] int getHandle() const
826
  {
191,371✔
827
    return d_fd;
191,371✔
828
  }
191,371✔
829

830
  operator int() const
831
  {
1,886✔
832
    return d_fd;
1,886✔
833
  }
1,886✔
834

835
  int reset()
836
  {
4,105✔
837
    int ret = 0;
4,105✔
838
    if (d_fd >= 0) {
4,105✔
839
      ret = close(d_fd);
483✔
840
    }
483✔
841
    d_fd = -1;
4,105✔
842
    return ret;
4,105✔
843
  }
4,105✔
844

845
private:
846
  int d_fd{-1};
847
};
848

849
namespace pdns
850
{
851
[[nodiscard]] std::optional<std::string> visit_directory(const std::string& directory, const std::function<bool(ino_t inodeNumber, const std::string_view& name)>& visitor);
852

853
struct FilePtrDeleter
854
{
855
  /* using a deleter instead of decltype(&fclose) has two big advantages:
856
     - the deleter is included in the type and does not have to be passed
857
       when creating a new object (easier to use, less memory usage, in theory
858
       better inlining)
859
     - we avoid the annoying "ignoring attributes on template argument ‘int (*)(FILE*)’"
860
       warning from the compiler, which is there because fclose is tagged as __nonnull((1))
861
  */
862
  void operator()(FILE* filePtr) const noexcept {
443✔
863
    fclose(filePtr);
443✔
864
  }
443✔
865
};
866

867
using UniqueFilePtr = std::unique_ptr<FILE, FilePtrDeleter>;
868

869
UniqueFilePtr openFileForWriting(const std::string& filePath, mode_t permissions, bool mustNotExist = true, bool appendIfExists = false);
870
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc