]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
this giant commit adds full IPv6 master/slaving/notification support which appeared...
authorBert Hubert <bert.hubert@netherlabs.nl>
Mon, 14 Feb 2011 10:56:27 +0000 (10:56 +0000)
committerBert Hubert <bert.hubert@netherlabs.nl>
Mon, 14 Feb 2011 10:56:27 +0000 (10:56 +0000)
In addition, the complete AXFR & outgoing resolver apparatus of the auth server has been revamped, removing some of the oldest code in PowerDNS.
This is a giant change, but it adds functionality while decreasing the size of the code.

git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@2012 d19b8d6e-7fed-0310-83ef-9ca221ded41b

pdns/communicator.cc
pdns/communicator.hh
pdns/mastercommunicator.cc
pdns/packethandler.cc
pdns/resolver.cc
pdns/resolver.hh
pdns/slavecommunicator.cc

index 59adf0497397a43bbfd773a6cb33951d6c65abbe..fd92f7994686ca34d10f4f65b358fa87544c7c0f 100644 (file)
@@ -75,7 +75,7 @@ void CommunicatorClass::mainloop(void)
     L<<Logger::Error<<"Master/slave communicator launching"<<endl;
     PacketHandler P;
     d_tickinterval=::arg().asNum("slave-cycle-interval");
-    makeNotifySocket();
+    makeNotifySockets();
 
     int rc;
     time_t next;
index a21d7557c933a4a1d8ae396512f9c71992ec0792..a0583efcd99aaf28f9c6a36caf5c19af86a352e2 100644 (file)
@@ -170,9 +170,9 @@ public:
   }
   bool notifyDomain(const string &domain);
 private:
-  void makeNotifySocket();
+  void makeNotifySockets();
   void queueNotifyDomain(const string &domain, DNSBackend *B);
-  int d_nsock;
+  int d_nsock4, d_nsock6;
   map<pair<string,string>,time_t>d_holes;
   pthread_mutex_t d_holelock;
   void launchRetrievalThreads();
index d9294453afc95c5ec347109b795aba81372031d7..7c17b85bc8d044a58fcb4b599ee0ab81486e9f2d 100644 (file)
@@ -156,10 +156,12 @@ time_t CommunicatorClass::doNotifications()
   Utility::socklen_t fromlen=sizeof(from);
   char buffer[1500];
   int size;
-  static Resolver d_nresolver;
   // receive incoming notifications on the nonblocking socket and take them off the list
