]>
Commit | Line | Data |
---|---|---|
12c86877 BH |
1 | /* |
2 | PowerDNS Versatile Database Driven Nameserver | |
2e7834cb | 3 | Copyright (C) 2002-2012 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. | |
12 | ||
12c86877 BH |
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 | */ |
379ab445 | 22 | #include "packetcache.hh" |
1258abe0 | 23 | #include "utility.hh" |
add640c0 | 24 | #include "dnssecinfra.hh" |
4c1474f3 | 25 | #include "dnsseckeeper.hh" |
12c86877 | 26 | #include <cstdio> |
4888e4b2 | 27 | #include "base32.hh" |
12c86877 BH |
28 | #include <cstring> |
29 | #include <cstdlib> | |
30 | #include <sys/types.h> | |
31 | #include <iostream> | |
32 | #include <string> | |
33 | #include "tcpreceiver.hh" | |
67d74e49 | 34 | #include "sstuff.hh" |
8edfedf1 | 35 | #include <boost/foreach.hpp> |
12c86877 BH |
36 | #include <errno.h> |
37 | #include <signal.h> | |
78bcb858 | 38 | #include "base64.hh" |
12c86877 BH |
39 | #include "ueberbackend.hh" |
40 | #include "dnspacket.hh" | |
41 | #include "nameserver.hh" | |
42 | #include "distributor.hh" | |
43 | #include "lock.hh" | |
44 | #include "logger.hh" | |
45 | #include "arguments.hh" | |
379ab445 | 46 | |
12c86877 BH |
47 | #include "packethandler.hh" |
48 | #include "statbag.hh" | |
49 | #include "resolver.hh" | |
50 | #include "communicator.hh" | |
61b26744 | 51 | #include "namespaces.hh" |
8e9b7d99 | 52 | #include "signingpipe.hh" |
12c86877 BH |
53 | extern PacketCache PC; |
54 | extern StatBag S; | |
55 | ||
56 | /** | |
57 | \file tcpreceiver.cc | |
58 | \brief This file implements the tcpreceiver that receives and answers questions over TCP/IP | |
59 | */ | |
60 | ||
ac2bb9e7 | 61 | pthread_mutex_t TCPNameserver::s_plock = PTHREAD_MUTEX_INITIALIZER; |
12c86877 BH |
62 | Semaphore *TCPNameserver::d_connectionroom_sem; |
63 | PacketHandler *TCPNameserver::s_P; | |
64 | int TCPNameserver::s_timeout; | |
9f1d5826 | 65 | NetmaskGroup TCPNameserver::d_ng; |
12c86877 | 66 | |
12c86877 BH |
67 | void TCPNameserver::go() |
68 | { | |
69 | L<<Logger::Error<<"Creating backend connection for TCP"<<endl; | |
70 | s_P=0; | |
71 | try { | |
72 | s_P=new PacketHandler; | |
73 | } | |
3f81d239 | 74 | catch(PDNSException &ae) { |
12c86877 | 75 | L<<Logger::Error<<Logger::NTLog<<"TCP server is unable to launch backends - will try again when questions come in"<<endl; |
fd8bc993 | 76 | L<<Logger::Error<<"TCP server is unable to launch backends - will try again when questions come in: "<<ae.reason<<endl; |
12c86877 BH |
77 | } |
78 | pthread_create(&d_tid, 0, launcher, static_cast<void *>(this)); | |
79 | } | |
80 | ||
81 | void *TCPNameserver::launcher(void *data) | |
82 | { | |
83 | static_cast<TCPNameserver *>(data)->thread(); | |
84 | return 0; | |
85 | } | |
86 | ||
3f81d239 | 87 | // throws PDNSException if things didn't go according to plan, returns 0 if really 0 bytes were read |
6a3e5d1a | 88 | int readnWithTimeout(int fd, void* buffer, unsigned int n, bool throwOnEOF=true) |
12c86877 | 89 | { |
6a3e5d1a BH |
90 | unsigned int bytes=n; |
91 | char *ptr = (char*)buffer; | |
92 | int ret; | |
93 | while(bytes) { | |
94 | ret=read(fd, ptr, bytes); | |
95 | if(ret < 0) { | |
96 | if(errno==EAGAIN) { | |
4957a608 BH |
97 | ret=waitForData(fd, 5); |
98 | if(ret < 0) | |
99 | throw NetworkError("Waiting for data read"); | |
100 | if(!ret) | |
101 | throw NetworkError("Timeout reading data"); | |
102 | continue; | |
6a3e5d1a BH |
103 | } |
104 | else | |
4957a608 | 105 | throw NetworkError("Reading data: "+stringerror()); |
6a3e5d1a BH |
106 | } |
107 | if(!ret) { | |
108 | if(!throwOnEOF && n == bytes) | |
4957a608 | 109 | return 0; |
6a3e5d1a | 110 | else |
4957a608 | 111 | throw NetworkError("Did not fulfill read from TCP due to EOF"); |
6a3e5d1a BH |
112 | } |
113 | ||
114 | ptr += ret; | |
115 | bytes -= ret; | |
116 | } | |
117 | return n; | |
118 | } | |
12c86877 | 119 | |
6a3e5d1a BH |
120 | // ditto |
121 | void writenWithTimeout(int fd, const void *buffer, unsigned int n) | |
122 | { | |
123 | unsigned int bytes=n; | |
124 | const char *ptr = (char*)buffer; | |
125 | int ret; | |
126 | while(bytes) { | |
127 | ret=write(fd, ptr, bytes); | |
128 | if(ret < 0) { | |
129 | if(errno==EAGAIN) { | |
4957a608 BH |
130 | ret=waitForRWData(fd, false, 5, 0); |
131 | if(ret < 0) | |
132 | throw NetworkError("Waiting for data write"); | |
133 | if(!ret) | |
134 | throw NetworkError("Timeout writing data"); | |
135 | continue; | |
6a3e5d1a BH |
136 | } |
137 | else | |
4957a608 | 138 | throw NetworkError("Writing data: "+stringerror()); |
6a3e5d1a | 139 | } |
12c86877 | 140 | if(!ret) { |
67d74e49 | 141 | throw NetworkError("Did not fulfill TCP write due to EOF"); |
12c86877 | 142 | } |
6a3e5d1a BH |
143 | |
144 | ptr += ret; | |
145 | bytes -= ret; | |
12c86877 | 146 | } |
12c86877 BH |
147 | } |
148 | ||
6a3e5d1a | 149 | void connectWithTimeout(int fd, struct sockaddr* remote, size_t socklen) |
12c86877 | 150 | { |
6a3e5d1a BH |
151 | int err; |
152 | Utility::socklen_t len=sizeof(err); | |
153 | ||
76473b92 | 154 | if((err=connect(fd, remote, socklen))<0 && errno!=EINPROGRESS) |
67d74e49 | 155 | throw NetworkError("connect: "+stringerror()); |
6a3e5d1a BH |
156 | |
157 | if(!err) | |
158 | goto done; | |
159 | ||
160 | err=waitForRWData(fd, false, 5, 0); | |
161 | if(err == 0) | |
67d74e49 | 162 | throw NetworkError("Timeout connecting to remote"); |
6a3e5d1a | 163 | if(err < 0) |
67d74e49 | 164 | throw NetworkError("Error connecting to remote"); |
12c86877 | 165 | |
6a3e5d1a | 166 | if(getsockopt(fd, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0) |
67d74e49 | 167 | throw NetworkError("Error connecting to remote: "+stringerror()); // Solaris |
6a3e5d1a BH |
168 | |
169 | if(err) | |
67d74e49 | 170 | throw NetworkError("Error connecting to remote: "+string(strerror(err))); |
6a3e5d1a BH |
171 | |
172 | done: | |
173 | ; | |
174 | } | |
12c86877 | 175 | |
6a3e5d1a BH |
176 | void TCPNameserver::sendPacket(shared_ptr<DNSPacket> p, int outsock) |
177 | { | |
9951e2d0 KM |
178 | |
179 | /* Query statistics */ | |
180 | if(p->qtype.getCode()!=QType::AXFR && p->qtype.getCode()!=QType::IXFR) { | |
181 | if(p->d.aa) { | |
182 | if(p->d.rcode==RCode::NXDomain) | |
183 | S.ringAccount("nxdomain-queries",p->qdomain+"/"+p->qtype.getName()); | |
184 | } else if(p->isEmpty()) { | |
4dbda652 | 185 | S.ringAccount("unauth-queries",p->qdomain+"/"+p->qtype.getName()); |
9951e2d0 KM |
186 | S.ringAccount("remotes-unauth",p->getRemote()); |
187 | } | |
188 | } | |
189 | ||
fbaa5e09 BH |
190 | uint16_t len=htons(p->getString().length()); |
191 | string buffer((const char*)&len, 2); | |
192 | buffer.append(p->getString()); | |
78bcb858 | 193 | writenWithTimeout(outsock, buffer.c_str(), buffer.length()); |
6a3e5d1a BH |
194 | } |
195 | ||
196 | ||
197 | void TCPNameserver::getQuestion(int fd, char *mesg, int pktlen, const ComboAddress &remote) | |
198 | try | |
199 | { | |
200 | readnWithTimeout(fd, mesg, pktlen); | |
201 | } | |
67d74e49 BH |
202 | catch(NetworkError& ae) { |
203 | throw NetworkError("Error reading DNS data from TCP client "+remote.toString()+": "+ae.what()); | |
12c86877 BH |
204 | } |
205 | ||
ff76e8b4 BH |
206 | static void proxyQuestion(shared_ptr<DNSPacket> packet) |
207 | { | |
208 | int sock=socket(AF_INET, SOCK_STREAM, 0); | |
326484be | 209 | |
42c235e5 | 210 | Utility::setCloseOnExec(sock); |
ff76e8b4 | 211 | if(sock < 0) |
67d74e49 | 212 | throw NetworkError("Error making TCP connection socket to recursor: "+stringerror()); |
ff76e8b4 | 213 | |
6a3e5d1a BH |
214 | Utility::setNonBlocking(sock); |
215 | ServiceTuple st; | |
216 | st.port=53; | |
379ab445 | 217 | parseService(::arg()["recursor"],st); |
6a3e5d1a | 218 | |
ff76e8b4 | 219 | try { |
ff76e8b4 | 220 | ComboAddress recursor(st.host, st.port); |
6a3e5d1a | 221 | connectWithTimeout(sock, (struct sockaddr*)&recursor, recursor.getSocklen()); |
ff76e8b4 BH |
222 | const string &buffer=packet->getString(); |
223 | ||
224 | uint16_t len=htons(buffer.length()), slen; | |
225 | ||
6a3e5d1a BH |
226 | writenWithTimeout(sock, &len, 2); |
227 | writenWithTimeout(sock, buffer.c_str(), buffer.length()); | |
ff76e8b4 | 228 | |
c20eca2b | 229 | readnWithTimeout(sock, &len, 2); |
ff76e8b4 BH |
230 | len=ntohs(len); |
231 | ||
232 | char answer[len]; | |
c20eca2b | 233 | readnWithTimeout(sock, answer, len); |
ff76e8b4 BH |
234 | |
235 | slen=htons(len); | |
6a3e5d1a | 236 | writenWithTimeout(packet->getSocket(), &slen, 2); |
ff76e8b4 | 237 | |
6a3e5d1a | 238 | writenWithTimeout(packet->getSocket(), answer, len); |
ff76e8b4 | 239 | } |
67d74e49 | 240 | catch(NetworkError& ae) { |
ff76e8b4 | 241 | close(sock); |
67d74e49 | 242 | throw NetworkError("While proxying a question to recursor "+st.host+": " +ae.what()); |
ff76e8b4 BH |
243 | } |
244 | close(sock); | |
245 | return; | |
246 | } | |
247 | ||
12c86877 BH |
248 | void *TCPNameserver::doConnection(void *data) |
249 | { | |
ff76e8b4 | 250 | shared_ptr<DNSPacket> packet; |
b014ad98 BH |
251 | // Fix gcc-4.0 error (on AMD64) |
252 | int fd=(int)(long)data; // gotta love C (generates a harmless warning on opteron) | |
12c86877 | 253 | pthread_detach(pthread_self()); |
027ffd26 | 254 | Utility::setNonBlocking(fd); |
12c86877 | 255 | try { |
3ac5be20 | 256 | char mesg[65535]; |
12c86877 BH |
257 | |
258 | DLOG(L<<"TCP Connection accepted on fd "<<fd<<endl); | |
21a303f3 | 259 | bool logDNSQueries= ::arg().mustDo("log-dns-queries"); |
12c86877 | 260 | for(;;) { |
809fe23f | 261 | ComboAddress remote; |
38a9b470 BH |
262 | socklen_t remotelen=sizeof(remote); |
263 | if(getpeername(fd, (struct sockaddr *)&remote, &remotelen) < 0) { | |
4957a608 BH |
264 | L<<Logger::Error<<"Received question from socket which had no remote address, dropping ("<<stringerror()<<")"<<endl; |
265 | break; | |
38a9b470 | 266 | } |
6a3e5d1a BH |
267 | |
268 | uint16_t pktlen; | |
269 | if(!readnWithTimeout(fd, &pktlen, 2, false)) | |
4957a608 | 270 | break; |
6a3e5d1a | 271 | else |
4957a608 | 272 | pktlen=ntohs(pktlen); |
12c86877 | 273 | |
366e1e5e AT |
274 | // this check will always be false *if* no one touches |
275 | // the mesg array. pktlen can be maximum of 65535 as | |
276 | // it is 2 byte unsigned variable. In getQuestion, we | |
277 | // write to 0 up to pktlen-1 so 65535 is just right. | |
278 | ||
279 | // do not remove this check as it will catch if someone | |
280 | // decreases the mesg buffer size for some reason. | |
3ac5be20 | 281 | if(pktlen>sizeof(mesg)) { |
4957a608 BH |
282 | L<<Logger::Error<<"Received an overly large question from "<<remote.toString()<<", dropping"<<endl; |
283 | break; | |
12c86877 BH |
284 | } |
285 | ||
78bcb858 | 286 | getQuestion(fd, mesg, pktlen, remote); |
12c86877 | 287 | S.inc("tcp-queries"); |
3e579e91 | 288 | |
ff76e8b4 | 289 | packet=shared_ptr<DNSPacket>(new DNSPacket); |
809fe23f | 290 | packet->setRemote(&remote); |
e9dd48f9 | 291 | packet->d_tcp=true; |
ff76e8b4 | 292 | packet->setSocket(fd); |
c1663439 | 293 | if(packet->parse(mesg, pktlen)<0) |
4957a608 | 294 | break; |
c1663439 | 295 | |
6e59a580 KM |
296 | if(packet->qtype.getCode()==QType::AXFR) { |
297 | if(doAXFR(packet->qdomain, packet, fd)) | |
298 | S.inc("tcp-answers"); | |
299 | continue; | |
300 | } | |
301 | ||
302 | if(packet->qtype.getCode()==QType::IXFR) { | |
303 | if(doIXFR(packet, fd)) | |
304 | S.inc("tcp-answers"); | |
4957a608 | 305 | continue; |
12c86877 BH |
306 | } |
307 | ||
ff76e8b4 | 308 | shared_ptr<DNSPacket> reply; |
ff76e8b4 | 309 | shared_ptr<DNSPacket> cached= shared_ptr<DNSPacket>(new DNSPacket); |
fe498ace BH |
310 | if(logDNSQueries) { |
311 | string remote; | |
312 | if(packet->hasEDNSSubnet()) | |
313 | remote = packet->getRemote() + "<-" + packet->getRealRemote().toString(); | |
314 | else | |
315 | remote = packet->getRemote(); | |
316 | L << Logger::Notice<<"TCP Remote "<< remote <<" wants '" << packet->qdomain<<"|"<<packet->qtype.getName() << | |
bb5903e2 | 317 | "', do = " <<packet->d_dnssecOk <<", bufsize = "<< packet->getMaxReplyLen()<<": "; |
fe498ace | 318 | } |
bb5903e2 | 319 | |
ff76e8b4 | 320 | |
75fde355 | 321 | if(!packet->d.rd && packet->couldBeCached() && PC.get(packet.get(), cached.get(), false)) { // short circuit - does the PacketCache recognize this question? |
21a303f3 | 322 | if(logDNSQueries) |
bb5903e2 | 323 | L<<"packetcache HIT"<<endl; |
d06799d4 | 324 | cached->setRemote(&packet->d_remote); |
4957a608 BH |
325 | cached->d.id=packet->d.id; |
326 | cached->d.rd=packet->d.rd; // copy in recursion desired bit | |
327 | cached->commitD(); // commit d to the packet inlined | |
328 | ||
e02d0a59 | 329 | sendPacket(cached, fd); // presigned, don't do it again |
4957a608 BH |
330 | S.inc("tcp-answers"); |
331 | continue; | |
12c86877 | 332 | } |
21a303f3 | 333 | if(logDNSQueries) |
bb5903e2 | 334 | L<<"packetcache MISS"<<endl; |
12c86877 | 335 | { |
4957a608 BH |
336 | Lock l(&s_plock); |
337 | if(!s_P) { | |
338 | L<<Logger::Error<<"TCP server is without backend connections, launching"<<endl; | |
339 | s_P=new PacketHandler; | |
340 | } | |
341 | bool shouldRecurse; | |
342 | ||
343 | reply=shared_ptr<DNSPacket>(s_P->questionOrRecurse(packet.get(), &shouldRecurse)); // we really need to ask the backend :-) | |
344 | ||
345 | if(shouldRecurse) { | |
346 | proxyQuestion(packet); | |
347 | continue; | |
348 | } | |
12c86877 BH |
349 | } |
350 | ||
12c86877 | 351 | if(!reply) // unable to write an answer? |
4957a608 BH |
352 | break; |
353 | ||
12c86877 | 354 | S.inc("tcp-answers"); |
ff76e8b4 | 355 | sendPacket(reply, fd); |
12c86877 | 356 | } |
12c86877 | 357 | } |
cc3afe25 | 358 | catch(DBException &e) { |
556252ea BH |
359 | Lock l(&s_plock); |
360 | delete s_P; | |
361 | s_P = 0; | |
362 | ||
363 | L<<Logger::Error<<"TCP Connection Thread unable to answer a question because of a backend error, cycling"<<endl; | |
12c86877 | 364 | } |
3f81d239 | 365 | catch(PDNSException &ae) { |
556252ea BH |
366 | Lock l(&s_plock); |
367 | delete s_P; | |
368 | s_P = 0; // on next call, backend will be recycled | |
027ffd26 | 369 | L<<Logger::Error<<"TCP nameserver had error, cycling backend: "<<ae.reason<<endl; |
ef1d2f44 | 370 | } |
0afa9049 | 371 | catch(NetworkError &e) { |
2e7834cb | 372 | L<<Logger::Info<<"TCP Connection Thread died because of network error: "<<e.what()<<endl; |
0afa9049 BH |
373 | } |
374 | ||
adc10f99 | 375 | catch(std::exception &e) { |
12c86877 BH |
376 | L<<Logger::Error<<"TCP Connection Thread died because of STL error: "<<e.what()<<endl; |
377 | } | |
378 | catch( ... ) | |
379 | { | |
380 | L << Logger::Error << "TCP Connection Thread caught unknown exception." << endl; | |
381 | } | |
12c86877 | 382 | d_connectionroom_sem->post(); |
c38f6509 | 383 | Utility::closesocket(fd); |
12c86877 BH |
384 | |
385 | return 0; | |
386 | } | |
387 | ||
78bcb858 | 388 | |
e082fb4c | 389 | // call this method with s_plock held! |
ff76e8b4 | 390 | bool TCPNameserver::canDoAXFR(shared_ptr<DNSPacket> q) |
12c86877 | 391 | { |
379ab445 | 392 | if(::arg().mustDo("disable-axfr")) |
318c3ec6 BH |
393 | return false; |
394 | ||
78bcb858 BH |
395 | if(q->d_havetsig) { // if you have one, it must be good |
396 | TSIGRecordContent trc; | |
397 | string keyname, secret; | |
78bcb858 BH |
398 | if(!checkForCorrectTSIG(q.get(), s_P->getBackend(), &keyname, &secret, &trc)) |
399 | return false; | |
400 | ||
401 | DNSSECKeeper dk; | |
402 | ||
403 | if(!dk.TSIGGrantsAccess(q->qdomain, keyname, trc.d_algoName)) { | |
404 | L<<Logger::Error<<"AXFR '"<<q->qdomain<<"' denied: key with name '"<<keyname<<"' and algorithm '"<<trc.d_algoName<<"' does not grant access to zone"<<endl; | |
405 | return false; | |
406 | } | |
407 | else { | |
408 | L<<Logger::Warning<<"AXFR of domain '"<<q->qdomain<<"' allowed: TSIG signed request with authorized key '"<<keyname<<"' and algorithm '"<<trc.d_algoName<<"'"<<endl; | |
409 | return true; | |
410 | } | |
411 | } | |
93afc0a3 PD |
412 | |
413 | // cerr<<"checking allow-axfr-ips"<<endl; | |
414 | if(!(::arg()["allow-axfr-ips"].empty()) && d_ng.match( (ComboAddress *) &q->d_remote )) { | |
415 | L<<Logger::Warning<<"AXFR of domain '"<<q->qdomain<<"' allowed: client IP "<<q->getRemote()<<" is in allow-axfr-ips"<<endl; | |
12c86877 | 416 | return true; |
ab5edd12 | 417 | } |
93afc0a3 PD |
418 | |
419 | FindNS fns; | |
420 | ||
421 | // cerr<<"doing per-zone-axfr-acls"<<endl; | |
422 | SOAData sd; | |
423 | sd.db=(DNSBackend *)-1; | |
424 | if(s_P->getBackend()->getSOA(q->qdomain,sd)) { | |
425 | // cerr<<"got backend and SOA"<<endl; | |
426 | DNSBackend *B=sd.db; | |
427 | vector<string> acl; | |
428 | B->getDomainMetadata(q->qdomain, "ALLOW-AXFR-FROM", acl); | |
429 | for (vector<string>::const_iterator i = acl.begin(); i != acl.end(); ++i) { | |
430 | // cerr<<"matching against "<<*i<<endl; | |
431 | if(pdns_iequals(*i, "AUTO-NS")) { | |
432 | // cerr<<"AUTO-NS magic please!"<<endl; | |
433 | ||
434 | DNSResourceRecord rr; | |
435 | set<string> nsset; | |
436 | ||
437 | B->lookup(QType(QType::NS),q->qdomain); | |
438 | while(B->get(rr)) | |
439 | nsset.insert(rr.content); | |
440 | for(set<string>::const_iterator j=nsset.begin();j!=nsset.end();++j) { | |
441 | vector<string> nsips=fns.lookup(*j, B); | |
442 | for(vector<string>::const_iterator k=nsips.begin();k!=nsips.end();++k) { | |
443 | // cerr<<"got "<<*k<<" from AUTO-NS"<<endl; | |
444 | if(*k == q->getRemote()) | |
445 | { | |
446 | // cerr<<"got AUTO-NS hit"<<endl; | |
447 | L<<Logger::Warning<<"AXFR of domain '"<<q->qdomain<<"' allowed: client IP "<<q->getRemote()<<" is in NSset"<<endl; | |
448 | return true; | |
449 | } | |
450 | } | |
451 | } | |
452 | } | |
453 | else | |
454 | { | |
455 | Netmask nm = Netmask(*i); | |
456 | if(nm.match( (ComboAddress *) &q->d_remote )) | |
457 | { | |
458 | L<<Logger::Warning<<"AXFR of domain '"<<q->qdomain<<"' allowed: client IP "<<q->getRemote()<<" is in per-domain ACL"<<endl; | |
459 | // cerr<<"hit!"<<endl; | |
460 | return true; | |
461 | } | |
462 | } | |
463 | } | |
464 | } | |
465 | ||
12c86877 BH |
466 | extern CommunicatorClass Communicator; |
467 | ||
468 | if(Communicator.justNotified(q->qdomain, q->getRemote())) { // we just notified this ip | |
469 | L<<Logger::Warning<<"Approved AXFR of '"<<q->qdomain<<"' from recently notified slave "<<q->getRemote()<<endl; | |
470 | return true; | |
471 | } | |
472 | ||
93afc0a3 | 473 | L<<Logger::Error<<"AXFR of domain '"<<q->qdomain<<"' denied: client IP "<<q->getRemote()<<" has no permission"<<endl; |
12c86877 BH |
474 | return false; |
475 | } | |
476 | ||
b317b510 | 477 | namespace { |
54d84273 PD |
478 | struct NSECXEntry |
479 | { | |
480 | set<uint16_t> d_set; | |
481 | unsigned int d_ttl; | |
feef1ece | 482 | bool d_auth; |
54d84273 | 483 | }; |
8e9b7d99 | 484 | |
54d84273 PD |
485 | DNSResourceRecord makeDNSRRFromSOAData(const SOAData& sd) |
486 | { | |
487 | DNSResourceRecord soa; | |
488 | soa.qname= sd.qname; | |
489 | soa.qtype=QType::SOA; | |
490 | soa.content=serializeSOAData(sd); | |
491 | soa.ttl=sd.ttl; | |
492 | soa.domain_id=sd.domain_id; | |
493 | soa.auth = true; | |
494 | soa.d_place=DNSResourceRecord::ANSWER; | |
495 | return soa; | |
496 | } | |
8e9b7d99 | 497 | |
54d84273 PD |
498 | shared_ptr<DNSPacket> getFreshAXFRPacket(shared_ptr<DNSPacket> q) |
499 | { | |
500 | shared_ptr<DNSPacket> ret = shared_ptr<DNSPacket>(q->replyPacket()); | |
501 | ret->setCompress(false); | |
502 | ret->d_dnssecOk=false; // RFC 5936, 2.2.5 | |
503 | ret->d_tcp = true; | |
504 | return ret; | |
505 | } | |
8e9b7d99 BH |
506 | } |
507 | ||
54d84273 | 508 | |
12c86877 | 509 | /** do the actual zone transfer. Return 0 in case of error, 1 in case of success */ |
ff76e8b4 | 510 | int TCPNameserver::doAXFR(const string &target, shared_ptr<DNSPacket> q, int outsock) |
12c86877 | 511 | { |
4888e4b2 BH |
512 | bool noAXFRBecauseOfNSEC3Narrow=false; |
513 | NSEC3PARAMRecordContent ns3pr; | |
514 | bool narrow; | |
515 | bool NSEC3Zone=false; | |
8e9b7d99 BH |
516 | |
517 | DNSSECKeeper dk; | |
627d2ca2 | 518 | dk.clearCaches(target); |
8267bd2c | 519 | bool securedZone = dk.isSecuredZone(target); |
ac4a2f1e KM |
520 | bool presignedZone = dk.isPresigned(target); |
521 | ||
4888e4b2 BH |
522 | if(dk.getNSEC3PARAM(target, &ns3pr, &narrow)) { |
523 | NSEC3Zone=true; | |
524 | if(narrow) { | |
525 | L<<Logger::Error<<"Not doing AXFR of an NSEC3 narrow zone.."<<endl; | |
526 | noAXFRBecauseOfNSEC3Narrow=true; | |
527 | } | |
83fcecff | 528 | } |
78bcb858 | 529 | |
8e9b7d99 | 530 | shared_ptr<DNSPacket> outpacket= getFreshAXFRPacket(q); |
c67e46a1 | 531 | if(q->d_dnssecOk) |
05e24311 | 532 | outpacket->d_dnssecOk=true; // RFC 5936, 2.2.5 'SHOULD' |
8e9b7d99 | 533 | |
e082fb4c | 534 | if(noAXFRBecauseOfNSEC3Narrow) { |
20ca8e7d | 535 | L<<Logger::Error<<"AXFR of domain '"<<target<<"' denied to "<<q->getRemote()<<endl; |
12c86877 BH |
536 | outpacket->setRcode(RCode::Refused); |
537 | // FIXME: should actually figure out if we are auth over a zone, and send out 9 if we aren't | |
ff76e8b4 | 538 | sendPacket(outpacket,outsock); |
12c86877 BH |
539 | return 0; |
540 | } | |
83fcecff | 541 | |
20ca8e7d | 542 | L<<Logger::Error<<"AXFR of domain '"<<target<<"' initiated by "<<q->getRemote()<<endl; |
12c86877 BH |
543 | |
544 | SOAData sd; | |
3de83124 | 545 | sd.db=(DNSBackend *)-1; // force uncached answer |
12c86877 BH |
546 | { |
547 | Lock l(&s_plock); | |
8e9b7d99 | 548 | DLOG(L<<"Looking for SOA"<<endl); // find domain_id via SOA and list complete domain. No SOA, no AXFR |
12a965c5 BH |
549 | if(!s_P) { |
550 | L<<Logger::Error<<"TCP server is without backend connections in doAXFR, launching"<<endl; | |
551 | s_P=new PacketHandler; | |
552 | } | |
12c86877 | 553 | |
e082fb4c | 554 | if(!s_P->getBackend()->getSOA(target, sd) || !canDoAXFR(q)) { |
d10f9034 | 555 | L<<Logger::Error<<"AXFR of domain '"<<target<<"' failed: not authoritative"<<endl; |
12c86877 | 556 | outpacket->setRcode(9); // 'NOTAUTH' |
ff76e8b4 | 557 | sendPacket(outpacket,outsock); |
12c86877 BH |
558 | return 0; |
559 | } | |
3de83124 | 560 | } |
8e9b7d99 BH |
561 | |
562 | UeberBackend db; | |
3de83124 | 563 | sd.db=(DNSBackend *)-1; // force uncached answer |
8e9b7d99 BH |
564 | if(!db.getSOA(target, sd)) { |
565 | L<<Logger::Error<<"AXFR of domain '"<<target<<"' failed: not authoritative in second instance"<<endl; | |
3de83124 | 566 | outpacket->setRcode(9); // 'NOTAUTH' |
ff76e8b4 | 567 | sendPacket(outpacket,outsock); |
3de83124 BH |
568 | return 0; |
569 | } | |
7b308b7e | 570 | |
3de83124 BH |
571 | if(!sd.db || sd.db==(DNSBackend *)-1) { |
572 | L<<Logger::Error<<"Error determining backend for domain '"<<target<<"' trying to serve an AXFR"<<endl; | |
573 | outpacket->setRcode(RCode::ServFail); | |
ff76e8b4 | 574 | sendPacket(outpacket,outsock); |
3de83124 | 575 | return 0; |
12c86877 | 576 | } |
3de83124 | 577 | |
78bcb858 BH |
578 | TSIGRecordContent trc; |
579 | string tsigkeyname, tsigsecret; | |
580 | ||
581 | q->getTSIGDetails(&trc, &tsigkeyname, 0); | |
582 | ||
583 | if(!tsigkeyname.empty()) { | |
584 | string tsig64, algorithm; | |
585 | Lock l(&s_plock); | |
586 | s_P->getBackend()->getTSIGKey(tsigkeyname, &algorithm, &tsig64); | |
587 | B64Decode(tsig64, tsigsecret); | |
588 | } | |
8e9b7d99 | 589 | |
8e9b7d99 BH |
590 | |
591 | UeberBackend signatureDB; | |
8e9b7d99 | 592 | |
8267bd2c | 593 | // SOA *must* go out first, our signing pipe might reorder |
12c86877 | 594 | DLOG(L<<"Sending out SOA"<<endl); |
8267bd2c BH |
595 | DNSResourceRecord soa = makeDNSRRFromSOAData(sd); |
596 | outpacket->addRecord(soa); | |
92c90b44 | 597 | editSOA(dk, sd.qname, outpacket.get()); |
8d3cbffa BH |
598 | if(securedZone) { |
599 | set<string, CIStringCompare> authSet; | |
600 | authSet.insert(target); | |
601 | addRRSigs(dk, signatureDB, authSet, outpacket->getRRS()); | |
602 | } | |
8e9b7d99 | 603 | |
78bcb858 BH |
604 | if(!tsigkeyname.empty()) |
605 | outpacket->setTSIGDetails(trc, tsigkeyname, tsigsecret, trc.d_mac); // first answer is 'normal' | |
606 | ||
8267bd2c | 607 | sendPacket(outpacket, outsock); |
78bcb858 BH |
608 | |
609 | trc.d_mac = outpacket->d_trc.d_mac; | |
8267bd2c BH |
610 | outpacket = getFreshAXFRPacket(q); |
611 | ||
ff99a74b | 612 | ChunkedSigningPipe csp(target, securedZone, "", ::arg().asNum("signing-threads", 1)); |
8e9b7d99 | 613 | |
bec14a20 | 614 | typedef map<string, NSECXEntry> nsecxrepo_t; |
9d3151d9 | 615 | nsecxrepo_t nsecxrepo; |
4888e4b2 BH |
616 | |
617 | // this is where the DNSKEYs go in | |
0c350cb5 | 618 | |
4c1474f3 | 619 | DNSSECKeeper::keyset_t keys = dk.getKeys(target); |
0c350cb5 | 620 | |
8e9b7d99 | 621 | DNSResourceRecord rr; |
0c350cb5 | 622 | |
794c2f92 PD |
623 | rr.qname = target; |
624 | rr.ttl = sd.default_ttl; | |
625 | rr.auth = 1; // please sign! | |
626 | ||
4c1474f3 | 627 | BOOST_FOREACH(const DNSSECKeeper::keyset_t::value_type& value, keys) { |
4c1474f3 | 628 | rr.qtype = QType(QType::DNSKEY); |
4c1474f3 | 629 | rr.content = value.first.getDNSKEY().getZoneRepresentation(); |
bec14a20 | 630 | string keyname = NSEC3Zone ? hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, rr.qname) : labelReverse(rr.qname); |
9d3151d9 | 631 | NSECXEntry& ne = nsecxrepo[keyname]; |
b317b510 BH |
632 | |
633 | ne.d_set.insert(rr.qtype.getCode()); | |
794c2f92 | 634 | ne.d_ttl = sd.default_ttl; |
8e9b7d99 | 635 | csp.submit(rr); |
4c1474f3 | 636 | } |
0c350cb5 | 637 | |
cc8df07f | 638 | if(::arg().mustDo("direct-dnskey")) { |
6dae726d PD |
639 | sd.db->lookup(QType(QType::DNSKEY), target, NULL, sd.domain_id); |
640 | while(sd.db->get(rr)) { | |
641 | rr.ttl = sd.default_ttl; | |
642 | csp.submit(rr); | |
643 | } | |
644 | } | |
645 | ||
b8adb30d KM |
646 | uint8_t flags; |
647 | ||
95c5bc40 | 648 | if(NSEC3Zone) { // now stuff in the NSEC3PARAM |
b8adb30d | 649 | flags = ns3pr.d_flags; |
ce464268 | 650 | rr.qtype = QType(QType::NSEC3PARAM); |
95c5bc40 | 651 | ns3pr.d_flags = 0; |
ce464268 | 652 | rr.content = ns3pr.getZoneRepresentation(); |
b8adb30d | 653 | ns3pr.d_flags = flags; |
ce464268 BH |
654 | string keyname = hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, rr.qname); |
655 | NSECXEntry& ne = nsecxrepo[keyname]; | |
656 | ||
657 | ne.d_set.insert(rr.qtype.getCode()); | |
658 | csp.submit(rr); | |
659 | } | |
8e9b7d99 | 660 | |
0c350cb5 BH |
661 | // now start list zone |
662 | if(!(sd.db->list(target, sd.domain_id))) { | |
663 | L<<Logger::Error<<"Backend signals error condition"<<endl; | |
664 | outpacket->setRcode(2); // 'SERVFAIL' | |
665 | sendPacket(outpacket,outsock); | |
666 | return 0; | |
667 | } | |
668 | ||
b772ffea | 669 | |
5633a4af | 670 | const bool rectify = !(presignedZone || ::arg().mustDo("disable-axfr-rectify")); |
b772ffea KM |
671 | set<string> qnames, nsset, terms; |
672 | vector<DNSResourceRecord> rrs; | |
673 | ||
674 | while(sd.db->get(rr)) { | |
675 | if(endsOn(rr.qname, target)) { | |
676 | if (rectify) { | |
677 | if (rr.qtype.getCode()) { | |
678 | qnames.insert(rr.qname); | |
679 | if(rr.qtype.getCode() == QType::NS && !pdns_iequals(rr.qname, target)) | |
680 | nsset.insert(rr.qname); | |
681 | } else { | |
682 | // remove existing ents | |
683 | continue; | |
684 | } | |
685 | } | |
686 | rrs.push_back(rr); | |
687 | } else { | |
688 | L<<Logger::Warning<<"Zone '"<<target<<"' contains out-of-zone data '"<<rr.qname<<"'|"<<rr.qtype.getName()<<"', ignoring"<<endl; | |
689 | continue; | |
690 | } | |
691 | } | |
692 | ||
693 | if(rectify) { | |
694 | // set auth | |
695 | BOOST_FOREACH(DNSResourceRecord &rr, rrs) { | |
696 | rr.auth=true; | |
697 | if (rr.qtype.getCode() != QType::NS || !pdns_iequals(rr.qname, target)) { | |
698 | string shorter(rr.qname); | |
699 | do { | |
700 | if (pdns_iequals(shorter, target)) // apex is always auth | |
701 | continue; | |
702 | if(nsset.count(shorter) && !(pdns_iequals(rr.qname, shorter) && rr.qtype.getCode() == QType::DS)) | |
703 | rr.auth=false; | |
704 | } while(chopOff(shorter)); | |
705 | } else | |
706 | continue; | |
707 | } | |
708 | ||
709 | if(NSEC3Zone) { | |
710 | // ents are only required for NSEC3 zones | |
711 | uint32_t maxent = ::arg().asNum("max-ent-entries"); | |
712 | map<string,bool> nonterm; | |
713 | BOOST_FOREACH(DNSResourceRecord &rr, rrs) { | |
714 | string shorter(rr.qname); | |
715 | while(!pdns_iequals(shorter, target) && chopOff(shorter)) { | |
716 | if(!qnames.count(shorter)) { | |
717 | if(!(maxent)) { | |
718 | L<<Logger::Warning<<"Zone '"<<target<<"' has too many empty non terminals."<<endl; | |
719 | return 0; | |
720 | } | |
721 | if (!nonterm.count(shorter)) { | |
722 | nonterm.insert(pair<string, bool>(shorter, rr.auth)); | |
723 | --maxent; | |
724 | } else if (rr.auth) | |
725 | nonterm[shorter]=true; | |
726 | } | |
727 | } | |
728 | } | |
729 | ||
730 | pair<string,bool> nt; | |
731 | BOOST_FOREACH(nt, nonterm) { | |
732 | DNSResourceRecord rr; | |
733 | rr.qname=nt.first; | |
734 | rr.qtype="TYPE0"; | |
735 | rr.auth=(nt.second || !ns3pr.d_flags); | |
736 | rrs.push_back(rr); | |
737 | } | |
738 | } | |
739 | } | |
740 | ||
741 | ||
12c86877 | 742 | /* now write all other records */ |
8e9b7d99 | 743 | |
9d3151d9 | 744 | string keyname; |
ac4a2f1e | 745 | set<string> ns3rrs; |
3370c993 | 746 | unsigned int udiff; |
1c6d9830 BH |
747 | DTime dt; |
748 | dt.set(); | |
bec14a20 | 749 | int records=0; |
b772ffea | 750 | BOOST_FOREACH(DNSResourceRecord &rr, rrs) { |
ac4a2f1e KM |
751 | if (rr.qtype.getCode() == QType::RRSIG) { |
752 | RRSIGRecordContent rrc(rr.content); | |
753 | if(presignedZone && rrc.d_type == QType::NSEC3) | |
754 | ns3rrs.insert(fromBase32Hex(makeRelative(rr.qname, target))); | |
794c2f92 | 755 | continue; |
ac4a2f1e | 756 | } |
6dae726d PD |
757 | |
758 | // only skip the DNSKEY if direct-dnskey is enabled, to avoid changing behaviour | |
759 | // when it is not enabled. | |
cc8df07f | 760 | if(::arg().mustDo("direct-dnskey") && rr.qtype.getCode() == QType::DNSKEY) |
6dae726d PD |
761 | continue; |
762 | ||
bec14a20 | 763 | records++; |
feef1ece | 764 | if(securedZone && (rr.auth || rr.qtype.getCode() == QType::NS)) { |
b5baefaf PD |
765 | if (NSEC3Zone || rr.qtype.getCode()) { |
766 | keyname = NSEC3Zone ? hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, rr.qname) : labelReverse(rr.qname); | |
767 | NSECXEntry& ne = nsecxrepo[keyname]; | |
768 | ne.d_ttl = sd.default_ttl; | |
ac4a2f1e | 769 | ne.d_auth = (ne.d_auth || rr.auth || (NSEC3Zone && (!ns3pr.d_flags || (presignedZone && ns3pr.d_flags)))); |
b5baefaf PD |
770 | if (rr.qtype.getCode()) { |
771 | ne.d_set.insert(rr.qtype.getCode()); | |
772 | } | |
773 | } | |
b317b510 | 774 | } |
b5baefaf PD |
775 | |
776 | if (!rr.qtype.getCode()) | |
777 | continue; // skip empty non-terminals | |
778 | ||
add640c0 | 779 | if(rr.qtype.getCode() == QType::SOA) |
12c86877 | 780 | continue; // skip SOA - would indicate end of AXFR |
add640c0 | 781 | |
1c6d9830 BH |
782 | if(csp.submit(rr)) { |
783 | for(;;) { | |
784 | outpacket->getRRS() = csp.getChunk(); | |
785 | if(!outpacket->getRRS().empty()) { | |
54d84273 PD |
786 | if(!tsigkeyname.empty()) |
787 | outpacket->setTSIGDetails(trc, tsigkeyname, tsigsecret, trc.d_mac, true); | |
1c6d9830 | 788 | sendPacket(outpacket, outsock); |
78bcb858 | 789 | trc.d_mac=outpacket->d_trc.d_mac; |
1c6d9830 BH |
790 | outpacket=getFreshAXFRPacket(q); |
791 | } | |
792 | else | |
793 | break; | |
794 | } | |
12c86877 BH |
795 | } |
796 | } | |
78bcb858 | 797 | /* |
3370c993 | 798 | udiff=dt.udiffNoReset(); |
1c6d9830 BH |
799 | cerr<<"Starting NSEC: "<<csp.d_signed/(udiff/1000000.0)<<" sigs/s, "<<csp.d_signed<<" / "<<udiff/1000000.0<<endl; |
800 | cerr<<"Outstanding: "<<csp.d_outstanding<<", "<<csp.d_queued - csp.d_signed << endl; | |
801 | cerr<<"Ready for consumption: "<<csp.getReady()<<endl; | |
78bcb858 | 802 | */ |
feef1ece | 803 | if(securedZone) { |
4888e4b2 | 804 | if(NSEC3Zone) { |
9d3151d9 | 805 | for(nsecxrepo_t::const_iterator iter = nsecxrepo.begin(); iter != nsecxrepo.end(); ++iter) { |
ac4a2f1e | 806 | if(iter->second.d_auth && (!presignedZone || !ns3pr.d_flags || ns3rrs.count(iter->first))) { |
feef1ece PD |
807 | NSEC3RecordContent n3rc; |
808 | n3rc.d_set = iter->second.d_set; | |
809 | if (n3rc.d_set.size() && (n3rc.d_set.size() != 1 || !n3rc.d_set.count(QType::NS))) | |
810 | n3rc.d_set.insert(QType::RRSIG); | |
811 | n3rc.d_salt=ns3pr.d_salt; | |
812 | n3rc.d_flags = ns3pr.d_flags; | |
813 | n3rc.d_iterations = ns3pr.d_iterations; | |
814 | n3rc.d_algorithm = 1; // SHA1, fixed in PowerDNS for now | |
815 | nsecxrepo_t::const_iterator inext = iter; | |
816 | inext++; | |
817 | if(inext == nsecxrepo.end()) | |
818 | inext = nsecxrepo.begin(); | |
ac4a2f1e | 819 | while((!inext->second.d_auth || (presignedZone && ns3pr.d_flags && !ns3rrs.count(inext->first))) && inext != iter) |
feef1ece PD |
820 | { |
821 | inext++; | |
822 | if(inext == nsecxrepo.end()) | |
823 | inext = nsecxrepo.begin(); | |
824 | } | |
825 | n3rc.d_nexthash = inext->first; | |
1bad4190 | 826 | rr.qname = dotConcat(toBase32Hex(iter->first), sd.qname); |
feef1ece PD |
827 | |
828 | rr.ttl = sd.default_ttl; | |
829 | rr.content = n3rc.getZoneRepresentation(); | |
830 | rr.qtype = QType::NSEC3; | |
831 | rr.d_place = DNSResourceRecord::ANSWER; | |
832 | rr.auth=true; | |
833 | if(csp.submit(rr)) { | |
834 | for(;;) { | |
835 | outpacket->getRRS() = csp.getChunk(); | |
836 | if(!outpacket->getRRS().empty()) { | |
837 | if(!tsigkeyname.empty()) | |
838 | outpacket->setTSIGDetails(trc, tsigkeyname, tsigsecret, trc.d_mac, true); | |
839 | sendPacket(outpacket, outsock); | |
840 | trc.d_mac=outpacket->d_trc.d_mac; | |
841 | outpacket=getFreshAXFRPacket(q); | |
842 | } | |
843 | else | |
844 | break; | |
1c6d9830 | 845 | } |
1c6d9830 | 846 | } |
8e9b7d99 | 847 | } |
4888e4b2 BH |
848 | } |
849 | } | |
9d3151d9 | 850 | else for(nsecxrepo_t::const_iterator iter = nsecxrepo.begin(); iter != nsecxrepo.end(); ++iter) { |
ed9c3a50 | 851 | NSECRecordContent nrc; |
b317b510 | 852 | nrc.d_set = iter->second.d_set; |
ed9c3a50 BH |
853 | nrc.d_set.insert(QType::RRSIG); |
854 | nrc.d_set.insert(QType::NSEC); | |
9d3151d9 | 855 | if(boost::next(iter) != nsecxrepo.end()) { |
bec14a20 | 856 | nrc.d_next = labelReverse(boost::next(iter)->first); |
ed9c3a50 BH |
857 | } |
858 | else | |
bec14a20 | 859 | nrc.d_next=labelReverse(nsecxrepo.begin()->first); |
ed9c3a50 | 860 | |
bec14a20 | 861 | rr.qname = labelReverse(iter->first); |
ed9c3a50 | 862 | |
b5baefaf | 863 | rr.ttl = sd.default_ttl; |
ed9c3a50 BH |
864 | rr.content = nrc.getZoneRepresentation(); |
865 | rr.qtype = QType::NSEC; | |
866 | rr.d_place = DNSResourceRecord::ANSWER; | |
5f5221b4 | 867 | rr.auth=true; |
8e9b7d99 | 868 | if(csp.submit(rr)) { |
1c6d9830 BH |
869 | for(;;) { |
870 | outpacket->getRRS() = csp.getChunk(); | |
871 | if(!outpacket->getRRS().empty()) { | |
78bcb858 BH |
872 | if(!tsigkeyname.empty()) |
873 | outpacket->setTSIGDetails(trc, tsigkeyname, tsigsecret, trc.d_mac, true); | |
1c6d9830 | 874 | sendPacket(outpacket, outsock); |
78bcb858 | 875 | trc.d_mac=outpacket->d_trc.d_mac; |
1c6d9830 BH |
876 | outpacket=getFreshAXFRPacket(q); |
877 | } | |
878 | else | |
879 | break; | |
880 | } | |
8e9b7d99 | 881 | } |
add640c0 | 882 | } |
add640c0 | 883 | } |
78bcb858 | 884 | /* |
3370c993 | 885 | udiff=dt.udiffNoReset(); |
1c6d9830 BH |
886 | cerr<<"Flushing pipe: "<<csp.d_signed/(udiff/1000000.0)<<" sigs/s, "<<csp.d_signed<<" / "<<udiff/1000000.0<<endl; |
887 | cerr<<"Outstanding: "<<csp.d_outstanding<<", "<<csp.d_queued - csp.d_signed << endl; | |
888 | cerr<<"Ready for consumption: "<<csp.getReady()<<endl; | |
78bcb858 | 889 | * */ |
bec14a20 BH |
890 | for(;;) { |
891 | outpacket->getRRS() = csp.getChunk(true); // flush the pipe | |
892 | if(!outpacket->getRRS().empty()) { | |
78bcb858 BH |
893 | if(!tsigkeyname.empty()) |
894 | outpacket->setTSIGDetails(trc, tsigkeyname, tsigsecret, trc.d_mac, true); // first answer is 'normal' | |
bec14a20 | 895 | sendPacket(outpacket, outsock); |
78bcb858 | 896 | trc.d_mac=outpacket->d_trc.d_mac; |
bec14a20 BH |
897 | outpacket=getFreshAXFRPacket(q); |
898 | } | |
899 | else | |
900 | break; | |
12c86877 | 901 | } |
8e9b7d99 | 902 | |
1c6d9830 | 903 | udiff=dt.udiffNoReset(); |
f1f85f12 BH |
904 | if(securedZone) |
905 | L<<Logger::Info<<"Done signing: "<<csp.d_signed/(udiff/1000000.0)<<" sigs/s, "<<endl; | |
1c6d9830 | 906 | |
12c86877 BH |
907 | DLOG(L<<"Done writing out records"<<endl); |
908 | /* and terminate with yet again the SOA record */ | |
8e9b7d99 | 909 | outpacket=getFreshAXFRPacket(q); |
12c86877 | 910 | outpacket->addRecord(soa); |
92c90b44 | 911 | editSOA(dk, sd.qname, outpacket.get()); |
78bcb858 BH |
912 | if(!tsigkeyname.empty()) |
913 | outpacket->setTSIGDetails(trc, tsigkeyname, tsigsecret, trc.d_mac, true); | |
92c90b44 | 914 | |
ff76e8b4 | 915 | sendPacket(outpacket, outsock); |
78bcb858 | 916 | |
12c86877 | 917 | DLOG(L<<"last packet - close"<<endl); |
20ca8e7d | 918 | L<<Logger::Error<<"AXFR of domain '"<<target<<"' to "<<q->getRemote()<<" finished"<<endl; |
12c86877 BH |
919 | |
920 | return 1; | |
921 | } | |
922 | ||
6e59a580 KM |
923 | int TCPNameserver::doIXFR(shared_ptr<DNSPacket> q, int outsock) |
924 | { | |
925 | shared_ptr<DNSPacket> outpacket=getFreshAXFRPacket(q); | |
926 | if(q->d_dnssecOk) | |
927 | outpacket->d_dnssecOk=true; // RFC 5936, 2.2.5 'SHOULD' | |
928 | ||
929 | DNSSECKeeper dk; | |
930 | NSEC3PARAMRecordContent ns3pr; | |
931 | bool narrow; | |
6e59a580 KM |
932 | |
933 | dk.clearCaches(q->qdomain); | |
934 | bool securedZone = dk.isSecuredZone(q->qdomain); | |
935 | if(dk.getNSEC3PARAM(q->qdomain, &ns3pr, &narrow)) { | |
6e59a580 | 936 | if(narrow) { |
a3bcd88b | 937 | L<<Logger::Error<<"Not doing IXFR of an NSEC3 narrow zone."<<endl; |
6e59a580 KM |
938 | L<<Logger::Error<<"IXFR of domain '"<<q->qdomain<<"' denied to "<<q->getRemote()<<endl; |
939 | outpacket->setRcode(RCode::Refused); | |
940 | sendPacket(outpacket,outsock); | |
941 | return 0; | |
942 | } | |
943 | } | |
944 | ||
945 | uint32_t serial = 0; | |
946 | MOADNSParser mdp(q->getString()); | |
947 | for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i != mdp.d_answers.end(); ++i) { | |
948 | const DNSRecord *rr = &i->first; | |
949 | if (rr->d_type == QType::SOA && rr->d_place == DNSRecord::Nameserver) { | |
950 | vector<string>parts; | |
951 | stringtok(parts, rr->d_content->getZoneRepresentation()); | |
952 | if (parts.size() >= 3) { | |
953 | serial=atoi(parts[2].c_str()); | |
954 | } else { | |
955 | L<<Logger::Error<<"No serial in IXFR query"<<endl; | |
956 | outpacket->setRcode(RCode::FormErr); | |
957 | sendPacket(outpacket,outsock); | |
958 | return 0; | |
959 | } | |
960 | } else { | |
961 | L<<Logger::Error<<"Additional records in IXFR query"<<endl; | |
962 | outpacket->setRcode(RCode::FormErr); | |
963 | sendPacket(outpacket,outsock); | |
964 | return 0; | |
965 | } | |
966 | } | |
967 | ||
968 | L<<Logger::Error<<"IXFR of domain '"<<q->qdomain<<"' initiated by "<<q->getRemote()<<" with serial "<<serial<<endl; | |
969 | ||
970 | SOAData sd; | |
971 | sd.db=(DNSBackend *)-1; // force uncached answer | |
972 | { | |
973 | Lock l(&s_plock); | |
974 | DLOG(L<<"Looking for SOA"<<endl); // find domain_id via SOA and list complete domain. No SOA, no IXFR | |
975 | if(!s_P) { | |
976 | L<<Logger::Error<<"TCP server is without backend connections in doIXFR, launching"<<endl; | |
977 | s_P=new PacketHandler; | |
978 | } | |
979 | ||
980 | if(!s_P->getBackend()->getSOA(q->qdomain, sd)) { | |
981 | L<<Logger::Error<<"IXFR of domain '"<<q->qdomain<<"' failed: not authoritative"<<endl; | |
982 | outpacket->setRcode(9); // 'NOTAUTH' | |
983 | sendPacket(outpacket,outsock); | |
984 | return 0; | |
985 | } | |
986 | } | |
987 | ||
988 | string target = q->qdomain; | |
989 | ||
990 | UeberBackend db; | |
991 | sd.db=(DNSBackend *)-1; // force uncached answer | |
992 | if(!db.getSOA(target, sd)) { | |
993 | L<<Logger::Error<<"IXFR of domain '"<<target<<"' failed: not authoritative in second instance"<<endl; | |
994 | outpacket->setRcode(9); // 'NOTAUTH' | |
995 | sendPacket(outpacket,outsock); | |
996 | return 0; | |
997 | } | |
998 | ||
999 | if(!sd.db || sd.db==(DNSBackend *)-1) { | |
1000 | L<<Logger::Error<<"Error determining backend for domain '"<<target<<"' trying to serve an IXFR"<<endl; | |
1001 | outpacket->setRcode(RCode::ServFail); | |
1002 | sendPacket(outpacket,outsock); | |
1003 | return 0; | |
1004 | } | |
1005 | if (!rfc1982LessThan(serial, sd.serial)) { | |
1006 | TSIGRecordContent trc; | |
1007 | string tsigkeyname, tsigsecret; | |
1008 | ||
1009 | q->getTSIGDetails(&trc, &tsigkeyname, 0); | |
1010 | ||
1011 | if(!tsigkeyname.empty()) { | |
1012 | string tsig64, algorithm; | |
1013 | Lock l(&s_plock); | |
1014 | s_P->getBackend()->getTSIGKey(tsigkeyname, &algorithm, &tsig64); | |
1015 | B64Decode(tsig64, tsigsecret); | |
1016 | } | |
1017 | ||
1018 | UeberBackend signatureDB; | |
1019 | ||
1020 | // SOA *must* go out first, our signing pipe might reorder | |
1021 | DLOG(L<<"Sending out SOA"<<endl); | |
1022 | DNSResourceRecord soa = makeDNSRRFromSOAData(sd); | |
1023 | outpacket->addRecord(soa); | |
1024 | editSOA(dk, sd.qname, outpacket.get()); | |
1025 | if(securedZone) { | |
1026 | set<string, CIStringCompare> authSet; | |
1027 | authSet.insert(target); | |
1028 | addRRSigs(dk, signatureDB, authSet, outpacket->getRRS()); | |
1029 | } | |
1030 | ||
1031 | if(!tsigkeyname.empty()) | |
1032 | outpacket->setTSIGDetails(trc, tsigkeyname, tsigsecret, trc.d_mac); // first answer is 'normal' | |
1033 | ||
1034 | sendPacket(outpacket, outsock); | |
1035 | ||
1036 | L<<Logger::Error<<"IXFR of domain '"<<target<<"' to "<<q->getRemote()<<" finished"<<endl; | |
1037 | ||
1038 | return 1; | |
1039 | } | |
1040 | ||
1041 | L<<Logger::Error<<"IXFR fallback to AXFR for domain '"<<target<<"' our serial "<<sd.serial<<endl; | |
1042 | return doAXFR(q->qdomain, q, outsock); | |
1043 | } | |
1044 | ||
12c86877 BH |
1045 | TCPNameserver::~TCPNameserver() |
1046 | { | |
1047 | delete d_connectionroom_sem; | |
1048 | } | |
1049 | ||
1050 | TCPNameserver::TCPNameserver() | |
1051 | { | |
379ab445 BH |
1052 | // sem_init(&d_connectionroom_sem,0,::arg().asNum("max-tcp-connections")); |
1053 | d_connectionroom_sem = new Semaphore( ::arg().asNum( "max-tcp-connections" )); | |
12c86877 BH |
1054 | |
1055 | s_timeout=10; | |
1056 | vector<string>locals; | |
379ab445 | 1057 | stringtok(locals,::arg()["local-address"]," ,"); |
12c86877 BH |
1058 | |
1059 | vector<string>locals6; | |
379ab445 | 1060 | stringtok(locals6,::arg()["local-ipv6"]," ,"); |
12c86877 | 1061 | |
12c86877 | 1062 | if(locals.empty() && locals6.empty()) |
3f81d239 | 1063 | throw PDNSException("No local address specified"); |
12c86877 | 1064 | |
68b011bd | 1065 | d_ng.toMasks(::arg()["allow-axfr-ips"] ); |
9f1d5826 | 1066 | |
12c86877 | 1067 | signal(SIGPIPE,SIG_IGN); |
12c86877 BH |
1068 | |
1069 | for(vector<string>::const_iterator laddr=locals.begin();laddr!=locals.end();++laddr) { | |
12c86877 | 1070 | int s=socket(AF_INET,SOCK_STREAM,0); |
326484be | 1071 | |
12c86877 | 1072 | if(s<0) |
3f81d239 | 1073 | throw PDNSException("Unable to acquire TCP socket: "+stringerror()); |
12c86877 | 1074 | |
fb316318 PD |
1075 | Utility::setCloseOnExec(s); |
1076 | ||
379ab445 | 1077 | ComboAddress local(*laddr, ::arg().asNum("local-port")); |
12c86877 BH |
1078 | |
1079 | int tmp=1; | |
1080 | if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0) { | |
1081 | L<<Logger::Error<<"Setsockopt failed"<<endl; | |
1082 | exit(1); | |
1083 | } | |
326484be | 1084 | |
379ab445 | 1085 | if(::bind(s, (sockaddr*)&local, local.getSocklen())<0) { |
2c896042 | 1086 | close(s); |
5ecb2885 MZ |
1087 | if( errno == EADDRNOTAVAIL && ! ::arg().mustDo("local-address-nonexist-fail") ) { |
1088 | L<<Logger::Error<<"IPv4 Address " << *laddr << " does not exist on this server - skipping TCP bind" << endl; | |
1089 | continue; | |
1090 | } else { | |
1091 | L<<Logger::Error<<"binding to TCP socket " << *laddr << ": "<<strerror(errno)<<endl; | |
2ab7e9ac | 1092 | throw PDNSException("Unable to bind to TCP socket"); |
5ecb2885 | 1093 | } |
12c86877 BH |
1094 | } |
1095 | ||
1096 | listen(s,128); | |
ff76e8b4 | 1097 | L<<Logger::Error<<"TCP server bound to "<<local.toStringWithPort()<<endl; |
12c86877 | 1098 | d_sockets.push_back(s); |
8edfedf1 BH |
1099 | struct pollfd pfd; |
1100 | memset(&pfd, 0, sizeof(pfd)); | |
1101 | pfd.fd = s; | |
1102 | pfd.events = POLLIN; | |
1103 | ||
1104 | d_prfds.push_back(pfd); | |
12c86877 BH |
1105 | } |
1106 | ||
76473b92 | 1107 | #if HAVE_IPV6 |
12c86877 | 1108 | for(vector<string>::const_iterator laddr=locals6.begin();laddr!=locals6.end();++laddr) { |
12c86877 BH |
1109 | int s=socket(AF_INET6,SOCK_STREAM,0); |
1110 | ||
1111 | if(s<0) | |
3f81d239 | 1112 | throw PDNSException("Unable to acquire TCPv6 socket: "+stringerror()); |
178d5134 | 1113 | |
fb316318 PD |
1114 | Utility::setCloseOnExec(s); |
1115 | ||
379ab445 | 1116 | ComboAddress local(*laddr, ::arg().asNum("local-port")); |
12c86877 BH |
1117 | |
1118 | int tmp=1; | |
1119 | if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0) { | |
1120 | L<<Logger::Error<<"Setsockopt failed"<<endl; | |
1121 | exit(1); | |
1122 | } | |
326484be BH |
1123 | if(setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &tmp, sizeof(tmp)) < 0) { |
1124 | L<<Logger::Error<<"Failed to set IPv6 socket to IPv6 only, continuing anyhow: "<<strerror(errno)<<endl; | |
1125 | } | |
ff76e8b4 | 1126 | if(bind(s, (const sockaddr*)&local, local.getSocklen())<0) { |
2c896042 | 1127 | close(s); |
5ecb2885 MZ |
1128 | if( errno == EADDRNOTAVAIL && ! ::arg().mustDo("local-ipv6-nonexist-fail") ) { |
1129 | L<<Logger::Error<<"IPv6 Address " << *laddr << " does not exist on this server - skipping TCP bind" << endl; | |
1130 | continue; | |
1131 | } else { | |
1132 | L<<Logger::Error<<"binding to TCPv6 socket" << *laddr << ": "<<strerror(errno)<<endl; | |
2ab7e9ac | 1133 | throw PDNSException("Unable to bind to TCPv6 socket"); |
5ecb2885 | 1134 | } |
12c86877 BH |
1135 | } |
1136 | ||
1137 | listen(s,128); | |
506a9050 | 1138 | L<<Logger::Error<<"TCPv6 server bound to "<<local.toStringWithPort()<<endl; // this gets %eth0 right |
12c86877 | 1139 | d_sockets.push_back(s); |
8edfedf1 BH |
1140 | |
1141 | struct pollfd pfd; | |
1142 | memset(&pfd, 0, sizeof(pfd)); | |
1143 | pfd.fd = s; | |
1144 | pfd.events = POLLIN; | |
1145 | ||
1146 | d_prfds.push_back(pfd); | |
12c86877 | 1147 | } |
76473b92 | 1148 | #endif |
12c86877 BH |
1149 | } |
1150 | ||
1151 | ||
ff76e8b4 | 1152 | //! Start of TCP operations thread, we launch a new thread for each incoming TCP question |
12c86877 BH |
1153 | void TCPNameserver::thread() |
1154 | { | |
12c86877 BH |
1155 | try { |
1156 | for(;;) { | |
1157 | int fd; | |
1158 | struct sockaddr_in remote; | |
1159 | Utility::socklen_t addrlen=sizeof(remote); | |
1160 | ||
8edfedf1 | 1161 | int ret=poll(&d_prfds[0], d_prfds.size(), -1); // blocks, forever if need be |
8a63d3ce | 1162 | if(ret <= 0) |
4957a608 | 1163 | continue; |
8a63d3ce | 1164 | |
12c86877 | 1165 | int sock=-1; |
8edfedf1 | 1166 | BOOST_FOREACH(const struct pollfd& pfd, d_prfds) { |
4957a608 BH |
1167 | if(pfd.revents == POLLIN) { |
1168 | sock = pfd.fd; | |
1169 | addrlen=sizeof(remote); | |
1170 | ||
1171 | if((fd=accept(sock, (sockaddr*)&remote, &addrlen))<0) { | |
1172 | L<<Logger::Error<<"TCP question accept error: "<<strerror(errno)<<endl; | |
1173 | ||
1174 | if(errno==EMFILE) { | |
1175 | L<<Logger::Error<<Logger::NTLog<<"TCP handler out of filedescriptors, exiting, won't recover from this"<<endl; | |
1176 | exit(1); | |
1177 | } | |
1178 | } | |
1179 | else { | |
1180 | pthread_t tid; | |
1181 | d_connectionroom_sem->wait(); // blocks if no connections are available | |
1182 | ||
1183 | int room; | |
1184 | d_connectionroom_sem->getValue( &room); | |
1185 | if(room<1) | |
1186 | L<<Logger::Warning<<Logger::NTLog<<"Limit of simultaneous TCP connections reached - raise max-tcp-connections"<<endl; | |
1187 | ||
222efdc0 | 1188 | if(pthread_create(&tid, 0, &doConnection, reinterpret_cast<void*>(fd))) { |
4957a608 BH |
1189 | L<<Logger::Error<<"Error creating thread: "<<stringerror()<<endl; |
1190 | d_connectionroom_sem->post(); | |
1191 | } | |
1192 | } | |
1193 | } | |
12c86877 BH |
1194 | } |
1195 | } | |
1196 | } | |
3f81d239 | 1197 | catch(PDNSException &AE) { |
abc1d928 | 1198 | L<<Logger::Error<<"TCP Nameserver thread dying because of fatal error: "<<AE.reason<<endl; |
12c86877 BH |
1199 | } |
1200 | catch(...) { | |
1201 | L<<Logger::Error<<"TCPNameserver dying because of an unexpected fatal error"<<endl; | |
1202 | } | |
1203 | exit(1); // take rest of server with us | |
1204 | } | |
1205 | ||
1206 |