From 3cd01b932e1c85394272ae64fae67ebeda92fb00 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 23:11:59 +0200 Subject: Adding upstream version 1.8.3. Signed-off-by: Daniel Baumann --- misc.hh | 826 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 826 insertions(+) create mode 100644 misc.hh (limited to 'misc.hh') diff --git a/misc.hh b/misc.hh new file mode 100644 index 0000000..8800cd7 --- /dev/null +++ b/misc.hh @@ -0,0 +1,826 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#pragma once +#include +#include +#include +#include +#include +#include + +#include + +#include "dns.hh" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "namespaces.hh" + +class DNSName; + +// Do not change to "using TSIGHashEnum ..." until you know CodeQL does not choke on it +typedef enum { TSIG_MD5, TSIG_SHA1, TSIG_SHA224, TSIG_SHA256, TSIG_SHA384, TSIG_SHA512, TSIG_GSS } TSIGHashEnum; +namespace pdns +{ +#if defined(HAVE_LIBCRYPTO) +/** + * \brief Retrieves the errno-based error message in a reentrant way. + * + * This internally handles the portability issues around using + * `strerror_r` and returns a `std::string` that owns the error + * message's contents. + * + * \param[in] errnum The errno value. + * + * \return The `std::string` error message. + */ +auto getMessageFromErrno(int errnum) -> std::string; + +namespace OpenSSL +{ + /** + * \brief Throws a `std::runtime_error` with the current OpenSSL error. + * + * \param[in] errorMessage The message to attach in addition to the OpenSSL error. + */ + [[nodiscard]] auto error(const std::string& errorMessage) -> std::runtime_error; + + /** + * \brief Throws a `std::runtime_error` with a name and the current OpenSSL error. + * + * \param[in] componentName The name of the component to mark the error message with. + * \param[in] errorMessage The message to attach in addition to the OpenSSL error. + */ + [[nodiscard]] auto error(const std::string& componentName, const std::string& errorMessage) -> std::runtime_error; +} +#endif // HAVE_LIBCRYPTO +} + +string nowTime(); +string unquotify(const string &item); +string humanDuration(time_t passed); +bool stripDomainSuffix(string *qname, const string &domain); +void stripLine(string &line); +std::optional getHostname(); +std::string getCarbonHostName(); +string urlEncode(const string &text); +int waitForData(int fileDesc, int seconds, int useconds = 0); +int waitFor2Data(int fd1, int fd2, int seconds, int useconds, int* fd); +int waitForMultiData(const set& fds, const int seconds, const int useconds, int* fd); +int waitForRWData(int fileDesc, bool waitForRead, int seconds, int useconds, bool* error = nullptr, bool* disconnected = nullptr); +bool getTSIGHashEnum(const DNSName& algoName, TSIGHashEnum& algoEnum); +DNSName getTSIGAlgoName(TSIGHashEnum& algoEnum); + +int logFacilityToLOG(unsigned int facility); + +template +void +stringtok (Container &container, string const &in, + const char * const delimiters = " \t\n") +{ + const string::size_type len = in.length(); + string::size_type i = 0; + + while (i bool rfc1982LessThan(T a, T b) +{ + static_assert(std::is_unsigned::value, "rfc1982LessThan only works for unsigned types"); + typedef typename std::make_signed::type signed_t; + return static_cast(a - b) < 0; +} + +// fills container with ranges, so {posbegin,posend} +template +void +vstringtok (Container &container, string const &in, + const char * const delimiters = " \t\n") +{ + const string::size_type len = in.length(); + string::size_type i = 0; + + while (i AtomicCounter ; + +// FIXME400 this should probably go? +struct CIStringCompare +{ + bool operator()(const string& a, const string& b) const + { + return pdns_ilexicographical_compare(a, b); + } +}; + +struct CIStringComparePOSIX +{ + bool operator() (const std::string& lhs, const std::string& rhs) + { + std::string::const_iterator a,b; + const std::locale &loc = std::locale("POSIX"); + a=lhs.begin();b=rhs.begin(); + while(a!=lhs.end()) { + if (b==rhs.end() || std::tolower(*b,loc)& a, const pair& b) const + { + if(pdns_ilexicographical_compare(a.first, b.first)) + return true; + if(pdns_ilexicographical_compare(b.first, a.first)) + return false; + return a.second < b.second; + } +}; + +inline size_t pdns_ci_find(const string& haystack, const string& needle) +{ + string::const_iterator it = std::search(haystack.begin(), haystack.end(), + needle.begin(), needle.end(), pdns_iequals_ch); + if (it == haystack.end()) { + // not found + return string::npos; + } else { + return it - haystack.begin(); + } +} + +pair splitField(const string& inp, char sepa); + +inline bool isCanonical(const string& qname) +{ + if(qname.empty()) + return false; + return qname[qname.size()-1]=='.'; +} + +inline DNSName toCanonic(const DNSName& zone, const string& qname) +{ + if(qname.size()==1 && qname[0]=='@') + return zone; + if(isCanonical(qname)) + return DNSName(qname); + return DNSName(qname) += zone; +} + +string stripDot(const string& dom); + +int makeIPv6sockaddr(const std::string& addr, struct sockaddr_in6* ret); +int makeIPv4sockaddr(const std::string& str, struct sockaddr_in* ret); +int makeUNsockaddr(const std::string& path, struct sockaddr_un* ret); +bool stringfgets(FILE* fp, std::string& line); + +template +std::pair +replacing_insert(Index& i,const typename Index::value_type& x) +{ + std::pair res=i.insert(x); + if(!res.second)res.second=i.replace(res.first,x); + return res; +} + +/** very small regex wrapper */ +class Regex +{ +public: + /** constructor that accepts the expression to regex */ + Regex(const string &expr); + + ~Regex() + { + regfree(&d_preg); + } + /** call this to find out if 'line' matches your expression */ + bool match(const string &line) const + { + return regexec(&d_preg,line.c_str(),0,0,0)==0; + } + bool match(const DNSName& name) const + { + return match(name.toStringNoDot()); + } + +private: + regex_t d_preg; +}; + +class SimpleMatch +{ +public: + SimpleMatch(const string &mask, bool caseFold = false): d_mask(mask), d_fold(caseFold) + { + } + + bool match(string::const_iterator mi, string::const_iterator mend, string::const_iterator vi, string::const_iterator vend) const + { + for(;;++mi) { + if (mi == mend) { + return vi == vend; + } else if (*mi == '?') { + if (vi == vend) return false; + ++vi; + } else if (*mi == '*') { + while(mi != mend && *mi == '*') ++mi; + if (mi == mend) return true; + while(vi != vend) { + if (match(mi,mend,vi,vend)) return true; + ++vi; + } + return false; + } else { + if ((mi == mend && vi != vend)|| + (mi != mend && vi == vend)) return false; + if (d_fold) { + if (dns_tolower(*mi) != dns_tolower(*vi)) return false; + } else { + if (*mi != *vi) return false; + } + ++vi; + } + } + } + + bool match(const string& value) const { + return match(d_mask.begin(), d_mask.end(), value.begin(), value.end()); + } + + bool match(const DNSName& name) const { + return match(name.toStringNoDot()); + } + +private: + const string d_mask; + const bool d_fold; +}; + +union ComboAddress; + +// An aligned type to hold cmsgbufs. See https://man.openbsd.org/CMSG_DATA +typedef union { struct cmsghdr hdr; char buf[256]; } cmsgbuf_aligned; + +/* itfIndex is an interface index, as returned by if_nametoindex(). 0 means default. */ +void addCMsgSrcAddr(struct msghdr* msgh, cmsgbuf_aligned* cbuf, const ComboAddress* source, int itfIndex); + +unsigned int getFilenumLimit(bool hardOrSoft=0); +void setFilenumLimit(unsigned int lim); +bool readFileIfThere(const char* fname, std::string* line); +bool setSocketTimestamps(int fd); + +//! Sets the socket into blocking mode. +bool setBlocking( int sock ); + +//! Sets the socket into non-blocking mode. +bool setNonBlocking( int sock ); +bool setTCPNoDelay(int sock); +bool setReuseAddr(int sock); +bool isNonBlocking(int sock); +bool setReceiveSocketErrors(int sock, int af); +int closesocket(int socket); +bool setCloseOnExec(int sock); + +size_t getPipeBufferSize(int fd); +bool setPipeBufferSize(int fd, size_t size); + +uint64_t udpErrorStats(const std::string& str); +uint64_t udp6ErrorStats(const std::string& str); +uint64_t tcpErrorStats(const std::string& str); +uint64_t getRealMemoryUsage(const std::string&); +uint64_t getSpecialMemoryUsage(const std::string&); +uint64_t getOpenFileDescriptors(const std::string&); +uint64_t getCPUTimeUser(const std::string&); +uint64_t getCPUTimeSystem(const std::string&); +uint64_t getCPUIOWait(const std::string&); +uint64_t getCPUSteal(const std::string&); +std::string getMACAddress(const ComboAddress& ca); +int getMACAddress(const ComboAddress& ca, char* dest, size_t len); + +template +const T& defTer(const T& a, const T& b) +{ + return a ? a : b; +} + +template +T valueOrEmpty(const P val) { + if (!val) return T{}; + return T(val); +} + + +// I'm not very OCD, but I appreciate loglines like "processing 1 delta", "processing 2 deltas" :-) +template ::value, bool> = true> +const char* addS(Integer siz, const char* singular = "", const char *plural = "s") +{ + if (siz == 1) { + return singular; + } + return plural; +} + +template ::value, bool> = true> +const char* addS(const C& c, const char* singular = "", const char *plural = "s") +{ + return addS(c.size(), singular, plural); +} + +template +const typename C::value_type::second_type* rplookup(const C& c, const typename C::value_type::first_type& key) +{ + auto fnd = c.find(key); + if(fnd == c.end()) + return 0; + return &fnd->second; +} + +double DiffTime(const struct timespec& first, const struct timespec& second); +double DiffTime(const struct timeval& first, const struct timeval& second); +uid_t strToUID(const string &str); +gid_t strToGID(const string &str); + +namespace pdns +{ +/** + * \brief Does a checked conversion from one integer type to another. + * + * \warning The source type `F` and target type `T` must have the same + * signedness, otherwise a compilation error is thrown. + * + * \exception std::out_of_range Thrown if the source value does not fit + * in the target type. + * + * \param[in] from The source value of type `F`. + * + * \return The target value of type `T`. + */ +template +auto checked_conv(F from) -> T +{ + static_assert(std::numeric_limits::is_integer, "checked_conv: The `F` type must be an integer"); + static_assert(std::numeric_limits::is_integer, "checked_conv: The `T` type must be an integer"); + static_assert((std::numeric_limits::is_signed && std::numeric_limits::is_signed) || (!std::numeric_limits::is_signed && !std::numeric_limits::is_signed), + "checked_conv: The `T` and `F` types must either both be signed or unsigned"); + + constexpr auto tMin = std::numeric_limits::min(); + if constexpr (std::numeric_limits::min() != tMin) { + if (from < tMin) { + string s = "checked_conv: source value " + std::to_string(from) + " is smaller than target's minimum possible value " + std::to_string(tMin); + throw std::out_of_range(s); + } + } + + constexpr auto tMax = std::numeric_limits::max(); + if constexpr (std::numeric_limits::max() != tMax) { + if (from > tMax) { + string s = "checked_conv: source value " + std::to_string(from) + " is larger than target's maximum possible value " + std::to_string(tMax); + throw std::out_of_range(s); + } + } + + return static_cast(from); +} + +/** + * \brief Performs a conversion from `std::string&` to integer. + * + * This function internally calls `std::stoll` and `std::stoull` to do + * the conversion from `std::string&` and calls `pdns::checked_conv` to + * do the checked conversion from `long long`/`unsigned long long` to + * `T`. + * + * \warning The target type `T` must be an integer, otherwise a + * compilation error is thrown. + * + * \exception std:stoll Throws what std::stoll throws. + * + * \exception std::stoull Throws what std::stoull throws. + * + * \exception pdns::checked_conv Throws what pdns::checked_conv throws. + * + * \param[in] str The input string to be converted. + * + * \param[in] idx Location to store the index at which processing + * stopped. If the input `str` is empty, `*idx` shall be set to 0. + * + * \param[in] base The numerical base for conversion. + * + * \return `str` converted to integer `T`, or 0 if `str` is empty. + */ +template +auto checked_stoi(const std::string& str, size_t* idx = nullptr, int base = 10) -> T +{ + static_assert(std::numeric_limits::is_integer, "checked_stoi: The `T` type must be an integer"); + + if (str.empty()) { + if (idx != nullptr) { + *idx = 0; + } + + return 0; // compatibility + } + + if constexpr (std::is_unsigned_v) { + return pdns::checked_conv(std::stoull(str, idx, base)); + } + else { + return pdns::checked_conv(std::stoll(str, idx, base)); + } +} + +/** + * \brief Performs a conversion from `std::string&` to integer. + * + * This function internally calls `pdns::checked_stoi` and stores its + * result in `out`. + * + * \exception pdns::checked_stoi Throws what pdns::checked_stoi throws. + * + * \param[out] out `str` converted to integer `T`, or 0 if `str` is + * empty. + * + * \param[in] str The input string to be converted. + * + * \param[in] idx Location to store the index at which processing + * stopped. If the input `str` is empty, `*idx` shall be set to 0. + * + * \param[in] base The numerical base for conversion. + * + * \return `str` converted to integer `T`, or 0 if `str` is empty. + */ +template +auto checked_stoi_into(T& out, const std::string& str, size_t* idx = nullptr, int base = 10) +{ + out = checked_stoi(str, idx, base); +} +} + +bool isSettingThreadCPUAffinitySupported(); +int mapThreadToCPUList(pthread_t tid, const std::set& cpus); + +std::vector getResolvers(const std::string& resolvConfPath); + +DNSName reverseNameFromIP(const ComboAddress& ip); + +size_t parseRFC1035CharString(const std::string &in, std::string &val); // from ragel +size_t parseSVCBValueListFromParsedRFC1035CharString(const std::string &in, vector &val); // from ragel +size_t parseSVCBValueList(const std::string &in, vector &val); + +std::string makeLuaString(const std::string& in); + +bool constantTimeStringEquals(const std::string& a, const std::string& b); + +// Used in NID and L64 records +struct NodeOrLocatorID { uint8_t content[8]; }; + +struct FDWrapper +{ + FDWrapper() = default; + FDWrapper(int desc): d_fd(desc) {} + FDWrapper(const FDWrapper&) = delete; + FDWrapper& operator=(const FDWrapper& rhs) = delete; + + + ~FDWrapper() + { + if (d_fd != -1) { + close(d_fd); + d_fd = -1; + } + } + + FDWrapper(FDWrapper&& rhs) noexcept : d_fd(rhs.d_fd) + { + rhs.d_fd = -1; + } + + FDWrapper& operator=(FDWrapper&& rhs) noexcept + { + if (d_fd != -1) { + close(d_fd); + } + d_fd = rhs.d_fd; + rhs.d_fd = -1; + return *this; + } + + [[nodiscard]] int getHandle() const + { + return d_fd; + } + + operator int() const + { + return d_fd; + } + +private: + int d_fd{-1}; +}; -- cgit v1.2.3