/* * 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 { /** * \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; #if defined(HAVE_LIBCRYPTO) 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_v, "rfc1982LessThan only works for unsigned types"); return std::make_signed_t(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) const { const std::locale &loc = std::locale("POSIX"); auto lhsIter = lhs.begin(); auto rhsIter = rhs.begin(); while (lhsIter != lhs.end()) { if (rhsIter == rhs.end() || std::tolower(*rhsIter,loc) < std::tolower(*lhsIter,loc)) { return false; } if (std::tolower(*lhsIter,loc) < std::tolower(*rhsIter,loc)) { return true; } ++lhsIter;++rhsIter; } return rhsIter != rhs.end(); } }; struct CIStringPairCompare { bool operator()(const pair& 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 , bool> = true> const char* addS(Integer siz, const char* singular = "", const char *plural = "s") { if (siz == 1) { return singular; } return plural; } template , 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() { reset(); } FDWrapper(FDWrapper&& rhs) noexcept : d_fd(rhs.d_fd) { rhs.d_fd = -1; } FDWrapper& operator=(FDWrapper&& rhs) noexcept { if (d_fd >= 0) { 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; } void reset() { if (d_fd >= 0) { close(d_fd); } d_fd = -1; } private: int d_fd{-1}; }; namespace pdns { [[nodiscard]] std::optional visit_directory(const std::string& directory, const std::function& visitor); struct FilePtrDeleter { /* using a deleter instead of decltype(&fclose) has two big advantages: - the deleter is included in the type and does not have to be passed when creating a new object (easier to use, less memory usage, in theory better inlining) - we avoid the annoying "ignoring attributes on template argument ‘int (*)(FILE*)’" warning from the compiler, which is there because fclose is tagged as __nonnull((1)) */ void operator()(FILE* filePtr) const noexcept { fclose(filePtr); } }; using UniqueFilePtr = std::unique_ptr; UniqueFilePtr openFileForWriting(const std::string& filePath, mode_t permissions, bool mustNotExist = true, bool appendIfExists = false); }