]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/resolver.cc
98d26fce7a57ebcc4205586b07129421c93a8beb
[thirdparty/pdns.git] / pdns / resolver.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "utility.hh"
26 #include "resolver.hh"
27 #include <pthread.h>
28 #include <semaphore.h>
29 #include <iostream>
30 #include <errno.h>
31 #include "misc.hh"
32 #include <algorithm>
33 #include <sstream>
34 #include "dnsrecords.hh"
35 #include <cstring>
36 #include <string>
37 #include <vector>
38 #include <boost/algorithm/string.hpp>
39 #include "dns.hh"
40 #include "qtype.hh"
41
42 #include "pdnsexception.hh"
43 #include "arguments.hh"
44 #include "base64.hh"
45 #include "dnswriter.hh"
46 #include "dnsparser.hh"
47
48
49 #include "dns_random.hh"
50 #include <poll.h>
51 #include "gss_context.hh"
52 #include "namespaces.hh"
53
54 int makeQuerySocket(const ComboAddress& local, bool udpOrTCP, bool nonLocalBind)
55 {
56 ComboAddress ourLocal(local);
57
58 int sock=socket(ourLocal.sin4.sin_family, udpOrTCP ? SOCK_DGRAM : SOCK_STREAM, 0);
59 if(sock < 0) {
60 if(errno == EAFNOSUPPORT && local.sin4.sin_family == AF_INET6) {
61 return -1;
62 }
63 unixDie("Creating local resolver socket for "+ourLocal.toString());
64 }
65
66 setCloseOnExec(sock);
67
68 if(nonLocalBind)
69 Utility::setBindAny(local.sin4.sin_family, sock);
70
71 if(udpOrTCP) {
72 // udp, try hard to bind an unpredictable port
73 int tries=10;
74 while(--tries) {
75 ourLocal.sin4.sin_port = htons(10000+(dns_random(10000)));
76
77 if (::bind(sock, (struct sockaddr *)&ourLocal, ourLocal.getSocklen()) >= 0)
78 break;
79 }
80 // cerr<<"bound udp port "<<ourLocal.sin4.sin_port<<", "<<tries<<" tries left"<<endl;
81
82 if(!tries) {
83 closesocket(sock);
84 throw PDNSException("Resolver binding to local UDP socket on "+ourLocal.toString()+": "+stringerror());
85 }
86 }
87 else {
88 // tcp, let the kernel figure out the port
89 // cerr<<"letting kernel pick TCP port"<<endl;
90 ourLocal.sin4.sin_port = 0;
91 if(::bind(sock, (struct sockaddr *)&ourLocal, ourLocal.getSocklen()) < 0)
92 throw PDNSException("Resolver binding to local TCP socket on "+ourLocal.toString()+": "+stringerror());
93 }
94 return sock;
95 }
96
97 Resolver::Resolver()
98 {
99 locals["default4"] = -1;
100 locals["default6"] = -1;
101 try {
102 if(!::arg()["query-local-address"].empty())
103 locals["default4"] = makeQuerySocket(ComboAddress(::arg()["query-local-address"]), true, ::arg().mustDo("non-local-bind"));
104 if(!::arg()["query-local-address6"].empty())
105 locals["default6"] = makeQuerySocket(ComboAddress(::arg()["query-local-address6"]), true, ::arg().mustDo("non-local-bind"));
106 }
107 catch(...) {
108 if(locals["default4"]>=0)
109 close(locals["default4"]);
110 if(locals["default6"]>=0)
111 close(locals["default6"]);
112 throw;
113 }
114 }
115
116 Resolver::~Resolver()
117 {
118 for (auto& iter: locals) {
119 if (iter.second >= 0)
120 close(iter.second);
121 }
122 }
123
124 uint16_t Resolver::sendResolve(const ComboAddress& remote, const ComboAddress& local,
125 const DNSName &domain, int type, int *localsock, bool dnssecOK,
126 const DNSName& tsigkeyname, const DNSName& tsigalgorithm,
127 const string& tsigsecret)
128 {
129 uint16_t randomid;
130 vector<uint8_t> packet;
131 DNSPacketWriter pw(packet, domain, type);
132 pw.getHeader()->id = randomid = dns_random(0xffff);
133
134 if(dnssecOK) {
135 pw.addOpt(2800, 0, EDNSOpts::DNSSECOK);
136 pw.commit();
137 }
138
139 if(!tsigkeyname.empty()) {
140 // cerr<<"Adding TSIG to notification, key name: '"<<tsigkeyname<<"', algo: '"<<tsigalgorithm<<"', secret: "<<Base64Encode(tsigsecret)<<endl;
141 TSIGRecordContent trc;
142 if (tsigalgorithm == DNSName("hmac-md5"))
143 trc.d_algoName = tsigalgorithm + DNSName("sig-alg.reg.int");
144 else
145 trc.d_algoName = tsigalgorithm;
146 trc.d_time = time(0);
147 trc.d_fudge = 300;
148 trc.d_origID=ntohs(randomid);
149 trc.d_eRcode=0;
150 addTSIG(pw, trc, tsigkeyname, tsigsecret, "", false);
151 }
152
153 int sock;
154
155 // choose socket based on local
156 if (local.sin4.sin_family == 0) {
157 // up to us.
158 sock = remote.sin4.sin_family == AF_INET ? locals["default4"] : locals["default6"];
159 if (sock == -1) {
160 string ipv = remote.sin4.sin_family == AF_INET ? "4" : "6";
161 string qla = remote.sin4.sin_family == AF_INET ? "" : "6";
162 throw ResolverException("No IPv" + ipv + " socket available, is query-local-address" + qla + " unset?");
163 }
164 } else {
165 std::string lstr = local.toString();
166 std::map<std::string, int>::iterator lptr;
167
168 // reuse an existing local socket or make a new one
169 if ((lptr = locals.find(lstr)) != locals.end()) {
170 sock = lptr->second;
171 } else {
172 // try to make socket
173 sock = makeQuerySocket(local, true);
174 if (sock < 0)
175 throw ResolverException("Unable to create local socket on "+lstr+" to "+remote.toStringWithPort()+": "+stringerror());
176 setNonBlocking( sock );
177 locals[lstr] = sock;
178 }
179 }
180
181 if (localsock != nullptr) {
182 *localsock = sock;
183 }
184 if(sendto(sock, &packet[0], packet.size(), 0, (struct sockaddr*)(&remote), remote.getSocklen()) < 0) {
185 throw ResolverException("Unable to ask query of "+remote.toStringWithPort()+": "+stringerror());
186 }
187 return randomid;
188 }
189
190 static int parseResult(MOADNSParser& mdp, const DNSName& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result)
191 {
192 result->clear();
193
194 if(mdp.d_header.rcode)
195 return mdp.d_header.rcode;
196
197 if(origQname.countLabels()) { // not AXFR
198 if(mdp.d_header.id != id)
199 throw ResolverException("Remote nameserver replied with wrong id");
200 if(mdp.d_header.qdcount != 1)
201 throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(mdp.d_header.qdcount)+")");
202 if(mdp.d_qname != origQname)
203 throw ResolverException(string("resolver: received an answer to another question (")+mdp.d_qname.toLogString()+"!="+ origQname.toLogString()+".)");
204 }
205
206 vector<DNSResourceRecord> ret;
207 DNSResourceRecord rr;
208 for (const auto& i: mdp.d_answers) {
209 rr.qname = i.first.d_name;
210 rr.qtype = i.first.d_type;
211 rr.ttl = i.first.d_ttl;
212 rr.content = i.first.d_content->getZoneRepresentation(true);
213 result->push_back(rr);
214 }
215
216 return 0;
217 }
218
219 bool Resolver::tryGetSOASerial(DNSName *domain, ComboAddress* remote, uint32_t *theirSerial, uint32_t *theirInception, uint32_t *theirExpire, uint16_t* id)
220 {
221 auto fds = std::unique_ptr<struct pollfd[]>(new struct pollfd[locals.size()]);
222 size_t i = 0, k;
223 int sock;
224
225 for (const auto& iter: locals) {
226 fds[i].fd = iter.second;
227 fds[i].events = POLLIN;
228 ++i;
229 }
230
231 if (poll(fds.get(), i, 250) < 1) { // wait for 0.25s
232 return false;
233 }
234
235 sock = -1;
236
237 // determine who
238 for(k=0;k<i;k++) {
239 if ((fds[k].revents & POLLIN) == POLLIN) {
240 sock = fds[k].fd;
241 break;
242 }
243 }
244
245 if (sock < 0) return false; // false alarm
246
247 int err;
248 remote->sin6.sin6_family = AF_INET6; // make sure getSocklen() below returns a large enough value
249 socklen_t addrlen=remote->getSocklen();
250 char buf[3000];
251 err = recvfrom(sock, buf, sizeof(buf), 0,(struct sockaddr*)(remote), &addrlen);
252 if(err < 0) {
253 if(errno == EAGAIN)
254 return false;
255
256 throw ResolverException("recvfrom error waiting for answer: "+stringerror());
257 }
258
259 MOADNSParser mdp(false, (char*)buf, err);
260 *id=mdp.d_header.id;
261 *domain = mdp.d_qname;
262
263 if(domain->empty())
264 throw ResolverException("SOA query to '" + remote->toStringWithPort() + "' produced response without domain name (RCode: " + RCode::to_s(mdp.d_header.rcode) + ")");
265
266 if(mdp.d_answers.empty())
267 throw ResolverException("Query to '" + remote->toStringWithPort() + "' for SOA of '" + domain->toLogString() + "' produced no results (RCode: " + RCode::to_s(mdp.d_header.rcode) + ")");
268
269 if(mdp.d_qtype != QType::SOA)
270 throw ResolverException("Query to '" + remote->toStringWithPort() + "' for SOA of '" + domain->toLogString() + "' returned wrong record type");
271
272 *theirInception = *theirExpire = 0;
273 bool gotSOA=false;
274 for(const MOADNSParser::answers_t::value_type& drc : mdp.d_answers) {
275 if(drc.first.d_type == QType::SOA) {
276 shared_ptr<SOARecordContent> src=getRR<SOARecordContent>(drc.first);
277 if (src) {
278 *theirSerial=src->d_st.serial;
279 gotSOA = true;
280 }
281 }
282 if(drc.first.d_type == QType::RRSIG) {
283 shared_ptr<RRSIGRecordContent> rrc=getRR<RRSIGRecordContent>(drc.first);
284 if(rrc && rrc->d_type == QType::SOA) {
285 *theirInception= std::max(*theirInception, rrc->d_siginception);
286 *theirExpire = std::max(*theirExpire, rrc->d_sigexpire);
287 }
288 }
289 }
290 if(!gotSOA)
291 throw ResolverException("Query to '" + remote->toString() + "' for SOA of '" + domain->toLogString() + "' did not return a SOA");
292 return true;
293 }
294
295 int Resolver::resolve(const ComboAddress& to, const DNSName &domain, int type, Resolver::res_t* res, const ComboAddress &local)
296 {
297 try {
298 int sock = -1;
299 int id = sendResolve(to, local, domain, type, &sock);
300 int err=waitForData(sock, 0, 3000000);
301
302 if(!err) {
303 throw ResolverException("Timeout waiting for answer");
304 }
305 if(err < 0)
306 throw ResolverException("Error waiting for answer: "+stringerror());
307
308 ComboAddress from;
309 socklen_t addrlen = sizeof(from);
310 char buffer[3000];
311 int len;
312
313 if((len=recvfrom(sock, buffer, sizeof(buffer), 0,(struct sockaddr*)(&from), &addrlen)) < 0)
314 throw ResolverException("recvfrom error waiting for answer: "+stringerror());
315
316 if (from != to) {
317 throw ResolverException("Got answer from the wrong peer while resolving ("+from.toStringWithPort()+" instead of "+to.toStringWithPort()+", discarding");
318 }
319
320 MOADNSParser mdp(false, buffer, len);
321 return parseResult(mdp, domain, type, id, res);
322 }
323 catch(ResolverException &re) {
324 throw ResolverException(re.reason+" from "+to.toStringWithPortExcept(53));
325 }
326 return -1;
327 }
328
329 int Resolver::resolve(const ComboAddress& ipport, const DNSName &domain, int type, Resolver::res_t* res) {
330 ComboAddress local;
331 local.sin4.sin_family = 0;
332 return resolve(ipport, domain, type, res, local);
333 }
334
335 void Resolver::getSoaSerial(const ComboAddress& ipport, const DNSName &domain, uint32_t *serial)
336 {
337 vector<DNSResourceRecord> res;
338 int ret = resolve(ipport, domain, QType::SOA, &res);
339
340 if(ret || res.empty())
341 throw ResolverException("Query to '" + ipport.toStringWithPortExcept(53) + "' for SOA of '" + domain.toString() + "' produced no answers");
342
343 if(res[0].qtype.getCode() != QType::SOA)
344 throw ResolverException("Query to '" + ipport.toStringWithPortExcept(53) + "' for SOA of '" + domain.toString() + "' produced a "+res[0].qtype.getName()+" record");
345
346 vector<string>parts;
347 stringtok(parts, res[0].content);
348 if(parts.size()<3)
349 throw ResolverException("Query to '" + ipport.toStringWithPortExcept(53) + "' for SOA of '" + domain.toString() + "' produced an unparseable response");
350
351 try {
352 *serial=pdns_stou(parts[2]);
353 }
354 catch(const std::out_of_range& oor) {
355 throw ResolverException("Query to '" + ipport.toStringWithPortExcept(53) + "' for SOA of '" + domain.toString() + "' produced an unparseable serial");
356 }
357 }
358
359 AXFRRetriever::AXFRRetriever(const ComboAddress& remote,
360 const DNSName& domain,
361 const TSIGTriplet& tt,
362 const ComboAddress* laddr,
363 size_t maxReceivedBytes)
364 : d_tsigVerifier(tt, remote, d_trc), d_receivedBytes(0), d_maxReceivedBytes(maxReceivedBytes)
365 {
366 ComboAddress local;
367 if (laddr != nullptr) {
368 local = ComboAddress(*laddr);
369 } else {
370 string qlas = remote.sin4.sin_family == AF_INET ? "query-local-address" : "query-local-address6";
371 if (::arg()[qlas].empty()) {
372 throw ResolverException("Unable to determine source address for AXFR request to " + remote.toStringWithPort() + " for " + domain.toLogString() + ". " + qlas + " is unset");
373 }
374 local=ComboAddress(::arg()[qlas]);
375 }
376 d_sock = -1;
377 try {
378 d_sock = makeQuerySocket(local, false); // make a TCP socket
379 if (d_sock < 0)
380 throw ResolverException("Error creating socket for AXFR request to "+d_remote.toStringWithPort());
381 d_buf = shared_array<char>(new char[65536]);
382 d_remote = remote; // mostly for error reporting
383 this->connect();
384 d_soacount = 0;
385
386 vector<uint8_t> packet;
387 DNSPacketWriter pw(packet, domain, QType::AXFR);
388 pw.getHeader()->id = dns_random(0xffff);
389
390 if(!tt.name.empty()) {
391 if (tt.algo == DNSName("hmac-md5"))
392 d_trc.d_algoName = tt.algo + DNSName("sig-alg.reg.int");
393 else
394 d_trc.d_algoName = tt.algo;
395 d_trc.d_time = time(0);
396 d_trc.d_fudge = 300;
397 d_trc.d_origID=ntohs(pw.getHeader()->id);
398 d_trc.d_eRcode=0;
399 addTSIG(pw, d_trc, tt.name, tt.secret, "", false);
400 }
401
402 uint16_t replen=htons(packet.size());
403 Utility::iovec iov[2];
404 iov[0].iov_base=reinterpret_cast<char*>(&replen);
405 iov[0].iov_len=2;
406 iov[1].iov_base=packet.data();
407 iov[1].iov_len=packet.size();
408
409 int ret=Utility::writev(d_sock, iov, 2);
410 if(ret < 0)
411 throw ResolverException("Error sending question to "+d_remote.toStringWithPort()+": "+stringerror());
412 if(ret != (int)(2+packet.size())) {
413 throw ResolverException("Partial write on AXFR request to "+d_remote.toStringWithPort());
414 }
415
416 int res = waitForData(d_sock, 10, 0);
417
418 if(!res)
419 throw ResolverException("Timeout waiting for answer from "+d_remote.toStringWithPort()+" during AXFR");
420 if(res<0)
421 throw ResolverException("Error waiting for answer from "+d_remote.toStringWithPort()+": "+stringerror());
422 }
423 catch(...) {
424 if(d_sock >= 0)
425 close(d_sock);
426 d_sock = -1;
427 throw;
428 }
429 }
430
431 AXFRRetriever::~AXFRRetriever()
432 {
433 close(d_sock);
434 }
435
436
437
438 int AXFRRetriever::getChunk(Resolver::res_t &res, vector<DNSRecord>* records, uint16_t timeout) // Implementation is making sure RFC2845 4.4 is followed.
439 {
440 if(d_soacount > 1)
441 return false;
442
443 // d_sock is connected and is about to spit out a packet
444 int len=getLength(timeout);
445 if(len<0)
446 throw ResolverException("EOF trying to read axfr chunk from remote TCP client");
447
448 if (d_maxReceivedBytes > 0 && (d_maxReceivedBytes - d_receivedBytes) < (size_t) len)
449 throw ResolverException("Reached the maximum number of received bytes during AXFR");
450
451 timeoutReadn(len, timeout);
452
453 d_receivedBytes += (uint16_t) len;
454
455 MOADNSParser mdp(false, d_buf.get(), len);
456
457 int err;
458 if(!records)
459 err=parseResult(mdp, DNSName(), 0, 0, &res);
460 else {
461 records->clear();
462 for(const auto& r: mdp.d_answers)
463 records->push_back(r.first);
464 err = mdp.d_header.rcode;
465 }
466
467 if(err)
468 throw ResolverException("AXFR chunk error: " + RCode::to_s(err));
469
470 for(const MOADNSParser::answers_t::value_type& answer : mdp.d_answers)
471 if (answer.first.d_type == QType::SOA)
472 d_soacount++;
473
474 try {
475 d_tsigVerifier.check(std::string(d_buf.get(), len), mdp);
476 }
477 catch(const std::runtime_error& re) {
478 throw ResolverException(re.what());
479 }
480
481 return true;
482 }
483
484 void AXFRRetriever::timeoutReadn(uint16_t bytes, uint16_t timeoutsec)
485 {
486 time_t start=time(nullptr);
487 int n=0;
488 int numread;
489 while(n<bytes) {
490 int res=waitForData(d_sock, timeoutsec-(time(nullptr)-start));
491 if(res<0)
492 throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
493 if(!res)
494 throw ResolverException("Timeout while reading data from remote nameserver over TCP");
495
496 numread=recv(d_sock, d_buf.get()+n, bytes-n, 0);
497 if(numread<0)
498 throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
499 if(numread==0)
500 throw ResolverException("Remote nameserver closed TCP connection");
501 n+=numread;
502 }
503 }
504
505 void AXFRRetriever::connect()
506 {
507 setNonBlocking( d_sock );
508
509 int err;
510
511 if((err=::connect(d_sock,(struct sockaddr*)&d_remote, d_remote.getSocklen()))<0 && errno!=EINPROGRESS) {
512 try {
513 closesocket(d_sock);
514 }
515 catch(const PDNSException& e) {
516 d_sock=-1;
517 throw ResolverException("Error closing AXFR socket after connect() failed: "+e.reason);
518 }
519
520 throw ResolverException("connect: "+stringerror());
521 }
522
523 if(!err)
524 goto done;
525
526 err=waitForRWData(d_sock, false, 10, 0); // wait for writeability
527
528 if(!err) {
529 try {
530 closesocket(d_sock); // timeout
531 }
532 catch(const PDNSException& e) {
533 d_sock=-1;
534 throw ResolverException("Error closing AXFR socket after timeout: "+e.reason);
535 }
536
537 d_sock=-1;
538 errno=ETIMEDOUT;
539
540 throw ResolverException("Timeout connecting to server");
541 }
542 else if(err < 0) {
543 throw ResolverException("Error connecting: "+string(strerror(err)));
544 }
545 else {
546 Utility::socklen_t len=sizeof(err);
547 if(getsockopt(d_sock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
548 throw ResolverException("Error connecting: "+stringerror()); // Solaris
549
550 if(err)
551 throw ResolverException("Error connecting: "+string(strerror(err)));
552 }
553
554 done:
555 setBlocking( d_sock );
556 // d_sock now connected
557 }
558
559 int AXFRRetriever::getLength(uint16_t timeout)
560 {
561 timeoutReadn(2, timeout);
562 return (unsigned char)d_buf[0]*256+(unsigned char)d_buf[1];
563 }
564