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

PowerDNS / pdns / 26633102090

29 May 2026 10:51AM UTC coverage: 71.072% (+4.1%) from 66.93%
26633102090

Pull #17481

github

web-flow
Merge 297bae12d into 82597ead2
Pull Request #17481: bump meson to 1.11.1

46324 of 81426 branches covered (56.89%)

Branch coverage included in aggregate %.

132737 of 170516 relevant lines covered (77.84%)

6394113.4 hits per line

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

86.87
/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 <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
{
170,059✔
126
  const string::size_type len = in.length();
170,059✔
127
  string::size_type i = 0;
170,059✔
128

129
  while (i<len) {
719,812✔
130
    // eat leading whitespace
131
    i = in.find_first_not_of (delimiters, i);
651,893✔
132
    if (i == string::npos)
651,893!
133
      return;   // nothing left but white space
2✔
134

135
    // find the end of the token
136
    string::size_type j = in.find_first_of (delimiters, i);
651,891✔
137

138
    // push token
139
    if (j == string::npos) {
651,891✔
140
      container.push_back (in.substr(i));
102,138✔
141
      return;
102,138✔
142
    } else
102,138✔
143
      container.push_back (in.substr(i, j-i));
549,753✔
144

145
    // set up for next loop
146
    i = j + 1;
549,753✔
147
  }
549,753✔
148
}
170,059✔
149

150
template<typename T> bool rfc1982LessThan(T lhs, T rhs)
151
{
289✔
152
  static_assert(std::is_unsigned_v<T>, "rfc1982LessThan only works for unsigned types");
289✔
153
  return static_cast<std::make_signed_t<T>>(lhs - rhs) < 0;
289✔
154
}
289✔
155

156
template<typename T> bool rfc1982LessThanOrEqual(T lhs, T rhs)
157
{
35,528✔
158
  static_assert(std::is_unsigned_v<T>, "rfc1982LessThanOrEqual only works for unsigned types");
35,528✔
159
  return static_cast<std::make_signed_t<T>>(lhs - rhs) <= 0;
35,528✔
160
}
35,528✔
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
{
4,319,453✔
168
  const string::size_type len = in.length();
4,319,453✔
169
  string::size_type i = 0;
4,319,453✔
170

171
  while (i<len) {
18,344,752✔
172
    // eat leading whitespace
173
    i = in.find_first_not_of (delimiters, i);
18,337,121✔
174
    if (i == string::npos)
18,337,121!
175
      return;   // nothing left but white space
×
176

177
    // find the end of the token
178
    string::size_type j = in.find_first_of (delimiters, i);
18,337,121✔
179

180
    // push token
181
    if (j == string::npos) {
18,337,121✔
182
      container.emplace_back(i, len);
4,311,822✔
183
      return;
4,311,822✔
184
    } else
4,311,822✔
185
      container.emplace_back(i, j);
14,025,299✔
186

187
    // set up for next loop
188
    i = j + 1;
14,025,299✔
189
  }
14,025,299✔
190
}
4,319,453✔
191

192
size_t writen2(int fd, const void *buf, size_t count);
193
inline size_t writen2(int fd, const std::string &s) { return writen2(fd, s.data(), s.size()); }
84✔
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
{
1,441✔
206
  return pdns::getMessageFromErrno(err);
1,441✔
207
}
1,441✔
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
  {
×
219
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &d_start);
×
220
  }
×
221
  uint64_t ndiff()
222
  {
×
223
    struct timespec now;
×
224
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
×
225
    return 1000000000ULL*(now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec);
×
226
  }
×
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;
2,485,456✔
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
  {
258,266✔
247
    return udiff(false);
258,266✔
248
  }
258,266✔
249
  void setTimeval(const struct timeval& tv)
250
  {
126,819✔
251
    d_set=tv;
126,819✔
252
  }
126,819✔
253
  struct timeval getTimeval() const
254
  {
24,207✔
255
    return d_set;
24,207✔
256
  }
24,207✔
257
private:
258
struct timeval d_set{0, 0};
259
};
260

261
inline time_t DTime::time() const
262
{
×
263
  return d_set.tv_sec;
×
264
}
×
265

266
inline void DTime::set()
267
{
16,673✔
268
  gettimeofday(&d_set, nullptr);
16,673✔
269
}
16,673✔
270

