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.
25 #include "tinydnsbackend.hh"
26 #include "pdns/misc.hh"
27 #include "pdns/dnsrecords.hh"
30 static string backendname
= "[TinyDNSBackend] ";
31 uint32_t TinyDNSBackend::s_lastId
;
32 LockGuarded
<TinyDNSBackend::TDI_suffix_t
> TinyDNSBackend::s_domainInfo
;
34 vector
<string
> TinyDNSBackend::getLocations()
42 //TODO: We do not have IPv6 support.
43 Netmask remote
= d_dnspacket
->getRealRemote();
44 if (remote
.getBits() != 32) {
48 unsigned long addr
= remote
.getNetwork().sin4
.sin_addr
.s_addr
;
54 key
[3] = (addr
>> 8) & 0xff;
55 key
[4] = (addr
>> 16) & 0xff;
56 key
[5] = (addr
>> 24) & 0xff;
58 for (int i
= 4; i
>= 0; i
--) {
59 string
searchkey(key
, i
+ 2);
61 auto reader
= std::make_unique
<CDB
>(getArg("dbfile"));
62 ret
= reader
->findall(searchkey
);
64 catch (const std::exception
& e
) {
65 g_log
<< Logger::Error
<< e
.what() << endl
;
66 throw PDNSException(e
.what());
69 //Biggest item wins, so when we find something, we can jump out.
78 TinyDNSBackend::TinyDNSBackend(const string
& suffix
)
80 setArgPrefix("tinydns" + suffix
);
82 d_locations
= mustDo("locations");
83 d_ignorebogus
= mustDo("ignore-bogus-records");
84 d_taiepoch
= 4611686018427387904ULL + getArgAsNum("tai-adjust");
88 d_isWildcardQuery
= false;
91 void TinyDNSBackend::getUpdatedMasters(vector
<DomainInfo
>* retDomains
)
93 auto domainInfo
= s_domainInfo
.lock(); //TODO: We could actually lock less if we do it per suffix.
94 if (!domainInfo
->count(d_suffix
)) {
96 domainInfo
->emplace(d_suffix
, tmp
);
99 TDI_t
* domains
= &(*domainInfo
)[d_suffix
];
101 vector
<DomainInfo
> allDomains
;
102 getAllDomains(&allDomains
, true, false);
103 if (domains
->size() == 0 && !mustDo("notify-on-startup")) {
104 for (vector
<DomainInfo
>::iterator di
= allDomains
.begin(); di
!= allDomains
.end(); ++di
) {
105 di
->notified_serial
= 0;
109 for (vector
<DomainInfo
>::iterator di
= allDomains
.begin(); di
!= allDomains
.end(); ++di
) {
110 TDIByZone_t
& zone_index
= domains
->get
<tag_zone
>();
111 TDIByZone_t::iterator itByZone
= zone_index
.find(di
->zone
);
112 if (itByZone
== zone_index
.end()) {
118 tmp
.notified_serial
= di
->serial
;
119 domains
->insert(tmp
);
122 if (di
->notified_serial
> 0) {
123 retDomains
->push_back(*di
);
127 if (itByZone
->notified_serial
< di
->serial
) {
128 di
->id
= itByZone
->id
;
129 retDomains
->push_back(*di
);
135 void TinyDNSBackend::setNotified(uint32_t id
, uint32_t serial
)
137 auto domainInfo
= s_domainInfo
.lock();
138 if (!domainInfo
->count(d_suffix
)) {
139 throw PDNSException("Can't get list of domains to set the serial.");
141 TDI_t
* domains
= &(*domainInfo
)[d_suffix
];
142 TDIById_t
& domain_index
= domains
->get
<tag_domainid
>();
143 TDIById_t::iterator itById
= domain_index
.find(id
);
144 if (itById
== domain_index
.end()) {
145 g_log
<< Logger::Error
<< backendname
<< "Received updated serial(" << serial
<< "), but domain ID (" << id
<< ") is not known in this backend." << endl
;
148 DLOG(g_log
<< Logger::Debug
<< backendname
<< "Setting serial for " << itById
->zone
<< " to " << serial
<< endl
);
149 domain_index
.modify(itById
, TDI_SerialModifier(serial
));
151 (*domainInfo
)[d_suffix
] = *domains
;
154 void TinyDNSBackend::getAllDomains(vector
<DomainInfo
>* domains
, bool getSerial
, bool include_disabled
)
157 d_isGetDomains
= true;
161 d_cdbReader
= std::make_unique
<CDB
>(getArg("dbfile"));
163 catch (const std::exception
& e
) {
164 g_log
<< Logger::Error
<< e
.what() << endl
;
165 throw PDNSException(e
.what());
168 d_cdbReader
->searchAll();
169 DNSResourceRecord rr
;
170 std::unordered_set
<DNSName
> dupcheck
;
173 if (rr
.qtype
.getCode() == QType::SOA
&& dupcheck
.insert(rr
.qname
).second
) {
175 di
.id
= -1; //TODO: Check if this is ok.
178 di
.kind
= DomainInfo::Master
;
179 di
.last_check
= time(0);
184 fillSOAData(rr
.content
, sd
);
185 di
.serial
= sd
.serial
;
192 di
.notified_serial
= di
.serial
;
193 domains
->push_back(di
);
198 bool TinyDNSBackend::list(const DNSName
& target
, int domain_id
, bool include_disabled
)
201 d_isGetDomains
= false;
202 string key
= target
.toDNSStringLC();
204 d_cdbReader
= std::make_unique
<CDB
>(getArg("dbfile"));
206 catch (const std::exception
& e
) {
207 g_log
<< Logger::Error
<< e
.what() << endl
;
208 throw PDNSException(e
.what());
211 return d_cdbReader
->searchSuffix(key
);
214 void TinyDNSBackend::lookup(const QType
& qtype
, const DNSName
& qdomain
, int zoneId
, DNSPacket
* pkt_p
)
217 d_isGetDomains
= false;
218 string queryDomain
= toLowerCanonic(qdomain
.toString());
220 string key
= simpleCompress(queryDomain
);
222 DLOG(g_log
<< Logger::Debug
<< backendname
<< "[lookup] query for qtype [" << qtype
.toString() << "] qdomain [" << qdomain
<< "]" << endl
);
223 DLOG(g_log
<< Logger::Debug
<< "[lookup] key [" << makeHexDump(key
) << "]" << endl
);
225 d_isWildcardQuery
= false;
226 if (key
[0] == '\001' && key
[1] == '\052') {
227 d_isWildcardQuery
= true;
234 d_cdbReader
= std::make_unique
<CDB
>(getArg("dbfile"));
236 catch (const std::exception
& e
) {
237 g_log
<< Logger::Error
<< e
.what() << endl
;
238 throw PDNSException(e
.what());
241 d_cdbReader
->searchKey(key
);
245 bool TinyDNSBackend::get(DNSResourceRecord
& rr
)
247 pair
<string
, string
> record
;
249 while (d_cdbReader
->readNext(record
)) {
250 string val
= record
.second
;
251 string key
= record
.first
;
253 //DLOG(g_log<<Logger::Debug<<"[GET] Key: "<<makeHexDump(key)<<endl);
254 //DLOG(g_log<<Logger::Debug<<"[GET] Val: "<<makeHexDump(val)<<endl);
255 if (key
[0] == '\000' && key
[1] == '\045') { // skip locations
260 // If we have a wildcard query, but the record we got is not a wildcard, we skip.
261 if (d_isWildcardQuery
&& val
[2] != '\052' && val
[2] != '\053') {
265 // If it is NOT a wildcard query, but we do find a wildcard record, we skip it.
266 if (!d_isWildcardQuery
&& (val
[2] == '\052' || val
[2] == '\053')) {
271 PacketReader
pr(val
, 0);
272 rr
.qtype
= QType(pr
.get16BitInt());
274 if (d_isGetDomains
&& rr
.qtype
!= QType::SOA
) {
278 if (d_isAxfr
|| d_qtype
.getCode() == QType::ANY
|| rr
.qtype
== d_qtype
) {
279 char locwild
= pr
.get8BitInt();
280 if (locwild
!= '\075' && (locwild
== '\076' || locwild
== '\053')) {
281 if (d_isAxfr
&& d_locations
) { // We skip records with a location in AXFR, unless we disable locations.
285 recloc
[0] = pr
.get8BitInt();
286 recloc
[1] = pr
.get8BitInt();
289 bool foundLocation
= false;
290 vector
<string
> locations
= getLocations();
291 while (locations
.size() > 0) {
292 string locId
= locations
.back();
293 locations
.pop_back();
295 if (recloc
[0] == locId
[0] && recloc
[1] == locId
[1]) {
296 foundLocation
= true;
300 if (!foundLocation
) {
306 if (d_isAxfr
&& (val
[2] == '\052' || val
[2] == '\053')) { // Keys are not stored with wildcard character, with AXFR we need to add that.
307 key
.insert(0, 1, '\052');
308 key
.insert(0, 1, '\001');
311 rr
.qname
= DNSName(key
.c_str(), key
.size(), 0, false);
313 // 11:13.21 <@ahu> IT IS ALWAYS AUTH --- well not really because we are just a backend :-)
314 // We could actually do NSEC3-NARROW DNSSEC according to Habbie, if we do, we need to change something here.
317 rr
.ttl
= pr
.get32BitInt();
318 uint64_t timestamp
= pr
.get32BitInt();
320 timestamp
+= pr
.get32BitInt();
322 uint64_t now
= d_taiepoch
+ time(NULL
);
324 if (timestamp
< now
) {
327 rr
.ttl
= timestamp
- now
;
333 else if (now
<= timestamp
) {
340 dr
.d_type
= rr
.qtype
.getCode();
341 dr
.d_clen
= val
.size() - pr
.getPosition();
343 auto drc
= DNSRecordContent::mastermake(dr
, pr
);
344 rr
.content
= drc
->getZoneRepresentation();
345 DLOG(cerr
<< "CONTENT: " << rr
.content
<< endl
);
348 g_log
<< Logger::Error
<< backendname
<< "Failed to parse record content for " << rr
.qname
<< " with type " << rr
.qtype
.toString();
349 if (d_ignorebogus
|| d_isGetDomains
) {
350 g_log
<< ". Ignoring!" << endl
;
354 g_log
<< ". Erroring out!" << endl
;
358 // DLOG(g_log<<Logger::Debug<<backendname<<"Returning ["<<rr.content<<"] for ["<<rr.qname<<"] of RecordType ["<<rr.qtype.toString()<<"]"<<endl;);
362 DLOG(g_log
<< Logger::Debug
<< backendname
<< "No more records to return." << endl
);
364 d_cdbReader
= nullptr;
369 class TinyDNSFactory
: public BackendFactory
373 BackendFactory("tinydns") {}
375 void declareArguments(const string
& suffix
= "") override
377 declare(suffix
, "notify-on-startup", "Tell the TinyDNSBackend to notify all the slave nameservers on startup. Default is no.", "no");
378 declare(suffix
, "dbfile", "Location of the cdb data file", "data.cdb");
379 declare(suffix
, "tai-adjust", "This adjusts the TAI value if timestamps are used. These seconds will be added to the start point (1970) and will allow you to adjust for leap seconds. The default is 11.", "11");
380 declare(suffix
, "locations", "Enable or Disable location support in the backend. Changing the value to 'no' will make the backend ignore the locations. This then returns all records!", "yes");
381 declare(suffix
, "ignore-bogus-records", "The data.cdb file might have some incorrect record data, this causes PowerDNS to fail, where tinydns would send out truncated data. This option makes powerdns ignore that data!", "no");
384 DNSBackend
* make(const string
& suffix
= "") override
386 return new TinyDNSBackend(suffix
);
396 BackendMakers().report(new TinyDNSFactory
);
397 g_log
<< Logger::Info
<< "[tinydnsbackend] This is the tinydns backend version " VERSION
399 << " (" __DATE__
" " __TIME__
")"
401 << " reporting" << endl
;
405 static TinyDNSLoader tinydnsloader
;