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