]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsbackend.cc
Merge pull request #6063 from cyclops1982/3760
[thirdparty/pdns.git] / pdns / dnsbackend.cc
CommitLineData
12c86877 1/*
12471842
PL
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 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
731f58b8 25#include "utility.hh"
12c86877
BH
26#include "dnsbackend.hh"
27#include "arguments.hh"
28#include "ueberbackend.hh"
29#include "logger.hh"
30
31#include <sys/types.h>
21e99384 32#include "pdns/packetcache.hh"
12c86877 33#include "dnspacket.hh"
c87f987e 34#include "dns.hh"
12c86877 35
cec52de6 36bool DNSBackend::getAuth(const DNSName &target, SOAData *sd)
c14bc34a 37{
94bfa5b6 38 return this->getSOA(target, *sd);
c14bc34a
MZ
39}
40
12c86877
BH
41void DNSBackend::setArgPrefix(const string &prefix)
42{
43 d_prefix=prefix;
44}
45
46bool DNSBackend::mustDo(const string &key)
47{
48 return arg().mustDo(d_prefix+"-"+key);
49}
50
51const string &DNSBackend::getArg(const string &key)
52{
53 return arg()[d_prefix+"-"+key];
54}
55
56int DNSBackend::getArgAsNum(const string &key)
57{
58 return arg().asNum(d_prefix+"-"+key);
59}
60
61void BackendFactory::declare(const string &suffix, const string &param, const string &help, const string &value)
62{
98a8b02e 63 string fullname=d_name+suffix+"-"+param;
12c86877
BH
64 arg().set(fullname,help)=value;
65}
66
67const string &BackendFactory::getName() const
68{
69 return d_name;
70}
71
72BackendMakerClass &BackendMakers()
73{
74 static BackendMakerClass bmc;
75 return bmc;
76}
77
78void BackendMakerClass::report(BackendFactory *bf)
79{
80 d_repository[bf->getName()]=bf;
81}
82
83
5f3eca86 84vector<string> BackendMakerClass::getModules()
12c86877
BH
85{
86 load_all();
87 vector<string> ret;
88 // copy(d_repository.begin(), d_repository.end(),back_inserter(ret));
89 for(d_repository_t::const_iterator i=d_repository.begin();i!=d_repository.end();++i)
90 ret.push_back(i->first);
91 return ret;
92}
93
94void BackendMakerClass::load_all()
731f58b8
BH
95{
96 // TODO: Implement this?
12c86877
BH
97 DIR *dir=opendir(arg()["module-dir"].c_str());
98 if(!dir) {
99 L<<Logger::Error<<"Unable to open module directory '"<<arg()["module-dir"]<<"'"<<endl;
100 return;
101 }
102 struct dirent *entry;
103 while((entry=readdir(dir))) {
5f3eca86 104 if(!strncmp(entry->d_name,"lib",3) &&
731f58b8
BH
105 strlen(entry->d_name)>13 &&
106 !strcmp(entry->d_name+strlen(entry->d_name)-10,"backend.so"))
12c86877
BH
107 load(entry->d_name);
108 }
731f58b8 109 closedir(dir);
12c86877
BH
110}
111
112void BackendMakerClass::load(const string &module)
113{
34c513f9 114 bool res;
12c86877
BH
115
116 if(module.find(".")==string::npos)
117 res=UeberBackend::loadmodule(arg()["module-dir"]+"/lib"+module+"backend.so");
118 else if(module[0]=='/' || (module[0]=='.' && module[1]=='/') || (module[0]=='.' && module[1]=='.')) // absolute or current path
119 res=UeberBackend::loadmodule(module);
120 else
121 res=UeberBackend::loadmodule(arg()["module-dir"]+"/"+module);
5f3eca86 122
12c86877 123 if(res==false) {
c057bfaa 124 L<<Logger::Error<<"DNSBackend unable to load module in "<<module<<endl;
12c86877
BH
125 exit(1);
126 }
127}
128
129void BackendMakerClass::launch(const string &instr)
130{
131 // if(instr.empty())
132 // throw ArgException("Not launching any backends - nameserver won't function");
5f3eca86 133
12c86877
BH
134 vector<string> parts;
135 stringtok(parts,instr,", ");
5f3eca86 136
c52dd6af
PL
137 for (const auto part : parts)
138 if (count(parts.begin(), parts.end(), part) > 1)
139 throw ArgException("Refusing to launch multiple backends with the same name '" + part + "', verify all 'launch' statements in your configuration");
140
12c86877
BH
141 for(vector<string>::const_iterator i=parts.begin();i!=parts.end();++i) {
142 const string &part=*i;
5f3eca86 143
12c86877
BH
144 string module, name;
145 vector<string>pparts;
146 stringtok(pparts,part,": ");
147 module=pparts[0];
148 if(pparts.size()>1)
98a8b02e 149 name="-"+pparts[1];
5f3eca86 150
12c86877
BH
151 if(d_repository.find(module)==d_repository.end()) {
152 // this is *so* userfriendly
153 load(module);
154 if(d_repository.find(module)==d_repository.end())
4957a608 155 throw ArgException("Trying to launch unknown backend '"+module+"'");
12c86877 156 }
12c86877
BH
157 d_repository[module]->declareArguments(name);
158 d_instances.push_back(make_pair(module,name));
159 }
160}
161
162int BackendMakerClass::numLauncheable()
163{
164 return d_instances.size();
165}
166
2717b8b3 167vector<DNSBackend *>BackendMakerClass::all(bool metadataOnly)
12c86877
BH
168{
169 vector<DNSBackend *>ret;
170 if(d_instances.empty())
3f81d239 171 throw PDNSException("No database backends configured for launch, unable to function");
12c86877
BH
172
173 try {
174 for(vector<pair<string,string> >::const_iterator i=d_instances.begin();i!=d_instances.end();++i) {
2717b8b3
BH
175 DNSBackend *made;
176 if(metadataOnly)
177 made = d_repository[i->first]->makeMetadataOnly(i->second);
5f3eca86 178 else
2717b8b3 179 made = d_repository[i->first]->make(i->second);
12c86877 180 if(!made)
3f81d239 181 throw PDNSException("Unable to launch backend '"+i->first+"'");
12c86877
BH
182
183 ret.push_back(made);
184 }
185 }
3f81d239 186 catch(PDNSException &ae) {
06ad4526
PD
187 L<<Logger::Error<<"Caught an exception instantiating a backend: "<<ae.reason<<endl;
188 L<<Logger::Error<<"Cleaning up"<<endl;
189 for(vector<DNSBackend *>::const_iterator i=ret.begin();i!=ret.end();++i)
190 delete *i;
07dbfb4f 191 throw;
06ad4526 192 } catch(...) {
12c86877 193 // and cleanup
bdc9f8d2 194 L<<Logger::Error<<"Caught an exception instantiating a backend, cleaning up"<<endl;
12c86877
BH
195 for(vector<DNSBackend *>::const_iterator i=ret.begin();i!=ret.end();++i)
196 delete *i;
07dbfb4f 197 throw;
12c86877 198 }
5f3eca86 199
12c86877
BH
200 return ret;
201}
202
bdc9f8d2 203/** getSOA() is a function that is called to get the SOA of a domain. Callers should ONLY
4dfd8645 204 use getSOA() and not perform a lookup() themselves as backends may decide to special case
bdc9f8d2 205 the SOA record.
5f3eca86 206
bdc9f8d2
BH
207 Returns false if there is definitely no SOA for the domain. May throw a DBException
208 to indicate that the backend is currently unable to supply an answer.
209
25b1d5d7
BH
210 WARNING: This function *may* fill out the db attribute of the SOAData, but then again,
211 it may not! If you find a zero in there, you may have been handed a non-live and cached
212 answer, in which case you need to perform a getDomainInfo call!
213
bdc9f8d2
BH
214 \param domain Domain we want to get the SOA details of
215 \param sd SOAData which is filled with the SOA details
216*/
94bfa5b6 217bool DNSBackend::getSOA(const DNSName &domain, SOAData &sd)
12c86877 218{
94bfa5b6 219 this->lookup(QType(QType::SOA),domain);
5f3eca86 220
12c86877 221 DNSResourceRecord rr;
5f3eca86 222 rr.auth = true;
12c86877
BH
223
224 int hits=0;
225
226 while(this->get(rr)) {
5f3eca86 227 if (rr.qtype != QType::SOA) throw PDNSException("Got non-SOA record when asking for SOA");
12c86877 228 hits++;
34b37bbb 229 fillSOAData(rr.content, sd);
12c86877
BH
230 sd.domain_id=rr.domain_id;
231 sd.ttl=rr.ttl;
af7d3ea6 232 sd.scopeMask = rr.scopeMask;
12c86877 233 }
7b308b7e 234
12c86877
BH
235 if(!hits)
236 return false;
5f5221b4 237 sd.qname = domain;
675fa24c 238 if(!sd.nameserver.countLabels())
290a083d 239 sd.nameserver= DNSName(arg()["default-soa-name"]);
5f3eca86 240
675fa24c 241 if(!sd.hostmaster.countLabels()) {
c87f987e 242 if (!arg().isEmpty("default-soa-mail")) {
290a083d 243 sd.hostmaster= DNSName(arg()["default-soa-mail"]);
3343ad1f 244 // attodot(sd.hostmaster); FIXME400
c87f987e
KM
245 }
246 else
7abbc40f 247 sd.hostmaster=DNSName("hostmaster")+domain;
c87f987e 248 }
12c86877
BH
249
250 if(!sd.serial) { // magic time!
f43c4448 251 DLOG(L<<Logger::Warning<<"Doing SOA serial number autocalculation for "<<rr.qname<<endl);
12c86877 252
d07fc616
PD
253 time_t serial;
254 if (calculateSOASerial(domain, sd, serial)) {
255 sd.serial = serial;
df8d5183 256 //DLOG(L<<"autocalculated soa serialnumber for "<<rr.qname<<" is "<<newest<<endl);
d07fc616 257 } else {
f43c4448 258 DLOG(L<<"soa serialnumber calculation failed for "<<rr.qname<<endl);
12c86877
BH
259 }
260
12c86877
BH
261 }
262 sd.db=this;
263 return true;
264}
265
90ba52e0 266bool DNSBackend::get(DNSZoneRecord& dzr)
267{
aa7b2405 268 // cout<<"DNSBackend::get(DNSZoneRecord&) called - translating into DNSResourceRecord query"<<endl;
90ba52e0 269 DNSResourceRecord rr;
270 if(!this->get(rr))
271 return false;
272 dzr.auth = rr.auth;
273 dzr.domain_id = rr.domain_id;
158bc1d1 274 dzr.scopeMask = rr.scopeMask;
c7d5b054 275 if(rr.qtype.getCode() == QType::TXT && !rr.content.empty() && rr.content[0]!='"')
276 rr.content = "\""+ rr.content + "\"";
9262e6e1 277 if(rr.qtype.getCode() == QType::SOA) {
278 try {
279 dzr.dr = DNSRecord(rr);
280 } catch(...) {
281 vector<string> parts;
282 stringtok(parts, rr.content, " \t");
283 if(parts.size() < 1)
484efba7 284 rr.content = arg()["default-soa-name"];
9262e6e1 285 if(parts.size() < 2)
484efba7 286 rr.content += " " +arg()["default-soa-mail"];
9262e6e1 287 if(parts.size() < 3)
288 rr.content += " 0";
289 if(parts.size() < 4)
290 rr.content += " " + ::arg()["soa-refresh-default"];
291 if(parts.size() < 5)
484efba7 292 rr.content += " " + ::arg()["soa-retry-default"];
9262e6e1 293 if(parts.size() < 6)
484efba7
KM
294 rr.content += " " + ::arg()["soa-expire-default"];
295 if(parts.size() < 7)
296 rr.content += " " + ::arg()["soa-minimum-ttl"];
297 dzr.dr = DNSRecord(rr);
9262e6e1 298 }
299 }
d42d2664 300 else {
301 try {
302 dzr.dr = DNSRecord(rr);
303 }
304 catch(...) {
305 while(this->get(rr));
306 throw;
307 }
308 }
90ba52e0 309 return true;
310}
311
675fa24c 312bool DNSBackend::getBeforeAndAfterNames(uint32_t id, const DNSName& zonename, const DNSName& qname, DNSName& before, DNSName& after)
75943e62 313{
29e0008a
KM
314 DNSName unhashed;
315 bool ret = this->getBeforeAndAfterNamesAbsolute(id, qname.makeRelative(zonename).makeLowerCase(), unhashed, before, after);
316 DNSName lczonename = zonename.makeLowerCase();
317 before += lczonename;
318 after += lczonename;
75943e62
BH
319 return ret;
320}
d07fc616
PD
321
322/**
323 * Calculates a SOA serial for the zone and stores it in the third
324 * argument. Returns false if calculation is not possible for some
325 * reason (in this case, the third argument is not inspected). If it
326 * returns true, the value returned in the third argument will be set
327 * as the SOA serial.
328 *
329 * \param domain The name of the domain
330 * \param sd Information about the SOA record already available
331 * \param serial Output parameter. Only inspected when we return true
332 */
675fa24c 333bool DNSBackend::calculateSOASerial(const DNSName& domain, const SOAData& sd, time_t& serial)
d07fc616
PD
334{
335 // we do this by listing the domain and taking the maximum last modified timestamp
336
337 DNSResourceRecord i;
338 time_t newest=0;
339
340 if(!(this->list(domain, sd.domain_id))) {
f43c4448 341 DLOG(L<<Logger::Warning<<"Backend error trying to determine magic serial number of zone '"<<domain<<"'"<<endl);
d07fc616
PD
342 return false;
343 }
5f3eca86 344
d07fc616
PD
345 while(this->get(i)) {
346 if(i.last_modified>newest)
347 newest=i.last_modified;
348 }
349
a1fe72a4 350 serial=newest;
d07fc616
PD
351
352 return true;
353}
90ba52e0 354void fillSOAData(const DNSZoneRecord& in, SOAData& sd)
355{
356 sd.domain_id = in.domain_id;
357 sd.ttl = in.dr.d_ttl;
358
359 auto src=getRR<SOARecordContent>(in.dr);
360 sd.nameserver = src->d_mname;
361 sd.hostmaster = src->d_rname;
362 sd.serial = src->d_st.serial;
363 sd.refresh = src->d_st.refresh;
364 sd.retry = src->d_st.retry;
365 sd.expire = src->d_st.expire;
366 sd.default_ttl = src->d_st.minimum;
367}
368
369std::shared_ptr<DNSRecordContent> makeSOAContent(const SOAData& sd)
370{
371 struct soatimes st;
372 st.serial = sd.serial;
373 st.refresh = sd.refresh;
374 st.retry = sd.retry;
375 st.expire = sd.expire;
376 st.minimum = sd.default_ttl;
377 return std::make_shared<SOARecordContent>(sd.nameserver, sd.hostmaster, st);
378}
379
c14bc34a 380
ceea1ba6 381void fillSOAData(const string &content, SOAData &data)
382{
383 // content consists of fields separated by spaces:
384 // nameservername hostmaster serial-number [refresh [retry [expire [ minimum] ] ] ]
385
386 // fill out data with some plausible defaults:
387 // 10800 3600 604800 3600
388 vector<string>parts;
389 stringtok(parts,content);
390 int pleft=parts.size();
391
392 // cout<<"'"<<content<<"'"<<endl;
393
394 if(pleft)
395 data.nameserver=DNSName(parts[0]);
396
397 if(pleft>1)
398 data.hostmaster=DNSName(attodot(parts[1])); // ahu@ds9a.nl -> ahu.ds9a.nl, piet.puk@ds9a.nl -> piet\.puk.ds9a.nl
399
97ce13be
RG
400 try {
401 data.serial = pleft > 2 ? pdns_stou(parts[2]) : 0;
ceea1ba6 402
97ce13be
RG
403 data.refresh = pleft > 3 ? pdns_stou(parts[3])
404 : ::arg().asNum("soa-refresh-default");
ceea1ba6 405
97ce13be
RG
406 data.retry = pleft > 4 ? pdns_stou(parts[4].c_str())
407 : ::arg().asNum("soa-retry-default");
ceea1ba6 408
97ce13be
RG
409 data.expire = pleft > 5 ? pdns_stou(parts[5].c_str())
410 : ::arg().asNum("soa-expire-default");
ceea1ba6 411
97ce13be
RG
412 data.default_ttl = pleft > 6 ? pdns_stou(parts[6].c_str())
413 : ::arg().asNum("soa-minimum-ttl");
414 }
415 catch(const std::out_of_range& oor) {
416 throw PDNSException("Out of range exception parsing "+content);
417 }
ceea1ba6 418}
419
420string serializeSOAData(const SOAData &d)
421{
422 ostringstream o;
423 // nameservername hostmaster serial-number [refresh [retry [expire [ minimum] ] ] ]
424 o<<d.nameserver.toString()<<" "<< d.hostmaster.toString() <<" "<< d.serial <<" "<< d.refresh << " "<< d.retry << " "<< d.expire << " "<< d.default_ttl;
425
426 return o.str();
427}