Coverage Report

Created: 2023-09-25 06:58

/src/pdns/pdns/misc.cc
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
23
#ifdef HAVE_CONFIG_H
24
#include "config.h"
25
#endif
26
27
#include <sys/param.h>
28
#include <sys/socket.h>
29
#include <fcntl.h>
30
#include <netdb.h>
31
#include <sys/time.h>
32
#include <ctime>
33
#include <sys/resource.h>
34
#include <netinet/in.h>
35
#include <sys/un.h>
36
#include <unistd.h>
37
#include <fstream>
38
#include "misc.hh"
39
#include <vector>
40
#include <string>
41
#include <sstream>
42
#include <cerrno>
43
#include <cstring>
44
#include <iostream>
45
#include <sys/types.h>
46
#include <dirent.h>
47
#include <algorithm>
48
#include <poll.h>
49
#include <iomanip>
50
#include <netinet/tcp.h>
51
#include <optional>
52
#include <cstdlib>
53
#include <cstdio>
54
#include "pdnsexception.hh"
55
#include <boost/algorithm/string.hpp>
56
#include <boost/format.hpp>
57
#include "iputils.hh"
58
#include "dnsparser.hh"
59
#include "dns_random.hh"
60
#include <pwd.h>
61
#include <grp.h>
62
#include <climits>
63
#ifdef __FreeBSD__
64
#  include <pthread_np.h>
65
#endif
66
#ifdef __NetBSD__
67
#  include <pthread.h>
68
#  include <sched.h>
69
#endif
70
71
#if defined(HAVE_LIBCRYPTO)
72
#include <openssl/err.h>
73
#endif // HAVE_LIBCRYPTO
74
75
size_t writen2(int fileDesc, const void *buf, size_t count)
76
0
{
77
0
  const char *ptr = static_cast<const char*>(buf);
78
0
  const char *eptr = ptr + count;
79
80
0
  while (ptr != eptr) {
81
0
    auto res = ::write(fileDesc, ptr, eptr - ptr);
82
0
    if (res < 0) {
83
0
      if (errno == EAGAIN) {
84
0
        throw std::runtime_error("used writen2 on non-blocking socket, got EAGAIN");
85
0
      }
86
0
      unixDie("failed in writen2");
87
0
    }
88
0
    else if (res == 0) {
89
0
      throw std::runtime_error("could not write all bytes, got eof in writen2");
90
0
    }
91
92
0
    ptr += res;
93
0
  }
94
95
0
  return count;
96
0
}
97
98
size_t readn2(int fd, void* buffer, size_t len)
99
0
{
100
0
  size_t pos=0;
101
0
  ssize_t res;
102
0
  for(;;) {
103
0
    res = read(fd, (char*)buffer + pos, len - pos);
104
0
    if(res == 0)
105
0
      throw runtime_error("EOF while reading message");
106
0
    if(res < 0) {
107
0
      if (errno == EAGAIN)
108
0
        throw std::runtime_error("used readn2 on non-blocking socket, got EAGAIN");
109
0
      else
110
0
        unixDie("failed in readn2");
111
0
    }
112
113
0
    pos+=(size_t)res;
114
0
    if(pos == len)
115
0
      break;
116
0
  }
117
0
  return len;
118
0
}
119
120
size_t readn2WithTimeout(int fd, void* buffer, size_t len, const struct timeval& idleTimeout, const struct timeval& totalTimeout, bool allowIncomplete)
121
0
{
122
0
  size_t pos = 0;
123
0
  struct timeval start{0,0};
124
0
  struct timeval remainingTime = totalTimeout;
125
0
  if (totalTimeout.tv_sec != 0 || totalTimeout.tv_usec != 0) {
126
0
    gettimeofday(&start, nullptr);
127
0
  }
128
129
0
  do {
130
0
    ssize_t got = read(fd, (char *)buffer + pos, len - pos);
131
0
    if (got > 0) {
132
0
      pos += (size_t) got;
133
0
      if (allowIncomplete) {
134
0
        break;
135
0
      }
136
0
    }
137
0
    else if (got == 0) {
138
0
      throw runtime_error("EOF while reading message");
139
0
    }
140
0
    else {
141
0
      if (errno == EAGAIN) {
142
0
        struct timeval w = ((totalTimeout.tv_sec == 0 && totalTimeout.tv_usec == 0) || idleTimeout <= remainingTime) ? idleTimeout : remainingTime;
143
0
        int res = waitForData(fd, w.tv_sec, w.tv_usec);
144
0
        if (res > 0) {
145
          /* there is data available */
146
0
        }
147
0
        else if (res == 0) {
148
0
          throw runtime_error("Timeout while waiting for data to read");
149
0
        } else {
150
0
          throw runtime_error("Error while waiting for data to read");
151
0
        }
152
0
      }
153
0
      else {
154
0
        unixDie("failed in readn2WithTimeout");
155
0
      }
156
0
    }
157
158
0
    if (totalTimeout.tv_sec != 0 || totalTimeout.tv_usec != 0) {
159
0
      struct timeval now;
160
0
      gettimeofday(&now, nullptr);
161
0
      struct timeval elapsed = now - start;
162
0
      if (remainingTime < elapsed) {
163
0
        throw runtime_error("Timeout while reading data");
164
0
      }
165
0
      start = now;
166
0
      remainingTime = remainingTime - elapsed;
167
0
    }
168
0
  }
169
0
  while (pos < len);
170
171
0
  return len;
172
0
}
173
174
size_t writen2WithTimeout(int fd, const void * buffer, size_t len, const struct timeval& timeout)
175
0
{
176
0
  size_t pos = 0;
177
0
  do {
178
0
    ssize_t written = write(fd, reinterpret_cast<const char *>(buffer) + pos, len - pos);
179
180
0
    if (written > 0) {
181
0
      pos += (size_t) written;
182
0
    }
183
0
    else if (written == 0)
184
0
      throw runtime_error("EOF while writing message");
185
0
    else {
186
0
      if (errno == EAGAIN) {
187
0
        int res = waitForRWData(fd, false, timeout.tv_sec, timeout.tv_usec);
188
0
        if (res > 0) {
189
          /* there is room available */
190
0
        }
191
0
        else if (res == 0) {
192
0
          throw runtime_error("Timeout while waiting to write data");
193
0
        } else {
194
0
          throw runtime_error("Error while waiting for room to write data");
195
0
        }
196
0
      }
197
0
      else {
198
0
        unixDie("failed in write2WithTimeout");
199
0
      }
200
0
    }
201
0
  }
202
0
  while (pos < len);
203
204
0
  return len;
205
0
}
206
207
auto pdns::getMessageFromErrno(const int errnum) -> std::string
208
0
{
209
0
  const size_t errLen = 2048;
210
0
  std::string errMsgData{};
211
0
  errMsgData.resize(errLen);
212
213
0
  const char* errMsg = nullptr;
214
0
#ifdef STRERROR_R_CHAR_P
215
0
  errMsg = strerror_r(errnum, errMsgData.data(), errMsgData.length());
216
#else
217
  // This can fail, and when it does, it sets errno. We ignore that and
218
  // set our own error message instead.
219
  int res = strerror_r(errnum, errMsgData.data(), errMsgData.length());
220
  errMsg = errMsgData.c_str();
221
  if (res != 0) {
222
    errMsg = "Unknown (the exact error could not be retrieved)";
223
  }
224
#endif
225
226
  // We make a copy here because `strerror_r()` might return a static
227
  // immutable buffer for an error message. The copy shouldn't be
228
  // critical though, we're on the bailout/error-handling path anyways.
229
0
  std::string message{errMsg};
230
0
  return message;
231
0
}
232
233
#if defined(HAVE_LIBCRYPTO)
234
auto pdns::OpenSSL::error(const std::string& errorMessage) -> std::runtime_error
235
0
{
236
0
  unsigned long errorCode = 0;
237
0
  auto fullErrorMessage{errorMessage};
238
#if OPENSSL_VERSION_MAJOR >= 3
239
  const char* filename = nullptr;
240
  const char* functionName = nullptr;
241
  int lineNumber = 0;
242
  while ((errorCode = ERR_get_error_all(&filename, &lineNumber, &functionName, nullptr, nullptr)) != 0) {
243
    fullErrorMessage += std::string(": ") + std::to_string(errorCode);
244
245
    const auto* lib = ERR_lib_error_string(errorCode);
246
    if (lib != nullptr) {
247
      fullErrorMessage += std::string(":") + lib;
248
    }
249
250
    const auto* reason = ERR_reason_error_string(errorCode);
251
    if (reason != nullptr) {
252
      fullErrorMessage += std::string("::") + reason;
253
    }
254
255
    if (filename != nullptr) {
256
      fullErrorMessage += std::string(" - ") + filename;
257
    }
258
    if (lineNumber != 0) {
259
      fullErrorMessage += std::string(":") + std::to_string(lineNumber);
260
    }
261
    if (functionName != nullptr) {
262
      fullErrorMessage += std::string(" - ") + functionName;
263
    }
264
  }
265
#else
266
0
  while ((errorCode = ERR_get_error()) != 0) {
267
0
    fullErrorMessage += std::string(": ") + std::to_string(errorCode);
268
269
0
    const auto* lib = ERR_lib_error_string(errorCode);
270
0
    if (lib != nullptr) {
271
0
      fullErrorMessage += std::string(":") + lib;
272
0
    }
273
274
0
    const auto* func = ERR_func_error_string(errorCode);
275
0
    if (func != nullptr) {
276
0
      fullErrorMessage += std::string(":") + func;
277
0
    }
278
279
0
    const auto* reason = ERR_reason_error_string(errorCode);
280
0
    if (reason != nullptr) {
281
0
      fullErrorMessage += std::string("::") + reason;
282
0
    }
283
0
  }
284
0
#endif
285
0
  return std::runtime_error(fullErrorMessage);
286
0
}
287
288
auto pdns::OpenSSL::error(const std::string& componentName, const std::string& errorMessage) -> std::runtime_error
289
0
{
290
0
  return pdns::OpenSSL::error(componentName + ": " + errorMessage);
291
0
}
292
#endif // HAVE_LIBCRYPTO
293
294
string nowTime()
295
0
{
296
0
  time_t now = time(nullptr);
297
0
  struct tm theTime{};
298
0
  localtime_r(&now, &theTime);
299
0
  std::array<char, 30> buffer{};
300
  // YYYY-mm-dd HH:MM:SS TZOFF
301
0
  size_t ret = strftime(buffer.data(), buffer.size(), "%F %T %z", &theTime);
302
0
  if (ret == 0) {
303
0
    buffer[0] = '\0';
304
0
  }
305
0
  return {buffer.data()};
306
0
}
307
308
static bool ciEqual(const string& lhs, const string& rhs)
309
0
{
310
0
  if (lhs.size() != rhs.size()) {
311
0
    return false;
312
0
  }
313
314
0
  string::size_type pos = 0;
315
0
  const string::size_type epos = lhs.size();
316
0
  for (; pos < epos; ++pos) {
317
0
    if (dns_tolower(lhs[pos]) != dns_tolower(rhs[pos])) {
318
0
      return false;
319
0
    }
320
0
  }
321
0
  return true;
322
0
}
323
324
/** does domain end on suffix? Is smart about "wwwds9a.nl" "ds9a.nl" not matching */
325
static bool endsOn(const string &domain, const string &suffix)
326
0
{
327
0
  if( suffix.empty() || ciEqual(domain, suffix) ) {
328
0
    return true;
329
0
  }
330
331
0
  if(domain.size() <= suffix.size()) {
332
0
    return false;
333
0
  }
334
335
0
  string::size_type dpos = domain.size() - suffix.size() - 1;
336
0
  string::size_type spos = 0;
337
338
0
  if (domain[dpos++] != '.') {
339
0
    return false;
340
0
  }
341
342
0
  for(; dpos < domain.size(); ++dpos, ++spos) {
343
0
    if (dns_tolower(domain[dpos]) != dns_tolower(suffix[spos])) {
344
0
      return false;
345
0
    }
346
0
  }
347
348
0
  return true;
349
0
}
350
351
/** strips a domain suffix from a domain, returns true if it stripped */
352
bool stripDomainSuffix(string *qname, const string &domain)
353
0
{
354
0
  if (!endsOn(*qname, domain)) {
355
0
    return false;
356
0
  }
357
358
0
  if (toLower(*qname) == toLower(domain)) {
359
0
    *qname="@";
360
0
  }
361
0
  else {
362
0
    if ((*qname)[qname->size() - domain.size() - 1] != '.') {
363
0
      return false;
364
0
    }
365
366
0
    qname->resize(qname->size() - domain.size()-1);
367
0
  }
368
0
  return true;
369
0
}
370
371
// returns -1 in case if error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
372
int waitForData(int fileDesc, int seconds, int useconds)
373
0
{
374
0
  return waitForRWData(fileDesc, true, seconds, useconds);
375
0
}
376
377
int waitForRWData(int fileDesc, bool waitForRead, int seconds, int useconds, bool* error, bool* disconnected)
378
0
{
379
0
  struct pollfd pfd{};
380
0
  memset(&pfd, 0, sizeof(pfd));
381
0
  pfd.fd = fileDesc;
382
383
0
  if (waitForRead) {
384
0
    pfd.events = POLLIN;
385
0
  }
386
0
  else {
387
0
    pfd.events = POLLOUT;
388
0
  }
389
390
0
  int ret = poll(&pfd, 1, seconds * 1000 + useconds/1000);
391
0
  if (ret > 0) {
392
0
    if ((error != nullptr) && (pfd.revents & POLLERR) != 0) {
393
0
      *error = true;
394
0
    }
395
0
    if ((disconnected != nullptr) && (pfd.revents & POLLHUP) != 0) {
396
0
      *disconnected = true;
397
0
    }
398
0
  }
399
400
0
  return ret;
401
0
}
402
403
// returns -1 in case of error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
404
0
int waitForMultiData(const set<int>& fds, const int seconds, const int useconds, int* fdOut) {
405
0
  set<int> realFDs;
406
0
  for (const auto& fd : fds) {
407
0
    if (fd >= 0 && realFDs.count(fd) == 0) {
408
0
      realFDs.insert(fd);
409
0
    }
410
0
  }
411
412
0
  std::vector<struct pollfd> pfds(realFDs.size());
413
0
  memset(pfds.data(), 0, realFDs.size()*sizeof(struct pollfd));
414
0
  int ctr = 0;
415
0
  for (const auto& fd : realFDs) {
416
0
    pfds[ctr].fd = fd;
417
0
    pfds[ctr].events = POLLIN;
418
0
    ctr++;
419
0
  }
420
421
0
  int ret;
422
0
  if(seconds >= 0)
423
0
    ret = poll(pfds.data(), realFDs.size(), seconds * 1000 + useconds/1000);
424
0
  else
425
0
    ret = poll(pfds.data(), realFDs.size(), -1);
426
0
  if(ret <= 0)
427
0
    return ret;
428
429
0
  set<int> pollinFDs;
430
0
  for (const auto& pfd : pfds) {
431
0
    if (pfd.revents & POLLIN) {
432
0
      pollinFDs.insert(pfd.fd);
433
0
    }
434
0
  }
435
0
  set<int>::const_iterator it(pollinFDs.begin());
436
0
  advance(it, dns_random(pollinFDs.size()));
437
0
  *fdOut = *it;
438
0
  return 1;
439
0
}
440
441
// returns -1 in case of error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
442
int waitFor2Data(int fd1, int fd2, int seconds, int useconds, int* fdPtr)
443
0
{
444
0
  std::array<pollfd,2> pfds{};
445
0
  memset(pfds.data(), 0, pfds.size() * sizeof(struct pollfd));
446
0
  pfds[0].fd = fd1;
447
0
  pfds[1].fd = fd2;
448
449
0
  pfds[0].events= pfds[1].events = POLLIN;
450
451
0
  int nsocks = 1 + static_cast<int>(fd2 >= 0); // fd2 can optionally be -1
452
453
0
  int ret{};
454
0
  if (seconds >= 0) {
455
0
    ret = poll(pfds.data(), nsocks, seconds * 1000 + useconds / 1000);
456
0
  }
457
0
  else {
458
0
    ret = poll(pfds.data(), nsocks, -1);
459
0
  }
460
0
  if (ret <= 0) {
461
0
    return ret;
462
0
  }
463
464
0
  if ((pfds[0].revents & POLLIN) != 0 && (pfds[1].revents & POLLIN) == 0) {
465
0
    *fdPtr = pfds[0].fd;
466
0
  }
467
0
  else if ((pfds[1].revents & POLLIN) != 0 && (pfds[0].revents & POLLIN) == 0) {
468
0
    *fdPtr = pfds[1].fd;
469
0
  }
470
0
  else if(ret == 2) {
471
0
    *fdPtr = pfds.at(dns_random_uint32() % 2).fd;
472
0
  }
473
0
  else {
474
0
    *fdPtr = -1; // should never happen
475
0
  }
476
477
0
  return 1;
478
0
}
479
480
481
string humanDuration(time_t passed)
482
0
{
483
0
  ostringstream ret;
484
0
  if(passed<60)
485
0
    ret<<passed<<" seconds";
486
0
  else if(passed<3600)
487
0
    ret<<std::setprecision(2)<<passed/60.0<<" minutes";
488
0
  else if(passed<86400)
489
0
    ret<<std::setprecision(3)<<passed/3600.0<<" hours";
490
0
  else if(passed<(86400*30.41))
491
0
    ret<<std::setprecision(3)<<passed/86400.0<<" days";
492
0
  else
493
0
    ret<<std::setprecision(3)<<passed/(86400*30.41)<<" months";
494
495
0
  return ret.str();
496
0
}
497
498
string unquotify(const string &item)
499
0
{
500
0
  if(item.size()<2)
501
0
    return item;
502
503
0
  string::size_type bpos=0, epos=item.size();
504
505
0
  if(item[0]=='"')
506
0
    bpos=1;
507
508
0
  if(item[epos-1]=='"')
509
0
    epos-=1;
510
511
0
  return item.substr(bpos,epos-bpos);
512
0
}
513
514
void stripLine(string &line)
515
0
{
516
0
  string::size_type pos=line.find_first_of("\r\n");
517
0
  if(pos!=string::npos) {
518
0
    line.resize(pos);
519
0
  }
520
0
}
521
522
string urlEncode(const string &text)
523
0
{
524
0
  string ret;
525
0
  for(char i : text)
526
0
    if(i==' ')ret.append("%20");
527
0
    else ret.append(1,i);
528
0
  return ret;
529
0
}
530
531
static size_t getMaxHostNameSize()
532
0
{
533
0
#if defined(HOST_NAME_MAX)
534
0
  return HOST_NAME_MAX;
535
0
#endif
536
537
0
#if defined(_SC_HOST_NAME_MAX)
538
0
  auto tmp = sysconf(_SC_HOST_NAME_MAX);
539
0
  if (tmp != -1) {
540
0
    return tmp;
541
0
  }
542
0
#endif
543
544
0
  const size_t maxHostNameSize = 255;
545
0
  return maxHostNameSize;
546
0
}
547
548
std::optional<string> getHostname()
549
0
{
550
0
  const size_t maxHostNameBufSize = getMaxHostNameSize() + 1;
551
0
  std::string hostname;
552
0
  hostname.resize(maxHostNameBufSize, 0);
553
554
0
  if (gethostname(hostname.data(), maxHostNameBufSize) == -1) {
555
0
    return std::nullopt;
556
0
  }
557
558
0
  hostname.resize(strlen(hostname.c_str()));
559
0
  return std::make_optional(hostname);
560
0
}
561
562
std::string getCarbonHostName()
563
0
{
564
0
  auto hostname = getHostname();
565
0
  if (!hostname.has_value()) {
566
0
    throw std::runtime_error(stringerror());
567
0
  }
568
569
0
  boost::replace_all(*hostname, ".", "_");
570
0
  return *hostname;
571
0
}
572
573
string bitFlip(const string &str)
574
0
{
575
0
  string::size_type pos=0, epos=str.size();
576
0
  string ret;
577
0
  ret.reserve(epos);
578
0
  for(;pos < epos; ++pos)
579
0
    ret.append(1, ~str[pos]);
580
0
  return ret;
581
0
}
582
583
void cleanSlashes(string &str)
584
0
{
585
0
  string::const_iterator i;
586
0
  string out;
587
0
  for(i=str.begin();i!=str.end();++i) {
588
0
    if(*i=='/' && i!=str.begin() && *(i-1)=='/')
589
0
      continue;
590
0
    out.append(1,*i);
591
0
  }
592
0
  str=out;
593
0
}
594
595
596
bool IpToU32(const string &str, uint32_t *ip)
597
0
{
598
0
  if(str.empty()) {
599
0
    *ip=0;
600
0
    return true;
601
0
  }
602
603
0
  struct in_addr inp;
604
0
  if(inet_aton(str.c_str(), &inp)) {
605
0
    *ip=inp.s_addr;
606
0
    return true;
607
0
  }
608
0
  return false;
609
0
}
610
611
string U32ToIP(uint32_t val)
612
0
{
613
0
  char tmp[17];
614
0
  snprintf(tmp, sizeof(tmp), "%u.%u.%u.%u",
615
0
           (val >> 24)&0xff,
616
0
           (val >> 16)&0xff,
617
0
           (val >>  8)&0xff,
618
0
           (val      )&0xff);
619
0
  return string(tmp);
620
0
}
621
622
623
string makeHexDump(const string& str)
624
0
{
625
0
  std::array<char, 5> tmp;
626
0
  string ret;
627
0
  ret.reserve(static_cast<size_t>(str.size()*2.2));
628
629
0
  for (char n : str) {
630
0
    snprintf(tmp.data(), tmp.size(), "%02x ", static_cast<unsigned char>(n));
631
0
    ret += tmp.data();
632
0
  }
633
0
  return ret;
634
0
}
635
636
0
string makeBytesFromHex(const string &in) {
637
0
  if (in.size() % 2 != 0) {
638
0
    throw std::range_error("odd number of bytes in hex string");
639
0
  }
640
0
  string ret;
641
0
  ret.reserve(in.size() / 2);
642
643
0
  for (size_t i = 0; i < in.size(); i += 2) {
644
0
    const auto numStr = in.substr(i, 2);
645
0
    unsigned int num = 0;
646
0
    if (sscanf(numStr.c_str(), "%02x", &num) != 1) {
647
0
      throw std::range_error("Invalid value while parsing the hex string '" + in + "'");
648
0
    }
649
0
    ret.push_back(static_cast<uint8_t>(num));
650
0
  }
651
652
0
  return ret;
653
0
}
654
655
void normalizeTV(struct timeval& tv)
656
0
{
657
0
  if(tv.tv_usec > 1000000) {
658
0
    ++tv.tv_sec;
659
0
    tv.tv_usec-=1000000;
660
0
  }
661
0
  else if(tv.tv_usec < 0) {
662
0
    --tv.tv_sec;
663
0
    tv.tv_usec+=1000000;
664
0
  }
665
0
}
666
667
struct timeval operator+(const struct timeval& lhs, const struct timeval& rhs)
668
0
{
669
0
  struct timeval ret;
670
0
  ret.tv_sec=lhs.tv_sec + rhs.tv_sec;
671
0
  ret.tv_usec=lhs.tv_usec + rhs.tv_usec;
672
0
  normalizeTV(ret);
673
0
  return ret;
674
0
}
675
676
struct timeval operator-(const struct timeval& lhs, const struct timeval& rhs)
677
0
{
678
0
  struct timeval ret;
679
0
  ret.tv_sec=lhs.tv_sec - rhs.tv_sec;
680
0
  ret.tv_usec=lhs.tv_usec - rhs.tv_usec;
681
0
  normalizeTV(ret);
682
0
  return ret;
683
0
}
684
685
pair<string, string> splitField(const string& inp, char sepa)
686
0
{
687
0
  pair<string, string> ret;
688
0
  string::size_type cpos=inp.find(sepa);
689
0
  if(cpos==string::npos)
690
0
    ret.first=inp;
691
0
  else {
692
0
    ret.first=inp.substr(0, cpos);
693
0
    ret.second=inp.substr(cpos+1);
694
0
  }
695
0
  return ret;
696
0
}
697
698
int logFacilityToLOG(unsigned int facility)
699
0
{
700
0
  switch(facility) {
701
0
  case 0:
702
0
    return LOG_LOCAL0;
703
0
  case 1:
704
0
    return(LOG_LOCAL1);
705
0
  case 2:
706
0
    return(LOG_LOCAL2);
707
0
  case 3:
708
0
    return(LOG_LOCAL3);
709
0
  case 4:
710
0
    return(LOG_LOCAL4);
711
0
  case 5:
712
0
    return(LOG_LOCAL5);
713
0
  case 6:
714
0
    return(LOG_LOCAL6);
715
0
  case 7:
716
0
    return(LOG_LOCAL7);
717
0
  default:
718
0
    return -1;
719
0
  }
720
0
}
721
722
string stripDot(const string& dom)
723
0
{
724
0
  if(dom.empty())
725
0
    return dom;
726
727
0
  if(dom[dom.size()-1]!='.')
728
0
    return dom;
729
730
0
  return dom.substr(0,dom.size()-1);
731
0
}
732
733
int makeIPv6sockaddr(const std::string& addr, struct sockaddr_in6* ret)
734
0
{
735
0
  if (addr.empty()) {
736
0
    return -1;
737
0
  }
738
739
0
  string ourAddr(addr);
740
0
  std::optional<uint16_t> port = std::nullopt;
741
742
0
  if (addr[0] == '[') { // [::]:53 style address
743
0
    string::size_type pos = addr.find(']');
744
0
    if (pos == string::npos) {
745
0
      return -1;
746
0
    }
747
748
0
    ourAddr.assign(addr.c_str() + 1, pos - 1);
749
0
    if (pos + 1 != addr.size()) { // complete after ], no port specified
750
0
      if (pos + 2 > addr.size() || addr[pos + 1] != ':') {
751
0
        return -1;
752
0
      }
753
754
0
      try {
755
0
        auto tmpPort = pdns::checked_stoi<uint16_t>(addr.substr(pos + 2));
756
0
        port = std::make_optional(tmpPort);
757
0
      }
758
0
      catch (const std::out_of_range&) {
759
0
        return -1;
760
0
      }
761
0
    }
762
0
  }
763
764
0
  ret->sin6_scope_id = 0;
765
0
  ret->sin6_family = AF_INET6;
766
767
0
  if (inet_pton(AF_INET6, ourAddr.c_str(), (void*)&ret->sin6_addr) != 1) {
768
0
    struct addrinfo hints{};
769
0
    std::memset(&hints, 0, sizeof(struct addrinfo));
770
0
    hints.ai_flags = AI_NUMERICHOST;
771
0
    hints.ai_family = AF_INET6;
772
773
0
    struct addrinfo* res = nullptr;
774
    // getaddrinfo has anomalous return codes, anything nonzero is an error, positive or negative
775
0
    if (getaddrinfo(ourAddr.c_str(), nullptr, &hints, &res) != 0) {
776
0
      return -1;
777
0
    }
778
779
0
    memcpy(ret, res->ai_addr, res->ai_addrlen);
780
0
    freeaddrinfo(res);
781
0
  }
782
783
0
  if (port.has_value()) {
784
0
    ret->sin6_port = htons(*port);
785
0
  }
786
787
0
  return 0;
788
0
}
789
790
int makeIPv4sockaddr(const std::string& str, struct sockaddr_in* ret)
791
0
{
792
0
  if(str.empty()) {
793
0
    return -1;
794
0
  }
795
0
  struct in_addr inp;
796
797
0
  string::size_type pos = str.find(':');
798
0
  if(pos == string::npos) { // no port specified, not touching the port
799
0
    if(inet_aton(str.c_str(), &inp)) {
800
0
      ret->sin_addr.s_addr=inp.s_addr;
801
0
      return 0;
802
0
    }
803
0
    return -1;
804
0
  }
805
0
  if(!*(str.c_str() + pos + 1)) // trailing :
806
0
    return -1;
807
808
0
  char *eptr = const_cast<char*>(str.c_str()) + str.size();
809
0
  int port = strtol(str.c_str() + pos + 1, &eptr, 10);
810
0
  if (port < 0 || port > 65535)
811
0
    return -1;
812
813
0
  if(*eptr)
814
0
    return -1;
815
816
0
  ret->sin_port = htons(port);
817
0
  if(inet_aton(str.substr(0, pos).c_str(), &inp)) {
818
0
    ret->sin_addr.s_addr=inp.s_addr;
819
0
    return 0;
820
0
  }
821
0
  return -1;
822
0
}
823
824
int makeUNsockaddr(const std::string& path, struct sockaddr_un* ret)
825
0
{
826
0
  if (path.empty())
827
0
    return -1;
828
829
0
  memset(ret, 0, sizeof(struct sockaddr_un));
830
0
  ret->sun_family = AF_UNIX;
831
0
  if (path.length() >= sizeof(ret->sun_path))
832
0
    return -1;
833
834
0
  path.copy(ret->sun_path, sizeof(ret->sun_path), 0);
835
0
  return 0;
836
0
}
837
838
//! read a line of text from a FILE* to a std::string, returns false on 'no data'
839
bool stringfgets(FILE* fp, std::string& line)
840
0
{
841
0
  char buffer[1024];
842
0
  line.clear();
843
844
0
  do {
845
0
    if(!fgets(buffer, sizeof(buffer), fp))
846
0
      return !line.empty();
847
848
0
    line.append(buffer);
849
0
  } while(!strchr(buffer, '\n'));
850
0
  return true;
851
0
}
852
853
bool readFileIfThere(const char* fname, std::string* line)
854
0
{
855
0
  line->clear();
856
0
  auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(fname, "r"), fclose);
