]>
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 | 68 | arg().set(fullname,help)=value; |
8864bdf6 | 69 | arg().setDefault(fullname,value); |
12c86877 BH |
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 | ||
5f3eca86 | 89 | vector<string> BackendMakerClass::getModules() |
12c86877 BH |
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) { | |
e6a9dde5 | 104 | g_log<<Logger::Error<<"Unable to open module directory '"<<arg()["module-dir"]<<"'"<<endl; |
12c86877 BH |
105 | return; |
106 | } | |
107 | struct dirent *entry; | |
108 | while((entry=readdir(dir))) { | |
5f3eca86 | 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 | { | |
34c513f9 | 119 | bool res; |
12c86877 BH |
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); | |
5f3eca86 | 127 | |
12c86877 | 128 | if(res==false) { |
e6a9dde5 | 129 | g_log<<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"); | |
5f3eca86 | 138 | |
12c86877 BH |
139 | vector<string> parts; |
140 | stringtok(parts,instr,", "); | |
5f3eca86 | 141 | |
aa93edd5 | 142 | for (const auto& part : parts) |
c52dd6af PL |
143 | if (count(parts.begin(), parts.end(), part) > 1) |
144 | throw ArgException("Refusing to launch multiple backends with the same name '" + part + "', verify all 'launch' statements in your configuration"); | |
145 | ||
12c86877 BH |
146 | for(vector<string>::const_iterator i=parts.begin();i!=parts.end();++i) { |
147 | const string &part=*i; | |
5f3eca86 | 148 | |
12c86877 BH |
149 | string module, name; |
150 | vector<string>pparts; | |
151 | stringtok(pparts,part,": "); | |
152 | module=pparts[0]; | |
153 | if(pparts.size()>1) | |
98a8b02e | 154 | name="-"+pparts[1]; |
5f3eca86 | 155 | |
12c86877 BH |
156 | if(d_repository.find(module)==d_repository.end()) { |
157 | // this is *so* userfriendly | |
158 | load(module); | |
159 | if(d_repository.find(module)==d_repository.end()) | |
4957a608 | 160 | throw ArgException("Trying to launch unknown backend '"+module+"'"); |
12c86877 | 161 | } |
12c86877 BH |
162 | d_repository[module]->declareArguments(name); |
163 | d_instances.push_back(make_pair(module,name)); | |
164 | } | |
165 | } | |
166 | ||
167 | int BackendMakerClass::numLauncheable() | |
168 | { | |
169 | return d_instances.size(); | |
170 | } | |
171 | ||
2717b8b3 | 172 | vector<DNSBackend *>BackendMakerClass::all(bool metadataOnly) |
12c86877 BH |
173 | { |
174 | vector<DNSBackend *>ret; | |
175 | if(d_instances.empty()) | |
3f81d239 | 176 | throw PDNSException("No database backends configured for launch, unable to function"); |
12c86877 BH |
177 | |
178 | try { | |
179 | for(vector<pair<string,string> >::const_iterator i=d_instances.begin();i!=d_instances.end();++i) { | |
2717b8b3 BH |
180 | DNSBackend *made; |
181 | if(metadataOnly) | |
182 | made = d_repository[i->first]->makeMetadataOnly(i->second); | |
5f3eca86 | 183 | else |
2717b8b3 | 184 | made = d_repository[i->first]->make(i->second); |
12c86877 | 185 | if(!made) |
3f81d239 | 186 | throw PDNSException("Unable to launch backend '"+i->first+"'"); |
12c86877 BH |
187 | |
188 | ret.push_back(made); | |
189 | } | |
190 | } | |
3f81d239 | 191 | catch(PDNSException &ae) { |
e6a9dde5 PL |
192 | g_log<<Logger::Error<<"Caught an exception instantiating a backend: "<<ae.reason<<endl; |
193 | g_log<<Logger::Error<<"Cleaning up"<<endl; | |
06ad4526 PD |
194 | for(vector<DNSBackend *>::const_iterator i=ret.begin();i!=ret.end();++i) |
195 | delete *i; | |
07dbfb4f | 196 | throw; |
06ad4526 | 197 | } catch(...) { |
12c86877 | 198 | // and cleanup |
e6a9dde5 | 199 | g_log<<Logger::Error<<"Caught an exception instantiating a backend, cleaning up"<<endl; |
12c86877 BH |
200 | for(vector<DNSBackend *>::const_iterator i=ret.begin();i!=ret.end();++i) |
201 | delete *i; | |
07dbfb4f | 202 | throw; |
12c86877 | 203 | } |
5f3eca86 | 204 | |
12c86877 BH |
205 | return ret; |
206 | } | |
207 | ||
bdc9f8d2 | 208 | /** getSOA() is a function that is called to get the SOA of a domain. Callers should ONLY |
4dfd8645 | 209 | use getSOA() and not perform a lookup() themselves as backends may decide to special case |
bdc9f8d2 | 210 | the SOA record. |
5f3eca86 | 211 | |
bdc9f8d2 BH |
212 | Returns false if there is definitely no SOA for the domain. May throw a DBException |
213 | to indicate that the backend is currently unable to supply an answer. | |
214 | ||
25b1d5d7 BH |
215 | WARNING: This function *may* fill out the db attribute of the SOAData, but then again, |
216 | it may not! If you find a zero in there, you may have been handed a non-live and cached | |
217 | answer, in which case you need to perform a getDomainInfo call! | |
218 | ||
bdc9f8d2 BH |
219 | \param domain Domain we want to get the SOA details of |
220 | \param sd SOAData which is filled with the SOA details | |
13f9e280 | 221 | \param unmodifiedSerial bool if set, serial will be returned as stored in the backend (maybe 0) |
bdc9f8d2 | 222 | */ |
76e1255a | 223 | bool DNSBackend::getSOA(const DNSName &domain, SOAData &sd) |
12c86877 | 224 | { |
acb61e0a | 225 | this->lookup(QType(QType::SOA),domain,-1); |
5f3eca86 | 226 | |
12c86877 | 227 | DNSResourceRecord rr; |
5f3eca86 | 228 | rr.auth = true; |
12c86877 BH |
229 | |
230 | int hits=0; | |
231 | ||
232 | while(this->get(rr)) { | |
5f3eca86 | 233 | if (rr.qtype != QType::SOA) throw PDNSException("Got non-SOA record when asking for SOA"); |
12c86877 | 234 | hits++; |
34b37bbb | 235 | fillSOAData(rr.content, sd); |
12c86877 BH |
236 | sd.domain_id=rr.domain_id; |
237 | sd.ttl=rr.ttl; | |
238 | } | |
7b308b7e | 239 | |
12c86877 BH |
240 | if(!hits) |
241 | return false; | |
5f5221b4 | 242 | sd.qname = domain; |
675fa24c | 243 | if(!sd.nameserver.countLabels()) |
290a083d | 244 | sd.nameserver= DNSName(arg()["default-soa-name"]); |
5f3eca86 | 245 | |
675fa24c | 246 | if(!sd.hostmaster.countLabels()) { |
c87f987e | 247 | if (!arg().isEmpty("default-soa-mail")) { |
290a083d | 248 | sd.hostmaster= DNSName(arg()["default-soa-mail"]); |
3343ad1f | 249 | // attodot(sd.hostmaster); FIXME400 |
c87f987e KM |
250 | } |
251 | else | |
7abbc40f | 252 | sd.hostmaster=DNSName("hostmaster")+domain; |
c87f987e | 253 | } |
12c86877 | 254 | |
12c86877 BH |
255 | sd.db=this; |
256 | return true; | |
257 | } | |
258 | ||
90ba52e0 | 259 | bool DNSBackend::get(DNSZoneRecord& dzr) |
260 | { | |
aa7b2405 | 261 | // cout<<"DNSBackend::get(DNSZoneRecord&) called - translating into DNSResourceRecord query"<<endl; |
90ba52e0 | 262 | DNSResourceRecord rr; |
263 | if(!this->get(rr)) | |
264 | return false; | |
265 | dzr.auth = rr.auth; | |
266 | dzr.domain_id = rr.domain_id; | |
158bc1d1 | 267 | dzr.scopeMask = rr.scopeMask; |
c7d5b054 | 268 | if(rr.qtype.getCode() == QType::TXT && !rr.content.empty() && rr.content[0]!='"') |
269 | rr.content = "\""+ rr.content + "\""; | |
9262e6e1 | 270 | if(rr.qtype.getCode() == QType::SOA) { |
271 | try { | |
272 | dzr.dr = DNSRecord(rr); | |
273 | } catch(...) { | |
274 | vector<string> parts; | |
275 | stringtok(parts, rr.content, " \t"); | |
276 | if(parts.size() < 1) | |
484efba7 | 277 | rr.content = arg()["default-soa-name"]; |
9262e6e1 | 278 | if(parts.size() < 2) |
484efba7 | 279 | rr.content += " " +arg()["default-soa-mail"]; |
9262e6e1 | 280 | if(parts.size() < 3) |
281 | rr.content += " 0"; | |
282 | if(parts.size() < 4) | |
283 | rr.content += " " + ::arg()["soa-refresh-default"]; | |
284 | if(parts.size() < 5) | |
484efba7 | 285 | rr.content += " " + ::arg()["soa-retry-default"]; |
9262e6e1 | 286 | if(parts.size() < 6) |
484efba7 KM |
287 | rr.content += " " + ::arg()["soa-expire-default"]; |
288 | if(parts.size() < 7) | |
289 | rr.content += " " + ::arg()["soa-minimum-ttl"]; | |
290 | dzr.dr = DNSRecord(rr); | |
9262e6e1 | 291 | } |
292 | } | |
d42d2664 | 293 | else { |
294 | try { | |
295 | dzr.dr = DNSRecord(rr); | |
296 | } | |
297 | catch(...) { | |
298 | while(this->get(rr)); | |
299 | throw; | |
300 | } | |
301 | } | |
90ba52e0 | 302 | return true; |
303 | } | |
304 | ||
675fa24c | 305 | bool DNSBackend::getBeforeAndAfterNames(uint32_t id, const DNSName& zonename, const DNSName& qname, DNSName& before, DNSName& after) |
75943e62 | 306 | { |
29e0008a KM |
307 | DNSName unhashed; |
308 | bool ret = this->getBeforeAndAfterNamesAbsolute(id, qname.makeRelative(zonename).makeLowerCase(), unhashed, before, after); | |
309 | DNSName lczonename = zonename.makeLowerCase(); | |
310 | before += lczonename; | |
311 | after += lczonename; | |
75943e62 BH |
312 | return ret; |
313 | } | |
d07fc616 | 314 | |
90ba52e0 | 315 | void fillSOAData(const DNSZoneRecord& in, SOAData& sd) |
316 | { | |
317 | sd.domain_id = in.domain_id; | |
318 | sd.ttl = in.dr.d_ttl; | |
319 | ||
320 | auto src=getRR<SOARecordContent>(in.dr); | |
321 | sd.nameserver = src->d_mname; | |
322 | sd.hostmaster = src->d_rname; | |
323 | sd.serial = src->d_st.serial; | |
324 | sd.refresh = src->d_st.refresh; | |
325 | sd.retry = src->d_st.retry; | |
326 | sd.expire = src->d_st.expire; | |
192bcba2 | 327 | sd.minimum = src->d_st.minimum; |
90ba52e0 | 328 | } |
329 | ||
330 | std::shared_ptr<DNSRecordContent> makeSOAContent(const SOAData& sd) | |
331 | { | |
332 | struct soatimes st; | |
333 | st.serial = sd.serial; | |
334 | st.refresh = sd.refresh; | |
335 | st.retry = sd.retry; | |
336 | st.expire = sd.expire; | |
192bcba2 | 337 | st.minimum = sd.minimum; |
90ba52e0 | 338 | return std::make_shared<SOARecordContent>(sd.nameserver, sd.hostmaster, st); |
339 | } | |
340 | ||
c14bc34a | 341 | |
ceea1ba6 | 342 | void fillSOAData(const string &content, SOAData &data) |
343 | { | |
344 | // content consists of fields separated by spaces: | |
345 | // nameservername hostmaster serial-number [refresh [retry [expire [ minimum] ] ] ] | |
346 | ||
347 | // fill out data with some plausible defaults: | |
348 | // 10800 3600 604800 3600 | |
349 | vector<string>parts; | |
9bbcf03a | 350 | parts.reserve(7); |
ceea1ba6 | 351 | stringtok(parts,content); |
352 | int pleft=parts.size(); | |
353 | ||
354 | // cout<<"'"<<content<<"'"<<endl; | |
355 | ||
356 | if(pleft) | |
357 | data.nameserver=DNSName(parts[0]); | |
358 | ||
359 | if(pleft>1) | |
360 | data.hostmaster=DNSName(attodot(parts[1])); // ahu@ds9a.nl -> ahu.ds9a.nl, piet.puk@ds9a.nl -> piet\.puk.ds9a.nl | |
361 | ||
97ce13be RG |
362 | try { |
363 | data.serial = pleft > 2 ? pdns_stou(parts[2]) : 0; | |
ceea1ba6 | 364 | |
97ce13be RG |
365 | data.refresh = pleft > 3 ? pdns_stou(parts[3]) |
366 | : ::arg().asNum("soa-refresh-default"); | |
ceea1ba6 | 367 | |
97ce13be RG |
368 | data.retry = pleft > 4 ? pdns_stou(parts[4].c_str()) |
369 | : ::arg().asNum("soa-retry-default"); | |
ceea1ba6 | 370 | |
97ce13be RG |
371 | data.expire = pleft > 5 ? pdns_stou(parts[5].c_str()) |
372 | : ::arg().asNum("soa-expire-default"); | |
ceea1ba6 | 373 | |
192bcba2 | 374 | data.minimum = pleft > 6 ? pdns_stou(parts[6].c_str()) |
97ce13be RG |
375 | : ::arg().asNum("soa-minimum-ttl"); |
376 | } | |
377 | catch(const std::out_of_range& oor) { | |
378 | throw PDNSException("Out of range exception parsing "+content); | |
379 | } | |
ceea1ba6 | 380 | } |