]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/misc.cc
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
27 #include <sys/param.h>
28 #include <sys/socket.h>
33 #include <sys/resource.h>
34 #include <netinet/in.h>
45 #include <sys/types.h>
50 #include <netinet/tcp.h>
54 #include "pdnsexception.hh"
55 #include <boost/algorithm/string.hpp>
56 #include <boost/format.hpp>
58 #include "dnsparser.hh"
59 #include "dns_random.hh"
64 # include <pthread_np.h>
71 #if defined(HAVE_LIBCRYPTO)
72 #include <openssl/err.h>
73 #endif // HAVE_LIBCRYPTO
75 size_t writen2(int fileDesc
, const void *buf
, size_t count
)
77 const char *ptr
= static_cast<const char*>(buf
);
78 const char *eptr
= ptr
+ count
;
81 auto res
= ::write(fileDesc
, ptr
, eptr
- ptr
);
83 if (errno
== EAGAIN
) {
84 throw std::runtime_error("used writen2 on non-blocking socket, got EAGAIN");
86 unixDie("failed in writen2");
89 throw std::runtime_error("could not write all bytes, got eof in writen2");
98 size_t readn2(int fd
, void* buffer
, size_t len
)
103 res
= read(fd
, (char*)buffer
+ pos
, len
- pos
);
105 throw runtime_error("EOF while reading message");
108 throw std::runtime_error("used readn2 on non-blocking socket, got EAGAIN");
110 unixDie("failed in readn2");
120 size_t readn2WithTimeout(int fd
, void* buffer
, size_t len
, const struct timeval
& idleTimeout
, const struct timeval
& totalTimeout
, bool allowIncomplete
)
123 struct timeval start
{0,0};
124 struct timeval remainingTime
= totalTimeout
;
125 if (totalTimeout
.tv_sec
!= 0 || totalTimeout
.tv_usec
!= 0) {
126 gettimeofday(&start
, nullptr);
130 ssize_t got
= read(fd
, (char *)buffer
+ pos
, len
- pos
);
133 if (allowIncomplete
) {
138 throw runtime_error("EOF while reading message");
141 if (errno
== EAGAIN
) {
142 struct timeval w
= ((totalTimeout
.tv_sec
== 0 && totalTimeout
.tv_usec
== 0) || idleTimeout
<= remainingTime
) ? idleTimeout
: remainingTime
;
143 int res
= waitForData(fd
, w
.tv_sec
, w
.tv_usec
);
145 /* there is data available */
148 throw runtime_error("Timeout while waiting for data to read");
150 throw runtime_error("Error while waiting for data to read");
154 unixDie("failed in readn2WithTimeout");
158 if (totalTimeout
.tv_sec
!= 0 || totalTimeout
.tv_usec
!= 0) {
160 gettimeofday(&now
, nullptr);
161 struct timeval elapsed
= now
- start
;
162 if (remainingTime
< elapsed
) {
163 throw runtime_error("Timeout while reading data");
166 remainingTime
= remainingTime
- elapsed
;
174 size_t writen2WithTimeout(int fd
, const void * buffer
, size_t len
, const struct timeval
& timeout
)
178 ssize_t written
= write(fd
, reinterpret_cast<const char *>(buffer
) + pos
, len
- pos
);
181 pos
+= (size_t) written
;
183 else if (written
== 0)
184 throw runtime_error("EOF while writing message");
186 if (errno
== EAGAIN
) {
187 int res
= waitForRWData(fd
, false, timeout
.tv_sec
, timeout
.tv_usec
);
189 /* there is room available */
192 throw runtime_error("Timeout while waiting to write data");
194 throw runtime_error("Error while waiting for room to write data");
198 unixDie("failed in write2WithTimeout");
207 auto pdns::getMessageFromErrno(const int errnum
) -> std::string
209 const size_t errLen
= 2048;
210 std::string errMsgData
{};
211 errMsgData
.resize(errLen
);
213 const char* errMsg
= nullptr;
214 #ifdef STRERROR_R_CHAR_P
215 errMsg
= strerror_r(errnum
, errMsgData
.data(), errMsgData
.length());
217 // This can fail, and when it does, it sets errno. We ignore that and
218 // set our own error message instead.
219 int res
= strerror_r(errnum
, errMsgData
.data(), errMsgData
.length());
220 errMsg
= errMsgData
.c_str();
222 errMsg
= "Unknown (the exact error could not be retrieved)";
226 // We make a copy here because `strerror_r()` might return a static
227 // immutable buffer for an error message. The copy shouldn't be
228 // critical though, we're on the bailout/error-handling path anyways.
229 std::string message
{errMsg
};
233 #if defined(HAVE_LIBCRYPTO)
234 auto pdns::OpenSSL::error(const std::string
& errorMessage
) -> std::runtime_error
236 unsigned long errorCode
= 0;
237 auto fullErrorMessage
{errorMessage
};
238 #if OPENSSL_VERSION_MAJOR >= 3
239 const char* filename
= nullptr;
240 const char* functionName
= nullptr;
242 while ((errorCode
= ERR_get_error_all(&filename
, &lineNumber
, &functionName
, nullptr, nullptr)) != 0) {
243 fullErrorMessage
+= std::string(": ") + std::to_string(errorCode
);
245 const auto* lib
= ERR_lib_error_string(errorCode
);
246 if (lib
!= nullptr) {
247 fullErrorMessage
+= std::string(":") + lib
;
250 const auto* reason
= ERR_reason_error_string(errorCode
);
251 if (reason
!= nullptr) {
252 fullErrorMessage
+= std::string("::") + reason
;
255 if (filename
!= nullptr) {
256 fullErrorMessage
+= std::string(" - ") + filename
;
258 if (lineNumber
!= 0) {
259 fullErrorMessage
+= std::string(":") + std::to_string(lineNumber
);
261 if (functionName
!= nullptr) {
262 fullErrorMessage
+= std::string(" - ") + functionName
;
266 while ((errorCode
= ERR_get_error()) != 0) {
267 fullErrorMessage
+= std::string(": ") + std::to_string(errorCode
);
269 const auto* lib
= ERR_lib_error_string(errorCode
);
270 if (lib
!= nullptr) {
271 fullErrorMessage
+= std::string(":") + lib
;
274 const auto* func
= ERR_func_error_string(errorCode
);
275 if (func
!= nullptr) {
276 fullErrorMessage
+= std::string(":") + func
;
279 const auto* reason
= ERR_reason_error_string(errorCode
);
280 if (reason
!= nullptr) {
281 fullErrorMessage
+= std::string("::") + reason
;
285 return std::runtime_error(fullErrorMessage
);
288 auto pdns::OpenSSL::error(const std::string
& componentName
, const std::string
& errorMessage
) -> std::runtime_error
290 return pdns::OpenSSL::error(componentName
+ ": " + errorMessage
);
292 #endif // HAVE_LIBCRYPTO
296 time_t now
= time(nullptr);
298 localtime_r(&now
, &theTime
);
299 std::array
<char, 30> buffer
{};
300 // YYYY-mm-dd HH:MM:SS TZOFF
301 size_t ret
= strftime(buffer
.data(), buffer
.size(), "%F %T %z", &theTime
);
305 return {buffer
.data()};
308 static bool ciEqual(const string
& lhs
, const string
& rhs
)
310 if (lhs
.size() != rhs
.size()) {
314 string::size_type pos
= 0;
315 const string::size_type epos
= lhs
.size();
316 for (; pos
< epos
; ++pos
) {
317 if (dns_tolower(lhs
[pos
]) != dns_tolower(rhs
[pos
])) {
324 /** does domain end on suffix? Is smart about "wwwds9a.nl" "ds9a.nl" not matching */
325 static bool endsOn(const string
&domain
, const string
&suffix
)
327 if( suffix
.empty() || ciEqual(domain
, suffix
) ) {
331 if(domain
.size() <= suffix
.size()) {
335 string::size_type dpos
= domain
.size() - suffix
.size() - 1;
336 string::size_type spos
= 0;
338 if (domain
[dpos
++] != '.') {
342 for(; dpos
< domain
.size(); ++dpos
, ++spos
) {
343 if (dns_tolower(domain
[dpos
]) != dns_tolower(suffix
[spos
])) {
351 /** strips a domain suffix from a domain, returns true if it stripped */
352 bool stripDomainSuffix(string
*qname
, const string
&domain
)
354 if (!endsOn(*qname
, domain
)) {
358 if (toLower(*qname
) == toLower(domain
)) {
362 if ((*qname
)[qname
->size() - domain
.size() - 1] != '.') {
366 qname
->resize(qname
->size() - domain
.size()-1);
371 // returns -1 in case if error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
372 int waitForData(int fileDesc
, int seconds
, int useconds
)
374 return waitForRWData(fileDesc
, true, seconds
, useconds
);
377 int waitForRWData(int fileDesc
, bool waitForRead
, int seconds
, int useconds
, bool* error
, bool* disconnected
)
380 memset(&pfd
, 0, sizeof(pfd
));
387 pfd
.events
= POLLOUT
;
390 int ret
= poll(&pfd
, 1, seconds
* 1000 + useconds
/1000);
392 if ((error
!= nullptr) && (pfd
.revents
& POLLERR
) != 0) {
395 if ((disconnected
!= nullptr) && (pfd
.revents
& POLLHUP
) != 0) {
396 *disconnected
= true;
403 // returns -1 in case of error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
404 int waitForMultiData(const set
<int>& fds
, const int seconds
, const int useconds
, int* fdOut
) {
406 for (const auto& fd
: fds
) {
407 if (fd
>= 0 && realFDs
.count(fd
) == 0) {
412 std::vector
<struct pollfd
> pfds(realFDs
.size());
413 memset(pfds
.data(), 0, realFDs
.size()*sizeof(struct pollfd
));
415 for (const auto& fd
: realFDs
) {
417 pfds
[ctr
].events
= POLLIN
;
423 ret
= poll(pfds
.data(), realFDs
.size(), seconds
* 1000 + useconds
/1000);
425 ret
= poll(pfds
.data(), realFDs
.size(), -1);
430 for (const auto& pfd
: pfds
) {
431 if (pfd
.revents
& POLLIN
) {
432 pollinFDs
.insert(pfd
.fd
);
435 set
<int>::const_iterator
it(pollinFDs
.begin());
436 advance(it
, dns_random(pollinFDs
.size()));
441 // returns -1 in case of error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
442 int waitFor2Data(int fd1
, int fd2
, int seconds
, int useconds
, int* fdPtr
)
444 std::array
<pollfd
,2> pfds
{};
445 memset(pfds
.data(), 0, pfds
.size() * sizeof(struct pollfd
));
449 pfds
[0].events
= pfds
[1].events
= POLLIN
;
451 int nsocks
= 1 + static_cast<int>(fd2
>= 0); // fd2 can optionally be -1
455 ret
= poll(pfds
.data(), nsocks
, seconds
* 1000 + useconds
/ 1000);
458 ret
= poll(pfds
.data(), nsocks
, -1);
464 if ((pfds
[0].revents
& POLLIN
) != 0 && (pfds
[1].revents
& POLLIN
) == 0) {
467 else if ((pfds
[1].revents
& POLLIN
) != 0 && (pfds
[0].revents
& POLLIN
) == 0) {
471 *fdPtr
= pfds
.at(dns_random_uint32() % 2).fd
;
474 *fdPtr
= -1; // should never happen
481 string
humanDuration(time_t passed
)
485 ret
<<passed
<<" seconds";
487 ret
<<std::setprecision(2)<<passed
/60.0<<" minutes";
488 else if(passed
<86400)
489 ret
<<std::setprecision(3)<<passed
/3600.0<<" hours";
490 else if(passed
<(86400*30.41))
491 ret
<<std::setprecision(3)<<passed
/86400.0<<" days";
493 ret
<<std::setprecision(3)<<passed
/(86400*30.41)<<" months";
498 string
unquotify(const string
&item
)
503 string::size_type bpos
=0, epos
=item
.size();
508 if(item
[epos
-1]=='"')
511 return item
.substr(bpos
,epos
-bpos
);
514 void stripLine(string
&line
)
516 string::size_type pos
=line
.find_first_of("\r\n");
517 if(pos
!=string::npos
) {
522 string
urlEncode(const string
&text
)
526 if(i
==' ')ret
.append("%20");
527 else ret
.append(1,i
);
531 static size_t getMaxHostNameSize()
533 #if defined(HOST_NAME_MAX)
534 return HOST_NAME_MAX
;
537 #if defined(_SC_HOST_NAME_MAX)
538 auto tmp
= sysconf(_SC_HOST_NAME_MAX
);
544 const size_t maxHostNameSize
= 255;
545 return maxHostNameSize
;
548 std::optional
<string
> getHostname()
550 const size_t maxHostNameBufSize
= getMaxHostNameSize() + 1;
551 std::string hostname
;
552 hostname
.resize(maxHostNameBufSize
, 0);
554 if (gethostname(hostname
.data(), maxHostNameBufSize
) == -1) {
558 hostname
.resize(strlen(hostname
.c_str()));
559 return std::make_optional(hostname
);
562 std::string
getCarbonHostName()
564 auto hostname
= getHostname();
565 if (!hostname
.has_value()) {
566 throw std::runtime_error(stringerror());
569 boost::replace_all(*hostname
, ".", "_");
573 string
bitFlip(const string
&str
)
575 string::size_type pos
=0, epos
=str
.size();
578 for(;pos
< epos
; ++pos
)
579 ret
.append(1, ~str
[pos
]);
583 void cleanSlashes(string
&str
)
586 bool keepNextSlash
= true;
587 for (const auto& value
: str
) {
590 keepNextSlash
= false;
597 keepNextSlash
= true;
599 out
.append(1, value
);
601 str
= std::move(out
);
604 bool IpToU32(const string
&str
, uint32_t *ip
)
612 if(inet_aton(str
.c_str(), &inp
)) {
619 string
U32ToIP(uint32_t val
)
622 snprintf(tmp
, sizeof(tmp
), "%u.%u.%u.%u",
631 string
makeHexDump(const string
& str
)
633 std::array
<char, 5> tmp
;
635 ret
.reserve(static_cast<size_t>(str
.size()*2.2));
638 snprintf(tmp
.data(), tmp
.size(), "%02x ", static_cast<unsigned char>(n
));
644 string
makeBytesFromHex(const string
&in
) {
645 if (in
.size() % 2 != 0) {
646 throw std::range_error("odd number of bytes in hex string");
649 ret
.reserve(in
.size() / 2);
651 for (size_t i
= 0; i
< in
.size(); i
+= 2) {
652 const auto numStr
= in
.substr(i
, 2);
653 unsigned int num
= 0;
654 if (sscanf(numStr
.c_str(), "%02x", &num
) != 1) {
655 throw std::range_error("Invalid value while parsing the hex string '" + in
+ "'");
657 ret
.push_back(static_cast<uint8_t>(num
));
663 void normalizeTV(struct timeval
& tv
)
665 if(tv
.tv_usec
> 1000000) {
669 else if(tv
.tv_usec
< 0) {
675 struct timeval
operator+(const struct timeval
& lhs
, const struct timeval
& rhs
)
678 ret
.tv_sec
=lhs
.tv_sec
+ rhs
.tv_sec
;
679 ret
.tv_usec
=lhs
.tv_usec
+ rhs
.tv_usec
;
684 struct timeval
operator-(const struct timeval
& lhs
, const struct timeval
& rhs
)
687 ret
.tv_sec
=lhs
.tv_sec
- rhs
.tv_sec
;
688 ret
.tv_usec
=lhs
.tv_usec
- rhs
.tv_usec
;
693 pair
<string
, string
> splitField(const string
& inp
, char sepa
)
695 pair
<string
, string
> ret
;
696 string::size_type cpos
=inp
.find(sepa
);
697 if(cpos
==string::npos
)
700 ret
.first
=inp
.substr(0, cpos
);
701 ret
.second
=inp
.substr(cpos
+1);
706 int logFacilityToLOG(unsigned int facility
)
730 string
stripDot(const string
& dom
)
735 if(dom
[dom
.size()-1]!='.')
738 return dom
.substr(0,dom
.size()-1);
741 int makeIPv6sockaddr(const std::string
& addr
, struct sockaddr_in6
* ret
)
747 string
ourAddr(addr
);
748 std::optional
<uint16_t> port
= std::nullopt
;
750 if (addr
[0] == '[') { // [::]:53 style address
751 string::size_type pos
= addr
.find(']');
752 if (pos
== string::npos
) {
756 ourAddr
.assign(addr
.c_str() + 1, pos
- 1);
757 if (pos
+ 1 != addr
.size()) { // complete after ], no port specified
758 if (pos
+ 2 > addr
.size() || addr
[pos
+ 1] != ':') {
763 auto tmpPort
= pdns::checked_stoi
<uint16_t>(addr
.substr(pos
+ 2));
764 port
= std::make_optional(tmpPort
);
766 catch (const std::out_of_range
&) {
772 ret
->sin6_scope_id
= 0;
773 ret
->sin6_family
= AF_INET6
;
775 if (inet_pton(AF_INET6
, ourAddr
.c_str(), (void*)&ret
->sin6_addr
) != 1) {
776 struct addrinfo hints
{};
777 std::memset(&hints
, 0, sizeof(struct addrinfo
));
778 hints
.ai_flags
= AI_NUMERICHOST
;
779 hints
.ai_family
= AF_INET6
;
781 struct addrinfo
* res
= nullptr;
782 // getaddrinfo has anomalous return codes, anything nonzero is an error, positive or negative
783 if (getaddrinfo(ourAddr
.c_str(), nullptr, &hints
, &res
) != 0) {
787 memcpy(ret
, res
->ai_addr
, res
->ai_addrlen
);
791 if (port
.has_value()) {
792 ret
->sin6_port
= htons(*port
);
798 int makeIPv4sockaddr(const std::string
& str
, struct sockaddr_in
* ret
)
805 string::size_type pos
= str
.find(':');
806 if(pos
== string::npos
) { // no port specified, not touching the port
807 if(inet_aton(str
.c_str(), &inp
)) {
808 ret
->sin_addr
.s_addr
=inp
.s_addr
;
813 if(!*(str
.c_str() + pos
+ 1)) // trailing :
816 char *eptr
= const_cast<char*>(str
.c_str()) + str
.size();
817 int port
= strtol(str
.c_str() + pos
+ 1, &eptr
, 10);
818 if (port
< 0 || port
> 65535)
824 ret
->sin_port
= htons(port
);
825 if(inet_aton(str
.substr(0, pos
).c_str(), &inp
)) {
826 ret
->sin_addr
.s_addr
=inp
.s_addr
;
832 int makeUNsockaddr(const std::string
& path
, struct sockaddr_un
* ret
)
837 memset(ret
, 0, sizeof(struct sockaddr_un
));
838 ret
->sun_family
= AF_UNIX
;
839 if (path
.length() >= sizeof(ret
->sun_path
))
842 path
.copy(ret
->sun_path
, sizeof(ret
->sun_path
), 0);
846 //! read a line of text from a FILE* to a std::string, returns false on 'no data'
847 bool stringfgets(FILE* fp
, std::string
& line
)
853 if(!fgets(buffer
, sizeof(buffer
), fp
))
854 return !line
.empty();
857 } while(!strchr(buffer
, '\n'));
861 bool readFileIfThere(const char* fname
, std::string
* line
)
864 auto fp
= std::unique_ptr
<FILE, int(*)(FILE*)>(fopen(fname
, "r"), fclose
);
868 return stringfgets(fp
.get(), *line
);
871 Regex::Regex(const string
&expr
)
873 if(regcomp(&d_preg
, expr
.c_str(), REG_ICASE
|REG_NOSUB
|REG_EXTENDED
))
874 throw PDNSException("Regular expression did not compile");
877 // if you end up here because valgrind told you were are doing something wrong
878 // with msgh->msg_controllen, please refer to https://github.com/PowerDNS/pdns/pull/3962
880 // Note that cmsgbuf should be aligned the same as a struct cmsghdr
881 void addCMsgSrcAddr(struct msghdr
* msgh
, cmsgbuf_aligned
* cmsgbuf
, const ComboAddress
* source
, int itfIndex
)
883 struct cmsghdr
*cmsg
= nullptr;
885 if(source
->sin4
.sin_family
== AF_INET6
) {
886 struct in6_pktinfo
*pkt
;
888 msgh
->msg_control
= cmsgbuf
;
889 #if !defined( __APPLE__ )
890 /* CMSG_SPACE is not a constexpr on macOS */
891 static_assert(CMSG_SPACE(sizeof(*pkt
)) <= sizeof(*cmsgbuf
), "Buffer is too small for in6_pktinfo");
892 #else /* __APPLE__ */
893 if (CMSG_SPACE(sizeof(*pkt
)) > sizeof(*cmsgbuf
)) {
894 throw std::runtime_error("Buffer is too small for in6_pktinfo");
896 #endif /* __APPLE__ */
897 msgh
->msg_controllen
= CMSG_SPACE(sizeof(*pkt
));
899 cmsg
= CMSG_FIRSTHDR(msgh
);
900 cmsg
->cmsg_level
= IPPROTO_IPV6
;
901 cmsg
->cmsg_type
= IPV6_PKTINFO
;
902 cmsg
->cmsg_len
= CMSG_LEN(sizeof(*pkt
));
904 pkt
= (struct in6_pktinfo
*) CMSG_DATA(cmsg
);
905 // Include the padding to stop valgrind complaining about passing uninitialized data
906 memset(pkt
, 0, CMSG_SPACE(sizeof(*pkt
)));
907 pkt
->ipi6_addr
= source
->sin6
.sin6_addr
;
908 pkt
->ipi6_ifindex
= itfIndex
;
911 #if defined(IP_PKTINFO)
912 struct in_pktinfo
*pkt
;
914 msgh
->msg_control
= cmsgbuf
;
915 #if !defined( __APPLE__ )
916 /* CMSG_SPACE is not a constexpr on macOS */
917 static_assert(CMSG_SPACE(sizeof(*pkt
)) <= sizeof(*cmsgbuf
), "Buffer is too small for in_pktinfo");
918 #else /* __APPLE__ */
919 if (CMSG_SPACE(sizeof(*pkt
)) > sizeof(*cmsgbuf
)) {
920 throw std::runtime_error("Buffer is too small for in_pktinfo");
922 #endif /* __APPLE__ */
923 msgh
->msg_controllen
= CMSG_SPACE(sizeof(*pkt
));
925 cmsg
= CMSG_FIRSTHDR(msgh
);
926 cmsg
->cmsg_level
= IPPROTO_IP
;
927 cmsg
->cmsg_type
= IP_PKTINFO
;
928 cmsg
->cmsg_len
= CMSG_LEN(sizeof(*pkt
));
930 pkt
= (struct in_pktinfo
*) CMSG_DATA(cmsg
);
931 // Include the padding to stop valgrind complaining about passing uninitialized data
932 memset(pkt
, 0, CMSG_SPACE(sizeof(*pkt
)));
933 pkt
->ipi_spec_dst
= source
->sin4
.sin_addr
;
934 pkt
->ipi_ifindex
= itfIndex
;
935 #elif defined(IP_SENDSRCADDR)
938 msgh
->msg_control
= cmsgbuf
;
939 #if !defined( __APPLE__ )
940 static_assert(CMSG_SPACE(sizeof(*in
)) <= sizeof(*cmsgbuf
), "Buffer is too small for in_addr");
941 #else /* __APPLE__ */
942 if (CMSG_SPACE(sizeof(*in
)) > sizeof(*cmsgbuf
)) {
943 throw std::runtime_error("Buffer is too small for in_addr");
945 #endif /* __APPLE__ */
946 msgh
->msg_controllen
= CMSG_SPACE(sizeof(*in
));
948 cmsg
= CMSG_FIRSTHDR(msgh
);
949 cmsg
->cmsg_level
= IPPROTO_IP
;
950 cmsg
->cmsg_type
= IP_SENDSRCADDR
;
951 cmsg
->cmsg_len
= CMSG_LEN(sizeof(*in
));
953 // Include the padding to stop valgrind complaining about passing uninitialized data
954 in
= (struct in_addr
*) CMSG_DATA(cmsg
);
955 memset(in
, 0, CMSG_SPACE(sizeof(*in
)));
956 *in
= source
->sin4
.sin_addr
;
961 unsigned int getFilenumLimit(bool hardOrSoft
)
964 if(getrlimit(RLIMIT_NOFILE
, &rlim
) < 0)
965 unixDie("Requesting number of available file descriptors");
966 return hardOrSoft
? rlim
.rlim_max
: rlim
.rlim_cur
;
969 void setFilenumLimit(unsigned int lim
)
973 if(getrlimit(RLIMIT_NOFILE
, &rlim
) < 0)
974 unixDie("Requesting number of available file descriptors");
976 if(setrlimit(RLIMIT_NOFILE
, &rlim
) < 0)
977 unixDie("Setting number of available file descriptors");
980 bool setSocketTimestamps(int fd
)
984 return setsockopt(fd
, SOL_SOCKET
, SO_TIMESTAMP
, (char*)&on
, sizeof(on
)) == 0;
986 return true; // we pretend this happened.
990 bool setTCPNoDelay(int sock
)
993 return setsockopt(sock
, /* socket affected */
994 IPPROTO_TCP
, /* set option at TCP level */
995 TCP_NODELAY
, /* name of option */
996 (char *) &flag
, /* the cast is historical cruft */
997 sizeof(flag
)) == 0; /* length of option value */
1001 bool setNonBlocking(int sock
)
1003 int flags
=fcntl(sock
,F_GETFL
,0);
1004 if(flags
<0 || fcntl(sock
, F_SETFL
,flags
|O_NONBLOCK
) <0)
1009 bool setBlocking(int sock
)
1011 int flags
=fcntl(sock
,F_GETFL
,0);
1012 if(flags
<0 || fcntl(sock
, F_SETFL
,flags
&(~O_NONBLOCK
)) <0)
1017 bool setReuseAddr(int sock
)
1020 if (setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (char*)&tmp
, static_cast<unsigned>(sizeof tmp
))<0)
1021 throw PDNSException(string("Setsockopt failed: ")+stringerror());
1025 bool isNonBlocking(int sock
)
1027 int flags
=fcntl(sock
,F_GETFL
,0);
1028 return flags
& O_NONBLOCK
;
1031 bool setReceiveSocketErrors([[maybe_unused
]] int sock
, [[maybe_unused
]] int af
)
1035 if (af
== AF_INET
) {
1036 ret
= setsockopt(sock
, IPPROTO_IP
, IP_RECVERR
, &tmp
, sizeof(tmp
));
1038 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_RECVERR
, &tmp
, sizeof(tmp
));
1041 throw PDNSException(string("Setsockopt failed: ") + stringerror());
1048 int closesocket(int socket
)
1050 int ret
= ::close(socket
);
1051 if(ret
< 0 && errno
== ECONNRESET
) { // see ticket 192, odd BSD behaviour
1056 throw PDNSException("Error closing socket: " + stringerror(err
));
1061 bool setCloseOnExec(int sock
)
1063 int flags
=fcntl(sock
,F_GETFD
,0);
1064 if(flags
<0 || fcntl(sock
, F_SETFD
,flags
|FD_CLOEXEC
) <0)
1070 #include <linux/rtnetlink.h>
1072 int getMACAddress(const ComboAddress
& ca
, char* dest
, size_t destLen
)
1075 struct nlmsghdr headermsg
;
1076 struct ndmsg neighbormsg
;
1079 std::array
<char, 8192> buffer
;
1081 auto sock
= FDWrapper(socket(AF_NETLINK
, SOCK_RAW
|SOCK_CLOEXEC
, NETLINK_ROUTE
));
1082 if (sock
.getHandle() == -1) {
1086 memset(&request
, 0, sizeof(request
));
1087 request
.headermsg
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct ndmsg
));
1088 request
.headermsg
.nlmsg_flags
= NLM_F_REQUEST
| NLM_F_DUMP
;
1089 request
.headermsg
.nlmsg_type
= RTM_GETNEIGH
;
1090 request
.neighbormsg
.ndm_family
= ca
.sin4
.sin_family
;
1093 ssize_t sent
= send(sock
.getHandle(), &request
, sizeof(request
), 0);
1095 if (errno
== EINTR
) {
1100 else if (static_cast<size_t>(sent
) != sizeof(request
)) {
1107 bool foundIP
= false;
1108 bool foundMAC
= false;
1110 ssize_t got
= recv(sock
.getHandle(), buffer
.data(), buffer
.size(), 0);
1113 if (errno
== EINTR
) {
1119 size_t remaining
= static_cast<size_t>(got
);
1120 for (struct nlmsghdr
* nlmsgheader
= reinterpret_cast<struct nlmsghdr
*>(buffer
.data());
1121 done
== false && NLMSG_OK (nlmsgheader
, remaining
);
1122 nlmsgheader
= reinterpret_cast<struct nlmsghdr
*>(NLMSG_NEXT(nlmsgheader
, remaining
))) {
1124 if (nlmsgheader
->nlmsg_type
== NLMSG_DONE
) {
1129 auto nd
= reinterpret_cast<struct ndmsg
*>(NLMSG_DATA(nlmsgheader
));
1130 auto rtatp
= reinterpret_cast<struct rtattr
*>(reinterpret_cast<char*>(nd
) + NLMSG_ALIGN(sizeof(struct ndmsg
)));
1131 int rtattrlen
= nlmsgheader
->nlmsg_len
- NLMSG_LENGTH(sizeof(struct ndmsg
));
1133 if (nd
->ndm_family
!= ca
.sin4
.sin_family
) {
1137 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
) {
1141 for (; done
== false && RTA_OK(rtatp
, rtattrlen
); rtatp
= RTA_NEXT(rtatp
, rtattrlen
)) {
1142 if (rtatp
->rta_type
== NDA_DST
){
1143 if (nd
->ndm_family
== AF_INET
) {
1144 auto inp
= reinterpret_cast<struct in_addr
*>(RTA_DATA(rtatp
));
1145 if (inp
->s_addr
== ca
.sin4
.sin_addr
.s_addr
) {
1149 else if (nd
->ndm_family
== AF_INET6
) {
1150 auto inp
= reinterpret_cast<struct in6_addr
*>(RTA_DATA(rtatp
));
1151 if (memcmp(inp
->s6_addr
, ca
.sin6
.sin6_addr
.s6_addr
, sizeof(ca
.sin6
.sin6_addr
.s6_addr
)) == 0) {
1156 else if (rtatp
->rta_type
== NDA_LLADDR
) {
1158 size_t addrLen
= rtatp
->rta_len
- sizeof(struct rtattr
);
1159 if (addrLen
> destLen
) {
1162 memcpy(dest
, reinterpret_cast<const char*>(rtatp
) + sizeof(struct rtattr
), addrLen
);
1171 while (done
== false);
1173 return foundMAC
? 0 : ENOENT
;
1176 int getMACAddress(const ComboAddress
& /* ca */, char* /* dest */, size_t /* len */)
1180 #endif /* __linux__ */
1182 string
getMACAddress(const ComboAddress
& ca
)
1186 if (getMACAddress(ca
, tmp
, sizeof(tmp
)) == 0) {
1187 ret
.append(tmp
, sizeof(tmp
));
1192 uint64_t udpErrorStats([[maybe_unused
]] const std::string
& str
)
1195 ifstream
ifs("/proc/net/snmp");
1201 while (getline(ifs
, line
)) {
1202 if (boost::starts_with(line
, "Udp: ") && isdigit(line
.at(5))) {
1203 vector
<string
> parts
;
1204 stringtok(parts
, line
, " \n\t\r");
1206 if (parts
.size() < 7) {
1210 if (str
== "udp-rcvbuf-errors") {
1211 return std::stoull(parts
.at(5));
1213 else if (str
== "udp-sndbuf-errors") {
1214 return std::stoull(parts
.at(6));
1216 else if (str
== "udp-noport-errors") {
1217 return std::stoull(parts
.at(2));
1219 else if (str
== "udp-in-errors") {
1220 return std::stoull(parts
.at(3));
1222 else if (parts
.size() >= 8 && str
== "udp-in-csum-errors") {
1223 return std::stoull(parts
.at(7));
1234 uint64_t udp6ErrorStats([[maybe_unused
]] const std::string
& str
)
1237 const std::map
<std::string
, std::string
> keys
= {
1238 { "udp6-in-errors", "Udp6InErrors" },
1239 { "udp6-recvbuf-errors", "Udp6RcvbufErrors" },
1240 { "udp6-sndbuf-errors", "Udp6SndbufErrors" },
1241 { "udp6-noport-errors", "Udp6NoPorts" },
1242 { "udp6-in-csum-errors", "Udp6InCsumErrors" }
1245 auto key
= keys
.find(str
);
1246 if (key
== keys
.end()) {
1250 ifstream
ifs("/proc/net/snmp6");
1256 while (getline(ifs
, line
)) {
1257 if (!boost::starts_with(line
, key
->second
)) {
1261 std::vector
<std::string
> parts
;
1262 stringtok(parts
, line
, " \n\t\r");
1264 if (parts
.size() != 2) {
1268 return std::stoull(parts
.at(1));
1274 uint64_t tcpErrorStats(const std::string
& /* str */)
1277 ifstream
ifs("/proc/net/netstat");
1283 vector
<string
> parts
;
1284 while (getline(ifs
,line
)) {
1285 if (line
.size() > 9 && boost::starts_with(line
, "TcpExt: ") && isdigit(line
.at(8))) {
1286 stringtok(parts
, line
, " \n\t\r");
1288 if (parts
.size() < 21) {
1292 return std::stoull(parts
.at(20));
1299 uint64_t getCPUIOWait(const std::string
& /* str */)
1302 ifstream
ifs("/proc/stat");
1308 vector
<string
> parts
;
1309 while (getline(ifs
, line
)) {
1310 if (boost::starts_with(line
, "cpu ")) {
1311 stringtok(parts
, line
, " \n\t\r");
1313 if (parts
.size() < 6) {
1317 return std::stoull(parts
[5]);
1324 uint64_t getCPUSteal(const std::string
& /* str */)
1327 ifstream
ifs("/proc/stat");
1333 vector
<string
> parts
;
1334 while (getline(ifs
, line
)) {
1335 if (boost::starts_with(line
, "cpu ")) {
1336 stringtok(parts
, line
, " \n\t\r");
1338 if (parts
.size() < 9) {
1342 return std::stoull(parts
[8]);
1349 bool getTSIGHashEnum(const DNSName
& algoName
, TSIGHashEnum
& algoEnum
)
1351 if (algoName
== DNSName("hmac-md5.sig-alg.reg.int") || algoName
== DNSName("hmac-md5"))
1352 algoEnum
= TSIG_MD5
;
1353 else if (algoName
== DNSName("hmac-sha1"))
1354 algoEnum
= TSIG_SHA1
;
1355 else if (algoName
== DNSName("hmac-sha224"))
1356 algoEnum
= TSIG_SHA224
;
1357 else if (algoName
== DNSName("hmac-sha256"))
1358 algoEnum
= TSIG_SHA256
;
1359 else if (algoName
== DNSName("hmac-sha384"))
1360 algoEnum
= TSIG_SHA384
;
1361 else if (algoName
== DNSName("hmac-sha512"))
1362 algoEnum
= TSIG_SHA512
;
1363 else if (algoName
== DNSName("gss-tsig"))
1364 algoEnum
= TSIG_GSS
;
1371 DNSName
getTSIGAlgoName(TSIGHashEnum
& algoEnum
)
1374 case TSIG_MD5
: return DNSName("hmac-md5.sig-alg.reg.int.");
1375 case TSIG_SHA1
: return DNSName("hmac-sha1.");
1376 case TSIG_SHA224
: return DNSName("hmac-sha224.");
1377 case TSIG_SHA256
: return DNSName("hmac-sha256.");
1378 case TSIG_SHA384
: return DNSName("hmac-sha384.");
1379 case TSIG_SHA512
: return DNSName("hmac-sha512.");
1380 case TSIG_GSS
: return DNSName("gss-tsig.");
1382 throw PDNSException("getTSIGAlgoName does not understand given algorithm, please fix!");
1385 uint64_t getOpenFileDescriptors(const std::string
&)
1388 uint64_t nbFileDescriptors
= 0;
1389 const auto dirName
= "/proc/" + std::to_string(getpid()) + "/fd/";
1390 auto directoryError
= pdns::visit_directory(dirName
, [&nbFileDescriptors
]([[maybe_unused
]] ino_t inodeNumber
, const std::string_view
& name
) {
1393 pdns::checked_stoi_into(num
, std::string(name
));
1394 if (std::to_string(num
) == name
) {
1395 nbFileDescriptors
++;
1398 // was not a number.
1402 if (directoryError
) {
1405 return nbFileDescriptors
;
1406 #elif defined(__OpenBSD__)
1407 // FreeBSD also has this in libopenbsd, but I don't know if that's available always
1408 return getdtablecount();
1414 uint64_t getRealMemoryUsage(const std::string
&)
1417 ifstream
ifs("/proc/self/statm");
1421 uint64_t size
, resident
, shared
, text
, lib
, data
;
1422 ifs
>> size
>> resident
>> shared
>> text
>> lib
>> data
;
1424 // We used to use "data" here, but it proves unreliable and even is marked "broken"
1425 // in https://www.kernel.org/doc/html/latest/filesystems/proc.html
1426 return resident
* getpagesize();
1429 if (getrusage(RUSAGE_SELF
, &ru
) != 0)
1431 return ru
.ru_maxrss
* 1024;
1436 uint64_t getSpecialMemoryUsage(const std::string
&)
1439 ifstream
ifs("/proc/self/smaps");
1444 string
header("Private_Dirty:");
1445 while(getline(ifs
, line
)) {
1446 if(boost::starts_with(line
, header
)) {
1447 bytes
+= std::stoull(line
.substr(header
.length() + 1))*1024;
1456 uint64_t getCPUTimeUser(const std::string
&)
1459 getrusage(RUSAGE_SELF
, &ru
);
1460 return (ru
.ru_utime
.tv_sec
*1000ULL + ru
.ru_utime
.tv_usec
/1000);
1463 uint64_t getCPUTimeSystem(const std::string
&)
1466 getrusage(RUSAGE_SELF
, &ru
);
1467 return (ru
.ru_stime
.tv_sec
*1000ULL + ru
.ru_stime
.tv_usec
/1000);
1470 double DiffTime(const struct timespec
& first
, const struct timespec
& second
)
1472 auto seconds
= second
.tv_sec
- first
.tv_sec
;
1473 auto nseconds
= second
.tv_nsec
- first
.tv_nsec
;
1477 nseconds
+= 1000000000;
1479 return static_cast<double>(seconds
) + static_cast<double>(nseconds
) / 1000000000.0;
1482 double DiffTime(const struct timeval
& first
, const struct timeval
& second
)
1484 int seconds
=second
.tv_sec
- first
.tv_sec
;
1485 int useconds
=second
.tv_usec
- first
.tv_usec
;
1491 return seconds
+ useconds
/1000000.0;
1494 uid_t
strToUID(const string
&str
)
1497 const char * cstr
= str
.c_str();
1498 struct passwd
* pwd
= getpwnam(cstr
);
1500 if (pwd
== nullptr) {
1506 catch(std::exception
& e
) {
1507 throw runtime_error((boost::format("Error: Unable to parse user ID %s") % cstr
).str() );
1510 if (val
< std::numeric_limits
<uid_t
>::min() || val
> std::numeric_limits
<uid_t
>::max()) {
1511 throw runtime_error((boost::format("Error: Unable to parse user ID %s") % cstr
).str() );
1514 result
= static_cast<uid_t
>(val
);
1517 result
= pwd
->pw_uid
;
1523 gid_t
strToGID(const string
&str
)
1526 const char * cstr
= str
.c_str();
1527 struct group
* grp
= getgrnam(cstr
);
1529 if (grp
== nullptr) {
1535 catch(std::exception
& e
) {
1536 throw runtime_error((boost::format("Error: Unable to parse group ID %s") % cstr
).str() );
1539 if (val
< std::numeric_limits
<gid_t
>::min() || val
> std::numeric_limits
<gid_t
>::max()) {
1540 throw runtime_error((boost::format("Error: Unable to parse group ID %s") % cstr
).str() );
1543 result
= static_cast<gid_t
>(val
);
1546 result
= grp
->gr_gid
;
1552 bool isSettingThreadCPUAffinitySupported()
1554 #ifdef HAVE_PTHREAD_SETAFFINITY_NP
1561 int mapThreadToCPUList([[maybe_unused
]] pthread_t tid
, [[maybe_unused
]] const std::set
<int>& cpus
)
1563 #ifdef HAVE_PTHREAD_SETAFFINITY_NP
1566 cpuset
= cpuset_create();
1567 for (const auto cpuID
: cpus
) {
1568 cpuset_set(cpuID
, cpuset
);
1571 return pthread_setaffinity_np(tid
,
1572 cpuset_size(cpuset
),
1576 # define cpu_set_t cpuset_t
1580 for (const auto cpuID
: cpus
) {
1581 CPU_SET(cpuID
, &cpuset
);
1584 return pthread_setaffinity_np(tid
,
1590 #endif /* HAVE_PTHREAD_SETAFFINITY_NP */
1593 std::vector
<ComboAddress
> getResolvers(const std::string
& resolvConfPath
)
1595 std::vector
<ComboAddress
> results
;
1597 ifstream
ifs(resolvConfPath
);
1603 while(std::getline(ifs
, line
)) {
1604 boost::trim_right_if(line
, boost::is_any_of(" \r\n\x1a"));
1605 boost::trim_left(line
); // leading spaces, let's be nice
1607 string::size_type tpos
= line
.find_first_of(";#");
1608 if (tpos
!= string::npos
) {
1612 if (boost::starts_with(line
, "nameserver ") || boost::starts_with(line
, "nameserver\t")) {
1613 vector
<string
> parts
;
1614 stringtok(parts
, line
, " \t,"); // be REALLY nice
1615 for (auto iter
= parts
.begin() + 1; iter
!= parts
.end(); ++iter
) {
1617 results
.emplace_back(*iter
, 53);
1629 size_t getPipeBufferSize([[maybe_unused
]] int fd
)
1632 int res
= fcntl(fd
, F_GETPIPE_SZ
);
1640 #endif /* F_GETPIPE_SZ */
1643 bool setPipeBufferSize([[maybe_unused
]] int fd
, [[maybe_unused
]] size_t size
)
1646 if (size
> static_cast<size_t>(std::numeric_limits
<int>::max())) {
1650 int newSize
= static_cast<int>(size
);
1651 int res
= fcntl(fd
, F_SETPIPE_SZ
, newSize
);
1659 #endif /* F_SETPIPE_SZ */
1662 DNSName
reverseNameFromIP(const ComboAddress
& ip
)
1665 std::string
result("in-addr.arpa.");
1666 auto ptr
= reinterpret_cast<const uint8_t*>(&ip
.sin4
.sin_addr
.s_addr
);
1667 for (size_t idx
= 0; idx
< sizeof(ip
.sin4
.sin_addr
.s_addr
); idx
++) {
1668 result
= std::to_string(ptr
[idx
]) + "." + result
;
1670 return DNSName(result
);
1672 else if (ip
.isIPv6()) {
1673 std::string
result("ip6.arpa.");
1674 auto ptr
= reinterpret_cast<const uint8_t*>(&ip
.sin6
.sin6_addr
.s6_addr
[0]);
1675 for (size_t idx
= 0; idx
< sizeof(ip
.sin6
.sin6_addr
.s6_addr
); idx
++) {
1676 std::stringstream stream
;
1677 stream
<< std::hex
<< (ptr
[idx
] & 0x0F);
1679 stream
<< std::hex
<< (((ptr
[idx
]) >> 4) & 0x0F);
1681 result
= stream
.str() + result
;
1683 return DNSName(result
);
1686 throw std::runtime_error("Calling reverseNameFromIP() for an address which is neither an IPv4 nor an IPv6");
1689 std::string
makeLuaString(const std::string
& in
)
1696 for (unsigned char n
: in
) {
1697 if (islower(n
) || isupper(n
)) {
1702 snprintf(item
, sizeof(item
), "\\%03d", n
);
1712 size_t parseSVCBValueList(const std::string
&in
, vector
<std::string
> &val
) {
1714 auto ret
= parseRFC1035CharString(in
, parsed
);
1715 parseSVCBValueListFromParsedRFC1035CharString(parsed
, val
);
1719 #ifdef HAVE_CRYPTO_MEMCMP
1720 #include <openssl/crypto.h>
1721 #else /* HAVE_CRYPTO_MEMCMP */
1722 #ifdef HAVE_SODIUM_MEMCMP
1724 #endif /* HAVE_SODIUM_MEMCMP */
1725 #endif /* HAVE_CRYPTO_MEMCMP */
1727 bool constantTimeStringEquals(const std::string
& a
, const std::string
& b
)
1729 if (a
.size() != b
.size()) {
1732 const size_t size
= a
.size();
1733 #ifdef HAVE_CRYPTO_MEMCMP
1734 return CRYPTO_memcmp(a
.c_str(), b
.c_str(), size
) == 0;
1735 #else /* HAVE_CRYPTO_MEMCMP */
1736 #ifdef HAVE_SODIUM_MEMCMP
1737 return sodium_memcmp(a
.c_str(), b
.c_str(), size
) == 0;
1738 #else /* HAVE_SODIUM_MEMCMP */
1739 const volatile unsigned char *_a
= (const volatile unsigned char *) a
.c_str();
1740 const volatile unsigned char *_b
= (const volatile unsigned char *) b
.c_str();
1741 unsigned char res
= 0;
1743 for (size_t idx
= 0; idx
< size
; idx
++) {
1744 res
|= _a
[idx
] ^ _b
[idx
];
1748 #endif /* !HAVE_SODIUM_MEMCMP */
1749 #endif /* !HAVE_CRYPTO_MEMCMP */
1754 std::optional
<std::string
> visit_directory(const std::string
& directory
, const std::function
<bool(ino_t inodeNumber
, const std::string_view
& name
)>& visitor
)
1756 auto dirHandle
= std::unique_ptr
<DIR, decltype(&closedir
)>(opendir(directory
.c_str()), closedir
);
1759 return std::string("Error opening directory '" + directory
+ "': " + stringerror(err
));
1762 bool keepGoing
= true;
1763 struct dirent
* ent
= nullptr;
1764 // NOLINTNEXTLINE(concurrency-mt-unsafe): readdir is thread-safe nowadays and readdir_r is deprecated
1765 while (keepGoing
&& (ent
= readdir(dirHandle
.get())) != nullptr) {
1766 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay: dirent API
1767 auto name
= std::string_view(ent
->d_name
, strlen(ent
->d_name
));
1768 keepGoing
= visitor(ent
->d_ino
, name
);
1771 return std::nullopt
;