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