271
inline int DTime::udiff(bool reset)
272
{
641,191✔
273
  struct timeval now;
641,191✔
274
  gettimeofday(&now, nullptr);
641,191✔
275

276
  int ret=1000000*(now.tv_sec-d_set.tv_sec)+(now.tv_usec-d_set.tv_usec);
641,191✔
277

278
  if (reset) {
641,194✔
279
    d_set = now;
373,824✔
280
  }
373,824✔
281

282
  return ret;
641,191✔
283
}
641,191✔
284

285
inline void toLowerInPlace(string& str)
286
{
8,620,165✔
287
  const size_t length = str.length();
8,620,165✔
288
  char c;
8,620,165✔
289
  for (size_t i = 0; i < length; ++i) {
164,191,704✔
290
    c = dns_tolower(str[i]);
155,571,539✔
291
    if (c != str[i]) {
155,571,539✔
292
      str[i] = c;
995,947✔
293
    }
995,947✔
294
  }
155,571,539✔
295
}
8,620,165✔
296

297
inline const string toLower(const string &upper)
298
{
3,027,512✔
299
  string reply(upper);
3,027,512✔
300

301
  toLowerInPlace(reply);
3,027,512✔
302

303
  return reply;
3,027,512✔
304
}
3,027,512✔
305

306
inline const string toLowerCanonic(const string &upper)
307
{
220✔
308
  string reply(upper);
220✔
309
  if (!reply.empty()) {
220!
310
    const auto length = reply.length();
220✔
311
    if (reply[length - 1] == '.') {
220!
312
      reply.resize(length - 1);
220✔
313
    }
220✔
314
    toLowerInPlace(reply);
220✔
315
  }
220✔
316
  return reply;
220✔
317
}
220✔
318

319
// Make s uppercase:
320
inline string toUpper( const string& s )
321
{
6,736,110✔
322
  string r(s);
6,736,110✔
323
  for (size_t i = 0; i < s.length(); ++i) {
24,622,445✔
324
    r[i] = dns_toupper(r[i]);
17,886,335✔
325
  }
17,886,335✔
326
  return r;
6,736,110✔
327
}
6,736,110✔
328

329
inline double getTime()
330
{
×
331
  struct timeval now;
×
332
  gettimeofday(&now,0);
×
333

×
334
  return now.tv_sec+now.tv_usec/1000000.0;
×
335
}
×
336

337
[[noreturn]] inline void unixDie(const string &why)
338
{
×
339
  throw runtime_error(why + ": " + stringerror(errno));
×
340
}
×
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
{
72,349✔
352
  return tv.tv_sec + tv.tv_usec/1000000.0f;
72,349✔
353
}
72,349✔
354
inline uint64_t uSec(const struct timeval& tv)
355
{
22,250✔
356
  return tv.tv_sec * 1000000 + tv.tv_usec;
22,250✔
357
}
22,250✔
358

359
inline bool operator<(const struct timeval& lhs, const struct timeval& rhs)
360
{
688,951✔
361
  return std::tie(lhs.tv_sec, lhs.tv_usec) < std::tie(rhs.tv_sec, rhs.tv_usec);
688,951✔
362
}
688,951✔
363
inline bool operator<=(const struct timeval& lhs, const struct timeval& rhs)
364
{
4,494✔
365
  return std::tie(lhs.tv_sec, lhs.tv_usec) <= std::tie(rhs.tv_sec, rhs.tv_usec);
4,494✔
366
}
4,494✔
367

368
inline bool operator<(const struct timespec& lhs, const struct timespec& rhs)
369
{
406,404✔
370
  return std::tie(lhs.tv_sec, lhs.tv_nsec) < std::tie(rhs.tv_sec, rhs.tv_nsec);
406,404✔
371
}
406,404✔
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
{
143,930,035✔
377
  const unsigned char *aPtr = (const unsigned char*)a.data(), *bPtr = (const unsigned char*)b.data();
143,930,035✔
378
  const unsigned char *aEptr = aPtr + a.length(), *bEptr = bPtr + b.length();
143,930,035✔
379
  while(aPtr != aEptr && bPtr != bEptr) {
732,575,482✔
380
    if (*aPtr != *bPtr) {
710,974,654✔
381
      if (int rc = dns_tolower(*aPtr) - dns_tolower(*bPtr); rc != 0) {
122,388,863✔
382
        return rc;
122,329,207✔
383
      }
122,329,207✔
384
    }
122,388,856✔
385
    aPtr++;
588,645,447✔
386
    bPtr++;
588,645,447✔
387
  }
588,645,447✔
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
  if (aPtr == aEptr) {
21,600,829✔
392
    if (bPtr != bEptr) {
18,567,926✔
393
      return -1; // a < b
135,746✔
394
    }
135,746✔
395
  }
18,567,926✔
396
  else {
2,150,512,352✔
397
    return 1; // a > b
2,150,512,352✔
398
  }
2,150,512,352✔
399
  return 0; // a == b
18,432,180✔
400
}
21,600,828✔
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
{
20,602✔
405
  return pdns_ilexicographical_compare_three_way(a, b) < 0;
20,602✔
406
}
20,602✔
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
{
13,801,895✔
411
  if (a.length() != b.length())
13,801,895✔
412
    return false;
9,295,355✔
413

414
  return pdns_ilexicographical_compare_three_way(a, b) == 0;
4,506,540✔
415
}
13,801,895✔
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
{
3,664✔
420
  if ((a != b) && (dns_tolower(a) != dns_tolower(b)))
3,664!
421
    return false;
12✔
422

423
  return true;
3,652✔
424
}
3,664✔
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
  {
20,434✔
435
    return pdns_ilexicographical_compare(a, b);
20,434✔
436
  }
