]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsbackend.cc
Merge pull request #9070 from rgacogne/boost-173
[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 68 arg().set(fullname,help)=value;
8864bdf6 69 arg().setDefault(fullname,value);
12c86877
BH
70}
71
72const string &BackendFactory::getName() const
73{
74 return d_name;
75}
76
77BackendMakerClass &BackendMakers()
78{
79 static BackendMakerClass bmc;
80 return bmc;
81}
82
83void BackendMakerClass::report(BackendFactory *bf)
84{
85 d_repository[bf->getName()]=bf;
86}
87
88
5f3eca86 89vector<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
99void 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
117void 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
134void 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
167int BackendMakerClass::numLauncheable()
168{
169 return d_instances.size();
170}
171
2717b8b3 172vector<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 223bool 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 259bool 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 305bool 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 315void 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
330std::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 342void 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}