]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/resolver.cc
Include config.h only in .cc files
[thirdparty/pdns.git] / pdns / resolver.cc
1 /*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002 - 2011 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 as
7 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 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "packetcache.hh"
26 #include "utility.hh"
27 #include "resolver.hh"
28 #include <pthread.h>
29 #include <semaphore.h>
30 #include <iostream>
31 #include <errno.h>
32 #include "misc.hh"
33 #include <algorithm>
34 #include <sstream>
35 #include "dnsrecords.hh"
36 #include <cstring>
37 #include <string>
38 #include <vector>
39 #include <boost/algorithm/string.hpp>
40 #include "dns.hh"
41 #include "qtype.hh"
42 #include "tcpreceiver.hh"
43 #include "pdnsexception.hh"
44 #include "statbag.hh"
45 #include "arguments.hh"
46 #include "base64.hh"
47 #include "dnswriter.hh"
48 #include "dnsparser.hh"
49 #include <boost/shared_ptr.hpp>
50 #include <boost/foreach.hpp>
51 #include "dns_random.hh"
52
53 #include "namespaces.hh"
54
55 int makeQuerySocket(const ComboAddress& local, bool udpOrTCP)
56 {
57 ComboAddress ourLocal(local);
58
59 int sock=socket(ourLocal.sin4.sin_family, udpOrTCP ? SOCK_DGRAM : SOCK_STREAM, 0);
60 Utility::setCloseOnExec(sock);
61 if(sock < 0) {
62 unixDie("Creating local resolver socket for "+ourLocal.toString() + ((local.sin4.sin_family == AF_INET6) ? ", does your OS miss IPv6?" : ""));
63 }
64
65 if(udpOrTCP) {
66 // udp, try hard to bind an unpredictable port
67 int tries=10;
68 while(--tries) {
69 ourLocal.sin4.sin_port = htons(10000+(dns_random(10000)));
70
71 if (::bind(sock, (struct sockaddr *)&ourLocal, ourLocal.getSocklen()) >= 0)
72 break;
73 }
74 // cerr<<"bound udp port "<<ourLocal.sin4.sin_port<<", "<<tries<<" tries left"<<endl;
75
76 if(!tries) {
77 Utility::closesocket(sock);
78 throw PDNSException("Resolver binding to local UDP socket on "+ourLocal.toString()+": "+stringerror());
79 }
80 }
81 else {
82 // tcp, let the kernel figure out the port
83 // cerr<<"letting kernel pick TCP port"<<endl;
84 ourLocal.sin4.sin_port = 0;
85 if(::bind(sock, (struct sockaddr *)&ourLocal, ourLocal.getSocklen()) < 0)
86 throw PDNSException("Resolver binding to local TCP socket on "+ourLocal.toString()+": "+stringerror());
87 }
88 return sock;
89 }
90
91 Resolver::Resolver()
92 try
93 {
94 locals["default4"] = makeQuerySocket(ComboAddress(::arg()["query-local-address"]), true);
95 if(!::arg()["query-local-address6"].empty())
96 locals["default6"] = makeQuerySocket(ComboAddress(::arg()["query-local-address6"]), true);
97 else
98 locals["default6"] = -1;
99 }
100 catch(...) {
101 if(locals["default4"]>=0)
102 close(locals["default4"]);
103 throw;
104 }
105
106 Resolver::~Resolver()
107 {
108 for(std::map<std::string,int>::iterator iter = locals.begin(); iter != locals.end(); iter++) {
109 if (iter->second >= 0)
110 close(iter->second);
111 }
112 }
113
114 uint16_t Resolver::sendResolve(const ComboAddress& remote, const ComboAddress& local,
115 const char *domain, int type, bool dnssecOK,
116 const string& tsigkeyname, const string& tsigalgorithm,
117 const string& tsigsecret)
118 {
119 uint16_t randomid;
120 vector<uint8_t> packet;
121 DNSPacketWriter pw(packet, domain, type);
122 pw.getHeader()->id = randomid = dns_random(0xffff);
123
124 if(dnssecOK) {
125 pw.addOpt(2800, 0, EDNSOpts::DNSSECOK);
126 pw.commit();
127 }
128
129 if(!tsigkeyname.empty()) {
130 // cerr<<"Adding TSIG to notification, key name: '"<<tsigkeyname<<"', algo: '"<<tsigalgorithm<<"', secret: "<<Base64Encode(tsigsecret)<<endl;
131 TSIGRecordContent trc;
132 if (tsigalgorithm == "hmac-md5")
133 trc.d_algoName = tsigalgorithm + ".sig-alg.reg.int.";
134 else
135 trc.d_algoName = tsigalgorithm;
136 trc.d_time = time(0);
137 trc.d_fudge = 300;
138 trc.d_origID=ntohs(randomid);
139 trc.d_eRcode=0;
140 addTSIG(pw, &trc, tsigkeyname, tsigsecret, "", false);
141 }
142
143 int sock;
144
145 // choose socket based on local
146 if (local.sin4.sin_family == 0) {
147 // up to us.
148 sock = remote.sin4.sin_family == AF_INET ? locals["default4"] : locals["default6"];
149 } else {
150 std::string lstr = local.toString();
151 std::map<std::string, int>::iterator lptr;
152 // see if there is a local
153
154 if ((lptr = locals.find(lstr)) != locals.end()) {
155 sock = lptr->second;
156 } else {
157 // try to make socket
158 sock = makeQuerySocket(local, true);
159 Utility::setNonBlocking( sock );
160 locals[lstr] = sock;
161 }
162 }
163
164 if(sendto(sock, &packet[0], packet.size(), 0, (struct sockaddr*)(&remote), remote.getSocklen()) < 0) {
165 throw ResolverException("Unable to ask query of "+remote.toStringWithPort()+": "+stringerror());
166 }
167 return randomid;
168 }
169
170 uint16_t Resolver::sendResolve(const ComboAddress& remote, const char *domain,
171 int type, bool dnssecOK,
172 const string& tsigkeyname, const string& tsigalgorithm,
173 const string& tsigsecret)
174 {
175 ComboAddress local;
176 local.sin4.sin_family = 0;
177 return this->sendResolve(remote, local, domain, type, dnssecOK, tsigkeyname, tsigalgorithm, tsigsecret);
178 }
179
180 static int parseResult(MOADNSParser& mdp, const std::string& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result)
181 {
182 result->clear();
183
184 if(mdp.d_header.rcode)
185 return mdp.d_header.rcode;
186
187 if(!origQname.empty()) { // not AXFR
188 if(mdp.d_header.id != id)
189 throw ResolverException("Remote nameserver replied with wrong id");
190 if(mdp.d_header.qdcount != 1)
191 throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(mdp.d_header.qdcount)+")");
192 if(mdp.d_qname != origQname+".")
193 throw ResolverException(string("resolver: received an answer to another question (")+mdp.d_qname+"!="+ origQname+".)");
194 }
195
196 vector<DNSResourceRecord> ret;
197 DNSResourceRecord rr;
198 for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) {
199 rr.qname = i->first.d_label;
200 if(!rr.qname.empty())
201 boost::erase_tail(rr.qname, 1); // strip .
202 rr.qtype = i->first.d_type;
203 rr.ttl = i->first.d_ttl;
204 rr.content = i->first.d_content->getZoneRepresentation();
205 switch(rr.qtype.getCode()) {
206 case QType::SRV:
207 case QType::MX:
208 if (rr.content.size() >= 2 && *(rr.content.rbegin()+1) == ' ')
209 break;
210 case QType::CNAME:
211 case QType::NS:
212 if(!rr.content.empty())
213 boost::erase_tail(rr.content, 1);
214 }
215 result->push_back(rr);
216 }
217
218 return 0;
219 }
220
221 bool Resolver::tryGetSOASerial(string* domain, uint32_t *theirSerial, uint32_t *theirInception, uint32_t *theirExpire, uint16_t* id)
222 {
223 struct pollfd *fds = new struct pollfd[locals.size()];
224 size_t i = 0, k;
225 int sock;
226
227 for(std::map<string,int>::iterator iter=locals.begin(); iter != locals.end(); iter++, i++) {
228 fds[i].fd = iter->second;
229 fds[i].events = POLLIN;
230 }
231
232 if (poll(fds, i, 250) < 1) { // wait for 0.25s
233 delete [] fds;
234 return false;
235 }
236
237 sock = -1;
238
239 // determine who
240 for(k=0;k<i;k++) {
241 if ((fds[k].revents & POLLIN) == POLLIN) {
242 sock = fds[k].fd;
243 break;
244 }
245 }
246
247 delete [] fds;
248
249 if (sock < 0) return false; // false alarm
250
251 int err;
252 ComboAddress fromaddr;
253 socklen_t addrlen=fromaddr.getSocklen();
254 char buf[3000];
255 err = recvfrom(sock, buf, sizeof(buf), 0,(struct sockaddr*)(&fromaddr), &addrlen);
256 if(err < 0) {
257 if(errno == EAGAIN)
258 return false;
259
260 throw ResolverException("recvfrom error waiting for answer: "+stringerror());
261 }
262
263 MOADNSParser mdp((char*)buf, err);
264 *id=mdp.d_header.id;
265 *domain = stripDot(mdp.d_qname);
266
267 if(mdp.d_answers.empty())
268 throw ResolverException("Query to '" + fromaddr.toStringWithPort() + "' for SOA of '" + *domain + "' produced no results (RCode: " + RCode::to_s(mdp.d_header.rcode) + ")");
269
270 if(mdp.d_qtype != QType::SOA)
271 throw ResolverException("Query to '" + fromaddr.toStringWithPort() + "' for SOA of '" + *domain + "' returned wrong record type");
272
273 *theirInception = *theirExpire = 0;
274 bool gotSOA=false;
275 BOOST_FOREACH(const MOADNSParser::answers_t::value_type& drc, mdp.d_answers) {
276 if(drc.first.d_type == QType::SOA) {
277 shared_ptr<SOARecordContent> src=boost::dynamic_pointer_cast<SOARecordContent>(drc.first.d_content);
278 *theirSerial=src->d_st.serial;
279 gotSOA = true;
280 }
281 if(drc.first.d_type == QType::RRSIG) {
282 shared_ptr<RRSIGRecordContent> rrc=boost::dynamic_pointer_cast<RRSIGRecordContent>(drc.first.d_content);
283 if(rrc->d_type == QType::SOA) {
284 *theirInception= std::max(*theirInception, rrc->d_siginception);
285 *theirExpire = std::max(*theirExpire, rrc->d_sigexpire);
286 }
287 }
288 }
289 if(!gotSOA)
290 throw ResolverException("Query to '" + fromaddr.toString() + "' for SOA of '" + *domain + "' did not return a SOA");
291 return true;
292 }
293
294 int Resolver::resolve(const string &ipport, const char *domain, int type, Resolver::res_t* res, const ComboAddress &local)
295 {
296 try {
297 ComboAddress to(ipport, 53);
298
299 int id = sendResolve(to, local, domain, type);
300 int sock;
301
302 // choose socket based on local
303 if (local.sin4.sin_family == 0) {
304 // up to us.
305 sock = to.sin4.sin_family == AF_INET ? locals["default4"] : locals["default6"];
306 } else {
307 std::string lstr = local.toString();
308 std::map<std::string, int>::iterator lptr;
309 // see if there is a local
310
311 if ((lptr = locals.find(lstr)) != locals.end()) sock = lptr->second;
312 else throw ResolverException("sendResolve did not create socket for " + lstr);
313 }
314
315 int err=waitForData(sock, 0, 3000000);
316
317 if(!err) {
318 throw ResolverException("Timeout waiting for answer");
319 }
320 if(err < 0)
321 throw ResolverException("Error waiting for answer: "+stringerror());
322
323 ComboAddress from;
324 socklen_t addrlen = sizeof(from);
325 char buffer[3000];
326 int len;
327
328 if((len=recvfrom(sock, buffer, sizeof(buffer), 0,(struct sockaddr*)(&from), &addrlen)) < 0)
329 throw ResolverException("recvfrom error waiting for answer: "+stringerror());
330
331 MOADNSParser mdp(buffer, len);
332 return parseResult(mdp, domain, type, id, res);
333 }
334 catch(ResolverException &re) {
335 throw ResolverException(re.reason+" from "+ipport);
336 }
337 return -1;
338 }
339
340 int Resolver::resolve(const string &ipport, const char *domain, int type, Resolver::res_t* res) {
341 ComboAddress local;
342 local.sin4.sin_family = 0;
343 return resolve(ipport, domain, type, res, local);
344 }
345
346 void Resolver::getSoaSerial(const string &ipport, const string &domain, uint32_t *serial)
347 {
348 vector<DNSResourceRecord> res;
349 int ret = resolve(ipport, domain.c_str(), QType::SOA, &res);
350
351 if(ret || res.empty())
352 throw ResolverException("Query to '" + ipport + "' for SOA of '" + domain + "' produced no answers");
353
354 if(res[0].qtype.getCode() != QType::SOA)
355 throw ResolverException("Query to '" + ipport + "' for SOA of '" + domain + "' produced a "+res[0].qtype.getName()+" record");
356
357 vector<string>parts;
358 stringtok(parts, res[0].content);
359 if(parts.size()<3)
360 throw ResolverException("Query to '" + ipport + "' for SOA of '" + domain + "' produced an unparseable response");
361
362 *serial=(uint32_t)atol(parts[2].c_str());
363 }
364
365 AXFRRetriever::AXFRRetriever(const ComboAddress& remote,
366 const string& domain,
367 const string& tsigkeyname,
368 const string& tsigalgorithm,
369 const string& tsigsecret,
370 const ComboAddress* laddr)
371 : d_tsigkeyname(tsigkeyname), d_tsigsecret(tsigsecret), d_tsigPos(0), d_nonSignedMessages(0)
372 {
373 ComboAddress local;
374 if (laddr != NULL) {
375 local = (ComboAddress) (*laddr);
376 } else {
377 if(remote.sin4.sin_family == AF_INET)
378 local=ComboAddress(::arg()["query-local-address"]);
379 else if(!::arg()["query-local-address6"].empty())
380 local=ComboAddress(::arg()["query-local-address6"]);
381 else
382 local=ComboAddress("::");
383 }
384 d_sock = -1;
385 try {
386 d_sock = makeQuerySocket(local, false); // make a TCP socket
387 d_buf = shared_array<char>(new char[65536]);
388 d_remote = remote; // mostly for error reporting
389 this->connect();
390 d_soacount = 0;
391
392 vector<uint8_t> packet;
393 DNSPacketWriter pw(packet, domain, QType::AXFR);
394 pw.getHeader()->id = dns_random(0xffff);
395
396 if(!tsigkeyname.empty()) {
397 if (tsigalgorithm == "hmac-md5")
398 d_trc.d_algoName = tsigalgorithm + ".sig-alg.reg.int.";
399 else
400 d_trc.d_algoName = tsigalgorithm;
401 d_trc.d_time = time(0);
402 d_trc.d_fudge = 300;
403 d_trc.d_origID=ntohs(pw.getHeader()->id);
404 d_trc.d_eRcode=0;
405 addTSIG(pw, &d_trc, tsigkeyname, tsigsecret, "", false);
406 }
407
408 uint16_t replen=htons(packet.size());
409 Utility::iovec iov[2];
410 iov[0].iov_base=(char*)&replen;
411 iov[0].iov_len=2;
412 iov[1].iov_base=(char*)&packet[0];
413 iov[1].iov_len=packet.size();
414
415 int ret=Utility::writev(d_sock, iov, 2);
416 if(ret < 0)
417 throw ResolverException("Error sending question to "+d_remote.toStringWithPort()+": "+stringerror());
418 if(ret != (int)(2+packet.size())) {
419 throw ResolverException("Partial write on AXFR request to "+d_remote.toStringWithPort());
420 }
421
422 int res = waitForData(d_sock, 10, 0);
423
424 if(!res)
425 throw ResolverException("Timeout waiting for answer from "+d_remote.toStringWithPort()+" during AXFR");
426 if(res<0)
427 throw ResolverException("Error waiting for answer from "+d_remote.toStringWithPort()+": "+stringerror());
428 }
429 catch(...) {
430 if(d_sock >= 0)
431 close(d_sock);
432 throw;
433 }
434 }
435
436 AXFRRetriever::~AXFRRetriever()
437 {
438 close(d_sock);
439 }
440
441
442
443 int AXFRRetriever::getChunk(Resolver::res_t &res) // Implementation is making sure RFC2845 4.4 is followed.
444 {
445 if(d_soacount > 1)
446 return false;
447
448 // d_sock is connected and is about to spit out a packet
449 int len=getLength();
450 if(len<0)
451 throw ResolverException("EOF trying to read axfr chunk from remote TCP client");
452
453 timeoutReadn(len);
454 MOADNSParser mdp(d_buf.get(), len);
455
456 int err = parseResult(mdp, "", 0, 0, &res);
457 if(err)
458 throw ResolverException("AXFR chunk error: " + RCode::to_s(err));
459
460 BOOST_FOREACH(const MOADNSParser::answers_t::value_type& answer, mdp.d_answers)
461 if (answer.first.d_type == QType::SOA)
462 d_soacount++;
463
464 if(!d_tsigkeyname.empty()) { // TSIG verify message
465 // If we have multiple messages, we need to concatenate them together. We also need to make sure we know the location of
466 // the TSIG record so we can remove it in makeTSIGMessageFromTSIGPacket
467 d_signData.append(d_buf.get(), len);
468 if (mdp.getTSIGPos() == 0)
469 d_tsigPos += len;
470 else
471 d_tsigPos += mdp.getTSIGPos();
472
473 string theirMac;
474 bool checkTSIG = false;
475
476 BOOST_FOREACH(const MOADNSParser::answers_t::value_type& answer, mdp.d_answers) {
477 if (answer.first.d_type == QType::SOA) // A SOA is either the first or the last record. We need to check TSIG if that's the case.
478 checkTSIG = true;
479
480 if(answer.first.d_type == QType::TSIG) {
481 shared_ptr<TSIGRecordContent> trc = boost::dynamic_pointer_cast<TSIGRecordContent>(answer.first.d_content);
482 theirMac = trc->d_mac;
483 d_trc.d_time = trc->d_time;
484 checkTSIG = true;
485 }
486 }
487
488 if( ! checkTSIG && d_nonSignedMessages > 99) { // We're allowed to get 100 digest without a TSIG.
489 throw ResolverException("No TSIG message received in last 100 messages of AXFR transfer.");
490 }
491
492 if (checkTSIG) {
493 if (theirMac.empty())
494 throw ResolverException("No TSIG on AXFR response from "+d_remote.toStringWithPort()+" , should be signed with TSIG key '"+d_tsigkeyname+"'");
495
496 string message;
497 if (!d_prevMac.empty()) {
498 message = makeTSIGMessageFromTSIGPacket(d_signData, d_tsigPos, d_tsigkeyname, d_trc, d_prevMac, true, d_signData.size()-len);
499 } else {
500 message = makeTSIGMessageFromTSIGPacket(d_signData, d_tsigPos, d_tsigkeyname, d_trc, d_trc.d_mac, false);
501 }
502
503 TSIGHashEnum algo;
504 if (!getTSIGHashEnum(d_trc.d_algoName, algo)) {
505 throw ResolverException("Unsupported TSIG HMAC algorithm " + d_trc.d_algoName);
506 }
507
508 string ourMac=calculateHMAC(d_tsigsecret, message, algo);
509
510 // ourMac[0]++; // sabotage == for testing :-)
511 if(ourMac != theirMac) {
512 throw ResolverException("Signature failed to validate on AXFR response from "+d_remote.toStringWithPort()+" signed with TSIG key '"+d_tsigkeyname+"'");
513 }
514
515 // Reset and store some values for the next chunks.
516 d_prevMac = theirMac;
517 d_nonSignedMessages = 0;
518 d_signData.clear();
519 d_tsigPos = 0;
520 }
521 else
522 d_nonSignedMessages++;
523 }
524
525 return true;
526 }
527
528 void AXFRRetriever::timeoutReadn(uint16_t bytes)
529 {
530 time_t start=time(0);
531 int n=0;
532 int numread;
533 while(n<bytes) {
534 int res=waitForData(d_sock, 10-(time(0)-start));
535 if(res<0)
536 throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
537 if(!res)
538 throw ResolverException("Timeout while reading data from remote nameserver over TCP");
539
540 numread=recv(d_sock, d_buf.get()+n, bytes-n, 0);
541 if(numread<0)
542 throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
543 if(numread==0)
544 throw ResolverException("Remote nameserver closed TCP connection");
545 n+=numread;
546 }
547 }
548
549 void AXFRRetriever::connect()
550 {
551 Utility::setNonBlocking( d_sock );
552
553 int err;
554
555 if((err=::connect(d_sock,(struct sockaddr*)&d_remote, d_remote.getSocklen()))<0 && errno!=EINPROGRESS) {
556 Utility::closesocket(d_sock);
557 d_sock=-1;
558 throw ResolverException("connect: "+stringerror());
559 }
560
561 if(!err)
562 goto done;
563
564 err=waitForRWData(d_sock, false, 10, 0); // wait for writeability
565
566 if(!err) {
567 Utility::closesocket(d_sock); // timeout
568 d_sock=-1;
569 errno=ETIMEDOUT;
570
571 throw ResolverException("Timeout connecting to server");
572 }
573 else if(err < 0) {
574 throw ResolverException("Error connecting: "+string(strerror(err)));
575 }
576 else {
577 Utility::socklen_t len=sizeof(err);
578 if(getsockopt(d_sock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
579 throw ResolverException("Error connecting: "+stringerror()); // Solaris
580
581 if(err)
582 throw ResolverException("Error connecting: "+string(strerror(err)));
583 }
584
585 done:
586 Utility::setBlocking( d_sock );
587 // d_sock now connected
588 }
589
590 int AXFRRetriever::getLength()
591 {
592 timeoutReadn(2);
593 return (unsigned char)d_buf[0]*256+(unsigned char)d_buf[1];
594 }
595