20,434✔
437
};
438

439
struct CIStringComparePOSIX
440
{
441
   bool operator() (const std::string& lhs, const std::string& rhs) const
442
   {
17,443✔
443
      const std::locale &loc = std::locale("POSIX");
17,443✔
444
      auto lhsIter = lhs.begin();
17,443✔
445
      auto rhsIter = rhs.begin();
17,443✔
446
      while (lhsIter != lhs.end()) {
563,494!
447
        if (rhsIter == rhs.end() || std::tolower(*rhsIter,loc) < std::tolower(*lhsIter,loc)) {
563,494!
448
          return false;
6,686✔
449
        }
6,686✔
450
        if (std::tolower(*lhsIter,loc) < std::tolower(*rhsIter,loc)) {
556,808✔
451
          return true;
10,757✔
452
        }
10,757✔
453
        ++lhsIter;++rhsIter;
546,051✔
454
      }
546,051✔
455
      return rhsIter != rhs.end();
×
456
   }
17,443✔
457
};
458

459
struct CIStringPairCompare
460
{
461
  bool operator()(const pair<string, uint16_t>& a, const pair<string, uint16_t>& b) const
462
  {
93✔
463
    if(pdns_ilexicographical_compare(a.first, b.first))
93✔
464
        return true;
45✔
465
    if(pdns_ilexicographical_compare(b.first, a.first))
48✔
466
        return false;
9✔
467
    return a.second < b.second;
39✔
468
  }
48✔
469
};
470

471
inline size_t pdns_ci_find(const string& haystack, const string& needle)
472
{
×
473
  string::const_iterator it = std::search(haystack.begin(), haystack.end(),
×
474
    needle.begin(), needle.end(), pdns_iequals_ch);
×
475
  if (it == haystack.end()) {
×
476
    // not found
477
    return string::npos;
×
478
  } else {
×
479
    return it - haystack.begin();
×
480
  }
×
481
}
×
482

483
pair<string, string> splitField(const string& inp, char sepa);
484

485
inline bool isCanonical(std::string_view qname)
486
{
4,322,132✔
487
  return boost::ends_with(qname, ".");
4,322,132✔
488
}
4,322,132✔
489

490
inline DNSName toCanonic(const ZoneName& zone, const string& qname)
491
{
37,996✔
492
  if(qname.size()==1 && qname[0]=='@')
37,996!
493
    return DNSName(zone);
×
494
  if(isCanonical(qname))
37,996✔
495
    return DNSName(qname);
27,861✔
496
  return DNSName(qname) += DNSName(zone);
10,135✔
497
}
37,996✔
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
{
2,815✔
510
  std::pair<typename Index::iterator,bool> res=i.insert(x);
2,815✔
511
  if(!res.second)res.second=i.replace(res.first,x);
2,815✔
512
  return res;
2,815✔
513
}
2,815✔
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;
17✔
524
  Regex& operator=(Regex&& rhs) = default;
525
  ~Regex() = default;
240✔
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
    void operator()(regex_t* ptr) const noexcept {
223✔
534
      regfree(ptr);
223✔
535
      delete ptr;
223✔
536
    }
223✔
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)
140✔
547
  {
140✔
548
  }
140✔
549

550
  bool match(string::const_iterator mi, string::const_iterator mend, string::const_iterator vi, string::const_iterator vend) const
