]> granicus.if.org Git - pdns/commitdiff
Now uses send/recv wrapper to handle exceptions and connector rebuild
authorAki Tuomi <cmouse@desteem.org>
Mon, 8 Jul 2013 09:45:45 +0000 (12:45 +0300)
committerPeter van Dijk <peter.van.dijk@netherlabs.nl>
Thu, 17 Oct 2013 19:39:47 +0000 (21:39 +0200)
Merged CoProcess into pipeconnector

Added endl to log messages in send/recv wrapper

Test suite for unix connector

Test suite for unixconnector

Fixed incorrect use or errno

Fixed coverity issues

Now uses indefinite wait instead of while loop

Added sleep after socat startup to avoid race condition

Add NULL initialization for d_c on httpconnector

Fix coverity CID 1066169

Remove unnecessary pipebackend include.

13 files changed:
modules/remotebackend/.gitignore
modules/remotebackend/Makefile.am
modules/remotebackend/httpconnector.cc
modules/remotebackend/pipeconnector.cc
modules/remotebackend/remotebackend.cc
modules/remotebackend/remotebackend.hh
modules/remotebackend/test-remotebackend-unix.cc [new file with mode: 0644]
modules/remotebackend/testrunner.sh [changed mode: 0644->0755]
modules/remotebackend/unittest_http.rb [changed mode: 0644->0755]
modules/remotebackend/unittest_json.rb [changed mode: 0644->0755]
modules/remotebackend/unittest_pipe.rb [changed mode: 0644->0755]
modules/remotebackend/unittest_post.rb [changed mode: 0644->0755]
modules/remotebackend/unixconnector.cc

index c14f877c6a6735bcbd36eda9e97e1bea7a271ab9..572d122258c9649eec018f55653b0c46f534ce51 100644 (file)
@@ -1,5 +1,6 @@
 remotebackend-access.log
 test_remotebackend_http
 test_remotebackend_pipe
+test_remotebackend_unix
 test_remotebackend_json
 test_remotebackend_post
index c4bf57930f7548dbc6238195f1534f65a694800f..4425db05bb561e1e1ac8cefa3ead3d9cc3019d58 100644 (file)
@@ -5,7 +5,7 @@ AM_CPPFLAGS=@THREADFLAGS@ $(BOOST_CPPFLAGS) $(LIBCURL_CFLAGS) -I../../pdns/ext/r
 #endif
 
 EXTRA_DIST=OBJECTFILES OBJECTLIBS
-EXTRA_PROGRAMS=test_remotebackend_pipe test_remotebackend_http test_remotebackend_post test_remotebackend_json
+EXTRA_PROGRAMS=test_remotebackend_pipe test_remotebackend_unix test_remotebackend_http test_remotebackend_post test_remotebackend_json
 EXTRA_LTLIBRARIES=libtestremotebackend.la
 
 lib_LTLIBRARIES = libremotebackend.la 
@@ -16,7 +16,7 @@ libremotebackend_la_LDFLAGS=-module -avoid-version
 libremotebackend_la_LIBS=$(LIBCURL_LIBS)
 
 TESTS_ENVIRONMENT = env BOOST_TEST_LOG_LEVEL=message REMOTEBACKEND_HTTP=$(REMOTEBACKEND_HTTP) ./testrunner.sh 
-TESTS=test_remotebackend_pipe test_remotebackend_http test_remotebackend_post test_remotebackend_json
+TESTS=test_remotebackend_pipe test_remotebackend_unix test_remotebackend_http test_remotebackend_post test_remotebackend_json
 
 BUILT_SOURCES=../../pdns/dnslabeltext.cc
 
@@ -31,13 +31,14 @@ libtestremotebackend_la_SOURCES=../../pdns/dnsbackend.hh ../../pdns/dnsbackend.c
         ../../pdns/aes/dns_random.cc ../../pdns/packetcache.hh ../../pdns/packetcache.cc \
         ../../pdns/aes/aescpp.h ../../pdns/dns.hh ../../pdns/dns.cc ../../pdns/json.hh ../../pdns/json.cc \
         ../../pdns/aes/aescrypt.c ../../pdns/aes/aes.h ../../pdns/aes/aeskey.c ../../pdns/aes/aes_modes.c ../../pdns/aes/aesopt.h \
