]>
git.ipfire.org Git - thirdparty/pdns.git/blob - modules/pipebackend/pipebackend.cc
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include "coprocess.hh"
32 #include "pdns/namespaces.hh"
34 #include "pdns/dns.hh"
35 #include "pdns/dnsbackend.hh"
36 #include "pdns/dnspacket.hh"
37 #include "pdns/pdnsexception.hh"
38 #include "pdns/logger.hh"
39 #include "pdns/arguments.hh"
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include "pipebackend.hh"
45 static const char *kBackendId
= "[PIPEBackend]";
47 CoWrapper::CoWrapper(const string
&command
, int timeout
, int abiVersion
)
52 d_abiVersion
= abiVersion
;
53 launch(); // let exceptions fall through - if initial launch fails, we want to die
57 CoWrapper::~CoWrapper()
64 void CoWrapper::launch()
70 throw ArgException("pipe-command is not specified");
72 if(isUnixSocket(d_command
))
73 d_cp
= new UnixRemote(d_command
, d_timeout
);
75 d_cp
= new CoProcess(d_command
, d_timeout
);
77 d_cp
->send("HELO\t"+std::to_string(d_abiVersion
));
79 d_cp
->receive(banner
);
80 g_log
<<Logger::Error
<<"Backend launched with banner: "<<banner
<<endl
;
83 void CoWrapper::send(const string
&line
)
90 catch(PDNSException
&ae
) {
96 void CoWrapper::receive(string
&line
)
103 catch(PDNSException
&ae
) {
104 g_log
<<Logger::Warning
<<kBackendId
<<" Unable to receive data from coprocess. "<<ae
.reason
<<endl
;
111 PipeBackend::PipeBackend(const string
&suffix
)
115 signal(SIGCHLD
, SIG_IGN
);
116 setArgPrefix("pipe"+suffix
);
120 catch(const ArgException
&A
) {
121 g_log
<<Logger::Error
<<kBackendId
<<" Unable to launch, fatal argument error: "<<A
.reason
<<endl
;
129 void PipeBackend::launch()
135 d_regex
=getArg("regex").empty() ? 0 : new Regex(getArg("regex"));
136 d_regexstr
=getArg("regex");
137 d_abiVersion
= getArgAsNum("abi-version");
138 d_coproc
=unique_ptr
<CoWrapper
> (new CoWrapper(getArg("command"), getArgAsNum("timeout"), getArgAsNum("abi-version")));
141 catch(const ArgException
&A
) {
148 * Cleans up the co-process wrapper
150 void PipeBackend::cleanup()
154 d_regexstr
= string();
158 void PipeBackend::lookup(const QType
& qtype
,const DNSName
& qname
, DNSPacket
*pkt_p
, int zoneId
)
163 if(d_regex
&& !d_regex
->match(qname
.toStringRootDot())) {
164 if(::arg().mustDo("query-logging"))
165 g_log
<<Logger::Error
<<"Query for '"<<qname
<<"' failed regex '"<<d_regexstr
<<"'"<<endl
;
166 d_disavow
=true; // don't pass to backend
169 string localIP
="0.0.0.0";
170 string remoteIP
="0.0.0.0";
171 Netmask
realRemote("0.0.0.0/0");
173 localIP
=pkt_p
->getLocal().toString();
174 realRemote
= pkt_p
->getRealRemote();
175 remoteIP
= pkt_p
->getRemote().toString();
178 // type qname qclass qtype id remote-ip-address
179 query
<<"Q\t"<<qname
.toStringRootDot()<<"\tIN\t"<<qtype
.getName()<<"\t"<<zoneId
<<"\t"<<remoteIP
;
181 // add the local-ip-address if abi-version is set to 2
182 if (d_abiVersion
>= 2)
183 query
<<"\t"<<localIP
;
184 if(d_abiVersion
>= 3)
185 query
<<"\t"<<realRemote
.toString();
187 if(::arg().mustDo("query-logging"))
188 g_log
<<Logger::Error
<<"Query: '"<<query
.str()<<"'"<<endl
;
189 d_coproc
->send(query
.str());
192 catch(PDNSException
&pe
) {
193 g_log
<<Logger::Error
<<kBackendId
<<" Error from coprocess: "<<pe
.reason
<<endl
;
200 bool PipeBackend::list(const DNSName
& target
, int inZoneId
, bool include_disabled
)
206 // The question format:
208 // type qname qclass qtype id ip-address
209 if (d_abiVersion
>= 4)
210 query
<<"AXFR\t"<<inZoneId
<<"\t"<<target
.toStringRootDot();
212 query
<<"AXFR\t"<<inZoneId
;
214 d_coproc
->send(query
.str());
216 catch(PDNSException
&ae
) {
217 g_log
<<Logger::Error
<<kBackendId
<<" Error from coprocess: "<<ae
.reason
<<endl
;
219 d_qname
=DNSName(itoa(inZoneId
)); // why do we store a number here??
223 string
PipeBackend::directBackendCmd(const string
&query
) {
224 if (d_abiVersion
< 5)
225 return "not supported on ABI version " + std::to_string(d_abiVersion
) + " (use ABI version 5 or later)\n";
231 d_coproc
->send(oss
.str());
233 catch(PDNSException
&ae
) {
234 g_log
<<Logger::Error
<<kBackendId
<<" Error from coprocess: "<<ae
.reason
<<endl
;
241 d_coproc
->receive(line
);
242 if (line
== "END") break;
243 oss
<< line
<< std::endl
;
249 //! For the dynamic loader
250 DNSBackend
*PipeBackend::maker()
253 return new PipeBackend();
256 g_log
<<Logger::Error
<<kBackendId
<<" Unable to instantiate a pipebackend!"<<endl
;
261 PipeBackend::~PipeBackend()
266 bool PipeBackend::get(DNSResourceRecord
&r
)
268 if(d_disavow
) // this query has been blocked
273 // The answer format:
274 // DATA qname qclass qtype ttl id content
275 unsigned int extraFields
= 0;
276 if(d_abiVersion
>= 3)
282 d_coproc
->receive(line
);
284 stringtok(parts
,line
,"\t");
286 g_log
<<Logger::Error
<<kBackendId
<<" Coprocess returned empty line in query for "<<d_qname
<<endl
;
287 throw PDNSException("Format error communicating with coprocess");
289 else if(parts
[0]=="FAIL") {
290 throw DBException("coprocess returned a FAIL");
292 else if(parts
[0]=="END") {
295 else if(parts
[0]=="LOG") {
296 g_log
<<Logger::Error
<<"Coprocess: "<<line
.substr(4)<<endl
;
299 else if(parts
[0]=="DATA") { // yay
300 if(parts
.size() < 7 + extraFields
) {
301 g_log
<<Logger::Error
<<kBackendId
<<" Coprocess returned incomplete or empty line in data section for query for "<<d_qname
<<endl
;
302 throw PDNSException("Format error communicating with coprocess in data section");
306 if(d_abiVersion
>= 3) {
307 r
.scopeMask
= std::stoi(parts
[1]);
308 r
.auth
= (parts
[2] == "1");
313 r
.qname
=DNSName(parts
[1+extraFields
]);
314 r
.qtype
=parts
[3+extraFields
];
315 r
.ttl
=pdns_stou(parts
[4+extraFields
]);
316 r
.domain_id
=std::stoi(parts
[5+extraFields
]);
318 if(r
.qtype
.getCode() != QType::MX
&& r
.qtype
.getCode() != QType::SRV
) {
320 for(unsigned int n
= 6 + extraFields
; n
< parts
.size(); ++n
) {
322 r
.content
.append(1,' ');
323 r
.content
.append(parts
[n
]);
327 if(parts
.size()< 8 + extraFields
) {
328 g_log
<<Logger::Error
<<kBackendId
<<" Coprocess returned incomplete MX/SRV line in data section for query for "<<d_qname
<<endl
;
329 throw PDNSException("Format error communicating with coprocess in data section of MX/SRV record");
332 r
.content
=parts
[6+extraFields
]+" "+parts
[7+extraFields
];
337 throw PDNSException("Coprocess backend sent incorrect response '"+line
+"'");
340 catch (DBException
&dbe
) {
341 g_log
<<Logger::Error
<<kBackendId
<<" "<<dbe
.reason
<<endl
;
344 catch (PDNSException
&pe
) {
345 g_log
<<Logger::Error
<<kBackendId
<<" "<<pe
.reason
<<endl
;
353 // Magic class that is activated when the dynamic library is loaded
356 class PipeFactory
: public BackendFactory
359 PipeFactory() : BackendFactory("pipe") {}
361 void declareArguments(const string
&suffix
="")
363 declare(suffix
,"command","Command to execute for piping questions to","");
364 declare(suffix
,"timeout","Number of milliseconds to wait for an answer","2000");
365 declare(suffix
,"regex","Regular expression of queries to pass to coprocess","");
366 declare(suffix
,"abi-version","Version of the pipe backend ABI","1");
369 DNSBackend
*make(const string
&suffix
="")
371 return new PipeBackend(suffix
);
380 BackendMakers().report(new PipeFactory
);
381 g_log
<< Logger::Info
<< kBackendId
<<" This is the pipe backend version " VERSION
383 << " (" __DATE__
" " __TIME__
")"
385 << " reporting" << endl
;
389 static PipeLoader pipeloader
;