551
  {
2,366,360✔
552
    for(;;++mi) {
2,465,808✔
553
      if (mi == mend) {
2,465,808✔
554
        return vi == vend;
41✔
555
      } else if (*mi == '?') {
2,465,767✔
556
        if (vi == vend) return false;
12✔
557
        ++vi;
9✔
558
      } else if (*mi == '*') {
2,465,755✔
559
        while(mi != mend && *mi == '*') ++mi;
246,610✔
560
        if (mi == mend) return true;
123,305✔
561
        while(vi != vend) {
2,243,000✔
562
          if (match(mi,mend,vi,vend)) return true;
2,119,762✔
563
          ++vi;
2,119,733✔
564
        }
2,119,733✔
565
        return false;
123,238✔
566
      } else {
2,342,450✔
567
        if ((mi == mend && vi != vend)||
2,342,450!
568
            (mi != mend && vi == vend)) return false;
2,342,450!
569
        if (d_fold) {
2,340,105✔
570
          if (dns_tolower(*mi) != dns_tolower(*vi)) return false;
2,340,015✔
571
        } else {
2,340,105✔
572
          if (*mi != *vi) return false;
90✔
573
        }
90✔
574
        ++vi;
99,439✔
575
      }
99,439✔
576
    }
2,465,808✔
577
  }
2,366,360✔
578

579
  bool match(const string& value) const {
246,598✔
580
    return match(d_mask.begin(), d_mask.end(), value.begin(), value.end());
246,598✔
581
  }
246,598✔
582

583
  bool match(const DNSName& name) const {
122,513✔
584
    return match(name.toStringNoDot());
122,513✔
585
  }
122,513✔
586

587
#if defined(PDNS_AUTH) // [
588
  bool match(const ZoneName& name) const {
1,532✔
589
    return match(name.toStringNoDot());
1,532✔
590
  }
1,532✔
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
{
9✔
644
  return a ? a : b;
9!
645
}
9✔
646

647
template<typename P, typename T>
648
T valueOrEmpty(const P val) {
92✔
649
  if (!val) return T{};
92!
650
  return T(val);
92✔
651
}
92✔
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
{
105✔
659
  if (siz == 1) {
105!
660
    return singular;
36✔
661
  }
36✔
662
  return plural;
69✔
663
}
105✔
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
{
5,522✔
675
  auto fnd = c.find(key);
5,522✔
676
  if(fnd == c.end())
5,522!
677
    return 0;
68✔
678
  return &fnd->second;
5,454✔
679
}
5,522✔
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
{
5,762,994✔
704
  static_assert(std::numeric_limits<F>::is_integer, "checked_conv: The `F` type must be an integer");
5,762,994✔
705
  static_assert(std::numeric_limits<T>::is_integer, "checked_conv: The `T` type must be an integer");
5,762,994✔
706
  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,762,994✔
707
                "checked_conv: The `T` and `F` types must either both be signed or unsigned");
5,762,994✔
708

709
  constexpr auto tMin = std::numeric_limits<T>::min();
5,762,994✔
710
  if constexpr (std::numeric_limits<F>::min() != tMin) {
5,416,901✔
711
    if (from < tMin) {
1,224,004✔
712
      string s = "checked_conv: source value " + std::to_string(from) + " is smaller than target's minimum possible value " + std::to_string(tMin);
3✔
713
      throw std::out_of_range(s);
3✔
714
    }
3✔
715
  }
1,224,004✔
716

717
  constexpr auto tMax = std::numeric_limits<T>::max();
1,570,094✔
718
  if constexpr (std::numeric_limits<F>::max() != tMax) {
5,762,994✔
719
    if (from > tMax) {
5,760,837✔
720
      string s = "checked_conv: source value " + std::to_string(from) + " is larger than target's maximum possible value " + std::to_string(tMax);
43✔
721
      throw std::out_of_range(s);
43✔
722
    }
43✔
723
  }
5,760,837✔
724

725
  return static_cast<T>(from);