857
0
  if (!fp) {
858
0
    return false;
859
0
  }
860
0
  return stringfgets(fp.get(), *line);
861
0
}
862
863
Regex::Regex(const string &expr)
864
0
{
865
0
  if(regcomp(&d_preg, expr.c_str(), REG_ICASE|REG_NOSUB|REG_EXTENDED))
866
0
    throw PDNSException("Regular expression did not compile");
867
0
}
868
869
// if you end up here because valgrind told you were are doing something wrong
870
// with msgh->msg_controllen, please refer to https://github.com/PowerDNS/pdns/pull/3962
871
// first.
872
// Note that cmsgbuf should be aligned the same as a struct cmsghdr
873
void addCMsgSrcAddr(struct msghdr* msgh, cmsgbuf_aligned* cmsgbuf, const ComboAddress* source, int itfIndex)
874
0
{
875
0
  struct cmsghdr *cmsg = nullptr;
876
877
0
  if(source->sin4.sin_family == AF_INET6) {
878
0
    struct in6_pktinfo *pkt;
879
880
0
    msgh->msg_control = cmsgbuf;
881
0
#if !defined( __APPLE__ )
882
    /* CMSG_SPACE is not a constexpr on macOS */
883
0
    static_assert(CMSG_SPACE(sizeof(*pkt)) <= sizeof(*cmsgbuf), "Buffer is too small for in6_pktinfo");
884
#else /* __APPLE__ */
885
    if (CMSG_SPACE(sizeof(*pkt)) > sizeof(*cmsgbuf)) {
886
      throw std::runtime_error("Buffer is too small for in6_pktinfo");
887
    }
888
#endif /* __APPLE__ */
889
0
    msgh->msg_controllen = CMSG_SPACE(sizeof(*pkt));
890
891
0
    cmsg = CMSG_FIRSTHDR(msgh);
892
0
    cmsg->cmsg_level = IPPROTO_IPV6;
893
0
    cmsg->cmsg_type = IPV6_PKTINFO;
894
0
    cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
895
896
0
    pkt = (struct in6_pktinfo *) CMSG_DATA(cmsg);
897
    // Include the padding to stop valgrind complaining about passing uninitialized data
898
0
    memset(pkt, 0, CMSG_SPACE(sizeof(*pkt)));
899
0
    pkt->ipi6_addr = source->sin6.sin6_addr;
900
0
    pkt->ipi6_ifindex = itfIndex;
901
0
  }
902
0
  else {
903
0
#if defined(IP_PKTINFO)
904
0
    struct in_pktinfo *pkt;
905
906
0
    msgh->msg_control = cmsgbuf;
907
0
#if !defined( __APPLE__ )
908
    /* CMSG_SPACE is not a constexpr on macOS */
909
0
    static_assert(CMSG_SPACE(sizeof(*pkt)) <= sizeof(*cmsgbuf), "Buffer is too small for in_pktinfo");
910
#else /* __APPLE__ */
911
    if (CMSG_SPACE(sizeof(*pkt)) > sizeof(*cmsgbuf)) {
912
      throw std::runtime_error("Buffer is too small for in_pktinfo");
913
    }
914
#endif /* __APPLE__ */
915
0
    msgh->msg_controllen = CMSG_SPACE(sizeof(*pkt));
916
917
0
    cmsg = CMSG_FIRSTHDR(msgh);
918
0
    cmsg->cmsg_level = IPPROTO_IP;
919
0
    cmsg->cmsg_type = IP_PKTINFO;
920
0
    cmsg->cmsg_len = CMSG_LEN(sizeof(*pkt));
921
922
0
    pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);
923
    // Include the padding to stop valgrind complaining about passing uninitialized data
924
0
    memset(pkt, 0, CMSG_SPACE(sizeof(*pkt)));
925
0
    pkt->ipi_spec_dst = source->sin4.sin_addr;
926
0
    pkt->ipi_ifindex = itfIndex;
927
#elif defined(IP_SENDSRCADDR)
928
    struct in_addr *in;
929
930
    msgh->msg_control = cmsgbuf;
931
#if !defined( __APPLE__ )
932
    static_assert(CMSG_SPACE(sizeof(*in)) <= sizeof(*cmsgbuf), "Buffer is too small for in_addr");
933
#else /* __APPLE__ */
934
    if (CMSG_SPACE(sizeof(*in)) > sizeof(*cmsgbuf)) {
935
      throw std::runtime_error("Buffer is too small for in_addr");
936
    }
937
#endif /* __APPLE__ */
938
    msgh->msg_controllen = CMSG_SPACE(sizeof(*in));
939
940
    cmsg = CMSG_FIRSTHDR(msgh);
941
    cmsg->cmsg_level = IPPROTO_IP;
942
    cmsg->cmsg_type = IP_SENDSRCADDR;
943
    cmsg->cmsg_len = CMSG_LEN(sizeof(*in));
944
945
    // Include the padding to stop valgrind complaining about passing uninitialized data
946
    in = (struct in_addr *) CMSG_DATA(cmsg);
947
    memset(in, 0, CMSG_SPACE(sizeof(*in)));
948
    *in = source->sin4.sin_addr;
949
#endif
950
0
  }
