From: Aki Tuomi Date: Sun, 2 Jun 2013 15:46:14 +0000 (+0300) Subject: Fixes and spanking new test suite for remotebackend X-Git-Tag: rec-3.6.0-rc1~698^2^2~5 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1db3e610ef47a410218c66f0a00288b03c5031e6;p=pdns Fixes and spanking new test suite for remotebackend --- diff --git a/configure.ac b/configure.ac index 04e0d70d9..d8c37ccf1 100644 --- a/configure.ac +++ b/configure.ac @@ -255,10 +255,12 @@ AM_CONDITIONAL(REMOTEBACKEND_HTTP,test x"$enable_remotebackend_http" = "xyes") if test "x$enable_remotebackend_http" = "xyes" then PKG_CHECK_MODULES(LIBCURL, libcurl, HAVE_LIBCURL=yes, AC_MSG_ERROR([Could not find libcurl])) + REMOTEBACKEND_HTTP=yes AC_SUBST(LIBCURL_LIBS) AC_SUBST(LIBCURL_CFLAGS) AC_DEFINE(HAVE_LIBCURL,1,[If we have libcurl]) AC_DEFINE(REMOTEBACKEND_HTTP,1,[If we want HTTP connector]) + AC_SUBST(REMOTEBACKEND_HTTP) fi AC_MSG_CHECKING(whether we should build static binaries) diff --git a/modules/remotebackend/.gitignore b/modules/remotebackend/.gitignore new file mode 100644 index 000000000..6b621dc02 --- /dev/null +++ b/modules/remotebackend/.gitignore @@ -0,0 +1,3 @@ +remotebackend-access.log +test_remotebackend_http +test_remotebackend_pipe diff --git a/modules/remotebackend/Makefile.am b/modules/remotebackend/Makefile.am index a47f8bbae..6ee7318a4 100644 --- a/modules/remotebackend/Makefile.am +++ b/modules/remotebackend/Makefile.am @@ -5,9 +5,46 @@ AM_CPPFLAGS=@THREADFLAGS@ $(BOOST_CPPFLAGS) $(LIBCURL_CFLAGS) -I../../pdns/ext/r #endif EXTRA_DIST=OBJECTFILES OBJECTLIBS -lib_LTLIBRARIES = libremotebackend.la +EXTRA_PROGRAMS=test_remotebackend_pipe test_remotebackend_http +EXTRA_LTLIBRARIES=libtestremotebackend.la + +lib_LTLIBRARIES = libremotebackend.la libremotebackend_la_SOURCES=remotebackend.hh remotebackend.cc unixconnector.cc httpconnector.cc pipeconnector.cc 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 + +BUILT_SOURCES=../../pdns/dnslabeltext.cc + +../../pdns/dnslabeltext.cc: ../../pdns/dnslabeltext.rl + make -C ../../pdns dnslabeltext.cc + +libtestremotebackend_la_SOURCES=../../pdns/dnsbackend.hh ../../pdns/dnsbackend.cc ../../pdns/ueberbackend.hh ../../pdns/ueberbackend.cc \ + ../../pdns/nameserver.cc ../../pdns/misc.cc ../../pdns/arguments.hh \ + ../../pdns/unix_utility.cc ../../pdns/logger.cc ../../pdns/statbag.cc ../../pdns/arguments.hh ../../pdns/arguments.cc ../../pdns/qtype.cc ../../pdns/dnspacket.cc \ + ../../pdns/dnswriter.cc ../../pdns/base64.cc ../../pdns/base32.cc ../../pdns/dnsrecords.cc ../../pdns/dnslabeltext.cc ../../pdns/dnsparser.cc \ + ../../pdns/rcpgenerator.cc ../../pdns/ednssubnet.cc ../../pdns/nsecrecords.cc ../../pdns/sillyrecords.cc ../../pdns/dnssecinfra.cc \ + ../../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 \ + 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_http_SOURCES=test-remotebackend.cc test-remotebackend-http.cc ../../config.h + +test_remotebackend_pipe_CFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(LIBCURL_CFLAGS) -g -O0 -I../../pdns +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_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 8706d2a8f..b52d858cb 100644 --- a/modules/remotebackend/httpconnector.cc +++ b/modules/remotebackend/httpconnector.cc @@ -40,12 +40,13 @@ size_t httpconnector_write_data(void *buffer, size_t size, size_t nmemb, void *u } // converts json value into string -void HTTPConnector::json2string(const rapidjson::Value &input, std::string &output) { +bool HTTPConnector::json2string(const rapidjson::Value &input, std::string &output) { if (input.IsString()) output = input.GetString(); else if (input.IsNull()) output = ""; else if (input.IsUint()) output = lexical_cast(input.GetUint()); else if (input.IsInt()) output = lexical_cast(input.GetInt()); - else output = "inconvertible value"; + else return false; + return true; } void HTTPConnector::addUrlComponent(const rapidjson::Value ¶meters, const char *element, std::stringstream& ss) { @@ -59,6 +60,33 @@ void HTTPConnector::addUrlComponent(const rapidjson::Value ¶meters, const ch } } +template std::string buildMemberListArgs(std::string prefix, const T* value, CURL* curlContext) { + std::stringstream stream; + + for (rapidjson::Value::ConstMemberIterator itr = value->MemberBegin(); itr != value->MemberEnd(); itr++) { + stream << prefix << "[" << itr->name.GetString() << "]="; + + if (itr->value.IsUint64()) { + stream << itr->value.GetUint64(); + } else if (itr->value.IsInt64()) { + stream << itr->value.GetInt64(); + } else if (itr->value.IsUint()) { + stream << itr->value.GetUint(); + } else if (itr->value.IsInt()) { + stream << itr->value.GetInt(); + } else if (itr->value.IsBool()) { + stream << (itr->value.GetBool() ? 1 : 0); + } else if (itr->value.IsString()) { + char *tmpstr = curl_easy_escape(curlContext, itr->value.GetString(), 0); + stream << tmpstr; + curl_free(tmpstr); + } + + stream << "&"; + } + + return stream.str(); +} // builds our request void HTTPConnector::requestbuilder(const std::string &method, const rapidjson::Value ¶meters, struct curl_slist **slist) @@ -66,8 +94,6 @@ void HTTPConnector::requestbuilder(const std::string &method, const rapidjson::V std::stringstream ss; std::string sparam; char *tmpstr; - size_t k=0; - k=k; // special names are qname, name, zonename, kind, others go to headers @@ -79,16 +105,13 @@ void HTTPConnector::requestbuilder(const std::string &method, const rapidjson::V // id must be first due to the fact that the qname/name can be empty addUrlComponent(parameters, "id", ss); + addUrlComponent(parameters, "domain_id", ss); addUrlComponent(parameters, "zonename", ss); addUrlComponent(parameters, "qname", ss); addUrlComponent(parameters, "name", ss); addUrlComponent(parameters, "kind", ss); addUrlComponent(parameters, "qtype", ss); - // finally add suffix - ss << d_url_suffix; - curl_easy_setopt(d_c, CURLOPT_URL, ss.str().c_str()); - (*slist) = NULL; // set the correct type of request based on method if (method == "activateDomainKey" || method == "deactivateDomainKey") { @@ -113,21 +136,10 @@ void HTTPConnector::requestbuilder(const std::string &method, const rapidjson::V addUrlComponent(parameters, "ip", ss); addUrlComponent(parameters, "domain", ss); // then we need to serialize rrset payload into POST - for(rapidjson::Value::ConstValueIterator itr = parameters["nsset"].Begin(), k=0; itr != parameters["nsset"].End(); itr++, k++) { - for (rapidjson::Value::ConstMemberIterator itr2 = itr->MemberBegin(); itr2 != itr->MemberEnd(); itr2++) { - ss2 << "nsset[" << k << "][" << itr2->name.GetString() << "]="; - if (itr2->value.IsUint()) { - ss2 << itr2->value.GetUint(); - } else if (itr2->value.IsInt()) { - ss2 << itr2->value.GetInt(); - } else if (itr2->value.IsBool()) { - ss2 << (itr2->value.GetBool() ? 1 : 0); - } else if (itr2->value.IsString()) { - tmpstr = curl_easy_escape(d_c, itr2->value.GetString(), 0); - ss2 << tmpstr; - curl_free(tmpstr); - } - } + size_t index = 0; + for(rapidjson::Value::ConstValueIterator itr = parameters["nsset"].Begin(); itr != parameters["nsset"].End(); itr++) { + index++; + ss2 << buildMemberListArgs("nsset[" + boost::lexical_cast(index) + "]", itr, d_c); } // then give it to curl std::string out = ss2.str(); @@ -136,50 +148,66 @@ void HTTPConnector::requestbuilder(const std::string &method, const rapidjson::V } else if (method == "createSlaveDomain") { addUrlComponent(parameters, "ip", ss); addUrlComponent(parameters, "domain", ss); - addUrlComponent(parameters, "account", ss); + if (parameters.HasMember("account")) { + std::string out = parameters["account"].GetString(); + curl_easy_setopt(d_c, CURLOPT_POSTFIELDSIZE, out.size()); + curl_easy_setopt(d_c, CURLOPT_COPYPOSTFIELDS, out.c_str()); + } else { + curl_easy_setopt(d_c, CURLOPT_POST, 1); + curl_easy_setopt(d_c, CURLOPT_POSTFIELDSIZE, 0); + } } else if (method == "replaceRRSet") { std::stringstream ss2; - for(rapidjson::Value::ConstValueIterator itr = parameters["nsset"].Begin(), k=0; itr != parameters["nsset"].End(); itr++, k++) { - for (rapidjson::Value::ConstMemberIterator itr2 = itr->MemberBegin(); itr2 != itr->MemberEnd(); itr2++) { - ss2 << "rrset[" << k << "][" << itr2->name.GetString() << "]="; - if (itr2->value.IsUint()) { - ss2 << itr2->value.GetUint(); - } else if (itr2->value.IsInt()) { - ss2 << itr2->value.GetInt(); - } else if (itr2->value.IsBool()) { - ss2 << (itr2->value.GetBool() ? 1 : 0); - } else if (itr2->value.IsString()) { - tmpstr = curl_easy_escape(d_c, itr2->value.GetString(), 0); - ss2 << tmpstr; - curl_free(tmpstr); - } - } + size_t index = 0; + for(rapidjson::Value::ConstValueIterator itr = parameters["rrset"].Begin(); itr != parameters["rrset"].End(); itr++) { + index++; + ss2 << buildMemberListArgs("rrset[" + boost::lexical_cast(index) + "]", itr, d_c); } // then give it to curl std::string out = ss2.str(); curl_easy_setopt(d_c, CURLOPT_POSTFIELDSIZE, out.size()); curl_easy_setopt(d_c, CURLOPT_COPYPOSTFIELDS, out.c_str()); } else if (method == "feedRecord") { + std::string out = buildMemberListArgs("rr", ¶meters["rr"], d_c); + curl_easy_setopt(d_c, CURLOPT_POSTFIELDSIZE, out.size()); + curl_easy_setopt(d_c, CURLOPT_COPYPOSTFIELDS, out.c_str()); + } else if (method == "feedEnts") { std::stringstream ss2; - for (rapidjson::Value::ConstMemberIterator itr2 = parameters["rr"].MemberBegin(); itr2 != parameters["rr"].MemberEnd(); itr2++) { - ss2 << "rr[" << itr2->name.GetString() << "]="; - if (itr2->value.IsUint()) { - ss2 << itr2->value.GetUint(); - } else if (itr2->value.IsInt()) { - ss2 << itr2->value.GetInt(); - } else if (itr2->value.IsBool()) { - ss2 << (itr2->value.GetBool() ? 1 : 0); - } else if (itr2->value.IsString()) { - tmpstr = curl_easy_escape(d_c, itr2->value.GetString(), 0); - ss2 << tmpstr; - curl_free(tmpstr); - } + for(rapidjson::Value::ConstValueIterator itr = parameters["nonterm"].Begin(); itr != parameters["nonterm"].End(); itr++) { + tmpstr = curl_easy_escape(d_c, itr->GetString(), 0); + ss2 << "nonterm[]=" << tmpstr << "&"; + curl_free(tmpstr); } std::string out = ss2.str(); curl_easy_setopt(d_c, CURLOPT_POSTFIELDSIZE, out.size()); curl_easy_setopt(d_c, CURLOPT_COPYPOSTFIELDS, out.c_str()); + } else if (method == "feedEnts3") { + std::stringstream ss2; + addUrlComponent(parameters, "domain", ss); + ss2 << "times=" << parameters["times"].GetInt() << "&salt=" << parameters["salt"].GetString() << "&narrow=" << (parameters["narrow"].GetBool() ? 1 : 0) << "&"; + for(rapidjson::Value::ConstValueIterator itr = parameters["nonterm"].Begin(); itr != parameters["nonterm"].End(); itr++) { + tmpstr = curl_easy_escape(d_c, itr->GetString(), 0); + ss2 << "nonterm[]=" << tmpstr << "&"; + curl_free(tmpstr); + } + std::string out = ss2.str(); + curl_easy_setopt(d_c, CURLOPT_POSTFIELDSIZE, out.size()); + curl_easy_setopt(d_c, CURLOPT_COPYPOSTFIELDS, out.c_str()); + } else if (method == "startTransaction") { + addUrlComponent(parameters, "domain", ss); + addUrlComponent(parameters, "trxid", ss); + curl_easy_setopt(d_c, CURLOPT_POST, 1); + curl_easy_setopt(d_c, CURLOPT_POSTFIELDSIZE, 0); + } else if (method == "commitTransaction" || method == "abortTransaction") { + addUrlComponent(parameters, "trxid", ss); + curl_easy_setopt(d_c, CURLOPT_POST, 1); + curl_easy_setopt(d_c, CURLOPT_POSTFIELDSIZE, 0); + } else if (method == "calculateSOASerial") { + addUrlComponent(parameters, "domain", ss); + std::string out = buildMemberListArgs("sd", ¶meters["sd"], d_c); + curl_easy_setopt(d_c, CURLOPT_POSTFIELDSIZE, out.size()); + curl_easy_setopt(d_c, CURLOPT_COPYPOSTFIELDS, out.c_str()); } else if (method == "setDomainMetadata") { - int n=0; // copy all metadata values into post std::stringstream ss2; const rapidjson::Value& param = parameters["value"]; @@ -187,7 +215,7 @@ void HTTPConnector::requestbuilder(const std::string &method, const rapidjson::V // this one has values too if (param.IsArray()) { for(rapidjson::Value::ConstValueIterator i = param.Begin(); i != param.End(); i++) { - ss2 << "value" << (++n) << "=" << i->GetString() << "&"; + ss2 << "value[]=" << i->GetString() << "&"; } } sparam = ss2.str(); @@ -196,6 +224,12 @@ void HTTPConnector::requestbuilder(const std::string &method, const rapidjson::V } else if (method == "removeDomainKey") { // this one is delete curl_easy_setopt(d_c, CURLOPT_CUSTOMREQUEST, "DELETE"); + } else if (method == "setNotified") { + tmpstr = (char*)malloc(128); + snprintf(tmpstr, 128, "serial=%u", parameters["serial"].GetInt()); + curl_easy_setopt(d_c, CURLOPT_POSTFIELDSIZE, strlen(tmpstr)); + curl_easy_setopt(d_c, CURLOPT_COPYPOSTFIELDS, tmpstr); + free(tmpstr); } else { // perform normal get curl_easy_setopt(d_c, CURLOPT_HTTPGET, 1); @@ -210,11 +244,16 @@ void HTTPConnector::requestbuilder(const std::string &method, const rapidjson::V !strncmp(member,"name",4) || !strncmp(member,"kind",4) || !strncmp(member,"qtype",5) || !strncmp(member,"id",2) || !strncmp(member,"key",3)) continue; - json2string(parameters[member], sparam); - snprintf(header, sizeof header, "X-RemoteBackend-%s: %s", iter->name.GetString(), sparam.c_str()); - (*slist) = curl_slist_append((*slist), header); + if (json2string(parameters[member], sparam)) { + snprintf(header, sizeof header, "X-RemoteBackend-%s: %s", iter->name.GetString(), sparam.c_str()); + (*slist) = curl_slist_append((*slist), header); + } }; + // finally add suffix and store url + ss << d_url_suffix; + curl_easy_setopt(d_c, CURLOPT_URL, ss.str().c_str()); + // store headers into request curl_easy_setopt(d_c, CURLOPT_HTTPHEADER, *slist); } diff --git a/modules/remotebackend/regression-tests/dnsbackend.rb b/modules/remotebackend/regression-tests/dnsbackend.rb index dbf9e9583..aaac5a5b7 100644 --- a/modules/remotebackend/regression-tests/dnsbackend.rb +++ b/modules/remotebackend/regression-tests/dnsbackend.rb @@ -8,6 +8,28 @@ class DNSBackendHandler < WEBrick::HTTPServlet::AbstractServlet @f = File.open("/tmp/tmp.txt","a") end + def parse_arrays(params) + newparams = {} + params.each do |key,val| + if key=~/^(.*)\[(.*)\]\[(.*)\]/ + newparams[$1] = {} unless newparams.has_key? $1 + newparams[$1][$2] = {} unless newparams[$1].has_key? $2 + newparams[$1][$2][$3] = val + params.delete key + elsif key=~/^(.*)\[(.*)\]/ + if $2 == "" + newparams[$1] = [] unless newparams.has_key? $1 + newparams[$1] << val + else + newparams[$1] = {} unless newparams.has_key? $1 + newparams[$1][$2] = val + end + params.delete key + end + end + params.merge newparams + end + def parse_url(url) url = url.split('/') method = url.shift.downcase @@ -75,13 +97,8 @@ class DNSBackendHandler < WEBrick::HTTPServlet::AbstractServlet } end - if method == "do_setdomainmetadata" - args["value"] = [] - args.each do |k,a| - args["value"] << a if k[/^value/] - end - end - + args = parse_arrays args + @f.puts method @f.puts args diff --git a/modules/remotebackend/remotebackend.cc b/modules/remotebackend/remotebackend.cc index c6b58bc1c..e81a6f3c8 100644 --- a/modules/remotebackend/remotebackend.cc +++ b/modules/remotebackend/remotebackend.cc @@ -331,7 +331,10 @@ bool RemoteBackend::getDomainKeys(const std::string& name, unsigned int kind, st DNSBackend::KeyData key; key.id = (*iter)["id"].GetUint(); key.flags = (*iter)["flags"].GetUint(); - key.active = (*iter)["active"].GetBool(); + if ((*iter)["active"].IsBool()) + key.active = (*iter)["active"].GetBool(); + else + key.active = ((*iter)["active"].GetInt() != 0 ? true : false ); // case where it's returned as non-boolean key.content = (*iter)["content"].GetString(); keys.push_back(key); } @@ -346,7 +349,7 @@ bool RemoteBackend::removeDomainKey(const string& name, unsigned int id) { if (d_dnssec == false) return false; query.SetObject(); - JSON_ADD_MEMBER(query, "method", "getDomainKeys", query.GetAllocator()); + JSON_ADD_MEMBER(query, "method", "removeDomainKey", query.GetAllocator()); parameters.SetObject(); JSON_ADD_MEMBER(parameters, "name", name.c_str(), query.GetAllocator()); JSON_ADD_MEMBER(parameters, "id", id, query.GetAllocator()); @@ -503,7 +506,7 @@ void RemoteBackend::setNotified(uint32_t id, uint32_t serial) { JSON_ADD_MEMBER(query, "method", "setNotified", query.GetAllocator()); parameters.SetObject(); JSON_ADD_MEMBER(parameters, "id", id, query.GetAllocator()); - JSON_ADD_MEMBER(parameters, "serial", id, query.GetAllocator()); + JSON_ADD_MEMBER(parameters, "serial", serial, query.GetAllocator()); query.AddMember("parameters", parameters, query.GetAllocator()); if (connector->send(query) == false || connector->recv(answer) == false) { @@ -525,7 +528,7 @@ bool RemoteBackend::superMasterBackend(const string &ip, const string &domain, c rrset.SetArray(); rrset.Reserve(nsset.size(), query.GetAllocator()); for(rapidjson::SizeType i = 0; i < nsset.size(); i++) { - rapidjson::Value &rr = rrset[i]; + rapidjson::Value rr; rr.SetObject(); JSON_ADD_MEMBER(rr, "qtype", nsset[i].qtype.getName().c_str(), query.GetAllocator()); JSON_ADD_MEMBER(rr, "qname", nsset[i].qname.c_str(), query.GetAllocator()); @@ -534,10 +537,13 @@ bool RemoteBackend::superMasterBackend(const string &ip, const string &domain, c JSON_ADD_MEMBER(rr, "ttl", nsset[i].ttl, query.GetAllocator()); JSON_ADD_MEMBER(rr, "priority", nsset[i].priority, query.GetAllocator()); JSON_ADD_MEMBER(rr, "auth", nsset[i].auth, query.GetAllocator()); + rrset.PushBack(rr, query.GetAllocator()); } parameters.AddMember("nsset", rrset, query.GetAllocator()); query.AddMember("parameters", parameters, query.GetAllocator()); + *ddb = 0; + if (connector->send(query) == false || connector->recv(answer) == false) return false; @@ -577,11 +583,13 @@ bool RemoteBackend::replaceRRSet(uint32_t domain_id, const string& qname, const JSON_ADD_MEMBER(parameters, "domain_id", domain_id, query.GetAllocator()); JSON_ADD_MEMBER(parameters, "qname", qname.c_str(), query.GetAllocator()); JSON_ADD_MEMBER(parameters, "qtype", qtype.getName().c_str(), query.GetAllocator()); + JSON_ADD_MEMBER(parameters, "trxid", d_trxid, query.GetAllocator()); + rj_rrset.SetArray(); rj_rrset.Reserve(rrset.size(), query.GetAllocator()); for(rapidjson::SizeType i = 0; i < rrset.size(); i++) { - rapidjson::Value &rr = rj_rrset[i]; + rapidjson::Value rr; rr.SetObject(); JSON_ADD_MEMBER(rr, "qtype", rrset[i].qtype.getName().c_str(), query.GetAllocator()); JSON_ADD_MEMBER(rr, "qname", rrset[i].qname.c_str(), query.GetAllocator()); @@ -590,6 +598,7 @@ bool RemoteBackend::replaceRRSet(uint32_t domain_id, const string& qname, const JSON_ADD_MEMBER(rr, "ttl", rrset[i].ttl, query.GetAllocator()); JSON_ADD_MEMBER(rr, "priority", rrset[i].priority, query.GetAllocator()); JSON_ADD_MEMBER(rr, "auth", rrset[i].auth, query.GetAllocator()); + rj_rrset.PushBack(rr, query.GetAllocator()); } parameters.AddMember("rrset", rj_rrset, query.GetAllocator()); query.AddMember("parameters", parameters, query.GetAllocator()); @@ -602,20 +611,26 @@ bool RemoteBackend::replaceRRSet(uint32_t domain_id, const string& qname, const bool RemoteBackend::feedRecord(const DNSResourceRecord &rr, string *ordername) { rapidjson::Document query,answer; - rapidjson::Value parameters; + rapidjson::Value parameters,rj_rr; query.SetObject(); JSON_ADD_MEMBER(query, "method", "feedRecord", query.GetAllocator()); parameters.SetObject(); - JSON_ADD_MEMBER(parameters, "qtype", rr.qtype.getName().c_str(), query.GetAllocator()); - JSON_ADD_MEMBER(parameters, "qname", rr.qname.c_str(), query.GetAllocator()); - JSON_ADD_MEMBER(parameters, "qclass", QClass::IN, query.GetAllocator()); - JSON_ADD_MEMBER(parameters, "content", rr.content.c_str(), query.GetAllocator()); - JSON_ADD_MEMBER(parameters, "ttl", rr.ttl, query.GetAllocator()); - JSON_ADD_MEMBER(parameters, "priority", rr.priority, query.GetAllocator()); - JSON_ADD_MEMBER(parameters, "auth", rr.auth, query.GetAllocator()); + rj_rr.SetObject(); + JSON_ADD_MEMBER(rj_rr, "qtype", rr.qtype.getName().c_str(), query.GetAllocator()); + JSON_ADD_MEMBER(rj_rr, "qname", rr.qname.c_str(), query.GetAllocator()); + JSON_ADD_MEMBER(rj_rr, "qclass", QClass::IN, query.GetAllocator()); + JSON_ADD_MEMBER(rj_rr, "content", rr.content.c_str(), query.GetAllocator()); + JSON_ADD_MEMBER(rj_rr, "ttl", rr.ttl, query.GetAllocator()); + JSON_ADD_MEMBER(rj_rr, "priority", rr.priority, query.GetAllocator()); + JSON_ADD_MEMBER(rj_rr, "auth", rr.auth, query.GetAllocator()); + parameters.AddMember("rr", rj_rr, query.GetAllocator()); + + JSON_ADD_MEMBER(parameters, "trxid", d_trxid, query.GetAllocator()); + if (ordername) { JSON_ADD_MEMBER(parameters, "ordername", ordername->c_str(), query.GetAllocator()); } + query.AddMember("parameters", parameters, query.GetAllocator()); if (connector->send(query) == false || connector->recv(answer) == false) @@ -631,6 +646,7 @@ bool RemoteBackend::feedEnts(int domain_id, set& nonterm) { JSON_ADD_MEMBER(query, "method", "feedEnts", query.GetAllocator()); parameters.SetObject(); JSON_ADD_MEMBER(parameters, "domain_id", domain_id, query.GetAllocator()); + JSON_ADD_MEMBER(parameters, "trxid", d_trxid, query.GetAllocator()); nts.SetArray(); BOOST_FOREACH(const string &t, nonterm) { nts.PushBack(t.c_str(), query.GetAllocator()); @@ -655,6 +671,7 @@ bool RemoteBackend::feedEnts3(int domain_id, const string &domain, set & JSON_ADD_MEMBER(parameters, "times", times, query.GetAllocator()); JSON_ADD_MEMBER(parameters, "salt", salt.c_str(), query.GetAllocator()); JSON_ADD_MEMBER(parameters, "narrow", narrow, query.GetAllocator()); + JSON_ADD_MEMBER(parameters, "trxid", d_trxid, query.GetAllocator()); nts.SetArray(); BOOST_FOREACH(const string &t, nonterm) { diff --git a/modules/remotebackend/remotebackend.hh b/modules/remotebackend/remotebackend.hh index 8984aa089..9e7d052f6 100644 --- a/modules/remotebackend/remotebackend.hh +++ b/modules/remotebackend/remotebackend.hh @@ -66,7 +66,7 @@ class HTTPConnector: public Connector { CURL *d_c; std::string d_data; int timeout; - void json2string(const rapidjson::Value &input, std::string &output); + bool json2string(const rapidjson::Value &input, std::string &output); void requestbuilder(const std::string &method, const rapidjson::Value ¶meters, struct curl_slist **slist); void addUrlComponent(const rapidjson::Value ¶meters, const char *element, std::stringstream& ss); }; diff --git a/modules/remotebackend/test-remotebackend-http.cc b/modules/remotebackend/test-remotebackend-http.cc new file mode 100644 index 000000000..f91d05ad9 --- /dev/null +++ b/modules/remotebackend/test-remotebackend-http.cc @@ -0,0 +1,73 @@ +#include "pdns/namespaces.hh" +#include +#include +#include +#include +#include +#include +#include +#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; + +#ifdef REMOTEBACKEND_HTTP + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MAIN +#define BOOST_TEST_MODULE unit + +#include +#include +#include +#include + +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")="http:url=http://localhost:62434/dns"; + ::arg().set("remote-dnssec")="yes"; + be = BackendMakers().all()[0]; + } catch (AhuException &ex) { + BOOST_TEST_MESSAGE("Cannot start remotebackend: " << ex.reason ); + }; + } + ~RemotebackendSetup() { } +}; + +BOOST_GLOBAL_FIXTURE( RemotebackendSetup ); + +#else + +#include + +int main(void) { + std::cout << "No HTTP support in remotebackend - skipping test" << std::endl; + return 0; +} + +#endif diff --git a/modules/remotebackend/test-remotebackend-keys.hh b/modules/remotebackend/test-remotebackend-keys.hh new file mode 100644 index 000000000..2ccc9b42b --- /dev/null +++ b/modules/remotebackend/test-remotebackend-keys.hh @@ -0,0 +1,3 @@ +DNSBackend::KeyData k1 = {1, 257, true, std::string("Private-key-format: v1.2\nAlgorithm: 5 (RSASHA1)\nModulus: qpe9fxlN4dBT38cLPWtqljZhcJjbqRprj9XsYmf2/uFu4kA5sHYrlQY7H9lpzGJPRfOAfxShBpKs1AVaVInfJQ==\nPublicExponent: AQAB\nPrivateExponent: Ad3YogzXvVDLsWuAfioY571QlolbdTbzVlhLEMLD6dSRx+xcZgw6c27ak2HAH00iSKTvqK3AyeaK8Eqy/oJ5QQ==\nPrime1: wo8LZrdU2y0xLGCeLhwziQDDtTMi18NEIwlx8tUPnhs=\nPrime2: 4HcuFqgo7NOiXFvN+V2PT+QaIt2+oi6D2m8/qtTDS78=\nExponent1: GUdCoPbi9JM7l1t6Ud1iKMPLqchaF5SMTs0UXAuous8=\nExponent2: nzgKqimX9f1corTAEw0pddrwKyEtcu8ZuhzFhZCsAxM=\nCoefficient: YGNxbulf5GTNiIu0oNKmAF0khNtx9layjOPEI0R4/RY=") }; + +DNSBackend::KeyData k2 = {2, 256, true, std::string("Private-key-format: v1.2\nAlgorithm: 5 (RSASHA1)\nModulus: tY2TAMgL/whZdSbn2aci4wcMqohO24KQAaq5RlTRwQ33M8FYdW5fZ3DMdMsSLQUkjGnKJPKEdN3Qd4Z5b18f+w==\nPublicExponent: AQAB\nPrivateExponent: BB6xibPNPrBV0PUp3CQq0OdFpk9v9EZ2NiBFrA7osG5mGIZICqgOx/zlHiHKmX4OLmL28oU7jPKgogeuONXJQQ==\nPrime1: yjxe/iHQ4IBWpvCmuGqhxApWF+DY9LADIP7bM3Ejf3M=\nPrime2: 5dGWTyYEQRBVK74q1a64iXgaNuYm1pbClvvZ6ccCq1k=\nExponent1: TwM5RebmWeAqerzJFoIqw5IaQugJO8hM4KZR9A4/BTs=\nExponent2: bpV2HSmu3Fvuj7jWxbFoDIXlH0uJnrI2eg4/4hSnvSk=\nCoefficient: e2uDDWN2zXwYa2P6VQBWQ4mR1ZZjFEtO/+YqOJZun1Y=") }; diff --git a/modules/remotebackend/test-remotebackend-pipe.cc b/modules/remotebackend/test-remotebackend-pipe.cc new file mode 100644 index 000000000..9a4eb1554 --- /dev/null +++ b/modules/remotebackend/test-remotebackend-pipe.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")="pipe:command=unittest_pipe.rb"; + ::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/test-remotebackend.cc b/modules/remotebackend/test-remotebackend.cc new file mode 100644 index 000000000..28427c8a6 --- /dev/null +++ b/modules/remotebackend/test-remotebackend.cc @@ -0,0 +1,247 @@ +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_NO_MAIN + +#include +#include +#include +#include +#include "pdns/namespaces.hh" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pdns/json.hh" +#include "pdns/statbag.hh" +#include "pdns/packetcache.hh" + +#include "test-remotebackend-keys.hh" + +extern DNSBackend *be; + +BOOST_AUTO_TEST_SUITE(test_remotebackend_so) + +BOOST_AUTO_TEST_CASE(test_method_lookup) { + BOOST_TEST_MESSAGE("Testing lookup method"); + DNSResourceRecord rr; + be->lookup(QType(QType::SOA), "unit.test"); + // then try to get() + BOOST_CHECK(be->get(rr)); // and this should be TRUE. + // then we check rr contains what we expect + BOOST_CHECK_EQUAL(rr.qname, "unit.test"); + BOOST_CHECK_MESSAGE(rr.qtype == QType::SOA, "returned qtype was not SOA"); + BOOST_CHECK_EQUAL(rr.content, "ns.unit.test hostmaster.unit.test 1 2 3 4 5 6"); + BOOST_CHECK_EQUAL(rr.ttl, 300); +} + +BOOST_AUTO_TEST_CASE(test_method_list) { + int record_count = 0; + DNSResourceRecord rr; + + BOOST_TEST_MESSAGE("Testing list method"); + be->list("unit.test", -1); + while(be->get(rr)) record_count++; + + BOOST_CHECK_EQUAL(record_count, 5); // number of records our test domain has +} + +BOOST_AUTO_TEST_CASE(test_method_doesDNSSEC) { + BOOST_TEST_MESSAGE("Testing doesDNSSEC method"); + BOOST_CHECK(be->doesDNSSEC()); // should be true +} + +BOOST_AUTO_TEST_CASE(test_method_setDomainMetadata) { + std::vector meta; + meta.push_back("VALUE"); + BOOST_TEST_MESSAGE("Testing setDomainMetadata method"); + BOOST_CHECK(be->setDomainMetadata("unit.test","TEST", meta)); +} + +BOOST_AUTO_TEST_CASE(test_method_getDomainMetadata) { + std::vector meta; + BOOST_TEST_MESSAGE("Testing getDomainMetadata method"); + be->getDomainMetadata("unit.test","TEST", meta); + BOOST_CHECK_EQUAL(meta.size(), 1); + // in case we got more than one value, which would be unexpected + // but not fatal + if (meta.size() > 0) + BOOST_CHECK_EQUAL(meta[0], "VALUE"); +} + +BOOST_AUTO_TEST_CASE(test_method_addDomainKey) { + BOOST_TEST_MESSAGE("Testing addDomainKey method"); + BOOST_CHECK_EQUAL(be->addDomainKey("unit.test",k1), 1); + BOOST_CHECK_EQUAL(be->addDomainKey("unit.test",k2), 2); +} + +BOOST_AUTO_TEST_CASE(test_method_getDomainKeys) { + std::vector keys; + BOOST_TEST_MESSAGE("Testing getDomainKeys method"); + // we expect to get two keys + be->getDomainKeys("unit.test",0,keys); + BOOST_CHECK_EQUAL(keys.size(), 2); + // in case we got more than 2 keys, which would be unexpected + // but not fatal + if (keys.size() > 1) { + // check that we have two keys + BOOST_FOREACH(DNSBackend::KeyData &kd, keys) { + BOOST_CHECK(kd.id > 0); + BOOST_CHECK(kd.flags == 256 || kd.flags == 257); + BOOST_CHECK(kd.active == true); + BOOST_CHECK(kd.content.size() > 500); + } + } +} + +BOOST_AUTO_TEST_CASE(test_method_deactivateDomainKey) { + BOOST_TEST_MESSAGE("Testing deactivateDomainKey method"); + BOOST_CHECK(be->deactivateDomainKey("unit.test",1)); +} + +BOOST_AUTO_TEST_CASE(test_method_activateDomainKey) { + BOOST_TEST_MESSAGE("Testing activateDomainKey method"); + BOOST_CHECK(be->activateDomainKey("unit.test",1)); +} + +BOOST_AUTO_TEST_CASE(test_method_removeDomainKey) { + BOOST_CHECK(be->removeDomainKey("unit.test",2)); + BOOST_CHECK(be->removeDomainKey("unit.test",1)); +} + +BOOST_AUTO_TEST_CASE(test_method_getBeforeAndAfterNamesAbsolute) { + std::string unhashed,before,after; + BOOST_TEST_MESSAGE("Testing getBeforeAndAfterNamesAbsolute method"); + + be->getBeforeAndAfterNamesAbsolute(-1, "middle.unit.test", unhashed, before, after); + BOOST_CHECK_EQUAL(unhashed, "middle"); + BOOST_CHECK_EQUAL(before, "begin"); + BOOST_CHECK_EQUAL(after, "stop"); +} + +BOOST_AUTO_TEST_CASE(test_method_getTSIGKey) { + std::string algorithm, content; + BOOST_TEST_MESSAGE("Testing getTSIGKey method"); + be->getTSIGKey("unit.test",&algorithm,&content); + BOOST_CHECK_EQUAL(algorithm, "NULL"); + BOOST_CHECK_EQUAL(content, "NULL"); +} + +BOOST_AUTO_TEST_CASE(test_method_setNotified) { + BOOST_TEST_MESSAGE("Testing setNotified method"); + be->setNotified(1, 2); + BOOST_CHECK(true); // we check this on next step +} + +BOOST_AUTO_TEST_CASE(test_method_getDomainInfo) { + DomainInfo di; + BOOST_TEST_MESSAGE("Testing getDomainInfo method"); + be->getDomainInfo("unit.test", di); + BOOST_CHECK_EQUAL(di.zone, "unit.test"); + BOOST_CHECK_EQUAL(di.serial, 2); + BOOST_CHECK_EQUAL(di.notified_serial, 2); + BOOST_CHECK_EQUAL(di.kind, DomainInfo::Native); + BOOST_CHECK_EQUAL(di.backend, be); +} + +BOOST_AUTO_TEST_CASE(test_method_superMasterBackend) { + DNSResourceRecord rr; + std::vector nsset; + DNSBackend *dbd; + BOOST_TEST_MESSAGE("Testing superMasterBackend method"); + + rr.qname = "example.com"; + rr.qtype = QType::NS; + rr.qclass = QClass::IN; + rr.ttl = 300; + rr.content = "ns1.example.com"; + nsset.push_back(rr); + rr.qname = "example.com"; + rr.qtype = QType::NS; + rr.qclass = QClass::IN; + rr.ttl = 300; + rr.content = "ns2.example.com"; + nsset.push_back(rr); + + BOOST_CHECK(be->superMasterBackend("10.0.0.1", "example.com", nsset, NULL, &dbd)); + + // let's see what we got + BOOST_CHECK_EQUAL(dbd, be); +} + +BOOST_AUTO_TEST_CASE(test_method_createSlaveDomain) { + BOOST_TEST_MESSAGE("Testing createSlaveDomain method"); + BOOST_CHECK(be->createSlaveDomain("10.0.0.1", "pirate.unit.test", "")); +} + +BOOST_AUTO_TEST_CASE(test_method_feedRecord) { + DNSResourceRecord rr; + BOOST_TEST_MESSAGE("Testing feedRecord method"); + be->startTransaction("example.com",2); + rr.qname = "example.com"; + rr.qtype = QType::SOA; + rr.qclass = QClass::IN; + rr.ttl = 300; + rr.content = "ns1.example.com hostmaster.example.com 2013013441 7200 3600 1209600 300"; + BOOST_CHECK(be->feedRecord(rr, NULL)); + rr.qname = "replace.example.com"; + rr.qtype = QType::A; + rr.qclass = QClass::IN; + rr.ttl = 300; + rr.content = "127.0.0.1"; + BOOST_CHECK(be->feedRecord(rr, NULL)); + be->commitTransaction(); +} + +BOOST_AUTO_TEST_CASE(test_method_replaceRRSet) { + be->startTransaction("example.com",2); + DNSResourceRecord rr; + std::vector rrset; + BOOST_TEST_MESSAGE("Testing replaceRRSet method"); + rr.qname = "replace.example.com"; + rr.qtype = QType::A; + rr.qclass = QClass::IN; + rr.ttl = 300; + rr.content = "1.1.1.1"; + rrset.push_back(rr); + BOOST_CHECK(be->replaceRRSet(2, "replace.example.com", QType(QType::A), rrset)); + be->commitTransaction(); +} + +BOOST_AUTO_TEST_CASE(test_method_feedEnts) { + BOOST_TEST_MESSAGE("Testing feedEnts method"); + be->startTransaction("example.com",2); + set nonterm = boost::assign::list_of("_udp")("_sip._udp"); + BOOST_CHECK(be->feedEnts(2, nonterm)); + be->commitTransaction(); +} + +BOOST_AUTO_TEST_CASE(test_method_feedEnts3) { + BOOST_TEST_MESSAGE("Testing feedEnts3 method"); + be->startTransaction("example.com",2); + set nonterm = boost::assign::list_of("_udp")("_sip._udp"); + BOOST_CHECK(be->feedEnts3(2, "example.com", nonterm, 1, "\xaa\xbb\xcc\xdd", 0)); + be->commitTransaction(); +} + +BOOST_AUTO_TEST_CASE(test_method_abortTransaction) { + BOOST_TEST_MESSAGE("Testing abortTransaction method"); + be->startTransaction("example.com",2); + BOOST_CHECK(be->abortTransaction()); +} + +BOOST_AUTO_TEST_CASE(test_method_calculateSOASerial) { + SOAData sd; + time_t serial; + + be->getSOA("unit.test",sd); + BOOST_CHECK(be->calculateSOASerial("unit.test",sd,serial)); + + BOOST_CHECK_EQUAL(serial, 2013060300); +} + +BOOST_AUTO_TEST_SUITE_END(); diff --git a/modules/remotebackend/testrunner.sh b/modules/remotebackend/testrunner.sh new file mode 100755 index 000000000..2828ad061 --- /dev/null +++ b/modules/remotebackend/testrunner.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +webrick_pid="" + +if [ x"$REMOTEBACKEND_HTTP" == "xyes" ]; then + +if [ `basename "$1"` == "test_remotebackend_http" ]; then + ./unittest_http.rb & + webrick_pid=$! + sleep 1 +fi + +$1 +rv=$? + +if [ ! -z "$webrick_pid" ]; then + kill -TERM $webrick_pid + # wait a moment for it to die + i=0 + while [ $i -lt 5 ]; do + sleep 1 + kill -0 $webrick_pid 2>/dev/null + if [ $? -ne 0 ]; then break; fi + let i=i+1 + done +fi + +else + +$1 +rv=$? + +fi + +exit $rv diff --git a/modules/remotebackend/unittest.rb b/modules/remotebackend/unittest.rb new file mode 100644 index 000000000..69ae27fe8 --- /dev/null +++ b/modules/remotebackend/unittest.rb @@ -0,0 +1,224 @@ +require 'rubygems' +require 'json' + +# define a simple $domain + +$ttl = 300 +$notified_serial = 1 + +$domain = { + "unit.test" => { + "SOA" => ["ns.unit.test hostmaster.unit.test 1 2 3 4 5 6"], + "NS" => ["ns1.unit.test", "ns2.unit.test"], + }, + "ns1.unit.test" => { + "A" => ["10.0.0.1"] + }, + "ns2.unit.test" => { + "A" => ["10.0.0.2"] + } +} + +$meta = {} + +$keys = {} + +class Handler + def initialize + end + + def rr(qname, qtype, content, ttl, priority = 0, auth = 1, domain_id = -1) + {:qname => qname, :qtype => qtype, :content => content, :ttl => ttl.to_i, :priority => priority.to_i, :auth => auth.to_i, :domain_id => domain_id.to_i} + end + + def do_initialize(*args) + return true, "Test bench initialized" + end + + def do_lookup(args) + ret = [] + if $domain.has_key?(args["qname"]) + if $domain[args["qname"]].has_key?(args["qtype"]) + $domain[args["qname"]][args["qtype"]].each do |rd| + ret << rr(args["qname"], args["qtype"], rd, $ttl) + end + elsif args["qtype"] == 'ANY' + $domain[args["qname"]].each do |qt,qr| + qr.each do |rd| + ret << rr(args["qname"], qt, rd, $ttl) + end + end + end + end + [false] unless ret.size>0 + [ret] + end + + def do_list(args) + ret = [] + if args["zonename"] == "unit.test" + $domain.each do |qname,rdata| + rdata.each do |rtype,rc| + rc.each do |rd| + ret << rr(qname,rtype,rd,$ttl) + end + end + end + end + [false] unless ret.size>0 + [ret] + end + + def do_getdomainmetadata(args) + return [ $meta[args["name"]][args["kind"]] ] if $meta.has_key?(args["name"]) and $meta[args["name"]].has_key?(args["kind"]) + return [false] + end + + def do_setdomainmetadata(args) + $meta[args["name"].to_s] = {} unless $meta.has_key? args["name"] + $meta[args["name"].to_s][args["kind"].to_s] = args["value"].to_a + [true] + end + + def do_adddomainkey(args) + $keys[args["name"]] = [] unless $keys.has_key? args["name"] + id=$keys[args["name"]].size + 1 + args["key"]["id"] = id + $keys[args["name"]] << args["key"] + [id] + end + + def do_getdomainkeys(args) + if $keys.has_key? args["name"] + return [ $keys[args["name"]] ] + end + [false] + end + + def do_activatedomainkey(args) + args["id"] = args["id"].to_i + if $keys.has_key? args["name"] + if $keys[args["name"]][args["id"]-1] + $keys[args["name"]][args["id"]-1]["active"] = true + return [true] + end + end + [false] + end + + def do_deactivatedomainkey(args) + args["id"] = args["id"].to_i + if $keys.has_key? args["name"] + if $keys[args["name"]][args["id"]-1] + $keys[args["name"]][args["id"]-1]["active"] = false + return [true] + end + end + [false] + end + + def do_removedomainkey(args) + args["id"] = args["id"].to_i + if $keys.has_key? args["name"] + if $keys[args["name"]][args["id"]-1] + $keys[args["name"]].delete_at args["id"]-1 + return [true] + end + end + [false] + end + + def do_getbeforeandafternamesabsolute(args) + return [ { :unhashed => "middle", :before => "begin", :after => "stop" } ] if args["qname"] == 'middle.unit.test' + [false] + end + + def do_gettsigkey(args) + if args["name"] == "unit.test" + return [{:algorithm => "NULL", :content => "NULL"}] + end + [false] + end + + def do_setnotified(args) + if args["id"].to_i == 1 + $notified_serial = args["serial"].to_i + return [true] + end + [false] + end + + def do_getdomaininfo(args) + if args["name"] == "unit.test" + return [{ + :id => 1, + :zone => "unit.test", + :masters => ["10.0.0.1"], + :notified_serial => $notified_serial, + :serial => $notified_serial, + :last_check => Time.now.to_i, + :kind => 'native' + }] + end + [false] + end + + def do_supermasterbackend(args) + $domain[args["domain"]] = { + "NS" => args["nsset"] + } + [true] + end + + def do_createslavedomain(args) + $domain[args["domain"]] = { + } + [true] + end + + def do_feedrecord(args) + args.delete "trxid" + rr = args["rr"] + name = rr["qname"] + qtype = rr["qtype"] + $domain[name] = {} unless $domain.has_key? name + $domain[name][qtype] = [] unless $domain[name].has_key? qtype + $domain[name][qtype] << rr["content"] + [true] + end + + def do_replacerrset(args) + $domain[args["qname"]].delete args["qtype"] if $domain.has_key?(args["qname"]) and $domain[args["qname"]].has_key?(args["qtype"]) + args["rrset"] = args["rrset"].values if args["rrset"].is_a?(Hash) + args["rrset"].each do |rr| + self.do_feedrecord({"trxid" => args["trxid"], "rr" => rr}) + end + [true] + end + + def do_feedents(args) + [true] + end + + def do_feedents3(args) + [true] + end + + def do_starttransaction(args) + [true] + end + + def do_committransaction(args) + [true] + end + + def do_aborttransaction(args) + [true] + end + + def do_calculatesoaserial(args) + return [2013060300] if args["sd"]["qname"] == "unit.test" + [false] + end +end + diff --git a/modules/remotebackend/unittest_http.rb b/modules/remotebackend/unittest_http.rb new file mode 100755 index 000000000..25b9f3689 --- /dev/null +++ b/modules/remotebackend/unittest_http.rb @@ -0,0 +1,179 @@ +#!/usr/bin/ruby + +require 'json' +require 'thread' +require "rubygems" +require "webrick" +require "./unittest" + +class DNSBackendHandler < WEBrick::HTTPServlet::AbstractServlet + def initialize(server, dnsbackend) + @dnsbackend = dnsbackend + @semaphore = Mutex.new + @f = File.open("/tmp/tmp.txt","a") + end + + def parse_arrays(params) + newparams = {} + params.each do |key,val| + if key=~/^(.*)\[(.*)\]\[(.*)\]/ + newparams[$1] = {} unless newparams.has_key? $1 + newparams[$1][$2] = {} unless newparams[$1].has_key? $2 + newparams[$1][$2][$3] = val + params.delete key + elsif key=~/^(.*)\[(.*)\]/ + if $2 == "" + newparams[$1] = [] unless newparams.has_key? $1 + newparams[$1] << val + else + newparams[$1] = {} unless newparams.has_key? $1 + newparams[$1][$2] = val + end + params.delete key + end + end + params.merge newparams + end + + def parse_url(url) + url = url.split('/') + method = url.shift.downcase + + # do some determining based on method names + args = case method + when "lookup" + { + "qname" => url.shift, + "qtype" => url.shift + } + when "list" + { + "zonename" => url.shift + } + when "getbeforeandafternamesabsolute", "getbeforeandafternames" + { + "id" => url.shift.to_i, + "qname" => url.shift + } + when "getdomainmetadata", "setdomainmetadata", "getdomainkeys" + { + "name" => url.shift, + "kind" => url.shift + } + when "removedomainkey", "activatedomainkey", "deactivatedomainkey" + { + "id" => url.shift.to_i, + "name" => url.shift + } + when "adddomainkey", "gettsigkey", "getdomaininfo" + { + "name" => url.shift + } + when "setnotified", "feedents" + { + "id" => url.shift.to_i + } + when "supermasterbackend", "createslavedomain" + { + "ip" => url.shift, + "domain" => url.shift + } + when "feedents3" + { + "id" => url.shift.to_i, + "domain" => url.shift + } + when "starttransaction" + { + "id" => url.shift.to_i, + "domain" => url.shift, + "trxid" => url.shift.to_i + } + when "committransaction", "aborttransaction" + { + "trxid" => url.shift.to_i + } + when "replacerrset" + { + "id" => url.shift.to_i, + "qname" => url.shift, + "qtype" => url.shift + } + else + {} + end + + [method, args] + end + + def do_GET(req,res) + req.continue + + tmp = req.path[/dns\/(.*)/,1] + return 400, "Bad request" if (tmp.nil?) + + method, args = parse_url(tmp) + + method = "do_#{method}" + + # get more arguments + req.each do |k,v| + attr = k[/X-RemoteBackend-(.*)/,1] + if attr + args[attr] = v + end + end + + args = args.merge req.query + + if method == "do_adddomainkey" + args["key"] = { + "flags" => args.delete("flags").to_i, + "active" => args.delete("active").to_i, + "content" => args.delete("content") + } + end + + args = parse_arrays args + + @f.puts method + @f.puts args + + @semaphore.synchronize do + if @dnsbackend.respond_to?(method.to_sym) + result, log = @dnsbackend.send(method.to_sym, args) + body = {:result => result, :log => log} + res.status = 200 + res["Content-Type"] = "application/javascript; charset=utf-8" + res.body = body.to_json + else + res.status = 404 + res["Content-Type"] = "application/javascript; charset=utf-8" + res.body = ({:result => false, :log => ["Method not found"]}).to_json + end + end + end + + def do_DELETE(req,res) + do_GET(req,res) + end + + def do_POST(req,res) + do_GET(req,res) + end +end + +server = WEBrick::HTTPServer.new( + :Port=>62434, + :BindAddress=>"localhost", +# Logger: WEBrick::Log.new("remotebackend-server.log"), + :AccessLog=>[ [ File.open("remotebackend-access.log", "w"), WEBrick::AccessLog::COMBINED_LOG_FORMAT ] ] +) + +be = Handler.new +server.mount "/dns", DNSBackendHandler, be + +trap('INT') { server.stop } +trap('TERM') { server.stop } + +server.start diff --git a/modules/remotebackend/unittest_pipe.rb b/modules/remotebackend/unittest_pipe.rb new file mode 100755 index 000000000..ea24e130b --- /dev/null +++ b/modules/remotebackend/unittest_pipe.rb @@ -0,0 +1,38 @@ +#!/usr/bin/ruby + +require 'rubygems' +require 'json' +require './unittest' + +h = Handler.new() +f = File.open "/tmp/tmp.txt","a" + +STDOUT.sync = true +begin + STDIN.each_line do |line| + f.puts line + # expect json + input = {} + line = line.strip + next if line.empty? + begin + input = JSON.parse(line) + method = "do_#{input["method"].downcase}" + args = input["parameters"] + + if h.respond_to?(method.to_sym) == false + res = false + elsif args.size > 0 + res, log = h.send(method,args) + else + res, log = h.send(method) + end + puts ({:result => res, :log => log}).to_json + f.puts({:result => res, :log => log}).to_json + rescue JSON::ParserError + puts ({:result => false, :log => "Cannot parse input #{line}"}).to_json + next + end + end +rescue SystemExit, Interrupt +end