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