]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/misc.cc
dnsproxy uses sendmsg instead of sendto
[thirdparty/pdns.git] / pdns / misc.cc
CommitLineData
12c86877
BH
1/*
2 PowerDNS Versatile Database Driven Nameserver
c68cc098 3 Copyright (C) 2002 - 2010 PowerDNS.COM BV
12c86877
BH
4
5 This program is free software; you can redistribute it and/or modify
22dc646a
BH
6 it under the terms of the GNU General Public License version 2
7 as published by the Free Software Foundation
f782fe38
MH
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.
12c86877
BH
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
06bd9ccf 20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
12c86877 21*/
705f31ae 22
705f31ae
BH
23#include <sys/param.h>
24#include <netdb.h>
25#include <sys/time.h>
26#include <time.h>
27#include <netinet/in.h>
76cb4593 28#include <sys/un.h>
705f31ae 29#include <unistd.h>
12c86877
BH
30#include "misc.hh"
31#include <vector>
32#include <sstream>
33#include <errno.h>
1258abe0 34#include <cstring>
c4bee46c 35#include <iostream>
705f31ae 36#include <algorithm>
6e2514e4 37#include <boost/optional.hpp>
fab091b9 38#include <poll.h>
12c86877 39#include <iomanip>
12c86877
BH
40#include <string.h>
41#include <stdlib.h>
42#include <stdio.h>
5c409fa2 43#include "pdnsexception.hh"
c6566265 44#include <sys/types.h>
12c86877 45#include "utility.hh"
040712e0 46#include <boost/algorithm/string.hpp>
ba1a571d 47#include "logger.hh"
040712e0 48
1e08edde
BH
49bool g_singleThreaded;
50
040712e0
BH
51int 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)
4957a608 61 throw std::runtime_error("used writen2 on non-blocking socket, got EAGAIN");
040712e0 62 else
4957a608 63 unixDie("failed in writen2");
040712e0
BH
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
36fbf590 74int 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
12c86877 96
cc3afe25
BH
97string nowTime()
98{
99 time_t now=time(0);
100 string t=ctime(&now);
040712e0 101 boost::trim_right(t);
cc3afe25
BH
102 return t;
103}
12c86877 104
092f210a 105uint16_t getShort(const unsigned char *p)
0d8612f2
BH
106{
107 return p[0] * 256 + p[1];
108}
109
110
092f210a 111uint16_t getShort(const char *p)
0d8612f2
BH
112{
113 return getShort((const unsigned char *)p);
114}
115
092f210a 116uint32_t getLong(const unsigned char* p)
b636533b
BH
117{
118 return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3];
119}
120
092f210a 121uint32_t getLong(const char* p)
b636533b
BH
122{
123 return getLong((unsigned char *)p);
124}
125
0d8612f2
BH
126
127
f2c11a48
BH
128/** strips a domain suffix from a domain, returns true if it stripped */
129bool stripDomainSuffix(string *qname, const string &domain)
130{
c4bee46c 131 if(!endsOn(*qname, domain))
f2c11a48
BH
132 return false;
133
c4bee46c 134 if(toLower(*qname)==toLower(domain))
f2c11a48
BH
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
7738a23f 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 */
f6a9e00a 146bool chopOff(string &domain)
728485ca
BH
147{
148 if(domain.empty())
149 return false;
150
151 string::size_type fdot=domain.find('.');
152
153 if(fdot==string::npos)
154 domain="";
f6a9e00a
BH
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 }
728485ca
BH
161 return true;
162}
163
7738a23f
BH
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 */
165bool chopOffDotted(string &domain)
166{
167 if(domain.empty() || (domain.size()==1 && domain[0]=='.'))
168 return false;
169
170 string::size_type fdot=domain.find('.');
02ac88c8
BH
171 if(fdot == string::npos)
172 return false;
7738a23f
BH
173
174 if(fdot==domain.size()-1)
175 domain=".";
f6a9e00a
BH
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 }
7738a23f
BH
182 return true;
183}
184
185
49bd5a20
BH
186bool 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
728485ca
BH
198/** does domain end on suffix? Is smart about "wwwds9a.nl" "ds9a.nl" not matching */
199bool endsOn(const string &domain, const string &suffix)
200{
49bd5a20 201 if( suffix.empty() || ciEqual(domain, suffix) )
728485ca 202 return true;
7738a23f 203
728485ca
BH
204 if(domain.size()<=suffix.size())
205 return false;
49bd5a20
BH
206
207 string::size_type dpos=domain.size()-suffix.size()-1, spos=0;
7738a23f 208
49bd5a20
BH
209 if(domain[dpos++]!='.')
210 return false;
728485ca 211
49bd5a20
BH
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}
f2c11a48 218
7738a23f
BH
219/** does domain end on suffix? Is smart about "wwwds9a.nl" "ds9a.nl" not matching */
220bool 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
32252594 240static void parseService4(const string &descr, ServiceTuple &st)
12c86877
BH
241{
242 vector<string>parts;
243 stringtok(parts,descr,":");
52936200 244 if(parts.empty())
3f81d239 245 throw PDNSException("Unable to parse '"+descr+"' as a service");
12c86877
BH
246 st.host=parts[0];
247 if(parts.size()>1)
248 st.port=atoi(parts[1].c_str());
249}
250
32252594
BH
251static void parseService6(const string &descr, ServiceTuple &st)
252{
253 string::size_type pos=descr.find(']');
254 if(pos == string::npos)
3f81d239 255 throw PDNSException("Unable to parse '"+descr+"' as an IPv6 service");
32252594
BH
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
263void parseService(const string &descr, ServiceTuple &st)
264{
265 if(descr.empty())
3f81d239 266 throw PDNSException("Unable to parse '"+descr+"' as a service");
32252594
BH
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}
12c86877 281
1fb3f0fc 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
a7d50153 283int waitForData(int fd, int seconds, int useconds)
649a88df
BH
284{
285 return waitForRWData(fd, true, seconds, useconds);
286}
287
288int waitForRWData(int fd, bool waitForRead, int seconds, int useconds)
12c86877 289{
1258abe0
BH
290 int ret;
291
fab091b9
BH
292 struct pollfd pfd;
293 memset(&pfd, 0, sizeof(pfd));
294 pfd.fd = fd;
295
649a88df 296 if(waitForRead)
fab091b9 297 pfd.events=POLLIN;
649a88df 298 else
fab091b9 299 pfd.events=POLLOUT;
1258abe0 300
6b3d4330 301 ret = poll(&pfd, 1, seconds * 1000 + useconds/1000);
1258abe0 302 if ( ret == -1 )
f4ff5929 303 errno = ETIMEDOUT; // ???
1258abe0 304
12c86877
BH
305 return ret;
306}
307
a71bee29 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
f4ff5929
BH
309int 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
12c86877
BH
342
343string humanDuration(time_t passed)
344{
345 ostringstream ret;
346 if(passed<60)
347 ret<<passed<<" seconds";
348 else if(passed<3600)
9e04108d 349 ret<<std::setprecision(2)<<passed/60.0<<" minutes";
12c86877 350 else if(passed<86400)
9e04108d 351 ret<<std::setprecision(3)<<passed/3600.0<<" hours";
12c86877 352 else if(passed<(86400*30.41))
9e04108d 353 ret<<std::setprecision(3)<<passed/86400.0<<" days";
12c86877 354 else
9e04108d 355 ret<<std::setprecision(3)<<passed/(86400*30.41)<<" months";
12c86877
BH
356
357 return ret.str();
358}
359
360DTime::DTime()
361{
eefd15f9 362// set(); // saves lots of gettimeofday calls
12c86877
BH
363}
364
365DTime::DTime(const DTime &dt)
366{
367 d_set=dt.d_set;
368}
369
370time_t DTime::time()
371{
372 return d_set.tv_sec;
373}
374
1d329048
BH
375const string unquotify(const string &item)
376{
377 if(item.size()<2)
378 return item;
379
380 string::size_type bpos=0, epos=item.size();
12c86877 381
c4bee46c 382 if(item[0]=='"')
1d329048 383 bpos=1;
c4bee46c 384
1d329048
BH
385 if(item[epos-1]=='"')
386 epos-=1;
387
c4bee46c 388 return item.substr(bpos,epos-bpos);
1d329048 389}
12c86877
BH
390
391void stripLine(string &line)
392{
bdf40704 393 string::size_type pos=line.find_first_of("\r\n");
12c86877
BH
394 if(pos!=string::npos) {
395 line.resize(pos);
396 }
397}
398
399string 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
408string getHostname()
409{
0db70140 410#ifndef MAXHOSTNAMELEN
76473b92 411#define MAXHOSTNAMELEN 255
0db70140 412#endif
ac2bb9e7 413
12c86877
BH
414 char tmp[MAXHOSTNAMELEN];
415 if(gethostname(tmp, MAXHOSTNAMELEN))
416 return "UNKNOWN";
417
418 return tmp;
419}
420
421string itoa(int i)
422{
423 ostringstream o;
424 o<<i;
425 return o.str();
426}
427
22c9c86a
BH
428string 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
12c86877
BH
436string stringerror()
437{
438 return strerror(errno);
439}
440
d3b4137e
BH
441string netstringerror()
442{
443 return stringerror();
444}
d3b4137e 445
12c86877
BH
446void 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
7b35aa49 458
092f210a 459bool IpToU32(const string &str, uint32_t *ip)
525b8a7c 460{
4cf26303
BH
461 if(str.empty()) {
462 *ip=0;
463 return true;
464 }
465
092c9cc4 466 struct in_addr inp;
d3b729e8 467 if(Utility::inet_aton(str.c_str(), &inp)) {
092c9cc4
BH
468 *ip=inp.s_addr;
469 return true;
470 }
471 return false;
525b8a7c
BH
472}
473
5730f30c
BH
474string U32ToIP(uint32_t val)
475{
476 char tmp[17];
477 snprintf(tmp, sizeof(tmp)-1, "%u.%u.%u.%u",
4957a608
BH
478 (val >> 24)&0xff,
479 (val >> 16)&0xff,
480 (val >> 8)&0xff,
481 (val )&0xff);
5730f30c
BH
482 return tmp;
483}
484
37d3f960 485
2db9c30e
BH
486string makeHexDump(const string& str)
487{
488 char tmp[5];
489 string ret;
ea634573 490 ret.reserve((int)(str.size()*2.2));
2db9c30e
BH
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
e67e250f
BH
499// shuffle, maintaining some semblance of order
500void 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}
88358c9b 526
92476c8b
PD
527static 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
534void orderAndShuffle(vector<DNSResourceRecord>& rrs)
535{
536 std::stable_sort(rrs.begin(), rrs.end(), comparePlace);
537 shuffle(rrs);
538}
88358c9b
BH
539
540void 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
552const 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
561const 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}
50c79a76
BH
569
570pair<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}
6e2514e4 582
f8499e52 583int logFacilityToLOG(unsigned int facility)
6e2514e4 584{
6e2514e4
BH
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:
f8499e52 603 return -1;
6e2514e4
BH
604 }
605}
da042e6e
BH
606
607string 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}
4dadd22f
BH
617
618
619string labelReverse(const std::string& qname)
620{
621 if(qname.empty())
622 return qname;
623
ee6bba9e
BH
624 bool dotName = qname.find('.') != string::npos;
625
4dadd22f 626 vector<string> labels;
ee6bba9e 627 stringtok(labels, qname, ". ");
4dadd22f
BH
628 if(labels.size()==1)
629 return qname;
630
11d8a43f
BH
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) {
4dadd22f 633 if(iter != labels.rbegin())
ee6bba9e 634 ret.append(1, dotName ? ' ' : '.');
4dadd22f
BH
635 ret+=*iter;
636 }
637 return ret;
638}
639
640// do NOT feed trailing dots!
641// www.powerdns.com, powerdns.com -> www
642string 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}
75943e62
BH
650
651string 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}
f71bc087
BH
658
659int makeIPv6sockaddr(const std::string& addr, struct sockaddr_in6* ret)
660{
85db02c5
BH
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
f71bc087
BH
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
f4f4f533 680 int error;
85db02c5 681 if((error=getaddrinfo(ourAddr.c_str(), 0, &hints, &res))) { // this is correct
f4f4f533
BH
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 */
f71bc087
BH
689 return -1;
690 }
691
f4f4f533 692 memcpy(ret, res->ai_addr, res->ai_addrlen);
85db02c5
BH
693 if(port >= 0)
694 ret->sin6_port = htons(port);
f71bc087
BH
695 freeaddrinfo(res);
696 return 0;
697}
834942f1 698
76cb4593 699int makeIPv4sockaddr(const std::string& str, struct sockaddr_in* ret)
85db02c5
BH
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
76cb4593
CH
730int 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}
85db02c5 743
834942f1
BH
744//! read a line of text from a FILE* to a std::string, returns false on 'no data'
745bool 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}
e611a06c 758
3ee84c3f
BH
759Regex::Regex(const string &expr)
760{
761 if(regcomp(&d_preg, expr.c_str(), REG_ICASE|REG_NOSUB|REG_EXTENDED))
3f81d239 762 throw PDNSException("Regular expression did not compile");
3ee84c3f 763}