]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsbackend.cc
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
26 #include "dnsbackend.hh"
27 #include "arguments.hh"
28 #include "ueberbackend.hh"
31 #include <sys/types.h>
32 #include "pdns/packetcache.hh"
33 #include "dnspacket.hh"
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
;
40 bool DNSBackend::getAuth(const DNSName
&target
, SOAData
*sd
)
42 return this->getSOA(target
, *sd
);
45 void DNSBackend::setArgPrefix(const string
&prefix
)
50 bool DNSBackend::mustDo(const string
&key
)
52 return arg().mustDo(d_prefix
+"-"+key
);
55 const string
&DNSBackend::getArg(const string
&key
)
57 return arg()[d_prefix
+"-"+key
];
60 int DNSBackend::getArgAsNum(const string
&key
)
62 return arg().asNum(d_prefix
+"-"+key
);
65 void BackendFactory::declare(const string
&suffix
, const string
¶m
, const string
&help
, const string
&value
)
67 string fullname
=d_name
+suffix
+"-"+param
;
68 arg().set(fullname
,help
)=value
;
71 const string
&BackendFactory::getName() const
76 BackendMakerClass
&BackendMakers()
78 static BackendMakerClass bmc
;
82 void BackendMakerClass::report(BackendFactory
*bf
)
84 d_repository
[bf
->getName()]=bf
;
88 vector
<string
> BackendMakerClass::getModules()
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
);
98 void BackendMakerClass::load_all()
100 // TODO: Implement this?
101 DIR *dir
=opendir(arg()["module-dir"].c_str());
103 g_log
<<Logger::Error
<<"Unable to open module directory '"<<arg()["module-dir"]<<"'"<<endl
;
106 struct dirent
*entry
;
107 while((entry
=readdir(dir
))) {
108 if(!strncmp(entry
->d_name
,"lib",3) &&
109 strlen(entry
->d_name
)>13 &&
110 !strcmp(entry
->d_name
+strlen(entry
->d_name
)-10,"backend.so"))
116 void BackendMakerClass::load(const string
&module
)
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
);
125 res
=UeberBackend::loadmodule(arg()["module-dir"]+"/"+module
);
128 g_log
<<Logger::Error
<<"DNSBackend unable to load module in "<<module
<<endl
;
133 void BackendMakerClass::launch(const string
&instr
)
136 // throw ArgException("Not launching any backends - nameserver won't function");
138 vector
<string
> parts
;
139 stringtok(parts
,instr
,", ");
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");
145 for(vector
<string
>::const_iterator i
=parts
.begin();i
!=parts
.end();++i
) {
146 const string
&part
=*i
;
149 vector
<string
>pparts
;
150 stringtok(pparts
,part
,": ");
155 if(d_repository
.find(module
)==d_repository
.end()) {
156 // this is *so* userfriendly
158 if(d_repository
.find(module
)==d_repository
.end())
159 throw ArgException("Trying to launch unknown backend '"+module
+"'");
161 d_repository
[module
]->declareArguments(name
);
162 d_instances
.push_back(make_pair(module
,name
));
166 int BackendMakerClass::numLauncheable()
168 return d_instances
.size();
171 vector
<DNSBackend
*>BackendMakerClass::all(bool metadataOnly
)
173 vector
<DNSBackend
*>ret
;
174 if(d_instances
.empty())
175 throw PDNSException("No database backends configured for launch, unable to function");
178 for(vector
<pair
<string
,string
> >::const_iterator i
=d_instances
.begin();i
!=d_instances
.end();++i
) {
181 made
= d_repository
[i
->first
]->makeMetadataOnly(i
->second
);
183 made
= d_repository
[i
->first
]->make(i
->second
);
185 throw PDNSException("Unable to launch backend '"+i
->first
+"'");
190 catch(PDNSException
&ae
) {
191 g_log
<<Logger::Error
<<"Caught an exception instantiating a backend: "<<ae
.reason
<<endl
;
192 g_log
<<Logger::Error
<<"Cleaning up"<<endl
;
193 for(vector
<DNSBackend
*>::const_iterator i
=ret
.begin();i
!=ret
.end();++i
)
198 g_log
<<Logger::Error
<<"Caught an exception instantiating a backend, cleaning up"<<endl
;
199 for(vector
<DNSBackend
*>::const_iterator i
=ret
.begin();i
!=ret
.end();++i
)
207 /** getSOA() is a function that is called to get the SOA of a domain. Callers should ONLY
208 use getSOA() and not perform a lookup() themselves as backends may decide to special case
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.
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!
218 \param domain Domain we want to get the SOA details of
219 \param sd SOAData which is filled with the SOA details
220 \param unmodifiedSerial bool if set, serial will be returned as stored in the backend (maybe 0)
222 bool DNSBackend::getSOA(const DNSName
&domain
, SOAData
&sd
, bool unmodifiedSerial
)
224 this->lookup(QType(QType::SOA
),domain
);
226 DNSResourceRecord rr
;
231 while(this->get(rr
)) {
232 if (rr
.qtype
!= QType::SOA
) throw PDNSException("Got non-SOA record when asking for SOA");
234 fillSOAData(rr
.content
, sd
);
235 sd
.domain_id
=rr
.domain_id
;
242 if(!sd
.nameserver
.countLabels())
243 sd
.nameserver
= DNSName(arg()["default-soa-name"]);
245 if(!sd
.hostmaster
.countLabels()) {
246 if (!arg().isEmpty("default-soa-mail")) {
247 sd
.hostmaster
= DNSName(arg()["default-soa-mail"]);
248 // attodot(sd.hostmaster); FIXME400
251 sd
.hostmaster
=DNSName("hostmaster")+domain
;
254 if(!unmodifiedSerial
&& !sd
.serial
) { // magic time!
255 DLOG(g_log
<<Logger::Warning
<<"Doing SOA serial number autocalculation for "<<rr
.qname
<<endl
);
258 if (calculateSOASerial(domain
, sd
, serial
)) {
260 //DLOG(g_log<<"autocalculated soa serialnumber for "<<rr.qname<<" is "<<newest<<endl);
262 DLOG(g_log
<<"soa serialnumber calculation failed for "<<rr
.qname
<<endl
);
270 bool DNSBackend::get(DNSZoneRecord
& dzr
)
272 // cout<<"DNSBackend::get(DNSZoneRecord&) called - translating into DNSResourceRecord query"<<endl;
273 DNSResourceRecord rr
;
277 dzr
.domain_id
= rr
.domain_id
;
278 dzr
.scopeMask
= rr
.scopeMask
;
279 if(rr
.qtype
.getCode() == QType::TXT
&& !rr
.content
.empty() && rr
.content
[0]!='"')
280 rr
.content
= "\""+ rr
.content
+ "\"";
281 if(rr
.qtype
.getCode() == QType::SOA
) {
283 dzr
.dr
= DNSRecord(rr
);
285 vector
<string
> parts
;
286 stringtok(parts
, rr
.content
, " \t");
288 rr
.content
= arg()["default-soa-name"];
290 rr
.content
+= " " +arg()["default-soa-mail"];
294 rr
.content
+= " " + ::arg()["soa-refresh-default"];
296 rr
.content
+= " " + ::arg()["soa-retry-default"];
298 rr
.content
+= " " + ::arg()["soa-expire-default"];
300 rr
.content
+= " " + ::arg()["soa-minimum-ttl"];
301 dzr
.dr
= DNSRecord(rr
);
306 dzr
.dr
= DNSRecord(rr
);
309 while(this->get(rr
));
316 bool DNSBackend::getBeforeAndAfterNames(uint32_t id
, const DNSName
& zonename
, const DNSName
& qname
, DNSName
& before
, DNSName
& after
)
319 bool ret
= this->getBeforeAndAfterNamesAbsolute(id
, qname
.makeRelative(zonename
).makeLowerCase(), unhashed
, before
, after
);
320 DNSName lczonename
= zonename
.makeLowerCase();
321 before
+= lczonename
;
327 * Calculates a SOA serial for the zone and stores it in the third
328 * argument. Returns false if calculation is not possible for some
329 * reason (in this case, the third argument is not inspected). If it
330 * returns true, the value returned in the third argument will be set
333 * \param domain The name of the domain
334 * \param sd Information about the SOA record already available
335 * \param serial Output parameter. Only inspected when we return true
337 bool DNSBackend::calculateSOASerial(const DNSName
& domain
, const SOAData
& sd
, uint32_t& serial
)
339 // we do this by listing the domain and taking the maximum last modified timestamp
344 if(!(this->list(domain
, sd
.domain_id
))) {
345 DLOG(g_log
<<Logger::Warning
<<"Backend error trying to determine magic serial number of zone '"<<domain
<<"'"<<endl
);
349 while(this->get(i
)) {
350 if(i
.last_modified
>newest
)
351 newest
=i
.last_modified
;
358 void fillSOAData(const DNSZoneRecord
& in
, SOAData
& sd
)
360 sd
.domain_id
= in
.domain_id
;
361 sd
.ttl
= in
.dr
.d_ttl
;
363 auto src
=getRR
<SOARecordContent
>(in
.dr
);
364 sd
.nameserver
= src
->d_mname
;
365 sd
.hostmaster
= src
->d_rname
;
366 sd
.serial
= src
->d_st
.serial
;
367 sd
.refresh
= src
->d_st
.refresh
;
368 sd
.retry
= src
->d_st
.retry
;
369 sd
.expire
= src
->d_st
.expire
;
370 sd
.default_ttl
= src
->d_st
.minimum
;
373 std::shared_ptr
<DNSRecordContent
> makeSOAContent(const SOAData
& sd
)
376 st
.serial
= sd
.serial
;
377 st
.refresh
= sd
.refresh
;
379 st
.expire
= sd
.expire
;
380 st
.minimum
= sd
.default_ttl
;
381 return std::make_shared
<SOARecordContent
>(sd
.nameserver
, sd
.hostmaster
, st
);
385 void fillSOAData(const string
&content
, SOAData
&data
)
387 // content consists of fields separated by spaces:
388 // nameservername hostmaster serial-number [refresh [retry [expire [ minimum] ] ] ]
390 // fill out data with some plausible defaults:
391 // 10800 3600 604800 3600
393 stringtok(parts
,content
);
394 int pleft
=parts
.size();
396 // cout<<"'"<<content<<"'"<<endl;
399 data
.nameserver
=DNSName(parts
[0]);
402 data
.hostmaster
=DNSName(attodot(parts
[1])); // ahu@ds9a.nl -> ahu.ds9a.nl, piet.puk@ds9a.nl -> piet\.puk.ds9a.nl
405 data
.serial
= pleft
> 2 ? pdns_stou(parts
[2]) : 0;
407 data
.refresh
= pleft
> 3 ? pdns_stou(parts
[3])
408 : ::arg().asNum("soa-refresh-default");
410 data
.retry
= pleft
> 4 ? pdns_stou(parts
[4].c_str())
411 : ::arg().asNum("soa-retry-default");
413 data
.expire
= pleft
> 5 ? pdns_stou(parts
[5].c_str())
414 : ::arg().asNum("soa-expire-default");
416 data
.default_ttl
= pleft
> 6 ? pdns_stou(parts
[6].c_str())
417 : ::arg().asNum("soa-minimum-ttl");
419 catch(const std::out_of_range
& oor
) {
420 throw PDNSException("Out of range exception parsing "+content
);