]>
Commit | Line | Data |
---|---|---|
6a40fc00 | 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 | */ | |
22 | #ifdef HAVE_CONFIG_H | |
23 | #include "config.h" | |
24 | #endif | |
25 | #include "pdns/utility.hh" | |
26 | #include "pdns/dnsbackend.hh" | |
27 | #include "pdns/dns.hh" | |
28 | #include "pdns/dnspacket.hh" | |
29 | #include "pdns/base32.hh" | |
30 | #include "pdns/dnssecinfra.hh" | |
31 | #include "pdns/pdnsexception.hh" | |
32 | #include "pdns/logger.hh" | |
33 | #include "pdns/version.hh" | |
34 | #include "pdns/arguments.hh" | |
35 | #include <boost/archive/binary_oarchive.hpp> | |
36 | #include <boost/archive/binary_iarchive.hpp> | |
37 | #include <boost/serialization/vector.hpp> | |
38 | #include <boost/serialization/string.hpp> | |
39 | #include <boost/serialization/utility.hpp> | |
40 | // #include <boost/iostreams/stream.hpp> | |
41 | // #include <boost/iostreams/stream_buffer.hpp> | |
42 | ||
43 | #include <boost/iostreams/device/back_inserter.hpp> | |
44 | // #include <sstream> | |
45 | ||
46 | ||
47 | #include "lmdbbackend.hh" | |
48 | ||
370ae1cd PD |
49 | #define SCHEMAVERSION 1 |
50 | ||
6a40fc00 | 51 | LMDBBackend::LMDBBackend(const std::string& suffix) |
52 | { | |
53 | setArgPrefix("lmdb"+suffix); | |
54 | ||
55 | string syncMode = toLower(getArg("sync-mode")); | |
56 | ||
57 | if(syncMode == "nosync") | |
58 | d_asyncFlag = MDB_NOSYNC; | |
59 | else if(syncMode == "nometasync") | |
60 | d_asyncFlag = MDB_NOMETASYNC; | |
61 | else if(syncMode == "mapasync") | |
62 | d_asyncFlag = MDB_MAPASYNC; | |
5b3b1081 | 63 | else if(syncMode.empty() || syncMode == "sync") |
6a40fc00 | 64 | d_asyncFlag = 0; |
65 | else | |
66 | throw std::runtime_error("Unknown sync mode "+syncMode+" requested for LMDB backend"); | |
67 | ||
68 | d_tdomains = std::make_shared<tdomains_t>(getMDBEnv(getArg("filename").c_str(), MDB_NOSUBDIR | d_asyncFlag, 0600), "domains"); | |
69 | d_tmeta = std::make_shared<tmeta_t>(d_tdomains->getEnv(), "metadata"); | |
70 | d_tkdb = std::make_shared<tkdb_t>(d_tdomains->getEnv(), "keydata"); | |
71 | d_ttsig = std::make_shared<ttsig_t>(d_tdomains->getEnv(), "tsig"); | |
72 | ||
73 | auto pdnsdbi = d_tdomains->getEnv()->openDB("pdns", MDB_CREATE); | |
74 | auto txn = d_tdomains->getEnv()->getRWTransaction(); | |
370ae1cd | 75 | MDBOutVal _schemaversion; |
9c1e5491 | 76 | if(!txn->get(pdnsdbi, "schemaversion", _schemaversion)) { |
370ae1cd PD |
77 | auto schemaversion = _schemaversion.get<uint32_t>(); |
78 | if (schemaversion != SCHEMAVERSION) { | |
79 | throw std::runtime_error("Expected LMDB schema version "+std::to_string(SCHEMAVERSION)+" but got "+std::to_string(schemaversion)); | |
80 | } | |
81 | } | |
82 | else { | |
9c1e5491 | 83 | txn->put(pdnsdbi, "schemaversion", SCHEMAVERSION); |
370ae1cd | 84 | } |
6a40fc00 | 85 | MDBOutVal shards; |
9c1e5491 | 86 | if(!txn->get(pdnsdbi, "shards", shards)) { |
6a40fc00 | 87 | |
88 | d_shards = shards.get<uint32_t>(); | |
89 | if(d_shards != atoi(getArg("shards").c_str())) { | |
90 | g_log << Logger::Warning<<"Note: configured number of lmdb shards ("<<atoi(getArg("shards").c_str())<<") is different from on-disk ("<<d_shards<<"). Using on-disk shard number"<<endl; | |
91 | } | |
92 | } | |
93 | else { | |
94 | d_shards = atoi(getArg("shards").c_str()); | |
9c1e5491 | 95 | txn->put(pdnsdbi, "shards", d_shards); |
6a40fc00 | 96 | } |
9c1e5491 | 97 | txn->commit(); |
370ae1cd | 98 | d_trecords.resize(d_shards); |
6a40fc00 | 99 | d_dolog = ::arg().mustDo("query-logging"); |
100 | } | |
101 | ||
102 | ||
103 | ||
104 | namespace boost { | |
105 | namespace serialization { | |
106 | ||
107 | template<class Archive> | |
108 | void save(Archive & ar, const DNSName& g, const unsigned int version) | |
109 | { | |
110 | if(!g.empty()) { | |
111 | std::string tmp = g.toDNSStringLC(); // g++ 4.8 woes | |
112 | ar & tmp; | |
113 | } | |
114 | else | |
115 | ar & ""; | |
116 | } | |
117 | ||
118 | template<class Archive> | |
119 | void load(Archive & ar, DNSName& g, const unsigned int version) | |
120 | { | |
121 | string tmp; | |
122 | ar & tmp; | |
123 | if(tmp.empty()) | |
124 | g = DNSName(); | |
125 | else | |
126 | g = DNSName(tmp.c_str(), tmp.size(), 0, false); | |
127 | } | |
128 | ||
129 | template<class Archive> | |
130 | void save(Archive & ar, const QType& g, const unsigned int version) | |
131 | { | |
132 | uint16_t tmp = g.getCode(); // g++ 4.8 woes | |
133 | ar & tmp; | |
134 | } | |
135 | ||
136 | template<class Archive> | |
137 | void load(Archive & ar, QType& g, const unsigned int version) | |
138 | { | |
139 | uint16_t tmp; | |
140 | ar & tmp; | |
141 | g = QType(tmp); | |
142 | } | |
143 | ||
144 | template<class Archive> | |
145 | void serialize(Archive & ar, DomainInfo& g, const unsigned int version) | |
146 | { | |
147 | ar & g.zone; | |
148 | ar & g.last_check; | |
149 | ar & g.account; | |
150 | ar & g.masters; | |
151 | ar & g.id; | |
152 | ar & g.notified_serial; | |
153 | ar & g.kind; | |
154 | } | |
155 | ||
156 | template<class Archive> | |
157 | void serialize(Archive & ar, LMDBBackend::DomainMeta& g, const unsigned int version) | |
158 | { | |
159 | ar & g.domain & g.key & g.value; | |
160 | } | |
161 | ||
162 | template<class Archive> | |
163 | void serialize(Archive & ar, LMDBBackend::KeyDataDB& g, const unsigned int version) | |
164 | { | |
165 | ar & g.domain & g.content & g.flags & g.active; | |
166 | } | |
167 | ||
168 | template<class Archive> | |
169 | void serialize(Archive & ar, TSIGKey& g, const unsigned int version) | |
170 | { | |
171 | ar & g.name; | |
172 | ar & g.algorithm; // this is the ordername | |
173 | ar & g.key; | |
174 | } | |
175 | ||
176 | ||
177 | ||
178 | } // namespace serialization | |
179 | } // namespace boost | |
180 | ||
181 | BOOST_SERIALIZATION_SPLIT_FREE(DNSName); | |
182 | BOOST_SERIALIZATION_SPLIT_FREE(QType); | |
183 | BOOST_IS_BITWISE_SERIALIZABLE(ComboAddress); | |
184 | ||
185 | template<> | |
186 | std::string serToString(const DNSResourceRecord& rr) | |
187 | { | |
188 | // only does content, ttl, auth | |
189 | std::string ret; | |
190 | uint16_t len = rr.content.length(); | |
191 | ret.reserve(2+len+8); | |
192 | ||
193 | ret.assign((const char*)&len, 2); | |
194 | ret += rr.content; | |
195 | ret.append((const char*)&rr.ttl, 4); | |
196 | ret.append(1, (char)rr.auth); | |
3bb3f561 | 197 | ret.append(1, (char)false); |
6a40fc00 | 198 | ret.append(1, (char)rr.disabled); |
199 | return ret; | |
200 | } | |
201 | ||
202 | template<> | |
203 | void serFromString(const string_view& str, DNSResourceRecord& rr) | |
204 | { | |
205 | uint16_t len; | |
206 | memcpy(&len, &str[0], 2); | |
207 | rr.content.assign(&str[2], len); // len bytes | |
208 | memcpy(&rr.ttl, &str[2] + len, 4); | |
3bb3f561 | 209 | rr.auth = str[str.size()-3]; |
6a40fc00 | 210 | rr.disabled = str[str.size()-1]; |
211 | rr.wildcardname.clear(); | |
212 | } | |
213 | ||
214 | ||
215 | std::string serializeContent(uint16_t qtype, const DNSName& domain, const std::string& content) | |
216 | { | |
217 | auto drc = DNSRecordContent::mastermake(qtype, 1, content); | |
218 | return drc->serialize(domain, false); | |
219 | } | |
220 | ||
221 | std::shared_ptr<DNSRecordContent> unserializeContentZR(uint16_t qtype, const DNSName& qname, const std::string& content) | |
222 | { | |
223 | if(qtype == QType::A && content.size() == 4) { | |
224 | return std::make_shared<ARecordContent>(*((uint32_t*)content.c_str())); | |
225 | } | |
226 | return DNSRecordContent::unserialize(qname, qtype, content); | |
227 | } | |
228 | ||
229 | ||
230 | /* design. If you ask a question without a zone id, we lookup the best | |
231 | zone id for you, and answer from that. This is different than other backends, but I can't see why it would not work. | |
232 | ||
233 | The index we use is "zoneid,canonical relative name". This index is also used | |
234 | for AXFR. | |
235 | ||
236 | Note - domain_id, name and type are ONLY present on the index! | |
237 | */ | |
238 | ||
01d7beb6 | 239 | #if BOOST_VERSION >= 106100 |
6a40fc00 | 240 | #define StringView string_view |
01d7beb6 | 241 | #else |
242 | #define StringView string | |
6a40fc00 | 243 | #endif |
244 | ||
245 | void LMDBBackend::deleteDomainRecords(RecordsRWTransaction& txn, uint32_t domain_id, uint16_t qtype) | |
246 | { | |
247 | compoundOrdername co; | |
248 | string match = co(domain_id); | |
249 | ||
9c1e5491 | 250 | auto cursor = txn.txn->getCursor(txn.db->dbi); |
6a40fc00 | 251 | MDBOutVal key, val; |
252 | // cout<<"Match: "<<makeHexDump(match); | |
253 | if(!cursor.lower_bound(match, key, val) ) { | |
254 | while(key.get<StringView>().rfind(match, 0) == 0) { | |
255 | if(qtype == QType::ANY || co.getQType(key.get<StringView>()) == qtype) | |
256 | cursor.del(MDB_NODUPDATA); | |
257 | if(cursor.next(key, val)) break; | |
258 | } | |
259 | } | |
260 | } | |
261 | ||
262 | /* Here's the complicated story. Other backends have just one transaction, which is either | |
263 | on or not. | |
264 | ||
265 | You can't call feedRecord without a transaction started with startTransaction. | |
266 | ||
267 | However, other functions can be called after startTransaction() or without startTransaction() | |
268 | (like updateDNSSECOrderNameAndAuth) | |
269 | ||
270 | ||
271 | ||
272 | */ | |
273 | ||
274 | bool LMDBBackend::startTransaction(const DNSName &domain, int domain_id) | |
275 | { | |
276 | // cout <<"startTransaction("<<domain<<", "<<domain_id<<")"<<endl; | |
277 | int real_id = domain_id; | |
278 | if(real_id < 0) { | |
279 | auto rotxn = d_tdomains->getROTransaction(); | |
280 | DomainInfo di; | |
281 | real_id = rotxn.get<0>(domain, di); | |
282 | // cout<<"real_id = "<<real_id << endl; | |
283 | if(!real_id) | |
284 | return false; | |
285 | } | |
286 | if(d_rwtxn) { | |
287 | throw DBException("Attempt to start a transaction while one was open already"); | |
288 | } | |
289 | d_rwtxn = getRecordsRWTransaction(real_id); | |
290 | ||
291 | d_transactiondomain = domain; | |
292 | d_transactiondomainid = real_id; | |
293 | if(domain_id >= 0) { | |
294 | deleteDomainRecords(*d_rwtxn, domain_id); | |
295 | } | |
296 | ||
297 | return true; | |
298 | } | |
299 | ||
300 | bool LMDBBackend::commitTransaction() | |
301 | { | |
302 | // cout<<"Commit transaction" <<endl; | |
9c1e5491 | 303 | d_rwtxn->txn->commit(); |
6a40fc00 | 304 | d_rwtxn.reset(); |
305 | return true; | |
306 | } | |
307 | ||
308 | bool LMDBBackend::abortTransaction() | |
309 | { | |
310 | // cout<<"Abort transaction"<<endl; | |
9c1e5491 | 311 | d_rwtxn->txn->abort(); |
6a40fc00 | 312 | d_rwtxn.reset(); |
313 | ||
314 | return true; | |
315 | } | |
316 | ||
317 | // d_rwtxn must be set here | |
3bb3f561 | 318 | bool LMDBBackend::feedRecord(const DNSResourceRecord &r, const DNSName &ordername, bool ordernameIsNSEC3) |
6a40fc00 | 319 | { |
320 | DNSResourceRecord rr(r); | |
321 | rr.qname.makeUsRelative(d_transactiondomain); | |
322 | rr.content = serializeContent(rr.qtype.getCode(), r.qname, rr.content); | |
3bb3f561 | 323 | rr.disabled = false; |
6a40fc00 | 324 | |
325 | compoundOrdername co; | |
9c1e5491 | 326 | d_rwtxn->txn->put(d_rwtxn->db->dbi, co(r.domain_id, rr.qname, rr.qtype.getCode()), serToString(rr)); |
6a40fc00 | 327 | |
3bb3f561 KM |
328 | if(ordernameIsNSEC3 && !ordername.empty()) { |
329 | MDBOutVal val; | |
9c1e5491 | 330 | if(d_rwtxn->txn->get(d_rwtxn->db->dbi, co(r.domain_id, rr.qname, QType::NSEC3), val)) { |
6a40fc00 | 331 | rr.ttl = 0; |
6a40fc00 | 332 | rr.content=rr.qname.toDNSStringLC(); |
3bb3f561 | 333 | rr.auth = 0; |
6a40fc00 | 334 | string ser = serToString(rr); |
9c1e5491 | 335 | d_rwtxn->txn->put(d_rwtxn->db->dbi, co(r.domain_id, ordername, QType::NSEC3), ser); |
6a40fc00 | 336 | |
337 | rr.ttl = 1; | |
338 | rr.content = ordername.toDNSString(); | |
339 | ser = serToString(rr); | |
9c1e5491 | 340 | d_rwtxn->txn->put(d_rwtxn->db->dbi, co(r.domain_id, rr.qname, QType::NSEC3), ser); |
3bb3f561 | 341 | } |
6a40fc00 | 342 | } |
343 | return true; | |
344 | } | |
345 | ||
346 | bool LMDBBackend::feedEnts(int domain_id, map<DNSName,bool>& nonterm) | |
347 | { | |
348 | DNSResourceRecord rr; | |
349 | rr.ttl = 0; | |
350 | compoundOrdername co; | |
351 | for(const auto& nt: nonterm) { | |
352 | rr.qname = nt.first.makeRelative(d_transactiondomain); | |
353 | rr.auth = nt.second; | |
3bb3f561 | 354 | rr.disabled = true; |
6a40fc00 | 355 | |
3bb3f561 | 356 | std::string ser = serToString(rr); |
9c1e5491 | 357 | d_rwtxn->txn->put(d_rwtxn->db->dbi, co(domain_id, rr.qname, 0), ser); |
6a40fc00 | 358 | } |
359 | return true; | |
360 | } | |
361 | ||
362 | bool LMDBBackend::feedEnts3(int domain_id, const DNSName &domain, map<DNSName,bool> &nonterm, const NSEC3PARAMRecordContent& ns3prc, bool narrow) | |
363 | { | |
3bb3f561 | 364 | string ser; |
6a40fc00 | 365 | DNSName ordername; |
366 | DNSResourceRecord rr; | |
367 | compoundOrdername co; | |
368 | for(const auto& nt: nonterm) { | |
369 | rr.qname = nt.first.makeRelative(domain); | |
370 | rr.ttl = 0; | |
371 | rr.auth = nt.second; | |
3bb3f561 KM |
372 | rr.disabled = nt.second; |
373 | ser = serToString(rr); | |
9c1e5491 | 374 | d_rwtxn->txn->put(d_rwtxn->db->dbi, co(domain_id, rr.qname, 0), ser); |
6a40fc00 | 375 | |
376 | if(!narrow && rr.auth) { | |
3bb3f561 KM |
377 | rr.content = rr.qname.toDNSString(); |
378 | rr.auth = false; | |
379 | rr.disabled = false; | |
6a40fc00 | 380 | ser = serToString(rr); |
381 | ||
382 | ordername=DNSName(toBase32Hex(hashQNameWithSalt(ns3prc, nt.first))); | |
9c1e5491 | 383 | d_rwtxn->txn->put(d_rwtxn->db->dbi, co(domain_id, ordername, QType::NSEC3), ser); |
6a40fc00 | 384 | |
385 | rr.ttl = 1; | |
386 | rr.content = ordername.toDNSString(); | |
387 | ser = serToString(rr); | |
9c1e5491 | 388 | d_rwtxn->txn->put(d_rwtxn->db->dbi, co(domain_id, rr.qname, QType::NSEC3), ser); |
6a40fc00 | 389 | } |
390 | } | |
391 | return true; | |
392 | } | |
393 | ||
394 | ||
395 | // might be called within a transaction, might also be called alone | |
396 | bool LMDBBackend::replaceRRSet(uint32_t domain_id, const DNSName& qname, const QType& qt, const vector<DNSResourceRecord>& rrset) | |
397 | { | |
398 | // zonk qname/qtype within domain_id (go through qname, check domain_id && qtype) | |
399 | shared_ptr<RecordsRWTransaction> txn; | |
400 | bool needCommit = false; | |
401 | if(d_rwtxn && d_transactiondomainid==domain_id) { | |
402 | txn = d_rwtxn; | |
403 | // cout<<"Reusing open transaction"<<endl; | |
404 | } | |
405 | else { | |
406 | // cout<<"Making a new RW txn for replace rrset"<<endl; | |
407 | txn = getRecordsRWTransaction(domain_id); | |
408 | needCommit = true; | |
409 | } | |
410 | ||
411 | DomainInfo di; | |
72f6d4c4 RG |
412 | if (!d_tdomains->getROTransaction().get(domain_id, di)) { |
413 | return false; | |
414 | } | |
415 | ||
6a40fc00 | 416 | compoundOrdername co; |
9c1e5491 | 417 | auto cursor = txn->txn->getCursor(txn->db->dbi); |
6a40fc00 | 418 | MDBOutVal key, val; |
419 | string match =co(domain_id, qname.makeRelative(di.zone), qt.getCode()); | |
420 | if(!cursor.find(match, key, val)) { | |
421 | do { | |
422 | cursor.del(MDB_NODUPDATA); | |
423 | } while(!cursor.next(key, val) && key.get<StringView>().rfind(match, 0) == 0); | |
424 | } | |
425 | ||
426 | for(auto rr : rrset) { | |
427 | rr.content = serializeContent(rr.qtype.getCode(), rr.qname, rr.content); | |
428 | rr.qname.makeUsRelative(di.zone); | |
9c1e5491 | 429 | txn->txn->put(txn->db->dbi, match, serToString(rr)); |
6a40fc00 | 430 | } |
431 | ||
432 | if(needCommit) | |
9c1e5491 | 433 | txn->txn->commit(); |
6a40fc00 | 434 | |
435 | return true; | |
436 | } | |
437 | ||
438 | // tempting to templatize these two functions but the pain is not worth it | |
439 | std::shared_ptr<LMDBBackend::RecordsRWTransaction> LMDBBackend::getRecordsRWTransaction(uint32_t id) | |
440 | { | |
441 | auto& shard =d_trecords[id % d_shards]; | |
442 | if(!shard.env) { | |
443 | shard.env = getMDBEnv( (getArg("filename")+"-"+std::to_string(id % d_shards)).c_str(), | |
444 | MDB_NOSUBDIR | d_asyncFlag, 0600); | |
445 | shard.dbi = shard.env->openDB("records", MDB_CREATE | MDB_DUPSORT); | |
446 | } | |
447 | auto ret = std::make_shared<RecordsRWTransaction>(shard.env->getRWTransaction()); | |
448 | ret->db = std::make_shared<RecordsDB>(shard); | |
449 | ||
450 | return ret; | |
451 | } | |
452 | ||
a1c121f9 | 453 | std::shared_ptr<LMDBBackend::RecordsROTransaction> LMDBBackend::getRecordsROTransaction(uint32_t id, std::shared_ptr<LMDBBackend::RecordsRWTransaction> rwtxn) |
6a40fc00 | 454 | { |
455 | auto& shard =d_trecords[id % d_shards]; | |
456 | if(!shard.env) { | |
a1c121f9 PD |
457 | if (rwtxn) { |
458 | throw DBException("attempting to start nested transaction without open parent env"); | |
459 | } | |
6a40fc00 | 460 | shard.env = getMDBEnv( (getArg("filename")+"-"+std::to_string(id % d_shards)).c_str(), |
461 | MDB_NOSUBDIR | d_asyncFlag, 0600); | |
462 | shard.dbi = shard.env->openDB("records", MDB_CREATE | MDB_DUPSORT); | |
463 | } | |
464 | ||
a1c121f9 PD |
465 | if (rwtxn) { |
466 | auto ret = std::make_shared<RecordsROTransaction>(rwtxn->txn->getROTransaction()); | |
467 | ret->db = std::make_shared<RecordsDB>(shard); | |
468 | return ret; | |
469 | } else { | |
470 | auto ret = std::make_shared<RecordsROTransaction>(shard.env->getROTransaction()); | |
471 | ret->db = std::make_shared<RecordsDB>(shard); | |
472 | return ret; | |
473 | } | |
6a40fc00 | 474 | } |
475 | ||
476 | ||
477 | bool LMDBBackend::deleteDomain(const DNSName &domain) | |
478 | { | |
479 | auto doms = d_tdomains->getRWTransaction(); | |
480 | ||
481 | DomainInfo di; | |
482 | auto id = doms.get<0>(domain, di); | |
483 | if(!id) | |
484 | return false; | |
485 | ||
486 | shared_ptr<RecordsRWTransaction> txn; | |
487 | bool needCommit = false; | |
488 | if(d_rwtxn && d_transactiondomainid == id) { | |
489 | txn = d_rwtxn; | |
490 | // cout<<"Reusing open transaction"<<endl; | |
491 | } | |
492 | else { | |
493 | // cout<<"Making a new RW txn for delete domain"<<endl; | |
494 | txn = getRecordsRWTransaction(id); | |
495 | needCommit = true; | |
496 | } | |
497 | ||
498 | ||
499 | doms.del(id); | |
500 | compoundOrdername co; | |
501 | string match=co(id); | |
502 | ||
9c1e5491 | 503 | auto cursor = txn->txn->getCursor(txn->db->dbi); |
6a40fc00 | 504 | MDBOutVal key, val; |
505 | if(!cursor.find(match, key, val)) { | |
506 | do { | |
507 | cursor.del(MDB_NODUPDATA); | |
508 | } while(!cursor.next(key, val) && key.get<StringView>().rfind(match, 0) == 0); | |
509 | } | |
510 | ||
511 | if(needCommit) | |
9c1e5491 | 512 | txn->txn->commit(); |
6a40fc00 | 513 | |
514 | doms.commit(); | |
515 | ||
516 | return true; | |
517 | } | |
518 | ||
519 | bool LMDBBackend::list(const DNSName &target, int id, bool include_disabled) | |
520 | { | |
521 | d_inlist=true; | |
522 | DomainInfo di; | |
523 | { | |
524 | auto dtxn = d_tdomains->getROTransaction(); | |
525 | ||
526 | if((di.id = dtxn.get<0>(target, di))) | |
527 | ; // cout<<"Found domain "<<target<<" on domain_id "<<di.id <<", list requested "<<id<<endl; | |
528 | else { | |
529 | // cout<<"Did not find "<<target<<endl; | |
530 | return false; | |
531 | } | |
532 | } | |
533 | ||
534 | d_rotxn = getRecordsROTransaction(di.id); | |
535 | compoundOrdername co; | |
536 | d_matchkey = co(di.id); | |
9c1e5491 | 537 | d_getcursor = std::make_shared<MDBROCursor>(d_rotxn->txn->getCursor(d_rotxn->db->dbi)); |
6a40fc00 | 538 | MDBOutVal key, val; |
539 | d_inlist = true; | |
540 | ||
541 | if(d_getcursor->lower_bound(d_matchkey, key, val) || key.get<StringView>().rfind(d_matchkey, 0) != 0) { | |
542 | // cout<<"Found nothing for list"<<endl; | |
543 | d_getcursor.reset(); | |
544 | return true; | |
545 | } | |
546 | ||
547 | d_lookupqname = target; | |
548 | ||
549 | return true; | |
550 | } | |
551 | ||
acb61e0a | 552 | void LMDBBackend::lookup(const QType &type, const DNSName &qdomain, int zoneId, DNSPacket *p) |
6a40fc00 | 553 | { |
554 | if(d_dolog) { | |
555 | g_log << Logger::Warning << "Got lookup for "<<qdomain<<"|"<<type.getName()<<" in zone "<< zoneId<<endl; | |
556 | d_dtime.set(); | |
557 | } | |
558 | DNSName hunt(qdomain); | |
6678e5ac | 559 | DomainInfo di; |
6a40fc00 | 560 | if(zoneId < 0) { |
561 | auto rotxn = d_tdomains->getROTransaction(); | |
562 | ||
6678e5ac KM |
563 | do { |
564 | zoneId = rotxn.get<0>(hunt, di); | |
565 | } while (!zoneId && type != QType::SOA && hunt.chopOff()); | |
6a40fc00 | 566 | if(zoneId <= 0) { |
567 | // cout << "Did not find zone for "<< qdomain<<endl; | |
568 | d_getcursor.reset(); | |
569 | return; | |
570 | } | |
571 | } | |
572 | else { | |
6a40fc00 | 573 | if(!d_tdomains->getROTransaction().get(zoneId, di)) { |
574 | // cout<<"Could not find a zone with id "<<zoneId<<endl; | |
575 | d_getcursor.reset(); | |
576 | return; | |
577 | } | |
578 | hunt = di.zone; | |
579 | } | |
580 | ||
581 | DNSName relqname = qdomain.makeRelative(hunt); | |
582 | // cout<<"get will look for "<<relqname<< " in zone "<<hunt<<" with id "<<zoneId<<endl; | |
a1c121f9 | 583 | d_rotxn = getRecordsROTransaction(zoneId, d_rwtxn); |
6a40fc00 | 584 | |
585 | compoundOrdername co; | |
9c1e5491 | 586 | d_getcursor = std::make_shared<MDBROCursor>(d_rotxn->txn->getCursor(d_rotxn->db->dbi)); |
6a40fc00 | 587 | MDBOutVal key, val; |
588 | if(type.getCode() == QType::ANY) { | |
589 | d_matchkey = co(zoneId,relqname); | |
590 | } | |
591 | else { | |
592 | d_matchkey= co(zoneId,relqname, type.getCode()); | |
593 | } | |
594 | d_inlist=false; | |
595 | ||
596 | if(d_getcursor->lower_bound(d_matchkey, key, val) || key.get<StringView>().rfind(d_matchkey, 0) != 0) { | |
597 | d_getcursor.reset(); | |
598 | if(d_dolog) { | |
599 | g_log<<Logger::Warning<< "Query "<<((long)(void*)this)<<": "<<d_dtime.udiffNoReset()<<" usec to execute (found nothing)"<<endl; | |
600 | } | |
601 | return; | |
602 | } | |
603 | ||
604 | if(d_dolog) { | |
605 | g_log<<Logger::Warning<< "Query "<<((long)(void*)this)<<": "<<d_dtime.udiffNoReset()<<" usec to execute"<<endl; | |
606 | } | |
607 | ||
608 | d_lookuptype=type; | |
609 | d_lookupqname = qdomain; | |
610 | d_lookupdomain = hunt; | |
611 | d_lookupdomainid = zoneId; | |
612 | } | |
613 | ||
614 | bool LMDBBackend::get(DNSZoneRecord& rr) | |
615 | { | |
616 | if(d_inlist) | |
617 | return get_list(rr); | |
618 | else | |
619 | return get_lookup(rr); | |
620 | } | |
621 | ||
622 | bool LMDBBackend::get(DNSResourceRecord& rr) | |
623 | { | |
624 | // cout <<"Old-school get called"<<endl; | |
625 | DNSZoneRecord dzr; | |
626 | if(d_inlist) { | |
627 | if(!get_list(dzr)) | |
628 | return false; | |
629 | } | |
630 | else { | |
631 | if(!get_lookup(dzr)) | |
632 | return false; | |
633 | } | |
634 | rr.qname = dzr.dr.d_name; | |
635 | rr.ttl = dzr.dr.d_ttl; | |
636 | rr.qtype =dzr.dr.d_type; | |
ca60f09f | 637 | rr.content = dzr.dr.d_content->getZoneRepresentation(true); |
6a40fc00 | 638 | rr.domain_id = dzr.domain_id; |
95c0727a | 639 | rr.auth = dzr.auth; |
6a40fc00 | 640 | // cout<<"old school called for "<<rr.qname<<", "<<rr.qtype.getName()<<endl; |
641 | return true; | |
642 | } | |
643 | ||
644 | bool LMDBBackend::getSOA(const DNSName &domain, SOAData &sd) | |
645 | { | |
646 | // cout <<"Native getSOA called"<<endl; | |
acb61e0a | 647 | lookup(QType(QType::SOA), domain, -1); |
6a40fc00 | 648 | DNSZoneRecord dzr; |
649 | bool found=false; | |
650 | while(get(dzr)) { | |
651 | auto src = getRR<SOARecordContent>(dzr.dr); | |
652 | sd.domain_id = dzr.domain_id; | |
653 | sd.ttl = dzr.dr.d_ttl; | |
654 | sd.qname = dzr.dr.d_name; | |
655 | ||
656 | sd.nameserver = src->d_mname; | |
657 | sd.hostmaster = src->d_rname; | |
658 | sd.serial = src->d_st.serial; | |
659 | sd.refresh = src->d_st.refresh; | |
660 | sd.retry = src->d_st.retry; | |
661 | sd.expire = src->d_st.expire; | |
662 | sd.default_ttl = src->d_st.minimum; | |
663 | ||
664 | sd.db = this; | |
665 | found=true; | |
666 | } | |
667 | return found; | |
668 | } | |
669 | bool LMDBBackend::get_list(DNSZoneRecord& rr) | |
670 | { | |
671 | for(;;) { | |
672 | if(!d_getcursor) { | |
673 | d_rotxn.reset(); | |
674 | return false; | |
675 | } | |
676 | ||
677 | MDBOutVal keyv, val; | |
678 | ||
679 | d_getcursor->current(keyv, val); | |
680 | DNSResourceRecord drr; | |
681 | serFromString(val.get<string>(), drr); | |
682 | ||
683 | auto key = keyv.get<string_view>(); | |
684 | rr.dr.d_name = compoundOrdername::getQName(key) + d_lookupqname; | |
685 | rr.domain_id = compoundOrdername::getDomainID(key); | |
686 | rr.dr.d_type = compoundOrdername::getQType(key).getCode(); | |
687 | rr.dr.d_ttl = drr.ttl; | |
688 | rr.auth = drr.auth; | |
689 | ||
690 | if(rr.dr.d_type == QType::NSEC3) { | |
691 | // cout << "Had a magic NSEC3, skipping it" << endl; | |
692 | if(d_getcursor->next(keyv, val) || keyv.get<StringView>().rfind(d_matchkey, 0) != 0) { | |
693 | d_getcursor.reset(); | |
694 | } | |
695 | continue; | |
696 | } | |
697 | rr.dr.d_content = unserializeContentZR(rr.dr.d_type, rr.dr.d_name, drr.content); | |
698 | ||
699 | if(d_getcursor->next(keyv, val) || keyv.get<StringView>().rfind(d_matchkey, 0) != 0) { | |
700 | d_getcursor.reset(); | |
701 | } | |
702 | break; | |
703 | } | |
704 | return true; | |
705 | } | |
706 | ||
707 | ||
708 | bool LMDBBackend::get_lookup(DNSZoneRecord& rr) | |
709 | { | |
710 | for(;;) { | |
711 | if(!d_getcursor) { | |
712 | d_rotxn.reset(); | |
713 | return false; | |
714 | } | |
715 | MDBOutVal keyv, val; | |
716 | d_getcursor->current(keyv, val); | |
717 | DNSResourceRecord drr; | |
718 | serFromString(val.get<string>(), drr); | |
719 | ||
720 | auto key = keyv.get<string_view>(); | |
721 | ||
722 | rr.dr.d_name = compoundOrdername::getQName(key) + d_lookupdomain; | |
723 | ||
724 | rr.domain_id = compoundOrdername::getDomainID(key); | |
725 | // cout << "We found "<<rr.qname<< " in zone id "<<rr.domain_id <<endl; | |
726 | rr.dr.d_type = compoundOrdername::getQType(key).getCode(); | |
727 | rr.dr.d_ttl = drr.ttl; | |
728 | if(rr.dr.d_type == QType::NSEC3) { | |
729 | // cout << "Hit a magic NSEC3 skipping" << endl; | |
730 | if(d_getcursor->next(keyv, val) || keyv.get<StringView>().rfind(d_matchkey, 0) != 0) { | |
731 | d_getcursor.reset(); | |
732 | d_rotxn.reset(); | |
733 | } | |
734 | continue; | |
735 | } | |
736 | ||
737 | rr.dr.d_content = unserializeContentZR(rr.dr.d_type, rr.dr.d_name, drr.content); | |
738 | rr.auth = drr.auth; | |
739 | if(d_getcursor->next(keyv, val) || keyv.get<StringView>().rfind(d_matchkey, 0) != 0) { | |
740 | d_getcursor.reset(); | |
741 | d_rotxn.reset(); | |
742 | } | |
743 | break; | |
744 | } | |
745 | ||
746 | ||
747 | return true; | |
748 | } | |
749 | ||
750 | ||
751 | bool LMDBBackend::getDomainInfo(const DNSName &domain, DomainInfo &di, bool getSerial) | |
752 | { | |
753 | auto txn = d_tdomains->getROTransaction(); | |
754 | ||
755 | if(!(di.id=txn.get<0>(domain, di))) | |
756 | return false; | |
757 | di.backend = this; | |
758 | return true; | |
759 | } | |
760 | ||
761 | ||
762 | int LMDBBackend::genChangeDomain(const DNSName& domain, std::function<void(DomainInfo&)> func) | |
763 | { | |
764 | auto txn = d_tdomains->getRWTransaction(); | |
765 | ||
766 | DomainInfo di; | |
767 | ||
768 | auto id = txn.get<0>(domain, di); | |
769 | func(di); | |
770 | txn.put(di, id); | |
771 | ||
772 | txn.commit(); | |
773 | return true; | |
774 | } | |
775 | ||
776 | int LMDBBackend::genChangeDomain(uint32_t id, std::function<void(DomainInfo&)> func) | |
777 | { | |
778 | DomainInfo di; | |
779 | ||
780 | auto txn = d_tdomains->getRWTransaction(); | |
781 | ||
782 | if(!txn.get(id , di)) | |
783 | return false; | |
784 | ||
785 | func(di); | |
786 | ||
787 | txn.put(di, id); | |
788 | ||
789 | txn.commit(); | |
790 | return true; | |
791 | } | |
792 | ||
793 | ||
794 | bool LMDBBackend::setKind(const DNSName &domain, const DomainInfo::DomainKind kind) | |
795 | { | |
796 | return genChangeDomain(domain, [kind](DomainInfo& di) { | |
797 | di.kind = kind; | |
798 | }); | |
799 | } | |
800 | ||
801 | bool LMDBBackend::setAccount(const DNSName &domain, const std::string& account) | |
802 | { | |
803 | return genChangeDomain(domain, [account](DomainInfo& di) { | |
804 | di.account = account; | |
805 | }); | |
806 | } | |
807 | ||
808 | ||
809 | void LMDBBackend::setFresh(uint32_t domain_id) | |
810 | { | |
811 | genChangeDomain(domain_id, [](DomainInfo& di) { | |
812 | di.last_check = time(0); | |
813 | }); | |
814 | } | |
815 | ||
816 | void LMDBBackend::setNotified(uint32_t domain_id, uint32_t serial) | |
817 | { | |
818 | genChangeDomain(domain_id, [serial](DomainInfo& di) { | |
819 | di.serial = serial; | |
820 | }); | |
821 | } | |
822 | ||
823 | ||
824 | bool LMDBBackend::setMaster(const DNSName &domain, const std::string& ips) | |
825 | { | |
826 | vector<ComboAddress> masters; | |
827 | vector<string> parts; | |
828 | stringtok(parts, ips, " \t;,"); | |
829 | for(const auto& ip : parts) | |
205c3599 | 830 | masters.push_back(ComboAddress(ip, 53)); |
6a40fc00 | 831 | |
832 | return genChangeDomain(domain, [&masters](DomainInfo& di) { | |
833 | di.masters = masters; | |
834 | }); | |
835 | } | |
836 | ||
837 | bool LMDBBackend::createDomain(const DNSName &domain) | |
838 | { | |
839 | return createDomain(domain, "NATIVE", "", ""); | |
840 | } | |
841 | ||
842 | bool LMDBBackend::createDomain(const DNSName &domain, const string &type, const string &masters, const string &account) | |
843 | { | |
844 | DomainInfo di; | |
845 | ||
846 | auto txn = d_tdomains->getRWTransaction(); | |
847 | if(txn.get<0>(domain, di)) { | |
848 | throw DBException("Domain '"+domain.toLogString()+"' exists already"); | |
849 | } | |
850 | ||
851 | di.zone = domain; | |
852 | if(pdns_iequals(type, "master")) | |
853 | di.kind = DomainInfo::Master; | |
854 | else if(pdns_iequals(type, "slave")) | |
855 | di.kind = DomainInfo::Slave; | |
856 | else if(pdns_iequals(type, "native")) | |
857 | di.kind = DomainInfo::Native; | |
858 | else | |
859 | throw DBException("Unable to create domain of unknown type '"+type+"'"); | |
860 | di.account = account; | |
861 | ||
862 | txn.put(di); | |
863 | txn.commit(); | |
864 | ||
865 | return true; | |
866 | } | |
867 | ||
868 | void LMDBBackend::getAllDomains(vector<DomainInfo> *domains, bool include_disabled) | |
869 | { | |
870 | compoundOrdername co; | |
871 | MDBOutVal val; | |
872 | domains->clear(); | |
873 | auto txn = d_tdomains->getROTransaction(); | |
874 | for(auto iter = txn.begin(); iter != txn.end(); ++iter) { | |
875 | DomainInfo di=*iter; | |
876 | di.id = iter.getID(); | |
877 | ||
e35f9e46 | 878 | auto txn2 = getRecordsROTransaction(iter.getID()); |
9c1e5491 | 879 | if(!txn2->txn->get(txn2->db->dbi, co(di.id, g_rootdnsname, QType::SOA), val)) { |
1293197a KM |
880 | DNSResourceRecord rr; |
881 | serFromString(val.get<string_view>(), rr); | |
882 | ||
883 | if(rr.content.size() >= 5 * sizeof(uint32_t)) { | |
884 | uint32_t serial = *reinterpret_cast<uint32_t*>(&rr.content[rr.content.size() - (5 * sizeof(uint32_t))]); | |
885 | di.serial = ntohl(serial); | |
886 | } | |
887 | } else if(!include_disabled) { | |
888 | continue; | |
6a40fc00 | 889 | } |
1293197a | 890 | domains->push_back(di); |
6a40fc00 | 891 | } |
892 | } | |
893 | ||
894 | void LMDBBackend::getUnfreshSlaveInfos(vector<DomainInfo>* domains) | |
895 | { | |
896 | // cout<<"Start of getUnfreshSlaveInfos"<<endl; | |
897 | domains->clear(); | |
898 | auto txn = d_tdomains->getROTransaction(); | |
899 | ||
900 | time_t now = time(0); | |
901 | for(auto iter = txn.begin(); iter != txn.end(); ++iter) { | |
902 | if(iter->kind != DomainInfo::Slave) | |
903 | continue; | |
904 | ||
905 | auto txn2 = getRecordsROTransaction(iter.getID()); | |
906 | compoundOrdername co; | |
907 | MDBOutVal val; | |
908 | uint32_t serial = 0; | |
9c1e5491 | 909 | if(!txn2->txn->get(txn2->db->dbi, co(iter.getID(), g_rootdnsname, QType::SOA), val)) { |
6a40fc00 | 910 | DNSResourceRecord rr; |
911 | serFromString(val.get<string_view>(), rr); | |
e35f9e46 OM |
912 | struct soatimes st; |
913 | ||
6a40fc00 | 914 | memcpy(&st, &rr.content[rr.content.size()-sizeof(soatimes)], sizeof(soatimes)); |
915 | ||
916 | if((time_t)(iter->last_check + ntohl(st.refresh)) >= now) { // still fresh | |
917 | continue; // try next domain | |
918 | } | |
919 | // cout << di.last_check <<" + " <<sdata.refresh<<" > = " << now << "\n"; | |
920 | serial = ntohl(st.serial); | |
921 | } | |
922 | else { | |
923 | // cout << "Could not find SOA for "<<iter->zone<<" with id "<<iter.getID()<<endl; | |
924 | serial=0; | |
925 | } | |
926 | DomainInfo di=*iter; | |
927 | di.id = iter.getID(); | |
928 | di.serial = serial; | |
929 | ||
930 | domains->push_back(di); | |
931 | } | |
932 | // cout<<"END of getUnfreshSlaveInfos"<<endl; | |
933 | } | |
934 | ||
935 | bool LMDBBackend::getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string> >& meta) | |
936 | { | |
937 | meta.clear(); | |
938 | auto txn = d_tmeta->getROTransaction(); | |
939 | auto range = txn.equal_range<0>(name); | |
940 | ||
941 | for(auto& iter = range.first; iter != range.second; ++iter) { | |
942 | meta[iter->key].push_back(iter->value); | |
943 | } | |
944 | return true; | |
945 | } | |
946 | ||
947 | bool LMDBBackend::setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta) | |
948 | { | |
949 | auto txn = d_tmeta->getRWTransaction(); | |
950 | ||
951 | auto range = txn.equal_range<0>(name); | |
952 | ||
953 | for(auto& iter = range.first; iter != range.second; ++iter) { | |
954 | if(iter-> key == kind) | |
955 | iter.del(); | |
956 | } | |
957 | ||
958 | for(const auto& m : meta) { | |
959 | DomainMeta dm{name, kind, m}; | |
960 | txn.put(dm); | |
961 | } | |
962 | txn.commit(); | |
963 | return true; | |
964 | ||
965 | } | |
966 | ||
967 | bool LMDBBackend::getDomainKeys(const DNSName& name, std::vector<KeyData>& keys) | |
968 | { | |
969 | auto txn = d_tkdb->getROTransaction(); | |
970 | auto range = txn.equal_range<0>(name); | |
971 | for(auto& iter = range.first; iter != range.second; ++iter) { | |
972 | KeyData kd{iter->content, iter.getID(), iter->flags, iter->active}; | |
973 | keys.push_back(kd); | |
974 | } | |
975 | ||
976 | return true; | |
977 | } | |
978 | ||
979 | bool LMDBBackend::removeDomainKey(const DNSName& name, unsigned int id) | |
980 | { | |
981 | auto txn = d_tkdb->getRWTransaction(); | |
982 | KeyDataDB kdb; | |
983 | if(txn.get(id, kdb)) { | |
984 | if(kdb.domain == name) { | |
985 | txn.del(id); | |
986 | txn.commit(); | |
987 | return true; | |
988 | } | |
989 | } | |
990 | // cout << "??? wanted to remove domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl; | |
991 | return true; | |
992 | } | |
993 | ||
994 | bool LMDBBackend::addDomainKey(const DNSName& name, const KeyData& key, int64_t& id) | |
995 | { | |
996 | auto txn = d_tkdb->getRWTransaction(); | |
997 | KeyDataDB kdb{name, key.content, key.flags, key.active}; | |
998 | id = txn.put(kdb); | |
999 | txn.commit(); | |
1000 | ||
1001 | return true; | |
1002 | } | |
1003 | ||
1004 | bool LMDBBackend::activateDomainKey(const DNSName& name, unsigned int id) | |
1005 | { | |
1006 | auto txn = d_tkdb->getRWTransaction(); | |
1007 | KeyDataDB kdb; | |
1008 | if(txn.get(id, kdb)) { | |
1009 | if(kdb.domain == name) { | |
e35f9e46 | 1010 | txn.modify(id, [](KeyDataDB& kdbarg) |
6a40fc00 | 1011 | { |
e35f9e46 | 1012 | kdbarg.active = true; |
6a40fc00 | 1013 | }); |
1014 | txn.commit(); | |
1015 | return true; | |
1016 | } | |
1017 | } | |
1018 | ||
1019 | // cout << "??? wanted to activate domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl; | |
1020 | return true; | |
1021 | } | |
1022 | ||
1023 | bool LMDBBackend::deactivateDomainKey(const DNSName& name, unsigned int id) | |
1024 | { | |
1025 | auto txn = d_tkdb->getRWTransaction(); | |
1026 | KeyDataDB kdb; | |
1027 | if(txn.get(id, kdb)) { | |
1028 | if(kdb.domain == name) { | |
e35f9e46 | 1029 | txn.modify(id, [](KeyDataDB& kdbarg) |
6a40fc00 | 1030 | { |
e35f9e46 | 1031 | kdbarg.active = false; |
6a40fc00 | 1032 | }); |
1033 | txn.commit(); | |
1034 | return true; | |
1035 | } | |
1036 | } | |
1037 | // cout << "??? wanted to activate domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl; | |
1038 | return true; | |
1039 | } | |
1040 | ||
1041 | bool LMDBBackend::getBeforeAndAfterNamesAbsolute(uint32_t id, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after) | |
1042 | { | |
1043 | // cout << __PRETTY_FUNCTION__<< ": "<<id <<", "<<qname << " " << unhashed<<endl; | |
1044 | ||
1045 | DomainInfo di; | |
1046 | if(!d_tdomains->getROTransaction().get(id, di)) { | |
1047 | // domain does not exist, tough luck | |
1048 | return false; | |
1049 | } | |
1050 | // cout <<"Zone: "<<di.zone<<endl; | |
1051 | ||
1052 | compoundOrdername co; | |
1053 | auto txn = getRecordsROTransaction(id); | |
1054 | ||
9c1e5491 | 1055 | auto cursor = txn->txn->getCursor(txn->db->dbi); |
6a40fc00 | 1056 | MDBOutVal key, val; |
1057 | ||
1058 | DNSResourceRecord rr; | |
1059 | ||
1060 | string matchkey = co(id, qname, QType::NSEC3); | |
1061 | if(cursor.lower_bound(matchkey, key, val)) { | |
1062 | // this is beyond the end of the database | |
1063 | // cout << "Beyond end of database!" << endl; | |
1064 | cursor.last(key, val); | |
1065 | ||
1066 | for(;;) { | |
1067 | if(co.getDomainID(key.get<StringView>()) != id) { | |
1068 | //cout<<"Last record also not part of this zone!"<<endl; | |
1069 | // this implies something is wrong in the database, nothing we can do | |
1070 | return false; | |
1071 | } | |
1072 | ||
1073 | if(co.getQType(key.get<StringView>()) == QType::NSEC3) { | |
1074 | serFromString(val.get<StringView>(), rr); | |
1075 | if(!rr.ttl) // the kind of NSEC3 we need | |
1076 | break; | |
1077 | } | |
1078 | if(cursor.prev(key, val)) { | |
1079 | // hit beginning of database, again means something is wrong with it | |
1080 | return false; | |
1081 | } | |
1082 | } | |
1083 | before = co.getQName(key.get<StringView>()); | |
1084 | unhashed = DNSName(rr.content.c_str(), rr.content.size(), 0, false) + di.zone; | |
1085 | ||
1086 | // now to find after .. at the beginning of the zone | |
1087 | if(cursor.lower_bound(co(id), key, val)) { | |
1088 | // cout<<"hit end of zone find when we shouldn't"<<endl; | |
1089 | return false; | |
1090 | } | |
1091 | for(;;) { | |
1092 | if(co.getQType(key.get<StringView>()) == QType::NSEC3) { | |
1093 | serFromString(val.get<StringView>(), rr); | |
1094 | if(!rr.ttl) | |
1095 | break; | |
1096 | } | |
1097 | ||
1098 | if(cursor.next(key, val) || co.getDomainID(key.get<StringView>()) != id) { | |
1099 | // cout<<"hit end of zone or database when we shouldn't"<<endl; | |
1100 | return false; | |
1101 | } | |
1102 | } | |
1103 | after = co.getQName(key.get<StringView>()); | |
1104 | // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl; | |
1105 | return true; | |
1106 | } | |
1107 | ||
1108 | // cout<<"Ended up at "<<co.getQName(key.get<StringView>()) <<endl; | |
1109 | ||
1110 | before = co.getQName(key.get<StringView>()); | |
1111 | if(before == qname) { | |
1112 | // cout << "Ended up on exact right node" << endl; | |
1113 | before = co.getQName(key.get<StringView>()); | |
1114 | // unhashed should be correct now, maybe check? | |
1115 | if(cursor.next(key, val)) { | |
1116 | // xxx should find first hash now | |
1117 | ||
1118 | if(cursor.lower_bound(co(id), key, val)) { | |
1119 | // cout<<"hit end of zone find when we shouldn't for id "<<id<< __LINE__<<endl; | |
1120 | return false; | |
1121 | } | |
1122 | for(;;) { | |
1123 | if(co.getQType(key.get<StringView>()) == QType::NSEC3) { | |
1124 | serFromString(val.get<StringView>(), rr); | |
1125 | if(!rr.ttl) | |
1126 | break; | |
1127 | } | |
1128 | ||
1129 | if(cursor.next(key, val) || co.getDomainID(key.get<StringView>()) != id) { | |
1130 | // cout<<"hit end of zone or database when we shouldn't" << __LINE__<<endl; | |
1131 | return false; | |
1132 | } | |
1133 | } | |
1134 | after = co.getQName(key.get<StringView>()); | |
1135 | // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl; | |
1136 | return true; | |
1137 | } | |
1138 | } | |
1139 | else { | |
1140 | // cout <<"Going backwards to find 'before'"<<endl; | |
1141 | int count=0; | |
1142 | for(;;) { | |
1143 | if(co.getQName(key.get<StringView>()).canonCompare(qname) && co.getQType(key.get<StringView>()) == QType::NSEC3) { | |
1144 | // cout<<"Potentially stopping traverse at "<< co.getQName(key.get<StringView>()) <<", " << (co.getQName(key.get<StringView>()).canonCompare(qname))<<endl; | |
1145 | // cout<<"qname = "<<qname<<endl; | |
1146 | // cout<<"here = "<<co.getQName(key.get<StringView>())<<endl; | |
1147 | serFromString(val.get<StringView>(), rr); | |
1148 | if(!rr.ttl) | |
1149 | break; | |
1150 | } | |
1151 | ||
1152 | if(cursor.prev(key, val) || co.getDomainID(key.get<StringView>()) != id ) { | |
1153 | // cout <<"XXX Hit *beginning* of zone or database"<<endl; | |
1154 | // this can happen, must deal with it | |
1155 | // should now find the last hash of the zone | |
1156 | ||
1157 | if(cursor.lower_bound(co(id+1), key, val)) { | |
1158 | // cout << "Could not find the next higher zone, going to the end of the database then"<<endl; | |
1159 | cursor.last(key, val); | |
1160 | } | |
1161 | else | |
1162 | cursor.prev(key, val); | |
1163 | ||
1164 | for(;;) { | |
1165 | if(co.getDomainID(key.get<StringView>()) != id) { | |
1166 | //cout<<"Last record also not part of this zone!"<<endl; | |
1167 | // this implies something is wrong in the database, nothing we can do | |
1168 | return false; | |
1169 | } | |
1170 | ||
1171 | if(co.getQType(key.get<StringView>()) == QType::NSEC3) { | |
1172 | serFromString(val.get<StringView>(), rr); | |
1173 | if(!rr.ttl) // the kind of NSEC3 we need | |
1174 | break; | |
1175 | } | |
1176 | if(cursor.prev(key, val)) { | |
1177 | // hit beginning of database, again means something is wrong with it | |
1178 | return false; | |
1179 | } | |
1180 | } | |
1181 | before = co.getQName(key.get<StringView>()); | |
1182 | unhashed = DNSName(rr.content.c_str(), rr.content.size(), 0, false) + di.zone; | |
1183 | // cout <<"Should still find 'after'!"<<endl; | |
1184 | // for 'after', we need to find the first hash of this zone | |
1185 | ||
1186 | if(cursor.lower_bound(co(id), key, val)) { | |
1187 | // cout<<"hit end of zone find when we shouldn't"<<endl; | |
1188 | // means database is wrong, nothing we can do | |
1189 | return false; | |
1190 | } | |
1191 | for(;;) { | |
1192 | if(co.getQType(key.get<StringView>()) == QType::NSEC3) { | |
1193 | serFromString(val.get<StringView>(), rr); | |
1194 | if(!rr.ttl) | |
1195 | break; | |
1196 | } | |
1197 | ||
1198 | if(cursor.next(key, val)) { | |
1199 | // means database is wrong, nothing we can do | |
1200 | // cout<<"hit end of zone when we shouldn't 2"<<endl; | |
1201 | return false; | |
1202 | } | |
1203 | } | |
1204 | after = co.getQName(key.get<StringView>()); | |
1205 | ||
1206 | ||
1207 | // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl; | |
1208 | return true; | |
1209 | } | |
1210 | ++count; | |
1211 | } | |
1212 | before = co.getQName(key.get<StringView>()); | |
1213 | unhashed = DNSName(rr.content.c_str(), rr.content.size(), 0, false) + di.zone; | |
1214 | // cout<<"Went backwards, found "<<before<<endl; | |
1215 | // return us to starting point | |
1216 | while(count--) | |
1217 | cursor.next(key, val); | |
1218 | } | |
1219 | // cout<<"Now going forward"<<endl; | |
1220 | for(int count = 0 ;;++count) { | |
1221 | if((count && cursor.next(key, val)) || co.getDomainID(key.get<StringView>()) != id ) { | |
1222 | // cout <<"Hit end of database or zone, finding first hash then in zone "<<id<<endl; | |
1223 | if(cursor.lower_bound(co(id), key, val)) { | |
1224 | // cout<<"hit end of zone find when we shouldn't"<<endl; | |
1225 | // means database is wrong, nothing we can do | |
1226 | return false; | |
1227 | } | |
1228 | for(;;) { | |
1229 | if(co.getQType(key.get<StringView>()) == QType::NSEC3) { | |
1230 | serFromString(val.get<StringView>(), rr); | |
1231 | if(!rr.ttl) | |
1232 | break; | |
1233 | } | |
1234 | ||
1235 | if(cursor.next(key, val)) { | |
1236 | // means database is wrong, nothing we can do | |
1237 | // cout<<"hit end of zone when we shouldn't 2"<<endl; | |
1238 | return false; | |
1239 | } | |
1240 | // cout << "Next.. "<<endl; | |
1241 | } | |
1242 | after = co.getQName(key.get<StringView>()); | |
1243 | ||
1244 | // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl; | |
1245 | return true; | |
1246 | } | |
1247 | ||
1248 | // cout<<"After "<<co.getQName(key.get<StringView>()) <<endl; | |
1249 | if(co.getQType(key.get<StringView>()) == QType::NSEC3) { | |
1250 | serFromString(val.get<StringView>(), rr); | |
1251 | if(!rr.ttl) { | |
1252 | break; | |
1253 | } | |
1254 | } | |
1255 | } | |
1256 | after = co.getQName(key.get<StringView>()); | |
1257 | // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl; | |
1258 | return true; | |
1259 | } | |
1260 | ||
1261 | bool LMDBBackend::getBeforeAndAfterNames(uint32_t id, const DNSName& zonenameU, const DNSName& qname, DNSName& before, DNSName& after) | |
1262 | { | |
1263 | DNSName zonename = zonenameU.makeLowerCase(); | |
1264 | // cout << __PRETTY_FUNCTION__<< ": "<<id <<", "<<zonename << ", '"<<qname<<"'"<<endl; | |
1265 | ||
1266 | auto txn = getRecordsROTransaction(id); | |
1267 | compoundOrdername co; | |
1268 | DNSName qname2 = qname.makeRelative(zonename); | |
1269 | string matchkey=co(id,qname2); | |
9c1e5491 | 1270 | auto cursor = txn->txn->getCursor(txn->db->dbi); |
6a40fc00 | 1271 | MDBOutVal key, val; |
1272 | // cout<<"Lower_bound for "<<qname2<<endl; | |
1273 | if(cursor.lower_bound(matchkey, key, val)) { | |
1274 | // cout << "Hit end of database, bummer"<<endl; | |
1275 | cursor.last(key, val); | |
1276 | if(co.getDomainID(key.get<string_view>()) == id) { | |
1277 | before = co.getQName(key.get<string_view>()) + zonename; | |
1278 | after = zonename; | |
1279 | } | |
1280 | // else | |
1281 | // cout << "We were at end of database, but this zone is not there?!"<<endl; | |
1282 | return true; | |
1283 | } | |
1284 | // cout<<"Cursor is at "<<co.getQName(key.get<string_view>()) <<", in zone id "<<co.getDomainID(key.get<string_view>())<< endl; | |
1285 | ||
1286 | if(co.getQType(key.get<string_view>()).getCode() && co.getDomainID(key.get<string_view>()) ==id && co.getQName(key.get<string_view>()) == qname2) { // don't match ENTs | |
1287 | // cout << "Had an exact match!"<<endl; | |
1288 | before = qname2 + zonename; | |
1289 | int rc; | |
1290 | for(;;) { | |
1291 | rc=cursor.next(key, val); | |
1292 | if(rc) break; | |
1293 | ||
1294 | if(co.getDomainID(key.get<string_view>()) == id && key.get<StringView>().rfind(matchkey, 0)==0) | |
1295 | continue; | |
1296 | DNSResourceRecord rr; | |
1297 | serFromString(val.get<StringView>(), rr); | |
1298 | if(co.getQType(key.get<string_view>()).getCode() && (rr.auth || co.getQType(key.get<string_view>()).getCode() == QType::NS)) | |
1299 | break; | |
1300 | } | |
1301 | if(rc || co.getDomainID(key.get<string_view>()) != id) { | |
1302 | // cout << "We hit the end of the zone or database. 'after' is apex" << endl; | |
1303 | after=zonename; | |
1304 | return false; | |
1305 | } | |
1306 | after = co.getQName(key.get<string_view>()) + zonename; | |
1307 | return true; | |
1308 | } | |
1309 | ||
1310 | ||
1311 | if(co.getDomainID(key.get<string_view>()) != id) { | |
1312 | // cout << "Ended up in next zone, 'after' is zonename" <<endl; | |
1313 | after = zonename; | |
1314 | // cout << "Now hunting for previous" << endl; | |
1315 | int rc; | |
1316 | for(;;) { | |
1317 | rc=cursor.prev(key, val); | |
1318 | if(rc) { | |
1319 | // cout<<"Reversed into zone, but got not found from lmdb" <<endl; | |
1320 | return false; | |
1321 | } | |
1322 | ||
1323 | if(co.getDomainID(key.get<string_view>()) != id) { | |
1324 | // cout<<"Reversed into zone, but found wrong zone id " << co.getDomainID(key.get<string_view>()) << " != "<<id<<endl; | |
1325 | // "this can't happen" | |
1326 | return false; | |
1327 | } | |
1328 | DNSResourceRecord rr; | |
1329 | serFromString(val.get<StringView>(), rr); | |
1330 | if(co.getQType(key.get<string_view>()).getCode() && (rr.auth || co.getQType(key.get<string_view>()).getCode() == QType::NS)) | |
1331 | break; | |
1332 | } | |
1333 | ||
1334 | before = co.getQName(key.get<string_view>()) + zonename; | |
1335 | // cout<<"Found: "<< before<<endl; | |
1336 | return true; | |
1337 | } | |
1338 | ||
1339 | // cout <<"We ended up after "<<qname<<", on "<<co.getQName(key.get<string_view>())<<endl; | |
1340 | ||
1341 | int skips = 0; | |
1342 | for(; ;) { | |
1343 | DNSResourceRecord rr; | |
1344 | serFromString(val.get<StringView>(), rr); | |
1345 | if(co.getQType(key.get<string_view>()).getCode() && (rr.auth || co.getQType(key.get<string_view>()).getCode() == QType::NS)) { | |
1346 | after = co.getQName(key.get<string_view>()) + zonename; | |
1347 | // cout <<"Found auth ("<<rr.auth<<") or an NS record "<<after<<", type: "<<co.getQType(key.get<string_view>()).getName()<<", ttl = "<<rr.ttl<<endl; | |
1348 | // cout << makeHexDump(val.get<string>()) << endl; | |
1349 | break; | |
1350 | } | |
1351 | // cout <<" oops, " << co.getQName(key.get<string_view>()) << " was not auth "<<rr.auth<< " type=" << rr.qtype.getName()<<" or NS, so need to skip ahead a bit more" << endl; | |
1352 | int rc = cursor.next(key, val); | |
1353 | if(!rc) | |
1354 | ++skips; | |
1355 | if(rc || co.getDomainID(key.get<string_view>()) != id ) { | |
1356 | // cout << " oops, hit end of database or zone. This means after is apex" <<endl; | |
1357 | after = zonename; | |
1358 | break; | |
1359 | } | |
1360 | } | |
1361 | // go back to where we were | |
1362 | while(skips--) | |
1363 | cursor.prev(key,val); | |
1364 | ||
1365 | for(;;) { | |
1366 | int rc = cursor.prev(key, val); | |
1367 | if(rc || co.getDomainID(key.get<string_view>()) != id) { | |
1368 | // XX I don't think this case can happen | |
1369 | // cout << "We hit the beginning of the zone or database.. now what" << endl; | |
1370 | return false; | |
1371 | } | |
1372 | before = co.getQName(key.get<string_view>()) + zonename; | |
1373 | DNSResourceRecord rr; | |
1374 | serFromString(val.get<string_view>(), rr); | |
1375 | // cout<<"And before to "<<before<<", auth = "<<rr.auth<<endl; | |
1376 | if(co.getQType(key.get<string_view>()).getCode() && (rr.auth || co.getQType(key.get<string_view>()) == QType::NS)) | |
1377 | break; | |
1378 | // cout << "Oops, that was wrong, go back one more"<<endl; | |
1379 | } | |
1380 | ||
1381 | return true; | |
1382 | ||
1383 | } | |
1384 | ||
1385 | bool LMDBBackend::updateDNSSECOrderNameAndAuth(uint32_t domain_id, const DNSName& qname, const DNSName& ordername, bool auth, const uint16_t qtype) | |
1386 | { | |
1387 | // cout << __PRETTY_FUNCTION__<< ": "<< domain_id <<", '"<<qname <<"', '"<<ordername<<"', "<<auth<< ", " << qtype << endl; | |
1388 | shared_ptr<RecordsRWTransaction> txn; | |
1389 | bool needCommit = false; | |
1390 | if(d_rwtxn && d_transactiondomainid==domain_id) { | |
1391 | txn = d_rwtxn; | |
1392 | // cout<<"Reusing open transaction"<<endl; | |
1393 | } | |
1394 | else { | |
1395 | // cout<<"Making a new RW txn for " << __PRETTY_FUNCTION__ <<endl; | |
1396 | txn = getRecordsRWTransaction(domain_id); | |
1397 | needCommit = true; | |
1398 | } | |
1399 | ||
1400 | DomainInfo di; | |
1401 | if(!d_tdomains->getROTransaction().get(domain_id, di)) { | |
1402 | // cout<<"Could not find domain_id "<<domain_id <<endl; | |
1403 | return false; | |
1404 | } | |
1405 | ||
1406 | DNSName rel = qname.makeRelative(di.zone); | |
1407 | ||
1408 | compoundOrdername co; | |
1409 | string matchkey = co(domain_id, rel); | |
1410 | ||
9c1e5491 | 1411 | auto cursor = txn->txn->getCursor(txn->db->dbi); |
6a40fc00 | 1412 | MDBOutVal key, val; |
1413 | if(cursor.lower_bound(matchkey, key, val)) { | |
1414 | // cout << "Could not find anything"<<endl; | |
1415 | return false; | |
1416 | } | |
1417 | ||
1418 | bool hasOrderName = !ordername.empty(); | |
1419 | bool needNSEC3 = hasOrderName; | |
1420 | ||
1421 | for(; key.get<StringView>().rfind(matchkey,0) == 0; ) { | |
1422 | DNSResourceRecord rr; | |
1423 | rr.qtype = co.getQType(key.get<StringView>()); | |
1424 | ||
1425 | if(rr.qtype != QType::NSEC3) { | |
1426 | serFromString(val.get<StringView>(), rr); | |
1427 | if(!needNSEC3 && qtype != QType::ANY) { | |
1428 | needNSEC3 = (rr.disabled && QType(qtype) != rr.qtype); | |
1429 | } | |
1430 | ||
1431 | if((qtype == QType::ANY || QType(qtype) == rr.qtype) && (rr.disabled != hasOrderName || rr.auth != auth)) { | |
1432 | rr.auth = auth; | |
1433 | rr.disabled = hasOrderName; | |
1434 | string repl = serToString(rr); | |
1435 | cursor.put(key, repl); | |
1436 | } | |
1437 | } | |
1438 | ||
1439 | if(cursor.next(key, val)) | |
1440 | break; | |
1441 | } | |
1442 | ||
1443 | bool del = false; | |
1444 | DNSResourceRecord rr; | |
1445 | matchkey = co(domain_id,rel,QType::NSEC3); | |
9c1e5491 | 1446 | if(!txn->txn->get(txn->db->dbi, matchkey, val)) { |
6a40fc00 | 1447 | serFromString(val.get<string_view>(), rr); |
1448 | ||
1449 | if(needNSEC3) { | |
1450 | if(hasOrderName && rr.content != ordername.toDNSStringLC()) { | |
1451 | del = true; | |
1452 | } | |
1453 | } else { | |
1454 | del = true; | |
1455 | } | |
1456 | if(del) { | |
9c1e5491 PL |
1457 | txn->txn->del(txn->db->dbi, co(domain_id, DNSName(rr.content.c_str(), rr.content.size(), 0, false), QType::NSEC3)); |
1458 | txn->txn->del(txn->db->dbi, matchkey); | |
6a40fc00 | 1459 | } |
1460 | } else { | |
1461 | del = true; | |
1462 | } | |
1463 | ||
1464 | if(hasOrderName && del) { | |
1465 | matchkey = co(domain_id,rel,QType::NSEC3); | |
1466 | ||
1467 | rr.ttl=0; | |
1468 | rr.auth=0; | |
1469 | rr.content=rel.toDNSStringLC(); | |
1470 | ||
1471 | string str = serToString(rr); | |
9c1e5491 | 1472 | txn->txn->put(txn->db->dbi, co(domain_id,ordername,QType::NSEC3), str); |
6a40fc00 | 1473 | rr.ttl = 1; |
1474 | rr.content = ordername.toDNSStringLC(); | |
1475 | str = serToString(rr); | |
9c1e5491 | 1476 | txn->txn->put(txn->db->dbi, matchkey, str); // 2 |
6a40fc00 | 1477 | } |
1478 | ||
1479 | if(needCommit) | |
9c1e5491 | 1480 | txn->txn->commit(); |
6a40fc00 | 1481 | return false; |
1482 | } | |
1483 | ||
1484 | bool LMDBBackend::updateEmptyNonTerminals(uint32_t domain_id, set<DNSName>& insert, set<DNSName>& erase, bool remove) | |
1485 | { | |
1486 | // cout << __PRETTY_FUNCTION__<< ": "<< domain_id << ", insert.size() "<<insert.size()<<", "<<erase.size()<<", " <<remove<<endl; | |
1487 | ||
1488 | bool needCommit = false; | |
1489 | shared_ptr<RecordsRWTransaction> txn; | |
1490 | if(d_rwtxn && d_transactiondomainid == domain_id) { | |
1491 | txn = d_rwtxn; | |
1492 | // cout<<"Reusing open transaction"<<endl; | |
1493 | } | |
1494 | else { | |
1495 | // cout<<"Making a new RW txn for delete domain"<<endl; | |
1496 | txn = getRecordsRWTransaction(domain_id); | |
1497 | needCommit = true; | |
1498 | } | |
1499 | ||
1500 | ||
1501 | // if remove is set, all ENTs should be removed & nothing else should be done | |
1502 | if(remove) { | |
1503 | deleteDomainRecords(*txn, domain_id, 0); | |
1504 | } | |
1505 | else { | |
1506 | DomainInfo di; | |
1507 | auto rotxn = d_tdomains->getROTransaction(); | |
1508 | if(!rotxn.get(domain_id, di)) { | |
1509 | // cout <<"No such domain with id "<<domain_id<<endl; | |
1510 | return false; | |
1511 | } | |
1512 | compoundOrdername co; | |
1513 | for(const auto& n : insert) { | |
1514 | DNSResourceRecord rr; | |
1515 | rr.qname = n.makeRelative(di.zone); | |
1516 | rr.ttl = 0; | |
1517 | rr.auth = true; | |
1518 | ||
1519 | std::string ser = serToString(rr); | |
1520 | ||
9c1e5491 | 1521 | txn->txn->put(txn->db->dbi, co(domain_id, rr.qname, 0), ser); |
6a40fc00 | 1522 | |
1523 | DNSResourceRecord rr2; | |
1524 | serFromString(ser, rr2); | |
1525 | ||
1526 | // cout <<" +"<<n<<endl; | |
1527 | } | |
1528 | for(auto n : erase) { | |
1529 | // cout <<" -"<<n<<endl; | |
1530 | n.makeUsRelative(di.zone); | |
9c1e5491 | 1531 | txn->txn->del(txn->db->dbi, co(domain_id, n, 0)); |
6a40fc00 | 1532 | } |
1533 | } | |
1534 | if(needCommit) | |
9c1e5491 | 1535 | txn->txn->commit(); |
6a40fc00 | 1536 | return false; |
1537 | } | |
1538 | ||
1539 | /* TSIG */ | |
1540 | bool LMDBBackend::getTSIGKey(const DNSName& name, DNSName* algorithm, string* content) | |
1541 | { | |
1542 | auto txn = d_ttsig->getROTransaction(); | |
1543 | ||
1544 | TSIGKey tk; | |
1545 | if(!txn.get<0>(name, tk)) | |
1546 | return false; | |
1547 | if(algorithm) | |
1548 | *algorithm = tk.algorithm; | |
1549 | if(content) | |
1550 | *content = tk.key; | |
1551 | return true; | |
1552 | ||
1553 | } | |
1554 | // this deletes an old key if it has the same algorithm | |
1555 | bool LMDBBackend::setTSIGKey(const DNSName& name, const DNSName& algorithm, const string& content) | |
1556 | { | |
1557 | auto txn = d_ttsig->getRWTransaction(); | |
1558 | ||
1559 | for(auto range = txn.equal_range<0>(name); range.first != range.second; ++range.first) { | |
1560 | if(range.first->algorithm == algorithm) | |
1561 | range.first.del(); | |
1562 | } | |
1563 | ||
1564 | TSIGKey tk; | |
1565 | tk.name = name; | |
1566 | tk.algorithm = algorithm; | |
1567 | tk.key=content; | |
1568 | ||
1569 | txn.put(tk); | |
1570 | txn.commit(); | |
1571 | ||
1572 | return true; | |
1573 | } | |
1574 | bool LMDBBackend::deleteTSIGKey(const DNSName& name) | |
1575 | { | |
1576 | auto txn = d_ttsig->getRWTransaction(); | |
1577 | TSIGKey tk; | |
1578 | ||
1579 | for(auto range = txn.equal_range<0>(name); range.first != range.second; ++range.first) { | |
1580 | range.first.del(); | |
1581 | } | |
1582 | txn.commit(); | |
1583 | return true; | |
1584 | } | |
1585 | bool LMDBBackend::getTSIGKeys(std::vector< struct TSIGKey > &keys) | |
1586 | { | |
1587 | auto txn = d_ttsig->getROTransaction(); | |
1588 | ||
1589 | keys.clear(); | |
1590 | for(auto iter = txn.begin(); iter != txn.end(); ++iter) { | |
1591 | keys.push_back(*iter); | |
1592 | } | |
1593 | return false; | |
1594 | } | |
1595 | ||
1596 | ||
1597 | ||
1598 | ||
1599 | class LMDBFactory : public BackendFactory | |
1600 | { | |
1601 | public: | |
1602 | LMDBFactory() : BackendFactory("lmdb") {} | |
1603 | void declareArguments(const string &suffix="") | |
1604 | { | |
1605 | declare(suffix,"filename","Filename for lmdb","./pdns.lmdb"); | |
83daa127 | 1606 | declare(suffix,"sync-mode","Synchronisation mode: nosync, nometasync, mapasync, sync","mapasync"); |
5d022657 | 1607 | // there just is no room for more on 32 bit |
1608 | declare(suffix,"shards","Records database will be split into this number of shards", (sizeof(long) == 4) ? "2" : "64"); | |
6a40fc00 | 1609 | } |
1610 | DNSBackend *make(const string &suffix="") | |
1611 | { | |
1612 | return new LMDBBackend(suffix); | |
1613 | } | |
1614 | }; | |
1615 | ||
1616 | ||
1617 | ||
1618 | ||
1619 | /* THIRD PART */ | |
1620 | ||
1621 | class LMDBLoader | |
1622 | { | |
1623 | public: | |
1624 | LMDBLoader() | |
1625 | { | |
1626 | BackendMakers().report(new LMDBFactory); | |
1627 | g_log << Logger::Info << "[lmdbbackend] This is the lmdb backend version " VERSION | |
1628 | #ifndef REPRODUCIBLE | |
1629 | << " (" __DATE__ " " __TIME__ ")" | |
1630 | #endif | |
1631 | << " reporting" << endl; | |
1632 | } | |
1633 | }; | |
1634 | ||
1635 | static LMDBLoader randomLoader; |