]>
Commit | Line | Data |
---|---|---|
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 | */ | |
ea76bac0 | 22 | #pragma once |
d2adfa5c | 23 | #include <cinttypes> |
8b3cfcd3 | 24 | #include <cstring> |
f9822705 | 25 | #include <cstdio> |
3ee84c3f | 26 | #include <regex.h> |
d2adfa5c | 27 | #include <climits> |
88bf3279 | 28 | #include <type_traits> |
d190894c | 29 | |
905dae56 | 30 | #include <boost/algorithm/string.hpp> |
12c86877 | 31 | |
e67e250f | 32 | #include "dns.hh" |
ac0995bb | 33 | #include <atomic> |
76473b92 KM |
34 | #include <sys/time.h> |
35 | #include <sys/types.h> | |
36 | #include <sys/socket.h> | |
d2adfa5c | 37 | #include <ctime> |
76473b92 | 38 | #include <syslog.h> |
a640a9d4 | 39 | #include <stdexcept> |
12c86877 | 40 | #include <string> |
d2adfa5c | 41 | #include <cctype> |
e67e250f BH |
42 | #include <vector> |
43 | ||
10f4eea8 | 44 | #include "namespaces.hh" |
42c83cbc OM |
45 | |
46 | class DNSName; | |
d72aaada | 47 | |
d2adfa5c | 48 | // Do not change to "using TSIGHashEnum ..." until you know CodeQL does not choke on it |
bcb5b94e | 49 | typedef enum { TSIG_MD5, TSIG_SHA1, TSIG_SHA224, TSIG_SHA256, TSIG_SHA384, TSIG_SHA512, TSIG_GSS } TSIGHashEnum; |
da78b86e FM |
50 | namespace pdns |
51 | { | |
52 | /** | |
53 | * \brief Retrieves the errno-based error message in a reentrant way. | |
54 | * | |
55 | * This internally handles the portability issues around using | |
56 | * `strerror_r` and returns a `std::string` that owns the error | |
57 | * message's contents. | |
58 | * | |
59 | * \param[in] errnum The errno value. | |
60 | * | |
61 | * \return The `std::string` error message. | |
62 | */ | |
63 | auto getMessageFromErrno(int errnum) -> std::string; | |
66910c5d | 64 | |
799aa185 | 65 | #if defined(HAVE_LIBCRYPTO) |
66910c5d FM |
66 | namespace OpenSSL |
67 | { | |
68 | /** | |
69 | * \brief Throws a `std::runtime_error` with the current OpenSSL error. | |
70 | * | |
71 | * \param[in] errorMessage The message to attach in addition to the OpenSSL error. | |
72 | */ | |
73 | [[nodiscard]] auto error(const std::string& errorMessage) -> std::runtime_error; | |
74 | ||
75 | /** | |
76 | * \brief Throws a `std::runtime_error` with a name and the current OpenSSL error. | |
77 | * | |
78 | * \param[in] componentName The name of the component to mark the error message with. | |
79 | * \param[in] errorMessage The message to attach in addition to the OpenSSL error. | |
80 | */ | |
81 | [[nodiscard]] auto error(const std::string& componentName, const std::string& errorMessage) -> std::runtime_error; | |
82 | } | |
83 | #endif // HAVE_LIBCRYPTO | |
da78b86e FM |
84 | } |
85 | ||
cc3afe25 | 86 | string nowTime(); |
d2adfa5c | 87 | string unquotify(const string &item); |
12c86877 | 88 | string humanDuration(time_t passed); |
f2c11a48 | 89 | bool stripDomainSuffix(string *qname, const string &domain); |
12c86877 | 90 | void stripLine(string &line); |
ff32c991 FM |
91 | std::optional<string> getHostname(); |
92 | std::string getCarbonHostName(); | |
12c86877 | 93 | string urlEncode(const string &text); |
d2adfa5c | 94 | int waitForData(int fileDesc, int seconds, int useconds = 0); |
f4ff5929 | 95 | int waitFor2Data(int fd1, int fd2, int seconds, int useconds, int* fd); |
d529011f | 96 | int waitForMultiData(const set<int>& fds, const int seconds, const int useconds, int* fd); |
d2adfa5c | 97 | int waitForRWData(int fileDesc, bool waitForRead, int seconds, int useconds, bool* error = nullptr, bool* disconnected = nullptr); |
21a3792f KM |
98 | bool getTSIGHashEnum(const DNSName& algoName, TSIGHashEnum& algoEnum); |
99 | DNSName getTSIGAlgoName(TSIGHashEnum& algoEnum); | |
58044407 | 100 | |
f8499e52 | 101 | int logFacilityToLOG(unsigned int facility); |
bbce5bf0 | 102 | |
6e01be56 | 103 | template<typename Container> |
12c86877 BH |
104 | void |
105 | stringtok (Container &container, string const &in, | |
106 | const char * const delimiters = " \t\n") | |
107 | { | |
108 | const string::size_type len = in.length(); | |
109 | string::size_type i = 0; | |
3ddb9247 | 110 | |
12c86877 BH |
111 | while (i<len) { |
112 | // eat leading whitespace | |
113 | i = in.find_first_not_of (delimiters, i); | |
114 | if (i == string::npos) | |
115 | return; // nothing left but white space | |
3ddb9247 | 116 | |
12c86877 BH |
117 | // find the end of the token |
118 | string::size_type j = in.find_first_of (delimiters, i); | |
3ddb9247 | 119 | |
12c86877 BH |
120 | // push token |
121 | if (j == string::npos) { | |
122 | container.push_back (in.substr(i)); | |
123 | return; | |
124 | } else | |
125 | container.push_back (in.substr(i, j-i)); | |
3ddb9247 | 126 | |
12c86877 BH |
127 | // set up for next loop |
128 | i = j + 1; | |
129 | } | |
130 | } | |
8c3149f2 | 131 | |
f7a69a4c RA |
132 | template<typename T> bool rfc1982LessThan(T a, T b) |
133 | { | |
abb11ca4 RP |
134 | static_assert(std::is_unsigned_v<T>, "rfc1982LessThan only works for unsigned types"); |
135 | return std::make_signed_t<T>(a - b) < 0; | |
f7a69a4c RA |
136 | } |
137 | ||
0ab3fdc6 | 138 | // fills container with ranges, so {posbegin,posend} |
8c3149f2 BH |
139 | template <typename Container> |
140 | void | |
141 | vstringtok (Container &container, string const &in, | |
142 | const char * const delimiters = " \t\n") | |
143 | { | |
144 | const string::size_type len = in.length(); | |
145 | string::size_type i = 0; | |
3ddb9247 | 146 | |
8c3149f2 BH |
147 | while (i<len) { |
148 | // eat leading whitespace | |
149 | i = in.find_first_not_of (delimiters, i); | |
150 | if (i == string::npos) | |
151 | return; // nothing left but white space | |
3ddb9247 | 152 | |
8c3149f2 BH |
153 | // find the end of the token |
154 | string::size_type j = in.find_first_of (delimiters, i); | |
3ddb9247 | 155 | |
8c3149f2 BH |
156 | // push token |
157 | if (j == string::npos) { | |
e32a8d46 | 158 | container.emplace_back(i, len); |
8c3149f2 BH |
159 | return; |
160 | } else | |
e32a8d46 | 161 | container.emplace_back(i, j); |
3ddb9247 | 162 | |
8c3149f2 BH |
163 | // set up for next loop |
164 | i = j + 1; | |
165 | } | |
166 | } | |
167 | ||
a683e8bd RG |
168 | size_t writen2(int fd, const void *buf, size_t count); |
169 | inline size_t writen2(int fd, const std::string &s) { return writen2(fd, s.data(), s.size()); } | |
170 | size_t readn2(int fd, void* buffer, size_t len); | |
1342b949 | 171 | size_t readn2WithTimeout(int fd, void* buffer, size_t len, const struct timeval& idleTimeout, const struct timeval& totalTimeout={0,0}, bool allowIncomplete=false); |
50111728 | 172 | size_t writen2WithTimeout(int fd, const void * buffer, size_t len, const struct timeval& timeout); |
040712e0 | 173 | |
5f2286f1 | 174 | void toLowerInPlace(string& str); |
c3d9d009 | 175 | const string toLower(const string &upper); |
b0d4fb45 | 176 | const string toLowerCanonic(const string &upper); |
092f210a | 177 | bool IpToU32(const string &str, uint32_t *ip); |
5730f30c | 178 | string U32ToIP(uint32_t); |
c20313ec OM |
179 | |
180 | inline string stringerror(int err = errno) | |
181 | { | |
182 | return pdns::getMessageFromErrno(err); | |
183 | } | |
184 | ||
d88babea | 185 | string bitFlip(const string &str); |
12c86877 BH |
186 | |
187 | void dropPrivs(int uid, int gid); | |
12c86877 BH |
188 | void cleanSlashes(string &str); |
189 | ||
c6d04bdc | 190 | #if defined(_POSIX_THREAD_CPUTIME) && defined(CLOCK_THREAD_CPUTIME_ID) |
df13e61d | 191 | /** CPUTime measurements */ |
192 | class CPUTime | |
193 | { | |
194 | public: | |
195 | void start() | |
196 | { | |
197 | clock_gettime(CLOCK_THREAD_CPUTIME_ID, &d_start); | |
198 | } | |
199 | uint64_t ndiff() | |
200 | { | |
201 | struct timespec now; | |
202 | clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now); | |
203 | return 1000000000ULL*(now.tv_sec - d_start.tv_sec) + (now.tv_nsec - d_start.tv_nsec); | |
204 | } | |
205 | private: | |
206 | struct timespec d_start; | |
207 | }; | |
3ddb9247 | 208 | #endif |
df13e61d | 209 | |
3ddb9247 | 210 | /** The DTime class can be used for timing statistics with microsecond resolution. |
12c86877 | 211 | On 32 bits systems this means that 2147 seconds is the longest time that can be measured. */ |
3ddb9247 | 212 | class DTime |
12c86877 BH |
213 | { |
214 | public: | |
c0833d0c RG |
215 | //!< Does not set the timer for you! Saves lots of gettimeofday() calls |
216 | DTime() = default; | |
f238ce64 OM |
217 | DTime(const DTime &dt) = default; |
218 | DTime & operator=(const DTime &dt) = default; | |
c0833d0c | 219 | inline time_t time() const; |
12c86877 | 220 | inline void set(); //!< Reset the timer |
c95554f3 RG |
221 | inline int udiff(bool reset = true); //!< Return the number of microseconds since the timer was last set. |
222 | ||
223 | int udiffNoReset() //!< Return the number of microseconds since the timer was last set. | |
224 | { | |
225 | return udiff(false); | |
226 | } | |
88358c9b BH |
227 | void setTimeval(const struct timeval& tv) |
228 | { | |
229 | d_set=tv; | |
230 | } | |
c0833d0c | 231 | struct timeval getTimeval() const |
88358c9b BH |
232 | { |
233 | return d_set; | |
234 | } | |
12c86877 | 235 | private: |
c0833d0c | 236 | struct timeval d_set{0, 0}; |
12c86877 | 237 | }; |
85db02c5 | 238 | |
c0833d0c RG |
239 | inline time_t DTime::time() const |
240 | { | |
241 | return d_set.tv_sec; | |
242 | } | |
243 | ||
12c86877 BH |
244 | inline void DTime::set() |
245 | { | |
c0833d0c | 246 | gettimeofday(&d_set, nullptr); |
12c86877 BH |
247 | } |
248 | ||
c95554f3 | 249 | inline int DTime::udiff(bool reset) |
12c86877 BH |
250 | { |
251 | struct timeval now; | |
c0833d0c | 252 | gettimeofday(&now, nullptr); |
c95554f3 | 253 | |
fe213470 | 254 | int ret=1000000*(now.tv_sec-d_set.tv_sec)+(now.tv_usec-d_set.tv_usec); |
c95554f3 RG |
255 | |
256 | if (reset) { | |
257 | d_set = now; | |
258 | } | |
259 | ||
fe213470 | 260 | return ret; |
12c86877 BH |
261 | } |
262 | ||
5f2286f1 | 263 | inline void toLowerInPlace(string& str) |
12c86877 | 264 | { |
5f2286f1 | 265 | const size_t length = str.length(); |
c536ca8d | 266 | char c; |
3bb964fd | 267 | for (size_t i = 0; i < length; ++i) { |
5f2286f1 RG |
268 | c = dns_tolower(str[i]); |
269 | if (c != str[i]) { | |
270 | str[i] = c; | |
271 | } | |
c536ca8d | 272 | } |
5f2286f1 RG |
273 | } |
274 | ||
275 | inline const string toLower(const string &upper) | |
276 | { | |
277 | string reply(upper); | |
278 | ||
279 | toLowerInPlace(reply); | |
280 | ||
12c86877 BH |
281 | return reply; |
282 | } | |
283 | ||
b0d4fb45 BH |
284 | inline const string toLowerCanonic(const string &upper) |
285 | { | |
286 | string reply(upper); | |
287 | if(!upper.empty()) { | |
705f31ae | 288 | unsigned int i, limit= ( unsigned int ) reply.length(); |
1352c9b6 | 289 | unsigned char c; |
c536ca8d | 290 | for(i = 0; i < limit ; i++) { |
e3cacd48 BH |
291 | c = dns_tolower(upper[i]); |
292 | if(c != upper[i]) | |
4957a608 | 293 | reply[i] = c; |
3ddb9247 | 294 | } |
e3cacd48 | 295 | if(upper[i-1]=='.') |
b0d4fb45 BH |
296 | reply.resize(i-1); |
297 | } | |
3ddb9247 | 298 | |
b0d4fb45 BH |
299 | return reply; |
300 | } | |
301 | ||
302 | ||
12c86877 | 303 | |
52936200 BH |
304 | // Make s uppercase: |
305 | inline string toUpper( const string& s ) | |
306 | { | |
4957a608 BH |
307 | string r(s); |
308 | for( unsigned int i = 0; i < s.length(); i++ ) { | |
315f45a0 | 309 | r[i] = dns_toupper(r[i]); |
4957a608 BH |
310 | } |
311 | return r; | |
52936200 BH |
312 | } |
313 | ||
a6d7640a BH |
314 | inline double getTime() |
315 | { | |
316 | struct timeval now; | |
15ad1a99 | 317 | gettimeofday(&now,0); |
3ddb9247 | 318 | |
a6d7640a BH |
319 | return now.tv_sec+now.tv_usec/1000000.0; |
320 | } | |
321 | ||
a640a9d4 BH |
322 | inline void unixDie(const string &why) |
323 | { | |
a2a81d42 | 324 | throw runtime_error(why+": "+stringerror()); |
a640a9d4 BH |
325 | } |
326 | ||
2db9c30e | 327 | string makeHexDump(const string& str); |
a02d0fa6 PL |
328 | //! Convert the hexstring in to a byte string |
329 | string makeBytesFromHex(const string &in); | |
88358c9b BH |
330 | |
331 | void normalizeTV(struct timeval& tv); | |
d2adfa5c OM |
332 | struct timeval operator+(const struct timeval& lhs, const struct timeval& rhs); |
333 | struct timeval operator-(const struct timeval& lhs, const struct timeval& rhs); | |
334 | ||
88358c9b BH |
335 | inline float makeFloat(const struct timeval& tv) |
336 | { | |
705f31ae | 337 | return tv.tv_sec + tv.tv_usec/1000000.0f; |
88358c9b | 338 | } |
a71665e5 | 339 | inline uint64_t uSec(const struct timeval& tv) |
7ab9f171 | 340 | { |
a71665e5 | 341 | return tv.tv_sec * 1000000 + tv.tv_usec; |
7ab9f171 | 342 | } |
5b0ddd18 | 343 | |
3ddb9247 | 344 | inline bool operator<(const struct timeval& lhs, const struct timeval& rhs) |
5b0ddd18 | 345 | { |
905dae56 | 346 | return std::tie(lhs.tv_sec, lhs.tv_usec) < std::tie(rhs.tv_sec, rhs.tv_usec); |
50111728 O |
347 | } |
348 | inline bool operator<=(const struct timeval& lhs, const struct timeval& rhs) | |
349 | { | |
905dae56 | 350 | return std::tie(lhs.tv_sec, lhs.tv_usec) <= std::tie(rhs.tv_sec, rhs.tv_usec); |
5b0ddd18 BH |
351 | } |
352 | ||
80a216c9 | 353 | inline bool operator<(const struct timespec& lhs, const struct timespec& rhs) |
354 | { | |
905dae56 | 355 | return std::tie(lhs.tv_sec, lhs.tv_nsec) < std::tie(rhs.tv_sec, rhs.tv_nsec); |
80a216c9 | 356 | } |
357 | ||
358 | ||
ec6480f3 | 359 | inline bool pdns_ilexicographical_compare(const std::string& a, const std::string& b) __attribute__((pure)); |
8fb5b29a | 360 | inline bool pdns_ilexicographical_compare(const std::string& a, const std::string& b) |
7738a23f | 361 | { |
97a24230 | 362 | const unsigned char *aPtr = (const unsigned char*)a.c_str(), *bPtr = (const unsigned char*)b.c_str(); |
7fd7a590 | 363 | const unsigned char *aEptr = aPtr + a.length(), *bEptr = bPtr + b.length(); |
364 | while(aPtr != aEptr && bPtr != bEptr) { | |
8fb5b29a KM |
365 | if ((*aPtr != *bPtr) && (dns_tolower(*aPtr) - dns_tolower(*bPtr))) |
366 | return (dns_tolower(*aPtr) - dns_tolower(*bPtr)) < 0; | |
367 | aPtr++; | |
368 | bPtr++; | |
7738a23f | 369 | } |
7fd7a590 | 370 | if(aPtr == aEptr && bPtr == bEptr) // strings are equal (in length) |
8fb5b29a | 371 | return false; |
7fd7a590 | 372 | return aPtr == aEptr; // true if first string was shorter |
ec6480f3 | 373 | } |
eff65ff8 | 374 | |
ec6480f3 | 375 | inline bool pdns_iequals(const std::string& a, const std::string& b) __attribute__((pure)); |
8fb5b29a | 376 | inline bool pdns_iequals(const std::string& a, const std::string& b) |
ec6480f3 | 377 | { |
8fb5b29a KM |
378 | if (a.length() != b.length()) |
379 | return false; | |
380 | ||
ec6480f3 | 381 | const char *aPtr = a.c_str(), *bPtr = b.c_str(); |
b10795de | 382 | const char *aEptr = aPtr + a.length(); |
383 | while(aPtr != aEptr) { | |
8fb5b29a KM |
384 | if((*aPtr != *bPtr) && (dns_tolower(*aPtr) != dns_tolower(*bPtr))) |
385 | return false; | |
386 | aPtr++; | |
387 | bPtr++; | |
eff65ff8 | 388 | } |
8fb5b29a | 389 | return true; |
ec6480f3 BH |
390 | } |
391 | ||
57cb86d8 CH |
392 | inline bool pdns_iequals_ch(const char a, const char b) __attribute__((pure)); |
393 | inline bool pdns_iequals_ch(const char a, const char b) | |
394 | { | |
395 | if ((a != b) && (dns_tolower(a) != dns_tolower(b))) | |
396 | return false; | |
397 | ||
398 | return true; | |
399 | } | |
400 | ||
1566533a | 401 | |
00c6f2b9 RG |
402 | typedef unsigned long AtomicCounterInner; |
403 | typedef std::atomic<AtomicCounterInner> AtomicCounter ; | |
1bc9e6bd | 404 | |
1e05b07c | 405 | // FIXME400 this should probably go? |
7587bcbe | 406 | struct CIStringCompare |
ec6480f3 BH |
407 | { |
408 | bool operator()(const string& a, const string& b) const | |
eff65ff8 | 409 | { |
ec6480f3 | 410 | return pdns_ilexicographical_compare(a, b); |
eff65ff8 | 411 | } |
7738a23f | 412 | }; |
eff65ff8 | 413 | |
2aa1b703 AT |
414 | struct CIStringComparePOSIX |
415 | { | |
455eba69 | 416 | bool operator() (const std::string& lhs, const std::string& rhs) const |
2aa1b703 | 417 | { |
2aa1b703 | 418 | const std::locale &loc = std::locale("POSIX"); |
455eba69 OM |
419 | auto lhsIter = lhs.begin(); |
420 | auto rhsIter = rhs.begin(); | |
421 | while (lhsIter != lhs.end()) { | |
422 | if (rhsIter == rhs.end() || std::tolower(*rhsIter,loc) < std::tolower(*lhsIter,loc)) { | |
423 | return false; | |
424 | } | |
425 | if (std::tolower(*lhsIter,loc) < std::tolower(*rhsIter,loc)) { | |
426 | return true; | |
427 | } | |
428 | ++lhsIter;++rhsIter; | |
2aa1b703 | 429 | } |
455eba69 | 430 | return rhsIter != rhs.end(); |
2aa1b703 AT |
431 | } |
432 | }; | |
433 | ||
7587bcbe | 434 | struct CIStringPairCompare |
21f0f88b BH |
435 | { |
436 | bool operator()(const pair<string, uint16_t>& a, const pair<string, uint16_t>& b) const | |
437 | { | |
438 | if(pdns_ilexicographical_compare(a.first, b.first)) | |
439 | return true; | |
440 | if(pdns_ilexicographical_compare(b.first, a.first)) | |
441 | return false; | |
442 | return a.second < b.second; | |
443 | } | |
444 | }; | |
445 | ||
57cb86d8 CH |
446 | inline size_t pdns_ci_find(const string& haystack, const string& needle) |
447 | { | |
448 | string::const_iterator it = std::search(haystack.begin(), haystack.end(), | |
449 | needle.begin(), needle.end(), pdns_iequals_ch); | |
450 | if (it == haystack.end()) { | |
451 | // not found | |
452 | return string::npos; | |
453 | } else { | |
454 | return it - haystack.begin(); | |
455 | } | |
456 | } | |
21f0f88b | 457 | |
50c79a76 | 458 | pair<string, string> splitField(const string& inp, char sepa); |
26cd1b3f | 459 | |
e720f311 | 460 | inline bool isCanonical(const string& qname) |
26cd1b3f | 461 | { |
e720f311 | 462 | if(qname.empty()) |
26cd1b3f | 463 | return false; |
e720f311 | 464 | return qname[qname.size()-1]=='.'; |
26cd1b3f BH |
465 | } |
466 | ||
e720f311 | 467 | inline DNSName toCanonic(const DNSName& zone, const string& qname) |
675fa24c | 468 | { |
e720f311 | 469 | if(qname.size()==1 && qname[0]=='@') |
8171ab83 | 470 | return zone; |
e720f311 | 471 | if(isCanonical(qname)) |
8171ab83 | 472 | return DNSName(qname); |
e720f311 | 473 | return DNSName(qname) += zone; |
26cd1b3f | 474 | } |
da042e6e BH |
475 | |
476 | string stripDot(const string& dom); | |
561434a6 | 477 | |
f71bc087 | 478 | int makeIPv6sockaddr(const std::string& addr, struct sockaddr_in6* ret); |
76cb4593 CH |
479 | int makeIPv4sockaddr(const std::string& str, struct sockaddr_in* ret); |
480 | int makeUNsockaddr(const std::string& path, struct sockaddr_un* ret); | |
834942f1 | 481 | bool stringfgets(FILE* fp, std::string& line); |
f0db590d BH |
482 | |
483 | template<typename Index> | |
484 | std::pair<typename Index::iterator,bool> | |
485 | replacing_insert(Index& i,const typename Index::value_type& x) | |
486 | { | |
487 | std::pair<typename Index::iterator,bool> res=i.insert(x); | |
488 | if(!res.second)res.second=i.replace(res.first,x); | |
489 | return res; | |
490 | } | |
491 | ||
3ee84c3f BH |
492 | /** very small regex wrapper */ |
493 | class Regex | |
494 | { | |
495 | public: | |
496 | /** constructor that accepts the expression to regex */ | |
497 | Regex(const string &expr); | |
3ddb9247 | 498 | |
3ee84c3f BH |
499 | ~Regex() |
500 | { | |
501 | regfree(&d_preg); | |
502 | } | |
503 | /** call this to find out if 'line' matches your expression */ | |
6eecd4c2 | 504 | bool match(const string &line) const |
3ee84c3f BH |
505 | { |
506 | return regexec(&d_preg,line.c_str(),0,0,0)==0; | |
507 | } | |
6eecd4c2 | 508 | bool match(const DNSName& name) const |
60ee1829 AT |
509 | { |
510 | return match(name.toStringNoDot()); | |
511 | } | |
512 | ||
3ee84c3f BH |
513 | private: |
514 | regex_t d_preg; | |
515 | }; | |
516 | ||
ac84f00c AT |
517 | class SimpleMatch |
518 | { | |
519 | public: | |
8a70e507 | 520 | SimpleMatch(const string &mask, bool caseFold = false): d_mask(mask), d_fold(caseFold) |
ac84f00c | 521 | { |
ac84f00c | 522 | } |
1e05b07c | 523 | |
fe3b0645 | 524 | bool match(string::const_iterator mi, string::const_iterator mend, string::const_iterator vi, string::const_iterator vend) const |
ac84f00c | 525 | { |
4b5da564 | 526 | for(;;++mi) { |
ac84f00c AT |
527 | if (mi == mend) { |
528 | return vi == vend; | |
529 | } else if (*mi == '?') { | |
530 | if (vi == vend) return false; | |
4b5da564 | 531 | ++vi; |
ac84f00c | 532 | } else if (*mi == '*') { |
17c75221 | 533 | while(mi != mend && *mi == '*') ++mi; |
fe3b0645 | 534 | if (mi == mend) return true; |
ac84f00c AT |
535 | while(vi != vend) { |
536 | if (match(mi,mend,vi,vend)) return true; | |
4b5da564 | 537 | ++vi; |
ac84f00c AT |
538 | } |
539 | return false; | |
540 | } else { | |
541 | if ((mi == mend && vi != vend)|| | |
542 | (mi != mend && vi == vend)) return false; | |
543 | if (d_fold) { | |
544 | if (dns_tolower(*mi) != dns_tolower(*vi)) return false; | |
545 | } else { | |
546 | if (*mi != *vi) return false; | |
547 | } | |
4b5da564 | 548 | ++vi; |
ac84f00c AT |
549 | } |
550 | } | |
551 | } | |
552 | ||
fe3b0645 | 553 | bool match(const string& value) const { |
ac84f00c AT |
554 | return match(d_mask.begin(), d_mask.end(), value.begin(), value.end()); |
555 | } | |
556 | ||
fe3b0645 | 557 | bool match(const DNSName& name) const { |
ac84f00c AT |
558 | return match(name.toStringNoDot()); |
559 | } | |
560 | ||
561 | private: | |
fe3b0645 RG |
562 | const string d_mask; |
563 | const bool d_fold; | |
ac84f00c AT |
564 | }; |
565 | ||
65d8e171 | 566 | union ComboAddress; |
7bec330a | 567 | |
00692909 | 568 | // An aligned type to hold cmsgbufs. See https://man.openbsd.org/CMSG_DATA |
7bec330a OM |
569 | typedef union { struct cmsghdr hdr; char buf[256]; } cmsgbuf_aligned; |
570 | ||
fbe2a2e0 | 571 | /* itfIndex is an interface index, as returned by if_nametoindex(). 0 means default. */ |
7bec330a | 572 | void addCMsgSrcAddr(struct msghdr* msgh, cmsgbuf_aligned* cbuf, const ComboAddress* source, int itfIndex); |
3a8a4d68 | 573 | |
574 | unsigned int getFilenumLimit(bool hardOrSoft=0); | |
575 | void setFilenumLimit(unsigned int lim); | |
4e9a20e6 | 576 | bool readFileIfThere(const char* fname, std::string* line); |
915b0c39 | 577 | bool setSocketTimestamps(int fd); |
15ad1a99 | 578 | |
579 | //! Sets the socket into blocking mode. | |
580 | bool setBlocking( int sock ); | |
581 | ||
582 | //! Sets the socket into non-blocking mode. | |
583 | bool setNonBlocking( int sock ); | |
883a30a7 | 584 | bool setTCPNoDelay(int sock); |
bf676f2f | 585 | bool setReuseAddr(int sock); |
18861f97 | 586 | bool isNonBlocking(int sock); |
29bb743c | 587 | bool setReceiveSocketErrors(int sock, int af); |
d2adfa5c | 588 | int closesocket(int socket); |
3897b9e1 | 589 | bool setCloseOnExec(int sock); |
74ad42dc | 590 | |
ee271fc4 RG |
591 | size_t getPipeBufferSize(int fd); |
592 | bool setPipeBufferSize(int fd, size_t size); | |
593 | ||
594 | uint64_t udpErrorStats(const std::string& str); | |
84d1d33a | 595 | uint64_t udp6ErrorStats(const std::string& str); |
e150c22c | 596 | uint64_t tcpErrorStats(const std::string& str); |
a1a787dc | 597 | uint64_t getRealMemoryUsage(const std::string&); |
330dcb5c | 598 | uint64_t getSpecialMemoryUsage(const std::string&); |
a9b6db56 | 599 | uint64_t getOpenFileDescriptors(const std::string&); |
4f99f3d3 RG |
600 | uint64_t getCPUTimeUser(const std::string&); |
601 | uint64_t getCPUTimeSystem(const std::string&); | |
591d1bd3 RG |
602 | uint64_t getCPUIOWait(const std::string&); |
603 | uint64_t getCPUSteal(const std::string&); | |
6907f014 | 604 | std::string getMACAddress(const ComboAddress& ca); |
6f238a33 | 605 | int getMACAddress(const ComboAddress& ca, char* dest, size_t len); |
1c150f27 | 606 | |
607 | template<typename T> | |
608 | const T& defTer(const T& a, const T& b) | |
609 | { | |
610 | return a ? a : b; | |
611 | } | |
3555371a AT |
612 | |
613 | template<typename P, typename T> | |
614 | T valueOrEmpty(const P val) { | |
615 | if (!val) return T{}; | |
616 | return T(val); | |
617 | } | |
f5f96ff1 | 618 | |
88bf3279 | 619 | |
f5f96ff1 | 620 | // I'm not very OCD, but I appreciate loglines like "processing 1 delta", "processing 2 deltas" :-) |
7f3b2173 | 621 | template <typename Integer, |
abb11ca4 | 622 | typename std::enable_if_t<std::is_integral_v<Integer>, bool> = true> |
7f3b2173 | 623 | const char* addS(Integer siz, const char* singular = "", const char *plural = "s") |
f5f96ff1 | 624 | { |
7f3b2173 O |
625 | if (siz == 1) { |
626 | return singular; | |
627 | } | |
628 | return plural; | |
f5f96ff1 | 629 | } |
88bf3279 | 630 | |
7f3b2173 | 631 | template <typename C, |
abb11ca4 | 632 | typename std::enable_if_t<std::is_class_v<C>, bool> = true> |
7f3b2173 | 633 | const char* addS(const C& c, const char* singular = "", const char *plural = "s") |
88bf3279 | 634 | { |
7f3b2173 | 635 | return addS(c.size(), singular, plural); |
88bf3279 | 636 | } |
637 | ||
2ac8ae89 | 638 | template<typename C> |
639 | const typename C::value_type::second_type* rplookup(const C& c, const typename C::value_type::first_type& key) | |
640 | { | |
641 | auto fnd = c.find(key); | |
642 | if(fnd == c.end()) | |
643 | return 0; | |
644 | return &fnd->second; | |
645 | } | |
646 | ||
3fcaeeac | 647 | double DiffTime(const struct timespec& first, const struct timespec& second); |
648 | double DiffTime(const struct timeval& first, const struct timeval& second); | |
ffb07158 | 649 | uid_t strToUID(const string &str); |
650 | gid_t strToGID(const string &str); | |
a5c4e4a6 | 651 | |
a0383aad FM |
652 | namespace pdns |
653 | { | |
654 | /** | |
655 | * \brief Does a checked conversion from one integer type to another. | |
656 | * | |
657 | * \warning The source type `F` and target type `T` must have the same | |
658 | * signedness, otherwise a compilation error is thrown. | |
659 | * | |
660 | * \exception std::out_of_range Thrown if the source value does not fit | |
661 | * in the target type. | |
662 | * | |
663 | * \param[in] from The source value of type `F`. | |
664 | * | |
665 | * \return The target value of type `T`. | |
666 | */ | |
667 | template <typename T, typename F> | |
668 | auto checked_conv(F from) -> T | |
669 | { | |
670 | static_assert(std::numeric_limits<F>::is_integer, "checked_conv: The `F` type must be an integer"); | |
671 | static_assert(std::numeric_limits<T>::is_integer, "checked_conv: The `T` type must be an integer"); | |
672 | static_assert((std::numeric_limits<F>::is_signed && std::numeric_limits<T>::is_signed) || (!std::numeric_limits<F>::is_signed && !std::numeric_limits<T>::is_signed), | |
673 | "checked_conv: The `T` and `F` types must either both be signed or unsigned"); | |
674 | ||
bfd1f31b FM |
675 | constexpr auto tMin = std::numeric_limits<T>::min(); |
676 | if constexpr (std::numeric_limits<F>::min() != tMin) { | |
677 | if (from < tMin) { | |
678 | string s = "checked_conv: source value " + std::to_string(from) + " is smaller than target's minimum possible value " + std::to_string(tMin); | |
679 | throw std::out_of_range(s); | |
680 | } | |
681 | } | |
682 | ||
683 | constexpr auto tMax = std::numeric_limits<T>::max(); | |
684 | if constexpr (std::numeric_limits<F>::max() != tMax) { | |
685 | if (from > tMax) { | |
686 | string s = "checked_conv: source value " + std::to_string(from) + " is larger than target's maximum possible value " + std::to_string(tMax); | |
687 | throw std::out_of_range(s); | |
688 | } | |
a0383aad FM |
689 | } |
690 | ||
691 | return static_cast<T>(from); | |
692 | } | |
693 | ||
694 | /** | |
695 | * \brief Performs a conversion from `std::string&` to integer. | |
696 | * | |
697 | * This function internally calls `std::stoll` and `std::stoull` to do | |
698 | * the conversion from `std::string&` and calls `pdns::checked_conv` to | |
699 | * do the checked conversion from `long long`/`unsigned long long` to | |
700 | * `T`. | |
701 | * | |
702 | * \warning The target type `T` must be an integer, otherwise a | |
703 | * compilation error is thrown. | |
704 | * | |
705 | * \exception std:stoll Throws what std::stoll throws. | |
706 | * | |
707 | * \exception std::stoull Throws what std::stoull throws. | |
708 | * | |
709 | * \exception pdns::checked_conv Throws what pdns::checked_conv throws. | |
710 | * | |
711 | * \param[in] str The input string to be converted. | |
712 | * | |
713 | * \param[in] idx Location to store the index at which processing | |
222b61ff | 714 | * stopped. If the input `str` is empty, `*idx` shall be set to 0. |
a0383aad FM |
715 | * |
716 | * \param[in] base The numerical base for conversion. | |
717 | * | |
718 | * \return `str` converted to integer `T`, or 0 if `str` is empty. | |
719 | */ | |
720 | template <typename T> | |
721 | auto checked_stoi(const std::string& str, size_t* idx = nullptr, int base = 10) -> T | |
722 | { | |
723 | static_assert(std::numeric_limits<T>::is_integer, "checked_stoi: The `T` type must be an integer"); | |
724 | ||
725 | if (str.empty()) { | |
222b61ff FM |
726 | if (idx != nullptr) { |
727 | *idx = 0; | |
728 | } | |
729 | ||
a0383aad FM |
730 | return 0; // compatibility |
731 | } | |
732 | ||
733 | if constexpr (std::is_unsigned_v<T>) { | |
734 | return pdns::checked_conv<T>(std::stoull(str, idx, base)); | |
735 | } | |
736 | else { | |
737 | return pdns::checked_conv<T>(std::stoll(str, idx, base)); | |
738 | } | |
739 | } | |
740 | ||
741 | /** | |
742 | * \brief Performs a conversion from `std::string&` to integer. | |
743 | * | |
744 | * This function internally calls `pdns::checked_stoi` and stores its | |
745 | * result in `out`. | |
746 | * | |
747 | * \exception pdns::checked_stoi Throws what pdns::checked_stoi throws. | |
748 | * | |
749 | * \param[out] out `str` converted to integer `T`, or 0 if `str` is | |
750 | * empty. | |
751 | * | |
752 | * \param[in] str The input string to be converted. | |
753 | * | |
754 | * \param[in] idx Location to store the index at which processing | |
222b61ff | 755 | * stopped. If the input `str` is empty, `*idx` shall be set to 0. |
a0383aad FM |
756 | * |
757 | * \param[in] base The numerical base for conversion. | |
758 | * | |
759 | * \return `str` converted to integer `T`, or 0 if `str` is empty. | |
760 | */ | |
761 | template <typename T> | |
762 | auto checked_stoi_into(T& out, const std::string& str, size_t* idx = nullptr, int base = 10) | |
763 | { | |
764 | out = checked_stoi<T>(str, idx, base); | |
765 | } | |
766 | } | |
60a1c204 | 767 | |
8fd25133 RG |
768 | bool isSettingThreadCPUAffinitySupported(); |
769 | int mapThreadToCPUList(pthread_t tid, const std::set<int>& cpus); | |
5d4e1ef8 RG |
770 | |
771 | std::vector<ComboAddress> getResolvers(const std::string& resolvConfPath); | |
ef3ee606 RG |
772 | |
773 | DNSName reverseNameFromIP(const ComboAddress& ip); | |
64d38231 | 774 | |
b1a048a9 | 775 | size_t parseRFC1035CharString(const std::string &in, std::string &val); // from ragel |
e701f9d4 PL |
776 | size_t parseSVCBValueListFromParsedRFC1035CharString(const std::string &in, vector<std::string> &val); // from ragel |
777 | size_t parseSVCBValueList(const std::string &in, vector<std::string> &val); | |
032382bc PD |
778 | |
779 | std::string makeLuaString(const std::string& in); | |
786ed0ff | 780 | |
b2504b29 RG |
781 | bool constantTimeStringEquals(const std::string& a, const std::string& b); |
782 | ||
786ed0ff | 783 | // Used in NID and L64 records |
2c510d90 | 784 | struct NodeOrLocatorID { uint8_t content[8]; }; |
ae3b96d9 RG |
785 | |
786 | struct FDWrapper | |
787 | { | |
d2adfa5c OM |
788 | FDWrapper() = default; |
789 | FDWrapper(int desc): d_fd(desc) {} | |
790 | FDWrapper(const FDWrapper&) = delete; | |
791 | FDWrapper& operator=(const FDWrapper& rhs) = delete; | |
ae3b96d9 | 792 | |
ae3b96d9 RG |
793 | |
794 | ~FDWrapper() | |
795 | { | |
5841c6fd | 796 | reset(); |
ae3b96d9 RG |
797 | } |
798 | ||
d2adfa5c | 799 | FDWrapper(FDWrapper&& rhs) noexcept : d_fd(rhs.d_fd) |
ae3b96d9 RG |
800 | { |
801 | rhs.d_fd = -1; | |
802 | } | |
803 | ||
d2adfa5c | 804 | FDWrapper& operator=(FDWrapper&& rhs) noexcept |
ae3b96d9 | 805 | { |
54b2435e | 806 | if (d_fd != -1) { |
ae3b96d9 RG |
807 | close(d_fd); |
808 | } | |
809 | d_fd = rhs.d_fd; | |
810 | rhs.d_fd = -1; | |
811 | return *this; | |
812 | } | |
813 | ||
d2adfa5c | 814 | [[nodiscard]] int getHandle() const |
ae3b96d9 RG |
815 | { |
816 | return d_fd; | |
817 | } | |
818 | ||
0adbef11 RG |
819 | operator int() const |
820 | { | |
821 | return d_fd; | |
822 | } | |
823 | ||
2f17d188 | 824 | int reset() |
5841c6fd | 825 | { |
2f17d188 | 826 | int ret = 0; |
5841c6fd | 827 | if (d_fd != -1) { |
2f17d188 | 828 | ret = close(d_fd); |
5841c6fd RG |
829 | d_fd = -1; |
830 | } | |
2f17d188 | 831 | return ret; |
5841c6fd RG |
832 | } |
833 | ||
ae3b96d9 RG |
834 | private: |
835 | int d_fd{-1}; | |
836 | }; | |
d3190b7d RG |
837 | |
838 | namespace pdns | |
839 | { | |
840 | [[nodiscard]] std::optional<std::string> visit_directory(const std::string& directory, const std::function<bool(ino_t inodeNumber, const std::string_view& name)>& visitor); | |
20b2f204 RG |
841 | |
842 | struct FilePtrDeleter | |
843 | { | |
844 | /* using a deleter instead of decltype(&fclose) has two big advantages: | |
845 | - the deleter is included in the type and does not have to be passed | |
846 | when creating a new object (easier to use, less memory usage, in theory | |
847 | better inlining) | |
848 | - we avoid the annoying "ignoring attributes on template argument ‘int (*)(FILE*)’" | |
849 | warning from the compiler, which is there because fclose is tagged as __nonnull((1)) | |
850 | */ | |
07d4785d | 851 | void operator()(FILE* filePtr) const noexcept { |
20b2f204 RG |
852 | fclose(filePtr); |
853 | } | |
854 | }; | |
855 | ||
856 | using UniqueFilePtr = std::unique_ptr<FILE, FilePtrDeleter>; | |
b1564d45 RG |
857 | |
858 | UniqueFilePtr openFileForWriting(const std::string& filePath, mode_t permissions, bool mustNotExist = true, bool appendIfExists = false); | |
d3190b7d | 859 | } |