-        ../../pdns/aes/aestab.c ../../pdns/aes/aestab.h ../../pdns/aes/brg_endian.h ../../pdns/aes/brg_types.h ../pipebackend/coprocess.cc \
+        ../../pdns/aes/aestab.c ../../pdns/aes/aestab.h ../../pdns/aes/brg_endian.h ../../pdns/aes/brg_types.h \
         remotebackend.hh remotebackend.cc unixconnector.cc httpconnector.cc pipeconnector.cc
 
 libtestremotebackend_la_CFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(LIBCURL_CFLAGS) -g -O0 -I../../pdns
 libtestremotebackend_la_CXXFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(LIBCURL_CFLAGS) -g -O0 -I../../pdns
 
 test_remotebackend_pipe_SOURCES=test-remotebackend.cc test-remotebackend-pipe.cc 
+test_remotebackend_unix_SOURCES=test-remotebackend.cc test-remotebackend-unix.cc
 test_remotebackend_http_SOURCES=test-remotebackend.cc test-remotebackend-http.cc ../../config.h
 test_remotebackend_post_SOURCES=test-remotebackend.cc test-remotebackend-post.cc ../../config.h
 test_remotebackend_json_SOURCES=test-remotebackend.cc test-remotebackend-json.cc ../../config.h
@@ -46,6 +47,10 @@ test_remotebackend_pipe_CFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(LIBCURL_CFLAGS)
 test_remotebackend_pipe_CXXFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(LIBCURL_CFLAGS) -g -O0 -I../../pdns
 test_remotebackend_pipe_LDADD=libtestremotebackend.la @DYNLINKFLAGS@ @THREADFLAGS@ $(BOOST_UNIT_TEST_FRAMEWORK_LDFLAGS) ../../pdns/ext/polarssl-1.1.2/library/libpolarssl.a $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) $(BOOST_SERIALIZATION_LIBS) $(BOOST_PROGRAM_OPTIONS_LIBS) @LIBDL@ $(LIBCURL_LIBS)
 
+test_remotebackend_unix_CFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(LIBCURL_CFLAGS) -g -O0 -I../../pdns
+test_remotebackend_unix_CXXFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(LIBCURL_CFLAGS) -g -O0 -I../../pdns
+test_remotebackend_unix_LDADD=libtestremotebackend.la @DYNLINKFLAGS@ @THREADFLAGS@ $(BOOST_UNIT_TEST_FRAMEWORK_LDFLAGS) ../../pdns/ext/polarssl-1.1.2/library/libpolarssl.a $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) $(BOOST_SERIALIZATION_LIBS) $(BOOST_PROGRAM_OPTIONS_LIBS) @LIBDL@ $(LIBCURL_LIBS)
+
 test_remotebackend_http_CFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(LIBCURL_CFLAGS) -g -O0 -I../../pdns
 test_remotebackend_http_CXXFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(LIBCURL_CFLAGS) -g -O0 -I../../pdns
 test_remotebackend_http_LDADD=libtestremotebackend.la @DYNLINKFLAGS@ @THREADFLAGS@ $(BOOST_UNIT_TEST_FRAMEWORK_LDFLAGS) ../../pdns/ext/polarssl-1.1.2/library/libpolarssl.a $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) $(BOOST_SERIALIZATION_LIBS) $(BOOST_PROGRAM_OPTIONS_LIBS) @LIBDL@ $(LIBCURL_LIBS)
