]>
Commit | Line | Data |
---|---|---|
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 |
35 | static string backendname="[TinyDNSBackend] "; |
36 | uint32_t TinyDNSBackend::s_lastId; | |
37 | pthread_mutex_t TinyDNSBackend::s_domainInfoLock=PTHREAD_MUTEX_INITIALIZER; | |
38 | TinyDNSBackend::TDI_suffix_t TinyDNSBackend::s_domainInfo; | |
39 | ||
ddee6d08 PD |
40 | vector<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 | ||
79 | TinyDNSBackend::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 | ||
92 | void 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 | ||
135 | void 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 | 152 | void 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 | 178 | bool 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 | 185 | void 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 | ||
208 | bool 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 | |
323 | class TinyDNSFactory: public BackendFactory | |
324 | { | |
325 | public: | |
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 | |
342 | class TinyDNSLoader | |
343 | { | |
344 | public: | |
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 | ||
355 | static TinyDNSLoader tinydnsloader; |