5,760,794✔
726
}
5,762,994✔
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
{
5,782,132✔
757
  static_assert(std::numeric_limits<T>::is_integer, "checked_stoi: The `T` type must be an integer");
5,782,132✔
758

759
  if (str.empty()) {
5,782,132!
760
    if (idx != nullptr) {
18,216!
761
      *idx = 0;
×
762
    }
×
763

764
    return 0; // compatibility
18,216✔
765
  }
18,216✔
766

767
  if constexpr (std::is_unsigned_v<T>) {
5,763,916✔
768
    return pdns::checked_conv<T>(std::stoull(str, idx, base));
4,538,690✔
769
  }
770
  else {
1,225,226✔
771
    return pdns::checked_conv<T>(std::stoll(str, idx, base));
1,225,226✔
772
  }
1,225,226✔
773
}
5,763,916✔
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
{
3✔
797
  T ret = checked_stoi<T>(str, idx, base);
3✔
798
  if (ret == 0) {
3!
799
    throw std::out_of_range("checked_stoi_nonzero: value cannot be 0");
3✔
800
  }
3✔
801
  return ret;
802
}
3✔
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
{
2,821,330✔
827
  out = checked_stoi<T>(str, idx, base);
2,821,330✔
828
}
2,821,330✔
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 three 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
// parseRFC1035CharStringRelaxed will too, except within a quoted section.
842
size_t parseRFC1035CharString(std::string_view in, std::string &val);
843
size_t parseRFC1035CharStringRelaxed(std::string_view in, std::string &val);
844
size_t parseSVCBValueListFromParsedRFC1035CharString(const std::string &in, vector<std::string> &val);
845
size_t parseSVCBValueList(const std::string &in, vector<std::string> &val);
846

847
std::string makeLuaString(const std::string& in);
848

849
bool constantTimeStringEquals(const std::string& a, const std::string& b);
850

851
// Used in NID and L64 records
852
struct NodeOrLocatorID { uint8_t content[8]; };
853

854
struct FDWrapper
855
{
856
  FDWrapper() = default;
25,661✔
857
  FDWrapper(int desc): d_fd(desc) {}
32,438✔
858
  FDWrapper(const FDWrapper&) = delete;
859
  FDWrapper& operator=(const FDWrapper& rhs) = delete;
860

861

862
  ~FDWrapper()
863
  {
100,189✔
864
    reset();
100,189✔
865
  }
100,189✔
866

867
  FDWrapper(FDWrapper&& rhs) noexcept : d_fd(rhs.d_fd)
85,965✔
868
  {
85,965✔
869
    rhs.d_fd = -1;
85,965✔
870
  }
85,965✔
871

872
  FDWrapper& operator=(FDWrapper&& rhs) noexcept
873
  {
26,313✔
874
    if (d_fd >= 0) {
26,313✔
875
      close(d_fd);
8✔
876
    }
8✔
877
    d_fd = rhs.d_fd;
26,313✔
878
    rhs.d_fd = -1;
26,313✔
879
    return *this;
26,313✔
880
  }
26,313✔
881

882
  [[nodiscard]] int getHandle() const
883
  {
9,706,786✔
884
    return d_fd;
9,706,786✔
885
  }
9,706,786✔
886

887
  operator int() const
888
  {
9,209✔
889
    return d_fd;
9,209✔
890
  }
9,209✔
891

892
  int reset()
893
  {
100,212✔
894
    int ret = 0;
100,212✔
895
    if (d_fd >= 0) { // NOLINT(clang-analyzer-core.UndefinedBinaryOperatorResult): obvious false positive
100,212✔
896
      ret = close(d_fd);
1,216✔
897
    }
1,216✔
898
    d_fd = -1;
100,212✔
899
    return ret;
100,212✔
900
  }
100,212✔
901

902
  int release()
903
  {
598✔
904
    auto ret = d_fd;
598✔
905
    d_fd = -1;
598✔
906
    return ret;
598✔
907
  }
598✔
908

909
private:
910
  int d_fd{-1};
911
};
912

913
namespace pdns
914
{
915
[[nodiscard]] std::optional<std::string> visit_directory(const std::string& directory, const std::function<bool(ino_t inodeNumber, const std::string_view& name)>& visitor);
916

917
struct FilePtrDeleter
918
{
919
  /* using a deleter instead of decltype(&fclose) has two big advantages:
920
     - the deleter is included in the type and does not have to be passed
921
       when creating a new object (easier to use, less memory usage, in theory
922
       better inlining)
923
     - we avoid the annoying "ignoring attributes on template argument ‘int (*)(FILE*)’"
924
       warning from the compiler, which is there because fclose is tagged as __nonnull((1))
925
  */
926
  void operator()(FILE* filePtr) const noexcept {
625✔
927
    fclose(filePtr);
625✔
928
  }
625✔
929
};
930

931
using UniqueFilePtr = std::unique_ptr<FILE, FilePtrDeleter>;
932

933
UniqueFilePtr openFileForWriting(const std::string& filePath, mode_t permissions, bool mustNotExist = true, bool appendIfExists = false);
934
}
935

936
using timebuf_t = std::array<char, 64>;
937
const char* timestamp(time_t arg, timebuf_t& buf);
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