From 044707eab970545c1cf8c24908a679f25c5599e6 Mon Sep 17 00:00:00 2001 From: Aki Tuomi Date: Mon, 8 Jul 2013 12:45:45 +0300 Subject: [PATCH] Now uses send/recv wrapper to handle exceptions and connector rebuild 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. --- modules/remotebackend/.gitignore | 1 + modules/remotebackend/Makefile.am | 11 +- modules/remotebackend/httpconnector.cc | 3 +- modules/remotebackend/pipeconnector.cc | 179 ++++++++++++++---- modules/remotebackend/remotebackend.cc | 90 ++++++--- modules/remotebackend/remotebackend.hh | 15 +- .../remotebackend/test-remotebackend-unix.cc | 65 +++++++ modules/remotebackend/testrunner.sh | 37 ++++ modules/remotebackend/unittest_http.rb | 0 modules/remotebackend/unittest_json.rb | 0 modules/remotebackend/unittest_pipe.rb | 0 modules/remotebackend/unittest_post.rb | 0 modules/remotebackend/unixconnector.cc | 16 +- 13 files changed, 342 insertions(+), 75 deletions(-) create mode 100644 modules/remotebackend/test-remotebackend-unix.cc mode change 100644 => 100755 modules/remotebackend/testrunner.sh mode change 100644 => 100755 modules/remotebackend/unittest_http.rb mode change 100644 => 100755 modules/remotebackend/unittest_json.rb mode change 100644 => 100755 modules/remotebackend/unittest_pipe.rb mode change 100644 => 100755 modules/remotebackend/unittest_post.rb diff --git a/modules/remotebackend/.gitignore b/modules/remotebackend/.gitignore index c14f877c6..572d12225 100644 --- a/modules/remotebackend/.gitignore +++ b/modules/remotebackend/.gitignore @@ -1,5 +1,6 @@ remotebackend-access.log test_remotebackend_http test_remotebackend_pipe +test_remotebackend_unix test_remotebackend_json test_remotebackend_post diff --git a/modules/remotebackend/Makefile.am b/modules/remotebackend/Makefile.am index c4bf57930..4425db05b 100644 --- a/modules/remotebackend/Makefile.am +++ b/modules/remotebackend/Makefile.am @@ -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) diff --git a/modules/remotebackend/httpconnector.cc b/modules/remotebackend/httpconnector.cc index fe6a4cda4..e21718f47 100644 --- a/modules/remotebackend/httpconnector.cc +++ b/modules/remotebackend/httpconnector.cc @@ -17,6 +17,7 @@ #ifdef REMOTEBACKEND_HTTP HTTPConnector::HTTPConnector(std::map 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); diff --git a/modules/remotebackend/pipeconnector.cc b/modules/remotebackend/pipeconnector.cc index 9e28980aa..f86fd28c8 100644 --- a/modules/remotebackend/pipeconnector.cc +++ b/modules/remotebackend/pipeconnector.cc @@ -1,3 +1,5 @@ +#include +#include #include "remotebackend.hh" PipeConnector::PipeConnector(std::map options) { @@ -7,26 +9,92 @@ PipeConnector::PipeConnector(std::map 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(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 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(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(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 , 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<(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; } diff --git a/modules/remotebackend/remotebackend.cc b/modules/remotebackend/remotebackend.cc index 91d426a7c..7adedf430 100644 --- a/modules/remotebackend/remotebackend.cc +++ b/modules/remotebackend/remotebackend.cc @@ -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<connector; + build(); + return false; +} + +bool RemoteBackend::recv(rapidjson::Document &value) { + try { + return connector->recv(value); + } catch (AhuException &ex) { + L<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 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<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& 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 & 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"]); diff --git a/modules/remotebackend/remotebackend.hh b/modules/remotebackend/remotebackend.hh index 18b6a97ab..972c44517 100644 --- a/modules/remotebackend/remotebackend.hh +++ b/modules/remotebackend/remotebackend.hh @@ -13,7 +13,6 @@ #include #include #include -#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 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 index 000000000..823da3be3 --- /dev/null +++ b/modules/remotebackend/test-remotebackend-unix.cc @@ -0,0 +1,65 @@ +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MAIN +#define BOOST_TEST_MODULE unit + +#include +#include +#include +#include +#include "pdns/namespaces.hh" +#include +#include +#include +#include +#include +#include +#include +#include "pdns/dnsrecords.hh" +#include +#include +#include +#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 ); + diff --git a/modules/remotebackend/testrunner.sh b/modules/remotebackend/testrunner.sh old mode 100644 new mode 100755 index 7f687a5ca..08e07de01 --- a/modules/remotebackend/testrunner.sh +++ b/modules/remotebackend/testrunner.sh @@ -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 diff --git a/modules/remotebackend/unittest_http.rb b/modules/remotebackend/unittest_http.rb old mode 100644 new mode 100755 diff --git a/modules/remotebackend/unittest_json.rb b/modules/remotebackend/unittest_json.rb old mode 100644 new mode 100755 diff --git a/modules/remotebackend/unittest_pipe.rb b/modules/remotebackend/unittest_pipe.rb old mode 100644 new mode 100755 diff --git a/modules/remotebackend/unittest_post.rb b/modules/remotebackend/unittest_post.rb old mode 100644 new mode 100755 diff --git a/modules/remotebackend/unixconnector.cc b/modules/remotebackend/unixconnector.cc index 801ed275e..bd7320d53 100644 --- a/modules/remotebackend/unixconnector.cc +++ b/modules/remotebackend/unixconnector.cc @@ -19,6 +19,7 @@ UnixsocketConnector::UnixsocketConnector(std::map 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<(&sock), sizeof sock)==-1 && (errno == EINPROGRESS)) { - waitForData(fd, 0, 500); + if((rv = connect(fd, reinterpret_cast(&sock), sizeof sock))==-1 && (errno == EINPROGRESS)) { + waitForData(fd, 0, -1); + rv = connect(fd, reinterpret_cast(&sock), sizeof sock); } - if (errno != EISCONN && errno != 0) { + if (rv != 0 && errno != EISCONN && errno != 0) { L<