]>
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.
25 #include <sys/param.h>
26 #include <sys/socket.h>
31 #include <sys/resource.h>
32 #include <netinet/in.h>
42 #include <sys/types.h>
45 #include <boost/optional.hpp>
48 #include <netinet/tcp.h>
52 #include "pdnsexception.hh"
53 #include <sys/types.h>
54 #include <boost/algorithm/string.hpp>
56 #include "dnsparser.hh"
57 #include <sys/types.h>
61 # include <pthread_np.h>
68 bool g_singleThreaded
;
70 size_t writen2(int fd
, const void *buf
, size_t count
)
72 const char *ptr
= (char*)buf
;
73 const char *eptr
= ptr
+ count
;
77 res
= ::write(fd
, ptr
, eptr
- ptr
);
80 throw std::runtime_error("used writen2 on non-blocking socket, got EAGAIN");
82 unixDie("failed in writen2");
85 throw std::runtime_error("could not write all bytes, got eof in writen2");
93 size_t readn2(int fd
, void* buffer
, size_t len
)
98 res
= read(fd
, (char*)buffer
+ pos
, len
- pos
);
100 throw runtime_error("EOF while reading message");
103 throw std::runtime_error("used readn2 on non-blocking socket, got EAGAIN");
105 unixDie("failed in readn2");
115 size_t readn2WithTimeout(int fd
, void* buffer
, size_t len
, int idleTimeout
, int totalTimeout
)
119 int remainingTime
= totalTimeout
;
125 ssize_t got
= read(fd
, (char *)buffer
+ pos
, len
- pos
);
130 throw runtime_error("EOF while reading message");
133 if (errno
== EAGAIN
) {
134 int res
= waitForData(fd
, (totalTimeout
== 0 || idleTimeout
<= remainingTime
) ? idleTimeout
: remainingTime
);
136 /* there is data available */
139 throw runtime_error("Timeout while waiting for data to read");
141 throw runtime_error("Error while waiting for data to read");
145 unixDie("failed in readn2WithTimeout");
150 time_t now
= time(NULL
);
151 int elapsed
= now
- start
;
152 if (elapsed
>= remainingTime
) {
153 throw runtime_error("Timeout while reading data");
156 remainingTime
-= elapsed
;
164 size_t writen2WithTimeout(int fd
, const void * buffer
, size_t len
, int timeout
)
168 ssize_t written
= write(fd
, (char *)buffer
+ pos
, len
- pos
);
171 pos
+= (size_t) written
;
173 else if (written
== 0)
174 throw runtime_error("EOF while writing message");
176 if (errno
== EAGAIN
) {
177 int res
= waitForRWData(fd
, false, timeout
, 0);
179 /* there is room available */
182 throw runtime_error("Timeout while waiting to write data");
184 throw runtime_error("Error while waiting for room to write data");
188 unixDie("failed in write2WithTimeout");
199 time_t now
= time(nullptr);
201 localtime_r(&now
, &tm
);
203 // YYYY-mm-dd HH:MM:SS TZOFF
204 strftime(buffer
, sizeof(buffer
), "%F %T %z", &tm
);
205 buffer
[sizeof(buffer
)-1] = '\0';
206 return string(buffer
);
209 uint16_t getShort(const unsigned char *p
)
211 return p
[0] * 256 + p
[1];
215 uint16_t getShort(const char *p
)
217 return getShort((const unsigned char *)p
);
220 uint32_t getLong(const unsigned char* p
)
222 return (p
[0]<<24) + (p
[1]<<16) + (p
[2]<<8) + p
[3];
225 uint32_t getLong(const char* p
)
227 return getLong((unsigned char *)p
);
230 static bool ciEqual(const string
& a
, const string
& b
)
232 if(a
.size()!=b
.size())
235 string::size_type pos
=0, epos
=a
.size();
236 for(;pos
< epos
; ++pos
)
237 if(dns_tolower(a
[pos
])!=dns_tolower(b
[pos
]))
242 /** does domain end on suffix? Is smart about "wwwds9a.nl" "ds9a.nl" not matching */
243 static bool endsOn(const string
&domain
, const string
&suffix
)
245 if( suffix
.empty() || ciEqual(domain
, suffix
) )
248 if(domain
.size()<=suffix
.size())
251 string::size_type dpos
=domain
.size()-suffix
.size()-1, spos
=0;
253 if(domain
[dpos
++]!='.')
256 for(; dpos
< domain
.size(); ++dpos
, ++spos
)
257 if(dns_tolower(domain
[dpos
]) != dns_tolower(suffix
[spos
]))
263 /** strips a domain suffix from a domain, returns true if it stripped */
264 bool stripDomainSuffix(string
*qname
, const string
&domain
)
266 if(!endsOn(*qname
, domain
))
269 if(toLower(*qname
)==toLower(domain
))
272 if((*qname
)[qname
->size()-domain
.size()-1]!='.')
275 qname
->resize(qname
->size()-domain
.size()-1);
280 static void parseService4(const string
&descr
, ServiceTuple
&st
)
283 stringtok(parts
,descr
,":");
285 throw PDNSException("Unable to parse '"+descr
+"' as a service");
288 st
.port
=pdns_stou(parts
[1]);
291 static void parseService6(const string
&descr
, ServiceTuple
&st
)
293 string::size_type pos
=descr
.find(']');
294 if(pos
== string::npos
)
295 throw PDNSException("Unable to parse '"+descr
+"' as an IPv6 service");
297 st
.host
=descr
.substr(1, pos
-1);
298 if(pos
+ 2 < descr
.length())
299 st
.port
=pdns_stou(descr
.substr(pos
+2));
303 void parseService(const string
&descr
, ServiceTuple
&st
)
306 throw PDNSException("Unable to parse '"+descr
+"' as a service");
308 vector
<string
> parts
;
309 stringtok(parts
, descr
, ":");
312 parseService6(descr
, st
);
314 else if(descr
[0]==':' || parts
.size() > 2 || descr
.find("::") != string::npos
) {
318 parseService4(descr
, st
);
322 // returns -1 in case if error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
323 int waitForData(int fd
, int seconds
, int useconds
)
325 return waitForRWData(fd
, true, seconds
, useconds
);
328 int waitForRWData(int fd
, bool waitForRead
, int seconds
, int useconds
, bool* error
, bool* disconnected
)
333 memset(&pfd
, 0, sizeof(pfd
));
341 ret
= poll(&pfd
, 1, seconds
* 1000 + useconds
/1000);
343 if (error
&& (pfd
.revents
& POLLERR
)) {
346 if (disconnected
&& (pfd
.revents
& POLLHUP
)) {
347 *disconnected
= true;
354 // returns -1 in case of error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
355 int waitForMultiData(const set
<int>& fds
, const int seconds
, const int useconds
, int* fdOut
) {
357 for (const auto& fd
: fds
) {
358 if (fd
>= 0 && realFDs
.count(fd
) == 0) {
363 std::vector
<struct pollfd
> pfds(realFDs
.size());
364 memset(pfds
.data(), 0, realFDs
.size()*sizeof(struct pollfd
));
366 for (const auto& fd
: realFDs
) {
368 pfds
[ctr
].events
= POLLIN
;
374 ret
= poll(pfds
.data(), realFDs
.size(), seconds
* 1000 + useconds
/1000);
376 ret
= poll(pfds
.data(), realFDs
.size(), -1);
381 for (const auto& pfd
: pfds
) {
382 if (pfd
.revents
& POLLIN
) {
383 pollinFDs
.insert(pfd
.fd
);
386 set
<int>::const_iterator
it(pollinFDs
.begin());
387 advance(it
, random() % pollinFDs
.size());
392 // returns -1 in case of error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
393 int waitFor2Data(int fd1
, int fd2
, int seconds
, int useconds
, int*fd
)
397 struct pollfd pfds
[2];
398 memset(&pfds
[0], 0, 2*sizeof(struct pollfd
));
402 pfds
[0].events
= pfds
[1].events
= POLLIN
;
404 int nsocks
= 1 + (fd2
>= 0); // fd2 can optionally be -1
407 ret
= poll(pfds
, nsocks
, seconds
* 1000 + useconds
/1000);
409 ret
= poll(pfds
, nsocks
, -1);
413 if((pfds
[0].revents
& POLLIN
) && !(pfds
[1].revents
& POLLIN
))
415 else if((pfds
[1].revents
& POLLIN
) && !(pfds
[0].revents
& POLLIN
))
418 *fd
= pfds
[random()%2].fd
;
421 *fd
= -1; // should never happen
427 string
humanDuration(time_t passed
)
431 ret
<<passed
<<" seconds";
433 ret
<<std::setprecision(2)<<passed
/60.0<<" minutes";
434 else if(passed
<86400)
435 ret
<<std::setprecision(3)<<passed
/3600.0<<" hours";
436 else if(passed
<(86400*30.41))
437 ret
<<std::setprecision(3)<<passed
/86400.0<<" days";
439 ret
<<std::setprecision(3)<<passed
/(86400*30.41)<<" months";
446 // set(); // saves lots of gettimeofday calls
447 d_set
.tv_sec
=d_set
.tv_usec
=0;
455 const string
unquotify(const string
&item
)
460 string::size_type bpos
=0, epos
=item
.size();
465 if(item
[epos
-1]=='"')
468 return item
.substr(bpos
,epos
-bpos
);
471 void stripLine(string
&line
)
473 string::size_type pos
=line
.find_first_of("\r\n");
474 if(pos
!=string::npos
) {
479 string
urlEncode(const string
&text
)
482 for(string::const_iterator i
=text
.begin();i
!=text
.end();++i
)
483 if(*i
==' ')ret
.append("%20");
484 else ret
.append(1,*i
);
490 #ifndef MAXHOSTNAMELEN
491 #define MAXHOSTNAMELEN 255
494 char tmp
[MAXHOSTNAMELEN
];
495 if(gethostname(tmp
, MAXHOSTNAMELEN
))
508 string
uitoa(unsigned int i
) // MSVC 6 doesn't grok overloading (un)signed
515 string
bitFlip(const string
&str
)
517 string::size_type pos
=0, epos
=str
.size();
520 for(;pos
< epos
; ++pos
)
521 ret
.append(1, ~str
[pos
]);
525 string
stringerror(int err
)
527 return strerror(err
);
532 return strerror(errno
);
535 void cleanSlashes(string
&str
)
537 string::const_iterator i
;
539 for(i
=str
.begin();i
!=str
.end();++i
) {
540 if(*i
=='/' && i
!=str
.begin() && *(i
-1)=='/')
548 bool IpToU32(const string
&str
, uint32_t *ip
)
556 if(inet_aton(str
.c_str(), &inp
)) {
563 string
U32ToIP(uint32_t val
)
566 snprintf(tmp
, sizeof(tmp
), "%u.%u.%u.%u",
575 string
makeHexDump(const string
& str
)
579 ret
.reserve((int)(str
.size()*2.2));
581 for(string::size_type n
=0;n
<str
.size();++n
) {
582 snprintf(tmp
, sizeof(tmp
), "%02x ", (unsigned char)str
[n
]);
588 // shuffle, maintaining some semblance of order
589 void shuffle(vector
<DNSZoneRecord
>& rrs
)
591 vector
<DNSZoneRecord
>::iterator first
, second
;
592 for(first
=rrs
.begin();first
!=rrs
.end();++first
)
593 if(first
->dr
.d_place
==DNSResourceRecord::ANSWER
&& first
->dr
.d_type
!= QType::CNAME
) // CNAME must come first
595 for(second
=first
;second
!=rrs
.end();++second
)
596 if(second
->dr
.d_place
!=DNSResourceRecord::ANSWER
)
600 random_shuffle(first
,second
);
602 // now shuffle the additional records
603 for(first
=second
;first
!=rrs
.end();++first
)
604 if(first
->dr
.d_place
==DNSResourceRecord::ADDITIONAL
&& first
->dr
.d_type
!= QType::CNAME
) // CNAME must come first
606 for(second
=first
;second
!=rrs
.end();++second
)
607 if(second
->dr
.d_place
!=DNSResourceRecord::ADDITIONAL
)
611 random_shuffle(first
,second
);
613 // we don't shuffle the rest
617 // shuffle, maintaining some semblance of order
618 void shuffle(vector
<DNSRecord
>& rrs
)
620 vector
<DNSRecord
>::iterator first
, second
;
621 for(first
=rrs
.begin();first
!=rrs
.end();++first
)
622 if(first
->d_place
==DNSResourceRecord::ANSWER
&& first
->d_type
!= QType::CNAME
) // CNAME must come first
624 for(second
=first
;second
!=rrs
.end();++second
)
625 if(second
->d_place
!=DNSResourceRecord::ANSWER
|| second
->d_type
== QType::RRSIG
) // leave RRSIGs at the end
629 random_shuffle(first
,second
);
631 // now shuffle the additional records
632 for(first
=second
;first
!=rrs
.end();++first
)
633 if(first
->d_place
==DNSResourceRecord::ADDITIONAL
&& first
->d_type
!= QType::CNAME
) // CNAME must come first
635 for(second
=first
; second
!=rrs
.end(); ++second
)
636 if(second
->d_place
!=DNSResourceRecord::ADDITIONAL
)
640 random_shuffle(first
,second
);
642 // we don't shuffle the rest
645 static uint16_t mapTypesToOrder(uint16_t type
)
647 if(type
== QType::CNAME
)
649 if(type
== QType::RRSIG
)
655 // make sure rrs is sorted in d_place order to avoid surprises later
656 // then shuffle the parts that desire shuffling
657 void orderAndShuffle(vector
<DNSRecord
>& rrs
)
659 std::stable_sort(rrs
.begin(), rrs
.end(), [](const DNSRecord
&a
, const DNSRecord
& b
) {
660 return std::make_tuple(a
.d_place
, mapTypesToOrder(a
.d_type
)) < std::make_tuple(b
.d_place
, mapTypesToOrder(b
.d_type
));
665 void normalizeTV(struct timeval
& tv
)
667 if(tv
.tv_usec
> 1000000) {
671 else if(tv
.tv_usec
< 0) {
677 const struct timeval
operator+(const struct timeval
& lhs
, const struct timeval
& rhs
)
680 ret
.tv_sec
=lhs
.tv_sec
+ rhs
.tv_sec
;
681 ret
.tv_usec
=lhs
.tv_usec
+ rhs
.tv_usec
;
686 const struct timeval
operator-(const struct timeval
& lhs
, const struct timeval
& rhs
)
689 ret
.tv_sec
=lhs
.tv_sec
- rhs
.tv_sec
;
690 ret
.tv_usec
=lhs
.tv_usec
- rhs
.tv_usec
;
695 pair
<string
, string
> splitField(const string
& inp
, char sepa
)
697 pair
<string
, string
> ret
;
698 string::size_type cpos
=inp
.find(sepa
);
699 if(cpos
==string::npos
)
702 ret
.first
=inp
.substr(0, cpos
);
703 ret
.second
=inp
.substr(cpos
+1);
708 int logFacilityToLOG(unsigned int facility
)
732 string
stripDot(const string
& dom
)
737 if(dom
[dom
.size()-1]!='.')
740 return dom
.substr(0,dom
.size()-1);
745 int makeIPv6sockaddr(const std::string
& addr
, struct sockaddr_in6
* ret
)
749 string
ourAddr(addr
);
750 bool portSet
= false;
752 if(addr
[0]=='[') { // [::]:53 style address
753 string::size_type pos
= addr
.find(']');
754 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]!=':')
761 port
= pdns_stou(addr
.substr(pos
+2));
764 catch(const std::out_of_range
&) {
769 ret
->sin6_scope_id
=0;
770 ret
->sin6_family
=AF_INET6
;
772 if(inet_pton(AF_INET6
, ourAddr
.c_str(), (void*)&ret
->sin6_addr
) != 1) {
773 struct addrinfo
* res
;
774 struct addrinfo hints
;
775 memset(&hints
, 0, sizeof(hints
));
777 hints
.ai_family
= AF_INET6
;
778 hints
.ai_flags
= AI_NUMERICHOST
;
780 // getaddrinfo has anomalous return codes, anything nonzero is an error, positive or negative
781 if (getaddrinfo(ourAddr
.c_str(), 0, &hints
, &res
) != 0) {
785 memcpy(ret
, res
->ai_addr
, res
->ai_addrlen
);
793 ret
->sin6_port
= htons(port
);
799 int makeIPv4sockaddr(const std::string
& str
, struct sockaddr_in
* ret
)
806 string::size_type pos
= str
.find(':');
807 if(pos
== string::npos
) { // no port specified, not touching the port
808 if(inet_aton(str
.c_str(), &inp
)) {
809 ret
->sin_addr
.s_addr
=inp
.s_addr
;
814 if(!*(str
.c_str() + pos
+ 1)) // trailing :
817 char *eptr
= (char*)str
.c_str() + str
.size();
818 int port
= strtol(str
.c_str() + pos
+ 1, &eptr
, 10);
819 if (port
< 0 || port
> 65535)
825 ret
->sin_port
= htons(port
);
826 if(inet_aton(str
.substr(0, pos
).c_str(), &inp
)) {
827 ret
->sin_addr
.s_addr
=inp
.s_addr
;
833 int makeUNsockaddr(const std::string
& path
, struct sockaddr_un
* ret
)
838 memset(ret
, 0, sizeof(struct sockaddr_un
));
839 ret
->sun_family
= AF_UNIX
;
840 if (path
.length() >= sizeof(ret
->sun_path
))
843 path
.copy(ret
->sun_path
, sizeof(ret
->sun_path
), 0);
847 //! read a line of text from a FILE* to a std::string, returns false on 'no data'
848 bool stringfgets(FILE* fp
, std::string
& line
)
854 if(!fgets(buffer
, sizeof(buffer
), fp
))
855 return !line
.empty();
858 } while(!strchr(buffer
, '\n'));
862 bool readFileIfThere(const char* fname
, std::string
* line
)
865 auto fp
= std::unique_ptr
<FILE, int(*)(FILE*)>(fopen(fname
, "r"), fclose
);
868 stringfgets(fp
.get(), *line
);
874 Regex::Regex(const string
&expr
)
876 if(regcomp(&d_preg
, expr
.c_str(), REG_ICASE
|REG_NOSUB
|REG_EXTENDED
))
877 throw PDNSException("Regular expression did not compile");
880 // if you end up here because valgrind told you were are doing something wrong
881 // with msgh->msg_controllen, please refer to https://github.com/PowerDNS/pdns/pull/3962
883 // Note that cmsgbuf should be aligned the same as a struct cmsghdr
884 void addCMsgSrcAddr(struct msghdr
* msgh
, cmsgbuf_aligned
* cmsgbuf
, const ComboAddress
* source
, int itfIndex
)
886 struct cmsghdr
*cmsg
= NULL
;
888 if(source
->sin4
.sin_family
== AF_INET6
) {
889 struct in6_pktinfo
*pkt
;
891 msgh
->msg_control
= cmsgbuf
;
892 #if !defined( __APPLE__ )
893 /* CMSG_SPACE is not a constexpr on macOS */
894 static_assert(CMSG_SPACE(sizeof(*pkt
)) <= sizeof(*cmsgbuf
), "Buffer is too small for in6_pktinfo");
895 #else /* __APPLE__ */
896 if (CMSG_SPACE(sizeof(*pkt
)) > sizeof(*cmsgbuf
)) {
897 throw std::runtime_error("Buffer is too small for in6_pktinfo");
899 #endif /* __APPLE__ */
900 msgh
->msg_controllen
= CMSG_SPACE(sizeof(*pkt
));
902 cmsg
= CMSG_FIRSTHDR(msgh
);
903 cmsg
->cmsg_level
= IPPROTO_IPV6
;
904 cmsg
->cmsg_type
= IPV6_PKTINFO
;
905 cmsg
->cmsg_len
= CMSG_LEN(sizeof(*pkt
));
907 pkt
= (struct in6_pktinfo
*) CMSG_DATA(cmsg
);
908 // Include the padding to stop valgrind complaining about passing uninitialized data
909 memset(pkt
, 0, CMSG_SPACE(sizeof(*pkt
)));
910 pkt
->ipi6_addr
= source
->sin6
.sin6_addr
;
911 pkt
->ipi6_ifindex
= itfIndex
;
914 #if defined(IP_PKTINFO)
915 struct in_pktinfo
*pkt
;
917 msgh
->msg_control
= cmsgbuf
;
918 #if !defined( __APPLE__ )
919 /* CMSG_SPACE is not a constexpr on macOS */
920 static_assert(CMSG_SPACE(sizeof(*pkt
)) <= sizeof(*cmsgbuf
), "Buffer is too small for in_pktinfo");
921 #else /* __APPLE__ */
922 if (CMSG_SPACE(sizeof(*pkt
)) > sizeof(*cmsgbuf
)) {
923 throw std::runtime_error("Buffer is too small for in_pktinfo");
925 #endif /* __APPLE__ */
926 msgh
->msg_controllen
= CMSG_SPACE(sizeof(*pkt
));
928 cmsg
= CMSG_FIRSTHDR(msgh
);
929 cmsg
->cmsg_level
= IPPROTO_IP
;
930 cmsg
->cmsg_type
= IP_PKTINFO
;
931 cmsg
->cmsg_len
= CMSG_LEN(sizeof(*pkt
));
933 pkt
= (struct in_pktinfo
*) CMSG_DATA(cmsg
);
934 // Include the padding to stop valgrind complaining about passing uninitialized data
935 memset(pkt
, 0, CMSG_SPACE(sizeof(*pkt
)));
936 pkt
->ipi_spec_dst
= source
->sin4
.sin_addr
;
937 pkt
->ipi_ifindex
= itfIndex
;
938 #elif defined(IP_SENDSRCADDR)
941 msgh
->msg_control
= cmsgbuf
;
942 #if !defined( __APPLE__ )
943 static_assert(CMSG_SPACE(sizeof(*in
)) <= sizeof(*cmsgbuf
), "Buffer is too small for in_addr");
944 #else /* __APPLE__ */
945 if (CMSG_SPACE(sizeof(*in
)) > sizeof(*cmsgbuf
)) {
946 throw std::runtime_error("Buffer is too small for in_addr");
948 #endif /* __APPLE__ */
949 msgh
->msg_controllen
= CMSG_SPACE(sizeof(*in
));
951 cmsg
= CMSG_FIRSTHDR(msgh
);
952 cmsg
->cmsg_level
= IPPROTO_IP
;
953 cmsg
->cmsg_type
= IP_SENDSRCADDR
;
954 cmsg
->cmsg_len
= CMSG_LEN(sizeof(*in
));
956 // Include the padding to stop valgrind complaining about passing uninitialized data
957 in
= (struct in_addr
*) CMSG_DATA(cmsg
);
958 memset(in
, 0, CMSG_SPACE(sizeof(*in
)));
959 *in
= source
->sin4
.sin_addr
;
964 unsigned int getFilenumLimit(bool hardOrSoft
)
967 if(getrlimit(RLIMIT_NOFILE
, &rlim
) < 0)
968 unixDie("Requesting number of available file descriptors");
969 return hardOrSoft
? rlim
.rlim_max
: rlim
.rlim_cur
;
972 void setFilenumLimit(unsigned int lim
)
976 if(getrlimit(RLIMIT_NOFILE
, &rlim
) < 0)
977 unixDie("Requesting number of available file descriptors");
979 if(setrlimit(RLIMIT_NOFILE
, &rlim
) < 0)
980 unixDie("Setting number of available file descriptors");
983 #define burtlemix(a,b,c) \
985 a -= b; a -= c; a ^= (c>>13); \
986 b -= c; b -= a; b ^= (a<<8); \
987 c -= a; c -= b; c ^= (b>>13); \
988 a -= b; a -= c; a ^= (c>>12); \
989 b -= c; b -= a; b ^= (a<<16); \
990 c -= a; c -= b; c ^= (b>>5); \
991 a -= b; a -= c; a ^= (c>>3); \
992 b -= c; b -= a; b ^= (a<<10); \
993 c -= a; c -= b; c ^= (b>>15); \
996 uint32_t burtle(const unsigned char* k
, uint32_t length
, uint32_t initval
)
1000 /* Set up the internal state */
1002 a
= b
= 0x9e3779b9; /* the golden ratio; an arbitrary value */
1003 c
= initval
; /* the previous hash value */
1005 /*---------------------------------------- handle most of the key */
1007 a
+= (k
[0] +((uint32_t)k
[1]<<8) +((uint32_t)k
[2]<<16) +((uint32_t)k
[3]<<24));
1008 b
+= (k
[4] +((uint32_t)k
[5]<<8) +((uint32_t)k
[6]<<16) +((uint32_t)k
[7]<<24));
1009 c
+= (k
[8] +((uint32_t)k
[9]<<8) +((uint32_t)k
[10]<<16)+((uint32_t)k
[11]<<24));
1014 /*------------------------------------- handle the last 11 bytes */
1016 switch(len
) { /* all the case statements fall through */
1017 case 11: c
+=((uint32_t)k
[10]<<24);
1019 case 10: c
+=((uint32_t)k
[9]<<16);
1021 case 9 : c
+=((uint32_t)k
[8]<<8);
1022 /* the first byte of c is reserved for the length */
1024 case 8 : b
+=((uint32_t)k
[7]<<24);
1026 case 7 : b
+=((uint32_t)k
[6]<<16);
1028 case 6 : b
+=((uint32_t)k
[5]<<8);
1032 case 4 : a
+=((uint32_t)k
[3]<<24);
1034 case 3 : a
+=((uint32_t)k
[2]<<16);
1036 case 2 : a
+=((uint32_t)k
[1]<<8);
1039 /* case 0: nothing left to add */
1042 /*-------------------------------------------- report the result */
1046 uint32_t burtleCI(const unsigned char* k
, uint32_t length
, uint32_t initval
)
1050 /* Set up the internal state */
1052 a
= b
= 0x9e3779b9; /* the golden ratio; an arbitrary value */
1053 c
= initval
; /* the previous hash value */
1055 /*---------------------------------------- handle most of the key */
1057 a
+= (dns_tolower(k
[0]) +((uint32_t)dns_tolower(k
[1])<<8) +((uint32_t)dns_tolower(k
[2])<<16) +((uint32_t)dns_tolower(k
[3])<<24));
1058 b
+= (dns_tolower(k
[4]) +((uint32_t)dns_tolower(k
[5])<<8) +((uint32_t)dns_tolower(k
[6])<<16) +((uint32_t)dns_tolower(k
[7])<<24));
1059 c
+= (dns_tolower(k
[8]) +((uint32_t)dns_tolower(k
[9])<<8) +((uint32_t)dns_tolower(k
[10])<<16)+((uint32_t)dns_tolower(k
[11])<<24));
1064 /*------------------------------------- handle the last 11 bytes */
1066 switch(len
) { /* all the case statements fall through */
1067 case 11: c
+=((uint32_t)dns_tolower(k
[10])<<24);
1069 case 10: c
+=((uint32_t)dns_tolower(k
[9])<<16);
1071 case 9 : c
+=((uint32_t)dns_tolower(k
[8])<<8);
1072 /* the first byte of c is reserved for the length */
1074 case 8 : b
+=((uint32_t)dns_tolower(k
[7])<<24);
1076 case 7 : b
+=((uint32_t)dns_tolower(k
[6])<<16);
1078 case 6 : b
+=((uint32_t)dns_tolower(k
[5])<<8);
1080 case 5 : b
+=dns_tolower(k
[4]);
1082 case 4 : a
+=((uint32_t)dns_tolower(k
[3])<<24);
1084 case 3 : a
+=((uint32_t)dns_tolower(k
[2])<<16);
1086 case 2 : a
+=((uint32_t)dns_tolower(k
[1])<<8);
1088 case 1 : a
+=dns_tolower(k
[0]);
1089 /* case 0: nothing left to add */
1092 /*-------------------------------------------- report the result */
1097 bool setSocketTimestamps(int fd
)
1101 return setsockopt(fd
, SOL_SOCKET
, SO_TIMESTAMP
, (char*)&on
, sizeof(on
)) == 0;
1103 return true; // we pretend this happened.
1107 bool setTCPNoDelay(int sock
)
1110 return setsockopt(sock
, /* socket affected */
1111 IPPROTO_TCP
, /* set option at TCP level */
1112 TCP_NODELAY
, /* name of option */
1113 (char *) &flag
, /* the cast is historical cruft */
1114 sizeof(flag
)) == 0; /* length of option value */
1118 bool setNonBlocking(int sock
)
1120 int flags
=fcntl(sock
,F_GETFL
,0);
1121 if(flags
<0 || fcntl(sock
, F_SETFL
,flags
|O_NONBLOCK
) <0)
1126 bool setBlocking(int sock
)
1128 int flags
=fcntl(sock
,F_GETFL
,0);
1129 if(flags
<0 || fcntl(sock
, F_SETFL
,flags
&(~O_NONBLOCK
)) <0)
1134 bool setReuseAddr(int sock
)
1137 if (setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (char*)&tmp
, static_cast<unsigned>(sizeof tmp
))<0)
1138 throw PDNSException(string("Setsockopt failed: ")+stringerror());
1142 bool isNonBlocking(int sock
)
1144 int flags
=fcntl(sock
,F_GETFL
,0);
1145 return flags
& O_NONBLOCK
;
1148 bool setReceiveSocketErrors(int sock
, int af
)
1152 if (af
== AF_INET
) {
1153 ret
= setsockopt(sock
, IPPROTO_IP
, IP_RECVERR
, &tmp
, sizeof(tmp
));
1155 ret
= setsockopt(sock
, IPPROTO_IPV6
, IPV6_RECVERR
, &tmp
, sizeof(tmp
));
1158 throw PDNSException(string("Setsockopt failed: ") + stringerror());
1165 int closesocket( int socket
)
1167 int ret
=::close(socket
);
1168 if(ret
< 0 && errno
== ECONNRESET
) // see ticket 192, odd BSD behaviour
1171 throw PDNSException("Error closing socket: "+stringerror());
1175 bool setCloseOnExec(int sock
)
1177 int flags
=fcntl(sock
,F_GETFD
,0);
1178 if(flags
<0 || fcntl(sock
, F_SETFD
,flags
|FD_CLOEXEC
) <0)
1183 string
getMACAddress(const ComboAddress
& ca
)
1187 ifstream
ifs("/proc/net/arp");
1191 string match
=ca
.toString()+' ';
1192 while(getline(ifs
, line
)) {
1193 if(boost::starts_with(line
, match
)) {
1194 vector
<string
> parts
;
1195 stringtok(parts
, line
, " \n\t\r");
1196 if(parts
.size() < 4)
1198 unsigned int tmp
[6];
1199 sscanf(parts
[3].c_str(), "%02x:%02x:%02x:%02x:%02x:%02x", tmp
, tmp
+1, tmp
+2, tmp
+3, tmp
+4, tmp
+5);
1200 for(int i
= 0 ; i
< 6 ; ++i
)
1201 ret
.append(1, (char)tmp
[i
]);
1209 uint64_t udpErrorStats(const std::string
& str
)
1212 ifstream
ifs("/proc/net/snmp");
1216 vector
<string
> parts
;
1217 while(getline(ifs
,line
)) {
1218 if(boost::starts_with(line
, "Udp: ") && isdigit(line
[5])) {
1219 stringtok(parts
, line
, " \n\t\r");
1220 if(parts
.size() < 7)
1222 if(str
=="udp-rcvbuf-errors")
1223 return std::stoull(parts
[5]);
1224 else if(str
=="udp-sndbuf-errors")
1225 return std::stoull(parts
[6]);
1226 else if(str
=="udp-noport-errors")
1227 return std::stoull(parts
[2]);
1228 else if(str
=="udp-in-errors")
1229 return std::stoull(parts
[3]);
1238 uint64_t getCPUIOWait(const std::string
& str
)
1241 ifstream
ifs("/proc/stat");
1247 vector
<string
> parts
;
1248 while (getline(ifs
, line
)) {
1249 if (boost::starts_with(line
, "cpu ")) {
1250 stringtok(parts
, line
, " \n\t\r");
1252 if (parts
.size() < 6) {
1256 return std::stoull(parts
[5]);
1263 uint64_t getCPUSteal(const std::string
& str
)
1266 ifstream
ifs("/proc/stat");
1272 vector
<string
> parts
;
1273 while (getline(ifs
, line
)) {
1274 if (boost::starts_with(line
, "cpu ")) {
1275 stringtok(parts
, line
, " \n\t\r");
1277 if (parts
.size() < 9) {
1281 return std::stoull(parts
[8]);
1288 bool getTSIGHashEnum(const DNSName
& algoName
, TSIGHashEnum
& algoEnum
)
1290 if (algoName
== DNSName("hmac-md5.sig-alg.reg.int") || algoName
== DNSName("hmac-md5"))
1291 algoEnum
= TSIG_MD5
;
1292 else if (algoName
== DNSName("hmac-sha1"))
1293 algoEnum
= TSIG_SHA1
;
1294 else if (algoName
== DNSName("hmac-sha224"))
1295 algoEnum
= TSIG_SHA224
;
1296 else if (algoName
== DNSName("hmac-sha256"))
1297 algoEnum
= TSIG_SHA256
;
1298 else if (algoName
== DNSName("hmac-sha384"))
1299 algoEnum
= TSIG_SHA384
;
1300 else if (algoName
== DNSName("hmac-sha512"))
1301 algoEnum
= TSIG_SHA512
;
1302 else if (algoName
== DNSName("gss-tsig"))
1303 algoEnum
= TSIG_GSS
;
1310 DNSName
getTSIGAlgoName(TSIGHashEnum
& algoEnum
)
1313 case TSIG_MD5
: return DNSName("hmac-md5.sig-alg.reg.int.");
1314 case TSIG_SHA1
: return DNSName("hmac-sha1.");
1315 case TSIG_SHA224
: return DNSName("hmac-sha224.");
1316 case TSIG_SHA256
: return DNSName("hmac-sha256.");
1317 case TSIG_SHA384
: return DNSName("hmac-sha384.");
1318 case TSIG_SHA512
: return DNSName("hmac-sha512.");
1319 case TSIG_GSS
: return DNSName("gss-tsig.");
1321 throw PDNSException("getTSIGAlgoName does not understand given algorithm, please fix!");
1324 uint64_t getOpenFileDescriptors(const std::string
&)
1327 DIR* dirhdl
=opendir(("/proc/"+std::to_string(getpid())+"/fd/").c_str());
1331 struct dirent
*entry
;
1333 while((entry
= readdir(dirhdl
))) {
1336 num
= pdns_stou(entry
->d_name
);
1338 continue; // was not a number.
1340 if(std::to_string(num
) == entry
->d_name
)
1351 uint64_t getRealMemoryUsage(const std::string
&)
1354 ifstream
ifs("/proc/self/statm");
1358 uint64_t size
, resident
, shared
, text
, lib
, data
;
1359 ifs
>> size
>> resident
>> shared
>> text
>> lib
>> data
;
1361 return data
* getpagesize();
1364 if (getrusage(RUSAGE_SELF
, &ru
) != 0)
1366 return ru
.ru_maxrss
* 1024;
1371 uint64_t getSpecialMemoryUsage(const std::string
&)
1374 ifstream
ifs("/proc/self/smaps");
1379 string
header("Private_Dirty:");
1380 while(getline(ifs
, line
)) {
1381 if(boost::starts_with(line
, header
)) {
1382 bytes
+= std::stoull(line
.substr(header
.length() + 1))*1024;
1391 uint64_t getCPUTimeUser(const std::string
&)
1394 getrusage(RUSAGE_SELF
, &ru
);
1395 return (ru
.ru_utime
.tv_sec
*1000ULL + ru
.ru_utime
.tv_usec
/1000);
1398 uint64_t getCPUTimeSystem(const std::string
&)
1401 getrusage(RUSAGE_SELF
, &ru
);
1402 return (ru
.ru_stime
.tv_sec
*1000ULL + ru
.ru_stime
.tv_usec
/1000);
1405 double DiffTime(const struct timespec
& first
, const struct timespec
& second
)
1407 int seconds
=second
.tv_sec
- first
.tv_sec
;
1408 int nseconds
=second
.tv_nsec
- first
.tv_nsec
;
1412 nseconds
+=1000000000;
1414 return seconds
+ nseconds
/1000000000.0;
1417 double DiffTime(const struct timeval
& first
, const struct timeval
& second
)
1419 int seconds
=second
.tv_sec
- first
.tv_sec
;
1420 int useconds
=second
.tv_usec
- first
.tv_usec
;
1426 return seconds
+ useconds
/1000000.0;
1429 uid_t
strToUID(const string
&str
)
1432 const char * cstr
= str
.c_str();
1433 struct passwd
* pwd
= getpwnam(cstr
);
1441 catch(std::exception
& e
) {
1442 throw runtime_error((boost::format("Error: Unable to parse user ID %s") % cstr
).str() );
1445 if (val
< std::numeric_limits
<uid_t
>::min() || val
> std::numeric_limits
<uid_t
>::max()) {
1446 throw runtime_error((boost::format("Error: Unable to parse user ID %s") % cstr
).str() );
1449 result
= static_cast<uid_t
>(val
);
1452 result
= pwd
->pw_uid
;
1458 gid_t
strToGID(const string
&str
)
1461 const char * cstr
= str
.c_str();
1462 struct group
* grp
= getgrnam(cstr
);
1470 catch(std::exception
& e
) {
1471 throw runtime_error((boost::format("Error: Unable to parse group ID %s") % cstr
).str() );
1474 if (val
< std::numeric_limits
<gid_t
>::min() || val
> std::numeric_limits
<gid_t
>::max()) {
1475 throw runtime_error((boost::format("Error: Unable to parse group ID %s") % cstr
).str() );
1478 result
= static_cast<gid_t
>(val
);
1481 result
= grp
->gr_gid
;
1487 unsigned int pdns_stou(const std::string
& str
, size_t * idx
, int base
)
1489 if (str
.empty()) return 0; // compatibility
1490 unsigned long result
;
1492 result
= std::stoul(str
, idx
, base
);
1494 catch(std::invalid_argument
& e
) {
1495 throw std::invalid_argument(string(e
.what()) + "; (invalid argument during std::stoul); data was \""+str
+"\"");
1497 catch(std::out_of_range
& e
) {
1498 throw std::out_of_range(string(e
.what()) + "; (out of range during std::stoul); data was \""+str
+"\"");
1500 if (result
> std::numeric_limits
<unsigned int>::max()) {
1501 throw std::out_of_range("stoul returned result out of unsigned int range; data was \""+str
+"\"");
1503 return static_cast<unsigned int>(result
);
1506 bool isSettingThreadCPUAffinitySupported()
1508 #ifdef HAVE_PTHREAD_SETAFFINITY_NP
1515 int mapThreadToCPUList(pthread_t tid
, const std::set
<int>& cpus
)
1517 #ifdef HAVE_PTHREAD_SETAFFINITY_NP
1520 cpuset
= cpuset_create();
1521 for (const auto cpuID
: cpus
) {
1522 cpuset_set(cpuID
, cpuset
);
1525 return pthread_setaffinity_np(tid
,
1526 cpuset_size(cpuset
),
1530 # define cpu_set_t cpuset_t
1534 for (const auto cpuID
: cpus
) {
1535 CPU_SET(cpuID
, &cpuset
);
1538 return pthread_setaffinity_np(tid
,
1544 #endif /* HAVE_PTHREAD_SETAFFINITY_NP */
1547 std::vector
<ComboAddress
> getResolvers(const std::string
& resolvConfPath
)
1549 std::vector
<ComboAddress
> results
;
1551 ifstream
ifs(resolvConfPath
);
1557 while(std::getline(ifs
, line
)) {
1558 boost::trim_right_if(line
, is_any_of(" \r\n\x1a"));
1559 boost::trim_left(line
); // leading spaces, let's be nice
1561 string::size_type tpos
= line
.find_first_of(";#");
1562 if (tpos
!= string::npos
) {
1566 if (boost::starts_with(line
, "nameserver ") || boost::starts_with(line
, "nameserver\t")) {
1567 vector
<string
> parts
;
1568 stringtok(parts
, line
, " \t,"); // be REALLY nice
1569 for(vector
<string
>::const_iterator iter
= parts
.begin() + 1; iter
!= parts
.end(); ++iter
) {
1571 results
.emplace_back(*iter
, 53);
1583 size_t getPipeBufferSize(int fd
)
1586 int res
= fcntl(fd
, F_GETPIPE_SZ
);
1594 #endif /* F_GETPIPE_SZ */
1597 bool setPipeBufferSize(int fd
, size_t size
)
1600 if (size
> std::numeric_limits
<int>::max()) {
1604 int newSize
= static_cast<int>(size
);
1605 int res
= fcntl(fd
, F_SETPIPE_SZ
, newSize
);
1613 #endif /* F_SETPIPE_SZ */
1616 DNSName
reverseNameFromIP(const ComboAddress
& ip
)
1619 std::string
result("in-addr.arpa.");
1620 auto ptr
= reinterpret_cast<const uint8_t*>(&ip
.sin4
.sin_addr
.s_addr
);
1621 for (size_t idx
= 0; idx
< sizeof(ip
.sin4
.sin_addr
.s_addr
); idx
++) {
1622 result
= std::to_string(ptr
[idx
]) + "." + result
;
1624 return DNSName(result
);
1626 else if (ip
.isIPv6()) {
1627 std::string
result("ip6.arpa.");
1628 auto ptr
= reinterpret_cast<const uint8_t*>(&ip
.sin6
.sin6_addr
.s6_addr
[0]);
1629 for (size_t idx
= 0; idx
< sizeof(ip
.sin6
.sin6_addr
.s6_addr
); idx
++) {
1630 std::stringstream stream
;
1631 stream
<< std::hex
<< (ptr
[idx
] & 0x0F);
1633 stream
<< std::hex
<< (((ptr
[idx
]) >> 4) & 0x0F);
1635 result
= stream
.str() + result
;
1637 return DNSName(result
);
1640 throw std::runtime_error("Calling reverseNameFromIP() for an address which is neither an IPv4 nor an IPv6");