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
23 #include "axfr-retriever.hh"
24 #include "arguments.hh"
25 #include "dns_random.hh"
26 #include "utility.hh"
27 #include "resolver.hh"
28 #include "query-local-address.hh"
29
30 using pdns::resolver::parseResult;
31
AXFRRetriever(const ComboAddress & remote,const DNSName & domain,const TSIGTriplet & tt,const ComboAddress * laddr,size_t maxReceivedBytes,uint16_t timeout)32 AXFRRetriever::AXFRRetriever(const ComboAddress& remote,
33 const DNSName& domain,
34 const TSIGTriplet& tt,
35 const ComboAddress* laddr,
36 size_t maxReceivedBytes,
37 uint16_t timeout)
38 : d_tsigVerifier(tt, remote, d_trc), d_receivedBytes(0), d_maxReceivedBytes(maxReceivedBytes)
39 {
40 ComboAddress local;
41 if (laddr != nullptr) {
42 local = ComboAddress(*laddr);
43 } else {
44 if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) {
45 throw ResolverException("Unable to determine source address for AXFR request to " + remote.toStringWithPort() + " for " + domain.toLogString() + ". Address family is not configured for outgoing queries");
46 }
47 local = pdns::getQueryLocalAddress(remote.sin4.sin_family, 0);
48 }
49 d_sock = -1;
50 try {
51 d_sock = makeQuerySocket(local, false); // make a TCP socket
52 if (d_sock < 0)
53 throw ResolverException("Error creating socket for AXFR request to "+d_remote.toStringWithPort());
54 d_buf = boost::shared_array<char>(new char[65536]);
55 d_remote = remote; // mostly for error reporting
56 this->connect(timeout);
57 d_soacount = 0;
58
59 vector<uint8_t> packet;
60 DNSPacketWriter pw(packet, domain, QType::AXFR);
61 pw.getHeader()->id = dns_random_uint16();
62
63 if(!tt.name.empty()) {
64 if (tt.algo == DNSName("hmac-md5"))
65 d_trc.d_algoName = tt.algo + DNSName("sig-alg.reg.int");
66 else
67 d_trc.d_algoName = tt.algo;
68 d_trc.d_time = time(nullptr);
69 d_trc.d_fudge = 300;
70 d_trc.d_origID=ntohs(pw.getHeader()->id);
71 d_trc.d_eRcode=0;
72 addTSIG(pw, d_trc, tt.name, tt.secret, "", false);
73 }
74
75 uint16_t replen=htons(packet.size());
76 Utility::iovec iov[2];
77 iov[0].iov_base=reinterpret_cast<char*>(&replen);
78 iov[0].iov_len=2;
79 iov[1].iov_base=packet.data();
80 iov[1].iov_len=packet.size();
81
82 int ret=Utility::writev(d_sock, iov, 2);
83 if(ret < 0)
84 throw ResolverException("Error sending question to "+d_remote.toStringWithPort()+": "+stringerror());
85 if(ret != (int)(2+packet.size())) {
86 throw ResolverException("Partial write on AXFR request to "+d_remote.toStringWithPort());
87 }
88
89 int res = waitForData(d_sock, timeout, 0);
90
91 if(!res)
92 throw ResolverException("Timeout waiting for answer from "+d_remote.toStringWithPort()+" during AXFR");
93 if(res<0)
94 throw ResolverException("Error waiting for answer from "+d_remote.toStringWithPort()+": "+stringerror());
95 }
96 catch(...) {
97 if(d_sock >= 0)
98 close(d_sock);
99 d_sock = -1;
100 throw;
101 }
102 }
103
~AXFRRetriever()104 AXFRRetriever::~AXFRRetriever()
105 {
106 close(d_sock);
107 }
108
109
110
getChunk(Resolver::res_t & res,vector<DNSRecord> * records,uint16_t timeout)111 int AXFRRetriever::getChunk(Resolver::res_t &res, vector<DNSRecord>* records, uint16_t timeout) // Implementation is making sure RFC2845 4.4 is followed.
112 {
113 if(d_soacount > 1)
114 return false;
115
116 // d_sock is connected and is about to spit out a packet
117 int len=getLength(timeout);
118 if(len<0)
119 throw ResolverException("EOF trying to read axfr chunk from remote TCP client");
120
121 if (d_maxReceivedBytes > 0 && (d_maxReceivedBytes - d_receivedBytes) < (size_t) len)
122 throw ResolverException("Reached the maximum number of received bytes during AXFR");
123
124 timeoutReadn(len, timeout);
125
126 d_receivedBytes += (uint16_t) len;
127
128 MOADNSParser mdp(false, d_buf.get(), len);
129
130 int err = mdp.d_header.rcode;
131
132 if(err) {
133 throw ResolverException("AXFR chunk error: " + RCode::to_s(err));
134 }
135
136 try {
137 d_tsigVerifier.check(std::string(d_buf.get(), len), mdp);
138 }
139 catch(const std::runtime_error& re) {
140 throw ResolverException(re.what());
141 }
142
143 if(!records) {
144 err = parseResult(mdp, DNSName(), 0, 0, &res);
145
146 if (!err) {
147 for(const auto& answer : mdp.d_answers)
148 if (answer.first.d_type == QType::SOA)
149 d_soacount++;
150 }
151 }
152 else {
153 records->clear();
154 records->reserve(mdp.d_answers.size());
155
156 for(auto& r: mdp.d_answers) {
157 if (r.first.d_type == QType::SOA) {
158 d_soacount++;
159 }
160
161 records->push_back(std::move(r.first));
162 }
163 }
164
165 return true;
166 }
167
timeoutReadn(uint16_t bytes,uint16_t timeoutsec)168 void AXFRRetriever::timeoutReadn(uint16_t bytes, uint16_t timeoutsec)
169 {
170 time_t start=time(nullptr);
171 int n=0;
172 int numread;
173 while(n<bytes) {
174 int res=waitForData(d_sock, timeoutsec-(time(nullptr)-start));
175 if(res<0)
176 throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
177 if(!res)
178 throw ResolverException("Timeout while reading data from remote nameserver over TCP");
179
180 numread=recv(d_sock, d_buf.get()+n, bytes-n, 0);
181 if(numread<0)
182 throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
183 if(numread==0)
184 throw ResolverException("Remote nameserver closed TCP connection");
185 n+=numread;
186 }
187 }
188
connect(uint16_t timeout)189 void AXFRRetriever::connect(uint16_t timeout)
190 {
191 setNonBlocking( d_sock );
192
193 int err;
194
195 if((err=::connect(d_sock,(struct sockaddr*)&d_remote, d_remote.getSocklen()))<0 && errno!=EINPROGRESS) {
196 try {
197 closesocket(d_sock);
198 }
199 catch(const PDNSException& e) {
200 d_sock=-1;
201 throw ResolverException("Error closing AXFR socket after connect() failed: "+e.reason);
202 }
203
204 throw ResolverException("connect: "+stringerror());
205 }
206
207 if(!err)
208 goto done;
209
210 err=waitForRWData(d_sock, false, timeout, 0); // wait for writeability
211
212 if(!err) {
213 try {
214 closesocket(d_sock); // timeout
215 }
216 catch(const PDNSException& e) {
217 d_sock=-1;
218 throw ResolverException("Error closing AXFR socket after timeout: "+e.reason);
219 }
220
221 d_sock=-1;
222 errno=ETIMEDOUT;
223
224 throw ResolverException("Timeout connecting to server");
225 }
226 else if(err < 0) {
227 throw ResolverException("Error connecting: "+stringerror());
228 }
229 else {
230 Utility::socklen_t len=sizeof(err);
231 if(getsockopt(d_sock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
232 throw ResolverException("Error connecting: "+stringerror()); // Solaris
233
234 if(err)
235 throw ResolverException("Error connecting: "+string(strerror(err)));
236 }
237
238 done:
239 setBlocking( d_sock );
240 // d_sock now connected
241 }
242
getLength(uint16_t timeout)243 int AXFRRetriever::getLength(uint16_t timeout)
244 {
245 timeoutReadn(2, timeout);
246 return (unsigned char)d_buf[0]*256+(unsigned char)d_buf[1];
247 }
248
249