]>
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 bool DNSBackend::getAuth(const DNSName
&target
, SOAData
*sd
)
38 return this->getSOA(target
, *sd
);
41 void DNSBackend::setArgPrefix(const string
&prefix
)
46 bool DNSBackend::mustDo(const string
&key
)
48 return arg().mustDo(d_prefix
+"-"+key
);
51 const string
&DNSBackend::getArg(const string
&key
)
53 return arg()[d_prefix
+"-"+key
];
56 int DNSBackend::getArgAsNum(const string
&key
)
58 return arg().asNum(d_prefix
+"-"+key
);
61 void BackendFactory::declare(const string
&suffix
, const string
¶m
, const string
&help
, const string
&value
)
63 string fullname
=d_name
+suffix
+"-"+param
;
64 arg().set(fullname
,help
)=value
;
67 const string
&BackendFactory::getName() const
72 BackendMakerClass
&BackendMakers()
74 static BackendMakerClass bmc
;
78 void BackendMakerClass::report(BackendFactory
*bf
)
80 d_repository
[bf
->getName()]=bf
;
84 vector
<string
> BackendMakerClass::getModules()
88 // copy(d_repository.begin(), d_repository.end(),back_inserter(ret));
89 for(d_repository_t::const_iterator i
=d_repository
.begin();i
!=d_repository
.end();++i
)
90 ret
.push_back(i
->first
);
94 void BackendMakerClass::load_all()
96 // TODO: Implement this?
97 DIR *dir
=opendir(arg()["module-dir"].c_str());
99 L
<<Logger::Error
<<"Unable to open module directory '"<<arg()["module-dir"]<<"'"<<endl
;
102 struct dirent
*entry
;
103 while((entry
=readdir(dir
))) {
104 if(!strncmp(entry
->d_name
,"lib",3) &&
105 strlen(entry
->d_name
)>13 &&
106 !strcmp(entry
->d_name
+strlen(entry
->d_name
)-10,"backend.so"))
112 void BackendMakerClass::load(const string
&module
)
116 if(module
.find(".")==string::npos
)
117 res
=UeberBackend::loadmodule(arg()["module-dir"]+"/lib"+module
+"backend.so");
118 else if(module
[0]=='/' || (module
[0]=='.' && module
[1]=='/') || (module
[0]=='.' && module
[1]=='.')) // absolute or current path
119 res
=UeberBackend::loadmodule(module
);
121 res
=UeberBackend::loadmodule(arg()["module-dir"]+"/"+module
);
124 L
<<Logger::Error
<<"DNSBackend unable to load module in "<<module
<<endl
;
129 void BackendMakerClass::launch(const string
&instr
)
132 // throw ArgException("Not launching any backends - nameserver won't function");
134 vector
<string
> parts
;
135 stringtok(parts
,instr
,", ");
137 for (const auto part
: parts
)
138 if (count(parts
.begin(), parts
.end(), part
) > 1)
139 throw ArgException("Refusing to launch multiple backends with the same name '" + part
+ "', verify all 'launch' statements in your configuration");
141 for(vector
<string
>::const_iterator i
=parts
.begin();i
!=parts
.end();++i
) {
142 const string
&part
=*i
;
145 vector
<string
>pparts
;
146 stringtok(pparts
,part
,": ");
151 if(d_repository
.find(module
)==d_repository
.end()) {
152 // this is *so* userfriendly
154 if(d_repository
.find(module
)==d_repository
.end())
155 throw ArgException("Trying to launch unknown backend '"+module
+"'");
157 d_repository
[module
]->declareArguments(name
);
158 d_instances
.push_back(make_pair(module
,name
));
162 int BackendMakerClass::numLauncheable()
164 return d_instances
.size();
167 vector
<DNSBackend
*>BackendMakerClass::all(bool metadataOnly
)
169 vector
<DNSBackend
*>ret
;
170 if(d_instances
.empty())
171 throw PDNSException("No database backends configured for launch, unable to function");
174 for(vector
<pair
<string
,string
> >::const_iterator i
=d_instances
.begin();i
!=d_instances
.end();++i
) {
177 made
= d_repository
[i
->first
]->makeMetadataOnly(i
->second
);
179 made
= d_repository
[i
->first
]->make(i
->second
);
181 throw PDNSException("Unable to launch backend '"+i
->first
+"'");
186 catch(PDNSException
&ae
) {
187 L
<<Logger::Error
<<"Caught an exception instantiating a backend: "<<ae
.reason
<<endl
;
188 L
<<Logger::Error
<<"Cleaning up"<<endl
;
189 for(vector
<DNSBackend
*>::const_iterator i
=ret
.begin();i
!=ret
.end();++i
)
194 L
<<Logger::Error
<<"Caught an exception instantiating a backend, cleaning up"<<endl
;
195 for(vector
<DNSBackend
*>::const_iterator i
=ret
.begin();i
!=ret
.end();++i
)
203 /** getSOA() is a function that is called to get the SOA of a domain. Callers should ONLY
204 use getSOA() and not perform a lookup() themselves as backends may decide to special case
207 Returns false if there is definitely no SOA for the domain. May throw a DBException
208 to indicate that the backend is currently unable to supply an answer.
210 WARNING: This function *may* fill out the db attribute of the SOAData, but then again,
211 it may not! If you find a zero in there, you may have been handed a non-live and cached
212 answer, in which case you need to perform a getDomainInfo call!
214 \param domain Domain we want to get the SOA details of
215 \param sd SOAData which is filled with the SOA details
216 \param unmodifiedSerial bool if set, serial will be returned as stored in the backend (maybe 0)
218 bool DNSBackend::getSOA(const DNSName
&domain
, SOAData
&sd
, bool unmodifiedSerial
)
220 this->lookup(QType(QType::SOA
),domain
);
222 DNSResourceRecord rr
;
227 while(this->get(rr
)) {
228 if (rr
.qtype
!= QType::SOA
) throw PDNSException("Got non-SOA record when asking for SOA");
230 fillSOAData(rr
.content
, sd
);
231 sd
.domain_id
=rr
.domain_id
;
233 sd
.scopeMask
= rr
.scopeMask
;
239 if(!sd
.nameserver
.countLabels())
240 sd
.nameserver
= DNSName(arg()["default-soa-name"]);
242 if(!sd
.hostmaster
.countLabels()) {
243 if (!arg().isEmpty("default-soa-mail")) {
244 sd
.hostmaster
= DNSName(arg()["default-soa-mail"]);
245 // attodot(sd.hostmaster); FIXME400
248 sd
.hostmaster
=DNSName("hostmaster")+domain
;
251 if(!unmodifiedSerial
&& !sd
.serial
) { // magic time!
252 DLOG(L
<<Logger::Warning
<<"Doing SOA serial number autocalculation for "<<rr
.qname
<<endl
);
255 if (calculateSOASerial(domain
, sd
, serial
)) {
257 //DLOG(L<<"autocalculated soa serialnumber for "<<rr.qname<<" is "<<newest<<endl);
259 DLOG(L
<<"soa serialnumber calculation failed for "<<rr
.qname
<<endl
);
267 bool DNSBackend::get(DNSZoneRecord
& dzr
)
269 // cout<<"DNSBackend::get(DNSZoneRecord&) called - translating into DNSResourceRecord query"<<endl;
270 DNSResourceRecord rr
;
274 dzr
.domain_id
= rr
.domain_id
;
275 dzr
.scopeMask
= rr
.scopeMask
;
276 if(rr
.qtype
.getCode() == QType::TXT
&& !rr
.content
.empty() && rr
.content
[0]!='"')
277 rr
.content
= "\""+ rr
.content
+ "\"";
278 if(rr
.qtype
.getCode() == QType::SOA
) {
280 dzr
.dr
= DNSRecord(rr
);
282 vector
<string
> parts
;
283 stringtok(parts
, rr
.content
, " \t");
285 rr
.content
= arg()["default-soa-name"];
287 rr
.content
+= " " +arg()["default-soa-mail"];
291 rr
.content
+= " " + ::arg()["soa-refresh-default"];
293 rr
.content
+= " " + ::arg()["soa-retry-default"];
295 rr
.content
+= " " + ::arg()["soa-expire-default"];
297 rr
.content
+= " " + ::arg()["soa-minimum-ttl"];
298 dzr
.dr
= DNSRecord(rr
);
303 dzr
.dr
= DNSRecord(rr
);
306 while(this->get(rr
));
313 bool DNSBackend::getBeforeAndAfterNames(uint32_t id
, const DNSName
& zonename
, const DNSName
& qname
, DNSName
& before
, DNSName
& after
)
316 bool ret
= this->getBeforeAndAfterNamesAbsolute(id
, qname
.makeRelative(zonename
).makeLowerCase(), unhashed
, before
, after
);
317 DNSName lczonename
= zonename
.makeLowerCase();
318 before
+= lczonename
;
324 * Calculates a SOA serial for the zone and stores it in the third
325 * argument. Returns false if calculation is not possible for some
326 * reason (in this case, the third argument is not inspected). If it
327 * returns true, the value returned in the third argument will be set
330 * \param domain The name of the domain
331 * \param sd Information about the SOA record already available
332 * \param serial Output parameter. Only inspected when we return true
334 bool DNSBackend::calculateSOASerial(const DNSName
& domain
, const SOAData
& sd
, time_t& serial
)
336 // we do this by listing the domain and taking the maximum last modified timestamp
341 if(!(this->list(domain
, sd
.domain_id
))) {
342 DLOG(L
<<Logger::Warning
<<"Backend error trying to determine magic serial number of zone '"<<domain
<<"'"<<endl
);
346 while(this->get(i
)) {
347 if(i
.last_modified
>newest
)
348 newest
=i
.last_modified
;
355 void fillSOAData(const DNSZoneRecord
& in
, SOAData
& sd
)
357 sd
.domain_id
= in
.domain_id
;
358 sd
.ttl
= in
.dr
.d_ttl
;
360 auto src
=getRR
<SOARecordContent
>(in
.dr
);
361 sd
.nameserver
= src
->d_mname
;
362 sd
.hostmaster
= src
->d_rname
;
363 sd
.serial
= src
->d_st
.serial
;
364 sd
.refresh
= src
->d_st
.refresh
;
365 sd
.retry
= src
->d_st
.retry
;
366 sd
.expire
= src
->d_st
.expire
;
367 sd
.default_ttl
= src
->d_st
.minimum
;
370 std::shared_ptr
<DNSRecordContent
> makeSOAContent(const SOAData
& sd
)
373 st
.serial
= sd
.serial
;
374 st
.refresh
= sd
.refresh
;
376 st
.expire
= sd
.expire
;
377 st
.minimum
= sd
.default_ttl
;
378 return std::make_shared
<SOARecordContent
>(sd
.nameserver
, sd
.hostmaster
, st
);
382 void fillSOAData(const string
&content
, SOAData
&data
)
384 // content consists of fields separated by spaces:
385 // nameservername hostmaster serial-number [refresh [retry [expire [ minimum] ] ] ]
387 // fill out data with some plausible defaults:
388 // 10800 3600 604800 3600
390 stringtok(parts
,content
);
391 int pleft
=parts
.size();
393 // cout<<"'"<<content<<"'"<<endl;
396 data
.nameserver
=DNSName(parts
[0]);
399 data
.hostmaster
=DNSName(attodot(parts
[1])); // ahu@ds9a.nl -> ahu.ds9a.nl, piet.puk@ds9a.nl -> piet\.puk.ds9a.nl
402 data
.serial
= pleft
> 2 ? pdns_stou(parts
[2]) : 0;
404 data
.refresh
= pleft
> 3 ? pdns_stou(parts
[3])
405 : ::arg().asNum("soa-refresh-default");
407 data
.retry
= pleft
> 4 ? pdns_stou(parts
[4].c_str())
408 : ::arg().asNum("soa-retry-default");
410 data
.expire
= pleft
> 5 ? pdns_stou(parts
[5].c_str())
411 : ::arg().asNum("soa-expire-default");
413 data
.default_ttl
= pleft
> 6 ? pdns_stou(parts
[6].c_str())
414 : ::arg().asNum("soa-minimum-ttl");
416 catch(const std::out_of_range
& oor
) {
417 throw PDNSException("Out of range exception parsing "+content
);