]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/misc.cc
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002 - 2010 PowerDNS.COM BV
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2
7 as published by the Free Software Foundation
9 Additionally, the license of this program contains a special
10 exception which allows to distribute the program in binary form when
11 it is linked against OpenSSL.
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 St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include <sys/param.h>
27 #include <netinet/in.h>
37 #include <boost/optional.hpp>
43 #include "pdnsexception.hh"
44 #include <sys/types.h>
46 #include <boost/algorithm/string.hpp>
49 bool g_singleThreaded
;
51 int writen2(int fd
, const void *buf
, size_t count
)
53 const char *ptr
= (char*)buf
;
54 const char *eptr
= ptr
+ count
;
58 res
= ::write(fd
, ptr
, eptr
- ptr
);
61 throw std::runtime_error("used writen2 on non-blocking socket, got EAGAIN");
63 unixDie("failed in writen2");
66 throw std::runtime_error("could not write all bytes, got eof in writen2");
74 int readn2(int fd
, void* buffer
, unsigned int len
)
79 res
= read(fd
, (char*)buffer
+ pos
, len
- pos
);
81 throw runtime_error("EOF while writing message");
84 throw std::runtime_error("used writen2 on non-blocking socket, got EAGAIN");
86 unixDie("failed in writen2");
100 string t
=ctime(&now
);
101 boost::trim_right(t
);
105 uint16_t getShort(const unsigned char *p
)
107 return p
[0] * 256 + p
[1];
111 uint16_t getShort(const char *p
)
113 return getShort((const unsigned char *)p
);
116 uint32_t getLong(const unsigned char* p
)
118 return (p
[0]<<24) + (p
[1]<<16) + (p
[2]<<8) + p
[3];
121 uint32_t getLong(const char* p
)
123 return getLong((unsigned char *)p
);
128 /** strips a domain suffix from a domain, returns true if it stripped */
129 bool stripDomainSuffix(string
*qname
, const string
&domain
)
131 if(!endsOn(*qname
, domain
))
134 if(toLower(*qname
)==toLower(domain
))
137 if((*qname
)[qname
->size()-domain
.size()-1]!='.')
140 qname
->resize(qname
->size()-domain
.size()-1);
145 /** Chops off the start of a domain, so goes from 'www.ds9a.nl' to 'ds9a.nl' to 'nl' to ''. Return zero on the empty string */
146 bool chopOff(string
&domain
)
151 string::size_type fdot
=domain
.find('.');
153 if(fdot
==string::npos
)
156 string::size_type remain
= domain
.length() - (fdot
+ 1);
158 memcpy(tmp
, domain
.c_str()+fdot
+1, remain
);
159 domain
.assign(tmp
, remain
); // don't dare to do this w/o tmp holder :-)
164 /** Chops off the start of a domain, so goes from 'www.ds9a.nl.' to 'ds9a.nl.' to 'nl.' to '.' Return zero on the empty string */
165 bool chopOffDotted(string
&domain
)
167 if(domain
.empty() || (domain
.size()==1 && domain
[0]=='.'))
170 string::size_type fdot
=domain
.find('.');
171 if(fdot
== string::npos
)
174 if(fdot
==domain
.size()-1)
177 string::size_type remain
= domain
.length() - (fdot
+ 1);
179 memcpy(tmp
, domain
.c_str()+fdot
+1, remain
);
180 domain
.assign(tmp
, remain
);
186 bool ciEqual(const string
& a
, const string
& b
)
188 if(a
.size()!=b
.size())
191 string::size_type pos
=0, epos
=a
.size();
192 for(;pos
< epos
; ++pos
)
193 if(dns_tolower(a
[pos
])!=dns_tolower(b
[pos
]))
198 /** does domain end on suffix? Is smart about "wwwds9a.nl" "ds9a.nl" not matching */
199 bool endsOn(const string
&domain
, const string
&suffix
)
201 if( suffix
.empty() || ciEqual(domain
, suffix
) )
204 if(domain
.size()<=suffix
.size())
207 string::size_type dpos
=domain
.size()-suffix
.size()-1, spos
=0;
209 if(domain
[dpos
++]!='.')
212 for(; dpos
< domain
.size(); ++dpos
, ++spos
)
213 if(dns_tolower(domain
[dpos
]) != dns_tolower(suffix
[spos
]))
219 /** does domain end on suffix? Is smart about "wwwds9a.nl" "ds9a.nl" not matching */
220 bool dottedEndsOn(const string
&domain
, const string
&suffix
)
222 if( suffix
=="." || ciEqual(domain
, suffix
) )
225 if(domain
.size()<=suffix
.size())
228 string::size_type dpos
=domain
.size()-suffix
.size()-1, spos
=0;
230 if(domain
[dpos
++]!='.')
233 for(; dpos
< domain
.size(); ++dpos
, ++spos
)
234 if(dns_tolower(domain
[dpos
]) != dns_tolower(suffix
[spos
]))
240 static void parseService4(const string
&descr
, ServiceTuple
&st
)
243 stringtok(parts
,descr
,":");
245 throw PDNSException("Unable to parse '"+descr
+"' as a service");
248 st
.port
=atoi(parts
[1].c_str());
251 static void parseService6(const string
&descr
, ServiceTuple
&st
)
253 string::size_type pos
=descr
.find(']');
254 if(pos
== string::npos
)
255 throw PDNSException("Unable to parse '"+descr
+"' as an IPv6 service");
257 st
.host
=descr
.substr(1, pos
-1);
258 if(pos
+ 2 < descr
.length())
259 st
.port
=atoi(descr
.c_str() + pos
+2);
263 void parseService(const string
&descr
, ServiceTuple
&st
)
266 throw PDNSException("Unable to parse '"+descr
+"' as a service");
268 vector
<string
> parts
;
269 stringtok(parts
, descr
, ":");
272 parseService6(descr
, st
);
274 else if(descr
[0]==':' || parts
.size() > 2 || descr
.find("::") != string::npos
) {
278 parseService4(descr
, st
);
282 // returns -1 in case if error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
283 int waitForData(int fd
, int seconds
, int useconds
)
285 return waitForRWData(fd
, true, seconds
, useconds
);
288 int waitForRWData(int fd
, bool waitForRead
, int seconds
, int useconds
)
293 memset(&pfd
, 0, sizeof(pfd
));
301 ret
= poll(&pfd
, 1, seconds
* 1000 + useconds
/1000);
303 errno
= ETIMEDOUT
; // ???
308 // returns -1 in case of error, 0 if no data is available, 1 if there is. In the first two cases, errno is set
309 int waitFor2Data(int fd1
, int fd2
, int seconds
, int useconds
, int*fd
)
313 struct pollfd pfds
[2];
314 memset(&pfds
[0], 0, 2*sizeof(struct pollfd
));
318 pfds
[0].events
= pfds
[1].events
= POLLIN
;
320 int nsocks
= 1 + (fd2
>= 0); // fd2 can optionally be -1
323 ret
= poll(pfds
, nsocks
, seconds
* 1000 + useconds
/1000);
325 ret
= poll(pfds
, nsocks
, -1);
329 if((pfds
[0].revents
& POLLIN
) && !(pfds
[1].revents
& POLLIN
))
331 else if((pfds
[1].revents
& POLLIN
) && !(pfds
[0].revents
& POLLIN
))
334 *fd
= pfds
[random()%2].fd
;
337 *fd
= -1; // should never happen
343 string
humanDuration(time_t passed
)
347 ret
<<passed
<<" seconds";
349 ret
<<std::setprecision(2)<<passed
/60.0<<" minutes";
350 else if(passed
<86400)
351 ret
<<std::setprecision(3)<<passed
/3600.0<<" hours";
352 else if(passed
<(86400*30.41))
353 ret
<<std::setprecision(3)<<passed
/86400.0<<" days";
355 ret
<<std::setprecision(3)<<passed
/(86400*30.41)<<" months";
362 // set(); // saves lots of gettimeofday calls
365 DTime::DTime(const DTime
&dt
)
375 const string
unquotify(const string
&item
)
380 string::size_type bpos
=0, epos
=item
.size();
385 if(item
[epos
-1]=='"')
388 return item
.substr(bpos
,epos
-bpos
);
391 void stripLine(string
&line
)
393 string::size_type pos
=line
.find_first_of("\r\n");
394 if(pos
!=string::npos
) {
399 string
urlEncode(const string
&text
)
402 for(string::const_iterator i
=text
.begin();i
!=text
.end();++i
)
403 if(*i
==' ')ret
.append("%20");
404 else ret
.append(1,*i
);
410 #ifndef MAXHOSTNAMELEN
411 #define MAXHOSTNAMELEN 255
414 char tmp
[MAXHOSTNAMELEN
];
415 if(gethostname(tmp
, MAXHOSTNAMELEN
))
428 string
uitoa(unsigned int i
) // MSVC 6 doesn't grok overloading (un)signed
438 return strerror(errno
);
441 string
netstringerror()
443 return stringerror();
446 void cleanSlashes(string
&str
)
448 string::const_iterator i
;
450 for(i
=str
.begin();i
!=str
.end();++i
) {
451 if(*i
=='/' && i
!=str
.begin() && *(i
-1)=='/')
459 bool IpToU32(const string
&str
, uint32_t *ip
)
467 if(Utility::inet_aton(str
.c_str(), &inp
)) {
474 string
U32ToIP(uint32_t val
)
477 snprintf(tmp
, sizeof(tmp
)-1, "%u.%u.%u.%u",
486 string
makeHexDump(const string
& str
)
490 ret
.reserve((int)(str
.size()*2.2));
492 for(string::size_type n
=0;n
<str
.size();++n
) {
493 sprintf(tmp
,"%02x ", (unsigned char)str
[n
]);
499 // shuffle, maintaining some semblance of order
500 void shuffle(vector
<DNSResourceRecord
>& rrs
)
502 vector
<DNSResourceRecord
>::iterator first
, second
;
503 for(first
=rrs
.begin();first
!=rrs
.end();++first
)
504 if(first
->d_place
==DNSResourceRecord::ANSWER
&& first
->qtype
.getCode() != QType::CNAME
) // CNAME must come first
506 for(second
=first
;second
!=rrs
.end();++second
)
507 if(second
->d_place
!=DNSResourceRecord::ANSWER
)
511 random_shuffle(first
,second
);
513 // now shuffle the additional records
514 for(first
=second
;first
!=rrs
.end();++first
)
515 if(first
->d_place
==DNSResourceRecord::ADDITIONAL
&& first
->qtype
.getCode() != QType::CNAME
) // CNAME must come first
517 for(second
=first
;second
!=rrs
.end();++second
)
518 if(second
->d_place
!=DNSResourceRecord::ADDITIONAL
)
522 random_shuffle(first
,second
);
524 // we don't shuffle the rest
527 static bool comparePlace(DNSResourceRecord a
, DNSResourceRecord b
)
529 return (a
.d_place
< b
.d_place
);
532 // make sure rrs is sorted in d_place order to avoid surprises later
533 // then shuffle the parts that desire shuffling
534 void orderAndShuffle(vector
<DNSResourceRecord
>& rrs
)
536 std::stable_sort(rrs
.begin(), rrs
.end(), comparePlace
);
540 void normalizeTV(struct timeval
& tv
)
542 if(tv
.tv_usec
> 1000000) {
546 else if(tv
.tv_usec
< 0) {
552 const struct timeval
operator+(const struct timeval
& lhs
, const struct timeval
& rhs
)
555 ret
.tv_sec
=lhs
.tv_sec
+ rhs
.tv_sec
;
556 ret
.tv_usec
=lhs
.tv_usec
+ rhs
.tv_usec
;
561 const struct timeval
operator-(const struct timeval
& lhs
, const struct timeval
& rhs
)
564 ret
.tv_sec
=lhs
.tv_sec
- rhs
.tv_sec
;
565 ret
.tv_usec
=lhs
.tv_usec
- rhs
.tv_usec
;
570 pair
<string
, string
> splitField(const string
& inp
, char sepa
)
572 pair
<string
, string
> ret
;
573 string::size_type cpos
=inp
.find(sepa
);
574 if(cpos
==string::npos
)
577 ret
.first
=inp
.substr(0, cpos
);
578 ret
.second
=inp
.substr(cpos
+1);
583 int logFacilityToLOG(unsigned int facility
)
607 string
stripDot(const string
& dom
)
612 if(dom
[dom
.size()-1]!='.')
615 return dom
.substr(0,dom
.size()-1);
619 string
labelReverse(const std::string
& qname
)
624 bool dotName
= qname
.find('.') != string::npos
;
626 vector
<string
> labels
;
627 stringtok(labels
, qname
, ". ");
631 string ret
; // vv const_reverse_iter http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11729
632 for(vector
<string
>::reverse_iterator iter
= labels
.rbegin(); iter
!= labels
.rend(); ++iter
) {
633 if(iter
!= labels
.rbegin())
634 ret
.append(1, dotName
? ' ' : '.');
640 // do NOT feed trailing dots!
641 // www.powerdns.com, powerdns.com -> www
642 string
makeRelative(const std::string
& fqdn
, const std::string
& zone
)
647 return fqdn
.substr(0, fqdn
.size() - zone
.length() - 1); // strip domain name
651 string
dotConcat(const std::string
& a
, const std::string
&b
)
653 if(a
.empty() || b
.empty())
659 int makeIPv6sockaddr(const std::string
& addr
, struct sockaddr_in6
* ret
)
663 string
ourAddr(addr
);
665 if(addr
[0]=='[') { // [::]:53 style address
666 string::size_type pos
= addr
.find(']');
667 if(pos
== string::npos
|| pos
+ 2 > addr
.size() || addr
[pos
+1]!=':')
669 ourAddr
.assign(addr
.c_str() + 1, pos
-1);
670 port
= atoi(addr
.c_str()+pos
+2);
673 struct addrinfo
* res
;
674 struct addrinfo hints
;
675 memset(&hints
, 0, sizeof(hints
));
677 hints
.ai_family
= AF_INET6
;
678 hints
.ai_flags
= AI_NUMERICHOST
;
681 if((error
=getaddrinfo(ourAddr
.c_str(), 0, &hints
, &res
))) { // this is correct
683 cerr<<"Error translating IPv6 address '"<<addr<<"': ";
684 if(error==EAI_SYSTEM)
685 cerr<<strerror(errno)<<endl;
687 cerr<<gai_strerror(error)<<endl;
692 memcpy(ret
, res
->ai_addr
, res
->ai_addrlen
);
694 ret
->sin6_port
= htons(port
);
699 int makeIPv4sockaddr(const std::string
& str
, struct sockaddr_in
* ret
)
706 string::size_type pos
= str
.find(':');
707 if(pos
== string::npos
) { // no port specified, not touching the port
708 if(Utility::inet_aton(str
.c_str(), &inp
)) {
709 ret
->sin_addr
.s_addr
=inp
.s_addr
;
714 if(!*(str
.c_str() + pos
+ 1)) // trailing :
717 char *eptr
= (char*)str
.c_str() + str
.size();
718 int port
= strtol(str
.c_str() + pos
+ 1, &eptr
, 10);
722 ret
->sin_port
= htons(port
);
723 if(Utility::inet_aton(str
.substr(0, pos
).c_str(), &inp
)) {
724 ret
->sin_addr
.s_addr
=inp
.s_addr
;
730 int makeUNsockaddr(const std::string
& path
, struct sockaddr_un
* ret
)
735 memset(ret
, 0, sizeof(struct sockaddr_un
));
736 ret
->sun_family
= AF_UNIX
;
737 if (path
.length() >= sizeof(ret
->sun_path
))
740 path
.copy(ret
->sun_path
, sizeof(ret
->sun_path
), 0);
744 //! read a line of text from a FILE* to a std::string, returns false on 'no data'
745 bool stringfgets(FILE* fp
, std::string
& line
)
751 if(!fgets(buffer
, sizeof(buffer
), fp
))
752 return !line
.empty();
755 } while(!strchr(buffer
, '\n'));
759 Regex::Regex(const string
&expr
)
761 if(regcomp(&d_preg
, expr
.c_str(), REG_ICASE
|REG_NOSUB
|REG_EXTENDED
))
762 throw PDNSException("Regular expression did not compile");