]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsbackend.cc
Merge pull request #7024 from cmouse/geoip-config
[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, bool unmodifiedSerial)
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 if(!unmodifiedSerial && !sd.serial) { // magic time!
255 DLOG(g_log<<Logger::Warning<<"Doing SOA serial number autocalculation for "<<rr.qname<<endl);
256
257 uint32_t serial = 0;
258 if (calculateSOASerial(domain, sd, serial)) {
259 sd.serial = serial;
260 //DLOG(g_log<<"autocalculated soa serialnumber for "<<rr.qname<<" is "<<newest<<endl);
261 } else {
262 DLOG(g_log<<"soa serialnumber calculation failed for "<<rr.qname<<endl);
263 }
264
265 }
266 sd.db=this;
267 return true;
268 }
269
270 bool DNSBackend::get(DNSZoneRecord& dzr)
271 {
272 // cout<<"DNSBackend::get(DNSZoneRecord&) called - translating into DNSResourceRecord query"<<endl;
273 DNSResourceRecord rr;
274 if(!this->get(rr))
275 return false;
276 dzr.auth = rr.auth;
277 dzr.domain_id = rr.domain_id;
278 dzr.scopeMask = rr.scopeMask;
279 if(rr.qtype.getCode() == QType::TXT && !rr.content.empty() && rr.content[0]!='"')
280 rr.content = "\""+ rr.content + "\"";
281 if(rr.qtype.getCode() == QType::SOA) {
282 try {
283 dzr.dr = DNSRecord(rr);
284 } catch(...) {
285 vector<string> parts;
286 stringtok(parts, rr.content, " \t");
287 if(parts.size() < 1)
288 rr.content = arg()["default-soa-name"];
289 if(parts.size() < 2)
290 rr.content += " " +arg()["default-soa-mail"];
291 if(parts.size() < 3)
292 rr.content += " 0";
293 if(parts.size() < 4)
294 rr.content += " " + ::arg()["soa-refresh-default"];
295 if(parts.size() < 5)
296 rr.content += " " + ::arg()["soa-retry-default"];
297 if(parts.size() < 6)
298 rr.content += " " + ::arg()["soa-expire-default"];
299 if(parts.size() < 7)
300 rr.content += " " + ::arg()["soa-minimum-ttl"];
301 dzr.dr = DNSRecord(rr);
302 }
303 }
304 else {
305 try {
306 dzr.dr = DNSRecord(rr);
307 }
308 catch(...) {
309 while(this->get(rr));
310 throw;
311 }
312 }
313 return true;
314 }
315
316 bool DNSBackend::getBeforeAndAfterNames(uint32_t id, const DNSName& zonename, const DNSName& qname, DNSName& before, DNSName& after)
317 {
318 DNSName unhashed;
319 bool ret = this->getBeforeAndAfterNamesAbsolute(id, qname.makeRelative(zonename).makeLowerCase(), unhashed, before, after);
320 DNSName lczonename = zonename.makeLowerCase();
321 before += lczonename;
322 after += lczonename;
323 return ret;
324 }
325
326 /**
327 * Calculates a SOA serial for the zone and stores it in the third
328 * argument. Returns false if calculation is not possible for some
329 * reason (in this case, the third argument is not inspected). If it
330 * returns true, the value returned in the third argument will be set
331 * as the SOA serial.
332 *
333 * \param domain The name of the domain
334 * \param sd Information about the SOA record already available
335 * \param serial Output parameter. Only inspected when we return true
336 */
337 bool DNSBackend::calculateSOASerial(const DNSName& domain, const SOAData& sd, uint32_t& serial)
338 {
339 // we do this by listing the domain and taking the maximum last modified timestamp
340
341 DNSResourceRecord i;
342 uint32_t newest=0;
343
344 if(!(this->list(domain, sd.domain_id))) {
345 DLOG(g_log<<Logger::Warning<<"Backend error trying to determine magic serial number of zone '"<<domain<<"'"<<endl);
346 return false;
347 }
348
349 while(this->get(i)) {
350 if(i.last_modified>newest)
351 newest=i.last_modified;
352 }
353
354 serial=newest;
355
356 return true;
357 }
358 void fillSOAData(const DNSZoneRecord& in, SOAData& sd)
359 {
360 sd.domain_id = in.domain_id;
361 sd.ttl = in.dr.d_ttl;
362
363 auto src=getRR<SOARecordContent>(in.dr);
364 sd.nameserver = src->d_mname;
365 sd.hostmaster = src->d_rname;
366 sd.serial = src->d_st.serial;
367 sd.refresh = src->d_st.refresh;
368 sd.retry = src->d_st.retry;
369 sd.expire = src->d_st.expire;
370 sd.default_ttl = src->d_st.minimum;
371 }
372
373 std::shared_ptr<DNSRecordContent> makeSOAContent(const SOAData& sd)
374 {
375 struct soatimes st;
376 st.serial = sd.serial;
377 st.refresh = sd.refresh;
378 st.retry = sd.retry;
379 st.expire = sd.expire;
380 st.minimum = sd.default_ttl;
381 return std::make_shared<SOARecordContent>(sd.nameserver, sd.hostmaster, st);
382 }
383
384
385 void fillSOAData(const string &content, SOAData &data)
386 {
387 // content consists of fields separated by spaces:
388 // nameservername hostmaster serial-number [refresh [retry [expire [ minimum] ] ] ]
389
390 // fill out data with some plausible defaults:
391 // 10800 3600 604800 3600
392 vector<string>parts;
393 stringtok(parts,content);
394 int pleft=parts.size();
395
396 // cout<<"'"<<content<<"'"<<endl;
397
398 if(pleft)
399 data.nameserver=DNSName(parts[0]);
400
401 if(pleft>1)
402 data.hostmaster=DNSName(attodot(parts[1])); // ahu@ds9a.nl -> ahu.ds9a.nl, piet.puk@ds9a.nl -> piet\.puk.ds9a.nl
403
404 try {
405 data.serial = pleft > 2 ? pdns_stou(parts[2]) : 0;
406
407 data.refresh = pleft > 3 ? pdns_stou(parts[3])
408 : ::arg().asNum("soa-refresh-default");
409
410 data.retry = pleft > 4 ? pdns_stou(parts[4].c_str())
411 : ::arg().asNum("soa-retry-default");
412
413 data.expire = pleft > 5 ? pdns_stou(parts[5].c_str())
414 : ::arg().asNum("soa-expire-default");
415
416 data.default_ttl = pleft > 6 ? pdns_stou(parts[6].c_str())
417 : ::arg().asNum("soa-minimum-ttl");
418 }
419 catch(const std::out_of_range& oor) {
420 throw PDNSException("Out of range exception parsing "+content);
421 }
422 }