Coverage Report

Created: 2023-09-25 07:00

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