-
-  while((size=recvfrom(d_nsock,buffer,sizeof(buffer),0,(struct sockaddr *)&from,&fromlen))>0) {
+  int sock;
+  while(waitFor2Data(d_nsock4, d_nsock6, 1, 0, &sock) > 0) {
+    size=recvfrom(sock,buffer,sizeof(buffer),0,(struct sockaddr *)&from,&fromlen);
+    if(size < 0)
+      break;
     DNSPacket p;
 
     p.setRemote(&from);
@@ -186,7 +188,9 @@ time_t CommunicatorClass::doNotifications()
   while(d_nq.getOne(domain, ip, &id, purged)) {
     if(!purged) {
       try {
-        d_nresolver.notify(d_nsock, domain, ip, id);
+        ComboAddress remote(ip, 53); // default to 53
+        //cerr<<"Going to send to "<<remote.toStringWithPort()<<endl;
+        sendNotification(remote.sin4.sin_family == AF_INET ? d_nsock4 : d_nsock6, domain, remote, id); // XXX FIXME this won't deal with IPv6 since nsock is IPv4!
         drillHole(domain, ip);
       }
       catch(ResolverException &re) {
@@ -219,49 +223,14 @@ bool CommunicatorClass::justNotified(const string &domain, const string &ip)
   return false;
 }
 
-void CommunicatorClass::makeNotifySocket()
+void CommunicatorClass::makeNotifySockets()
 {
-  if((d_nsock=socket(AF_INET, SOCK_DGRAM,0))<0)
-    throw AhuException(string("notification socket: ")+strerror(errno));
-
-  struct sockaddr_in sin;
-  memset((char *)&sin,0, sizeof(sin));
-  
-  sin.sin_family = AF_INET;
-
-  // Bind to a specific IP (query-local-address) if specified
-  string querylocaladdress(::arg()["query-local-address"]);
-  if (querylocaladdress=="") {
-    sin.sin_addr.s_addr = INADDR_ANY;
-  }
+  d_nsock4 = makeQuerySocket(ComboAddress(::arg()["query-local-address"]), true);
+  if(::arg().parmIsset("query-local-address6"))
+    d_nsock6 = makeQuerySocket(ComboAddress(::arg()["query-local-address6"]), true);
   else
-  {
-    struct hostent *h=0;
-    h=gethostbyname(querylocaladdress.c_str());
-    if(!h) {
-      Utility::closesocket(d_nsock);
-      d_nsock=-1;        
-      throw AhuException("Unable to resolve query local address");
-    }
-
-    sin.sin_addr.s_addr = *(int*)h->h_addr;
-  }
-  
-  int n=0;
-  for(;n<10;n++) {
-    sin.sin_port = htons(10000+(Utility::random()%50000));
-    
-    if(::bind(d_nsock, (struct sockaddr *)&sin, sizeof(sin)) >= 0) 
-      break;
-  }
-  if(n==10) {
-    Utility::closesocket(d_nsock);
-    d_nsock=-1;
-    throw AhuException(string("binding notify socket: ")+strerror(errno));
-  }
-  if( !Utility::setNonBlocking( d_nsock ))
-    throw AhuException(string("error getting or setting notify socket non-blocking: ")+strerror(errno));
-
+    d_nsock6 = -1;
+  cerr<<"socks: "<<d_nsock4<<", "<<d_nsock6;
 }
 
 void CommunicatorClass::notify(const string &domain, const string &ip)
index 4a2478f2724f2e2a9356095560ccd48ef76fc275..65c2bcc69085bf62bbe9d80661964c9dc55a4cea 100644 (file)
@@ -815,11 +815,8 @@ int PacketHandler::trySuperMaster(DNSPacket *p)
   try {
     Resolver resolver;
     uint32_t theirserial;
-    resolver.getSoaSerial(p->getRemote(),p->qdomain, &theirserial);  
-  
-    resolver.resolve(p->getRemote(),p->qdomain.c_str(), QType::NS);
-
-    nsset=resolver.result();
+    resolver.getSoaSerial(p->getRemote(),p->qdomain, &theirserial);    
+    resolver.resolve(p->getRemote(), p->qdomain.c_str(), QType::NS, &nsset);
   }
   catch(ResolverException &re) {
     L<<Logger::Error<<"Error resolving SOA or NS at: "<< p->getRemote() <<": "<<re.reason<<endl;
index 5a81e7261c3b2b39645a3242b9316f4a332eb8be..55a782f91b08acf4eeef798f79fccce46c50de66 100644 (file)
@@ -1,6 +1,6 @@
 /*
     PowerDNS Versatile Database Driven Nameserver
-    Copyright (C) 2002 - 2009 PowerDNS.COM BV
+    Copyright (C) 2002 - 2011 PowerDNS.COM BV
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License version 2 as 
 
 #include "namespaces.hh"
 
-void Resolver::makeUDPSocket()
+int sendNotification(int sock, const string& domain, const ComboAddress& remote, uint16_t id)
 {
-  makeSocket(SOCK_DGRAM);
+  vector<uint8_t> packet;
+  DNSPacketWriter pw(packet, domain, QType::SOA, 1, Opcode::Notify);
+  pw.getHeader()->id = id;
+  pw.getHeader()->aa = true; 
+
+  if(sendto(sock, &packet[0], packet.size(), 0, (struct sockaddr*)(&remote), remote.getSocklen())<0) {
+    throw ResolverException("Unable to send notify to "+remote.toStringWithPort()+": "+stringerror());
+  }
+  return true;
 }
 
-void Resolver::makeSocket(int type)
+int makeQuerySocket(const ComboAddress& local, bool udpOrTCP)
 {
+  ComboAddress ourLocal(local);
   static uint16_t port_counter=5000;
   port_counter++; // this makes us use a new port for each query, fixes ticket #2
-  if(d_sock>0)
-    return;
-
-  d_sock=socket(AF_INET, type,0);
-  if(d_sock<0) 
-    throw AhuException("Making a socket for resolver: "+stringerror());
-
-  struct sockaddr_in sin;
-  memset((char *)&sin,0, sizeof(sin));
-  
-  sin.sin_family = AF_INET;
-  if(!IpToU32(::arg()["query-local-address"], &sin.sin_addr.s_addr))
-    throw AhuException("Unable to resolve local address '"+ ::arg()["query-local-address"] +"'"); 
-
-  int tries=10;
-  while(--tries) {
-    sin.sin_port = htons(10000+(dns_random(10000)));
   
-    if (::bind(d_sock, (struct sockaddr *)&sin, sizeof(sin)) >= 0) 
-      break;
-
+  int sock=socket(ourLocal.sin4.sin_family, udpOrTCP ? SOCK_DGRAM : SOCK_STREAM, 0);
+  if(sock < 0)
+    unixDie("Creating local resolver socket for "+ourLocal.toString());
+
+  if(!udpOrTCP) {
+    int tries=10;
+    while(--tries) {
+      ourLocal.sin4.sin_port = htons(10000+(dns_random(10000)));
+    
+      if (::bind(sock, (struct sockaddr *)&ourLocal, ourLocal.getSocklen()) >= 0) 
+        break;
+    }
+    if(!tries) {
+      Utility::closesocket(sock);
+      throw AhuException("Resolver binding to local socket: "+stringerror());
+    }
   }
-  if(!tries) {
-    Utility::closesocket(d_sock);
-    d_sock=-1;
-    throw AhuException("Resolver binding to local socket: "+stringerror());
+  else {
+    ourLocal.sin4.sin_port = 0;
+    if(::bind(sock, (struct sockaddr *)&ourLocal, ourLocal.getSocklen()) < 0)
+      throw AhuException("Resolver binding to local socket: "+stringerror());
   }
+  return sock;
 }
 
 Resolver::Resolver()
+try
 {
-  d_sock=-1;
+  d_sock4 = d_sock6 = 0;
+  d_sock4 = makeQuerySocket(ComboAddress(::arg()["query-local-address"]), true);
+  if(::arg().parmIsset("query-local-address6"))
+    d_sock6 = makeQuerySocket(ComboAddress(::arg()["query-local-address6"]), true);
+  else 
+    d_sock6 = -1;
   d_timeout=500000;
-  d_soacount=0;
-  d_buf=new unsigned char[66000];
 }
-
-Resolver::~Resolver()
-{
-  if(d_sock>=0)
-    Utility::closesocket(d_sock);
-  delete[] d_buf;
+catch(...) {
+  if(d_sock4>=0)
+    close(d_sock4);
+  throw;
 }
 
-void Resolver::timeoutReadn(char *buffer, int bytes)
+Resolver::~Resolver()
 {
-  time_t start=time(0);
-  int n=0;
-  int numread;
-  while(n<bytes) {
-    if(waitForData(d_sock, 10-(time(0)-start))<0)
-      throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
-
-    numread=recv(d_sock,buffer+n,bytes-n,0);
-    if(numread<0)
-      throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
-    if(numread==0)
-      throw ResolverException("Remote nameserver closed TCP connection");
-    n+=numread;
-  }
+  if(d_sock4>=0)
+    Utility::closesocket(d_sock4);
+  if(d_sock6>=0)
+    Utility::closesocket(d_sock6);
 }
 
-int Resolver::notify(int sock, const string &domain, const string &ip, uint16_t id)
-{
-  vector<uint8_t> packet;
-  DNSPacketWriter pw(packet, domain, QType::SOA, 1, Opcode::Notify);
-  pw.getHeader()->id = d_randomid = id;
-  pw.getHeader()->aa = true; 
-  
-  ComboAddress dest(ip, 53);
 
-  if(sendto(sock, &packet[0], packet.size(), 0, (struct sockaddr*)(&dest), dest.getSocklen())<0) {
-    throw ResolverException("Unable to send notify to "+ip+": "+stringerror());
-  }
-  return true;
-}
-
-uint16_t Resolver::sendResolve(const string &ip, const char *domain, int type, bool dnssecOK)
+uint16_t Resolver::sendResolve(const ComboAddress& remote, const char *domain, int type, bool dnssecOK)
 {
   vector<uint8_t> packet;
   DNSPacketWriter pw(packet, domain, type);
@@ -140,39 +124,89 @@ uint16_t Resolver::sendResolve(const string &ip, const char *domain, int type, b
     pw.addOpt(2800, 0, EDNSOpts::DNSSECOK);
     pw.commit();
   }
+    
+  int sock = remote.sin4.sin_family == AF_INET ? d_sock4 : d_sock6;
+  
+  if(sendto(sock, &packet[0], packet.size(), 0, (struct sockaddr*)(&remote), remote.getSocklen()) < 0) {
+    throw ResolverException("Unable to ask query of "+remote.toStringWithPort()+": "+stringerror());
+  }
+  return d_randomid;
+}
 
-  d_domain=domain;
-  d_type=type;
-  d_inaxfr=false;
-
-  ServiceTuple st;
-  st.port=53;
+static int parseResult(const char* buffer, unsigned int len, const std::string& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result)
+{
+  result->clear();
+  shared_ptr<MOADNSParser> mdp;
+  
   try {
-    parseService(ip, st);
+    mdp=shared_ptr<MOADNSParser>(new MOADNSParser(buffer, len));
   }
-  catch(AhuException &ae) {
-    throw ResolverException("Sending a dns question to '"+ip+"': "+ae.reason);
+  catch(...) {
+    throw ResolverException("resolver: unable to parse packet of "+itoa(len)+" bytes");
   }
-
-  ComboAddress remote(st.host, st.port);
-
-  if(sendto(d_sock, &packet[0], packet.size(), 0, (struct sockaddr*)(&remote), remote.getSocklen()) < 0) {
-    throw ResolverException("Unable to ask query of "+st.host+":"+itoa(st.port)+": "+stringerror());
+  
+  if(mdp->d_header.rcode) 
+    return mdp->d_header.rcode;
+      
+  if(!origQname.empty()) {  // not AXFR
+    if(mdp->d_header.id != id) 
+      throw ResolverException("Remote nameserver replied with wrong id");
+    if(mdp->d_header.qdcount != 1)
+      throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(mdp->d_header.qdcount)+")");
+    if(mdp->d_qname != origQname+".")
+      throw ResolverException(string("resolver: received an answer to another question (")+mdp->d_qname+"!="+ origQname+".)");
   }
-  return d_randomid;
+    
+  vector<DNSResourceRecord> ret; 
+  DNSResourceRecord rr;
+  for(MOADNSParser::answers_t::const_iterator i=mdp->d_answers.begin(); i!=mdp->d_answers.end(); ++i) {          
+    rr.qname = i->first.d_label;
+    if(!rr.qname.empty())
+      boost::erase_tail(rr.qname, 1); // strip .
+    rr.qtype = i->first.d_type;
+    rr.ttl = i->first.d_ttl;
+    rr.content = i->first.d_content->getZoneRepresentation();
+    rr.priority = 0;
+    
+    uint16_t qtype=rr.qtype.getCode();
+
+    if(!rr.content.empty() && (qtype==QType::MX || qtype==QType::NS || qtype==QType::CNAME))
+      boost::erase_tail(rr.content, 1);
+
+    if(rr.qtype.getCode() == QType::MX) {
+      vector<string> parts;
+      stringtok(parts, rr.content);
+      rr.priority = atoi(parts[0].c_str());
+      if(parts.size() > 1)
+        rr.content=parts[1];
+    } else if(rr.qtype.getCode() == QType::SRV) {
+      rr.priority = atoi(rr.content.c_str());
+      vector<pair<string::size_type, string::size_type> > fields;
+      vstringtok(fields, rr.content, " ");
+      if(fields.size()==4)
+        rr.content=string(rr.content.c_str() + fields[1].first, fields[3].second - fields[1].first);
+    }
+    result->push_back(rr);
+  }
+  
+  return 0;
 }
 
+
 bool Resolver::tryGetSOASerial(string* domain, uint32_t *theirSerial, uint32_t *theirInception, uint32_t *theirExpire, uint16_t* id)
 {
-  Utility::setNonBlocking( d_sock );
+  Utility::setNonBlocking( d_sock4 );
+  Utility::setNonBlocking( d_sock6 );
   
-  if(waitForData(d_sock, 0, 500000) == 0)
+  int sock;
+  if(!waitFor2Data(d_sock4, d_sock6, 0, 250000, &sock)) // lame function, I know.. 
     return false;
   
   int err;
   ComboAddress fromaddr;
   socklen_t addrlen=fromaddr.getSocklen();
-  err = recvfrom(d_sock, reinterpret_cast< char * >( d_buf ), 512, 0,(struct sockaddr*)(&fromaddr), &addrlen);
+  char buf[3000];
+  err = recvfrom(sock, buf, sizeof(buf), 0,(struct sockaddr*)(&fromaddr), &addrlen);
   if(err < 0) {
     if(errno == EAGAIN)
       return false;
@@ -180,7 +214,7 @@ bool Resolver::tryGetSOASerial(string* domain, uint32_t *theirSerial, uint32_t *
     throw ResolverException("recvfrom error waiting for answer: "+stringerror());
   }
   
-  MOADNSParser mdp((char*)d_buf, err);
+  MOADNSParser mdp((char*)buf, err);
   *id=mdp.d_header.id;
   *domain = stripDot(mdp.d_qname);
   
@@ -209,123 +243,77 @@ bool Resolver::tryGetSOASerial(string* domain, uint32_t *theirSerial, uint32_t *
   return true;
 }
 
-int Resolver::receiveResolve(struct sockaddr* fromaddr, Utility::socklen_t addrlen)
+int Resolver::resolve(const string &ipport, const char *domain, int type, Resolver::res_t* res)
 {
-  int res=waitForData(d_sock, 0, 7500000); 
-  
-  if(!res) {
-    throw ResolverException("Timeout waiting for answer");
-  }
-  if(res<0)
-    throw ResolverException("Error waiting for answer: "+stringerror());
-
-
-  if((d_len=recvfrom(d_sock, reinterpret_cast< char * >( d_buf ), 512,0,(struct sockaddr*)(fromaddr), &addrlen))<0) 
-    throw ResolverException("recvfrom error waiting for answer: "+stringerror());
-
-  return 1;
-}
-  
-int Resolver::resolve(const string &ipport, const char *domain, int type)
-{
-  makeUDPSocket();
-  sendResolve(ipport, domain, type);
   try {
-    ServiceTuple st;
-    st.port = 53;   
-    parseService(ipport, st);
-    ComboAddress from(st.host, st.port);
+    ComboAddress to(ipport, 53);
 
-    return receiveResolve((sockaddr*)&from, from.getSocklen());
+    int id = sendResolve(to, domain, type);
+    int sock =  to.sin4.sin_family == AF_INET ? d_sock4 : d_sock6;
+    int err=waitForData(sock, 0, 7500000); 
+  
+    if(!err) {
+      throw ResolverException("Timeout waiting for answer");
+    }
+    if(err < 0)
+      throw ResolverException("Error waiting for answer: "+stringerror());
+  
+    ComboAddress from;
+    socklen_t addrlen = sizeof(from);
+    char buffer[3000];
+    int len;
+    
+    if((len=recvfrom(sock, buffer, sizeof(buffer), 0,(struct sockaddr*)(&from), &addrlen)) < 0) 
+      throw ResolverException("recvfrom error waiting for answer: "+stringerror());
+  
+    return parseResult(buffer, len, domain, type, id, res);
   }
   catch(ResolverException &re) {
     throw ResolverException(re.reason+" from "+ipport);
   }
-  return 1;
-  
+  return -1;
 }
 
-void Resolver::makeTCPSocket(const string &ip, uint16_t port)
-{
-  if(d_sock>=0)
-    return;
-
-  d_toaddr=ComboAddress(ip, port);
-
-  d_sock=socket(AF_INET,SOCK_STREAM,0);
-  if(d_sock<0)
-    throw ResolverException("Unable to make a TCP socket for resolver: "+stringerror());
-
-  // Use query-local-address as source IP for queries, if specified.
-  string querylocaladdress(::arg()["query-local-address"]);
-  if (!querylocaladdress.empty()) {
-    ComboAddress fromaddr(querylocaladdress, 0);
-
-    if (::bind(d_sock, (struct sockaddr *)&fromaddr, fromaddr.getSocklen()) < 0) {
-      Utility::closesocket(d_sock);
-      d_sock=-1;
-      throw ResolverException("Binding to query-local-address: "+stringerror());
-    }
-  }  
-    
-  Utility::setNonBlocking( d_sock );
-
-  int err;
-#ifndef WIN32
-  if((err=connect(d_sock,(struct sockaddr*)&d_toaddr, d_toaddr.getSocklen()))<0 && errno!=EINPROGRESS) {
-#else
-  if((err=connect(d_sock,(struct sockaddr*)&d_toaddr, d_toaddr.getSocklen()))<0 && WSAGetLastError() != WSAEWOULDBLOCK ) {
-#endif // WIN32
-    Utility::closesocket(d_sock);
-    d_sock=-1;
-    throw ResolverException("connect: "+stringerror());
-  }
 
-  if(!err)
-    goto done;
 
-  err=waitForRWData(d_sock, false, 10, 0); // wait for writeability
+void Resolver::getSoaSerial(const string &ipport, const string &domain, uint32_t *serial)
+{
+  vector<DNSResourceRecord> res;
+  int ret = resolve(ipport, domain.c_str(), QType::SOA, &res);
   
-  if(!err) {
-    Utility::closesocket(d_sock); // timeout
-    d_sock=-1;
-    errno=ETIMEDOUT;
-    
-    throw ResolverException("Timeout connecting to server");
-  }
-  else if(err < 0) {
-    throw ResolverException("Error connecting: "+string(strerror(err)));
-  }
-  else {
-    Utility::socklen_t len=sizeof(err);
-    if(getsockopt(d_sock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
-      throw ResolverException("Error connecting: "+stringerror()); // Solaris
+  if(ret || res.empty())
+    throw ResolverException("Query to '" + ipport + "' for SOA of '" + domain + "' produced no answers");
 
-    if(err)
-      throw ResolverException("Error connecting: "+string(strerror(err)));
-  }
+  if(res[0].qtype.getCode() != QType::SOA) 
+    throw ResolverException("Query to '" + ipport + "' for SOA of '" + domain + "' produced a "+res[0].qtype.getName()+" record");
+
+  vector<string>parts;
+  stringtok(parts, res[0].content);
+  if(parts.size()<3)
+    throw ResolverException("Query to '" + ipport + "' for SOA of '" + domain + "' produced an unparseable response");
   
- done:
-  Utility::setBlocking( d_sock );
-  // d_sock now connected
+  *serial=(uint32_t)atol(parts[2].c_str());
 }
 
-//! returns -1 for permanent error, 0 for timeout, 1 for success
-int Resolver::axfr(const string &ipport, const char *domain)
+AXFRRetriever::AXFRRetriever(const ComboAddress& remote, const string& domain)
 {
-  d_domain=domain;
-
-  
-  ServiceTuple st;  
-  st.port = 53;     
-  parseService(ipport, st);
-  makeTCPSocket(st.host, st.port);
+  ComboAddress local;
+  if(remote.sin4.sin_family == AF_INET)
+    local=ComboAddress(::arg()["query-local-address"]);
+  else if(::arg().parmIsset("query-local-address6"))
+    local=ComboAddress(::arg()["query-local-address6"]);
+  else
+    local=ComboAddress("::");
+    
+  d_sock = makeQuerySocket(local, false); // make a TCP socket
+  d_buf = shared_array<char>(new char[65536]);
+  d_remote = remote; // mostly for error reporting
+  this->connect();
+  d_soacount = 0;
 
-  d_type=QType::AXFR;
-  
   vector<uint8_t> packet;
   DNSPacketWriter pw(packet, domain, QType::AXFR);
-  pw.getHeader()->id = d_randomid = dns_random(0xffff);
+  pw.getHeader()->id = dns_random(0xffff);
 
   uint16_t replen=htons(packet.size());
   Utility::iovec iov[2];
@@ -334,151 +322,109 @@ int Resolver::axfr(const string &ipport, const char *domain)
   iov[1].iov_base=(char*)&packet[0];
   iov[1].iov_len=packet.size();
 
-  int ret=Utility::writev(d_sock,iov,2);
+  int ret=Utility::writev(d_sock, iov,2);
   if(ret<0)
-    throw ResolverException("Error sending question to "+ipport+": "+stringerror());
+    throw ResolverException("Error sending question to "+d_remote.toStringWithPort()+": "+stringerror());
 
   int res = waitForData(d_sock, 10, 0);
   
   if(!res)
-    throw ResolverException("Timeout waiting for answer from "+ipport+" during AXFR");
+    throw ResolverException("Timeout waiting for answer from "+d_remote.toStringWithPort()+" during AXFR");
   if(res<0)
-    throw ResolverException("Error waiting for answer from "+ipport+": "+stringerror());
-
-  d_soacount=0;
-  d_inaxfr=true;
-  return 1;
+    throw ResolverException("Error waiting for answer from "+d_remote.toStringWithPort()+": "+stringerror());
 }
 
-int Resolver::getLength()
-{
-  int bytesLeft=2;
-  unsigned char buf[2];
-  
-  while(bytesLeft) {
-    int ret=waitForData(d_sock, 10);
-    if(ret<0) {
-      Utility::closesocket(d_sock);
-      d_sock=-1;
-      throw ResolverException("Waiting on data from remote TCP client: "+stringerror());
-    }
-  
-    ret=recv(d_sock, reinterpret_cast< char * >( buf ) +2-bytesLeft, bytesLeft,0);
-    if(ret<0)
-      throw ResolverException("Trying to read data from remote TCP client: "+stringerror());
-    if(!ret) 
-      return -1;
-    
-    bytesLeft-=ret;
-  }
-  return buf[0]*256+buf[1];
-}
 
-int Resolver::axfrChunk(Resolver::res_t &res)
+int AXFRRetriever::getChunk(Resolver::res_t &res)
 {
-  if(d_soacount>1) {
-    Utility::closesocket(d_sock);
-    d_sock=-1;
-    return 0;
-  }
-
+  if(d_soacount > 1)
+    return false;
   // d_sock is connected and is about to spit out a packet
   int len=getLength();
+  
   if(len<0)
     throw ResolverException("EOF trying to read axfr chunk from remote TCP client");
   
-  timeoutReadn((char *)d_buf,len); 
-  d_len=len;
+  timeoutReadn(len); 
 
-  res=result();
-  for(res_t::const_iterator i=res.begin();i!=res.end();++i)
+  int err = parseResult(d_buf.get(), len, "", 0, 0, &res);
+  if(err) 
+    throw ResolverException("AXFR chunk with a non-zero rcode "+lexical_cast<string>(err));
+    
+  for(Resolver::res_t::const_iterator i= res.begin(); i!=res.end(); ++i)
     if(i->qtype.getCode()==QType::SOA) {
       d_soacount++;
     }
 
   if(d_soacount>1 && !res.empty()) // chop off the last SOA
     res.resize(res.size()-1);
+  return true;
+}
 
-  return 1;
+void AXFRRetriever::timeoutReadn(uint16_t bytes)
+{
+  time_t start=time(0);
+  int n=0;
+  int numread;
+  while(n<bytes) {
+    if(waitForData(d_sock, 10-(time(0)-start))<0)
+      throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
+
+    numread=recv(d_sock, d_buf.get()+n, bytes-n, 0);
+    if(numread<0)
+      throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
+    if(numread==0)
+      throw ResolverException("Remote nameserver closed TCP connection");
+    n+=numread;
+  }
 }
 
-Resolver::res_t Resolver::result()
+
+
+void AXFRRetriever::connect()
 {
-  shared_ptr<MOADNSParser> mdp;
-  
-  try {
-    mdp=shared_ptr<MOADNSParser>(new MOADNSParser((char*)d_buf, d_len));
+  Utility::setNonBlocking( d_sock );
+
+  int err;
+
+  if((err=::connect(d_sock,(struct sockaddr*)&d_remote, d_remote.getSocklen()))<0 && errno!=EINPROGRESS) {
+    Utility::closesocket(d_sock);
+    d_sock=-1;
+    throw ResolverException("connect: "+stringerror());
   }
-  catch(...) {
-    throw ResolverException("resolver: unable to parse packet of "+itoa(d_len)+" bytes");
+
+  if(!err)
+    goto done;
+
+  err=waitForRWData(d_sock, false, 10, 0); // wait for writeability
+  
+  if(!err) {
+    Utility::closesocket(d_sock); // timeout
+    d_sock=-1;
+    errno=ETIMEDOUT;
+    
+    throw ResolverException("Timeout connecting to server");
   }
-  if(mdp->d_header.id != d_randomid) {
-    throw ResolverException("Remote nameserver replied with wrong id");
+  else if(err < 0) {
+    throw ResolverException("Error connecting: "+string(strerror(err)));
   }
-  if(mdp->d_header.rcode) {
-    if(d_inaxfr)
-      throw ResolverException("Remote nameserver unable/unwilling to AXFR with us: RCODE="+itoa(mdp->d_header.rcode));
-    else
-      throw ResolverException("Remote nameserver reported error: RCODE="+itoa(mdp->d_header.rcode));
+  else {
+    Utility::socklen_t len=sizeof(err);
+    if(getsockopt(d_sock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
+      throw ResolverException("Error connecting: "+stringerror()); // Solaris
+
+    if(err)
+      throw ResolverException("Error connecting: "+string(strerror(err)));
   }
-    if(!d_inaxfr) {
-      if(mdp->d_header.qdcount!=1)
-        throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(mdp->d_header.qdcount)+")");
-      
-      if(mdp->d_qname != d_domain+".")
-        throw ResolverException(string("resolver: received an answer to another question (")+mdp->d_qname+"!="+d_domain+".)");
-    }
-    
-    vector<DNSResourceRecord> ret; 
-    DNSResourceRecord rr;
-    for(MOADNSParser::answers_t::const_iterator i=mdp->d_answers.begin(); i!=mdp->d_answers.end(); ++i) {          
-      rr.qname = i->first.d_label;
-      if(!rr.qname.empty())
-        boost::erase_tail(rr.qname, 1); // strip .
-      rr.qtype = i->first.d_type;
-      rr.ttl = i->first.d_ttl;
-      rr.content = i->first.d_content->getZoneRepresentation();
-      rr.priority = 0;
-      
-      uint16_t qtype=rr.qtype.getCode();
-
-      if(!rr.content.empty() && (qtype==QType::MX || qtype==QType::NS || qtype==QType::CNAME))
-        boost::erase_tail(rr.content, 1);
-
-      if(rr.qtype.getCode() == QType::MX) {
-        vector<string> parts;
-        stringtok(parts, rr.content);
-        rr.priority = atoi(parts[0].c_str());
-        if(parts.size() > 1)
-          rr.content=parts[1];
-      } else if(rr.qtype.getCode() == QType::SRV) {
-        rr.priority = atoi(rr.content.c_str());
-        vector<pair<string::size_type, string::size_type> > fields;
-        vstringtok(fields, rr.content, " ");
-        if(fields.size()==4)
-          rr.content=string(rr.content.c_str() + fields[1].first, fields[3].second - fields[1].first);
-      }
-      ret.push_back(rr);
-    }
-    
-    return ret;
+  
+ done:
+  Utility::setBlocking( d_sock );
+  // d_sock now connected
 }
 
-void Resolver::getSoaSerial(const string &ip, const string &domain, uint32_t *serial)
+int AXFRRetriever::getLength()
 {
-  resolve(ip, domain.c_str(), QType::SOA);
-  res_t res=result();
-  if(res.empty())
-    throw ResolverException("Query to '" + ip + "' for SOA of '" + domain + "' produced no answers");
-
-  if(res[0].qtype.getCode() != QType::SOA) 
-    throw ResolverException("Query to '" + ip + "' for SOA of '" + domain + "' produced a "+res[0].qtype.getName()+" record");
-
-  vector<string>parts;
-  stringtok(parts, res[0].content);
-  if(parts.size()<3)
-    throw ResolverException("Query to '" + ip + "' for SOA of '" + domain + "' produced an unparseable response");
-  
-  *serial=(uint32_t)atol(parts[2].c_str());
+  timeoutReadn(2);
+  return (unsigned char)d_buf[0]*256+(unsigned char)d_buf[1];
 }
 
index d828bc71cb3b43f9ae0ba02e6319e98511858892..b5315cb7416f576222109e16d714e71507d39afb 100644 (file)
@@ -43,45 +43,57 @@ public:
   ResolverException(const string &reason) : AhuException(reason){}
 };
 
-//! Resolver class 
-class Resolver
+// send out an update notification for a domain to an IPv4/v6 address
+int sendNotification(int sock, const string &domain, const ComboAddress& remote, uint16_t id);
+
+// make an IPv4 or IPv6 query socket 
+int makeQuerySocket(const ComboAddress& local, bool udpOrTCP);
+//! Resolver class. Can be used synchronously and asynchronously, over IPv4 and over IPv6 (simultaneously)
+class Resolver  : public boost::noncopyable
 {
 public:
   Resolver();
   ~Resolver();
-  string i;
 
   typedef vector<DNSResourceRecord> res_t;
-  void makeSocket(int type);
-  void makeUDPSocket();
-  void makeTCPSocket(const string &ip, uint16_t port=53);
-  int notify(int sock, const string &domain, const string &ip, uint16_t id);
-  int resolve(const string &ip, const char *domain, int type);
-  uint16_t sendResolve(const string &ip, const char *domain, int type, bool dnssecOk=false);
+  //! synchronously resolve domain|type at IP, store result in result, rcode in ret
+  int resolve(const string &ip, const char *domain, int type, res_t* result);
+  
+  //! only send out a resolution request
+  uint16_t sendResolve(const ComboAddress& remote, const char *domain, int type, bool dnssecOk=false);
+  
+  //! see if we got a SOA response from our sendResolve
   bool tryGetSOASerial(string* theirDomain, uint32_t* theirSerial, uint32_t* theirInception, uint32_t* theirExpire, uint16_t* id);
-
-  int receiveResolve(struct sockaddr* fromaddr, Utility::socklen_t addrlen);
   
+  //! convenience function that calls resolve above
   void getSoaSerial(const string &, const string &, uint32_t *);
-  int axfrChunk(Resolver::res_t &res);
-  vector<DNSResourceRecord> result();
-  
-  void setRemote(const string &remote);
-  int axfr(const string &ip, const char *domain);
   
 private:
-  void timeoutReadn(char *buffer, int bytes);
-  int d_sock;
-  unsigned char *d_buf;
-  int getLength();
-  int d_len;
-  int d_soacount;
-  string d_domain;
+  int d_sock4, d_sock6;
+  
   int d_type;
   int d_timeout;
-  uint32_t d_ip;
+  string d_domain;
   uint16_t d_randomid;
-  bool d_inaxfr;
-  ComboAddress d_toaddr;
+  
+  ComboAddress d_remote;
+};
+
+class AXFRRetriever : public boost::noncopyable
+{
+  public:
+    AXFRRetriever(const ComboAddress& remote, const string& zone);
+    int getChunk(Resolver::res_t &res);  
+  
+  private:
+    void connect();
+    int getLength();
+    void timeoutReadn(uint16_t bytes);  
+
+    shared_array<char> d_buf;
+    string d_domain;
+    int d_sock;
+    int d_soacount;
+    ComboAddress d_remote;
 };
 
index 556999d44d1365f0a557de15b6354342cf68b7c4..28cbc6736b928259cecbfe8e57a4f1c6ccebb68e 100644 (file)
@@ -69,8 +69,8 @@ void CommunicatorClass::suck(const string &domain,const string &remote)
   di.backend=0;
   bool first=true;    
   try {
-    Resolver resolver;
-    resolver.axfr(remote, domain.c_str());
+    ComboAddress raddr(remote, 53);
+    AXFRRetriever retriever(raddr, domain.c_str());
 
     UeberBackend *B=dynamic_cast<UeberBackend *>(P.getBackend());
     NSEC3PARAMRecordContent ns3pr;
@@ -100,7 +100,7 @@ void CommunicatorClass::suck(const string &domain,const string &remote)
 
     Resolver::res_t recs;
     set<string> nsset, qnames;
-    while(resolver.axfrChunk(recs)) {
+    while(retriever.getChunk(recs)) {
       if(first) {
         L<<Logger::Error<<"AXFR started for '"<<domain<<"', transaction started"<<endl;
         di.backend->startTransaction(domain, domain_id);
@@ -198,16 +198,22 @@ struct SlaveSenderReceiver
   
   SlaveSenderReceiver()
   {
-    d_resolver.makeUDPSocket();
   }
   
   void deliverTimeout(const Identifier& i)
-  {}
+  {
+  }
   
   Identifier send(DomainInfo& di)
   {
     random_shuffle(di.masters.begin(), di.masters.end());
-    return make_pair(di.zone, d_resolver.sendResolve(*di.masters.begin(), di.zone.c_str(), QType::SOA, true));
+    try {
+      ComboAddress remote(*di.masters.begin());
+      return make_pair(di.zone, d_resolver.sendResolve(ComboAddress(*di.masters.begin(), 53), di.zone.c_str(), QType::SOA, true));
+    }
+    catch(AhuException& e) {
+      throw runtime_error("While attempting to query freshness of '"+di.zone+"': "+e.reason);
+    }
   }
   
   bool receive(Identifier& id, Answer& a)
@@ -283,7 +289,7 @@ void CommunicatorClass::slaveRefresh(PacketHandler *P)
       L<<Logger::Error<<"While checking domain freshness: " << re.reason<<endl;
     }
   }
-  L<<Logger::Warning<<"Received serial number updates for "<<ssr.d_freshness.size()<<" zones"<<endl;
+  L<<Logger::Warning<<"Received serial number updates for "<<ssr.d_freshness.size()<<" zones, had "<<ifl.getTimeouts()<<" timeouts"<<endl;
   DNSSECKeeper dk;
   BOOST_FOREACH(DomainInfo& di, sdomains) {
     if(!ssr.d_freshness.count(di.id))