]>
Commit | Line | Data |
---|---|---|
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 |
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 | { | |
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 |
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 ¶m, 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 | ||
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() | |
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 | ||
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) { | |
731f58b8 | 129 | L<<Logger::Error<<"dnsbackend unable to load module in "<<module<<endl; |
12c86877 BH |
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) | |
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 | ||
163 | int BackendMakerClass::numLauncheable() | |
164 | { | |
165 | return d_instances.size(); | |
166 | } | |
167 | ||
2717b8b3 | 168 | vector<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 | 218 | bool 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 |
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); | |
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 | */ | |
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 | } |