]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsbackend.cc
Merge pull request #1184 from mind04/supermaster
[thirdparty/pdns.git] / pdns / dnsbackend.cc
CommitLineData
12c86877
BH
1/*
2 PowerDNS Versatile Database Driven Nameserver
835256f7 3 Copyright (C) 2005 PowerDNS.COM BV
12c86877
BH
4
5 This program is free software; you can redistribute it and/or modify
22dc646a
BH
6 it under the terms of the GNU General Public License version 2
7 as published by the Free Software Foundation
f782fe38
MH
8
9 Additionally, the license of this program contains a special
10 exception which allows to distribute the program in binary form when
11 it is linked against OpenSSL.
12c86877
BH
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
06bd9ccf 20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
12c86877 21*/
731f58b8 22#include "utility.hh"
12c86877
BH
23#include "dnsbackend.hh"
24#include "arguments.hh"
25#include "ueberbackend.hh"
26#include "logger.hh"
27
28#include <sys/types.h>
29#include "dnspacket.hh"
c87f987e 30#include "dns.hh"
12c86877 31
12c86877
BH
32string DNSBackend::getRemote(DNSPacket *p)
33{
34 return p->getRemote();
35}
36
37bool DNSBackend::getRemote(DNSPacket *p, struct sockaddr *sa, Utility::socklen_t *len)
38{
d06799d4 39 if(p->d_remote.getSocklen() < *len)
12c86877 40 return false;
d06799d4
BH
41 *len=p->d_remote.getSocklen();
42 memcpy(sa,&p->d_remote,*len);
12c86877
BH
43 return true;
44}
45
12c86877
BH
46void DNSBackend::setArgPrefix(const string &prefix)
47{
48 d_prefix=prefix;
49}
50
51bool DNSBackend::mustDo(const string &key)
52{
53 return arg().mustDo(d_prefix+"-"+key);
54}
55
56const string &DNSBackend::getArg(const string &key)
57{
58 return arg()[d_prefix+"-"+key];
59}
60
61int DNSBackend::getArgAsNum(const string &key)
62{
63 return arg().asNum(d_prefix+"-"+key);
64}
65
66void BackendFactory::declare(const string &suffix, const string &param, const string &help, const string &value)
67{
98a8b02e 68 string fullname=d_name+suffix+"-"+param;
12c86877
BH
69 arg().set(fullname,help)=value;
70}
71
72const string &BackendFactory::getName() const
73{
74 return d_name;
75}
76
77BackendMakerClass &BackendMakers()
78{
79 static BackendMakerClass bmc;
80 return bmc;
81}
82
83void BackendMakerClass::report(BackendFactory *bf)
84{
85 d_repository[bf->getName()]=bf;
86}
87
88
89vector<string> BackendMakerClass::getModules()
90{
91 load_all();
92 vector<string> ret;
93 // copy(d_repository.begin(), d_repository.end(),back_inserter(ret));
94 for(d_repository_t::const_iterator i=d_repository.begin();i!=d_repository.end();++i)
95 ret.push_back(i->first);
96 return ret;
97}
98
99void BackendMakerClass::load_all()
731f58b8
BH
100{
101 // TODO: Implement this?
12c86877
BH
102 DIR *dir=opendir(arg()["module-dir"].c_str());
103 if(!dir) {
104 L<<Logger::Error<<"Unable to open module directory '"<<arg()["module-dir"]<<"'"<<endl;
105 return;
106 }
107 struct dirent *entry;
108 while((entry=readdir(dir))) {
109 if(!strncmp(entry->d_name,"lib",3) &&
731f58b8
BH
110 strlen(entry->d_name)>13 &&
111 !strcmp(entry->d_name+strlen(entry->d_name)-10,"backend.so"))
12c86877
BH
112 load(entry->d_name);
113 }
731f58b8 114 closedir(dir);
12c86877
BH
115}
116
117void BackendMakerClass::load(const string &module)
118{
119 int res;
120
121 if(module.find(".")==string::npos)
122 res=UeberBackend::loadmodule(arg()["module-dir"]+"/lib"+module+"backend.so");
123 else if(module[0]=='/' || (module[0]=='.' && module[1]=='/') || (module[0]=='.' && module[1]=='.')) // absolute or current path
124 res=UeberBackend::loadmodule(module);
125 else
126 res=UeberBackend::loadmodule(arg()["module-dir"]+"/"+module);
127
128 if(res==false) {
731f58b8 129 L<<Logger::Error<<"dnsbackend unable to load module in "<<module<<endl;
12c86877
BH
130 exit(1);
131 }
132}
133
134void BackendMakerClass::launch(const string &instr)
135{
136 // if(instr.empty())
137 // throw ArgException("Not launching any backends - nameserver won't function");
138
139 vector<string> parts;
140 stringtok(parts,instr,", ");
141
142 for(vector<string>::const_iterator i=parts.begin();i!=parts.end();++i) {
143 const string &part=*i;
144
145 string module, name;
146 vector<string>pparts;
147 stringtok(pparts,part,": ");
148 module=pparts[0];
149 if(pparts.size()>1)
98a8b02e 150 name="-"+pparts[1];
12c86877
BH
151
152 if(d_repository.find(module)==d_repository.end()) {
153 // this is *so* userfriendly
154 load(module);
155 if(d_repository.find(module)==d_repository.end())
4957a608 156 throw ArgException("Trying to launch unknown backend '"+module+"'");
12c86877 157 }
12c86877
BH
158 d_repository[module]->declareArguments(name);
159 d_instances.push_back(make_pair(module,name));
160 }
161}
162
163int BackendMakerClass::numLauncheable()
164{
165 return d_instances.size();
166}
167
2717b8b3 168vector<DNSBackend *>BackendMakerClass::all(bool metadataOnly)
12c86877
BH
169{
170 vector<DNSBackend *>ret;
171 if(d_instances.empty())
3f81d239 172 throw PDNSException("No database backends configured for launch, unable to function");
12c86877
BH
173
174 try {
175 for(vector<pair<string,string> >::const_iterator i=d_instances.begin();i!=d_instances.end();++i) {
2717b8b3
BH
176 DNSBackend *made;
177 if(metadataOnly)
178 made = d_repository[i->first]->makeMetadataOnly(i->second);
179 else
180 made = d_repository[i->first]->make(i->second);
12c86877 181 if(!made)
3f81d239 182 throw PDNSException("Unable to launch backend '"+i->first+"'");
12c86877
BH
183
184 ret.push_back(made);
185 }
186 }
3f81d239 187 catch(PDNSException &ae) {
06ad4526
PD
188 L<<Logger::Error<<"Caught an exception instantiating a backend: "<<ae.reason<<endl;
189 L<<Logger::Error<<"Cleaning up"<<endl;
190 for(vector<DNSBackend *>::const_iterator i=ret.begin();i!=ret.end();++i)
191 delete *i;
192 throw;
193 } catch(...) {
12c86877 194 // and cleanup
bdc9f8d2 195 L<<Logger::Error<<"Caught an exception instantiating a backend, cleaning up"<<endl;
12c86877
BH
196 for(vector<DNSBackend *>::const_iterator i=ret.begin();i!=ret.end();++i)
197 delete *i;
198 throw;
199 }
200
201 return ret;
202}
203
bdc9f8d2 204/** getSOA() is a function that is called to get the SOA of a domain. Callers should ONLY
4dfd8645 205 use getSOA() and not perform a lookup() themselves as backends may decide to special case
bdc9f8d2
BH
206 the SOA record.
207
208 Returns false if there is definitely no SOA for the domain. May throw a DBException
209 to indicate that the backend is currently unable to supply an answer.
210
25b1d5d7
BH
211 WARNING: This function *may* fill out the db attribute of the SOAData, but then again,
212 it may not! If you find a zero in there, you may have been handed a non-live and cached
213 answer, in which case you need to perform a getDomainInfo call!
214
bdc9f8d2
BH
215 \param domain Domain we want to get the SOA details of
216 \param sd SOAData which is filled with the SOA details
217*/
35933370 218bool DNSBackend::getSOA(const string &domain, SOAData &sd, DNSPacket *p)
12c86877 219{
35933370 220 this->lookup(QType(QType::SOA),domain,p);
5f5221b4 221
12c86877 222 DNSResourceRecord rr;
702b226d 223 rr.auth = true;
12c86877
BH
224
225 int hits=0;
226
227 while(this->get(rr)) {
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;
12c86877
BH
238 if(sd.nameserver.empty())
239 sd.nameserver=arg()["default-soa-name"];
240
c87f987e
KM
241 if(sd.hostmaster.empty()) {
242 if (!arg().isEmpty("default-soa-mail")) {
243 sd.hostmaster=arg()["default-soa-mail"];
244 attodot(sd.hostmaster);
245 }
246 else
247 sd.hostmaster="hostmaster."+domain;
248 }
12c86877
BH
249
250 if(!sd.serial) { // magic time!
251 DLOG(L<<Logger::Warning<<"Doing soa serialnumber autocalculation for "<<rr.qname<<endl);
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
PD
257 } else {
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
75943e62
BH
266bool DNSBackend::getBeforeAndAfterNames(uint32_t id, const std::string& zonename, const std::string& qname, std::string& before, std::string& after)
267{
268 string lcqname=toLower(qname);
4b153d8c
PD
269 string lczonename=toLower(zonename);
270 lcqname=makeRelative(lcqname, lczonename);
75943e62
BH
271
272 lcqname=labelReverse(lcqname);
c3c89361
BH
273 string dnc;
274 bool ret = this->getBeforeAndAfterNamesAbsolute(id, lcqname, dnc, before, after);
75943e62 275
4b153d8c
PD
276 before=dotConcat(labelReverse(before), lczonename);
277 after=dotConcat(labelReverse(after), lczonename);
75943e62
BH
278 return ret;
279}
d07fc616
PD
280
281/**
282 * Calculates a SOA serial for the zone and stores it in the third
283 * argument. Returns false if calculation is not possible for some
284 * reason (in this case, the third argument is not inspected). If it
285 * returns true, the value returned in the third argument will be set
286 * as the SOA serial.
287 *
288 * \param domain The name of the domain
289 * \param sd Information about the SOA record already available
290 * \param serial Output parameter. Only inspected when we return true
291 */
292bool DNSBackend::calculateSOASerial(const string& domain, const SOAData& sd, time_t& serial)
293{
294 // we do this by listing the domain and taking the maximum last modified timestamp
295
296 DNSResourceRecord i;
297 time_t newest=0;
298
299 if(!(this->list(domain, sd.domain_id))) {
300 DLOG(L<<Logger::Warning<<"Backend error trying to determine magic serial number of zone '"<<domain<<"'"<<endl);
301 return false;
302 }
303
304 while(this->get(i)) {
305 if(i.last_modified>newest)
306 newest=i.last_modified;
307 }
308
309 serial=newest; // +arg().asNum("soa-serial-offset");
310
311 return true;
312}