index fe6a4cda47c3c1acd68ec097a62f8c203bfde634..e21718f47c84018731e6bd75391a1e66936a6aa4 100644 (file)
@@ -17,6 +17,7 @@
 
 #ifdef REMOTEBACKEND_HTTP
 HTTPConnector::HTTPConnector(std::map<std::string,std::string> options) {
+    this->d_c = NULL;
     this->d_url = options.find("url")->second;
     if (options.find("url-suffix") != options.end()) {
       this->d_url_suffix = options.find("url-suffix")->second;
@@ -151,7 +152,7 @@ void HTTPConnector::restful_requestbuilder(const std::string &method, const rapi
         curl_easy_setopt(d_c, CURLOPT_POSTFIELDSIZE, nsize);
         curl_easy_setopt(d_c, CURLOPT_COPYPOSTFIELDS, postfields);
         curl_free(tmpstr);
-        delete postfields;
+        delete [] postfields;
     } else if (method == "superMasterBackend") {
         std::stringstream ss2;
         addUrlComponent(parameters, "ip", ss);
index 9e28980aad96e618f82635214f0abd906a05fc12..f86fd28c863a1575aaa1b564e1cab5f6c9142f3e 100644 (file)
@@ -1,3 +1,5 @@
+#include <sys/types.h>
+#include <sys/wait.h>
 #include "remotebackend.hh"
 
 PipeConnector::PipeConnector(std::map<std::string,std::string> options) {
@@ -7,26 +9,92 @@ PipeConnector::PipeConnector(std::map<std::string,std::string> options) {
   }
   this->command = options.find("command")->second;
   this->options = options;
-  this->coproc = NULL;
+  d_timeout=2000;
+
+  if (options.find("timeout") != options.end()) {
+     d_timeout = boost::lexical_cast<int>(options.find("timeout")->second);
+  }
+
+  d_pid = -1;
+  d_fp = NULL;
   launch();
 }
 
 PipeConnector::~PipeConnector(){
-  if (this->coproc != NULL) 
-    delete coproc; 
+  int status;
+  // just in case...
+  if (d_pid == -1) return;
+
+  if(!waitpid(d_pid, &status, WNOHANG)) {
+    kill(d_pid, 9);
+    waitpid(d_pid, &status, 0);
+  }
+
+  close(d_fd1[1]);
+  if (d_fp != NULL) fclose(d_fp);
 }
 
 void PipeConnector::launch() {
-  if (coproc != NULL) return;
+  // no relaunch
+  if (d_pid > 0 && checkStatus()) return;
+
+  std::vector <std::string> v;
+  split(v, command, is_any_of(" "));
+
+  const char *argv[v.size()+1];
+  argv[v.size()]=0;
+
+  for (size_t n = 0; n < v.size(); n++)
+    argv[n]=v[n].c_str();
+
+  signal(SIGPIPE, SIG_IGN);
+
+  if(access(argv[0],X_OK)) // check before fork so we can throw
+    throw AhuException("Command '"+string(argv[0])+"' cannot be executed: "+stringerror());
+
+  if(pipe(d_fd1)<0 || pipe(d_fd2)<0)
+    throw AhuException("Unable to open pipe for coprocess: "+string(strerror(errno)));
+
+  if((d_pid=fork())<0)
+    throw AhuException("Unable to fork for coprocess: "+stringerror());
+  else if(d_pid>0) { // parent speaking
+    close(d_fd1[0]);
+    Utility::setCloseOnExec(d_fd1[1]);
+    close(d_fd2[1]);
+    Utility::setCloseOnExec(d_fd2[0]);
+    if(!(d_fp=fdopen(d_fd2[0],"r")))
+      throw AhuException("Unable to associate a file pointer with pipe: "+stringerror());
+    setbuf(d_fp,0); // no buffering please, confuses select
+  }
+  else if(!d_pid) { // child
+    signal(SIGCHLD, SIG_DFL); // silence a warning from perl
+    close(d_fd1[1]);
+    close(d_fd2[0]);
+
+    if(d_fd1[0]!= 0) {
+      dup2(d_fd1[0], 0);
+      close(d_fd1[0]);
+    }
+
+    if(d_fd2[1]!= 1) {
+      dup2(d_fd2[1], 1);
+      close(d_fd2[1]);
+    }
+
+    // stdin & stdout are now connected, fire up our coprocess!
+
+    if(execv(argv[0], const_cast<char * const *>(argv))<0) // now what
+      exit(123);
+
+    /* not a lot we can do here. We shouldn't return because that will leave a forked process around.
+       no way to log this either - only thing we can do is make sure that our parent catches this soonest! */
+  }
+
   rapidjson::Value val;
   rapidjson::Document init,res;
-  int timeout=2000;
-  if (options.find("timeout") != options.end()) { 
-     timeout = boost::lexical_cast<int>(options.find("timeout")->second);
-  }
-  coproc = new CoProcess(this->command, timeout);
   init.SetObject();
   val = "initialize";
+
   init.AddMember("method",val, init.GetAllocator());
   val.SetObject();
   init.AddMember("parameters", val, init.GetAllocator());
@@ -44,42 +112,83 @@ void PipeConnector::launch() {
 
 int PipeConnector::send_message(const rapidjson::Document &input)
 {
-   std::string data;
+   std::string line;
+   line = makeStringFromDocument(input);
+   launch();
 
-   data = makeStringFromDocument(input);
+   line.append(1,'\n');
 
-   launch();
-   try {
-      coproc->send(data);
-      return 1;
+   unsigned int sent=0;
+   int bytes;
+
+   // writen routine - socket may not accept al data in one go
+   while(sent<line.size()) {
+     bytes=write(d_fd1[1],line.c_str()+sent,line.length()-sent);
+     if(bytes<0)
+       throw AhuException("Writing to coprocess failed: "+std::string(strerror(errno)));
+
+     sent+=bytes;
    }
-   catch(AhuException &ae) {
-      delete coproc;
-      coproc=NULL;
-      throw;
-   } 
+   return sent;
 }
 
 int PipeConnector::recv_message(rapidjson::Document &output) 
 {
+   std::string receive;
    rapidjson::GenericReader<rapidjson::UTF8<> , rapidjson::MemoryPoolAllocator<> > r;
    std::string tmp;
    std::string s_output;
-
    launch();
-   try {
-      while(1) {
-        coproc->receive(tmp);
-        s_output.append(tmp);
-        rapidjson::StringStream ss(s_output.c_str());
-        output.ParseStream<0>(ss); 
-        if (output.HasParseError() == false)
-          return s_output.size();
-      }
-   } catch(AhuException &ae) {
-      L<<Logger::Warning<<"[pipeconnector] "<<" unable to receive data from coprocess. "<<ae.reason<<endl;
-      delete coproc;
-      coproc = NULL;
-      throw;
+
+   while(1) {
+     receive.clear();
+     if(d_timeout) {
+       struct timeval tv;
+       tv.tv_sec = d_timeout/1000;
+       tv.tv_usec = (d_timeout % 1000) * 1000;
+       fd_set rds;
+       FD_ZERO(&rds);
+       FD_SET(fileno(d_fp),&rds);
+       int ret=select(fileno(d_fp)+1,&rds,0,0,&tv);
+       if(ret<0) 
+         throw AhuException("Error waiting on data from coprocess: "+stringerror());
+       if(!ret)
+         throw AhuException("Timeout waiting for data from coprocess");
+     }
+
+     if(!stringfgets(d_fp, receive))
+       throw AhuException("Child closed pipe");
+  
+      s_output.append(receive);
+      rapidjson::StringStream ss(s_output.c_str());
+      output.ParseStream<0>(ss); 
+      if (output.HasParseError() == false)
+        return s_output.size();
    }
+   return 0;
+}
+
+bool PipeConnector::checkStatus()
+{
+  int status;
+  int ret=waitpid(d_pid, &status, WNOHANG);
+  if(ret<0)
+    throw AhuException("Unable to ascertain status of coprocess "+itoa(d_pid)+" from "+itoa(getpid())+": "+string(strerror(errno)));
+  else if(ret) {
+    if(WIFEXITED(status)) {
+      int ret=WEXITSTATUS(status);
+      throw AhuException("Coprocess exited with code "+itoa(ret));
+    }
+    if(WIFSIGNALED(status)) {
+      int sig=WTERMSIG(status);
+      string reason="CoProcess died on receiving signal "+itoa(sig);
+#ifdef WCOREDUMP
+      if(WCOREDUMP(status))
+        reason+=". Dumped core";
+#endif
+
+      throw AhuException(reason);
+    }
+  }
+  return true;
 }
index 91d426a7ce35956c9d1ada158b25afcf61892055..7adedf43047a578f0a04bafc2f65b8cf59cb99a9 100644 (file)
@@ -49,11 +49,14 @@ bool Connector::recv(rapidjson::Document &value) {
 RemoteBackend::RemoteBackend(const std::string &suffix)
 {
       setArgPrefix("remote"+suffix);
-      build(getArg("connection-string"));
+
+      this->d_connstr = getArg("connection-string");
       this->d_result = NULL;
       this->d_dnssec = mustDo("dnssec");
       this->d_index = -1;
       this->d_trxid = 0;
+    
+      build();
 }
 
 RemoteBackend::~RemoteBackend() {
@@ -62,11 +65,40 @@ RemoteBackend::~RemoteBackend() {
      }
 }
 
+bool RemoteBackend::send(rapidjson::Document &value) {
+   try {
+     return connector->send(value);
+   } catch (AhuException &ex) {
+     L<<Logger::Error<<"Exception caught when sending: "<<ex.reason<<std::endl;
+   } catch (...) {
+     L<<Logger::Error<<"Exception caught when sending"<<std::endl;
+   }
+
+   delete this->connector;
+   build();
+   return false;
+}
+
+bool RemoteBackend::recv(rapidjson::Document &value) {
+   try {
+     return connector->recv(value);
+   } catch (AhuException &ex) {
+     L<<Logger::Error<<"Exception caught when receiving: "<<ex.reason<<std::endl;
+   } catch (...) {
+     L<<Logger::Error<<"Exception caught when receiving"<<std::endl;;
+   }
+
+   delete this->connector;
+   build();
+   return false;
+}
+
+
 /** 
  * Builds connector based on options
  * Currently supports unix,pipe and http
  */
-int RemoteBackend::build(const std::string &connstr) {
+int RemoteBackend::build() {
       std::vector<std::string> parts;
       std::string type;
       std::string opts;
@@ -74,12 +106,12 @@ int RemoteBackend::build(const std::string &connstr) {
 
       // connstr is of format "type:options"
       size_t pos;
-      pos = connstr.find_first_of(":");
+      pos = d_connstr.find_first_of(":");
       if (pos == std::string::npos)
          throw AhuException("Invalid connection string: malformed");
 
-      type = connstr.substr(0, pos);
-      opts = connstr.substr(pos+1);
+      type = d_connstr.substr(0, pos);
+      opts = d_connstr.substr(pos+1);
 
       // tokenize the string on comma
       stringtok(parts, opts, ",");
@@ -155,7 +187,7 @@ void RemoteBackend::lookup(const QType &qtype, const std::string &qdomain, DNSPa
 
    d_result = new rapidjson::Document();
 
-   if (connector->send(query) == false || connector->recv(*d_result) == false) { 
+   if (this->send(query) == false || this->recv(*d_result) == false) { 
       delete d_result;
       return;
    }
@@ -186,7 +218,7 @@ bool RemoteBackend::list(const std::string &target, int domain_id) {
 
    d_result = new rapidjson::Document();
 
-   if (connector->send(query) == false || connector->recv(*d_result) == false) {
+   if (this->send(query) == false || this->recv(*d_result) == false) {
      delete d_result;
      return false;
    }
@@ -245,7 +277,7 @@ bool RemoteBackend::getBeforeAndAfterNamesAbsolute(uint32_t id, const std::strin
    JSON_ADD_MEMBER(parameters, "qname", qname.c_str(), query.GetAllocator());
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
 
    unhashed = getString(answer["result"]["unhashed"]);
@@ -266,13 +298,13 @@ bool RemoteBackend::getDomainMetadata(const std::string& name, const std::string
    JSON_ADD_MEMBER(parameters, "kind", kind.c_str(), query.GetAllocator());
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false)
+   if (this->send(query) == false)
      return false;
 
    meta.clear();
 
    // not mandatory to implement
-   if (connector->recv(answer) == false)
+   if (this->recv(answer) == false)
      return true;
 
    if (answer["result"].IsArray()) {
@@ -301,7 +333,7 @@ bool RemoteBackend::setDomainMetadata(const string& name, const std::string& kin
    parameters.AddMember("value", val, query.GetAllocator());
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
 
    return getBool(answer["result"]);
@@ -321,7 +353,7 @@ bool RemoteBackend::getDomainKeys(const std::string& name, unsigned int kind, st
    JSON_ADD_MEMBER(parameters, "kind", kind, query.GetAllocator());
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
 
    keys.clear();
@@ -351,7 +383,7 @@ bool RemoteBackend::removeDomainKey(const string& name, unsigned int id) {
    JSON_ADD_MEMBER(parameters, "id", id, query.GetAllocator());
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
 
    return true;
@@ -374,7 +406,7 @@ int RemoteBackend::addDomainKey(const string& name, const KeyData& key) {
    parameters.AddMember("key", jkey, query.GetAllocator());
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
 
    return getInt(answer["result"]);
@@ -394,7 +426,7 @@ bool RemoteBackend::activateDomainKey(const string& name, unsigned int id) {
    JSON_ADD_MEMBER(parameters, "id", id, query.GetAllocator());
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
 
    return true;
@@ -414,7 +446,7 @@ bool RemoteBackend::deactivateDomainKey(const string& name, unsigned int id) {
    JSON_ADD_MEMBER(parameters, "id", id, query.GetAllocator());
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
 
    return true;
@@ -436,7 +468,7 @@ bool RemoteBackend::getTSIGKey(const std::string& name, std::string* algorithm,
    JSON_ADD_MEMBER(parameters, "name", name.c_str(), query.GetAllocator());
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
 
    if (algorithm != NULL)
@@ -459,7 +491,7 @@ bool RemoteBackend::getDomainInfo(const string &domain, DomainInfo &di) {
    JSON_ADD_MEMBER(parameters, "name", domain.c_str(), query.GetAllocator());
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
 
    // make sure we got zone & kind
@@ -506,7 +538,7 @@ void RemoteBackend::setNotified(uint32_t id, uint32_t serial) {
    JSON_ADD_MEMBER(parameters, "serial", serial, query.GetAllocator());
    query.AddMember("parameters", parameters, query.GetAllocator());
  
-   if (connector->send(query) == false || connector->recv(answer) == false) {
+   if (this->send(query) == false || this->recv(answer) == false) {
       L<<Logger::Error<<kBackendId<<"Failed to execute RPC for RemoteBackend::setNotified("<<id<<","<<serial<<")"<<endl;
    }
 }
@@ -541,7 +573,7 @@ bool RemoteBackend::superMasterBackend(const string &ip, const string &domain, c
 
    *ddb = 0;
 
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
 
    // we are the backend
@@ -565,7 +597,7 @@ bool RemoteBackend::createSlaveDomain(const string &ip, const string &domain, co
    JSON_ADD_MEMBER(parameters, "account", account.c_str(), query.GetAllocator());
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
    return true;
 }
@@ -600,7 +632,7 @@ bool RemoteBackend::replaceRRSet(uint32_t domain_id, const string& qname, const
    parameters.AddMember("rrset", rj_rrset, query.GetAllocator());
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
 
    return true;
@@ -630,7 +662,7 @@ bool RemoteBackend::feedRecord(const DNSResourceRecord &rr, string *ordername) {
 
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
    return true; // XXX FIXME this API should not return 'true' I think -ahu
 }
@@ -651,7 +683,7 @@ bool RemoteBackend::feedEnts(int domain_id, set<string>& nonterm) {
    parameters.AddMember("nonterm", nts, query.GetAllocator());
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
    return true; 
 }
@@ -677,7 +709,7 @@ bool RemoteBackend::feedEnts3(int domain_id, const string &domain, set<string> &
    parameters.AddMember("nonterm", nts, query.GetAllocator());
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
    return true;
 }
@@ -696,7 +728,7 @@ bool RemoteBackend::startTransaction(const string &domain, int domain_id) {
 
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false || connector->recv(answer) == false) {
+   if (this->send(query) == false || this->recv(answer) == false) {
      d_trxid = -1;
      return false;
    }
@@ -714,7 +746,7 @@ bool RemoteBackend::commitTransaction() {
    query.AddMember("parameters", parameters, query.GetAllocator());
 
    d_trxid = -1;
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
    return true;
 }
@@ -730,7 +762,7 @@ bool RemoteBackend::abortTransaction() {
    query.AddMember("parameters", parameters, query.GetAllocator());
 
    d_trxid = -1;
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
    return true;
 }
@@ -759,7 +791,7 @@ bool RemoteBackend::calculateSOASerial(const string& domain, const SOAData& sd,
    parameters.AddMember("sd", soadata, query.GetAllocator());
    query.AddMember("parameters", parameters, query.GetAllocator());
 
-   if (connector->send(query) == false || connector->recv(answer) == false)
+   if (this->send(query) == false || this->recv(answer) == false)
      return false;
 
    serial = getInt64(answer["result"]);
index 18b6a97ab4098008b8522cdbaf64dfb42d258551..972c44517258e7c99357122d7c7230a077171782 100644 (file)
@@ -13,7 +13,6 @@
 #include <boost/lexical_cast.hpp>
 #include <rapidjson/rapidjson.h>
 #include <rapidjson/document.h>
-#include "../pipebackend/coprocess.hh"
 #include "pdns/json.hh"
 
 #ifdef REMOTEBACKEND_HTTP
@@ -92,9 +91,15 @@ class PipeConnector: public Connector {
   private:
 
   void launch();
-  CoProcess *coproc;
+  bool checkStatus();
+
   std::string command;
   std::map<std::string,std::string> options;
+  int d_fd1[2], d_fd2[2];
+  int d_pid;
+  int d_timeout;
+  FILE *d_fp;
 };
 
 class RemoteBackend : public DNSBackend
@@ -133,12 +138,13 @@ class RemoteBackend : public DNSBackend
   static DNSBackend *maker();
 
   private:
-    int build(const std::string &connstr);
+    int build();
     Connector *connector;
     bool d_dnssec;
     rapidjson::Document *d_result;
     int d_index;
     int64_t d_trxid;
+    std::string d_connstr;
 
     bool getBool(rapidjson::Value &value);
     int getInt(rapidjson::Value &value);
@@ -146,5 +152,8 @@ class RemoteBackend : public DNSBackend
     int64_t getInt64(rapidjson::Value &value);
     std::string getString(rapidjson::Value &value);
     double getDouble(rapidjson::Value &value);
+
+    bool send(rapidjson::Document &value);
+    bool recv(rapidjson::Document &value);
 };
 #endif
diff --git a/modules/remotebackend/test-remotebackend-unix.cc b/modules/remotebackend/test-remotebackend-unix.cc
new file mode 100644 (file)
index 0000000..823da3b
--- /dev/null
@@ -0,0 +1,65 @@
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MAIN
+#define BOOST_TEST_MODULE unit
+
+#include <boost/test/unit_test.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/foreach.hpp>
+#include <boost/tuple/tuple.hpp>
+#include "pdns/namespaces.hh"
+#include <pdns/dns.hh>
+#include <pdns/dnsbackend.hh>
+#include <pdns/dnspacket.hh>
+#include <pdns/ueberbackend.hh>
+#include <pdns/ahuexception.hh>
+#include <pdns/logger.hh>
+#include <pdns/arguments.hh>
+#include "pdns/dnsrecords.hh"
+#include <boost/lexical_cast.hpp>
+#include <rapidjson/rapidjson.h>
+#include <rapidjson/document.h>
+#include "pdns/json.hh"
+#include "pdns/statbag.hh"
+#include "pdns/packetcache.hh"
+
+StatBag S;
+PacketCache PC;
+ArgvMap &arg()
+{
+  static ArgvMap arg;
+  return arg;
+};
+
+class RemoteLoader
+{
+   public:
+      RemoteLoader();
+};
+
+DNSBackend *be;
+
+struct RemotebackendSetup {
+    RemotebackendSetup()  {
+       be = 0; 
+       try {
+               // setup minimum arguments
+               ::arg().set("module-dir")="";
+                new RemoteLoader();
+               BackendMakers().launch("remote");
+                // then get us a instance of it 
+                ::arg().set("remote-connection-string")="unix:path=/tmp/remotebackend.sock";
+                ::arg().set("remote-dnssec")="yes";
+                be = BackendMakers().all()[0];
+               // load few record types to help out
+               SOARecordContent::report();
+               NSRecordContent::report();
+                ARecordContent::report();
+       } catch (AhuException &ex) {
+               BOOST_TEST_MESSAGE("Cannot start remotebackend: " << ex.reason );
+       };
+    }
+    ~RemotebackendSetup()  {  }
+};
+
+BOOST_GLOBAL_FIXTURE( RemotebackendSetup );
+
old mode 100644 (file)
new mode 100755 (executable)
index 7f687a5..08e07de
@@ -1,6 +1,8 @@
 #!/bin/bash 
 
 webrick_pid=""
+socat_pid=""
+socat=/usr/bin/socat
 
 function start_web() {
   if [ x"$REMOTEBACKEND_HTTP" == "xyes" ]; then
@@ -24,6 +26,35 @@ function stop_web() {
  fi
 }
 
+function start_unix() {
+  if [ ! -x $socat ]; then
+     echo "Cannot find socat - cannot test (non-fatal)"
+     exit 0
+  fi
+  
+  $socat unix-listen:/tmp/remotebackend.sock exec:./unittest_pipe.rb &
+  socat_pid=$!
+  sleep 1
+}
+
+function stop_unix() {
+ if [ ! -z "$socat_pid" ]; then
+   kill -TERM $socat_pid 2>/dev/null
+   if [ $? -ne 0 ]; then
+     # already dead
+     return 
+   fi
+   # wait a moment for it to die
+   i=0
+   while [ $i -lt 5 ]; do
+     sleep 1
+     kill -0 $socat_pid 2>/dev/null
+     if [ $? -ne 0 ]; then break; fi
+     let i=i+1
+   done
+ fi
+}
+
 mode=`basename "$1"`
 
 case "$mode" in
@@ -31,6 +62,12 @@ case "$mode" in
     ./test_remotebackend_pipe
     rv=$?
   ;;
+  test_remotebackend_unix)
+    start_unix
+    ./test_remotebackend_unix
+    rv=$?
+    stop_unix
+  ;;
   test_remotebackend_http)
     start_web "http"
     ./test_remotebackend_http
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
index 801ed275e137e2fdc04e30c6a9a59cb88e2327d2..bd7320d535c005f4d3d2905cd34d5af233fabeec 100644 (file)
@@ -19,6 +19,7 @@ UnixsocketConnector::UnixsocketConnector(std::map<std::string,std::string> optio
    this->path = options.find("path")->second;
    this->options = options;
    this->connected = false;
+   this->fd = -1;
 }
 
 UnixsocketConnector::~UnixsocketConnector() {
@@ -120,6 +121,7 @@ void UnixsocketConnector::reconnect() {
    struct sockaddr_un sock;
    rapidjson::Document init,res;
    rapidjson::Value val;
+   int rv;
 
    if (connected) return; // no point reconnecting if connected...
    connected = true;
@@ -134,13 +136,19 @@ void UnixsocketConnector::reconnect() {
    sock.sun_family = AF_UNIX;
    memset(sock.sun_path, 0, UNIX_PATH_MAX);
    path.copy(sock.sun_path, UNIX_PATH_MAX, 0);
-   fcntl(fd, F_SETFL, O_NONBLOCK, &fd);
+   if (fcntl(fd, F_SETFL, O_NONBLOCK, &fd)) {
+      connected = false;
+      L<<Logger::Error<<"Cannot manipulate socket: " << strerror(errno) << std::endl;;
+      close(fd);
+      return;
+   }
 
-   while(connect(fd, reinterpret_cast<struct sockaddr*>(&sock), sizeof sock)==-1 && (errno == EINPROGRESS)) {
-     waitForData(fd, 0, 500);
+   if((rv = connect(fd, reinterpret_cast<struct sockaddr*>(&sock), sizeof sock))==-1 && (errno == EINPROGRESS)) {
+     waitForData(fd, 0, -1);
+     rv = connect(fd, reinterpret_cast<struct sockaddr*>(&sock), sizeof sock);
    }
 
-   if (errno != EISCONN && errno != 0) {
+   if (rv != 0 && errno != EISCONN && errno != 0) {
       L<<Logger::Error<<"Cannot connect to socket: " << strerror(errno) << std::endl;
       close(fd);
       connected = false;