951
0
}
952
953
unsigned int getFilenumLimit(bool hardOrSoft)
954
0
{
955
0
  struct rlimit rlim;
956
0
  if(getrlimit(RLIMIT_NOFILE, &rlim) < 0)
957
0
    unixDie("Requesting number of available file descriptors");
958
0
  return hardOrSoft ? rlim.rlim_max : rlim.rlim_cur;
959
0
}
960
961
void setFilenumLimit(unsigned int lim)
962
0
{
963
0
  struct rlimit rlim;
964
965
0
  if(getrlimit(RLIMIT_NOFILE, &rlim) < 0)
966
0
    unixDie("Requesting number of available file descriptors");
967
0
  rlim.rlim_cur=lim;
968
0
  if(setrlimit(RLIMIT_NOFILE, &rlim) < 0)
969
0
    unixDie("Setting number of available file descriptors");
970
0
}
971
972
bool setSocketTimestamps(int fd)
973
0
{
974
0
#ifdef SO_TIMESTAMP
975
0
  int on=1;
976
0
  return setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, (char*)&on, sizeof(on)) == 0;
977
#else
978
  return true; // we pretend this happened.
979
#endif
980
0
}
981
982
bool setTCPNoDelay(int sock)
983
0
{
984
0
  int flag = 1;
985
0
  return setsockopt(sock,            /* socket affected */
986
0
                    IPPROTO_TCP,     /* set option at TCP level */
987
0
                    TCP_NODELAY,     /* name of option */
988
0
                    (char *) &flag,  /* the cast is historical cruft */
989
0
                    sizeof(flag)) == 0;    /* length of option value */
990
0
}
991
992
993
bool setNonBlocking(int sock)
994
0
{
995
0
  int flags=fcntl(sock,F_GETFL,0);
996
0
  if(flags<0 || fcntl(sock, F_SETFL,flags|O_NONBLOCK) <0)
997
0
    return false;
998
0
  return true;
999
0
}
1000
1001
bool setBlocking(int sock)
1002
0
{
1003
0
  int flags=fcntl(sock,F_GETFL,0);
1004
0
  if(flags<0 || fcntl(sock, F_SETFL,flags&(~O_NONBLOCK)) <0)
1005
0
    return false;
1006
0
  return true;
1007
0
}
1008
1009
bool setReuseAddr(int sock)
1010
0
{
1011
0
  int tmp = 1;
1012
0
  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&tmp, static_cast<unsigned>(sizeof tmp))<0)
