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.
27 #include "dnsbackend.hh"
28 #include "arguments.hh"
29 #include "ueberbackend.hh"
32 #include <sys/types.h>
33 #include "packetcache.hh"
34 #include "auth-zonecache.hh"
35 #include "dnspacket.hh"
41 // this has to be somewhere central, and not in a file that requires Lua
42 // this is so the geoipbackend can set this pointer if loaded for lua-record.cc
43 std::function
<std::string(const std::string
&, int)> g_getGeo
;
45 bool DNSBackend::getAuth(const DNSName
& target
, SOAData
* soaData
)
47 return this->getSOA(target
, *soaData
);
50 void DNSBackend::setArgPrefix(const string
& prefix
)
55 bool DNSBackend::mustDo(const string
& key
)
57 return arg().mustDo(d_prefix
+ "-" + key
);
60 const string
& DNSBackend::getArg(const string
& key
)
62 return arg()[d_prefix
+ "-" + key
];
65 int DNSBackend::getArgAsNum(const string
& key
)
67 return arg().asNum(d_prefix
+ "-" + key
);
70 void BackendFactory::declare(const string
& suffix
, const string
& param
, const string
& explanation
, const string
& value
)
72 string fullname
= d_name
+ suffix
+ "-" + param
;
73 arg().set(fullname
, explanation
) = value
;
74 arg().setDefault(fullname
, value
);
77 const string
& BackendFactory::getName() const
82 BackendMakerClass
& BackendMakers()
84 static BackendMakerClass bmc
;
88 void BackendMakerClass::report(BackendFactory
* backendFactory
)
90 d_repository
[backendFactory
->getName()] = backendFactory
;
93 void BackendMakerClass::clear()
96 for (auto& repo
: d_repository
) {
98 repo
.second
= nullptr;
100 d_repository
.clear();
103 vector
<string
> BackendMakerClass::getModules()
107 // copy(d_repository.begin(), d_repository.end(),back_inserter(ret));
108 for (auto& repo
: d_repository
) {
109 ret
.push_back(repo
.first
);
114 void BackendMakerClass::load_all()
116 auto directoryError
= pdns::visit_directory(arg()["module-dir"], []([[maybe_unused
]] ino_t inodeNumber
, const std::string_view
& name
) {
117 if (boost::starts_with(name
, "lib") && name
.size() > 13 && boost::ends_with(name
, "backend.so")) {
118 load(std::string(name
));
122 if (directoryError
) {
123 g_log
<< Logger::Error
<< "Unable to open module directory '" << arg()["module-dir"] << "': " << *directoryError
<< endl
;
127 void BackendMakerClass::load(const string
& module
)
131 if (module
.find('.') == string::npos
) {
132 res
= UeberBackend::loadmodule(arg()["module-dir"] + "/lib" + module
+ "backend.so");
134 else if (module
[0] == '/' || (module
[0] == '.' && module
[1] == '/') || (module
[0] == '.' && module
[1] == '.')) { // absolute or current path
135 res
= UeberBackend::loadmodule(module
);
138 res
= UeberBackend::loadmodule(arg()["module-dir"] + "/" + module
);
142 g_log
<< Logger::Error
<< "DNSBackend unable to load module in " << module
<< endl
;
147 void BackendMakerClass::launch(const string
& instr
)
150 // throw ArgException("Not launching any backends - nameserver won't function");
152 vector
<string
> parts
;
153 stringtok(parts
, instr
, ", ");
155 for (const auto& part
: parts
) {
156 if (count(parts
.begin(), parts
.end(), part
) > 1) {
157 throw ArgException("Refusing to launch multiple backends with the same name '" + part
+ "', verify all 'launch' statements in your configuration");
161 for (const auto& part
: parts
) {
164 vector
<string
> pparts
;
165 stringtok(pparts
, part
, ": ");
167 if (pparts
.size() > 1) {
168 name
= "-" + pparts
[1];
171 if (d_repository
.find(module
) == d_repository
.end()) {
172 // this is *so* userfriendly
174 if (d_repository
.find(module
) == d_repository
.end()) {
175 throw ArgException("Trying to launch unknown backend '" + module
+ "'");
178 d_repository
[module
]->declareArguments(name
);
179 d_instances
.emplace_back(module
, name
);
183 size_t BackendMakerClass::numLauncheable() const
185 return d_instances
.size();
188 vector
<std::unique_ptr
<DNSBackend
>> BackendMakerClass::all(bool metadataOnly
)
190 if (d_instances
.empty()) {
191 throw PDNSException("No database backends configured for launch, unable to function");
194 vector
<unique_ptr
<DNSBackend
>> ret
;
195 ret
.reserve(d_instances
.size());
197 std::string current
; // to make the exception text more useful
200 for (const auto& instance
: d_instances
) {
201 current
= instance
.first
+ instance
.second
;
202 auto* repo
= d_repository
[instance
.first
];
203 std::unique_ptr
<DNSBackend
> made
{metadataOnly
? repo
->makeMetadataOnly(instance
.second
) : repo
->make(instance
.second
)};
204 if (made
== nullptr) {
205 throw PDNSException("Unable to launch backend '" + instance
.first
+ "'");
207 ret
.push_back(std::move(made
));
210 catch (const PDNSException
& ae
) {
211 g_log
<< Logger::Error
<< "Caught an exception instantiating a backend (" << current
<< "): " << ae
.reason
<< endl
;
212 g_log
<< Logger::Error
<< "Cleaning up" << endl
;
218 g_log
<< Logger::Error
<< "Caught an exception instantiating a backend (" << current
<< "), cleaning up" << endl
;
226 /** getSOA() is a function that is called to get the SOA of a domain. Callers should ONLY
227 use getSOA() and not perform a lookup() themselves as backends may decide to special case
230 Returns false if there is definitely no SOA for the domain. May throw a DBException
231 to indicate that the backend is currently unable to supply an answer.
233 WARNING: This function *may* fill out the db attribute of the SOAData, but then again,
234 it may not! If you find a zero in there, you may have been handed a non-live and cached
235 answer, in which case you need to perform a getDomainInfo call!
237 \param domain Domain we want to get the SOA details of
238 \param sd SOAData which is filled with the SOA details
239 \param unmodifiedSerial bool if set, serial will be returned as stored in the backend (maybe 0)
241 bool DNSBackend::getSOA(const DNSName
& domain
, SOAData
& soaData
)
243 this->lookup(QType(QType::SOA
), domain
, -1);
244 S
.inc("backend-queries");
246 DNSResourceRecord resourceRecord
;
249 soaData
.db
= nullptr;
252 while (this->get(resourceRecord
)) {
253 if (resourceRecord
.qtype
!= QType::SOA
) {
254 throw PDNSException("Got non-SOA record when asking for SOA, zone: '" + domain
.toLogString() + "'");
257 soaData
.qname
= domain
;
258 soaData
.ttl
= resourceRecord
.ttl
;
260 soaData
.domain_id
= resourceRecord
.domain_id
;
261 fillSOAData(resourceRecord
.content
, soaData
);
265 while (this->get(resourceRecord
)) {
274 bool DNSBackend::get(DNSZoneRecord
& zoneRecord
)
276 // cout<<"DNSBackend::get(DNSZoneRecord&) called - translating into DNSResourceRecord query"<<endl;
277 DNSResourceRecord resourceRecord
;
278 if (!this->get(resourceRecord
)) {
281 zoneRecord
.auth
= resourceRecord
.auth
;
282 zoneRecord
.domain_id
= resourceRecord
.domain_id
;
283 zoneRecord
.scopeMask
= resourceRecord
.scopeMask
;
284 if (resourceRecord
.qtype
.getCode() == QType::TXT
&& !resourceRecord
.content
.empty() && resourceRecord
.content
[0] != '"') {
285 resourceRecord
.content
= "\"" + resourceRecord
.content
+ "\"";
288 zoneRecord
.dr
= DNSRecord(resourceRecord
);
291 while (this->get(resourceRecord
)) {
299 bool DNSBackend::getBeforeAndAfterNames(uint32_t id
, const DNSName
& zonename
, const DNSName
& qname
, DNSName
& before
, DNSName
& after
)
302 bool ret
= this->getBeforeAndAfterNamesAbsolute(id
, qname
.makeRelative(zonename
).makeLowerCase(), unhashed
, before
, after
);
303 DNSName lczonename
= zonename
.makeLowerCase();
304 before
+= lczonename
;
309 void DNSBackend::getAllDomains(vector
<DomainInfo
>* /* domains */, bool /* getSerial */, bool /* include_disabled */)
311 if (g_zoneCache
.isEnabled()) {
312 g_log
<< Logger::Error
<< "One of the backends does not support zone caching. Put zone-cache-refresh-interval=0 in the config file to disable this cache." << endl
;
317 void fillSOAData(const DNSZoneRecord
& inZoneRecord
, SOAData
& soaData
)
319 soaData
.domain_id
= inZoneRecord
.domain_id
;
320 soaData
.ttl
= inZoneRecord
.dr
.d_ttl
;
322 auto src
= getRR
<SOARecordContent
>(inZoneRecord
.dr
);
323 soaData
.nameserver
= src
->d_mname
;
324 soaData
.rname
= src
->d_rname
;
325 soaData
.serial
= src
->d_st
.serial
;
326 soaData
.refresh
= src
->d_st
.refresh
;
327 soaData
.retry
= src
->d_st
.retry
;
328 soaData
.expire
= src
->d_st
.expire
;
329 soaData
.minimum
= src
->d_st
.minimum
;
332 std::shared_ptr
<DNSRecordContent
> makeSOAContent(const SOAData
& soaData
)
334 struct soatimes soaTimes
336 .serial
= soaData
.serial
,
337 .refresh
= soaData
.refresh
,
338 .retry
= soaData
.retry
,
339 .expire
= soaData
.expire
,
340 .minimum
= soaData
.minimum
,
342 return std::make_shared
<SOARecordContent
>(soaData
.nameserver
, soaData
.rname
, soaTimes
);
345 void fillSOAData(const string
& content
, SOAData
& soaData
)
347 vector
<string
> parts
;
349 stringtok(parts
, content
);
352 soaData
.nameserver
= DNSName(parts
.at(0));
353 soaData
.rname
= DNSName(parts
.at(1));
354 pdns::checked_stoi_into(soaData
.serial
, parts
.at(2));
355 pdns::checked_stoi_into(soaData
.refresh
, parts
.at(3));
356 pdns::checked_stoi_into(soaData
.retry
, parts
.at(4));
357 pdns::checked_stoi_into(soaData
.expire
, parts
.at(5));
358 pdns::checked_stoi_into(soaData
.minimum
, parts
.at(6));
360 catch (const std::out_of_range
& oor
) {
361 throw PDNSException("Out of range exception parsing '" + content
+ "'");