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