From fd378ba9299a056923efd7c80de6cd2f3010f4f9 Mon Sep 17 00:00:00 2001 From: Pieter Lexis Date: Mon, 23 Nov 2015 15:21:07 +0100 Subject: [PATCH] pipe: don't crash on exceptions This commit cleans up the PipeBackend code so it handles all exceptions itself. Be it initialization errors (i.e. "file not found", "command not executable"), or error during runtime ("data not returned in pipe format"). When these errors occur, we now recycle the coprocess so the rest of the nameserver keeps running. This behaviour is similar to e.g. an unavailable database server. Also, make the error-message for ArgMap::asNum display the value of the setting. Closes #2619 --- modules/pipebackend/pipebackend.cc | 289 ++++++++++++++++------------- modules/pipebackend/pipebackend.hh | 4 +- pdns/arguments.cc | 2 +- 3 files changed, 168 insertions(+), 127 deletions(-) diff --git a/modules/pipebackend/pipebackend.cc b/modules/pipebackend/pipebackend.cc index 0fc29ae4e..41e19df4c 100644 --- a/modules/pipebackend/pipebackend.cc +++ b/modules/pipebackend/pipebackend.cc @@ -51,13 +51,17 @@ void CoWrapper::launch() if(d_cp) return; + if(d_command.empty()) + throw ArgException("pipe-command is not specified"); + if(isUnixSocket(d_command)) d_cp = new UnixRemote(d_command, d_timeout); else - d_cp = new CoProcess(d_command, d_timeout); + d_cp = new CoProcess(d_command, d_timeout); + d_cp->send("HELO\t"+std::to_string(d_abiVersion)); string banner; - d_cp->receive(banner); + d_cp->receive(banner); L<(new CoWrapper(getArg("command"), getArgAsNum("timeout"), getArgAsNum("abi-version"))); - d_regex=getArg("regex").empty() ? 0 : new Regex(getArg("regex")); - d_regexstr=getArg("regex"); - d_abiVersion = getArgAsNum("abi-version"); + launch(); } catch(const ArgException &A) { - L< (new CoWrapper(getArg("command"), getArgAsNum("timeout"), getArgAsNum("abi-version"))); + } + + catch(const ArgException &A) { + cleanup(); + throw; + } +} + +/* + * Cleans up the co-process wrapper + */ +void PipeBackend::cleanup() +{ + d_coproc.reset(0); + delete d_regex; + d_regexstr = string(); + d_abiVersion = 0; +} + void PipeBackend::lookup(const QType& qtype,const DNSName& qname, DNSPacket *pkt_p, int zoneId) { - try { - d_disavow=false; - if(d_regex && !d_regex->match(qname.toStringNoDot())) { - if(::arg().mustDo("query-logging")) - L<getLocal(); - realRemote = pkt_p->getRealRemote(); - remoteIP = pkt_p->getRemote(); - } - // abi-version = 1 - // type qname qclass qtype id remote-ip-address - query<<"Q\t"<= 2) - query<<"\t"<= 3) - query <<"\t"<send(query.str()); + try { + launch(); + d_disavow=false; + if(d_regex && !d_regex->match(qname.toStringNoDot())) { + if(::arg().mustDo("query-logging")) + L<getLocal(); + realRemote = pkt_p->getRealRemote(); + remoteIP = pkt_p->getRemote(); } - } - catch(PDNSException &ae) { - L<= 2) + query<<"\t"<= 3) + query <<"\t"<send(query.str()); + } + } + catch(PDNSException &pe) { + L<= 4) - query<<"AXFR\t"<send(query.str()); - } - catch(PDNSException &ae) { - L<= 4) + query<<"AXFR\t"<send(query.str()); + } + catch(PDNSException &ae) { + L<send(oss.str()); - } - catch(PDNSException &ae) { - L<receive(line); - if (line == "END") break; - oss << line << std::endl; - }; - - return oss.str(); + launch(); + ostringstream oss; + oss<<"CMD\t"<send(oss.str()); + } + catch(PDNSException &ae) { + L<receive(line); + if (line == "END") break; + oss << line << std::endl; + }; + + return oss.str(); } //! For the dynamic loader @@ -214,47 +245,49 @@ DNSBackend *PipeBackend::maker() PipeBackend::~PipeBackend() { - delete d_regex; + cleanup(); } bool PipeBackend::get(DNSResourceRecord &r) { - if(d_disavow) // this query has been blocked - return false; - - string line; - - // The answer format: - // DATA qname qclass qtype ttl id content - unsigned int extraFields = 0; - if(d_abiVersion >= 3) - extraFields = 2; - - for(;;) { + if(d_disavow) // this query has been blocked + return false; + + string line; + + // The answer format: + // DATA qname qclass qtype ttl id content + unsigned int extraFields = 0; + if(d_abiVersion >= 3) + extraFields = 2; + + try{ + launch(); + for(;;) { d_coproc->receive(line); vectorparts; stringtok(parts,line,"\t"); if(parts.empty()) { - L<= 3) { r.scopeMask = std::stoi(parts[1]); r.auth = (parts[2] == "1"); @@ -266,29 +299,35 @@ bool PipeBackend::get(DNSResourceRecord &r) r.qtype=parts[3+extraFields]; r.ttl=pdns_stou(parts[4+extraFields]); r.domain_id=std::stoi(parts[5+extraFields]); - - if(r.qtype.getCode() != QType::MX && r.qtype.getCode() != QType::SRV) { - r.content.clear(); - for(unsigned int n= 6 + extraFields; n < parts.size(); ++n) { - if(n!=6+extraFields) - r.content.append(1,' '); - r.content.append(parts[n]); - } - } - else { - if(parts.size()< 8 + extraFields) { + + if(r.qtype.getCode() != QType::MX && r.qtype.getCode() != QType::SRV) { + r.content.clear(); + for(unsigned int n= 6 + extraFields; n < parts.size(); ++n) { + if(n!=6+extraFields) + r.content.append(1,' '); + r.content.append(parts[n]); + } + } + else { + if(parts.size()< 8 + extraFields) { L< d_coproc; + void launch(); + void cleanup(); + unique_ptr d_coproc; DNSName d_qname; QType d_qtype; Regex* d_regex; diff --git a/pdns/arguments.cc b/pdns/arguments.cc index 8e9294b4e..cba7796d4 100644 --- a/pdns/arguments.cc +++ b/pdns/arguments.cc @@ -255,7 +255,7 @@ int ArgvMap::asNum(const string &arg, int def) cptr_orig = params[arg].c_str(); retval = static_cast(strtol(cptr_orig, &cptr_ret, 0)); if (!retval && cptr_ret == cptr_orig) - throw ArgException("'"+arg+string("' is not valid number")); + throw ArgException("'"+arg+"' value '"+string(cptr_orig) + string( "' is not a valid number")); return retval; } -- 2.40.0