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