]>
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
;
69 arg().setDefault(fullname
,value
);
72 const string
&BackendFactory::getName() const
77 BackendMakerClass
&BackendMakers()
79 static BackendMakerClass bmc
;
83 void BackendMakerClass::report(BackendFactory
*bf
)
85 d_repository
[bf
->getName()]=bf
;
89 vector
<string
> BackendMakerClass::getModules()
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
);
99 void BackendMakerClass::load_all()
101 // TODO: Implement this?
102 DIR *dir
=opendir(arg()["module-dir"].c_str());
104 g_log
<<Logger::Error
<<"Unable to open module directory '"<<arg()["module-dir"]<<"'"<<endl
;
107 struct dirent
*entry
;
108 while((entry
=readdir(dir
))) {
109 if(!strncmp(entry
->d_name
,"lib",3) &&
110 strlen(entry
->d_name
)>13 &&
111 !strcmp(entry
->d_name
+strlen(entry
->d_name
)-10,"backend.so"))
117 void BackendMakerClass::load(const string
&module
)
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
);
126 res
=UeberBackend::loadmodule(arg()["module-dir"]+"/"+module
);
129 g_log
<<Logger::Error
<<"DNSBackend unable to load module in "<<module
<<endl
;
134 void BackendMakerClass::launch(const string
&instr
)
137 // throw ArgException("Not launching any backends - nameserver won't function");
139 vector
<string
> parts
;
140 stringtok(parts
,instr
,", ");
142 for (const auto part
: parts
)
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");
146 for(vector
<string
>::const_iterator i
=parts
.begin();i
!=parts
.end();++i
) {
147 const string
&part
=*i
;
150 vector
<string
>pparts
;
151 stringtok(pparts
,part
,": ");
156 if(d_repository
.find(module
)==d_repository
.end()) {
157 // this is *so* userfriendly
159 if(d_repository
.find(module
)==d_repository
.end())
160 throw ArgException("Trying to launch unknown backend '"+module
+"'");
162 d_repository
[module
]->declareArguments(name
);
163 d_instances
.push_back(make_pair(module
,name
));
167 int BackendMakerClass::numLauncheable()
169 return d_instances
.size();
172 vector
<DNSBackend
*>BackendMakerClass::all(bool metadataOnly
)
174 vector
<DNSBackend
*>ret
;
175 if(d_instances
.empty())
176 throw PDNSException("No database backends configured for launch, unable to function");
179 for(vector
<pair
<string
,string
> >::const_iterator i
=d_instances
.begin();i
!=d_instances
.end();++i
) {
182 made
= d_repository
[i
->first
]->makeMetadataOnly(i
->second
);
184 made
= d_repository
[i
->first
]->make(i
->second
);
186 throw PDNSException("Unable to launch backend '"+i
->first
+"'");
191 catch(PDNSException
&ae
) {
192 g_log
<<Logger::Error
<<"Caught an exception instantiating a backend: "<<ae
.reason
<<endl
;
193 g_log
<<Logger::Error
<<"Cleaning up"<<endl
;
194 for(vector
<DNSBackend
*>::const_iterator i
=ret
.begin();i
!=ret
.end();++i
)
199 g_log
<<Logger::Error
<<"Caught an exception instantiating a backend, cleaning up"<<endl
;
200 for(vector
<DNSBackend
*>::const_iterator i
=ret
.begin();i
!=ret
.end();++i
)
208 /** getSOA() is a function that is called to get the SOA of a domain. Callers should ONLY
209 use getSOA() and not perform a lookup() themselves as backends may decide to special case
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.
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!
219 \param domain Domain we want to get the SOA details of
220 \param sd SOAData which is filled with the SOA details
221 \param unmodifiedSerial bool if set, serial will be returned as stored in the backend (maybe 0)
223 bool DNSBackend::getSOA(const DNSName
&domain
, SOAData
&sd
)
225 this->lookup(QType(QType::SOA
),domain
,-1);
227 DNSResourceRecord rr
;
232 while(this->get(rr
)) {
233 if (rr
.qtype
!= QType::SOA
) throw PDNSException("Got non-SOA record when asking for SOA");
235 fillSOAData(rr
.content
, sd
);
236 sd
.domain_id
=rr
.domain_id
;
243 if(!sd
.nameserver
.countLabels())
244 sd
.nameserver
= DNSName(arg()["default-soa-name"]);
246 if(!sd
.hostmaster
.countLabels()) {
247 if (!arg().isEmpty("default-soa-mail")) {
248 sd
.hostmaster
= DNSName(arg()["default-soa-mail"]);
249 // attodot(sd.hostmaster); FIXME400
252 sd
.hostmaster
=DNSName("hostmaster")+domain
;
259 bool DNSBackend::get(DNSZoneRecord
& dzr
)
261 // cout<<"DNSBackend::get(DNSZoneRecord&) called - translating into DNSResourceRecord query"<<endl;
262 DNSResourceRecord rr
;
266 dzr
.domain_id
= rr
.domain_id
;
267 dzr
.scopeMask
= rr
.scopeMask
;
268 if(rr
.qtype
.getCode() == QType::TXT
&& !rr
.content
.empty() && rr
.content
[0]!='"')
269 rr
.content
= "\""+ rr
.content
+ "\"";
270 if(rr
.qtype
.getCode() == QType::SOA
) {
272 dzr
.dr
= DNSRecord(rr
);
274 vector
<string
> parts
;
275 stringtok(parts
, rr
.content
, " \t");
277 rr
.content
= arg()["default-soa-name"];
279 rr
.content
+= " " +arg()["default-soa-mail"];
283 rr
.content
+= " " + ::arg()["soa-refresh-default"];
285 rr
.content
+= " " + ::arg()["soa-retry-default"];
287 rr
.content
+= " " + ::arg()["soa-expire-default"];
289 rr
.content
+= " " + ::arg()["soa-minimum-ttl"];
290 dzr
.dr
= DNSRecord(rr
);
295 dzr
.dr
= DNSRecord(rr
);
298 while(this->get(rr
));
305 bool DNSBackend::getBeforeAndAfterNames(uint32_t id
, const DNSName
& zonename
, const DNSName
& qname
, DNSName
& before
, DNSName
& after
)
308 bool ret
= this->getBeforeAndAfterNamesAbsolute(id
, qname
.makeRelative(zonename
).makeLowerCase(), unhashed
, before
, after
);
309 DNSName lczonename
= zonename
.makeLowerCase();
310 before
+= lczonename
;
315 void fillSOAData(const DNSZoneRecord
& in
, SOAData
& sd
)
317 sd
.domain_id
= in
.domain_id
;
318 sd
.ttl
= in
.dr
.d_ttl
;
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
;
327 sd
.default_ttl
= src
->d_st
.minimum
;
330 std::shared_ptr
<DNSRecordContent
> makeSOAContent(const SOAData
& sd
)
333 st
.serial
= sd
.serial
;
334 st
.refresh
= sd
.refresh
;
336 st
.expire
= sd
.expire
;
337 st
.minimum
= sd
.default_ttl
;
338 return std::make_shared
<SOARecordContent
>(sd
.nameserver
, sd
.hostmaster
, st
);
342 void fillSOAData(const string
&content
, SOAData
&data
)
344 // content consists of fields separated by spaces:
345 // nameservername hostmaster serial-number [refresh [retry [expire [ minimum] ] ] ]
347 // fill out data with some plausible defaults:
348 // 10800 3600 604800 3600
350 stringtok(parts
,content
);
351 int pleft
=parts
.size();
353 // cout<<"'"<<content<<"'"<<endl;
356 data
.nameserver
=DNSName(parts
[0]);
359 data
.hostmaster
=DNSName(attodot(parts
[1])); // ahu@ds9a.nl -> ahu.ds9a.nl, piet.puk@ds9a.nl -> piet\.puk.ds9a.nl
362 data
.serial
= pleft
> 2 ? pdns_stou(parts
[2]) : 0;
364 data
.refresh
= pleft
> 3 ? pdns_stou(parts
[3])
365 : ::arg().asNum("soa-refresh-default");
367 data
.retry
= pleft
> 4 ? pdns_stou(parts
[4].c_str())
368 : ::arg().asNum("soa-retry-default");
370 data
.expire
= pleft
> 5 ? pdns_stou(parts
[5].c_str())
371 : ::arg().asNum("soa-expire-default");
373 data
.default_ttl
= pleft
> 6 ? pdns_stou(parts
[6].c_str())
374 : ::arg().asNum("soa-minimum-ttl");
376 catch(const std::out_of_range
& oor
) {
377 throw PDNSException("Out of range exception parsing "+content
);