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

Revision 116, 9.8 KB (checked in by ahu, 10 years ago)

umf

  • 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  p.setQuestion(Opcode::Query,domain,type);
157  p.wrapup();
158
159  d_domain=domain;
160  d_type=type;
161  d_inaxfr=false;
162
163  struct sockaddr_in toaddr;
164  struct in_addr inp;
165  Utility::inet_aton(ip.c_str(),&inp);
166  toaddr.sin_addr.s_addr=inp.s_addr;
167
168  toaddr.sin_port=htons(53);
169  toaddr.sin_family=AF_INET;
170
171  if(sendto(d_sock, p.getData(), p.len, 0, (struct sockaddr*)(&toaddr), sizeof(toaddr))<0) {
172    throw ResolverException("Unable to ask query of "+ip+": "+stringerror());
173  }
174
175  Utility::socklen_t addrlen=sizeof(toaddr);
176
177  fd_set rd;
178  FD_ZERO(&rd);
179  FD_SET(d_sock, &rd);
180
181  struct timeval timeout;
182  timeout.tv_sec=1;
183  timeout.tv_usec=500000;
184
185  int res=select(d_sock+1,&rd,0,0,&timeout);
186  if(!res)
187    throw ResolverException("Timeout waiting for answer from "+ip);
188  if(res<0)
189    throw ResolverException("Error waiting for answer: "+stringerror());
190
191
192  if((d_len=recvfrom(d_sock, reinterpret_cast< char * >( d_buf ), 512,0,(struct sockaddr*)(&toaddr), &addrlen))<0) 
193    throw ResolverException("recvfrom error waiting for answer: "+stringerror());
194
195  return 1;
196}
197
198void Resolver::makeTCPSocket(const string &ip, u_int16_t port)
199{
200  if(d_sock>=0)
201    return;
202  struct sockaddr_in toaddr;
203  struct in_addr inp;
204  Utility::inet_aton(ip.c_str(),&inp);
205  toaddr.sin_addr.s_addr=inp.s_addr;
206
207  toaddr.sin_port=htons(port);
208  toaddr.sin_family=AF_INET;
209 
210
211  d_sock=socket(AF_INET,SOCK_STREAM,0);
212  if(d_sock<0)
213    throw ResolverException("Unable to make a TCP socket for resolver: "+stringerror());
214 
215  Utility::setNonBlocking( d_sock );
216
217  int err;
218#ifndef WIN32
219  if((err=connect(d_sock,(struct sockaddr*)&toaddr,sizeof(toaddr)))<0 && errno!=EINPROGRESS) {
220#else
221  if((err=connect(d_sock,(struct sockaddr*)&toaddr,sizeof(toaddr)))<0 && WSAGetLastError() != WSAEWOULDBLOCK ) {
222#endif // WIN32
223    throw ResolverException("connect: "+stringerror());
224  }
225
226  if(!err)
227    goto done;
228
229  fd_set rset,wset;
230  struct timeval tval;
231
232  FD_ZERO(&rset);
233  FD_SET(d_sock, &rset);
234  wset=rset;
235  tval.tv_sec=10;
236  tval.tv_usec=0;
237
238  if(!select(d_sock+1,&rset,&wset,0,tval.tv_sec ? &tval : 0)) {
239    Utility::closesocket(d_sock); // timeout
240    d_sock=-1;
241    errno=ETIMEDOUT;
242   
243    throw ResolverException("Timeout connecting to server");
244  }
245 
246  if(FD_ISSET(d_sock, &rset) || FD_ISSET(d_sock, &wset))
247    {
248    Utility::socklen_t len=sizeof(err);
249      if(getsockopt(d_sock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
250        throw ResolverException("Error connecting: "+stringerror()); // Solaris
251
252      if(err)
253        throw ResolverException("Error connecting: "+string(strerror(err)));
254
255    }
256  else
257    throw ResolverException("nonblocking connect failed");
258
259 done:
260  Utility::setBlocking( d_sock );
261  // d_sock now connected
262}
263
264
265//! returns -1 for permanent error, 0 for timeout, 1 for success
266int Resolver::axfr(const string &ip, const char *domain)
267{
268  d_domain=domain;
269
270  makeTCPSocket(ip);
271
272  d_type=QType::AXFR;
273  DNSPacket p;
274  p.setQuestion(Opcode::Query,domain,QType::AXFR);
275  p.wrapup();
276
277  int replen=htons(p.len);
278  Utility::iovec iov[2];
279  iov[0].iov_base=(char*)&replen;
280  iov[0].iov_len=2;
281  iov[1].iov_base=(char*)p.getData();
282  iov[1].iov_len=p.len;
283
284  int ret=Utility::writev(d_sock,iov,2);
285  if(ret<0)
286    throw ResolverException("Error sending question to "+ip+": "+stringerror());
287
288  fd_set rd;
289  FD_ZERO(&rd);
290  FD_SET(d_sock, &rd);
291
292  struct timeval timeout;
293  timeout.tv_sec=10;
294  timeout.tv_usec=0;
295
296  int res=select(d_sock+1,&rd,0,0,&timeout);
297  if(!res)
298    throw ResolverException("Timeout waiting for answer from "+ip+" during AXFR");
299  if(res<0)
300    throw ResolverException("Error waiting for answer from "+ip+": "+stringerror());
301
302  d_soacount=0;
303  d_inaxfr=true;
304  return 1;
305}
306
307int Resolver::getLength()
308{
309  int bytesLeft=2;
310  unsigned char buf[2];
311 
312  while(bytesLeft) {
313    int ret=waitForData(d_sock, 10);
314    if(ret<0) {
315      Utility::closesocket(d_sock);
316      throw ResolverException("Waiting on data from remote TCP client: "+stringerror());
317    }
318 
319    ret=recv(d_sock, reinterpret_cast< char * >( buf ) +2-bytesLeft, bytesLeft,0);
320    if(ret<0)
321      throw ResolverException("Trying to read data from remote TCP client: "+stringerror());
322    if(!ret) 
323      return -1;
324   
325    bytesLeft-=ret;
326  }
327  return buf[0]*256+buf[1];
328}
329
330int Resolver::axfrChunk(Resolver::res_t &res)
331{
332  if(d_soacount>1) {
333    Utility::closesocket(d_sock);
334    d_sock=-1;
335    return 0;
336  }
337
338  // d_sock is connected and is about to spit out a packet
339  int len=getLength();
340  if(len<0)
341    throw ResolverException("EOF trying to read axfr chunk from remote TCP client");
342 
343  timeoutReadn((char *)d_buf,len); 
344  d_len=len;
345
346  res=result();
347  for(res_t::const_iterator i=res.begin();i!=res.end();++i)
348    if(i->qtype.getCode()==QType::SOA) {
349      d_soacount++;
350    }
351
352  if(d_soacount>1 && !res.empty()) // chop off the last SOA
353    res.resize(res.size()-1);
354   
355
356  return 1;
357}
358
359
360Resolver::res_t Resolver::result()
361{
362  try {
363    DNSPacket p;
364   
365    if(p.parse((char *)d_buf, d_len)<0)
366      throw ResolverException("resolver: unable to parse packet of "+itoa(d_len)+" bytes");
367   
368    if(p.d.rcode)
369      if(d_inaxfr)
370        throw ResolverException("Remote nameserver unable/unwilling to AXFR with us: RCODE="+itoa(p.d.rcode));
371      else
372        throw ResolverException("Remote nameserver reported error: RCODE="+itoa(p.d.rcode));
373   
374    if(!d_inaxfr) {
375      if(ntohs(p.d.qdcount)!=1)
376        throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(ntohs(p.d.qdcount))+")");
377     
378      if(p.qdomain!=d_domain)
379        throw ResolverException(string("resolver: received an answer to another question (")+p.qdomain+"!="+d_domain+")");
380    }
381    return p.getAnswers();
382  }
383  catch(AhuException &ae) { // translate
384    throw ResolverException(ae.reason);
385  }
386}
387
388
389int Resolver::getSoaSerial(const string &ip, const string &domain, u_int32_t *serial)
390{
391  resolve(ip,domain.c_str(),QType::SOA);
392  res_t res=result();
393  if(res.empty())
394    return 0;
395 
396  vector<string>parts;
397  stringtok(parts,res[0].content);
398  if(parts.size()<3)
399    return 0;
400 
401  *serial=atoi(parts[2].c_str());
402  return 1;
403}
404
Note: See TracBrowser for help on using the browser.