]> git.ipfire.org Git - thirdparty/pdns.git/blame - modules/tinydnsbackend/tinydnsbackend.cc
Merge pull request #7908 from omoerbeek/rec-4.1.14-changelog
[thirdparty/pdns.git] / modules / tinydnsbackend / tinydnsbackend.cc
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
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.
8 *
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.
12 *
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.
17 *
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.
21 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
ddee6d08
PD
25#include "tinydnsbackend.hh"
26#include "pdns/lock.hh"
27#include <cdb.h>
21e99384
RK
28#include "pdns/misc.hh"
29#include "pdns/iputils.hh"
30#include "pdns/dnspacket.hh"
31#include "pdns/dnsrecords.hh"
ddee6d08 32#include <utility>
fa8fd4d2 33
ddee6d08 34
ddee6d08
PD
35static string backendname="[TinyDNSBackend] ";
36uint32_t TinyDNSBackend::s_lastId;
37pthread_mutex_t TinyDNSBackend::s_domainInfoLock=PTHREAD_MUTEX_INITIALIZER;
38TinyDNSBackend::TDI_suffix_t TinyDNSBackend::s_domainInfo;
39
ddee6d08
PD
40vector<string> TinyDNSBackend::getLocations()
41{
9153c7cf
AT
42 vector<string> ret;
43
44 if (! d_dnspacket) {
45 return ret;
46 }
47
48 //TODO: We do not have IPv6 support.
49 Netmask remote = d_dnspacket->getRealRemote();
50 if (remote.getBits() != 32) {
51 return ret;
52 }
53
54 unsigned long addr = remote.getNetwork().sin4.sin_addr.s_addr;
55
56 char key[6];
57 key[0] = '\000';
58 key[1] = '\045';
59 key[2] = (addr )&0xff;
60 key[3] = (addr >> 8)&0xff;
61 key[4] = (addr >> 16)&0xff;
62 key[5] = (addr >> 24)&0xff;
63
64 for (int i=4;i>=0;i--) {
65 string searchkey(key, i+2);
66 CDB *reader = new CDB(getArg("dbfile"));
67 ret = reader->findall(searchkey);
68 delete reader;
69
70 //Biggest item wins, so when we find something, we can jump out.
71 if (ret.size() > 0) {
72 break;
73 }
74 }
75
76 return ret;
ddee6d08
PD
77}
78
79TinyDNSBackend::TinyDNSBackend(const string &suffix)
80{
9153c7cf
AT
81 setArgPrefix("tinydns"+suffix);
82 d_suffix = suffix;
83 d_locations = mustDo("locations");
84 d_ignorebogus = mustDo("ignore-bogus-records");
85 d_taiepoch = 4611686018427387904ULL + getArgAsNum("tai-adjust");
86 d_dnspacket = NULL;
87 d_cdbReader = NULL;
88 d_isAxfr = false;
89 d_isWildcardQuery = false;
ddee6d08
PD
90}
91
92void TinyDNSBackend::getUpdatedMasters(vector<DomainInfo>* retDomains) {
9153c7cf
AT
93 Lock l(&s_domainInfoLock); //TODO: We could actually lock less if we do it per suffix.
94
95 if (! s_domainInfo.count(d_suffix)) {
96 TDI_t tmp;
97 s_domainInfo.insert( make_pair(d_suffix,tmp) );
98 }
99
100 TDI_t *domains = &s_domainInfo[d_suffix];
101
102 vector<DomainInfo> allDomains;
103 getAllDomains(&allDomains);
104 if (domains->size() == 0 && !mustDo("notify-on-startup")) {
105 for (vector<DomainInfo>::iterator di=allDomains.begin(); di!=allDomains.end(); ++di) {
106 di->notified_serial = 0;
107 }
108 }
109
110 for(vector<DomainInfo>::iterator di=allDomains.begin(); di!=allDomains.end(); ++di) {
111 TDIByZone_t& zone_index = domains->get<tag_zone>();
112 TDIByZone_t::iterator itByZone = zone_index.find(di->zone);
113 if (itByZone == zone_index.end()) {
114 s_lastId++;
115
116 TinyDomainInfo tmp;
117 tmp.zone = di->zone;
118 tmp.id = s_lastId;
119 tmp.notified_serial = di->serial;
120 domains->insert(tmp);
121
122 di->id = s_lastId;
123 if (di->notified_serial > 0) {
124 retDomains->push_back(*di);
125 }
126 } else {
127 if (itByZone->notified_serial < di->serial) {
128 di->id = itByZone->id;
129 retDomains->push_back(*di);
130 }
131 }
132 }
ddee6d08
PD
133}
134
135void TinyDNSBackend::setNotified(uint32_t id, uint32_t serial) {
9153c7cf
AT
136 Lock l(&s_domainInfoLock);
137 if (!s_domainInfo.count(d_suffix)) {
138 throw new PDNSException("Can't get list of domains to set the serial.");
139 }
140 TDI_t *domains = &s_domainInfo[d_suffix];
141 TDIById_t& domain_index = domains->get<tag_domainid>();
142 TDIById_t::iterator itById = domain_index.find(id);
143 if (itById == domain_index.end()) {
e6a9dde5 144 g_log<<Logger::Error<<backendname<<"Received updated serial("<<serial<<"), but domain ID ("<<id<<") is not known in this backend."<<endl;
9153c7cf 145 } else {
e6a9dde5 146 DLOG(g_log<<Logger::Debug<<backendname<<"Setting serial for "<<itById->zone<<" to "<<serial<<endl);
9153c7cf
AT
147 domain_index.modify(itById, TDI_SerialModifier(serial));
148 }
149 s_domainInfo[d_suffix] = *domains;
ddee6d08
PD
150}
151
cea26350 152void TinyDNSBackend::getAllDomains(vector<DomainInfo> *domains, bool include_disabled) {
9153c7cf
AT
153 d_isAxfr=true;
154 d_dnspacket = NULL;
155
10700675 156 d_cdbReader=std::unique_ptr<CDB>(new CDB(getArg("dbfile")));
9153c7cf
AT
157 d_cdbReader->searchAll();
158 DNSResourceRecord rr;
159
160 while (get(rr)) {
161 if (rr.qtype.getCode() == QType::SOA) {
162 SOAData sd;
163 fillSOAData(rr.content, sd);
164
165 DomainInfo di;
166 di.id = -1; //TODO: Check if this is ok.
167 di.backend=this;
168 di.zone = rr.qname;
169 di.serial = sd.serial;
170 di.notified_serial = sd.serial;
171 di.kind = DomainInfo::Master;
172 di.last_check = time(0);
173 domains->push_back(di);
174 }
175 }
ddee6d08
PD
176}
177
5e1031cd 178bool TinyDNSBackend::list(const DNSName &target, int domain_id, bool include_disabled) {
9153c7cf 179 d_isAxfr=true;
d33190b4 180 string key = target.toDNSStringLC();
10700675 181 d_cdbReader=std::unique_ptr<CDB>(new CDB(getArg("dbfile")));
9153c7cf 182 return d_cdbReader->searchSuffix(key);
ddee6d08
PD
183}
184
5e1031cd 185void TinyDNSBackend::lookup(const QType &qtype, const DNSName &qdomain, DNSPacket *pkt_p, int zoneId) {
9153c7cf
AT
186 d_isAxfr = false;
187 string queryDomain = toLowerCanonic(qdomain.toString());
ddee6d08 188
9153c7cf 189 string key=simpleCompress(queryDomain);
ddee6d08 190
e6a9dde5
PL
191 DLOG(g_log<<Logger::Debug<<backendname<<"[lookup] query for qtype ["<<qtype.getName()<<"] qdomain ["<<qdomain<<"]"<<endl);
192 DLOG(g_log<<Logger::Debug<<"[lookup] key ["<<makeHexDump(key)<<"]"<<endl);
ddee6d08 193
9153c7cf
AT
194 d_isWildcardQuery = false;
195 if (key[0] == '\001' && key[1] == '\052') {
196 d_isWildcardQuery = true;
197 key.erase(0,2);
198 }
ddee6d08 199
9153c7cf 200 d_qtype=qtype;
ddee6d08 201
10700675 202 d_cdbReader=std::unique_ptr<CDB>(new CDB(getArg("dbfile")));
9153c7cf
AT
203 d_cdbReader->searchKey(key);
204 d_dnspacket = pkt_p;
ddee6d08
PD
205}
206
207
208bool TinyDNSBackend::get(DNSResourceRecord &rr)
209{
9153c7cf
AT
210 pair<string, string> record;
211
212 while (d_cdbReader->readNext(record)) {
213 string val = record.second;
214 string key = record.first;
215
e6a9dde5
PL
216 //DLOG(g_log<<Logger::Debug<<"[GET] Key: "<<makeHexDump(key)<<endl);
217 //DLOG(g_log<<Logger::Debug<<"[GET] Val: "<<makeHexDump(val)<<endl);
9153c7cf
AT
218 if (key[0] == '\000' && key[1] == '\045') { // skip locations
219 continue;
220 }
221
222 if (!d_isAxfr) {
223 // If we have a wildcard query, but the record we got is not a wildcard, we skip.
224 if (d_isWildcardQuery && val[2] != '\052' && val[2] != '\053') {
225 continue;
226 }
227
228 // If it is NOT a wildcard query, but we do find a wildcard record, we skip it.
229 if (!d_isWildcardQuery && (val[2] == '\052' || val[2] == '\053')) {
230 continue;
231 }
232 }
233
234
78f56b38 235 PacketReader pr(val, 0);
9153c7cf
AT
236 rr.qtype = QType(pr.get16BitInt());
237
238 if(d_isAxfr || d_qtype.getCode() == QType::ANY || rr.qtype == d_qtype) {
239 char locwild = pr.get8BitInt();
240 if(locwild != '\075' && (locwild == '\076' || locwild == '\053')) {
241 if (d_isAxfr && d_locations) { // We skip records with a location in AXFR, unless we disable locations.
242 continue;
243 }
244 char recloc[2];
245 recloc[0] = pr.get8BitInt();
246 recloc[1] = pr.get8BitInt();
247
248 if (d_locations) {
249 bool foundLocation = false;
250 vector<string> locations = getLocations();
251 while(locations.size() > 0) {
252 string locId = locations.back();
253 locations.pop_back();
254
255 if (recloc[0] == locId[0] && recloc[1] == locId[1]) {
256 foundLocation = true;
257 break;
258 }
259 }
260 if (!foundLocation) {
261 continue;
262 }
263 }
264 }
265
266 if (d_isAxfr && (val[2] == '\052' || val[2] == '\053' )) { // Keys are not stored with wildcard character, with AXFR we need to add that.
267 key.insert(0, 1, '\052');
268 key.insert(0, 1, '\001');
269 }
270 // rr.qname.clear();
271 rr.qname=DNSName(key.c_str(), key.size(), 0, false);
272 rr.domain_id=-1;
273 // 11:13.21 <@ahu> IT IS ALWAYS AUTH --- well not really because we are just a backend :-)
5add4380 274 // We could actually do NSEC3-NARROW DNSSEC according to Habbie, if we do, we need to change something here.
9153c7cf
AT
275 rr.auth = true;
276
277 rr.ttl = pr.get32BitInt();
278 uint64_t timestamp = pr.get32BitInt();
279 timestamp <<= 32;
280 timestamp += pr.get32BitInt();
281 if(timestamp) {
282 uint64_t now = d_taiepoch + time(NULL);
283 if (rr.ttl == 0) {
284 if (timestamp < now) {
285 continue;
286 }
287 rr.ttl = timestamp - now;
288 } else if (now <= timestamp) {
289 continue;
290 }
291 }
292 try {
293 DNSRecord dr;
294 dr.d_class = 1;
295 dr.d_type = rr.qtype.getCode();
78f56b38 296 dr.d_clen = val.size()-pr.getPosition();
9153c7cf 297
9a2c1e06 298 auto drc = DNSRecordContent::mastermake(dr, pr);
9153c7cf 299 rr.content = drc->getZoneRepresentation();
3ee044f3 300 DLOG(cerr<<"CONTENT: "<<rr.content<<endl);
9153c7cf
AT
301 }
302 catch (...) {
e6a9dde5 303 g_log<<Logger::Error<<backendname<<"Failed to parse record content for "<<rr.qname<<" with type "<<rr.qtype.getName();
9153c7cf 304 if (d_ignorebogus) {
e6a9dde5 305 g_log<<". Ignoring!"<<endl;
9153c7cf 306 continue;
be870c30 307 } else {
e6a9dde5 308 g_log<<". Erroring out!"<<endl;
9153c7cf 309 throw;
be870c30 310 }
9153c7cf 311 }
e6a9dde5 312// DLOG(g_log<<Logger::Debug<<backendname<<"Returning ["<<rr.content<<"] for ["<<rr.qname<<"] of RecordType ["<<rr.qtype.getName()<<"]"<<endl;);
9153c7cf
AT
313 return true;
314 }
315 } // end of while
e6a9dde5 316 DLOG(g_log<<Logger::Debug<<backendname<<"No more records to return."<<endl);
9153c7cf 317
10700675 318 d_cdbReader = nullptr;
9153c7cf 319 return false;
ddee6d08
PD
320}
321
322// boilerplate
323class TinyDNSFactory: public BackendFactory
324{
325public:
9153c7cf
AT
326 TinyDNSFactory() : BackendFactory("tinydns") {}
327
328 void declareArguments(const string &suffix="") {
329 declare(suffix, "notify-on-startup", "Tell the TinyDNSBackend to notify all the slave nameservers on startup. Default is no.", "no");
330 declare(suffix, "dbfile", "Location of the cdb data file", "data.cdb");
331 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");
332 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");
7d491af1 333 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");
9153c7cf
AT
334 }
335
336 DNSBackend *make(const string &suffix="") {
337 return new TinyDNSBackend(suffix);
338 }
ddee6d08
PD
339};
340
341// boilerplate
342class TinyDNSLoader
343{
344public:
9153c7cf
AT
345 TinyDNSLoader() {
346 BackendMakers().report(new TinyDNSFactory);
e6a9dde5 347 g_log << Logger::Info << "[tinydnsbackend] This is the tinydns backend version " VERSION
5e6a3d93 348#ifndef REPRODUCIBLE
9153c7cf 349 << " (" __DATE__ " " __TIME__ ")"
5e6a3d93 350#endif
9153c7cf
AT
351 << " reporting" << endl;
352 }
ddee6d08
PD
353};
354
355static TinyDNSLoader tinydnsloader;