]>
Commit | Line | Data |
---|---|---|
12c86877 BH |
1 | /* |
2 | PowerDNS Versatile Database Driven Nameserver | |
7b308b7e | 3 | Copyright (C) 2002-2005 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 | |
8 | ||
12c86877 BH |
9 | |
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program; if not, write to the Free Software | |
06bd9ccf | 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
12c86877 | 18 | */ |
1258abe0 | 19 | #include "utility.hh" |
12c86877 BH |
20 | #include <cstdio> |
21 | #include <cstring> | |
22 | #include <cstdlib> | |
23 | #include <sys/types.h> | |
24 | #include <iostream> | |
25 | #include <string> | |
26 | #include "tcpreceiver.hh" | |
1258abe0 | 27 | |
12c86877 BH |
28 | #include <errno.h> |
29 | #include <signal.h> | |
30 | ||
31 | #include "ueberbackend.hh" | |
32 | #include "dnspacket.hh" | |
33 | #include "nameserver.hh" | |
34 | #include "distributor.hh" | |
35 | #include "lock.hh" | |
36 | #include "logger.hh" | |
37 | #include "arguments.hh" | |
38 | #include "packetcache.hh" | |
39 | #include "packethandler.hh" | |
40 | #include "statbag.hh" | |
41 | #include "resolver.hh" | |
42 | #include "communicator.hh" | |
43 | ||
44 | extern PacketCache PC; | |
45 | extern StatBag S; | |
46 | ||
47 | /** | |
48 | \file tcpreceiver.cc | |
49 | \brief This file implements the tcpreceiver that receives and answers questions over TCP/IP | |
50 | */ | |
51 | ||
ac2bb9e7 | 52 | pthread_mutex_t TCPNameserver::s_plock = PTHREAD_MUTEX_INITIALIZER; |
12c86877 BH |
53 | Semaphore *TCPNameserver::d_connectionroom_sem; |
54 | PacketHandler *TCPNameserver::s_P; | |
55 | int TCPNameserver::s_timeout; | |
9f1d5826 | 56 | NetmaskGroup TCPNameserver::d_ng; |
12c86877 | 57 | |
12c86877 BH |
58 | int TCPNameserver::sendDelPacket(DNSPacket *p, int outsock) |
59 | { | |
60 | const char *buf=p->getData(); | |
61 | int res=sendData(buf, p->len, outsock); | |
62 | delete p; | |
63 | return res; | |
64 | } | |
65 | ||
12c86877 BH |
66 | void TCPNameserver::go() |
67 | { | |
68 | L<<Logger::Error<<"Creating backend connection for TCP"<<endl; | |
69 | s_P=0; | |
70 | try { | |
71 | s_P=new PacketHandler; | |
72 | } | |
73 | catch(AhuException &ae) { | |
74 | L<<Logger::Error<<Logger::NTLog<<"TCP server is unable to launch backends - will try again when questions come in"<<endl; | |
fd8bc993 | 75 | L<<Logger::Error<<"TCP server is unable to launch backends - will try again when questions come in: "<<ae.reason<<endl; |
12c86877 BH |
76 | } |
77 | pthread_create(&d_tid, 0, launcher, static_cast<void *>(this)); | |
78 | } | |
79 | ||
80 | void *TCPNameserver::launcher(void *data) | |
81 | { | |
82 | static_cast<TCPNameserver *>(data)->thread(); | |
83 | return 0; | |
84 | } | |
85 | ||
86 | ||
87 | int TCPNameserver::readLength(int fd, struct sockaddr_in *remote) | |
88 | { | |
89 | int bytesLeft=2; | |
90 | unsigned char buf[2]; | |
91 | ||
92 | Utility::socklen_t remotelen=sizeof(*remote); | |
93 | getpeername(fd, (struct sockaddr *)remote, &remotelen); | |
94 | ||
95 | while(bytesLeft) { | |
96 | int ret=waitForData(fd, s_timeout); | |
97 | if(ret<0) | |
98 | throw AhuException("Waiting on data from remote TCP client "+string(inet_ntoa(remote->sin_addr))+": "+stringerror()); | |
99 | ||
100 | ret=recv(fd, reinterpret_cast< char * >( buf ) +2-bytesLeft, bytesLeft,0); | |
101 | if(ret<0) | |
102 | throw AhuException("Trying to read data from remote TCP client "+string(inet_ntoa(remote->sin_addr))+": "+stringerror()); | |
103 | if(!ret) { | |
104 | DLOG(L<<"Remote TCP client "+string(inet_ntoa(remote->sin_addr))+" closed connection"); | |
105 | return -1; | |
106 | } | |
107 | bytesLeft-=ret; | |
108 | } | |
109 | return buf[0]*256+buf[1]; | |
110 | } | |
111 | ||
112 | void TCPNameserver::getQuestion(int fd, char *mesg, int pktlen, const struct sockaddr_in &remote) | |
113 | { | |
114 | int ret=0, bytesread=0; | |
115 | while(bytesread<pktlen) { | |
116 | if((ret=waitForData(fd,s_timeout))<0 || (ret=recv(fd,mesg+bytesread,pktlen-bytesread,0))<=0) | |
117 | goto err; | |
118 | ||
119 | bytesread+=ret; | |
120 | } | |
121 | return; | |
122 | ||
123 | err:; | |
124 | if(ret<0) | |
125 | throw AhuException("Error reading DNS data from TCP client "+string(inet_ntoa(remote.sin_addr))+": "+stringerror()); | |
126 | else | |
127 | throw AhuException("Remote TCP client "+string(inet_ntoa(remote.sin_addr))+" closed connection"); | |
128 | } | |
129 | ||
130 | void *TCPNameserver::doConnection(void *data) | |
131 | { | |
b014ad98 BH |
132 | // Fix gcc-4.0 error (on AMD64) |
133 | int fd=(int)(long)data; // gotta love C (generates a harmless warning on opteron) | |
12c86877 BH |
134 | pthread_detach(pthread_self()); |
135 | ||
136 | try { | |
556252ea BH |
137 | { |
138 | Lock l(&s_plock); | |
139 | if(!s_P) { | |
140 | L<<Logger::Error<<"TCP server is without backend connections, launching"<<endl; | |
141 | s_P=new PacketHandler; | |
142 | } | |
12c86877 BH |
143 | } |
144 | char mesg[512]; | |
145 | ||
146 | DLOG(L<<"TCP Connection accepted on fd "<<fd<<endl); | |
147 | ||
148 | for(;;) { | |
149 | struct sockaddr_in remote; | |
150 | ||
151 | int pktlen=readLength(fd, &remote); | |
152 | if(pktlen<0) // EOF | |
153 | break; | |
154 | ||
155 | if(pktlen>511) { | |
156 | L<<Logger::Error<<"Received an overly large question from "<<inet_ntoa(remote.sin_addr)<<", dropping"<<endl; | |
157 | break; | |
158 | } | |
159 | ||
160 | getQuestion(fd,mesg,pktlen,remote); | |
161 | S.inc("tcp-queries"); | |
162 | DNSPacket *packet=new DNSPacket; | |
163 | ||
12c86877 | 164 | packet->setRemote((struct sockaddr *)&remote,sizeof(remote)); |
e9dd48f9 | 165 | packet->d_tcp=true; |
c1663439 BH |
166 | if(packet->parse(mesg, pktlen)<0) |
167 | break; | |
168 | ||
12c86877 BH |
169 | if(packet->qtype.getCode()==QType::AXFR) { |
170 | if(doAXFR(packet->qdomain, packet, fd)) | |
171 | S.inc("tcp-answers"); | |
172 | continue; | |
173 | } | |
174 | ||
dc45a198 | 175 | |
12c86877 BH |
176 | if(packet->d.rd && arg().mustDo("recursor")) { |
177 | // now what | |
178 | // this is a pretty rare event all in all, so we can afford to be slow | |
8d022964 BH |
179 | |
180 | // this code SHOULD attempt to answer from the local cache first! | |
12c86877 BH |
181 | S.inc("recursing-questions"); |
182 | Resolver res; | |
183 | unsigned int len; | |
184 | DLOG(L<<"About to hand query to recursor"<<endl); | |
185 | ServiceTuple st; | |
186 | st.port=53; | |
187 | parseService(arg()["recursor"],st); | |
188 | ||
189 | char *buffer=res.sendReceive(st.host,st.port,packet->getRaw(),packet->len,&len); | |
190 | DLOG(L<<"got an answer from recursor: "<<len<<" bytes, "<<(int)buffer<<endl); | |
191 | if(buffer) { | |
192 | sendData(buffer,len,fd); | |
193 | DLOG(L<<"sent out to customer: "<<len<<" bytes"<<endl); | |
194 | delete buffer; | |
195 | S.inc("recursing-answers"); | |
196 | S.inc("tcp-answers"); | |
197 | } | |
198 | continue; | |
199 | } | |
200 | ||
201 | DNSPacket* cached=new DNSPacket; | |
202 | if(!packet->d.rd && (PC.get(packet, cached))) { // short circuit - does the PacketCache recognize this question? | |
203 | cached->setRemote((struct sockaddr *)(packet->remote), sizeof(struct sockaddr_in)); | |
204 | cached->spoofID(packet->d.id); | |
205 | if(sendDelPacket(cached, fd)<0) | |
206 | goto out; | |
207 | ||
208 | S.inc("tcp-answers"); | |
209 | continue; | |
210 | } | |
211 | else | |
212 | delete cached; | |
213 | ||
214 | DNSPacket *reply; | |
215 | { | |
216 | Lock l(&s_plock); | |
217 | reply=s_P->question(packet); // we really need to ask the backend :-) | |
218 | } | |
219 | ||
220 | delete packet; | |
221 | ||
222 | if(!reply) // unable to write an answer? | |
223 | break; | |
224 | ||
225 | S.inc("tcp-answers"); | |
226 | sendDelPacket(reply, fd); | |
227 | } | |
228 | ||
229 | out:; | |
230 | } | |
cc3afe25 | 231 | catch(DBException &e) { |
556252ea BH |
232 | Lock l(&s_plock); |
233 | delete s_P; | |
234 | s_P = 0; | |
235 | ||
236 | L<<Logger::Error<<"TCP Connection Thread unable to answer a question because of a backend error, cycling"<<endl; | |
12c86877 | 237 | } |
ef1d2f44 | 238 | catch(AhuException &ae) { |
556252ea BH |
239 | Lock l(&s_plock); |
240 | delete s_P; | |
241 | s_P = 0; // on next call, backend will be recycled | |
242 | L<<Logger::Error<<"TCP nameserver had error, cycling backend:"<<ae.reason<<endl; | |
ef1d2f44 | 243 | } |
12c86877 BH |
244 | catch(exception &e) { |
245 | L<<Logger::Error<<"TCP Connection Thread died because of STL error: "<<e.what()<<endl; | |
246 | } | |
247 | catch( ... ) | |
248 | { | |
249 | L << Logger::Error << "TCP Connection Thread caught unknown exception." << endl; | |
250 | } | |
251 | Utility::closesocket(fd); | |
252 | d_connectionroom_sem->post(); | |
253 | ||
254 | return 0; | |
255 | } | |
256 | ||
9f1d5826 | 257 | bool TCPNameserver::canDoAXFR(DNSPacket *q) |
12c86877 | 258 | { |
ae1b2e98 | 259 | if(arg().mustDo("disable-axfr")) |
318c3ec6 BH |
260 | return false; |
261 | ||
9f1d5826 | 262 | if( arg()["allow-axfr-ips"].empty() || d_ng.match( (struct sockaddr_in *) &q->remote ) ) |
12c86877 BH |
263 | return true; |
264 | ||
12c86877 BH |
265 | extern CommunicatorClass Communicator; |
266 | ||
267 | if(Communicator.justNotified(q->qdomain, q->getRemote())) { // we just notified this ip | |
268 | L<<Logger::Warning<<"Approved AXFR of '"<<q->qdomain<<"' from recently notified slave "<<q->getRemote()<<endl; | |
269 | return true; | |
270 | } | |
271 | ||
272 | return false; | |
273 | } | |
274 | ||
275 | /** do the actual zone transfer. Return 0 in case of error, 1 in case of success */ | |
276 | int TCPNameserver::doAXFR(const string &target, DNSPacket *q, int outsock) | |
277 | { | |
278 | DNSPacket *outpacket=0; | |
279 | if(!canDoAXFR(q)) { | |
20ca8e7d | 280 | L<<Logger::Error<<"AXFR of domain '"<<target<<"' denied to "<<q->getRemote()<<endl; |
12c86877 BH |
281 | |
282 | outpacket=q->replyPacket(); | |
283 | outpacket->setRcode(RCode::Refused); | |
284 | // FIXME: should actually figure out if we are auth over a zone, and send out 9 if we aren't | |
285 | sendDelPacket(outpacket,outsock); | |
286 | return 0; | |
287 | } | |
20ca8e7d | 288 | L<<Logger::Error<<"AXFR of domain '"<<target<<"' initiated by "<<q->getRemote()<<endl; |
12c86877 BH |
289 | outpacket=q->replyPacket(); |
290 | ||
291 | DNSResourceRecord soa; | |
292 | DNSResourceRecord rr; | |
293 | ||
294 | SOAData sd; | |
3de83124 | 295 | sd.db=(DNSBackend *)-1; // force uncached answer |
12c86877 BH |
296 | { |
297 | Lock l(&s_plock); | |
298 | ||
299 | // find domain_id via SOA and list complete domain. No SOA, no AXFR | |
300 | ||
301 | DLOG(L<<"Looking for SOA"<<endl); | |
302 | ||
303 | if(!s_P->getBackend()->getSOA(target,sd)) { | |
d10f9034 | 304 | L<<Logger::Error<<"AXFR of domain '"<<target<<"' failed: not authoritative"<<endl; |
12c86877 BH |
305 | outpacket->setRcode(9); // 'NOTAUTH' |
306 | sendDelPacket(outpacket,outsock); | |
307 | return 0; | |
308 | } | |
7b308b7e | 309 | |
3de83124 BH |
310 | } |
311 | PacketHandler P; // now open up a database connection, we'll need it | |
7b308b7e | 312 | |
3de83124 BH |
313 | sd.db=(DNSBackend *)-1; // force uncached answer |
314 | if(!P.getBackend()->getSOA(target,sd)) { | |
d10f9034 | 315 | L<<Logger::Error<<"AXFR of domain '"<<target<<"' failed: not authoritative in second instance"<<endl; |
3de83124 BH |
316 | outpacket->setRcode(9); // 'NOTAUTH' |
317 | sendDelPacket(outpacket,outsock); | |
318 | return 0; | |
319 | } | |
7b308b7e | 320 | |
3de83124 BH |
321 | soa.qname=target; |
322 | soa.qtype=QType::SOA; | |
323 | soa.content=DNSPacket::serializeSOAData(sd); | |
324 | soa.ttl=sd.default_ttl; | |
325 | soa.domain_id=sd.domain_id; | |
326 | soa.d_place=DNSResourceRecord::ANSWER; | |
25b1d5d7 | 327 | |
3de83124 BH |
328 | if(!sd.db || sd.db==(DNSBackend *)-1) { |
329 | L<<Logger::Error<<"Error determining backend for domain '"<<target<<"' trying to serve an AXFR"<<endl; | |
330 | outpacket->setRcode(RCode::ServFail); | |
331 | sendDelPacket(outpacket,outsock); | |
332 | return 0; | |
12c86877 BH |
333 | } |
334 | ||
335 | DLOG(L<<"Issuing list command - opening dedicated database connection"<<endl); | |
3de83124 | 336 | |
36d772ab | 337 | DNSBackend *B=sd.db; // get the RIGHT backend |
12c86877 BH |
338 | |
339 | // now list zone | |
88def049 | 340 | if(!(B->list(target, sd.domain_id))) { |
12c86877 BH |
341 | L<<Logger::Error<<"Backend signals error condition"<<endl; |
342 | outpacket->setRcode(2); // 'SERVFAIL' | |
343 | sendDelPacket(outpacket,outsock); | |
344 | return 0; | |
345 | } | |
12c86877 BH |
346 | /* write first part of answer */ |
347 | ||
348 | DLOG(L<<"Sending out SOA"<<endl); | |
349 | outpacket->addRecord(soa); // AXFR format begins and ends with a SOA record, so we add one | |
350 | sendDelPacket(outpacket, outsock); | |
351 | ||
352 | /* now write all other records */ | |
353 | ||
354 | int count=0; | |
355 | int chunk=100; // FIXME: this should probably be autosizing | |
356 | if(arg().mustDo("strict-rfc-axfrs")) | |
357 | chunk=1; | |
358 | ||
359 | outpacket=q->replyPacket(); | |
360 | outpacket->setCompress(false); | |
361 | ||
362 | while(B->get(rr)) { | |
363 | if(rr.qtype.getCode()==6) | |
364 | continue; // skip SOA - would indicate end of AXFR | |
365 | ||
366 | outpacket->addRecord(rr); | |
367 | ||
368 | if(!((++count)%chunk)) { | |
369 | count=0; | |
370 | ||
371 | if(sendDelPacket(outpacket, outsock)<0) // FIXME: this leaks memory! | |
372 | return 0; | |
373 | ||
374 | outpacket=q->replyPacket(); | |
375 | outpacket->setCompress(false); | |
376 | // FIXME: Subsequent messages SHOULD NOT have a question section, though the final message MAY. | |
377 | } | |
378 | } | |
379 | if(count) { | |
380 | sendDelPacket(outpacket, outsock); | |
381 | } | |
382 | ||
383 | DLOG(L<<"Done writing out records"<<endl); | |
384 | /* and terminate with yet again the SOA record */ | |
385 | outpacket=q->replyPacket(); | |
386 | outpacket->addRecord(soa); | |
387 | sendDelPacket(outpacket, outsock); | |
388 | DLOG(L<<"last packet - close"<<endl); | |
20ca8e7d | 389 | L<<Logger::Error<<"AXFR of domain '"<<target<<"' to "<<q->getRemote()<<" finished"<<endl; |
12c86877 BH |
390 | |
391 | return 1; | |
392 | } | |
393 | ||
394 | TCPNameserver::~TCPNameserver() | |
395 | { | |
396 | delete d_connectionroom_sem; | |
397 | } | |
398 | ||
399 | TCPNameserver::TCPNameserver() | |
400 | { | |
401 | // sem_init(&d_connectionroom_sem,0,arg().asNum("max-tcp-connections")); | |
402 | d_connectionroom_sem = new Semaphore( arg().asNum( "max-tcp-connections" )); | |
403 | ||
404 | s_timeout=10; | |
405 | vector<string>locals; | |
406 | stringtok(locals,arg()["local-address"]," ,"); | |
407 | ||
408 | vector<string>locals6; | |
409 | stringtok(locals6,arg()["local-ipv6"]," ,"); | |
410 | ||
411 | ||
412 | if(locals.empty() && locals6.empty()) | |
413 | throw AhuException("No local address specified"); | |
414 | ||
415 | d_highfd=0; | |
416 | ||
9f1d5826 BH |
417 | vector<string> parts; |
418 | stringtok( parts, arg()["allow-axfr-ips"], ", \t" ); // is this IP on the guestlist? | |
419 | for( vector<string>::const_iterator i = parts.begin(); i != parts.end(); ++i ) { | |
420 | d_ng.addMask( *i ); | |
421 | } | |
422 | ||
12c86877 BH |
423 | #ifndef WIN32 |
424 | signal(SIGPIPE,SIG_IGN); | |
425 | #endif // WIN32 | |
426 | FD_ZERO(&d_rfds); | |
427 | ||
428 | for(vector<string>::const_iterator laddr=locals.begin();laddr!=locals.end();++laddr) { | |
429 | struct sockaddr_in local; | |
430 | int s=socket(AF_INET,SOCK_STREAM,0); | |
431 | ||
432 | if(s<0) | |
433 | throw AhuException("Unable to acquire TCP socket: "+stringerror()); | |
434 | ||
435 | memset(&local,0,sizeof(local)); | |
436 | local.sin_family=AF_INET; | |
437 | ||
438 | struct hostent *h; | |
439 | ||
440 | if ( *laddr == "0.0.0.0" ) | |
441 | { | |
442 | local.sin_addr.s_addr = INADDR_ANY; | |
443 | } | |
444 | else | |
445 | { | |
446 | h=gethostbyname(laddr->c_str()); | |
447 | ||
448 | if(!h) | |
449 | throw AhuException("Unable to resolve local address '"+*laddr+"'"); | |
450 | ||
451 | local.sin_addr.s_addr=*(int*)h->h_addr; | |
452 | } | |
453 | ||
454 | int tmp=1; | |
455 | if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0) { | |
456 | L<<Logger::Error<<"Setsockopt failed"<<endl; | |
457 | exit(1); | |
458 | } | |
459 | ||
460 | local.sin_port=htons(arg().asNum("local-port")); | |
461 | ||
462 | if(bind(s, (sockaddr*)&local,sizeof(local))<0) { | |
463 | L<<Logger::Error<<"binding to TCP socket: "<<strerror(errno)<<endl; | |
464 | throw AhuException("Unable to bind to TCP socket"); | |
465 | } | |
466 | ||
467 | listen(s,128); | |
da44c9ea | 468 | L<<Logger::Error<<"TCP server bound to "<<*laddr<<":"<<arg().asNum("local-port")<<endl; |
12c86877 BH |
469 | d_sockets.push_back(s); |
470 | FD_SET(s, &d_rfds); | |
471 | d_highfd=max(s,d_highfd); | |
472 | } | |
473 | ||
474 | // TODO: Implement ipv6 | |
1258abe0 | 475 | #if !WIN32 && HAVE_IPV6 |
12c86877 | 476 | for(vector<string>::const_iterator laddr=locals6.begin();laddr!=locals6.end();++laddr) { |
12c86877 BH |
477 | int s=socket(AF_INET6,SOCK_STREAM,0); |
478 | ||
479 | if(s<0) | |
480 | throw AhuException("Unable to acquire TCPv6 socket: "+stringerror()); | |
178d5134 BH |
481 | |
482 | sockaddr_in6 locala; | |
483 | locala.sin6_port=ntohs(arg().asNum("local-port")); | |
bdc9f8d2 BH |
484 | locala.sin6_family=AF_INET6; |
485 | locala.sin6_flowinfo=0; | |
486 | ||
178d5134 BH |
487 | |
488 | if(!inet_pton(AF_INET6, laddr->c_str(), (void *)&locala.sin6_addr)) { | |
489 | addrinfo *addrinfos; | |
490 | addrinfo hints; | |
491 | memset(&hints,0,sizeof(hints)); | |
492 | hints.ai_socktype=SOCK_STREAM; | |
493 | hints.ai_family=AF_INET6; | |
12c86877 | 494 | |
178d5134 BH |
495 | if(getaddrinfo(laddr->c_str(),arg()["local-port"].c_str(),&hints,&addrinfos)) |
496 | throw AhuException("Unable to resolve local IPv6 address '"+*laddr+"'"); | |
497 | ||
498 | memcpy(&locala,addrinfos->ai_addr,addrinfos->ai_addrlen); | |
499 | } | |
12c86877 BH |
500 | |
501 | int tmp=1; | |
502 | if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0) { | |
503 | L<<Logger::Error<<"Setsockopt failed"<<endl; | |
504 | exit(1); | |
505 | } | |
506 | ||
bdc9f8d2 | 507 | |
178d5134 | 508 | if(bind(s, (const sockaddr*)&locala, sizeof(locala))<0) { |
12c86877 BH |
509 | L<<Logger::Error<<"binding to TCP socket: "<<strerror(errno)<<endl; |
510 | throw AhuException("Unable to bind to TCPv6 socket"); | |
511 | } | |
512 | ||
513 | listen(s,128); | |
bd852e59 | 514 | L<<Logger::Error<<"TCPv6 server bound to ["<<*laddr<<"]:"<<arg()["local-port"]<<endl; |
12c86877 BH |
515 | d_sockets.push_back(s); |
516 | FD_SET(s, &d_rfds); | |
517 | d_highfd=max(s,d_highfd); | |
518 | } | |
519 | #endif // WIN32 | |
520 | } | |
521 | ||
522 | ||
523 | //! Start of TCP operations thread | |
524 | void TCPNameserver::thread() | |
525 | { | |
526 | struct timeval tv; | |
527 | tv.tv_sec=1; | |
528 | tv.tv_usec=0; | |
529 | try { | |
530 | for(;;) { | |
531 | int fd; | |
532 | struct sockaddr_in remote; | |
533 | Utility::socklen_t addrlen=sizeof(remote); | |
534 | ||
535 | fd_set rfds=d_rfds; | |
536 | ||
8a63d3ce BH |
537 | int ret=select(d_highfd+1, &rfds, 0, 0, 0); // blocks |
538 | if(ret <= 0) | |
539 | continue; | |
540 | ||
12c86877 BH |
541 | int sock=-1; |
542 | for(vector<int>::const_iterator i=d_sockets.begin();i!=d_sockets.end();++i) { | |
543 | if(FD_ISSET(*i, &rfds)) { | |
544 | sock=*i; | |
545 | addrlen=sizeof(remote); | |
546 | ||
547 | if((fd=accept(sock, (sockaddr*)&remote, &addrlen))<0) { | |
548 | L<<Logger::Error<<"TCP question accept error: "<<strerror(errno)<<endl; | |
549 | ||
550 | if(errno==EMFILE) { | |
551 | L<<Logger::Error<<Logger::NTLog<<"TCP handler out of filedescriptors, exiting, won't recover from this"<<endl; | |
552 | exit(1); | |
553 | } | |
554 | } | |
555 | else { | |
556 | pthread_t tid; | |
557 | d_connectionroom_sem->wait(); // blocks if no connections are available | |
558 | ||
559 | int room; | |
560 | d_connectionroom_sem->getValue( &room); | |
561 | if(room<1) | |
562 | L<<Logger::Warning<<Logger::NTLog<<"Limit of simultaneous TCP connections reached - raise max-tcp-connections"<<endl; | |
563 | ||
564 | if(pthread_create(&tid, 0, &doConnection, (void *)fd)) { | |
565 | L<<Logger::Error<<"Error creating thread: "<<stringerror()<<endl; | |
566 | d_connectionroom_sem->post(); | |
567 | } | |
568 | } | |
569 | } | |
570 | } | |
571 | } | |
572 | } | |
573 | catch(AhuException &AE) { | |
574 | L<<Logger::Error<<"TCP Namerserver thread dying because of fatal error: "<<AE.reason<<endl; | |
575 | } | |
576 | catch(...) { | |
577 | L<<Logger::Error<<"TCPNameserver dying because of an unexpected fatal error"<<endl; | |
578 | } | |
579 | exit(1); // take rest of server with us | |
580 | } | |
581 | ||
582 |