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