]>
Commit | Line | Data |
---|---|---|
8bd743a8 BH |
1 | // -*- sateh-c -*- |
2 | // File : pdnsbackend.cc | |
35933370 | 3 | // Version : $Id$ |
8bd743a8 BH |
4 | // |
5 | ||
870a0fe4 AT |
6 | #ifdef HAVE_CONFIG_H |
7 | #include "config.h" | |
8 | #endif | |
8bd743a8 BH |
9 | #include <string> |
10 | #include <map> | |
11 | #include <unistd.h> | |
12 | #include <stdlib.h> | |
13 | #include <sstream> | |
14 | #include "coprocess.hh" | |
15 | ||
10f4eea8 | 16 | #include "pdns/namespaces.hh" |
8bd743a8 | 17 | |
21e99384 RK |
18 | #include "pdns/dns.hh" |
19 | #include "pdns/dnsbackend.hh" | |
20 | #include "pdns/dnspacket.hh" | |
21 | #include "pdns/ueberbackend.hh" | |
22 | #include "pdns/pdnsexception.hh" | |
23 | #include "pdns/logger.hh" | |
24 | #include "pdns/arguments.hh" | |
8bd743a8 BH |
25 | #include <sys/socket.h> |
26 | #include <netinet/in.h> | |
27 | #include <arpa/inet.h> | |
8348e9fd | 28 | #include <boost/lexical_cast.hpp> |
8bd743a8 BH |
29 | #include "pipebackend.hh" |
30 | ||
31 | static const char *kBackendId = "[PIPEBackend]"; | |
32 | ||
33 | CoWrapper::CoWrapper(const string &command, int timeout) | |
34 | { | |
35 | d_cp=0; | |
36 | d_command=command; | |
37 | d_timeout=timeout; | |
4b16334f | 38 | d_abiVersion = ::arg().asNum("pipebackend-abi-version"); |
8bd743a8 BH |
39 | launch(); // let exceptions fall through - if initial launch fails, we want to die |
40 | // I think | |
41 | } | |
42 | ||
f7a23c1c BH |
43 | CoWrapper::~CoWrapper() |
44 | { | |
45 | if(d_cp) | |
46 | delete d_cp; | |
47 | } | |
48 | ||
49 | ||
8bd743a8 BH |
50 | void CoWrapper::launch() |
51 | { | |
52 | if(d_cp) | |
53 | return; | |
54 | ||
2f2b0145 BH |
55 | if(isUnixSocket(d_command)) |
56 | d_cp = new UnixRemote(d_command, d_timeout); | |
57 | else | |
58 | d_cp = new CoProcess(d_command, d_timeout); | |
4b16334f | 59 | d_cp->send("HELO\t"+lexical_cast<string>(d_abiVersion)); |
8bd743a8 BH |
60 | string banner; |
61 | d_cp->receive(banner); | |
62 | L<<Logger::Error<<"Backend launched with banner: "<<banner<<endl; | |
63 | } | |
64 | ||
65 | void CoWrapper::send(const string &line) | |
66 | { | |
67 | launch(); | |
68 | try { | |
69 | d_cp->send(line); | |
70 | return; | |
71 | } | |
3f81d239 | 72 | catch(PDNSException &ae) { |
8bd743a8 BH |
73 | delete d_cp; |
74 | d_cp=0; | |
75 | throw; | |
76 | } | |
77 | } | |
78 | void CoWrapper::receive(string &line) | |
79 | { | |
80 | launch(); | |
81 | try { | |
82 | d_cp->receive(line); | |
83 | return; | |
84 | } | |
3f81d239 | 85 | catch(PDNSException &ae) { |
c057bfaa | 86 | L<<Logger::Warning<<kBackendId<<" Unable to receive data from coprocess. "<<ae.reason<<endl; |
8bd743a8 BH |
87 | delete d_cp; |
88 | d_cp=0; | |
89 | throw; | |
90 | } | |
91 | } | |
92 | ||
93 | PipeBackend::PipeBackend(const string &suffix) | |
94 | { | |
87da3242 | 95 | signal(SIGCHLD, SIG_IGN); |
35933370 | 96 | setArgPrefix("pipe"+suffix); |
8bd743a8 | 97 | try { |
f7a23c1c BH |
98 | d_coproc=shared_ptr<CoWrapper>(new CoWrapper(getArg("command"), getArgAsNum("timeout"))); |
99 | d_regex=getArg("regex").empty() ? 0 : new Regex(getArg("regex")); | |
100 | d_regexstr=getArg("regex"); | |
4b16334f | 101 | d_abiVersion = ::arg().asNum("pipebackend-abi-version"); |
8bd743a8 BH |
102 | } |
103 | catch(const ArgException &A) { | |
104 | L<<Logger::Error<<kBackendId<<" Fatal argument error: "<<A.reason<<endl; | |
105 | throw; | |
106 | } | |
107 | catch(...) { | |
108 | throw; | |
109 | } | |
110 | } | |
111 | ||
112 | void PipeBackend::lookup(const QType &qtype,const string &qname, DNSPacket *pkt_p, int zoneId) | |
113 | { | |
114 | try { | |
115 | d_disavow=false; | |
116 | if(d_regex && !d_regex->match(qname+";"+qtype.getName())) { | |
0579f0db | 117 | if(::arg().mustDo("query-logging")) |
8bd743a8 BH |
118 | L<<Logger::Error<<"Query for '"<<qname<<"' type '"<<qtype.getName()<<"' failed regex '"<<d_regexstr<<"'"<<endl; |
119 | d_disavow=true; // don't pass to backend | |
120 | } else { | |
121 | ostringstream query; | |
35933370 BH |
122 | string localIP="0.0.0.0"; |
123 | string remoteIP="0.0.0.0"; | |
19a39147 | 124 | Netmask realRemote("0.0.0.0/0"); |
35933370 BH |
125 | if (pkt_p) { |
126 | localIP=pkt_p->getLocal(); | |
f674c49e BH |
127 | realRemote = pkt_p->getRealRemote(); |
128 | remoteIP = pkt_p->getRemote(); | |
35933370 | 129 | } |
35933370 BH |
130 | // pipebackend-abi-version = 1 |
131 | // type qname qclass qtype id remote-ip-address | |
132 | query<<"Q\t"<<qname<<"\tIN\t"<<qtype.getName()<<"\t"<<zoneId<<"\t"<<remoteIP; | |
133 | ||
134 | // add the local-ip-address if pipebackend-abi-version is set to 2 | |
4b16334f | 135 | if (d_abiVersion >= 2) |
35933370 | 136 | query<<"\t"<<localIP; |
4b16334f | 137 | if(d_abiVersion >= 3) |
f674c49e | 138 | query <<"\t"<<realRemote.toString(); |
35933370 | 139 | |
0579f0db | 140 | if(::arg().mustDo("query-logging")) |
8bd743a8 BH |
141 | L<<Logger::Error<<"Query: '"<<query.str()<<"'"<<endl; |
142 | d_coproc->send(query.str()); | |
143 | } | |
144 | } | |
3f81d239 | 145 | catch(PDNSException &ae) { |
8bd743a8 BH |
146 | L<<Logger::Error<<kBackendId<<" Error from coprocess: "<<ae.reason<<endl; |
147 | throw; // hop | |
148 | } | |
149 | d_qtype=qtype; | |
150 | d_qname=qname; | |
151 | } | |
152 | ||
cea26350 | 153 | bool PipeBackend::list(const string &target, int inZoneId, bool include_disabled) |
8bd743a8 BH |
154 | { |
155 | try { | |
156 | d_disavow=false; | |
157 | ostringstream query; | |
158 | // The question format: | |
159 | ||
160 | // type qname qclass qtype id ip-address | |
4b16334f | 161 | if (d_abiVersion >= 4) |
4b25ef35 AT |
162 | query<<"AXFR\t"<<inZoneId<<"\t"<<target; |
163 | else | |
4b16334f | 164 | query<<"AXFR\t"<<inZoneId; |
8bd743a8 BH |
165 | |
166 | d_coproc->send(query.str()); | |
167 | } | |
3f81d239 | 168 | catch(PDNSException &ae) { |
8bd743a8 BH |
169 | L<<Logger::Error<<kBackendId<<" Error from coprocess: "<<ae.reason<<endl; |
170 | throw; | |
171 | } | |
172 | d_qname=itoa(inZoneId); | |
173 | return true; | |
174 | } | |
175 | ||
176 | //! For the dynamic loader | |
177 | DNSBackend *PipeBackend::maker() | |
178 | { | |
179 | try { | |
180 | return new PipeBackend(); | |
181 | } | |
182 | catch(...) { | |
183 | L<<Logger::Error<<kBackendId<<" Unable to instantiate a pipebackend!"<<endl; | |
184 | return 0; | |
185 | } | |
186 | } | |
187 | ||
188 | PipeBackend::~PipeBackend() | |
189 | { | |
190 | delete d_regex; | |
191 | } | |
192 | ||
193 | bool PipeBackend::get(DNSResourceRecord &r) | |
194 | { | |
195 | if(d_disavow) // this query has been blocked | |
196 | return false; | |
197 | ||
198 | string line; | |
199 | ||
200 | // The answer format: | |
201 | // DATA qname qclass qtype ttl id content | |
42f1cf3e | 202 | unsigned int extraFields = 0; |
d29e264e | 203 | if(d_abiVersion >= 3) |
42f1cf3e | 204 | extraFields = 2; |
f674c49e | 205 | |
8bd743a8 BH |
206 | for(;;) { |
207 | d_coproc->receive(line); | |
208 | vector<string>parts; | |
209 | stringtok(parts,line,"\t"); | |
210 | if(parts.empty()) { | |
c057bfaa | 211 | L<<Logger::Error<<kBackendId<<" Coprocess returned empty line in query for "<<d_qname<<endl; |
3f81d239 | 212 | throw PDNSException("Format error communicating with coprocess"); |
8bd743a8 | 213 | } |
668056a5 BH |
214 | else if(parts[0]=="FAIL") { |
215 | throw DBException("coprocess returned a FAIL"); | |
216 | } | |
8bd743a8 BH |
217 | else if(parts[0]=="END") { |
218 | return false; | |
219 | } | |
220 | else if(parts[0]=="LOG") { | |
221 | L<<Logger::Error<<"Coprocess: "<<line.substr(4)<<endl; | |
222 | continue; | |
223 | } | |
224 | else if(parts[0]=="DATA") { // yay | |
42f1cf3e | 225 | if(parts.size() < 7 + extraFields) { |
c057bfaa | 226 | L<<Logger::Error<<kBackendId<<" Coprocess returned incomplete or empty line in data section for query for "<<d_qname<<endl; |
3f81d239 | 227 | throw PDNSException("Format error communicating with coprocess in data section"); |
8bd743a8 BH |
228 | // now what? |
229 | } | |
f674c49e | 230 | |
d29e264e | 231 | if(d_abiVersion >= 3) { |
f674c49e | 232 | r.scopeMask = atoi(parts[1].c_str()); |
42f1cf3e BH |
233 | r.auth = atoi(parts[2].c_str()); |
234 | } else { | |
235 | r.scopeMask = 0; | |
236 | r.auth = 1; | |
237 | } | |
238 | r.qname=parts[1+extraFields]; | |
239 | r.qtype=parts[3+extraFields]; | |
240 | r.ttl=atoi(parts[4+extraFields].c_str()); | |
241 | r.domain_id=atoi(parts[5+extraFields].c_str()); | |
242 | ||
f674c49e | 243 | if(r.qtype.getCode() != QType::MX && r.qtype.getCode() != QType::SRV) { |
e0ec6e79 | 244 | r.content.clear(); |
42f1cf3e | 245 | for(unsigned int n= 6 + extraFields; n < parts.size(); ++n) { |
64ff52f3 | 246 | if(n!=6+extraFields) |
e0ec6e79 BH |
247 | r.content.append(1,' '); |
248 | r.content.append(parts[n]); | |
249 | } | |
250 | } | |
251 | else { | |
42f1cf3e | 252 | if(parts.size()< 8 + extraFields) { |
c057bfaa | 253 | L<<Logger::Error<<kBackendId<<" Coprocess returned incomplete MX/SRV line in data section for query for "<<d_qname<<endl; |
3f81d239 | 254 | throw PDNSException("Format error communicating with coprocess in data section of MX/SRV record"); |
e0ec6e79 BH |
255 | } |
256 | ||
b9bafae0 | 257 | r.content=parts[6+extraFields]+" "+parts[7+extraFields]; |
e0ec6e79 | 258 | } |
8bd743a8 BH |
259 | break; |
260 | } | |
261 | else | |
3f81d239 | 262 | throw PDNSException("Coprocess backend sent incorrect response '"+line+"'"); |
8bd743a8 BH |
263 | } |
264 | return true; | |
265 | } | |
266 | ||
267 | // | |
268 | // Magic class that is activated when the dynamic library is loaded | |
269 | // | |
270 | ||
271 | class PipeFactory : public BackendFactory | |
272 | { | |
273 | public: | |
274 | PipeFactory() : BackendFactory("pipe") {} | |
275 | ||
276 | void declareArguments(const string &suffix="") | |
277 | { | |
278 | declare(suffix,"command","Command to execute for piping questions to",""); | |
ecc96bb7 | 279 | declare(suffix,"timeout","Number of milliseconds to wait for an answer","2000"); |
8bd743a8 BH |
280 | declare(suffix,"regex","Regular exception of queries to pass to coprocess",""); |
281 | } | |
282 | ||
283 | DNSBackend *make(const string &suffix="") | |
284 | { | |
285 | return new PipeBackend(suffix); | |
286 | } | |
287 | }; | |
288 | ||
1258abe0 | 289 | class PipeLoader |
8bd743a8 BH |
290 | { |
291 | public: | |
1258abe0 | 292 | PipeLoader() |
8bd743a8 BH |
293 | { |
294 | BackendMakers().report(new PipeFactory); | |
40428724 | 295 | L << Logger::Info << kBackendId <<" This is the pipe backend version " VERSION " (" __DATE__ ", " __TIME__ ") reporting" << endl; |
8bd743a8 BH |
296 | } |
297 | }; | |
298 | ||
1258abe0 | 299 | static PipeLoader pipeloader; |
8bd743a8 | 300 |