1013
0
    throw PDNSException(string("Setsockopt failed: ")+stringerror());
1014
0
  return true;
1015
0
}
1016
1017
bool isNonBlocking(int sock)
1018
0
{
1019
0
  int flags=fcntl(sock,F_GETFL,0);
1020
0
  return flags & O_NONBLOCK;
1021
0
}
1022
1023
bool setReceiveSocketErrors([[maybe_unused]] int sock, [[maybe_unused]] int af)
1024
0
{
1025
0
#ifdef __linux__
1026
0
  int tmp = 1, ret;
1027
0
  if (af == AF_INET) {
1028
0
    ret = setsockopt(sock, IPPROTO_IP, IP_RECVERR, &tmp, sizeof(tmp));
1029
0
  } else {
1030
0
    ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVERR, &tmp, sizeof(tmp));
1031
0
  }
1032
0
  if (ret < 0) {
1033
0
    throw PDNSException(string("Setsockopt failed: ") + stringerror());
1034
0
  }
1035
0
#endif
1036
0
  return true;
1037
0
}
1038
1039
// Closes a socket.
1040
int closesocket(int socket)
1041
0
{
1042
0
  int ret = ::close(socket);
1043
0
  if(ret < 0 && errno == ECONNRESET) { // see ticket 192, odd BSD behaviour
1044
0
    return 0;
1045
0
  }
1046
0
  if (ret < 0) {
1047
0
    int err = errno;
1048
0
    throw PDNSException("Error closing socket: " + stringerror(err));
1049
0
  }
1050
0
  return ret;
1051
0
}
1052
1053
bool setCloseOnExec(int sock)
1054
0
{
1055
0
  int flags=fcntl(sock,F_GETFD,0);
1056
0
  if(flags<0 || fcntl(sock, F_SETFD,flags|FD_CLOEXEC) <0)
1057
0
    return false;
1058
0
  return true;
1059
0
}
1060
1061
#ifdef __linux__
1062
#include <linux/rtnetlink.h>
1063
1064
int getMACAddress(const ComboAddress& ca, char* dest, size_t destLen)
1065
0
{
1066
0
  struct {
1067
0
    struct nlmsghdr headermsg;
1068
0
    struct ndmsg neighbormsg;
1069
0
  } request;
1070
1071
0
  std::array<char, 8192> buffer;
1072
1073
0
  auto sock = FDWrapper(socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE));
