From: Pieter Lexis Date: Mon, 23 Nov 2015 14:21:07 +0000 (+0100) Subject: pipe: don't crash on exceptions X-Git-Tag: auth-4.0.0-alpha1^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=fd378ba9299a056923efd7c80de6cd2f3010f4f9;p=pdns 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 --- 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; }