]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsbackend.cc
Merge pull request #8223 from PowerDNS/omoerbeek-patch-1
[thirdparty/pdns.git] / pdns / dnsbackend.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
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.
8 *
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.
12 *
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.
17 *
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.
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "utility.hh"
26 #include "dnsbackend.hh"
27 #include "arguments.hh"
28 #include "ueberbackend.hh"
29 #include "logger.hh"
30
31 #include <sys/types.h>
32 #include "pdns/packetcache.hh"
33 #include "dnspacket.hh"
34 #include "dns.hh"
35
36 // this has to be somewhere central, and not in a file that requires Lua
37 // this is so the geoipbackend can set this pointer if loaded for lua-record.cc
38 std::function<std::string(const std::string&, int)> g_getGeo;
39
40 bool DNSBackend::getAuth(const DNSName &target, SOAData *sd)
41 {
42 return this->getSOA(target, *sd);
43 }
44
45 void DNSBackend::setArgPrefix(const string &prefix)
46 {
47 d_prefix=prefix;
48 }
49
50 bool DNSBackend::mustDo(const string &key)
51 {
52 return arg().mustDo(d_prefix+"-"+key);
53 }
54
55 const string &DNSBackend::getArg(const string &key)
56 {
57 return arg()[d_prefix+"-"+key];
58 }
59
60 int DNSBackend::getArgAsNum(const string &key)
61 {
62 return arg().asNum(d_prefix+"-"+key);
63 }
64
65 void BackendFactory::declare(const string &suffix, const string &param, const string &help, const string &value)
66 {
67 string fullname=d_name+suffix+"-"+param;
68 arg().set(fullname,help)=value;
69 }
70
71 const string &BackendFactory::getName() const
72 {
73 return d_name;
74 }
75
76 BackendMakerClass &BackendMakers()
77 {
78 static BackendMakerClass bmc;
79 return bmc;
80 }
81
82 void BackendMakerClass::report(BackendFactory *bf)
83 {
84 d_repository[bf->getName()]=bf;
85 }
86
87
88 vector<string> BackendMakerClass::getModules()
89 {
90 load_all();
91 vector<string> ret;
92 // copy(d_repository.begin(), d_repository.end(),back_inserter(ret));
93 for(d_repository_t::const_iterator i=d_repository.begin();i!=d_repository.end();++i)
94 ret.push_back(i->first);
95 return ret;
96 }
97
98 void BackendMakerClass::load_all()
99 {
100 // TODO: Implement this?
101 DIR *dir=opendir(arg()["module-dir"].c_str());
102 if(!dir) {
103 g_log<<Logger::Error<<"Unable to open module directory '"<<arg()["module-dir"]<<"'"<<endl;
104 return;
105 }
106 struct dirent *entry;
107 while((entry=readdir(dir))) {
108 if(!strncmp(entry->d_name,"lib",3) &&
109 strlen(entry->d_name)>13 &&
110 !strcmp(entry->d_name+strlen(entry->d_name)-10,"backend.so"))
111 load(entry->d_name);
112 }
113 closedir(dir);
114 }
115
116 void BackendMakerClass::load(const string &module)
117 {
118 bool res;
119
120 if(module.find(".")==string::npos)
121 res=UeberBackend::loadmodule(arg()["module-dir"]+"/lib"+module+"backend.so");
122 else if(module[0]=='/' || (module[0]=='.' && module[1]=='/') || (module[0]=='.' && module[1]=='.')) // absolute or current path
123 res=UeberBackend::loadmodule(module);
124 else
125 res=UeberBackend::loadmodule(arg()["module-dir"]+"/"+module);
126
127 if(res==false) {
128 g_log<<Logger::Error<<"DNSBackend unable to load module in "<<module<<endl;
129 exit(1);
130 }
131 }
132
133 void BackendMakerClass::launch(const string &instr)
134 {
135 // if(instr.empty())
136 // throw ArgException("Not launching any backends - nameserver won't function");
137
138 vector<string> parts;
139 stringtok(parts,instr,", ");
140
141 for (const auto part : parts)
142 if (count(parts.begin(), parts.end(), part) > 1)
143 throw ArgException("Refusing to launch multiple backends with the same name '" + part + "', verify all 'launch' statements in your configuration");
144
145 for(vector<string>::const_iterator i=parts.begin();i!=parts.end();++i) {
146 const string &part=*i;
147
148 string module, name;
149 vector<string>pparts;
150 stringtok(pparts,part,": ");
151 module=pparts[0];
152 if(pparts.size()>1)
153 name="-"+pparts[1];
154
155 if(d_repository.find(module)==d_repository.end()) {
156 // this is *so* userfriendly
157 load(module);
158 if(d_repository.find(module)==d_repository.end())
159 throw ArgException("Trying to launch unknown backend '"+module+"'");
160 }
161 d_repository[module]->declareArguments(name);
162 d_instances.push_back(make_pair(module,name));
163 }
164 }
165
166 int BackendMakerClass::numLauncheable()
167 {
168 return d_instances.size();
169 }
170
171 vector<DNSBackend *>BackendMakerClass::all(bool metadataOnly)
172 {
173 vector<DNSBackend *>ret;
174 if(d_instances.empty())
175 throw PDNSException("No database backends configured for launch, unable to function");
176
177 try {
178 for(vector<pair<string,string> >::const_iterator i=d_instances.begin();i!=d_instances.end();++i) {
179 DNSBackend *made;
180 if(metadataOnly)
181 made = d_repository[i->first]->makeMetadataOnly(i->second);
182 else
183 made = d_repository[i->first]->make(i->second);
184 if(!made)
185 throw PDNSException("Unable to launch backend '"+i->first+"'");
186
187 ret.push_back(made);
188 }
189 }
190 catch(PDNSException &ae) {
191 g_log<<Logger::Error<<"Caught an exception instantiating a backend: "<<ae.reason<<endl;
192 g_log<<Logger::Error<<"Cleaning up"<<endl;
193 for(vector<DNSBackend *>::const_iterator i=ret.begin();i!=ret.end();++i)
194 delete *i;
195 throw;
196 } catch(...) {
197 // and cleanup
198 g_log<<Logger::Error<<"Caught an exception instantiating a backend, cleaning up"<<endl;
199 for(vector<DNSBackend *>::const_iterator i=ret.begin();i!=ret.end();++i)
200 delete *i;
201 throw;
202 }
203
204 return ret;
205 }
206
207 /** getSOA() is a function that is called to get the SOA of a domain. Callers should ONLY
208 use getSOA() and not perform a lookup() themselves as backends may decide to special case
209 the SOA record.
210
211 Returns false if there is definitely no SOA for the domain. May throw a DBException
212 to indicate that the backend is currently unable to supply an answer.
213
214 WARNING: This function *may* fill out the db attribute of the SOAData, but then again,
215 it may not! If you find a zero in there, you may have been handed a non-live and cached
216 answer, in which case you need to perform a getDomainInfo call!
217
218 \param domain Domain we want to get the SOA details of
219 \param sd SOAData which is filled with the SOA details
220 \param unmodifiedSerial bool if set, serial will be returned as stored in the backend (maybe 0)
221 */
222 bool DNSBackend::getSOA(const DNSName &domain, SOAData &sd)
223 {
224 this->lookup(QType(QType::SOA),domain);
225
226 DNSResourceRecord rr;
227 rr.auth = true;
228
229 int hits=0;
230
231 while(this->get(rr)) {
232 if (rr.qtype != QType::SOA) throw PDNSException("Got non-SOA record when asking for SOA");
233 hits++;
234 fillSOAData(rr.content, sd);
235 sd.domain_id=rr.domain_id;
236 sd.ttl=rr.ttl;
237 }
238
239 if(!hits)
240 return false;
241 sd.qname = domain;
242 if(!sd.nameserver.countLabels())
243 sd.nameserver= DNSName(arg()["default-soa-name"]);
244
245 if(!sd.hostmaster.countLabels()) {
246 if (!arg().isEmpty("default-soa-mail")) {
247 sd.hostmaster= DNSName(arg()["default-soa-mail"]);
248 // attodot(sd.hostmaster); FIXME400
249 }
250 else
251 sd.hostmaster=DNSName("hostmaster")+domain;
252 }
253
254 sd.db=this;
255 return true;
256 }
257
258 bool DNSBackend::get(DNSZoneRecord& dzr)
259 {
260 // cout<<"DNSBackend::get(DNSZoneRecord&) called - translating into DNSResourceRecord query"<<endl;
261 DNSResourceRecord rr;
262 if(!this->get(rr))
263 return false;
264 dzr.auth = rr.auth;
265 dzr.domain_id = rr.domain_id;
266 dzr.scopeMask = rr.scopeMask;
267 if(rr.qtype.getCode() == QType::TXT && !rr.content.empty() && rr.content[0]!='"')
268 rr.content = "\""+ rr.content + "\"";
269 if(rr.qtype.getCode() == QType::SOA) {
270 try {
271 dzr.dr = DNSRecord(rr);
272 } catch(...) {
273 vector<string> parts;
274 stringtok(parts, rr.content, " \t");
275 if(parts.size() < 1)
276 rr.content = arg()["default-soa-name"];
277 if(parts.size() < 2)
278 rr.content += " " +arg()["default-soa-mail"];
279 if(parts.size() < 3)
280 rr.content += " 0";
281 if(parts.size() < 4)
282 rr.content += " " + ::arg()["soa-refresh-default"];
283 if(parts.size() < 5)
284 rr.content += " " + ::arg()["soa-retry-default"];
285 if(parts.size() < 6)
286 rr.content += " " + ::arg()["soa-expire-default"];
287 if(parts.size() < 7)
288 rr.content += " " + ::arg()["soa-minimum-ttl"];
289 dzr.dr = DNSRecord(rr);
290 }
291 }
292 else {
293 try {
294 dzr.dr = DNSRecord(rr);
295 }
296 catch(...) {
297 while(this->get(rr));
298 throw;
299 }
300 }
301 return true;
302 }
303
304 bool DNSBackend::getBeforeAndAfterNames(uint32_t id, const DNSName& zonename, const DNSName& qname, DNSName& before, DNSName& after)
305 {
306 DNSName unhashed;
307 bool ret = this->getBeforeAndAfterNamesAbsolute(id, qname.makeRelative(zonename).makeLowerCase(), unhashed, before, after);
308 DNSName lczonename = zonename.makeLowerCase();
309 before += lczonename;
310 after += lczonename;
311 return ret;
312 }
313
314 void fillSOAData(const DNSZoneRecord& in, SOAData& sd)
315 {
316 sd.domain_id = in.domain_id;
317 sd.ttl = in.dr.d_ttl;
318
319 auto src=getRR<SOARecordContent>(in.dr);
320 sd.nameserver = src->d_mname;
321 sd.hostmaster = src->d_rname;
322 sd.serial = src->d_st.serial;
323 sd.refresh = src->d_st.refresh;
324 sd.retry = src->d_st.retry;
325 sd.expire = src->d_st.expire;
326 sd.default_ttl = src->d_st.minimum;
327 }
328
329 std::shared_ptr<DNSRecordContent> makeSOAContent(const SOAData& sd)
330 {
331 struct soatimes st;
332 st.serial = sd.serial;
333 st.refresh = sd.refresh;
334 st.retry = sd.retry;
335 st.expire = sd.expire;
336 st.minimum = sd.default_ttl;
337 return std::make_shared<SOARecordContent>(sd.nameserver, sd.hostmaster, st);
338 }
339
340
341 void fillSOAData(const string &content, SOAData &data)
342 {
343 // content consists of fields separated by spaces:
344 // nameservername hostmaster serial-number [refresh [retry [expire [ minimum] ] ] ]
345
346 // fill out data with some plausible defaults:
347 // 10800 3600 604800 3600
348 vector<string>parts;
349 stringtok(parts,content);
350 int pleft=parts.size();
351
352 // cout<<"'"<<content<<"'"<<endl;
353
354 if(pleft)
355 data.nameserver=DNSName(parts[0]);
356
357 if(pleft>1)
358 data.hostmaster=DNSName(attodot(parts[1])); // ahu@ds9a.nl -> ahu.ds9a.nl, piet.puk@ds9a.nl -> piet\.puk.ds9a.nl
359
360 try {
361 data.serial = pleft > 2 ? pdns_stou(parts[2]) : 0;
362
363 data.refresh = pleft > 3 ? pdns_stou(parts[3])
364 : ::arg().asNum("soa-refresh-default");
365
366 data.retry = pleft > 4 ? pdns_stou(parts[4].c_str())
367 : ::arg().asNum("soa-retry-default");
368
369 data.expire = pleft > 5 ? pdns_stou(parts[5].c_str())
370 : ::arg().asNum("soa-expire-default");
371
372 data.default_ttl = pleft > 6 ? pdns_stou(parts[6].c_str())
373 : ::arg().asNum("soa-minimum-ttl");
374 }
375 catch(const std::out_of_range& oor) {
376 throw PDNSException("Out of range exception parsing "+content);
377 }
378 }