]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/misc.cc
add OpenSSL exception to PowerDNS, Netherlabs, van Dijk and Hubert copyrights
[thirdparty/pdns.git] / pdns / misc.cc
1 /*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002 - 2010 PowerDNS.COM BV
4
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
8
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.
12
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.
17
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
21 */
22
23 #include <sys/param.h>
24 #include <netdb.h>
25 #include <sys/time.h>
26 #include <time.h>
27 #include <netinet/in.h>
28 #include <sys/un.h>
29 #include <unistd.h>
30 #include "misc.hh"
31 #include <vector>
32 #include <sstream>
33 #include <errno.h>
34 #include <cstring>
35 #include <iostream>
36 #include <algorithm>
37 #include <boost/optional.hpp>
38 #include <poll.h>
39 #include <iomanip>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include "pdnsexception.hh"
44 #include <sys/types.h>
45 #include "utility.hh"
46 #include <boost/algorithm/string.hpp>
47 #include "logger.hh"
48
49 bool g_singleThreaded;
50
51 int writen2(int fd, const void *buf, size_t count)
52 {
53 const char *ptr = (char*)buf;
54 const char *eptr = ptr + count;
55
56 int res;
57 while(ptr != eptr) {
58 res = ::write(fd, ptr, eptr - ptr);
59 if(res < 0) {
60 if (errno == EAGAIN)
61 throw std::runtime_error("used writen2 on non-blocking socket, got EAGAIN");
62 else
63 unixDie("failed in writen2");
64 }
65 else if (res == 0)
66 throw std::runtime_error("could not write all bytes, got eof in writen2");
67
68 ptr += res;
69 }
70
71 return count;
72 }
73
74 int readn2(int fd, void* buffer, unsigned int len)
75 {
76 unsigned int pos=0;
77 int res;
78 for(;;) {
79 res = read(fd, (char*)buffer + pos, len - pos);
80 if(res == 0)
81 throw runtime_error("EOF while writing message");
82 if(res < 0) {
83 if (errno == EAGAIN)
84 throw std::runtime_error("used writen2 on non-blocking socket, got EAGAIN");
85 else
86 unixDie("failed in writen2");
87 }
88
89 pos+=res;
90 if(pos == len)
91 break;
92 }
93 return len;
94 }
95
96
97 string nowTime()
98 {
99 time_t now=time(0);
100 string t=ctime(&now);
101 boost::trim_right(t);
102 return t;
103 }
104
105 uint16_t getShort(const unsigned char *p)
106 {
107 return p[0] * 256 + p[1];
108 }
109
110
111 uint16_t getShort(const char *p)
112 {
113 return getShort((const unsigned char *)p);
114 }
115
116 uint32_t getLong(const unsigned char* p)
117 {
118 return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3];
119 }
120
121 uint32_t getLong(const char* p)
122 {
123 return getLong((unsigned char *)p);
124 }
125
126
127
128 /** strips a domain suffix from a domain, returns true if it stripped */
129 bool stripDomainSuffix(string *qname, const string &domain)
130 {
131 if(!endsOn(*qname, domain))
132 return false;
133
134 if(toLower(*qname)==toLower(domain))
135 *qname="@";
136 else {
137 if((*qname)[qname->size()-domain.size()-1]!='.')
138 return false;
139
140 qname->resize(qname->size()-domain.size()-1);
141 }
142 return true;
143 }
144
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)
147 {
148 if(domain.empty())
149 return false;
150
151 string::size_type fdot=domain.find('.');
152
153 if(fdot==string::npos)
154 domain="";
155 else {
156 string::size_type remain = domain.length() - (fdot + 1);
157 char tmp[remain];
158 memcpy(tmp, domain.c_str()+fdot+1, remain);
159 domain.assign(tmp, remain); // don't dare to do this w/o tmp holder :-)
160 }
161 return true;
162 }
163
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)
166 {
167 if(domain.empty() || (domain.size()==1 && domain[0]=='.'))
168 return false;
169
170 string::size_type fdot=domain.find('.');
171 if(fdot == string::npos)
172 return false;
173
174 if(fdot==domain.size()-1)
175 domain=".";
176 else {
177 string::size_type remain = domain.length() - (fdot + 1);
178 char tmp[remain];
179 memcpy(tmp, domain.c_str()+fdot+1, remain);
180 domain.assign(tmp, remain);
181 }
182 return true;
183 }
184
185
186 bool ciEqual(const string& a, const string& b)
187 {
188 if(a.size()!=b.size())
189 return false;
190
191 string::size_type pos=0, epos=a.size();
192 for(;pos < epos; ++pos)
193 if(dns_tolower(a[pos])!=dns_tolower(b[pos]))
194 return false;
195 return true;
196 }
197
198 /** does domain end on suffix? Is smart about "wwwds9a.nl" "ds9a.nl" not matching */
199 bool endsOn(const string &domain, const string &suffix)
200 {
201 if( suffix.empty() || ciEqual(domain, suffix) )
202 return true;
203
204 if(domain.size()<=suffix.size())
205 return false;
206
207 string::size_type dpos=domain.size()-suffix.size()-1, spos=0;
208
209 if(domain[dpos++]!='.')
210 return false;
211
212 for(; dpos < domain.size(); ++dpos, ++spos)
213 if(dns_tolower(domain[dpos]) != dns_tolower(suffix[spos]))
214 return false;
215
216 return true;
217 }
218
219 /** does domain end on suffix? Is smart about "wwwds9a.nl" "ds9a.nl" not matching */
220 bool dottedEndsOn(const string &domain, const string &suffix)
221 {
222 if( suffix=="." || ciEqual(domain, suffix) )
223 return true;
224
225 if(domain.size()<=suffix.size())
226 return false;
227
228 string::size_type dpos=domain.size()-suffix.size()-1, spos=0;
229
230 if(domain[dpos++]!='.')
231 return false;
232
233 for(; dpos < domain.size(); ++dpos, ++spos)
234 if(dns_tolower(domain[dpos]) != dns_tolower(suffix[spos]))
235 return false;
236
237 return true;
238 }
239
240 static void parseService4(const string &descr, ServiceTuple &st)
241 {
242 vector<string>parts;
243 stringtok(parts,descr,":");
244 if(parts.empty())
245 throw PDNSException("Unable to parse '"+descr+"' as a service");
246 st.host=parts[0];
247 if(parts.size()>1)
248 st.port=atoi(parts[1].c_str());
249 }
250
251 static void parseService6(const string &descr, ServiceTuple &st)
252 {
253 string::size_type pos=descr.find(']');
254 if(pos == string::npos)
255 throw PDNSException("Unable to parse '"+descr+"' as an IPv6 service");
256
257 st.host=descr.substr(1, pos-1);
258 if(pos + 2 < descr.length())
259 st.port=atoi(descr.c_str() + pos +2);
260 }
261
262
263 void parseService(const string &descr, ServiceTuple &st)
264 {
265 if(descr.empty())
266 throw PDNSException("Unable to parse '"+descr+"' as a service");
267
268 vector<string> parts;
269 stringtok(parts, descr, ":");
270
271 if(descr[0]=='[') {
272 parseService6(descr, st);
273 }
274 else if(descr[0]==':' || parts.size() > 2 || descr.find("::") != string::npos) {
275 st.host=descr;
276 }
277 else {
278 parseService4(descr, st);
279 }
280 }
281
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)
284 {
285 return waitForRWData(fd, true, seconds, useconds);
286 }
287
288 int waitForRWData(int fd, bool waitForRead, int seconds, int useconds)
289 {
290 int ret;
291
292 struct pollfd pfd;
293 memset(&pfd, 0, sizeof(pfd));
294 pfd.fd = fd;
295
296 if(waitForRead)
297 pfd.events=POLLIN;
298 else
299 pfd.events=POLLOUT;
300
301 ret = poll(&pfd, 1, seconds * 1000 + useconds/1000);
302 if ( ret == -1 )
303 errno = ETIMEDOUT; // ???
304
305 return ret;
306 }
307
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)
310 {
311 int ret;
312
313 struct pollfd pfds[2];
314 memset(&pfds[0], 0, 2*sizeof(struct pollfd));
315 pfds[0].fd = fd1;
316 pfds[1].fd = fd2;
317
318 pfds[0].events= pfds[1].events = POLLIN;
319
320 int nsocks = 1 + (fd2 >= 0); // fd2 can optionally be -1
321
322 if(seconds >= 0)
323 ret = poll(pfds, nsocks, seconds * 1000 + useconds/1000);
324 else
325 ret = poll(pfds, nsocks, -1);
326 if(!ret || ret < 0)
327 return ret;
328
329 if((pfds[0].revents & POLLIN) && !(pfds[1].revents & POLLIN))
330 *fd = pfds[0].fd;
331 else if((pfds[1].revents & POLLIN) && !(pfds[0].revents & POLLIN))
332 *fd = pfds[1].fd;
333 else if(ret == 2) {
334 *fd = pfds[random()%2].fd;
335 }
336 else
337 *fd = -1; // should never happen
338
339 return 1;
340 }
341
342
343 string humanDuration(time_t passed)
344 {
345 ostringstream ret;
346 if(passed<60)
347 ret<<passed<<" seconds";
348 else if(passed<3600)
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";
354 else
355 ret<<std::setprecision(3)<<passed/(86400*30.41)<<" months";
356
357 return ret.str();
358 }
359
360 DTime::DTime()
361 {
362 // set(); // saves lots of gettimeofday calls
363 }
364
365 DTime::DTime(const DTime &dt)
366 {
367 d_set=dt.d_set;
368 }
369
370 time_t DTime::time()
371 {
372 return d_set.tv_sec;
373 }
374
375 const string unquotify(const string &item)
376 {
377 if(item.size()<2)
378 return item;
379
380 string::size_type bpos=0, epos=item.size();
381
382 if(item[0]=='"')
383 bpos=1;
384
385 if(item[epos-1]=='"')
386 epos-=1;
387
388 return item.substr(bpos,epos-bpos);
389 }
390
391 void stripLine(string &line)
392 {
393 string::size_type pos=line.find_first_of("\r\n");
394 if(pos!=string::npos) {
395 line.resize(pos);
396 }
397 }
398
399 string urlEncode(const string &text)
400 {
401 string ret;
402 for(string::const_iterator i=text.begin();i!=text.end();++i)
403 if(*i==' ')ret.append("%20");
404 else ret.append(1,*i);
405 return ret;
406 }
407
408 string getHostname()
409 {
410 #ifndef MAXHOSTNAMELEN
411 #define MAXHOSTNAMELEN 255
412 #endif
413
414 char tmp[MAXHOSTNAMELEN];
415 if(gethostname(tmp, MAXHOSTNAMELEN))
416 return "UNKNOWN";
417
418 return tmp;
419 }
420
421 string itoa(int i)
422 {
423 ostringstream o;
424 o<<i;
425 return o.str();
426 }
427
428 string uitoa(unsigned int i) // MSVC 6 doesn't grok overloading (un)signed
429 {
430 ostringstream o;
431 o<<i;
432 return o.str();
433 }
434
435
436 string stringerror()
437 {
438 return strerror(errno);
439 }
440
441 string netstringerror()
442 {
443 return stringerror();
444 }
445
446 void cleanSlashes(string &str)
447 {
448 string::const_iterator i;
449 string out;
450 for(i=str.begin();i!=str.end();++i) {
451 if(*i=='/' && i!=str.begin() && *(i-1)=='/')
452 continue;
453 out.append(1,*i);
454 }
455 str=out;
456 }
457
458
459 bool IpToU32(const string &str, uint32_t *ip)
460 {
461 if(str.empty()) {
462 *ip=0;
463 return true;
464 }
465
466 struct in_addr inp;
467 if(Utility::inet_aton(str.c_str(), &inp)) {
468 *ip=inp.s_addr;
469 return true;
470 }
471 return false;
472 }
473
474 string U32ToIP(uint32_t val)
475 {
476 char tmp[17];
477 snprintf(tmp, sizeof(tmp)-1, "%u.%u.%u.%u",
478 (val >> 24)&0xff,
479 (val >> 16)&0xff,
480 (val >> 8)&0xff,
481 (val )&0xff);
482 return tmp;
483 }
484
485
486 string makeHexDump(const string& str)
487 {
488 char tmp[5];
489 string ret;
490 ret.reserve((int)(str.size()*2.2));
491
492 for(string::size_type n=0;n<str.size();++n) {
493 sprintf(tmp,"%02x ", (unsigned char)str[n]);
494 ret+=tmp;
495 }
496 return ret;
497 }
498
499 // shuffle, maintaining some semblance of order
500 void shuffle(vector<DNSResourceRecord>& rrs)
501 {
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
505 break;
506 for(second=first;second!=rrs.end();++second)
507 if(second->d_place!=DNSResourceRecord::ANSWER)
508 break;
509
510 if(second-first>1)
511 random_shuffle(first,second);
512
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
516 break;
517 for(second=first;second!=rrs.end();++second)
518 if(second->d_place!=DNSResourceRecord::ADDITIONAL)
519 break;
520
521 if(second-first>1)
522 random_shuffle(first,second);
523
524 // we don't shuffle the rest
525 }
526
527 static bool comparePlace(DNSResourceRecord a, DNSResourceRecord b)
528 {
529 return (a.d_place < b.d_place);
530 }
531
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)
535 {
536 std::stable_sort(rrs.begin(), rrs.end(), comparePlace);
537 shuffle(rrs);
538 }
539
540 void normalizeTV(struct timeval& tv)
541 {
542 if(tv.tv_usec > 1000000) {
543 ++tv.tv_sec;
544 tv.tv_usec-=1000000;
545 }
546 else if(tv.tv_usec < 0) {
547 --tv.tv_sec;
548 tv.tv_usec+=1000000;
549 }
550 }
551
552 const struct timeval operator+(const struct timeval& lhs, const struct timeval& rhs)
553 {
554 struct timeval ret;
555 ret.tv_sec=lhs.tv_sec + rhs.tv_sec;
556 ret.tv_usec=lhs.tv_usec + rhs.tv_usec;
557 normalizeTV(ret);
558 return ret;
559 }
560
561 const struct timeval operator-(const struct timeval& lhs, const struct timeval& rhs)
562 {
563 struct timeval ret;
564 ret.tv_sec=lhs.tv_sec - rhs.tv_sec;
565 ret.tv_usec=lhs.tv_usec - rhs.tv_usec;
566 normalizeTV(ret);
567 return ret;
568 }
569
570 pair<string, string> splitField(const string& inp, char sepa)
571 {
572 pair<string, string> ret;
573 string::size_type cpos=inp.find(sepa);
574 if(cpos==string::npos)
575 ret.first=inp;
576 else {
577 ret.first=inp.substr(0, cpos);
578 ret.second=inp.substr(cpos+1);
579 }
580 return ret;
581 }
582
583 int logFacilityToLOG(unsigned int facility)
584 {
585 switch(facility) {
586 case 0:
587 return LOG_LOCAL0;
588 case 1:
589 return(LOG_LOCAL1);
590 case 2:
591 return(LOG_LOCAL2);
592 case 3:
593 return(LOG_LOCAL3);
594 case 4:
595 return(LOG_LOCAL4);
596 case 5:
597 return(LOG_LOCAL5);
598 case 6:
599 return(LOG_LOCAL6);
600 case 7:
601 return(LOG_LOCAL7);
602 default:
603 return -1;
604 }
605 }
606
607 string stripDot(const string& dom)
608 {
609 if(dom.empty())
610 return dom;
611
612 if(dom[dom.size()-1]!='.')
613 return dom;
614
615 return dom.substr(0,dom.size()-1);
616 }
617
618
619 string labelReverse(const std::string& qname)
620 {
621 if(qname.empty())
622 return qname;
623
624 bool dotName = qname.find('.') != string::npos;
625
626 vector<string> labels;
627 stringtok(labels, qname, ". ");
628 if(labels.size()==1)
629 return qname;
630
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 ? ' ' : '.');
635 ret+=*iter;
636 }
637 return ret;
638 }
639
640 // do NOT feed trailing dots!
641 // www.powerdns.com, powerdns.com -> www
642 string makeRelative(const std::string& fqdn, const std::string& zone)
643 {
644 if(zone.empty())
645 return fqdn;
646 if(fqdn != zone)
647 return fqdn.substr(0, fqdn.size() - zone.length() - 1); // strip domain name
648 return "";
649 }
650
651 string dotConcat(const std::string& a, const std::string &b)
652 {
653 if(a.empty() || b.empty())
654 return a+b;
655 else
656 return a+"."+b;
657 }
658
659 int makeIPv6sockaddr(const std::string& addr, struct sockaddr_in6* ret)
660 {
661 if(addr.empty())
662 return -1;
663 string ourAddr(addr);
664 int port = -1;
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]!=':')
668 return -1;
669 ourAddr.assign(addr.c_str() + 1, pos-1);
670 port = atoi(addr.c_str()+pos+2);
671 }
672
673 struct addrinfo* res;
674 struct addrinfo hints;
675 memset(&hints, 0, sizeof(hints));
676
677 hints.ai_family = AF_INET6;
678 hints.ai_flags = AI_NUMERICHOST;
679
680 int error;
681 if((error=getaddrinfo(ourAddr.c_str(), 0, &hints, &res))) { // this is correct
682 /*
683 cerr<<"Error translating IPv6 address '"<<addr<<"': ";
684 if(error==EAI_SYSTEM)
685 cerr<<strerror(errno)<<endl;
686 else
687 cerr<<gai_strerror(error)<<endl;
688 */
689 return -1;
690 }
691
692 memcpy(ret, res->ai_addr, res->ai_addrlen);
693 if(port >= 0)
694 ret->sin6_port = htons(port);
695 freeaddrinfo(res);
696 return 0;
697 }
698
699 int makeIPv4sockaddr(const std::string& str, struct sockaddr_in* ret)
700 {
701 if(str.empty()) {
702 return -1;
703 }
704 struct in_addr inp;
705
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;
710 return 0;
711 }
712 return -1;
713 }
714 if(!*(str.c_str() + pos + 1)) // trailing :
715 return -1;
716
717 char *eptr = (char*)str.c_str() + str.size();
718 int port = strtol(str.c_str() + pos + 1, &eptr, 10);
719 if(*eptr)
720 return -1;
721
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;
725 return 0;
726 }
727 return -1;
728 }
729
730 int makeUNsockaddr(const std::string& path, struct sockaddr_un* ret)
731 {
732 if (path.empty())
733 return -1;
734
735 memset(ret, 0, sizeof(struct sockaddr_un));
736 ret->sun_family = AF_UNIX;
737 if (path.length() >= sizeof(ret->sun_path))
738 return -1;
739
740 path.copy(ret->sun_path, sizeof(ret->sun_path), 0);
741 return 0;
742 }
743
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)
746 {
747 char buffer[1024];
748 line.clear();
749
750 do {
751 if(!fgets(buffer, sizeof(buffer), fp))
752 return !line.empty();
753
754 line.append(buffer);
755 } while(!strchr(buffer, '\n'));
756 return true;
757 }
758
759 Regex::Regex(const string &expr)
760 {
761 if(regcomp(&d_preg, expr.c_str(), REG_ICASE|REG_NOSUB|REG_EXTENDED))
762 throw PDNSException("Regular expression did not compile");
763 }