]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsbackend.cc
poll events are bitmasks, not values
[thirdparty/pdns.git] / pdns / dnsbackend.cc
CommitLineData
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
38std::function<std::string(const std::string&, int)> g_getGeo;
39
cec52de6 40bool DNSBackend::getAuth(const DNSName &target, SOAData *sd)
c14bc34a 41{
94bfa5b6 42 return this->getSOA(target, *sd);
c14bc34a
MZ
43}
44
12c86877
BH
45void DNSBackend::setArgPrefix(const string &prefix)
46{
47 d_prefix=prefix;
48}
49
50bool DNSBackend::mustDo(const string &key)
51{
52 return arg().mustDo(d_prefix+"-"+key);
53}
54
55const string &DNSBackend::getArg(const string &key)
56{
57 return arg()[d_prefix+"-"+key];
58}
59
60int DNSBackend::getArgAsNum(const string &key)
61{
62 return arg().asNum(d_prefix+"-"+key);
63}
64
65void BackendFactory::declare(const string &suffix, const string &param, 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
71const string &BackendFactory::getName() const
72{
73 return d_name;
74}
75
76BackendMakerClass &BackendMakers()
77{
78 static BackendMakerClass bmc;
79 return bmc;
80}
81
82void BackendMakerClass::report(BackendFactory *bf)
83{
84 d_repository[bf->getName()]=bf;
85}
86
87
5f3eca86 88vector<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
98void 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
116void 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
133void 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
166int BackendMakerClass::numLauncheable()
167{
168 return d_instances.size();
169}
170
2717b8b3 171vector<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 222bool 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 258bool 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 304bool 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 314void 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
329std::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 341void 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}