1074
0
  if (sock.getHandle() == -1) {
1075
0
    return errno;
1076
0
  }
1077
1078
0
  memset(&request, 0, sizeof(request));
1079
0
  request.headermsg.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
1080
0
  request.headermsg.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
1081
0
  request.headermsg.nlmsg_type = RTM_GETNEIGH;
1082
0
  request.neighbormsg.ndm_family = ca.sin4.sin_family;
1083
1084
0
  while (true) {
1085
0
    ssize_t sent = send(sock.getHandle(), &request, sizeof(request), 0);
1086
0
    if (sent == -1) {
1087
0
      if (errno == EINTR) {
1088
0
        continue;
1089
0
      }
1090
0
      return errno;
1091
0
    }
1092
0
    else if (static_cast<size_t>(sent) != sizeof(request)) {
1093
0
      return EIO;
1094
0
    }
1095
0
    break;
1096
0
  }
1097
1098
0
  bool done = false;
1099
0
  bool foundIP = false;
1100
0
  bool foundMAC = false;
1101
0
  do {
1102
0
    ssize_t got = recv(sock.getHandle(), buffer.data(), buffer.size(), 0);
1103
1104
0
    if (got < 0) {
1105
0
      if (errno == EINTR) {
1106
0
        continue;
1107
0
      }
1108
0
      return errno;
1109
0
    }
1110
1111
0
    size_t remaining = static_cast<size_t>(got);
1112
0
    for (struct nlmsghdr* nlmsgheader = reinterpret_cast<struct nlmsghdr*>(buffer.data());
1113
0
         done == false && NLMSG_OK (nlmsgheader, remaining);
1114
0
         nlmsgheader = reinterpret_cast<struct nlmsghdr*>(NLMSG_NEXT(nlmsgheader, remaining))) {
1115
1116
0
      if (nlmsgheader->nlmsg_type == NLMSG_DONE) {
1117
0
        done = true;
1118
0
        break;
1119
0
      }
1120
1121
0
      auto nd = reinterpret_cast<struct ndmsg*>(NLMSG_DATA(nlmsgheader));
1122
0
      auto rtatp = reinterpret_cast<struct rtattr*>(reinterpret_cast<char*>(nd) + NLMSG_ALIGN(sizeof(struct ndmsg)));
1123
0
      int rtattrlen = nlmsgheader->nlmsg_len - NLMSG_LENGTH(sizeof(struct ndmsg));
1124
1125
0
      if (nd->ndm_family != ca.sin4.sin_family) {
1126
0
        continue;
1127
0
      }
1128
1129
0
      if (ca.sin4.sin_family == AF_INET6 && ca.sin6.sin6_scope_id != 0 && static_cast<int32_t>(ca.sin6.sin6_scope_id) != nd->ndm_ifindex) {
1130
0
        continue;
1131
0
      }
1132
1133
0
      for (; done == false && RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) {
1134
0
        if (rtatp->rta_type == NDA_DST){
1135
0
          if (nd->ndm_family == AF_INET) {
1136
0
            auto inp = reinterpret_cast<struct in_addr*>(RTA_DATA(rtatp));
1137
0
            if (inp->s_addr == ca.sin4.sin_addr.s_addr) {
1138
0
              foundIP = true;
1139
0
            }
1140
0
          }
1141
0
          else if (nd->ndm_family == AF_INET6) {
1142
0
            auto inp = reinterpret_cast<struct in6_addr *>(RTA_DATA(rtatp));
1143
0
            if (memcmp(inp->s6_addr, ca.sin6.sin6_addr.s6_addr, sizeof(ca.sin6.sin6_addr.s6_addr)) == 0) {
1144
0
              foundIP = true;
1145
0
            }
1146
0
          }
1147
0
        }
1148
0
        else if (rtatp->rta_type == NDA_LLADDR) {
1149
0
          if (foundIP) {
1150
0
            size_t addrLen = rtatp->rta_len - sizeof(struct rtattr);
1151
0
            if (addrLen > destLen) {
1152
0
              return ENOBUFS;
1153
0
            }
1154
0
            memcpy(dest, reinterpret_cast<const char*>(rtatp) + sizeof(struct rtattr), addrLen);
1155
0
            foundMAC = true;
1156
0
            done = true;
1157
0
            break;
1158
0
          }
1159
0
        }
1160
0
      }
1161
0
    }
1162
0
  }
