]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsbackend.cc
add OpenSSL exception to PowerDNS, Netherlabs, van Dijk and Hubert copyrights
[thirdparty/pdns.git] / pdns / dnsbackend.cc
1 /*
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2005 PowerDNS.COM BV
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2
7 as published by the Free Software Foundation
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.
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 St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22 #include "utility.hh"
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"
30 #include "dns.hh"
31
32 string DNSBackend::getRemote(DNSPacket *p)
33 {
34 return p->getRemote();
35 }
36
37 bool DNSBackend::getRemote(DNSPacket *p, struct sockaddr *sa, Utility::socklen_t *len)
38 {
39 if(p->d_remote.getSocklen() < *len)
40 return false;
41 *len=p->d_remote.getSocklen();
42 memcpy(sa,&p->d_remote,*len);
43 return true;
44 }
45
46 void DNSBackend::setArgPrefix(const string &prefix)
47 {
48 d_prefix=prefix;
49 }
50
51 bool DNSBackend::mustDo(const string &key)
52 {
53 return arg().mustDo(d_prefix+"-"+key);
54 }
55
56 const string &DNSBackend::getArg(const string &key)
57 {
58 return arg()[d_prefix+"-"+key];
59 }
60
61 int DNSBackend::getArgAsNum(const string &key)
62 {
63 return arg().asNum(d_prefix+"-"+key);
64 }
65
66 void BackendFactory::declare(const string &suffix, const string &param, const string &help, const string &value)
67 {
68 string fullname=d_name+suffix+"-"+param;
69 arg().set(fullname,help)=value;
70 }
71
72 const string &BackendFactory::getName() const
73 {
74 return d_name;
75 }
76
77 BackendMakerClass &BackendMakers()
78 {
79 static BackendMakerClass bmc;
80 return bmc;
81 }
82
83 void BackendMakerClass::report(BackendFactory *bf)
84 {
85 d_repository[bf->getName()]=bf;
86 }
87
88
89 vector<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
99 void BackendMakerClass::load_all()
100 {
101 // TODO: Implement this?
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) &&
110 strlen(entry->d_name)>13 &&
111 !strcmp(entry->d_name+strlen(entry->d_name)-10,"backend.so"))
112 load(entry->d_name);
113 }
114 closedir(dir);
115 }
116
117 void 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) {
129 L<<Logger::Error<<"dnsbackend unable to load module in "<<module<<endl;
130 exit(1);
131 }
132 }
133
134 void 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)
150 name="-"+pparts[1];
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())
156 throw ArgException("Trying to launch unknown backend '"+module+"'");
157 }
158 d_repository[module]->declareArguments(name);
159 d_instances.push_back(make_pair(module,name));
160 }
161 }
162
163 int BackendMakerClass::numLauncheable()
164 {
165 return d_instances.size();
166 }
167
168 vector<DNSBackend *>BackendMakerClass::all(bool metadataOnly)
169 {
170 vector<DNSBackend *>ret;
171 if(d_instances.empty())
172 throw PDNSException("No database backends configured for launch, unable to function");
173
174 try {
175 for(vector<pair<string,string> >::const_iterator i=d_instances.begin();i!=d_instances.end();++i) {
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);
181 if(!made)
182 throw PDNSException("Unable to launch backend '"+i->first+"'");
183
184 ret.push_back(made);
185 }
186 }
187 catch(PDNSException &ae) {
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(...) {
194 // and cleanup
195 L<<Logger::Error<<"Caught an exception instantiating a backend, cleaning up"<<endl;
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
204 /** getSOA() is a function that is called to get the SOA of a domain. Callers should ONLY
205 use getSOA() and not perform a lookup() themselves as backends may decide to special case
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
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
215 \param domain Domain we want to get the SOA details of
216 \param sd SOAData which is filled with the SOA details
217 */
218 bool DNSBackend::getSOA(const string &domain, SOAData &sd, DNSPacket *p)
219 {
220 this->lookup(QType(QType::SOA),domain,p);
221
222 DNSResourceRecord rr;
223 rr.auth = true;
224
225 int hits=0;
226
227 while(this->get(rr)) {
228 hits++;
229 fillSOAData(rr.content, sd);
230 sd.domain_id=rr.domain_id;
231 sd.ttl=rr.ttl;
232 sd.scopeMask = rr.scopeMask;
233 }
234
235 if(!hits)
236 return false;
237 sd.qname = domain;
238 if(sd.nameserver.empty())
239 sd.nameserver=arg()["default-soa-name"];
240
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 }
249
250 if(!sd.serial) { // magic time!
251 DLOG(L<<Logger::Warning<<"Doing soa serialnumber autocalculation for "<<rr.qname<<endl);
252
253 time_t serial;
254 if (calculateSOASerial(domain, sd, serial)) {
255 sd.serial = serial;
256 //DLOG(L<<"autocalculated soa serialnumber for "<<rr.qname<<" is "<<newest<<endl);
257 } else {
258 DLOG(L<<"soa serialnumber calculation failed for "<<rr.qname<<endl);
259 }
260
261 }
262 sd.db=this;
263 return true;
264 }
265
266 bool 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);
269 string lczonename=toLower(zonename);
270 lcqname=makeRelative(lcqname, lczonename);
271
272 lcqname=labelReverse(lcqname);
273 string dnc;
274 bool ret = this->getBeforeAndAfterNamesAbsolute(id, lcqname, dnc, before, after);
275
276 before=dotConcat(labelReverse(before), lczonename);
277 after=dotConcat(labelReverse(after), lczonename);
278 return ret;
279 }
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 */
292 bool 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 }