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