1163
0
  while (done == false);
1164
1165
0
  return foundMAC ? 0 : ENOENT;
1166
0
}
1167
#else
1168
int getMACAddress(const ComboAddress& /* ca */, char* /* dest */, size_t /* len */)
1169
{
1170
  return ENOENT;
1171
}
1172
#endif /* __linux__ */
1173
1174
string getMACAddress(const ComboAddress& ca)
1175
0
{
1176
0
  string ret;
1177
0
  char tmp[6];
1178
0
  if (getMACAddress(ca, tmp, sizeof(tmp)) == 0) {
1179
0
    ret.append(tmp, sizeof(tmp));
1180
0
  }
1181
0
  return ret;
1182
0
}
1183
1184
uint64_t udpErrorStats([[maybe_unused]] const std::string& str)
1185
0
{
1186
0
#ifdef __linux__
1187
0
  ifstream ifs("/proc/net/snmp");
1188
0
  if (!ifs) {
1189
0
    return 0;
1190
0
  }
1191
1192
0
  string line;
1193
0
  while (getline(ifs, line)) {
1194
0
    if (boost::starts_with(line, "Udp: ") && isdigit(line.at(5))) {
1195
0
      vector<string> parts;
1196
0
      stringtok(parts, line, " \n\t\r");
1197
1198
0
      if (parts.size() < 7) {
1199
0
        break;
1200
0
      }
1201
1202
0
      if (str == "udp-rcvbuf-errors") {
1203
0
        return std::stoull(parts.at(5));
1204
0
      }
1205
0
      else if (str == "udp-sndbuf-errors") {
1206
0
        return std::stoull(parts.at(6));
1207
0
      }
1208
0
      else if (str == "udp-noport-errors") {
1209
0
        return std::stoull(parts.at(2));
1210
0
      }
1211
0
      else if (str == "udp-in-errors") {
1212
0
        return std::stoull(parts.at(3));
1213
0
      }
1214
0
      else if (parts.size() >= 8 && str == "udp-in-csum-errors") {
1215
0
        return std::stoull(parts.at(7));
1216
0
      }
1217
0
      else {
1218
0
        return 0;
1219
0
      }
1220
0
    }
1221
0
  }
1222
0
#endif
1223
0
  return 0;
1224
0
}
1225
1226
uint64_t udp6ErrorStats([[maybe_unused]] const std::string& str)
1227
0
{
1228
0
#ifdef __linux__
1229
0
  const std::map<std::string, std::string> keys = {
1230
0
    { "udp6-in-errors", "Udp6InErrors" },
1231
0
    { "udp6-recvbuf-errors", "Udp6RcvbufErrors" },
1232
0
    { "udp6-sndbuf-errors", "Udp6SndbufErrors" },
1233
0
    { "udp6-noport-errors", "Udp6NoPorts" },
1234
0
    { "udp6-in-csum-errors", "Udp6InCsumErrors" }
1235
0
  };
1236
1237
0
  auto key = keys.find(str);
1238
0
  if (key == keys.end()) {
1239
0
    return 0;
1240
0
  }
1241
1242
0
  ifstream ifs("/proc/net/snmp6");
1243
0
  if (!ifs) {
1244
0
    return 0;
1245
0
  }
1246
1247
0
  std::string line;
1248
0
  while (getline(ifs, line)) {
1249
0
    if (!boost::starts_with(line, key->second)) {
1250
0
      continue;
1251
0
    }
1252
1253
0
    std::vector<std::string> parts;
1254
0
    stringtok(parts, line, " \n\t\r");
1255
1256
0
    if (parts.size() != 2) {
1257
0
      return 0;
1258
0
    }
1259
1260
0
    return std::stoull(parts.at(1));
1261
0
  }
1262
0
#endif
1263
0
  return 0;
1264
0
}
1265
1266
uint64_t tcpErrorStats(const std::string& /* str */)
1267
0
{
1268
0
#ifdef __linux__
1269
0
  ifstream ifs("/proc/net/netstat");
1270
0
  if (!ifs) {
1271
0
    return 0;
1272
0
  }
1273
1274
0
  string line;
1275
0
  vector<string> parts;
1276
0
  while (getline(ifs,line)) {
1277
0
    if (line.size() > 9 && boost::starts_with(line, "TcpExt: ") && isdigit(line.at(8))) {
1278
0
      stringtok(parts, line, " \n\t\r");
1279
1280
0
      if (parts.size() < 21) {
1281
0
        break;
1282
0
      }
1283
1284
0
      return std::stoull(parts.at(20));
1285
0
    }
1286
0
  }
1287
0
#endif
1288
0
  return 0;
1289
0
}
1290
1291
uint64_t getCPUIOWait(const std::string& /* str */)
1292
0
{
1293
0
#ifdef __linux__
1294
0
  ifstream ifs("/proc/stat");
1295
0
  if (!ifs) {
1296
0
    return 0;
1297
0
  }
1298
1299
0
  string line;
1300
0
  vector<string> parts;
1301
0
  while (getline(ifs, line)) {
1302
0
    if (boost::starts_with(line, "cpu ")) {
1303
0
      stringtok(parts, line, " \n\t\r");
1304
1305
0
      if (parts.size() < 6) {
1306
0
        break;
1307
0
      }
1308
1309
0
      return std::stoull(parts[5]);
1310
0
    }
1311
0
  }
1312
0
#endif
1313
0
  return 0;
1314
0
}
1315
1316
uint64_t getCPUSteal(const std::string& /* str */)
1317
0
{
1318
0
#ifdef __linux__
1319
0
  ifstream ifs("/proc/stat");
1320
0
  if (!ifs) {
1321
0
    return 0;
1322
0
  }
1323
1324
0
  string line;
1325
0
  vector<string> parts;
1326
0
  while (getline(ifs, line)) {
1327
0
    if (boost::starts_with(line, "cpu ")) {
1328
0
      stringtok(parts, line, " \n\t\r");
1329
1330
0
      if (parts.size() < 9) {
1331
0
        break;
1332
0
      }
1333
1334
0
      return std::stoull(parts[8]);
1335
0
    }
1336
0
  }
1337
0
#endif
1338
0
  return 0;
1339
0
}
1340
1341
bool getTSIGHashEnum(const DNSName& algoName, TSIGHashEnum& algoEnum)
1342
0
{
1343
0
  if (algoName == DNSName("hmac-md5.sig-alg.reg.int") || algoName == DNSName("hmac-md5"))
1344
0
    algoEnum = TSIG_MD5;
1345
0
  else if (algoName == DNSName("hmac-sha1"))
1346
0
    algoEnum = TSIG_SHA1;
1347
0
  else if (algoName == DNSName("hmac-sha224"))
1348
0
    algoEnum = TSIG_SHA224;
1349
0
  else if (algoName == DNSName("hmac-sha256"))
1350
0
    algoEnum = TSIG_SHA256;
1351
0
  else if (algoName == DNSName("hmac-sha384"))
1352
0
    algoEnum = TSIG_SHA384;
1353
0
  else if (algoName == DNSName("hmac-sha512"))
1354
0
    algoEnum = TSIG_SHA512;
1355
0
  else if (algoName == DNSName("gss-tsig"))
1356
0
    algoEnum = TSIG_GSS;
1357
0
  else {
1358
0
     return false;
1359
0
  }
1360
0
  return true;
1361
0
}
1362
1363
DNSName getTSIGAlgoName(TSIGHashEnum& algoEnum)
1364
0
{
1365
0
  switch(algoEnum) {
1366
0
  case TSIG_MD5: return DNSName("hmac-md5.sig-alg.reg.int.");
1367
0
  case TSIG_SHA1: return DNSName("hmac-sha1.");
1368
0
  case TSIG_SHA224: return DNSName("hmac-sha224.");
1369
0
  case TSIG_SHA256: return DNSName("hmac-sha256.");
1370
0
  case TSIG_SHA384: return DNSName("hmac-sha384.");
1371
0
  case TSIG_SHA512: return DNSName("hmac-sha512.");
1372
0
  case TSIG_GSS: return DNSName("gss-tsig.");
1373
0
  }
1374
0
  throw PDNSException("getTSIGAlgoName does not understand given algorithm, please fix!");
1375
0
}
1376
1377
uint64_t getOpenFileDescriptors(const std::string&)
1378
0
{
1379
0
#ifdef __linux__
1380
0
  DIR* dirhdl=opendir(("/proc/"+std::to_string(getpid())+"/fd/").c_str());
1381
0
  if(!dirhdl)
1382
0
    return 0;
1383
1384
0
  struct dirent *entry;
1385
0
  int ret=0;
1386
0
  while((entry = readdir(dirhdl))) {
1387
0
    uint32_t num;
1388
0
    try {
1389
0
      pdns::checked_stoi_into(num, entry->d_name);
1390
0
    } catch (...) {
1391
0
      continue; // was not a number.
1392
0
    }
1393
0
    if(std::to_string(num) == entry->d_name)
1394
0
      ret++;
1395
0
  }
1396
0
  closedir(dirhdl);
1397
0
  return ret;
1398
1399
#elif defined(__OpenBSD__)
1400
  // FreeBSD also has this in libopenbsd, but I don't know if that's available always
1401
  return getdtablecount();
1402
#else
1403
  return 0;
1404
#endif
1405
0
}
1406
1407
uint64_t getRealMemoryUsage(const std::string&)
1408
0
{
1409
0
#ifdef __linux__
1410
0
  ifstream ifs("/proc/self/statm");
1411
0
  if(!ifs)
1412
0
    return 0;
1413
1414
0
  uint64_t size, resident, shared, text, lib, data;
1415
0
  ifs >> size >> resident >> shared >> text >> lib >> data;
1416
1417
  // We used to use "data" here, but it proves unreliable and even is marked "broken"
1418
  // in https://www.kernel.org/doc/html/latest/filesystems/proc.html
1419
0
  return resident * getpagesize();
1420
#else
1421
  struct rusage ru;
1422
  if (getrusage(RUSAGE_SELF, &ru) != 0)
1423
    return 0;
1424
  return ru.ru_maxrss * 1024;
1425
#endif
1426
0
}
1427
1428
1429
uint64_t getSpecialMemoryUsage(const std::string&)
1430
0
{
1431
0
#ifdef __linux__
1432
0
  ifstream ifs("/proc/self/smaps");
1433
0
  if(!ifs)
1434
0
    return 0;
1435
0
  string line;
1436
0
  uint64_t bytes=0;
1437
0
  string header("Private_Dirty:");
1438
0
  while(getline(ifs, line)) {
1439
0
    if(boost::starts_with(line, header)) {
1440
0
      bytes += std::stoull(line.substr(header.length() + 1))*1024;
1441
0
    }
1442
0
  }
1443
0
  return bytes;
1444
#else
1445
  return 0;
1446
#endif
1447
0
}
1448
1449
uint64_t getCPUTimeUser(const std::string&)
1450
0
{
1451
0
  struct rusage ru;
1452
0
  getrusage(RUSAGE_SELF, &ru);
1453
0
  return (ru.ru_utime.tv_sec*1000ULL + ru.ru_utime.tv_usec/1000);
1454
0
}
1455
1456
uint64_t getCPUTimeSystem(const std::string&)
1457
0
{
1458
0
  struct rusage ru;
1459
0
  getrusage(RUSAGE_SELF, &ru);
1460
0
  return (ru.ru_stime.tv_sec*1000ULL + ru.ru_stime.tv_usec/1000);
1461
0
}
1462
1463
double DiffTime(const struct timespec& first, const struct timespec& second)
1464
0
{
1465
0
  auto seconds = second.tv_sec - first.tv_sec;
1466
0
  auto nseconds = second.tv_nsec - first.tv_nsec;
1467
1468
0
  if (nseconds < 0) {
1469
0
    seconds -= 1;
1470
0
    nseconds += 1000000000;
1471
0
  }
1472
0
  return static_cast<double>(seconds) + static_cast<double>(nseconds) / 1000000000.0;
1473
0
}
1474
1475
double DiffTime(const struct timeval& first, const struct timeval& second)
1476
0
{
1477
0
  int seconds=second.tv_sec - first.tv_sec;
1478
0
  int useconds=second.tv_usec - first.tv_usec;
1479
1480
0
  if(useconds < 0) {
1481
0
    seconds-=1;
1482
0
    useconds+=1000000;
1483
0
  }
1484
0
  return seconds + useconds/1000000.0;
1485
0
}
1486
1487
uid_t strToUID(const string &str)
1488
0
{
1489
0
  uid_t result = 0;
1490
0
  const char * cstr = str.c_str();
1491
0
  struct passwd * pwd = getpwnam(cstr);
1492
1493
0
  if (pwd == nullptr) {
1494
0
    long long val;
1495
1496
0
    try {
1497
0
      val = stoll(str);
1498
0
    }
1499
0
    catch(std::exception& e) {
1500
0
      throw runtime_error((boost::format("Error: Unable to parse user ID %s") % cstr).str() );
1501
0
    }
1502
1503
0
    if (val < std::numeric_limits<uid_t>::min() || val > std::numeric_limits<uid_t>::max()) {
1504
0
      throw runtime_error((boost::format("Error: Unable to parse user ID %s") % cstr).str() );
1505
0
    }
1506
1507
0
    result = static_cast<uid_t>(val);
1508
0
  }
1509
0
  else {
1510
0
    result = pwd->pw_uid;
1511
0
  }
1512
1513
0
  return result;
1514
0
}
1515
1516
gid_t strToGID(const string &str)
1517
0
{
1518
0
  gid_t result = 0;
1519
0
  const char * cstr = str.c_str();
1520
0
  struct group * grp = getgrnam(cstr);
1521
1522
0
  if (grp == nullptr) {
1523
0
    long long val;
1524
1525
0
    try {
1526
0
      val = stoll(str);
1527
0
    }
1528
0
    catch(std::exception& e) {
1529
0
      throw runtime_error((boost::format("Error: Unable to parse group ID %s") % cstr).str() );
1530
0
    }
1531
1532
0
    if (val < std::numeric_limits<gid_t>::min() || val > std::numeric_limits<gid_t>::max()) {
1533
0
      throw runtime_error((boost::format("Error: Unable to parse group ID %s") % cstr).str() );
1534
0
    }
1535
1536
0
    result = static_cast<gid_t>(val);
1537
0
  }
1538
0
  else {
1539
0
    result = grp->gr_gid;
1540
0
  }
1541
1542
0
  return result;
1543
0
}
1544
1545
bool isSettingThreadCPUAffinitySupported()
1546
0
{
1547
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
1548
  return true;
1549
#else
1550
0
  return false;
1551
0
#endif
1552
0
}
1553
1554
int mapThreadToCPUList([[maybe_unused]] pthread_t tid, [[maybe_unused]] const std::set<int>& cpus)
1555
0
{
1556
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
1557
#  ifdef __NetBSD__
1558
  cpuset_t *cpuset;
1559
  cpuset = cpuset_create();
1560
  for (const auto cpuID : cpus) {
1561
    cpuset_set(cpuID, cpuset);
1562
  }
1563
1564
  return pthread_setaffinity_np(tid,
1565
                                cpuset_size(cpuset),
1566
                                cpuset);
1567
#  else
1568
#    ifdef __FreeBSD__
1569
#      define cpu_set_t cpuset_t
1570
#    endif
1571
  cpu_set_t cpuset;
1572
  CPU_ZERO(&cpuset);
1573
  for (const auto cpuID : cpus) {
1574
    CPU_SET(cpuID, &cpuset);
1575
  }
1576
1577
  return pthread_setaffinity_np(tid,
1578
                                sizeof(cpuset),
1579
                                &cpuset);
1580
#  endif
1581
#else
1582
0
  return ENOSYS;
1583
0
#endif /* HAVE_PTHREAD_SETAFFINITY_NP */
1584
0
}
1585
1586
std::vector<ComboAddress> getResolvers(const std::string& resolvConfPath)
1587
0
{
1588
0
  std::vector<ComboAddress> results;
1589
1590
0
  ifstream ifs(resolvConfPath);
1591
0
  if (!ifs) {
1592
0
    return results;
1593
0
  }
1594
1595
0
  string line;
1596
0
  while(std::getline(ifs, line)) {
1597
0
    boost::trim_right_if(line, boost::is_any_of(" \r\n\x1a"));
1598
0
    boost::trim_left(line); // leading spaces, let's be nice
1599
1600
0
    string::size_type tpos = line.find_first_of(";#");
1601
0
    if (tpos != string::npos) {
1602
0
      line.resize(tpos);
1603
0
    }
1604
1605
0
    if (boost::starts_with(line, "nameserver ") || boost::starts_with(line, "nameserver\t")) {
1606
0
      vector<string> parts;
1607
0
      stringtok(parts, line, " \t,"); // be REALLY nice
1608
0
      for (auto iter = parts.begin() + 1; iter != parts.end(); ++iter) {
1609
0
        try {
1610
0
          results.emplace_back(*iter, 53);
1611
0
        }
1612
0
        catch(...)
1613
0
        {
1614
0
        }
1615
0
      }
1616
0
    }
1617
0
  }
1618
1619
0
  return results;
1620
0
}
1621
1622
size_t getPipeBufferSize([[maybe_unused]] int fd)
1623
0
{
1624
0
#ifdef F_GETPIPE_SZ
1625
0
  int res = fcntl(fd, F_GETPIPE_SZ);
1626
0
  if (res == -1) {
1627
0
    return 0;
1628
0
  }
1629
0
  return res;
1630
#else
1631
  errno = ENOSYS;
1632
  return 0;
1633
#endif /* F_GETPIPE_SZ */
1634
0
}
1635
1636
bool setPipeBufferSize([[maybe_unused]] int fd, [[maybe_unused]] size_t size)
1637
0
{
1638
0
#ifdef F_SETPIPE_SZ
1639
0
  if (size > static_cast<size_t>(std::numeric_limits<int>::max())) {
1640
0
    errno = EINVAL;
1641
0
    return false;
1642
0
  }
1643
0
  int newSize = static_cast<int>(size);
1644
0
  int res = fcntl(fd, F_SETPIPE_SZ, newSize);
1645
0
  if (res == -1) {
1646
0
    return false;
1647
0
  }
1648
0
  return true;
1649
#else
1650
  errno = ENOSYS;
1651
  return false;
1652
#endif /* F_SETPIPE_SZ */
1653
0
}
1654
1655
DNSName reverseNameFromIP(const ComboAddress& ip)
1656
0
{
1657
0
  if (ip.isIPv4()) {
1658
0
    std::string result("in-addr.arpa.");
1659
0
    auto ptr = reinterpret_cast<const uint8_t*>(&ip.sin4.sin_addr.s_addr);
1660
0
    for (size_t idx = 0; idx < sizeof(ip.sin4.sin_addr.s_addr); idx++) {
1661
0
      result = std::to_string(ptr[idx]) + "." + result;
1662
0
    }
1663
0
    return DNSName(result);
1664
0
  }
1665
0
  else if (ip.isIPv6()) {
1666
0
    std::string result("ip6.arpa.");
1667
0
    auto ptr = reinterpret_cast<const uint8_t*>(&ip.sin6.sin6_addr.s6_addr[0]);
1668
0
    for (size_t idx = 0; idx < sizeof(ip.sin6.sin6_addr.s6_addr); idx++) {
1669
0
      std::stringstream stream;
1670
0
      stream << std::hex << (ptr[idx] & 0x0F);
1671
0
      stream << '.';
1672
0
      stream << std::hex << (((ptr[idx]) >> 4) & 0x0F);
1673
0
      stream << '.';
1674
0
      result = stream.str() + result;
1675
0
    }
1676
0
    return DNSName(result);
1677
0
  }
1678
1679
0
  throw std::runtime_error("Calling reverseNameFromIP() for an address which is neither an IPv4 nor an IPv6");
1680
0
}
1681
1682
std::string makeLuaString(const std::string& in)
1683
0
{
1684
0
  ostringstream str;
1685
1686
0
  str<<'"';
1687
1688
0
  char item[5];
1689
0
  for (unsigned char n : in) {
1690
0
    if (islower(n) || isupper(n)) {
1691
0
      item[0] = n;
1692
0
      item[1] = 0;
1693
0
    }
1694
0
    else {
1695
0
      snprintf(item, sizeof(item), "\\%03d", n);
1696
0
    }
1697
0
    str << item;
1698
0
  }
1699
1700
0
  str<<'"';
1701
1702
0
  return str.str();
1703
0
}
1704
1705
0
size_t parseSVCBValueList(const std::string &in, vector<std::string> &val) {
1706
0
  std::string parsed;
1707
0
  auto ret = parseRFC1035CharString(in, parsed);
1708
0
  parseSVCBValueListFromParsedRFC1035CharString(parsed, val);
1709
0
  return ret;
1710
0
};
1711
1712
#ifdef HAVE_CRYPTO_MEMCMP
1713
#include <openssl/crypto.h>
1714
#else /* HAVE_CRYPTO_MEMCMP */
1715
#ifdef HAVE_SODIUM_MEMCMP
1716
#include <sodium.h>
1717
#endif /* HAVE_SODIUM_MEMCMP */
1718
#endif /* HAVE_CRYPTO_MEMCMP */
1719
1720
bool constantTimeStringEquals(const std::string& a, const std::string& b)
1721
0
{
1722
0
  if (a.size() != b.size()) {
1723
0
    return false;
1724
0
  }
1725
0
  const size_t size = a.size();
1726
0
#ifdef HAVE_CRYPTO_MEMCMP
1727
0
  return CRYPTO_memcmp(a.c_str(), b.c_str(), size) == 0;
1728
#else /* HAVE_CRYPTO_MEMCMP */
1729
#ifdef HAVE_SODIUM_MEMCMP
1730
  return sodium_memcmp(a.c_str(), b.c_str(), size) == 0;
1731
#else /* HAVE_SODIUM_MEMCMP */
1732
  const volatile unsigned char *_a = (const volatile unsigned char *) a.c_str();
1733
  const volatile unsigned char *_b = (const volatile unsigned char *) b.c_str();
1734
  unsigned char res = 0;
1735
1736
  for (size_t idx = 0; idx < size; idx++) {
1737
    res |= _a[idx] ^ _b[idx];
1738
  }
1739
1740
  return res == 0;
1741
#endif /* !HAVE_SODIUM_MEMCMP */
1742
#endif /* !HAVE_CRYPTO_MEMCMP */
1743
0
}