root/trunk/pdns/pdns/resolver.cc @ 151

Revision 151, 9.9 KB (checked in by ahu, 10 years ago)

lots

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1/*
2    PowerDNS Versatile Database Driven Nameserver
3    Copyright (C) 2002  PowerDNS.COM BV
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18*/
19#include "utility.hh"
20#include "resolver.hh"
21#include <pthread.h>
22#include <semaphore.h>
23#include <iostream>
24#include <errno.h>
25#include "misc.hh"
26#include <algorithm>
27#include <sstream>
28#include <cstring>
29#include <string>
30#include <vector>
31#include "dnspacket.hh"
32#include "dns.hh"
33#include "qtype.hh"
34#include "tcpreceiver.hh"
35#include "ahuexception.hh"
36#include "statbag.hh"
37#include "arguments.hh"
38
39void Resolver::makeUDPSocket()
40{
41  makeSocket(SOCK_DGRAM);
42}
43
44void Resolver::makeSocket(int type)
45{
46  static u_int16_t port_counter=5000;
47  if(d_sock>0)
48    return;
49
50  d_sock=socket(AF_INET, type,0);
51  if(d_sock<0) 
52    throw AhuException("Making a socket for resolver: "+stringerror());
53
54  struct sockaddr_in sin;
55  memset((char *)&sin,0, sizeof(sin));
56 
57  sin.sin_family = AF_INET;
58  sin.sin_addr.s_addr = INADDR_ANY;
59
60  int tries=10;
61  while(--tries) {
62    sin.sin_port = htons(10000+(port_counter++)%10000); // should be random!
63 
64    if (bind(d_sock, (struct sockaddr *)&sin, sizeof(sin)) >= 0) 
65      break;
66
67  }
68  if(!tries)
69    throw AhuException("Resolver binding to local socket: "+stringerror());
70}
71
72Resolver::Resolver()
73{
74  d_sock=-1;
75  d_timeout=500000;
76  d_soacount=0;
77  d_buf=new unsigned char[66000];
78}
79
80Resolver::~Resolver()
81{
82  if(d_sock>=0)
83    Utility::closesocket(d_sock);
84  delete[] d_buf;
85}
86
87void Resolver::timeoutReadn(char *buffer, int bytes)
88{
89  time_t start=time(0);
90  int n=0;
91  int numread;
92  while(n<bytes) {
93    if(waitForData(d_sock, 10-(time(0)-start))<0)
94      throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
95
96    numread=recv(d_sock,buffer+n,bytes-n,0);
97    if(numread<0)
98      throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
99    if(numread==0)
100      throw ResolverException("Remote nameserver closed TCP connection");
101    n+=numread;
102  }
103}
104
105char* Resolver::sendReceive(const string &ip, u_int16_t remotePort, const char *packet, int length, unsigned int *replen)
106{
107  makeTCPSocket(ip, remotePort);
108
109  if(sendData(packet,length,d_sock)<0) 
110    throw ResolverException("Unable to send packet to remote nameserver "+ip+": "+stringerror());
111
112  int plen=getLength();
113  if(plen<0)
114    throw ResolverException("EOF trying to get length of answer from remote TCP server");
115
116  char *answer=new char[plen];
117  try {
118    timeoutReadn(answer,plen);
119    *replen=plen;
120    return answer;
121  }
122  catch(...) {
123    delete answer;
124    throw; // whop!
125  }
126  return 0;
127}
128
129int Resolver::notify(int sock, const string &domain, const string &ip, u_int16_t id)
130{
131  DNSPacket p;
132  p.setQuestion(Opcode::Notify,domain,QType::SOA);
133  p.wrapup();
134  p.spoofID(id);
135
136  struct in_addr inp;
137  Utility::inet_aton(ip.c_str(),&inp);
138
139  struct sockaddr_in toaddr;
140  toaddr.sin_addr.s_addr=inp.s_addr;
141
142  toaddr.sin_port=htons(53);
143  toaddr.sin_family=AF_INET;
144
145  if(sendto(sock, p.getData(), p.len, 0, (struct sockaddr*)(&toaddr), sizeof(toaddr))<0) {
146    throw ResolverException("Unable to send notify to "+ip+": "+stringerror());
147  }
148  return true;
149}
150
151
152int Resolver::resolve(const string &ip, const char *domain, int type)
153{
154  makeUDPSocket();
155  DNSPacket p;
156
157  p.setQuestion(Opcode::Query,domain,type);
158  p.wrapup();
159
160  d_domain=domain;
161  d_type=type;
162  d_inaxfr=false;
163
164  struct sockaddr_in toaddr;
165  struct in_addr inp;
166  ServiceTuple st;
167  st.port=53;
168  parseService(ip, st);
169  Utility::inet_aton(st.host.c_str(),&inp);
170  toaddr.sin_addr.s_addr=inp.s_addr;
171
172  toaddr.sin_port=htons(st.port);
173  toaddr.sin_family=AF_INET;
174
175  if(sendto(d_sock, p.getData(), p.len, 0, (struct sockaddr*)(&toaddr), sizeof(toaddr))<0) {
176    throw ResolverException("Unable to ask query of "+st.host+":"+itoa(st.port)+": "+stringerror());
177  }
178
179  Utility::socklen_t addrlen=sizeof(toaddr);
180
181  fd_set rd;
182  FD_ZERO(&rd);
183  FD_SET(d_sock, &rd);
184
185  struct timeval timeout;
186  timeout.tv_sec=1;
187  timeout.tv_usec=500000;
188
189  int res=select(d_sock+1,&rd,0,0,&timeout);
190
191  if(!res)
192    throw ResolverException("Timeout waiting for answer from "+ip);
193  if(res<0)
194    throw ResolverException("Error waiting for answer: "+stringerror());
195
196
197  if((d_len=recvfrom(d_sock, reinterpret_cast< char * >( d_buf ), 512,0,(struct sockaddr*)(&toaddr), &addrlen))<0) 
198    throw ResolverException("recvfrom error waiting for answer: "+stringerror());
199
200  return 1;
201}
202
203void Resolver::makeTCPSocket(const string &ip, u_int16_t port)
204{
205  if(d_sock>=0)
206    return;
207  struct sockaddr_in toaddr;
208  struct in_addr inp;
209  Utility::inet_aton(ip.c_str(),&inp);
210  toaddr.sin_addr.s_addr=inp.s_addr;
211
212  toaddr.sin_port=htons(port);
213  toaddr.sin_family=AF_INET;
214 
215
216  d_sock=socket(AF_INET,SOCK_STREAM,0);
217  if(d_sock<0)
218    throw ResolverException("Unable to make a TCP socket for resolver: "+stringerror());
219 
220  Utility::setNonBlocking( d_sock );
221
222  int err;
223#ifndef WIN32
224  if((err=connect(d_sock,(struct sockaddr*)&toaddr,sizeof(toaddr)))<0 && errno!=EINPROGRESS) {
225#else
226  if((err=connect(d_sock,(struct sockaddr*)&toaddr,sizeof(toaddr)))<0 && WSAGetLastError() != WSAEWOULDBLOCK ) {
227#endif // WIN32
228    throw ResolverException("connect: "+stringerror());
229  }
230
231  if(!err)
232    goto done;
233
234  fd_set rset,wset;
235  struct timeval tval;
236
237  FD_ZERO(&rset);
238  FD_SET(d_sock, &rset);
239  wset=rset;
240  tval.tv_sec=10;
241  tval.tv_usec=0;
242
243  if(!select(d_sock+1,&rset,&wset,0,tval.tv_sec ? &tval : 0)) {
244    Utility::closesocket(d_sock); // timeout
245    d_sock=-1;
246    errno=ETIMEDOUT;
247   
248    throw ResolverException("Timeout connecting to server");
249  }
250 
251  if(FD_ISSET(d_sock, &rset) || FD_ISSET(d_sock, &wset))
252    {
253    Utility::socklen_t len=sizeof(err);
254      if(getsockopt(d_sock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
255        throw ResolverException("Error connecting: "+stringerror()); // Solaris
256
257      if(err)
258        throw ResolverException("Error connecting: "+string(strerror(err)));
259
260    }
261  else
262    throw ResolverException("nonblocking connect failed");
263
264 done:
265  Utility::setBlocking( d_sock );
266  // d_sock now connected
267}
268
269
270//! returns -1 for permanent error, 0 for timeout, 1 for success
271int Resolver::axfr(const string &ip, const char *domain)
272{
273  d_domain=domain;
274
275  makeTCPSocket(ip);
276
277  d_type=QType::AXFR;
278  DNSPacket p;
279  p.setQuestion(Opcode::Query,domain,QType::AXFR);
280  p.wrapup();
281
282  int replen=htons(p.len);
283  Utility::iovec iov[2];
284  iov[0].iov_base=(char*)&replen;
285  iov[0].iov_len=2;
286  iov[1].iov_base=(char*)p.getData();
287  iov[1].iov_len=p.len;
288
289  int ret=Utility::writev(d_sock,iov,2);
290  if(ret<0)
291    throw ResolverException("Error sending question to "+ip+": "+stringerror());
292
293  fd_set rd;
294  FD_ZERO(&rd);
295  FD_SET(d_sock, &rd);
296
297  struct timeval timeout;
298  timeout.tv_sec=10;
299  timeout.tv_usec=0;
300
301  int res=select(d_sock+1,&rd,0,0,&timeout);
302  if(!res)
303    throw ResolverException("Timeout waiting for answer from "+ip+" during AXFR");
304  if(res<0)
305    throw ResolverException("Error waiting for answer from "+ip+": "+stringerror());
306
307  d_soacount=0;
308  d_inaxfr=true;
309  return 1;
310}
311
312int Resolver::getLength()
313{
314  int bytesLeft=2;
315  unsigned char buf[2];
316 
317  while(bytesLeft) {
318    int ret=waitForData(d_sock, 10);
319    if(ret<0) {
320      Utility::closesocket(d_sock);
321      throw ResolverException("Waiting on data from remote TCP client: "+stringerror());
322    }
323 
324    ret=recv(d_sock, reinterpret_cast< char * >( buf ) +2-bytesLeft, bytesLeft,0);
325    if(ret<0)
326      throw ResolverException("Trying to read data from remote TCP client: "+stringerror());
327    if(!ret) 
328      return -1;
329   
330    bytesLeft-=ret;
331  }
332  return buf[0]*256+buf[1];
333}
334
335int Resolver::axfrChunk(Resolver::res_t &res)
336{
337  if(d_soacount>1) {
338    Utility::closesocket(d_sock);
339    d_sock=-1;
340    return 0;
341  }
342
343  // d_sock is connected and is about to spit out a packet
344  int len=getLength();
345  if(len<0)
346    throw ResolverException("EOF trying to read axfr chunk from remote TCP client");
347 
348  timeoutReadn((char *)d_buf,len); 
349  d_len=len;
350
351  res=result();
352  for(res_t::const_iterator i=res.begin();i!=res.end();++i)
353    if(i->qtype.getCode()==QType::SOA) {
354      d_soacount++;
355    }
356
357  if(d_soacount>1 && !res.empty()) // chop off the last SOA
358    res.resize(res.size()-1);
359
360  return 1;
361}
362
363
364Resolver::res_t Resolver::result()
365{
366  try {
367    DNSPacket p;
368   
369    if(p.parse((char *)d_buf, d_len)<0)
370      throw ResolverException("resolver: unable to parse packet of "+itoa(d_len)+" bytes");
371   
372    if(p.d.rcode)
373      if(d_inaxfr)
374        throw ResolverException("Remote nameserver unable/unwilling to AXFR with us: RCODE="+itoa(p.d.rcode));
375      else
376        throw ResolverException("Remote nameserver reported error: RCODE="+itoa(p.d.rcode));
377   
378    if(!d_inaxfr) {
379      if(ntohs(p.d.qdcount)!=1)
380        throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(ntohs(p.d.qdcount))+")");
381     
382      if(p.qdomain!=d_domain)
383        throw ResolverException(string("resolver: received an answer to another question (")+p.qdomain+"!="+d_domain+")");
384    }
385    return p.getAnswers();
386  }
387  catch(AhuException &ae) { // translate
388    throw ResolverException(ae.reason);
389  }
390}
391
392
393int Resolver::getSoaSerial(const string &ip, const string &domain, u_int32_t *serial)
394{
395  resolve(ip,domain.c_str(),QType::SOA);
396  res_t res=result();
397  if(res.empty())
398    return 0;
399 
400  vector<string>parts;
401  stringtok(parts,res[0].content);
402  if(parts.size()<3)
403    return 0;
404 
405  *serial=atoi(parts[2].c_str());
406  return 1;
407}
408
Note: See TracBrowser for help on using the browser.