• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 20127514965

08 Dec 2025 01:47PM UTC coverage: 63.199% (+0.07%) from 63.127%
20127514965

push

github

web-flow
Merge pull request #16600 from miodvallat/backport-16589-to-auth-4.9.x

auth 4.9: backport "allow finer-grained rrset changes through the API"

13041 of 27384 branches covered (47.62%)

Branch coverage included in aggregate %.

237 of 258 new or added lines in 1 file covered. (91.86%)

16 existing lines in 3 files now uncovered.

41995 of 59699 relevant lines covered (70.34%)

7159263.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.83
/modules/lmdbbackend/lmdbbackend.cc
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

23
#include "ext/lmdb-safe/lmdb-safe.hh"
24
#include <lmdb.h>
25
#include <stdexcept>
26
#include <utility>
27
#ifdef HAVE_CONFIG_H
28
#include "config.h"
29
#endif
30
#include "pdns/utility.hh"
31
#include "pdns/dnsbackend.hh"
32
#include "pdns/dns.hh"
33
#include "pdns/dnspacket.hh"
34
#include "pdns/base32.hh"
35
#include "pdns/dnssecinfra.hh"
36
#include "pdns/pdnsexception.hh"
37
#include "pdns/logger.hh"
38
#include "pdns/version.hh"
39
#include "pdns/arguments.hh"
40
#include "pdns/lock.hh"
41
#include "pdns/uuid-utils.hh"
42
#include <boost/archive/binary_oarchive.hpp>
43
#include <boost/archive/binary_iarchive.hpp>
44
#include <boost/serialization/vector.hpp>
45
#include <boost/serialization/string.hpp>
46
#include <boost/serialization/utility.hpp>
47
#include <boost/uuid/uuid_serialize.hpp>
48

49
#include <boost/iostreams/device/back_inserter.hpp>
50

51
#ifdef HAVE_SYSTEMD
52
#include <systemd/sd-daemon.h>
53
#endif
54

55
#include <stdio.h>
56
#include <unistd.h>
57

58
#include "lmdbbackend.hh"
59

60
#define SCHEMAVERSION 5
1,302✔
61

62
// List the class version here. Default is 0
63
BOOST_CLASS_VERSION(LMDBBackend::KeyDataDB, 1)
64
BOOST_CLASS_VERSION(DomainInfo, 1)
65

66
static bool s_first = true;
67
static int s_shards = 0;
68
static std::mutex s_lmdbStartupLock;
69

70
std::pair<uint32_t, uint32_t> LMDBBackend::getSchemaVersionAndShards(std::string& filename)
71
{
653✔
72
  // cerr << "getting schema version for path " << filename << endl;
73

74
  uint32_t schemaversion;
653✔
75

76
  int rc;
653✔
77
  MDB_env* env = nullptr;
653✔
78

79
  if ((rc = mdb_env_create(&env)) != 0) {
653!
80
    throw std::runtime_error("mdb_env_create failed");
×
81
  }
×
82

83
  if ((rc = mdb_env_set_mapsize(env, 0)) != 0) {
653!
84
    throw std::runtime_error("mdb_env_set_mapsize failed");
×
85
  }
×
86

87
  if ((rc = mdb_env_set_maxdbs(env, 20)) != 0) { // we need 17: 1 {"pdns"} + 4 {"domains", "keydata", "tsig", "metadata"} * 2 {v4, v5} * 2 {main, index in _0}
653!
88
    mdb_env_close(env);
×
89
    throw std::runtime_error("mdb_env_set_maxdbs failed");
×
90
  }
×
91

92
  if ((rc = mdb_env_open(env, filename.c_str(), MDB_NOSUBDIR | MDB_RDONLY, 0600)) != 0) {
653✔
93
    if (rc == ENOENT) {
14!
94
      // we don't have a database yet! report schema 0, with 0 shards
95
      return {0u, 0u};
14✔
96
    }
14✔
97
    mdb_env_close(env);
×
98
    throw std::runtime_error("mdb_env_open failed");
×
99
  }
14✔
100

101
  MDB_txn* txn = nullptr;
639✔
102

103
  if ((rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn)) != 0) {
639!
104
    mdb_env_close(env);
×
105
    throw std::runtime_error("mdb_txn_begin failed");
×
106
  }
×
107

108
  MDB_dbi dbi;
639✔
109

110
  if ((rc = mdb_dbi_open(txn, "pdns", 0, &dbi)) != 0) {
639!
111
    if (rc == MDB_NOTFOUND) {
×
112
      // this means nothing has been inited yet
113
      // we pretend this means 5
114
      mdb_txn_abort(txn);
×
115
      mdb_env_close(env);
×
116
      return {5u, 0u};
×
117
    }
×
118
    mdb_txn_abort(txn);
×
119
    mdb_env_close(env);
×
120
    throw std::runtime_error("mdb_dbi_open failed");
×
121
  }
×
122

123
  MDB_val key, data;
639✔
124

125
  key.mv_data = (char*)"schemaversion";
639✔
126
  key.mv_size = strlen((char*)key.mv_data);
639✔
127

128
  if ((rc = mdb_get(txn, dbi, &key, &data)) != 0) {
639!
129
    if (rc == MDB_NOTFOUND) {
×
130
      // this means nothing has been inited yet
131
      // we pretend this means 5
132
      mdb_txn_abort(txn);
×
133
      mdb_env_close(env);
×
134
      return {5u, 0u};
×
135
    }
×
136

137
    throw std::runtime_error("mdb_get pdns.schemaversion failed");
×
138
  }
×
139

140
  if (data.mv_size == 4) {
639✔
141
    // schemaversion is < 5 and is stored in 32 bits, in host order
142

143
    memcpy(&schemaversion, data.mv_data, data.mv_size);
4✔
144
  }
4✔
145
  else if (data.mv_size >= LMDBLS::LS_MIN_HEADER_SIZE + sizeof(schemaversion)) {
635!
146
    // schemaversion presumably is 5, stored in 32 bits, network order, after the LS header
147

148
    // FIXME: get actual header size (including extension blocks) instead of just reading from the back
149
    // FIXME: add a test for reading schemaversion and shards (and actual data, later) when there are variably sized headers
150
    memcpy(&schemaversion, (char*)data.mv_data + data.mv_size - sizeof(schemaversion), sizeof(schemaversion));
635✔
151
    schemaversion = ntohl(schemaversion);
635✔
152
  }
635✔
153
  else {
×
154
    throw std::runtime_error("pdns.schemaversion had unexpected size");
×
155
  }
×
156

157
  uint32_t shards;
639✔
158

159
  key.mv_data = (char*)"shards";
639✔
160
  key.mv_size = strlen((char*)key.mv_data);
639✔
161

162
  if ((rc = mdb_get(txn, dbi, &key, &data)) != 0) {
639!
163
    if (rc == MDB_NOTFOUND) {
×
164
      cerr << "schemaversion was set, but shards was not. Dazed and confused, trying to exit." << endl;
×
165
      mdb_txn_abort(txn);
×
166
      mdb_env_close(env);
×
167
      exit(1);
×
168
    }
×
169

170
    throw std::runtime_error("mdb_get pdns.shards failed");
×
171
  }
×
172

173
  if (data.mv_size == 4) {
639✔
174
    // 'shards' is stored in 32 bits, in host order
175

176
    memcpy(&shards, data.mv_data, data.mv_size);
4✔
177
  }
4✔
178
  else if (data.mv_size >= LMDBLS::LS_MIN_HEADER_SIZE + sizeof(shards)) {
635!
179
    // FIXME: get actual header size (including extension blocks) instead of just reading from the back
180
    memcpy(&shards, (char*)data.mv_data + data.mv_size - sizeof(shards), sizeof(shards));
635✔
181
    shards = ntohl(shards);
635✔
182
  }
635✔
183
  else {
×
184
    throw std::runtime_error("pdns.shards had unexpected size");
×
185
  }
×
186

187
  mdb_txn_abort(txn);
639✔
188
  mdb_env_close(env);
639✔
189

190
  return {schemaversion, shards};
639✔
191
}
639✔
192

193
namespace
194
{
195
// copy sdbi to tdbi, prepending an empty LS header (24 bytes of '\0') to all values
196
void copyDBIAndAddLSHeader(MDB_txn* txn, MDB_dbi sdbi, MDB_dbi tdbi)
197
{
4✔
198
  // FIXME: clear out target dbi first
199

200
  std::string header(LMDBLS::LS_MIN_HEADER_SIZE, '\0');
4✔
201
  int rc;
4✔
202

203
  MDB_cursor* cur;
4✔
204

205
  if ((rc = mdb_cursor_open(txn, sdbi, &cur)) != 0) {
4!
206
    throw std::runtime_error("mdb_cursur_open failed");
×
207
  }
×
208

209
  MDB_val key, data;
4✔
210

211
  rc = mdb_cursor_get(cur, &key, &data, MDB_FIRST);
4✔
212

213
  while (rc == 0) {
40,448✔
214
    std::string skey(reinterpret_cast<const char*>(key.mv_data), key.mv_size);
40,444✔
215
    std::string sdata(reinterpret_cast<const char*>(data.mv_data), data.mv_size);
40,444✔
216

217
    std::string stdata = header + sdata;
40,444✔
218

219
    // cerr<<"got key="<<makeHexDump(skey)<<", data="<<makeHexDump(sdata)<<", sdata="<<makeHexDump(stdata)<<endl;
220

221
    MDB_val tkey;
40,444✔
222
    MDB_val tdata;
40,444✔
223

224
    tkey.mv_data = const_cast<char*>(skey.c_str());
40,444✔
225
    tkey.mv_size = skey.size();
40,444✔
226
    tdata.mv_data = const_cast<char*>(stdata.c_str());
40,444✔
227
    tdata.mv_size = stdata.size();
40,444✔
228

229
    if ((rc = mdb_put(txn, tdbi, &tkey, &tdata, 0)) != 0) {
40,444!
230
      throw std::runtime_error("mdb_put failed");
×
231
    }
×
232

233
    rc = mdb_cursor_get(cur, &key, &data, MDB_NEXT);
40,444✔
234
  }
40,444✔
235
  if (rc != MDB_NOTFOUND) {
4!
236
    cerr << "rc=" << rc << endl;
×
237
    throw std::runtime_error("error while iterating dbi");
×
238
  }
×
239
}
4✔
240

241
// migrated a typed DBI:
242
// 1. change keys (uint32_t) from host to network order
243
// 2. prepend empty LS header to values
244
void copyTypedDBI(MDB_txn* txn, MDB_dbi sdbi, MDB_dbi tdbi)
245
{
8✔
246
  // FIXME: clear out target dbi first
247

248
  std::string header(LMDBLS::LS_MIN_HEADER_SIZE, '\0');
8✔
249
  int rc;
8✔
250

251
  MDB_cursor* cur;
8✔
252

253
  if ((rc = mdb_cursor_open(txn, sdbi, &cur)) != 0) {
8!
254
    throw std::runtime_error("mdb_cursur_open failed");
×
255
  }
×
256

257
  MDB_val key, data;
8✔
258

259
  rc = mdb_cursor_get(cur, &key, &data, MDB_FIRST);
8✔
260

261
  while (rc == 0) {
40✔
262
    // std::string skey((char*) key.mv_data, key.mv_size);
263
    std::string sdata(reinterpret_cast<const char*>(data.mv_data), data.mv_size);
32✔
264

265
    std::string stdata = header + sdata;
32✔
266

267
    uint32_t id;
32✔
268

269
    if (key.mv_size != sizeof(uint32_t)) {
32!
270
      throw std::runtime_error("got non-uint32_t key in TypedDBI");
×
271
    }
×
272

273
    memcpy(&id, key.mv_data, sizeof(uint32_t));
32✔
274

275
    id = htonl(id);
32✔
276

277
    // cerr<<"got key="<<makeHexDump(skey)<<", data="<<makeHexDump(sdata)<<", sdata="<<makeHexDump(stdata)<<endl;
278

279
    MDB_val tkey;
32✔
280
    MDB_val tdata;
32✔
281

282
    tkey.mv_data = reinterpret_cast<char*>(&id);
32✔
283
    tkey.mv_size = sizeof(uint32_t);
32✔
284
    tdata.mv_data = const_cast<char*>(stdata.c_str());
32✔
285
    tdata.mv_size = stdata.size();
32✔
286

287
    if ((rc = mdb_put(txn, tdbi, &tkey, &tdata, 0)) != 0) {
32!
288
      throw std::runtime_error("mdb_put failed");
×
289
    }
×
290

291
    rc = mdb_cursor_get(cur, &key, &data, MDB_NEXT);
32✔
292
  }
32✔
293
  if (rc != MDB_NOTFOUND) {
8!
294
    cerr << "rc=" << rc << endl;
×
295
    throw std::runtime_error("error while iterating dbi");
×
296
  }
×
297
}
8✔
298

299
// migrating an index DBI:
300
// newkey = oldkey.len(), oldkey, htonl(oldvalue)
301
// newvalue = empty lsheader
302
void copyIndexDBI(MDB_txn* txn, MDB_dbi sdbi, MDB_dbi tdbi)
303
{
8✔
304
  // FIXME: clear out target dbi first
305

306
  std::string header(LMDBLS::LS_MIN_HEADER_SIZE, '\0');
8✔
307
  int rc;
8✔
308

309
  MDB_cursor* cur;
8✔
310

311
  if ((rc = mdb_cursor_open(txn, sdbi, &cur)) != 0) {
8!
312
    throw std::runtime_error("mdb_cursur_open failed");
×
313
  }
×
314

315
  MDB_val key, data;
8✔
316

317
  rc = mdb_cursor_get(cur, &key, &data, MDB_FIRST);
8✔
318

319
  while (rc == 0) {
40✔
320
    std::string lenprefix(sizeof(uint16_t), '\0');
32✔
321
    std::string skey((char*)key.mv_data, key.mv_size);
32✔
322

323
    uint32_t id;
32✔
324

325
    if (data.mv_size != sizeof(uint32_t)) {
32!
326
      throw std::runtime_error("got non-uint32_t ID value in IndexDBI");
×
327
    }
×
328

329
    memcpy((void*)&id, data.mv_data, sizeof(uint32_t));
32✔
330
    id = htonl(id);
32✔
331

332
    uint16_t len = htons(skey.size());
32✔
333
    memcpy((void*)lenprefix.data(), &len, sizeof(len));
32✔
334
    std::string stkey = lenprefix + skey + std::string((char*)&id, sizeof(uint32_t));
32✔
335

336
    MDB_val tkey;
32✔
337
    MDB_val tdata;
32✔
338

339
    tkey.mv_data = (char*)stkey.c_str();
32✔
340
    tkey.mv_size = stkey.size();
32✔
341
    tdata.mv_data = (char*)header.c_str();
32✔
342
    tdata.mv_size = header.size();
32✔
343

344
    if ((rc = mdb_put(txn, tdbi, &tkey, &tdata, 0)) != 0) {
32!
345
      throw std::runtime_error("mdb_put failed");
×
346
    }
×
347

348
    rc = mdb_cursor_get(cur, &key, &data, MDB_NEXT);
32✔
349
  }
32✔
350
  if (rc != MDB_NOTFOUND) {
8!
351
    throw std::runtime_error("error while iterating dbi");
×
352
  }
×
353
}
8✔
354

355
}
356

357
bool LMDBBackend::upgradeToSchemav5(std::string& filename)
358
{
2✔
359
  int rc;
2✔
360

361
  auto currentSchemaVersionAndShards = getSchemaVersionAndShards(filename);
2✔
362
  uint32_t currentSchemaVersion = currentSchemaVersionAndShards.first;
2✔
363
  uint32_t shards = currentSchemaVersionAndShards.second;
2✔
364

365
  if (currentSchemaVersion != 3 && currentSchemaVersion != 4) {
2!
366
    throw std::runtime_error("upgrade to v5 requested but current schema is not v3 or v4, stopping");
×
367
  }
×
368

369
  MDB_env* env = nullptr;
2✔
370

371
  if ((rc = mdb_env_create(&env)) != 0) {
2!
372
    throw std::runtime_error("mdb_env_create failed");
×
373
  }
×
374

375
  if ((rc = mdb_env_set_maxdbs(env, 20)) != 0) {
2!
376
    mdb_env_close(env);
×
377
    throw std::runtime_error("mdb_env_set_maxdbs failed");
×
378
  }
×
379

380
  if ((rc = mdb_env_open(env, filename.c_str(), MDB_NOSUBDIR, 0600)) != 0) {
2!
381
    mdb_env_close(env);
×
382
    throw std::runtime_error("mdb_env_open failed");
×
383
  }
×
384

385
  MDB_txn* txn = nullptr;
2✔
386

387
  if ((rc = mdb_txn_begin(env, NULL, 0, &txn)) != 0) {
2!
388
    mdb_env_close(env);
×
389
    throw std::runtime_error("mdb_txn_begin failed");
×
390
  }
×
391

392
#ifdef HAVE_SYSTEMD
2✔
393
  /* A schema migration may take a long time. Extend the startup service timeout to 1 day,
394
   * but only if this is beyond the original maximum time of TimeoutStartSec=.
395
   */
396
  sd_notify(0, "EXTEND_TIMEOUT_USEC=86400000000");
2✔
397
#endif
2✔
398

399
  std::cerr << "migrating shards" << std::endl;
2✔
400
  for (uint32_t i = 0; i < shards; i++) {
6✔
401
    string shardfile = filename + "-" + std::to_string(i);
4✔
402
    if (access(shardfile.c_str(), F_OK) < 0) {
4!
403
      if (errno == ENOENT) {
×
404
        // apparently this shard doesn't exist yet, moving on
405
        std::cerr << "shard " << shardfile << " not found, continuing" << std::endl;
×
406
        continue;
×
407
      }
×
408
    }
×
409

410
    std::cerr << "migrating shard " << shardfile << std::endl;
4✔
411
    MDB_env* shenv = nullptr;
4✔
412

413
    if ((rc = mdb_env_create(&shenv)) != 0) {
4!
414
      throw std::runtime_error("mdb_env_create failed");
×
415
    }
×
416

417
    if ((rc = mdb_env_set_maxdbs(shenv, 8)) != 0) {
4!
418
      mdb_env_close(env);
×
419
      throw std::runtime_error("mdb_env_set_maxdbs failed");
×
420
    }
×
421

422
    if ((rc = mdb_env_open(shenv, shardfile.c_str(), MDB_NOSUBDIR, 0600)) != 0) {
4!
423
      mdb_env_close(env);
×
424
      throw std::runtime_error("mdb_env_open failed");
×
425
    }
×
426

427
    MDB_txn* shtxn = nullptr;
4✔
428

429
    if ((rc = mdb_txn_begin(shenv, NULL, 0, &shtxn)) != 0) {
4!
430
      mdb_env_close(env);
×
431
      throw std::runtime_error("mdb_txn_begin failed");
×
432
    }
×
433

434
    MDB_dbi shdbi;
4✔
435

436
    if ((rc = mdb_dbi_open(shtxn, "records", 0, &shdbi)) != 0) {
4!
437
      if (rc == MDB_NOTFOUND) {
×
438
        mdb_txn_abort(shtxn);
×
439
        mdb_env_close(shenv);
×
440
        continue;
×
441
      }
×
442
      mdb_txn_abort(shtxn);
×
443
      mdb_env_close(shenv);
×
444
      throw std::runtime_error("mdb_dbi_open shard records failed");
×
445
    }
×
446

447
    MDB_dbi shdbi2;
4✔
448

449
    if ((rc = mdb_dbi_open(shtxn, "records_v5", MDB_CREATE, &shdbi2)) != 0) {
4!
450
      mdb_dbi_close(shenv, shdbi);
×
451
      mdb_txn_abort(shtxn);
×
452
      mdb_env_close(shenv);
×
453
      throw std::runtime_error("mdb_dbi_open shard records_v5 failed");
×
454
    }
×
455

456
    try {
4✔
457
      copyDBIAndAddLSHeader(shtxn, shdbi, shdbi2);
4✔
458
    }
4✔
459
    catch (std::exception& e) {
4✔
460
      mdb_dbi_close(shenv, shdbi2);
×
461
      mdb_dbi_close(shenv, shdbi);
×
462
      mdb_txn_abort(shtxn);
×
463
      mdb_env_close(shenv);
×
464
      throw std::runtime_error("copyDBIAndAddLSHeader failed");
×
465
    }
×
466

467
    cerr << "shard mbd_drop=" << mdb_drop(shtxn, shdbi, 1) << endl;
4✔
468
    mdb_txn_commit(shtxn);
4✔
469
    mdb_dbi_close(shenv, shdbi2);
4✔
470
    mdb_env_close(shenv);
4✔
471
  }
4✔
472

473
  std::array<MDB_dbi, 4> fromtypeddbi;
2✔
474
  std::array<MDB_dbi, 4> totypeddbi;
2✔
475

476
  int index = 0;
2✔
477

478
  for (const std::string dbname : {"domains", "keydata", "tsig", "metadata"}) {
8✔
479
    std::cerr << "migrating " << dbname << std::endl;
8✔
480
    std::string tdbname = dbname + "_v5";
8✔
481

482
    if ((rc = mdb_dbi_open(txn, dbname.c_str(), 0, &fromtypeddbi[index])) != 0) {
8!
483
      mdb_txn_abort(txn);
×
484
      mdb_env_close(env);
×
485
      throw std::runtime_error("mdb_dbi_open typeddbi failed");
×
486
    }
×
487

488
    if ((rc = mdb_dbi_open(txn, tdbname.c_str(), MDB_CREATE, &totypeddbi[index])) != 0) {
8!
489
      mdb_dbi_close(env, fromtypeddbi[index]);
×
490
      mdb_txn_abort(txn);
×
491
      mdb_env_close(env);
×
492
      throw std::runtime_error("mdb_dbi_open typeddbi target failed");
×
493
    }
×
494

495
    try {
8✔
496
      copyTypedDBI(txn, fromtypeddbi[index], totypeddbi[index]);
8✔
497
    }
8✔
498
    catch (std::exception& e) {
8✔
499
      mdb_dbi_close(env, totypeddbi[index]);
×
500
      mdb_dbi_close(env, fromtypeddbi[index]);
×
501
      mdb_txn_abort(txn);
×
502
      mdb_env_close(env);
×
503
      throw std::runtime_error("copyTypedDBI failed");
×
504
    }
×
505

506
    // mdb_dbi_close(env, dbi2);
507
    // mdb_dbi_close(env, dbi);
508
    std::cerr << "migrated " << dbname << std::endl;
8✔
509

510
    index++;
8✔
511
  }
8✔
512

513
  std::array<MDB_dbi, 4> fromindexdbi;
2✔
514
  std::array<MDB_dbi, 4> toindexdbi;
2✔
515

516
  index = 0;
2✔
517

518
  for (const std::string dbname : {"domains", "keydata", "tsig", "metadata"}) {
8✔
519
    std::string fdbname = dbname + "_0";
8✔
520
    std::cerr << "migrating " << dbname << std::endl;
8✔
521
    std::string tdbname = dbname + "_v5_0";
8✔
522

523
    if ((rc = mdb_dbi_open(txn, fdbname.c_str(), 0, &fromindexdbi[index])) != 0) {
8!
524
      mdb_txn_abort(txn);
×
525
      mdb_env_close(env);
×
526
      throw std::runtime_error("mdb_dbi_open indexdbi failed");
×
527
    }
×
528

529
    if ((rc = mdb_dbi_open(txn, tdbname.c_str(), MDB_CREATE, &toindexdbi[index])) != 0) {
8!
530
      mdb_dbi_close(env, fromindexdbi[index]);
×
531
      mdb_txn_abort(txn);
×
532
      mdb_env_close(env);
×
533
      throw std::runtime_error("mdb_dbi_open indexdbi target failed");
×
534
    }
×
535

536
    try {
8✔
537
      copyIndexDBI(txn, fromindexdbi[index], toindexdbi[index]);
8✔
538
    }
8✔
539
    catch (std::exception& e) {
8✔
540
      mdb_dbi_close(env, toindexdbi[index]);
×
541
      mdb_dbi_close(env, fromindexdbi[index]);
×
542
      mdb_txn_abort(txn);
×
543
      mdb_env_close(env);
×
544
      throw std::runtime_error("copyIndexDBI failed");
×
545
    }
×
546

547
    // mdb_dbi_close(env, dbi2);
548
    // mdb_dbi_close(env, dbi);
549
    std::cerr << "migrated " << dbname << std::endl;
8✔
550

551
    index++;
8✔
552
  }
8✔
553

554
  MDB_dbi dbi;
2✔
555

556
  // finally, migrate the pdns db
557
  if ((rc = mdb_dbi_open(txn, "pdns", 0, &dbi)) != 0) {
2!
558
    mdb_txn_abort(txn);
×
559
    mdb_env_close(env);
×
560
    throw std::runtime_error("mdb_dbi_open pdns failed");
×
561
  }
×
562

563
  MDB_val key, data;
2✔
564

565
  std::string header(LMDBLS::LS_MIN_HEADER_SIZE, '\0');
2✔
566

567
  for (const std::string keyname : {"schemaversion", "shards"}) {
4✔
568
    cerr << "migrating pdns." << keyname << endl;
4✔
569

570
    key.mv_data = (char*)keyname.c_str();
4✔
571
    key.mv_size = keyname.size();
4✔
572

573
    if ((rc = mdb_get(txn, dbi, &key, &data))) {
4!
574
      throw std::runtime_error("mdb_get pdns.shards failed");
×
575
    }
×
576

577
    uint32_t value;
4✔
578

579
    if (data.mv_size != sizeof(uint32_t)) {
4!
580
      throw std::runtime_error("got non-uint32_t key");
×
581
    }
×
582

583
    memcpy((void*)&value, data.mv_data, sizeof(uint32_t));
4✔
584

585
    value = htonl(value);
4✔
586
    if (keyname == "schemaversion") {
4✔
587
      value = htonl(5);
2✔
588
    }
2✔
589

590
    std::string sdata((char*)data.mv_data, data.mv_size);
4✔
591

592
    std::string stdata = header + std::string((char*)&value, sizeof(uint32_t));
4✔
593
    ;
4✔
594

595
    MDB_val tdata;
4✔
596

597
    tdata.mv_data = (char*)stdata.c_str();
4✔
598
    tdata.mv_size = stdata.size();
4✔
599

600
    if ((rc = mdb_put(txn, dbi, &key, &tdata, 0)) != 0) {
4!
601
      throw std::runtime_error("mdb_put failed");
×
602
    }
×
603
  }
4✔
604

605
  for (const std::string keyname : {"uuid"}) {
2✔
606
    cerr << "migrating pdns." << keyname << endl;
2✔
607

608
    key.mv_data = (char*)keyname.c_str();
2✔
609
    key.mv_size = keyname.size();
2✔
610

611
    if ((rc = mdb_get(txn, dbi, &key, &data))) {
2!
612
      throw std::runtime_error("mdb_get pdns.shards failed");
×
613
    }
×
614

615
    std::string sdata((char*)data.mv_data, data.mv_size);
2✔
616

617
    std::string stdata = header + sdata;
2✔
618

619
    MDB_val tdata;
2✔
620

621
    tdata.mv_data = (char*)stdata.c_str();
2✔
622
    tdata.mv_size = stdata.size();
2✔
623

624
    if ((rc = mdb_put(txn, dbi, &key, &tdata, 0)) != 0) {
2!
625
      throw std::runtime_error("mdb_put failed");
×
626
    }
×
627
  }
2✔
628

629
  for (int i = 0; i < 4; i++) {
10✔
630
    mdb_drop(txn, fromtypeddbi[i], 1);
8✔
631
    mdb_drop(txn, fromindexdbi[i], 1);
8✔
632
  }
8✔
633

634
  cerr << "txn commit=" << mdb_txn_commit(txn) << endl;
2✔
635

636
  for (int i = 0; i < 4; i++) {
10✔
637
    mdb_dbi_close(env, totypeddbi[i]);
8✔
638
    mdb_dbi_close(env, toindexdbi[i]);
8✔
639
  }
8✔
640
  mdb_env_close(env);
2✔
641

642
  // throw std::runtime_error("migration done");
643
  cerr << "migration done" << endl;
2✔
644
  // exit(1);
645
  return true;
2✔
646
}
2✔
647

648
// Serial number cache
649

650
// Retrieve the transient domain info for the given domain, if any
651
bool LMDBBackend::TransientDomainInfoCache::get(uint32_t domainid, TransientDomainInfo& data) const
652
{
×
653
  if (auto iter = d_data.find(domainid); iter != d_data.end()) {
×
654
    data = iter->second;
×
655
    return true;
×
656
  }
×
657
  return false;
×
658
}
×
659

660
// Remove the transient domain info for the given domain
661
void LMDBBackend::TransientDomainInfoCache::remove(uint32_t domainid)
662
{
18✔
663
  if (auto iter = d_data.find(domainid); iter != d_data.end()) {
18!
664
    d_data.erase(iter);
×
665
  }
×
666
}
18✔
667

668
// Create or update the transient domain info for the given domain
669
void LMDBBackend::TransientDomainInfoCache::update(uint32_t domainid, const TransientDomainInfo& data)
670
{
×
671
  d_data.insert_or_assign(domainid, data);
×
672
}
×
673

674
SharedLockGuarded<LMDBBackend::TransientDomainInfoCache> LMDBBackend::s_transient_domain_info;
675

676
LMDBBackend::LMDBBackend(const std::string& suffix)
677
{
2,255✔
678
  // overlapping domain ids in combination with relative names are a recipe for disaster
679
  if (!suffix.empty()) {
2,255!
680
    throw std::runtime_error("LMDB backend does not support multiple instances");
×
681
  }
×
682

683
  setArgPrefix("lmdb" + suffix);
2,255✔
684

685
  string syncMode = toLower(getArg("sync-mode"));
2,255✔
686

687
  if (syncMode == "nosync")
2,255!
688
    d_asyncFlag = MDB_NOSYNC;
×
689
  else if (syncMode == "nometasync")
2,255!
690
    d_asyncFlag = MDB_NOMETASYNC;
×
691
  else if (syncMode.empty() || syncMode == "sync")
2,255!
692
    d_asyncFlag = 0;
2,255✔
693
  else
×
694
    throw std::runtime_error("Unknown sync mode " + syncMode + " requested for LMDB backend");
×
695

696
  d_mapsize = 0;
2,255✔
697
  try {
2,255✔
698
    d_mapsize = std::stoll(getArg("map-size"));
2,255✔
699
  }
2,255✔
700
  catch (const std::exception& e) {
2,255✔
701
    throw std::runtime_error(std::string("Unable to parse the 'map-size' LMDB value: ") + e.what());
×
702
  }
×
703

704
  d_write_notification_update = mustDo("write-notification-update");
2,255✔
705

706
  if (mustDo("lightning-stream")) {
2,255!
707
    d_random_ids = true;
×
708
    d_handle_dups = true;
×
709
    LMDBLS::s_flag_deleted = true;
×
710

711
    if (atoi(getArg("shards").c_str()) != 1) {
×
712
      throw std::runtime_error(std::string("running with Lightning Stream support requires shards=1"));
×
713
    }
×
714
  }
×
715
  else {
2,255✔
716
    d_random_ids = mustDo("random-ids");
2,255✔
717
    d_handle_dups = false;
2,255✔
718
    LMDBLS::s_flag_deleted = mustDo("flag-deleted");
2,255✔
719
  }
2,255✔
720

721
  bool opened = false;
2,255✔
722

723
  if (s_first) {
2,255✔
724
    std::lock_guard<std::mutex> l(s_lmdbStartupLock);
651✔
725
    if (s_first) {
651!
726
      auto filename = getArg("filename");
651✔
727

728
      auto currentSchemaVersionAndShards = getSchemaVersionAndShards(filename);
651✔
729
      uint32_t currentSchemaVersion = currentSchemaVersionAndShards.first;
651✔
730
      // std::cerr<<"current schema version: "<<currentSchemaVersion<<", shards="<<currentSchemaVersionAndShards.second<<std::endl;
731

732
      if (getArgAsNum("schema-version") != SCHEMAVERSION) {
651!
733
        throw std::runtime_error("This version of the lmdbbackend only supports schema version 5. Configuration demands a lower version. Not starting up.");
×
734
      }
×
735

736
      if (currentSchemaVersion > 0 && currentSchemaVersion < 3) {
651!
737
        throw std::runtime_error("this version of the lmdbbackend can only upgrade from schema v3/v4 to v5. Upgrading from older schemas is not yet supported.");
×
738
      }
×
739

740
      if (currentSchemaVersion == 0) {
651✔
741
        // no database is present yet, we can just create them
742
        currentSchemaVersion = 5;
14✔
743
      }
14✔
744

745
      if (currentSchemaVersion == 3 || currentSchemaVersion == 4) {
651✔
746
        if (!upgradeToSchemav5(filename)) {
2!
747
          throw std::runtime_error("Failed to perform LMDB schema version upgrade from v4 to v5");
×
748
        }
×
749
        currentSchemaVersion = 5;
2✔
750
      }
2✔
751

752
      if (currentSchemaVersion != 5) {
651!
753
        throw std::runtime_error("Somehow, we are not at schema version 5. Giving up");
×
754
      }
×
755

756
      d_tdomains = std::make_shared<tdomains_t>(getMDBEnv(getArg("filename").c_str(), MDB_NOSUBDIR | d_asyncFlag, 0600, d_mapsize), "domains_v5");
651✔
757
      d_tmeta = std::make_shared<tmeta_t>(d_tdomains->getEnv(), "metadata_v5");
651✔
758
      d_tkdb = std::make_shared<tkdb_t>(d_tdomains->getEnv(), "keydata_v5");
651✔
759
      d_ttsig = std::make_shared<ttsig_t>(d_tdomains->getEnv(), "tsig_v5");
651✔
760

761
      auto pdnsdbi = d_tdomains->getEnv()->openDB("pdns", MDB_CREATE);
651✔
762

763
      opened = true;
651✔
764

765
      auto txn = d_tdomains->getEnv()->getRWTransaction();
651✔
766

767
      MDBOutVal shards;
651✔
768
      if (!txn->get(pdnsdbi, "shards", shards)) {
651✔
769
        s_shards = shards.get<uint32_t>();
637✔
770

771
        if (mustDo("lightning-stream") && s_shards != 1) {
637!
772
          throw std::runtime_error(std::string("running with Lightning Stream support enabled requires a database with exactly 1 shard"));
×
773
        }
×
774

775
        if (s_shards != atoi(getArg("shards").c_str())) {
637!
776
          g_log << Logger::Warning << "Note: configured number of lmdb shards (" << atoi(getArg("shards").c_str()) << ") is different from on-disk (" << s_shards << "). Using on-disk shard number" << endl;
×
777
        }
×
778
      }
637✔
779
      else {
14✔
780
        s_shards = atoi(getArg("shards").c_str());
14✔
781
        txn->put(pdnsdbi, "shards", s_shards);
14✔
782
      }
14✔
783

784
      MDBOutVal gotuuid;
651✔
785
      if (txn->get(pdnsdbi, "uuid", gotuuid)) {
651✔
786
        const auto uuid = getUniqueID();
14✔
787
        const string uuids(uuid.begin(), uuid.end());
14✔
788
        txn->put(pdnsdbi, "uuid", uuids);
14✔
789
      }
14✔
790

791
      MDBOutVal _schemaversion;
651✔
792
      if (txn->get(pdnsdbi, "schemaversion", _schemaversion)) {
651✔
793
        // our DB is entirely new, so we need to write the schemaversion
794
        txn->put(pdnsdbi, "schemaversion", currentSchemaVersion);
14✔
795
      }
14✔
796
      txn->commit();
651✔
797

798
      s_first = false;
651✔
799
    }
651✔
800
  }
651✔
801

802
  if (!opened) {
2,255✔
803
    d_tdomains = std::make_shared<tdomains_t>(getMDBEnv(getArg("filename").c_str(), MDB_NOSUBDIR | d_asyncFlag, 0600, d_mapsize), "domains_v5");
1,604✔
804
    d_tmeta = std::make_shared<tmeta_t>(d_tdomains->getEnv(), "metadata_v5");
1,604✔
805
    d_tkdb = std::make_shared<tkdb_t>(d_tdomains->getEnv(), "keydata_v5");
1,604✔
806
    d_ttsig = std::make_shared<ttsig_t>(d_tdomains->getEnv(), "tsig_v5");
1,604✔
807
  }
1,604✔
808
  d_trecords.resize(s_shards);
2,255✔
809
  d_dolog = ::arg().mustDo("query-logging");
2,255✔
810
}
2,255✔
811

812
namespace boost
813
{
814
namespace serialization
815
{
816

817
  template <class Archive>
818
  void save(Archive& ar, const DNSName& g, const unsigned int /* version */)
819
  {
2,339✔
820
    if (g.empty()) {
2,339✔
821
      ar& std::string();
631✔
822
    }
631✔
823
    else {
1,708✔
824
      ar& g.toDNSStringLC();
1,708✔
825
    }
1,708✔
826
  }
2,339✔
827

828
  template <class Archive>
829
  void load(Archive& ar, DNSName& g, const unsigned int /* version */)
830
  {
148,193✔
831
    string tmp;
148,193✔
832
    ar& tmp;
148,193✔
833
    if (tmp.empty()) {
148,193✔
834
      g = DNSName();
65,574✔
835
    }
65,574✔
836
    else {
82,619✔
837
      g = DNSName(tmp.c_str(), tmp.size(), 0, false);
82,619✔
838
    }
82,619✔
839
  }
148,193✔
840

841
  template <class Archive>
842
  void save(Archive& ar, const QType& g, const unsigned int /* version */)
843
  {
844
    ar& g.getCode();
845
  }
846

847
  template <class Archive>
848
  void load(Archive& ar, QType& g, const unsigned int /* version */)
849
  {
850
    uint16_t tmp;
851
    ar& tmp;
852
    g = QType(tmp);
853
  }
854

855
  template <class Archive>
856
  void save(Archive& ar, const DomainInfo& g, const unsigned int /* version */)
857
  {
909✔
858
    ar& g.zone;
909✔
859
    ar& g.last_check;
909✔
860
    ar& g.account;
909✔
861
    ar& g.primaries;
909✔
862
    ar& g.id;
909✔
863
    ar& g.notified_serial;
909✔
864
    ar& g.kind;
909✔
865
    ar& g.options;
909✔
866
    ar& g.catalog;
909✔
867
  }
909✔
868

869
  template <class Archive>
870
  void load(Archive& ar, DomainInfo& g, const unsigned int version)
871
  {
71,890✔
872
    ar& g.zone;
71,890✔
873
    ar& g.last_check;
71,890✔
874
    ar& g.account;
71,890✔
875
    ar& g.primaries;
71,890✔
876
    ar& g.id;
71,890✔
877
    ar& g.notified_serial;
71,890✔
878
    ar& g.kind;
71,890✔
879
    if (version >= 1) {
71,890✔
880
      ar& g.options;
71,826✔
881
      ar& g.catalog;
71,826✔
882
    }
71,826✔
883
    else {
64✔
884
      g.options.clear();
64✔
885
      g.catalog.clear();
64✔
886
    }
64✔
887
  }
71,890✔
888

889
  template <class Archive>
890
  void serialize(Archive& ar, LMDBBackend::DomainMeta& g, const unsigned int /* version */)
891
  {
4,258✔
892
    ar& g.domain& g.key& g.value;
4,258✔
893
  }
4,258✔
894

895
  template <class Archive>
896
  void save(Archive& ar, const LMDBBackend::KeyDataDB& g, const unsigned int /* version */)
897
  {
132✔
898
    ar& g.domain& g.content& g.flags& g.active& g.published;
132✔
899
  }
132✔
900

901
  template <class Archive>
902
  void load(Archive& ar, LMDBBackend::KeyDataDB& g, const unsigned int version)
903
  {
420✔
904
    ar& g.domain& g.content& g.flags& g.active;
420✔
905
    if (version >= 1) {
420!
906
      ar& g.published;
420✔
907
    }
420✔
908
    else {
×
909
      g.published = true;
×
910
    }
×
911
  }
420✔
912

913
  template <class Archive>
914
  void serialize(Archive& ar, TSIGKey& g, const unsigned int /* version */)
915
  {
94✔
916
    ar& g.name;
94✔
917
    ar& g.algorithm; // this is the ordername
94✔
918
    ar& g.key;
94✔
919
  }
94✔
920

921
} // namespace serialization
922
} // namespace boost
923

924
BOOST_SERIALIZATION_SPLIT_FREE(DNSName);
925
BOOST_SERIALIZATION_SPLIT_FREE(QType);
926
BOOST_SERIALIZATION_SPLIT_FREE(LMDBBackend::KeyDataDB);
927
BOOST_SERIALIZATION_SPLIT_FREE(DomainInfo);
928
BOOST_IS_BITWISE_SERIALIZABLE(ComboAddress);
929

930
template <>
931
std::string serToString(const LMDBBackend::LMDBResourceRecord& lrr)
932
{
589,046✔
933
  std::string ret;
589,046✔
934
  uint16_t len = lrr.content.length();
589,046✔
935
  ret.reserve(2 + len + 7);
589,046✔
936

937
  ret.assign((const char*)&len, 2);
589,046✔
938
  ret += lrr.content;
589,046✔
939
  ret.append((const char*)&lrr.ttl, 4);
589,046✔
940
  ret.append(1, (char)lrr.auth);
589,046✔
941
  ret.append(1, (char)lrr.disabled);
589,046✔
942
  ret.append(1, (char)lrr.ordername);
589,046✔
943
  return ret;
589,046✔
944
}
589,046✔
945

946
template <>
947
std::string serToString(const vector<LMDBBackend::LMDBResourceRecord>& lrrs)
948
{
60,958✔
949
  std::string ret;
60,958✔
950
  for (const auto& lrr : lrrs) {
61,338✔
951
    ret += serToString(lrr);
61,338✔
952
  }
61,338✔
953
  return ret;
60,958✔
954
}
60,958✔
955

956
static inline size_t serOneRRFromString(const string_view& str, LMDBBackend::LMDBResourceRecord& lrr)
957
{
346,803✔
958
  uint16_t len;
346,803✔
959
  memcpy(&len, &str[0], 2);
346,803✔
960
  lrr.content.assign(&str[2], len); // len bytes
346,803✔
961
  memcpy(&lrr.ttl, &str[2] + len, 4);
346,803✔
962
  lrr.auth = str[2 + len + 4];
346,803✔
963
  lrr.disabled = str[2 + len + 4 + 1];
346,803✔
964
  lrr.ordername = str[2 + len + 4 + 2];
346,803✔
965
  lrr.wildcardname.clear();
346,803✔
966

967
  return 2 + len + 7;
346,803✔
968
}
346,803✔
969

970
template <>
971
void serFromString(const string_view& str, LMDBBackend::LMDBResourceRecord& lrr)
972
{
10,946✔
973
  serOneRRFromString(str, lrr);
10,946✔
974
}
10,946✔
975

976
template <>
977
void serFromString(const string_view& str, vector<LMDBBackend::LMDBResourceRecord>& lrrs)
978
{
331,348✔
979
  auto str_copy = str;
331,348✔
980
  while (str_copy.size() >= 9) { // minimum length for a record is 10
667,205✔
981
    LMDBBackend::LMDBResourceRecord lrr;
335,857✔
982
    auto rrLength = serOneRRFromString(str_copy, lrr);
335,857✔
983
    lrrs.emplace_back(lrr);
335,857✔
984
    str_copy.remove_prefix(rrLength);
335,857✔
985
  }
335,857✔
986
}
331,348✔
987

988
static std::string serializeContent(uint16_t qtype, const DNSName& domain, const std::string& content)
989
{
325,534✔
990
  auto drc = DNSRecordContent::make(qtype, QClass::IN, content);
325,534✔
991
  return drc->serialize(domain, false);
325,534✔
992
}
325,534✔
993

994
static std::shared_ptr<DNSRecordContent> deserializeContentZR(uint16_t qtype, const DNSName& qname, const std::string& content)
995
{
273,643✔
996
  if (qtype == QType::A && content.size() == 4) {
273,643!
997
    return std::make_shared<ARecordContent>(*((uint32_t*)content.c_str()));
263,928✔
998
  }
263,928✔
999
  return DNSRecordContent::deserialize(qname, qtype, content);
9,715✔
1000
}
273,643✔
1001

1002
/* design. If you ask a question without a zone id, we lookup the best
1003
   zone id for you, and answer from that. This is different than other backends, but I can't see why it would not work.
1004

1005
   The index we use is "zoneid,canonical relative name". This index is also used
1006
   for AXFR.
1007

1008
   Note - domain_id, name and type are ONLY present on the index!
1009
*/
1010

1011
#if BOOST_VERSION >= 106100
1012
#define StringView string_view
1013
#else
1014
#define StringView string
1015
#endif
1016

1017
void LMDBBackend::deleteDomainRecords(RecordsRWTransaction& txn, uint32_t domain_id, uint16_t qtype)
1018
{
328✔
1019
  compoundOrdername co;
328✔
1020
  string match = co(domain_id);
328✔
1021

1022
  auto cursor = txn.txn->getCursor(txn.db->dbi);
328✔
1023
  MDBOutVal key, val;
328✔
1024
  //  cout<<"Match: "<<makeHexDump(match);
1025
  if (!cursor.lower_bound(match, key, val)) {
328✔
1026
    while (key.getNoStripHeader<StringView>().rfind(match, 0) == 0) {
637✔
1027
      if (qtype == QType::ANY || co.getQType(key.getNoStripHeader<StringView>()) == qtype)
633!
1028
        cursor.del();
633✔
1029
      if (cursor.next(key, val))
633✔
1030
        break;
15✔
1031
    }
633✔
1032
  }
19✔
1033
}
328✔
1034

1035
bool LMDBBackend::findDomain(const DNSName& domain, DomainInfo& info) const
1036
{
4,012✔
1037
  auto rotxn = d_tdomains->getROTransaction();
4,012✔
1038
  auto domain_id = rotxn.get<0>(domain, info);
4,012✔
1039
  if (domain_id == 0) {
4,012✔
1040
    return false;
586✔
1041
  }
586✔
1042
  info.id = static_cast<uint32_t>(domain_id);
3,426✔
1043
  return true;
3,426✔
1044
}
4,012✔
1045

1046
bool LMDBBackend::findDomain(uint32_t domainid, DomainInfo& info) const
1047
{
67,520✔
1048
  auto rotxn = d_tdomains->getROTransaction();
67,520✔
1049
  if (!rotxn.get(domainid, info)) {
67,520✔
1050
    return false;
8✔
1051
  }
8✔
1052
  info.id = domainid;
67,512✔
1053
  return true;
67,512✔
1054
}
67,520✔
1055

1056
void LMDBBackend::consolidateDomainInfo(DomainInfo& info) const
1057
{
2,584✔
1058
  // Update the DomainInfo values if we have cached data in memory.
1059
  if (!d_write_notification_update) {
2,584!
1060
    auto container = s_transient_domain_info.read_lock();
×
1061
    TransientDomainInfo tdi;
×
1062
    if (container->get(info.id, tdi)) {
×
1063
      info.notified_serial = tdi.notified_serial;
×
1064
      info.last_check = tdi.last_check;
×
1065
    }
×
1066
  }
×
1067
}
2,584✔
1068

1069
void LMDBBackend::writeDomainInfo(const DomainInfo& info)
1070
{
619✔
1071
  if (!d_write_notification_update) {
619!
1072
    auto container = s_transient_domain_info.write_lock();
×
1073
    TransientDomainInfo tdi;
×
1074
    if (container->get(info.id, tdi)) {
×
1075
      // Only remove the in-memory value if it has not been modified since the
1076
      // DomainInfo data was set up.
1077
      if (tdi.notified_serial == info.notified_serial && tdi.last_check == info.last_check) {
×
1078
        container->remove(info.id);
×
1079
      }
×
1080
    }
×
1081
  }
×
1082
  auto txn = d_tdomains->getRWTransaction();
619✔
1083
  txn.put(info, info.id);
619✔
1084
  txn.commit();
619✔
1085
}
619✔
1086

1087
/* Here's the complicated story. Other backends have just one transaction, which is either
1088
   on or not.
1089

1090
   You can't call feedRecord without a transaction started with startTransaction.
1091

1092
   However, other functions can be called after startTransaction() or without startTransaction()
1093
     (like updateDNSSECOrderNameAndAuth)
1094

1095

1096

1097
*/
1098

1099
bool LMDBBackend::startTransaction(const DNSName& domain, int domain_id)
1100
{
532✔
1101
  // cout <<"startTransaction("<<domain<<", "<<domain_id<<")"<<endl;
1102
  int real_id = domain_id;
532✔
1103
  if (real_id < 0) {
532✔
1104
    DomainInfo info;
204✔
1105
    if (!findDomain(domain, info)) {
204!
1106
      return false;
×
1107
    }
×
1108
    real_id = static_cast<int>(info.id);
204✔
1109
  }
204✔
1110
  if (d_rwtxn) {
532!
1111
    throw DBException("Attempt to start a transaction while one was open already");
×
1112
  }
×
1113
  d_rwtxn = getRecordsRWTransaction(real_id);
532✔
1114

1115
  d_transactiondomain = domain;
532✔
1116
  d_transactiondomainid = real_id;
532✔
1117
  if (domain_id >= 0) {
532✔
1118
    deleteDomainRecords(*d_rwtxn, domain_id);
328✔
1119
  }
328✔
1120

1121
  return true;
532✔
1122
}
532✔
1123

1124
bool LMDBBackend::commitTransaction()
1125
{
487✔
1126
  // cout<<"Commit transaction" <<endl;
1127
  if (!d_rwtxn) {
487!
1128
    throw DBException("Attempt to commit a transaction while there isn't one open");
×
1129
  }
×
1130

1131
  d_rwtxn->txn->commit();
487✔
1132
  d_rwtxn.reset();
487✔
1133
  return true;
487✔
1134
}
487✔
1135

1136
bool LMDBBackend::abortTransaction()
1137
{
42✔
1138
  // cout<<"Abort transaction"<<endl;
1139
  if (!d_rwtxn) {
42!
1140
    throw DBException("Attempt to abort a transaction while there isn't one open");
×
1141
  }
×
1142

1143
  d_rwtxn->txn->abort();
42✔
1144
  d_rwtxn.reset();
42✔
1145

1146
  return true;
42✔
1147
}
42✔
1148

1149
// d_rwtxn must be set here
1150
bool LMDBBackend::feedRecord(const DNSResourceRecord& r, const DNSName& ordername, bool ordernameIsNSEC3)
1151
{
325,381✔
1152
  LMDBResourceRecord lrr(r);
325,381✔
1153
  lrr.qname.makeUsRelative(d_transactiondomain);
325,381✔
1154
  lrr.content = serializeContent(lrr.qtype.getCode(), r.qname, lrr.content);
325,381✔
1155

1156
  compoundOrdername co;
325,381✔
1157
  string matchName = co(lrr.domain_id, lrr.qname, lrr.qtype.getCode());
325,381✔
1158

1159
  string rrs;
325,381✔
1160
  MDBOutVal _rrs;
325,381✔
1161
  if (!d_rwtxn->txn->get(d_rwtxn->db->dbi, matchName, _rrs)) {
325,381✔
1162
    rrs = _rrs.get<string>();
21,773✔
1163
  }
21,773✔
1164

1165
  rrs += serToString(lrr);
325,381✔
1166

1167
  d_rwtxn->txn->put(d_rwtxn->db->dbi, matchName, rrs);
325,381✔
1168

1169
  if (ordernameIsNSEC3 && !ordername.empty()) {
325,381!
1170
    MDBOutVal val;
40,752✔
1171
    if (d_rwtxn->txn->get(d_rwtxn->db->dbi, co(lrr.domain_id, lrr.qname, QType::NSEC3), val)) {
40,752✔
1172
      lrr.ttl = 0;
40,372✔
1173
      lrr.content = lrr.qname.toDNSStringLC();
40,372✔
1174
      lrr.auth = 0;
40,372✔
1175
      string ser = serToString(lrr);
40,372✔
1176
      d_rwtxn->txn->put(d_rwtxn->db->dbi, co(lrr.domain_id, ordername, QType::NSEC3), ser);
40,372✔
1177

1178
      lrr.ttl = 1;
40,372✔
1179
      lrr.content = ordername.toDNSString();
40,372✔
1180
      ser = serToString(lrr);
40,372✔
1181
      d_rwtxn->txn->put(d_rwtxn->db->dbi, co(lrr.domain_id, lrr.qname, QType::NSEC3), ser);
40,372✔
1182
    }
40,372✔
1183
  }
40,752✔
1184
  return true;
325,381✔
1185
}
325,381✔
1186

1187
bool LMDBBackend::feedEnts(int domain_id, map<DNSName, bool>& nonterm)
1188
{
20✔
1189
  LMDBResourceRecord lrr;
20✔
1190
  lrr.ttl = 0;
20✔
1191
  compoundOrdername co;
20✔
1192
  for (const auto& nt : nonterm) {
86✔
1193
    lrr.qname = nt.first.makeRelative(d_transactiondomain);
86✔
1194
    lrr.auth = nt.second;
86✔
1195
    lrr.ordername = true;
86✔
1196

1197
    std::string ser = serToString(lrr);
86✔
1198
    d_rwtxn->txn->put(d_rwtxn->db->dbi, co(domain_id, lrr.qname, QType::ENT), ser);
86✔
1199
  }
86✔
1200
  return true;
20✔
1201
}
20✔
1202

1203
bool LMDBBackend::feedEnts3(int domain_id, const DNSName& domain, map<DNSName, bool>& nonterm, const NSEC3PARAMRecordContent& ns3prc, bool narrow)
1204
{
16✔
1205
  string ser;
16✔
1206
  DNSName ordername;
16✔
1207
  LMDBResourceRecord lrr;
16✔
1208
  compoundOrdername co;
16✔
1209
  for (const auto& nt : nonterm) {
82✔
1210
    lrr.qname = nt.first.makeRelative(domain);
82✔
1211
    lrr.ttl = 0;
82✔
1212
    lrr.auth = nt.second;
82✔
1213
    lrr.ordername = nt.second;
82✔
1214
    ser = serToString(lrr);
82✔
1215
    d_rwtxn->txn->put(d_rwtxn->db->dbi, co(domain_id, lrr.qname, QType::ENT), ser);
82✔
1216

1217
    if (!narrow && lrr.auth) {
82!
1218
      lrr.content = lrr.qname.toDNSString();
80✔
1219
      lrr.auth = false;
80✔
1220
      lrr.ordername = false;
80✔
1221
      ser = serToString(lrr);
80✔
1222

1223
      ordername = DNSName(toBase32Hex(hashQNameWithSalt(ns3prc, nt.first)));
80✔
1224
      d_rwtxn->txn->put(d_rwtxn->db->dbi, co(domain_id, ordername, QType::NSEC3), ser);
80✔
1225

1226
      lrr.ttl = 1;
80✔
1227
      lrr.content = ordername.toDNSString();
80✔
1228
      ser = serToString(lrr);
80✔
1229
      d_rwtxn->txn->put(d_rwtxn->db->dbi, co(domain_id, lrr.qname, QType::NSEC3), ser);
80✔
1230
    }
80✔
1231
  }
82✔
1232
  return true;
16✔
1233
}
16✔
1234

1235
// might be called within a transaction, might also be called alone
1236
bool LMDBBackend::replaceRRSet(uint32_t domain_id, const DNSName& qname, const QType& qt, const vector<DNSResourceRecord>& rrset)
1237
{
148✔
1238
  // zonk qname/qtype within domain_id (go through qname, check domain_id && qtype)
1239
  shared_ptr<RecordsRWTransaction> txn;
148✔
1240
  bool needCommit = false;
148✔
1241
  if (d_rwtxn && d_transactiondomainid == domain_id) {
148!
1242
    txn = d_rwtxn;
148✔
1243
    //    cout<<"Reusing open transaction"<<endl;
1244
  }
148✔
1245
  else {
×
1246
    //    cout<<"Making a new RW txn for replace rrset"<<endl;
1247
    txn = getRecordsRWTransaction(domain_id);
×
1248
    needCommit = true;
×
1249
  }
×
1250

1251
  DomainInfo info;
148✔
1252
  if (!findDomain(domain_id, info)) {
148!
1253
    return false;
×
1254
  }
×
1255

1256
  compoundOrdername co;
148✔
1257
  auto cursor = txn->txn->getCursor(txn->db->dbi);
148✔
1258
  MDBOutVal key, val;
148✔
1259
  string match = co(domain_id, qname.makeRelative(info.zone), qt.getCode());
148✔
1260
  if (!cursor.find(match, key, val)) {
148✔
1261
    cursor.del();
131✔
1262
  }
131✔
1263

1264
  if (!rrset.empty()) {
148✔
1265
    vector<LMDBResourceRecord> adjustedRRSet;
145✔
1266
    for (const auto& rr : rrset) {
153✔
1267
      LMDBResourceRecord lrr(rr);
153✔
1268
      lrr.content = serializeContent(lrr.qtype.getCode(), lrr.qname, lrr.content);
153✔
1269
      lrr.qname.makeUsRelative(info.zone);
153✔
1270

1271
      adjustedRRSet.emplace_back(lrr);
153✔
1272
    }
153✔
1273
    txn->txn->put(txn->db->dbi, match, serToString(adjustedRRSet));
145✔
1274
  }
145✔
1275

1276
  if (needCommit)
148!
1277
    txn->txn->commit();
×
1278

1279
  return true;
148✔
1280
}
148✔
1281

1282
bool LMDBBackend::replaceComments([[maybe_unused]] const uint32_t domain_id, [[maybe_unused]] const DNSName& qname, [[maybe_unused]] const QType& qt, const vector<Comment>& comments)
1283
{
2✔
1284
  // if the vector is empty, good, that's what we do here (LMDB does not store comments)
1285
  // if it's not, report failure
1286
  return comments.empty();
2✔
1287
}
2✔
1288

1289
// tempting to templatize these two functions but the pain is not worth it
1290
std::shared_ptr<LMDBBackend::RecordsRWTransaction> LMDBBackend::getRecordsRWTransaction(uint32_t id)
1291
{
532✔
1292
  auto& shard = d_trecords[id % s_shards];
532✔
1293
  if (!shard.env) {
532✔
1294
    shard.env = getMDBEnv((getArg("filename") + "-" + std::to_string(id % s_shards)).c_str(),
65✔
1295
                          MDB_NOSUBDIR | d_asyncFlag, 0600, d_mapsize);
65✔
1296
    shard.dbi = shard.env->openDB("records_v5", MDB_CREATE);
65✔
1297
  }
65✔
1298
  auto ret = std::make_shared<RecordsRWTransaction>(shard.env->getRWTransaction());
532✔
1299
  ret->db = std::make_shared<RecordsDB>(shard);
532✔
1300

1301
  return ret;
532✔
1302
}
532✔
1303

1304
std::shared_ptr<LMDBBackend::RecordsROTransaction> LMDBBackend::getRecordsROTransaction(uint32_t id, const std::shared_ptr<LMDBBackend::RecordsRWTransaction>& rwtxn)
1305
{
10,564✔
1306
  auto& shard = d_trecords[id % s_shards];
10,564✔
1307
  if (!shard.env) {
10,564✔
1308
    if (rwtxn) {
2,183!
1309
      throw DBException("attempting to start nested transaction without open parent env");
×
1310
    }
×
1311
    shard.env = getMDBEnv((getArg("filename") + "-" + std::to_string(id % s_shards)).c_str(),
2,183✔
1312
                          MDB_NOSUBDIR | d_asyncFlag, 0600, d_mapsize);
2,183✔
1313
    shard.dbi = shard.env->openDB("records_v5", MDB_CREATE);
2,183✔
1314
  }
2,183✔
1315

1316
  if (rwtxn) {
10,564✔
1317
    auto ret = std::make_shared<RecordsROTransaction>(rwtxn->txn->getROTransaction());
435✔
1318
    ret->db = std::make_shared<RecordsDB>(shard);
435✔
1319
    return ret;
435✔
1320
  }
435✔
1321
  else {
10,129✔
1322
    auto ret = std::make_shared<RecordsROTransaction>(shard.env->getROTransaction());
10,129✔
1323
    ret->db = std::make_shared<RecordsDB>(shard);
10,129✔
1324
    return ret;
10,129✔
1325
  }
10,129✔
1326
}
10,564✔
1327

1328
#if 0
1329
// FIXME reinstate soon
1330
bool LMDBBackend::upgradeToSchemav3()
1331
{
1332
  g_log << Logger::Warning << "Upgrading LMDB schema" << endl;
1333

1334
  for (auto i = 0; i < s_shards; i++) {
1335
    string filename = getArg("filename") + "-" + std::to_string(i);
1336
    if (rename(filename.c_str(), (filename + "-old").c_str()) < 0) {
1337
      if (errno == ENOENT) {
1338
        // apparently this shard doesn't exist yet, moving on
1339
        continue;
1340
      }
1341
      unixDie("Rename failed during LMDB upgrade");
1342
    }
1343

1344
    LMDBBackend::RecordsDB oldShard, newShard;
1345

1346
    oldShard.env = getMDBEnv((filename + "-old").c_str(),
1347
                             MDB_NOSUBDIR | d_asyncFlag, 0600);
1348
    oldShard.dbi = oldShard.env->openDB("records", MDB_CREATE | MDB_DUPSORT);
1349
    auto txn = oldShard.env->getROTransaction();
1350
    auto cursor = txn->getROCursor(oldShard.dbi);
1351

1352
    newShard.env = getMDBEnv((filename).c_str(),
1353
                             MDB_NOSUBDIR | d_asyncFlag, 0600);
1354
    newShard.dbi = newShard.env->openDB("records", MDB_CREATE);
1355
    auto newTxn = newShard.env->getRWTransaction();
1356

1357
    MDBOutVal key, val;
1358
    if (cursor.first(key, val) != 0) {
1359
      cursor.close();
1360
      txn->abort();
1361
      newTxn->abort();
1362
      continue;
1363
    }
1364
    string_view currentKey;
1365
    string value;
1366
    for (;;) {
1367
      auto newKey = key.getNoStripHeader<string_view>();
1368
      if (currentKey.compare(newKey) != 0) {
1369
        if (value.size() > 0) {
1370
          newTxn->put(newShard.dbi, currentKey, value);
1371
        }
1372
        currentKey = newKey;
1373
        value = "";
1374
      }
1375
      value += val.get<string>();
1376
      if (cursor.next(key, val) != 0) {
1377
        if (value.size() > 0) {
1378
          newTxn->put(newShard.dbi, currentKey, value);
1379
        }
1380
        break;
1381
      }
1382
    }
1383

1384
    cursor.close();
1385
    txn->commit();
1386
    newTxn->commit();
1387
  }
1388

1389
  return true;
1390
}
1391
#endif
1392

1393
bool LMDBBackend::deleteDomain(const DNSName& domain)
1394
{
18✔
1395
  if (!d_rwtxn) {
18!
1396
    throw DBException(std::string(__PRETTY_FUNCTION__) + " called without a transaction");
×
1397
  }
×
1398

1399
  int transactionDomainId = d_transactiondomainid;
18✔
1400
  DNSName transactionDomain = d_transactiondomain;
18✔
1401

1402
  abortTransaction();
18✔
1403

1404
  LMDBIDvec idvec;
18✔
1405

1406
  if (!d_handle_dups) {
18!
1407
    // get domain id
1408
    DomainInfo info;
18✔
1409
    if (findDomain(domain, info)) {
18!
1410
      idvec.push_back(info.id);
18✔
1411
    }
18✔
1412
  }
18✔
1413
  else {
×
1414
    // this transaction used to be RO.
1415
    // it is now RW to narrow a race window between PowerDNS and Lightning Stream
1416
    // FIXME: turn the entire delete, including this ID scan, into one RW transaction
1417
    // when doing that, first do a short RO check to see if we actually have anything to delete
1418
    auto txn = d_tdomains->getRWTransaction();
×
1419

1420
    txn.get_multi<0>(domain, idvec);
×
1421
  }
×
1422

1423
  for (auto id : idvec) {
18✔
1424

1425
    startTransaction(domain, id);
18✔
1426

1427
    { // Remove metadata
18✔
1428
      auto txn = d_tmeta->getRWTransaction();
18✔
1429
      LMDBIDvec ids;
18✔
1430

1431
      txn.get_multi<0>(domain, ids);
18✔
1432

1433
      for (auto& _id : ids) {
20✔
1434
        txn.del(_id);
8✔
1435
      }
8✔
1436

1437
      txn.commit();
18✔
1438
    }
18✔
1439

1440
    { // Remove cryptokeys
18✔
1441
      auto txn = d_tkdb->getRWTransaction();
18✔
1442
      LMDBIDvec ids;
18✔
1443
      txn.get_multi<0>(domain, ids);
18✔
1444

1445
      for (auto _id : ids) {
18!
1446
        txn.del(_id);
×
1447
      }
×
1448

1449
      txn.commit();
18✔
1450
    }
18✔
1451

1452
    // Remove records
1453
    commitTransaction();
18✔
1454

1455
    // Remove zone
1456
    {
18✔
1457
      auto container = s_transient_domain_info.write_lock();
18✔
1458
      container->remove(static_cast<uint32_t>(id));
18✔
1459
    }
18✔
1460
    auto txn = d_tdomains->getRWTransaction();
18✔
1461
    txn.del(id);
18✔
1462
    txn.commit();
18✔
1463
  }
18✔
1464

1465
  startTransaction(transactionDomain, transactionDomainId);
18✔
1466

1467
  return true;
18✔
1468
}
18✔
1469

1470
bool LMDBBackend::list(const DNSName& target, int /* id */, bool include_disabled)
1471
{
599✔
1472
  d_includedisabled = include_disabled;
599✔
1473

1474
  DomainInfo info;
599✔
1475
  if (!findDomain(target, info)) {
599!
1476
    // cerr << "Did not find " << target << endl;
1477
    return false;
×
1478
  }
×
1479
  // cerr << "Found domain " << target << " on domain_id " << info.id << ", list requested " << id << endl;
1480

1481
  d_rotxn = getRecordsROTransaction(info.id, d_rwtxn);
599✔
1482
  d_getcursor = std::make_shared<MDBROCursor>(d_rotxn->txn->getCursor(d_rotxn->db->dbi));
599✔
1483

1484
  compoundOrdername co;
599✔
1485
  d_matchkey = co(info.id);
599✔
1486

1487
  MDBOutVal key, val;
599✔
1488
  if (d_getcursor->prefix(d_matchkey, key, val) != 0) {
599✔
1489
    d_getcursor.reset();
11✔
1490
  }
11✔
1491

1492
  d_lookupdomain = target;
599✔
1493

1494
  // Make sure we start with fresh data
1495
  d_currentrrset.clear();
599✔
1496
  d_currentrrsetpos = 0;
599✔
1497

1498
  return true;
599✔
1499
}
599✔
1500

1501
void LMDBBackend::lookup(const QType& type, const DNSName& qdomain, int zoneId, DNSPacket* /* p */)
1502
{
3,551✔
1503
  if (d_dolog) {
3,551!
1504
    g_log << Logger::Warning << "Got lookup for " << qdomain << "|" << type.toString() << " in zone " << zoneId << endl;
×
1505
    d_dtime.set();
×
1506
  }
×
1507

1508
  d_includedisabled = false;
3,551✔
1509

1510
  DNSName hunt(qdomain);
3,551✔
1511
  DomainInfo info;
3,551✔
1512
  if (zoneId < 0) {
3,551✔
1513
    do {
759✔
1514
      if (findDomain(hunt, info)) {
759!
1515
        break;
759✔
1516
      }
759✔
1517
    } while (type != QType::SOA && hunt.chopOff());
759!
1518
    if (info.id <= 0) {
759!
1519
      //      cout << "Did not find zone for "<< qdomain<<endl;
1520
      d_getcursor.reset();
×
1521
      return;
×
1522
    }
×
1523
  }
759✔
1524
  else {
2,792✔
1525
    if (!findDomain(zoneId, info)) {
2,792✔
1526
      // cout<<"Could not find a zone with id "<<zoneId<<endl;
1527
      d_getcursor.reset();
8✔
1528
      return;
8✔
1529
    }
8✔
1530
    hunt = info.zone;
2,784✔
1531
  }
2,784✔
1532

1533
  DNSName relqname = qdomain.makeRelative(hunt);
3,543✔
1534
  if (relqname.empty()) {
3,543!
1535
    return;
×
1536
  }
×
1537
  // cout<<"get will look for "<<relqname<< " in zone "<<hunt<<" with id "<<zoneId<<" and type "<<type.toString()<<endl;
1538
  d_rotxn = getRecordsROTransaction(info.id, d_rwtxn);
3,543✔
1539

1540
  compoundOrdername co;
3,543✔
1541
  d_getcursor = std::make_shared<MDBROCursor>(d_rotxn->txn->getCursor(d_rotxn->db->dbi));
3,543✔
1542
  MDBOutVal key, val;
3,543✔
1543
  if (type.getCode() == QType::ANY) {
3,543✔
1544
    d_matchkey = co(info.id, relqname);
2,771✔
1545
  }
2,771✔
1546
  else {
772✔
1547
    d_matchkey = co(info.id, relqname, type.getCode());
772✔
1548
  }
772✔
1549

1550
  if (d_getcursor->prefix(d_matchkey, key, val) != 0) {
3,543✔
1551
    d_getcursor.reset();
1,202✔
1552
    if (d_dolog) {
1,202!
1553
      g_log << Logger::Warning << "Query " << ((long)(void*)this) << ": " << d_dtime.udiffNoReset() << " us to execute (found nothing)" << endl;
×
1554
    }
×
1555
    return;
1,202✔
1556
  }
1,202✔
1557

1558
  if (d_dolog) {
2,341!
1559
    g_log << Logger::Warning << "Query " << ((long)(void*)this) << ": " << d_dtime.udiffNoReset() << " us to execute" << endl;
×
1560
  }
×
1561

1562
  d_lookupdomain = hunt;
2,341✔
1563

1564
  // Make sure we start with fresh data
1565
  d_currentrrset.clear();
2,341✔
1566
  d_currentrrsetpos = 0;
2,341✔
1567
}
2,341✔
1568

1569
bool LMDBBackend::get(DNSZoneRecord& zr)
1570
{
277,793✔
1571
  for (;;) {
381,848✔
1572
    // std::cerr<<"d_getcursor="<<d_getcursor<<std::endl;
1573
    if (!d_getcursor) {
381,848✔
1574
      d_rotxn.reset();
4,150✔
1575
      return false;
4,150✔
1576
    }
4,150✔
1577

1578
    string_view key;
377,698✔
1579

1580
    if (d_currentrrset.empty()) {
377,698✔
1581
      d_getcursor->current(d_currentKey, d_currentVal);
373,824✔
1582

1583
      key = d_currentKey.getNoStripHeader<string_view>();
373,824✔
1584
      zr.dr.d_type = compoundOrdername::getQType(key).getCode();
373,824✔
1585

1586
      if (zr.dr.d_type == QType::NSEC3) {
373,824✔
1587
        // Hit a magic NSEC3 skipping
1588
        if (d_getcursor->next(d_currentKey, d_currentVal) != 0) {
104,052✔
1589
          // cerr<<"resetting d_getcursor 1"<<endl;
1590
          d_getcursor.reset();
1,047✔
1591
        }
1,047✔
1592
        continue;
104,052✔
1593
      }
104,052✔
1594

1595
      serFromString(d_currentVal.get<string_view>(), d_currentrrset);
269,772✔
1596
      d_currentrrsettime = static_cast<time_t>(LMDBLS::LSgetTimestamp(d_currentVal.getNoStripHeader<string_view>()) / (1000UL * 1000UL * 1000UL));
269,772✔
1597
      d_currentrrsetpos = 0;
269,772✔
1598
    }
269,772✔
1599
    else {
3,874✔
1600
      key = d_currentKey.getNoStripHeader<string_view>();
3,874✔
1601
    }
3,874✔
1602
    try {
273,646✔
1603
      const auto& lrr = d_currentrrset.at(d_currentrrsetpos++);
273,646✔
1604

1605
      zr.disabled = lrr.disabled;
273,646✔
1606
      if (!zr.disabled || d_includedisabled) {
273,646✔
1607
        zr.dr.d_name = compoundOrdername::getQName(key) + d_lookupdomain;
273,643✔
1608
        zr.domain_id = compoundOrdername::getDomainID(key);
273,643✔
1609
        zr.dr.d_type = compoundOrdername::getQType(key).getCode();
273,643✔
1610
        zr.dr.d_ttl = lrr.ttl;
273,643✔
1611
        zr.dr.setContent(deserializeContentZR(zr.dr.d_type, zr.dr.d_name, lrr.content));
273,643✔
1612
        zr.auth = lrr.auth;
273,643✔
1613
      }
273,643✔
1614

1615
      if (d_currentrrsetpos >= d_currentrrset.size()) {
273,646✔
1616
        d_currentrrset.clear(); // will invalidate lrr
269,773✔
1617
        if (d_getcursor->next(d_currentKey, d_currentVal) != 0) {
269,773✔
1618
          // cerr<<"resetting d_getcursor 2"<<endl;
1619
          d_getcursor.reset();
1,882✔
1620
        }
1,882✔
1621
      }
269,773✔
1622

1623
      if (zr.disabled && !d_includedisabled) {
273,646✔
1624
        continue;
3✔
1625
      }
3✔
1626
    }
273,646✔
1627
    catch (const std::exception& e) {
273,646✔
1628
      throw PDNSException(e.what());
×
1629
    }
×
1630

1631
    break;
273,643✔
1632
  }
273,646✔
1633

1634
  return true;
273,643✔
1635
}
277,793✔
1636

1637
bool LMDBBackend::get(DNSResourceRecord& rr)
1638
{
185,568✔
1639
  DNSZoneRecord zr;
185,568✔
1640
  if (!get(zr)) {
185,568✔
1641
    return false;
1,197✔
1642
  }
1,197✔
1643

1644
  rr.qname = zr.dr.d_name;
184,371✔
1645
  rr.ttl = zr.dr.d_ttl;
184,371✔
1646
  rr.qtype = zr.dr.d_type;
184,371✔
1647
  rr.content = zr.dr.getContent()->getZoneRepresentation(true);
184,371✔
1648
  rr.domain_id = zr.domain_id;
184,371✔
1649
  rr.auth = zr.auth;
184,371✔
1650
  rr.disabled = zr.disabled;
184,371✔
1651
  rr.last_modified = d_currentrrsettime;
184,371✔
1652

1653
  return true;
184,371✔
1654
}
185,568✔
1655

1656
bool LMDBBackend::getSerial(DomainInfo& di)
1657
{
1,678✔
1658
  auto txn = getRecordsROTransaction(di.id);
1,678✔
1659
  compoundOrdername co;
1,678✔
1660
  MDBOutVal val;
1,678✔
1661
  if (!txn->txn->get(txn->db->dbi, co(di.id, g_rootdnsname, QType::SOA), val)) {
1,678✔
1662
    LMDBResourceRecord lrr;
1,179✔
1663
    serFromString(val.get<string_view>(), lrr);
1,179✔
1664
    if (lrr.content.size() >= 5 * sizeof(uint32_t)) {
1,179!
1665
      uint32_t serial;
1,179✔
1666
      // a SOA has five 32 bit fields, the first of which is the serial
1667
      // there are two variable length names before the serial, so we calculate from the back
1668
      memcpy(&serial, &lrr.content[lrr.content.size() - (5 * sizeof(uint32_t))], sizeof(serial));
1,179✔
1669
      di.serial = ntohl(serial);
1,179✔
1670
    }
1,179✔
1671
    return !lrr.disabled;
1,179✔
1672
  }
1,179✔
1673
  return false;
499✔
1674
}
1,678✔
1675

1676
bool LMDBBackend::getDomainInfo(const DNSName& domain, DomainInfo& di, bool getserial)
1677
{
1,610✔
1678
  if (!findDomain(domain, di)) {
1,610✔
1679
    return false;
296✔
1680
  }
296✔
1681
  di.backend = this;
1,314✔
1682
  consolidateDomainInfo(di);
1,314✔
1683

1684
  if (getserial) {
1,314✔
1685
    getSerial(di);
1,055✔
1686
  }
1,055✔
1687

1688
  return true;
1,314✔
1689
}
1,610✔
1690

1691
int LMDBBackend::genChangeDomain(const DNSName& domain, const std::function<void(DomainInfo&)>& func)
1692
{
532✔
1693
  DomainInfo info;
532✔
1694
  if (!findDomain(domain, info)) {
532!
1695
    return static_cast<int>(false);
×
1696
  }
×
1697
  consolidateDomainInfo(info);
532✔
1698
  func(info);
532✔
1699
  writeDomainInfo(info);
532✔
1700
  return true;
532✔
1701
}
532✔
1702

1703
int LMDBBackend::genChangeDomain(uint32_t id, const std::function<void(DomainInfo&)>& func)
1704
{
87✔
1705
  DomainInfo info;
87✔
1706
  if (!findDomain(id, info)) {
87!
1707
    return static_cast<int>(false);
×
1708
  }
×
1709
  consolidateDomainInfo(info);
87✔
1710
  func(info);
87✔
1711
  writeDomainInfo(info);
87✔
1712
  return true;
87✔
1713
}
87✔
1714

1715
bool LMDBBackend::setKind(const DNSName& domain, const DomainInfo::DomainKind kind)
1716
{
211✔
1717
  return genChangeDomain(domain, [kind](DomainInfo& di) {
211✔
1718
    di.kind = kind;
211✔
1719
  });
211✔
1720
}
211✔
1721

1722
bool LMDBBackend::setAccount(const DNSName& domain, const std::string& account)
1723
{
1✔
1724
  return genChangeDomain(domain, [account](DomainInfo& di) {
1✔
1725
    di.account = account;
1✔
1726
  });
1✔
1727
}
1✔
1728

1729
bool LMDBBackend::setPrimaries(const DNSName& domain, const vector<ComboAddress>& primaries)
1730
{
71✔
1731
  return genChangeDomain(domain, [&primaries](DomainInfo& di) {
71✔
1732
    di.primaries = primaries;
71✔
1733
  });
71✔
1734
}
71✔
1735

1736
bool LMDBBackend::createDomain(const DNSName& domain, const DomainInfo::DomainKind kind, const vector<ComboAddress>& primaries, const string& account)
1737
{
290✔
1738
  DomainInfo info;
290✔
1739

1740
  if (findDomain(domain, info)) {
290!
1741
    throw DBException("Domain '" + domain.toLogString() + "' exists already");
×
1742
  }
×
1743
  {
290✔
1744
    auto txn = d_tdomains->getRWTransaction();
290✔
1745

1746
    info.zone = domain;
290✔
1747
    info.kind = kind;
290✔
1748
    info.primaries = primaries;
290✔
1749
    info.account = account;
290✔
1750

1751
    txn.put(info, 0, d_random_ids);
290✔
1752
    txn.commit();
290✔
1753
  }
290✔
1754

1755
  return true;
290✔
1756
}
290✔
1757

1758
void LMDBBackend::getAllDomainsFiltered(vector<DomainInfo>* domains, const std::function<bool(DomainInfo&)>& allow)
1759
{
56✔
1760
  auto txn = d_tdomains->getROTransaction();
56✔
1761
  if (d_handle_dups) {
56!
1762
    map<DNSName, DomainInfo> zonemap;
×
1763
    set<DNSName> dups;
×
1764

1765
    for (auto iter = txn.begin(); iter != txn.end(); ++iter) {
×
1766
      DomainInfo di = *iter;
×
1767
      di.id = iter.getID();
×
1768
      di.backend = this;
×
1769

1770
      if (!zonemap.emplace(di.zone, di).second) {
×
1771
        dups.insert(di.zone);
×
1772
      }
×
1773
    }
×
1774

1775
    for (const auto& zone : dups) {
×
1776
      DomainInfo info;
×
1777
      // this get grabs the oldest item if there are duplicates
1778
      if (!findDomain(zone, info)) {
×
1779
        continue;
×
1780
      }
×
1781
      info.backend = this;
×
1782
      zonemap[info.zone] = info;
×
1783
    }
×
1784

1785
    for (auto& [k, v] : zonemap) {
×
1786
      if (allow(v)) {
×
1787
        consolidateDomainInfo(v);
×
1788
        domains->push_back(std::move(v));
×
1789
      }
×
1790
    }
×
1791
  }
×
1792
  else {
56✔
1793
    for (auto iter = txn.begin(); iter != txn.end(); ++iter) {
990✔
1794
      DomainInfo di = *iter;
934✔
1795
      di.id = iter.getID();
934✔
1796
      di.backend = this;
934✔
1797

1798
      if (allow(di)) {
934✔
1799
        consolidateDomainInfo(di);
651✔
1800
        domains->push_back(di);
651✔
1801
      }
651✔
1802
    }
934✔
1803
  }
56✔
1804
}
56✔
1805

1806
void LMDBBackend::getAllDomains(vector<DomainInfo>* domains, bool /* doSerial */, bool include_disabled)
1807
{
33✔
1808
  domains->clear();
33✔
1809

1810
  getAllDomainsFiltered(domains, [this, include_disabled](DomainInfo& di) {
623✔
1811
    if (!getSerial(di) && !include_disabled) {
623!
1812
      return false;
×
1813
    }
×
1814

1815
    return true;
623✔
1816
  });
623✔
1817
}
33✔
1818

1819
void LMDBBackend::getUnfreshSecondaryInfos(vector<DomainInfo>* domains)
1820
{
7✔
1821
  uint32_t serial;
7✔
1822
  time_t now = time(0);
7✔
1823
  LMDBResourceRecord lrr;
7✔
1824
  soatimes st;
7✔
1825

1826
  getAllDomainsFiltered(domains, [this, &lrr, &st, &now, &serial](DomainInfo& di) {
79✔
1827
    if (!di.isSecondaryType()) {
79!
1828
      return false;
×
1829
    }
×
1830

1831
    auto txn2 = getRecordsROTransaction(di.id);
79✔
1832
    compoundOrdername co;
79✔
1833
    MDBOutVal val;
79✔
1834
    if (!txn2->txn->get(txn2->db->dbi, co(di.id, g_rootdnsname, QType::SOA), val)) {
79✔
1835
      serFromString(val.get<string_view>(), lrr);
51✔
1836
      memcpy(&st, &lrr.content[lrr.content.size() - sizeof(soatimes)], sizeof(soatimes));
51✔
1837
      if ((time_t)(di.last_check + ntohl(st.refresh)) > now) { // still fresh
51!
1838
        return false;
51✔
1839
      }
51✔
1840
      serial = ntohl(st.serial);
×
1841
    }
×
1842
    else {
28✔
1843
      serial = 0;
28✔
1844
    }
28✔
1845

1846
    return true;
28✔
1847
  });
79✔
1848
}
7✔
1849

1850
void LMDBBackend::setStale(uint32_t domain_id)
1851
{
2✔
1852
  setLastCheckTime(domain_id, 0);
2✔
1853
}
2✔
1854

1855
void LMDBBackend::setFresh(uint32_t domain_id)
1856
{
84✔
1857
  setLastCheckTime(domain_id, time(nullptr));
84✔
1858
}
84✔
1859

1860
void LMDBBackend::setLastCheckTime(uint32_t domain_id, time_t last_check)
1861
{
86✔
1862
  if (d_write_notification_update) {
86!
1863
    genChangeDomain(domain_id, [last_check](DomainInfo& info) {
86✔
1864
      info.last_check = last_check;
86✔
1865
    });
86✔
1866
    return;
86✔
1867
  }
86✔
1868

1869
  DomainInfo info;
×
1870
  if (findDomain(domain_id, info)) {
×
1871
    auto container = s_transient_domain_info.write_lock();
×
1872
    TransientDomainInfo tdi;
×
1873
    if (!container->get(info.id, tdi)) {
×
1874
      // No data yet, initialize from DomainInfo
1875
      tdi.notified_serial = info.notified_serial;
×
1876
    }
×
1877
    tdi.last_check = last_check;
×
1878
    container->update(info.id, tdi);
×
1879
  }
×
1880
}
×
1881

1882
void LMDBBackend::getUpdatedPrimaries(vector<DomainInfo>& updatedDomains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes)
1883
{
×
1884
  CatalogInfo ci;
×
1885

1886
  getAllDomainsFiltered(&(updatedDomains), [this, &catalogs, &catalogHashes, &ci](DomainInfo& di) {
×
1887
    if (!di.isPrimaryType()) {
×
1888
      return false;
×
1889
    }
×
1890

1891
    if (di.kind == DomainInfo::Producer) {
×
1892
      catalogs.insert(di.zone);
×
1893
      catalogHashes[di.zone].process("\0");
×
1894
      return false; // Producer fresness check is performed elsewhere
×
1895
    }
×
1896

1897
    if (!di.catalog.empty()) {
×
1898
      ci.fromJson(di.options, CatalogInfo::CatalogType::Producer);
×
1899
      ci.updateHash(catalogHashes, di);
×
1900
    }
×
1901

1902
    if (getSerial(di) && di.serial != di.notified_serial) {
×
1903
      di.backend = this;
×
1904
      return true;
×
1905
    }
×
1906

1907
    return false;
×
1908
  });
×
1909
}
×
1910

1911
void LMDBBackend::setNotified(uint32_t domain_id, uint32_t serial)
1912
{
1✔
1913
  if (d_write_notification_update) {
1!
1914
    genChangeDomain(domain_id, [serial](DomainInfo& info) {
1✔
1915
      info.notified_serial = serial;
1✔
1916
    });
1✔
1917
    return;
1✔
1918
  }
1✔
1919

1920
  DomainInfo info;
×
1921
  if (findDomain(domain_id, info)) {
×
1922
    auto container = s_transient_domain_info.write_lock();
×
1923
    TransientDomainInfo tdi;
×
1924
    if (!container->get(info.id, tdi)) {
×
1925
      // No data yet, initialize from DomainInfo
1926
      tdi.last_check = info.last_check;
×
1927
    }
×
1928
    tdi.notified_serial = serial;
×
1929
    container->update(info.id, tdi);
×
1930
  }
×
1931
}
×
1932

1933
class getCatalogMembersReturnFalseException : std::runtime_error
1934
{
1935
public:
1936
  getCatalogMembersReturnFalseException() :
1937
    std::runtime_error("getCatalogMembers should return false") {}
×
1938
};
1939

1940
bool LMDBBackend::getCatalogMembers(const DNSName& catalog, vector<CatalogInfo>& members, CatalogInfo::CatalogType type)
1941
{
16✔
1942
  vector<DomainInfo> scratch;
16✔
1943

1944
  try {
16✔
1945
    getAllDomainsFiltered(&scratch, [&catalog, &members, &type](DomainInfo& di) {
232✔
1946
      if ((type == CatalogInfo::CatalogType::Producer && di.kind != DomainInfo::Primary) || (type == CatalogInfo::CatalogType::Consumer && di.kind != DomainInfo::Secondary) || di.catalog != catalog) {
232✔
1947
        return false;
28✔
1948
      }
28✔
1949

1950
      CatalogInfo ci;
204✔
1951
      ci.d_id = di.id;
204✔
1952
      ci.d_zone = di.zone;
204✔
1953
      ci.d_primaries = di.primaries;
204✔
1954
      try {
204✔
1955
        ci.fromJson(di.options, type);
204✔
1956
      }
204✔
1957
      catch (const std::runtime_error& e) {
204✔
1958
        g_log << Logger::Warning << __PRETTY_FUNCTION__ << " options '" << di.options << "' for zone '" << di.zone << "' is no valid JSON: " << e.what() << endl;
×
1959
        members.clear();
×
1960
        throw getCatalogMembersReturnFalseException();
×
1961
      }
×
1962
      members.emplace_back(ci);
204✔
1963

1964
      return false;
204✔
1965
    });
204✔
1966
  }
16✔
1967
  catch (const getCatalogMembersReturnFalseException& e) {
16✔
1968
    return false;
×
1969
  }
×
1970
  return true;
16✔
1971
}
16✔
1972

1973
bool LMDBBackend::setOptions(const DNSName& domain, const std::string& options)
1974
{
86✔
1975
  return genChangeDomain(domain, [options](DomainInfo& di) {
86✔
1976
    di.options = options;
86✔
1977
  });
86✔
1978
}
86✔
1979

1980
bool LMDBBackend::setCatalog(const DNSName& domain, const DNSName& catalog)
1981
{
163✔
1982
  return genChangeDomain(domain, [catalog](DomainInfo& di) {
163✔
1983
    di.catalog = catalog;
163✔
1984
  });
163✔
1985
}
163✔
1986

1987
bool LMDBBackend::getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string>>& meta)
1988
{
3,408✔
1989
  meta.clear();
3,408✔
1990
  auto txn = d_tmeta->getROTransaction();
3,408✔
1991
  LMDBIDvec ids;
3,408✔
1992
  txn.get_multi<0>(name, ids);
3,408✔
1993

1994
  DomainMeta dm;
3,408✔
1995
  // cerr<<"getAllDomainMetadata start"<<endl;
1996
  for (auto id : ids) {
3,738✔
1997
    if (txn.get(id, dm)) {
3,587!
1998
      meta[dm.key].push_back(dm.value);
3,587✔
1999
    }
3,587✔
2000
  }
3,587✔
2001
  return true;
3,408✔
2002
}
3,408✔
2003

2004
bool LMDBBackend::setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta)
2005
{
434✔
2006
  auto txn = d_tmeta->getRWTransaction();
434✔
2007

2008
  LMDBIDvec ids;
434✔
2009
  txn.get_multi<0>(name, ids);
434✔
2010

2011
  DomainMeta dmeta;
434✔
2012
  for (auto id : ids) {
434✔
2013
    if (txn.get(id, dmeta)) {
286!
2014
      if (dmeta.key == kind) {
286✔
2015
        // cerr<<"delete"<<endl;
2016
        txn.del(id);
32✔
2017
      }
32✔
2018
    }
286✔
2019
  }
286✔
2020

2021
  for (const auto& m : meta) {
434✔
2022
    DomainMeta dm{name, kind, m};
345✔
2023
    txn.put(dm, 0, d_random_ids);
345✔
2024
  }
345✔
2025
  txn.commit();
434✔
2026
  return true;
434✔
2027
}
434✔
2028

2029
bool LMDBBackend::getDomainKeys(const DNSName& name, std::vector<KeyData>& keys)
2030
{
695✔
2031
  auto txn = d_tkdb->getROTransaction();
695✔
2032
  LMDBIDvec ids;
695✔
2033
  txn.get_multi<0>(name, ids);
695✔
2034

2035
  KeyDataDB key;
695✔
2036

2037
  for (auto id : ids) {
695✔
2038
    if (txn.get(id, key)) {
314!
2039
      KeyData kd{key.content, id, key.flags, key.active, key.published};
314✔
2040
      keys.push_back(kd);
314✔
2041
    }
314✔
2042
  }
314✔
2043

2044
  return true;
695✔
2045
}
695✔
2046

2047
bool LMDBBackend::removeDomainKey(const DNSName& name, unsigned int id)
2048
{
29✔
2049
  auto txn = d_tkdb->getRWTransaction();
29✔
2050
  KeyDataDB kdb;
29✔
2051
  if (txn.get(id, kdb)) {
29✔
2052
    if (kdb.domain == name) {
20!
2053
      txn.del(id);
20✔
2054
      txn.commit();
20✔
2055
      return true;
20✔
2056
    }
20✔
2057
  }
20✔
2058
  // cout << "??? wanted to remove domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
2059
  return true;
9✔
2060
}
29✔
2061

2062
bool LMDBBackend::addDomainKey(const DNSName& name, const KeyData& key, int64_t& id)
2063
{
110✔
2064
  auto txn = d_tkdb->getRWTransaction();
110✔
2065
  KeyDataDB kdb{name, key.content, key.flags, key.active, key.published};
110✔
2066
  id = txn.put(kdb, 0, d_random_ids);
110✔
2067
  txn.commit();
110✔
2068

2069
  return true;
110✔
2070
}
110✔
2071

2072
bool LMDBBackend::activateDomainKey(const DNSName& name, unsigned int id)
2073
{
6✔
2074
  auto txn = d_tkdb->getRWTransaction();
6✔
2075
  KeyDataDB kdb;
6✔
2076
  if (txn.get(id, kdb)) {
6!
2077
    if (kdb.domain == name) {
6!
2078
      txn.modify(id, [](KeyDataDB& kdbarg) {
6✔
2079
        kdbarg.active = true;
6✔
2080
      });
6✔
2081
      txn.commit();
6✔
2082
      return true;
6✔
2083
    }
6✔
2084
  }
6✔
2085

2086
  // cout << "??? wanted to activate domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
2087
  return true;
×
2088
}
6✔
2089

2090
bool LMDBBackend::deactivateDomainKey(const DNSName& name, unsigned int id)
2091
{
4✔
2092
  auto txn = d_tkdb->getRWTransaction();
4✔
2093
  KeyDataDB kdb;
4✔
2094
  if (txn.get(id, kdb)) {
4!
2095
    if (kdb.domain == name) {
4!
2096
      txn.modify(id, [](KeyDataDB& kdbarg) {
4✔
2097
        kdbarg.active = false;
4✔
2098
      });
4✔
2099
      txn.commit();
4✔
2100
      return true;
4✔
2101
    }
4✔
2102
  }
4✔
2103
  // cout << "??? wanted to deactivate domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
2104
  return true;
×
2105
}
4✔
2106

2107
bool LMDBBackend::publishDomainKey(const DNSName& name, unsigned int id)
2108
{
6✔
2109
  auto txn = d_tkdb->getRWTransaction();
6✔
2110
  KeyDataDB kdb;
6✔
2111
  if (txn.get(id, kdb)) {
6!
2112
    if (kdb.domain == name) {
6!
2113
      txn.modify(id, [](KeyDataDB& kdbarg) {
6✔
2114
        kdbarg.published = true;
6✔
2115
      });
6✔
2116
      txn.commit();
6✔
2117
      return true;
6✔
2118
    }
6✔
2119
  }
6✔
2120

2121
  // cout << "??? wanted to hide domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
2122
  return true;
×
2123
}
6✔
2124

2125
bool LMDBBackend::unpublishDomainKey(const DNSName& name, unsigned int id)
2126
{
6✔
2127
  auto txn = d_tkdb->getRWTransaction();
6✔
2128
  KeyDataDB kdb;
6✔
2129
  if (txn.get(id, kdb)) {
6!
2130
    if (kdb.domain == name) {
6!
2131
      txn.modify(id, [](KeyDataDB& kdbarg) {
6✔
2132
        kdbarg.published = false;
6✔
2133
      });
6✔
2134
      txn.commit();
6✔
2135
      return true;
6✔
2136
    }
6✔
2137
  }
6✔
2138
  // cout << "??? wanted to unhide domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
2139
  return true;
×
2140
}
6✔
2141

2142
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
2143
bool LMDBBackend::getBeforeAndAfterNamesAbsolute(uint32_t id, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after)
2144
{
3,292✔
2145
  //  cout << __PRETTY_FUNCTION__<< ": "<<id <<", "<<qname << " " << unhashed<<endl;
2146

2147
  DomainInfo info;
3,292✔
2148
  if (!findDomain(id, info)) {
3,292!
2149
    // domain does not exist, tough luck
2150
    return false;
×
2151
  }
×
2152
  // cout <<"Zone: "<<info.zone<<endl;
2153

2154
  compoundOrdername co;
3,292✔
2155
  auto txn = getRecordsROTransaction(id);
3,292✔
2156

2157
  auto cursor = txn->txn->getCursor(txn->db->dbi);
3,292✔
2158
  MDBOutVal key, val;
3,292✔
2159

2160
  LMDBResourceRecord lrr;
3,292✔
2161

2162
  string matchkey = co(id, qname, QType::NSEC3);
3,292✔
2163
  if (cursor.lower_bound(matchkey, key, val)) {
3,292✔
2164
    // this is beyond the end of the database
2165
    // cout << "Beyond end of database!" << endl;
2166
    cursor.last(key, val);
78✔
2167

2168
    for (;;) {
202✔
2169
      if (co.getDomainID(key.getNoStripHeader<StringView>()) != id) {
202!
2170
        //cout<<"Last record also not part of this zone!"<<endl;
2171
        // this implies something is wrong in the database, nothing we can do
2172
        return false;
×
2173
      }
×
2174

2175
      if (co.getQType(key.getNoStripHeader<StringView>()) == QType::NSEC3) {
202✔
2176
        serFromString(val.get<StringView>(), lrr);
132✔
2177
        if (!lrr.ttl) // the kind of NSEC3 we need
132✔
2178
          break;
78✔
2179
      }
132✔
2180
      if (cursor.prev(key, val)) {
124!
2181
        // hit beginning of database, again means something is wrong with it
2182
        return false;
×
2183
      }
×
2184
    }
124✔
2185
    before = co.getQName(key.getNoStripHeader<StringView>());
78✔
2186
    unhashed = DNSName(lrr.content.c_str(), lrr.content.size(), 0, false) + info.zone;
78✔
2187

2188
    // now to find after .. at the beginning of the zone
2189
    if (cursor.lower_bound(co(id), key, val)) {
78!
2190
      // cout<<"hit end of zone find when we shouldn't"<<endl;
2191
      return false;
×
2192
    }
×
2193
    for (;;) {
512✔
2194
      if (co.getQType(key.getNoStripHeader<StringView>()) == QType::NSEC3) {
512✔
2195
        serFromString(val.get<StringView>(), lrr);
174✔
2196
        if (!lrr.ttl)
174✔
2197
          break;
78✔
2198
      }
174✔
2199

2200
      if (cursor.next(key, val) || co.getDomainID(key.getNoStripHeader<StringView>()) != id) {
434!
2201
        // cout<<"hit end of zone or database when we shouldn't"<<endl;
2202
        return false;
×
2203
      }
×
2204
    }
434✔
2205
    after = co.getQName(key.getNoStripHeader<StringView>());
78✔
2206
    // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl;
2207
    return true;
78✔
2208
  }
78✔
2209

2210
  // cout<<"Ended up at "<<co.getQName(key.get<StringView>()) <<endl;
2211

2212
  before = co.getQName(key.getNoStripHeader<StringView>());
3,214✔
2213
  if (before == qname) {
3,214✔
2214
    // cout << "Ended up on exact right node" << endl;
2215
    before = co.getQName(key.getNoStripHeader<StringView>());
1,790✔
2216
    // unhashed should be correct now, maybe check?
2217
    if (cursor.next(key, val)) {
1,790✔
2218
      // xxx should find first hash now
2219

2220
      if (cursor.lower_bound(co(id), key, val)) {
70!
2221
        // cout<<"hit end of zone find when we shouldn't for id "<<id<< __LINE__<<endl;
2222
        return false;
×
2223
      }
×
2224
      for (;;) {
402✔
2225
        if (co.getQType(key.getNoStripHeader<StringView>()) == QType::NSEC3) {
402✔
2226
          serFromString(val.get<StringView>(), lrr);
140✔
2227
          if (!lrr.ttl)
140✔
2228
            break;
70✔
2229
        }
140✔
2230

2231
        if (cursor.next(key, val) || co.getDomainID(key.getNoStripHeader<StringView>()) != id) {
332!
2232
          // cout<<"hit end of zone or database when we shouldn't" << __LINE__<<endl;
2233
          return false;
×
2234
        }
×
2235
      }
332✔
2236
      after = co.getQName(key.getNoStripHeader<StringView>());
70✔
2237
      // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl;
2238
      return true;
70✔
2239
    }
70✔
2240
  }
1,790✔
2241
  else {
1,424✔
2242
    // cout <<"Going backwards to find 'before'"<<endl;
2243
    int count = 0;
1,424✔
2244
    for (;;) {
3,588✔
2245
      if (co.getQName(key.getNoStripHeader<StringView>()).canonCompare(qname) && co.getQType(key.getNoStripHeader<StringView>()) == QType::NSEC3) {
3,588✔
2246
        // cout<<"Potentially stopping traverse at "<< co.getQName(key.get<StringView>()) <<", " << (co.getQName(key.get<StringView>()).canonCompare(qname))<<endl;
2247
        // cout<<"qname = "<<qname<<endl;
2248
        // cout<<"here  = "<<co.getQName(key.get<StringView>())<<endl;
2249
        serFromString(val.get<StringView>(), lrr);
1,649✔
2250
        if (!lrr.ttl)
1,649✔
2251
          break;
1,404✔
2252
      }
1,649✔
2253

2254
      if (cursor.prev(key, val) || co.getDomainID(key.getNoStripHeader<StringView>()) != id) {
2,184✔
2255
        // cout <<"XXX Hit *beginning* of zone or database"<<endl;
2256
        // this can happen, must deal with it
2257
        // should now find the last hash of the zone
2258

2259
        if (cursor.lower_bound(co(id + 1), key, val)) {
20!
2260
          // cout << "Could not find the next higher zone, going to the end of the database then"<<endl;
2261
          cursor.last(key, val);
20✔
2262
        }
20✔
2263
        else
×
2264
          cursor.prev(key, val);
×
2265

2266
        for (;;) {
125✔
2267
          if (co.getDomainID(key.getNoStripHeader<StringView>()) != id) {
125!
2268
            //cout<<"Last record also not part of this zone!"<<endl;
2269
            // this implies something is wrong in the database, nothing we can do
2270
            return false;
×
2271
          }
×
2272

2273
          if (co.getQType(key.getNoStripHeader<StringView>()) == QType::NSEC3) {
125✔
2274
            serFromString(val.get<StringView>(), lrr);
50✔
2275
            if (!lrr.ttl) // the kind of NSEC3 we need
50✔
2276
              break;
20✔
2277
          }
50✔
2278
          if (cursor.prev(key, val)) {
105!
2279
            // hit beginning of database, again means something is wrong with it
2280
            return false;
×
2281
          }
×
2282
        }
105✔
2283
        before = co.getQName(key.getNoStripHeader<StringView>());
20✔
2284
        unhashed = DNSName(lrr.content.c_str(), lrr.content.size(), 0, false) + info.zone;
20✔
2285
        // cout <<"Should still find 'after'!"<<endl;
2286
        // for 'after', we need to find the first hash of this zone
2287

2288
        if (cursor.lower_bound(co(id), key, val)) {
20!
2289
          // cout<<"hit end of zone find when we shouldn't"<<endl;
2290
          // means database is wrong, nothing we can do
2291
          return false;
×
2292
        }
×
2293
        for (;;) {
200✔
2294
          if (co.getQType(key.getNoStripHeader<StringView>()) == QType::NSEC3) {
200✔
2295
            serFromString(val.get<StringView>(), lrr);
60✔
2296
            if (!lrr.ttl)
60✔
2297
              break;
20✔
2298
          }
60✔
2299

2300
          if (cursor.next(key, val)) {
180!
2301
            // means database is wrong, nothing we can do
2302
            // cout<<"hit end of zone when we shouldn't 2"<<endl;
2303
            return false;
×
2304
          }
×
2305
        }
180✔
2306
        after = co.getQName(key.getNoStripHeader<StringView>());
20✔
2307

2308
        // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl;
2309
        return true;
20✔
2310
      }
20✔
2311
      ++count;
2,164✔
2312
    }
2,164✔
2313
    before = co.getQName(key.getNoStripHeader<StringView>());
1,404✔
2314
    unhashed = DNSName(lrr.content.c_str(), lrr.content.size(), 0, false) + info.zone;
1,404✔
2315
    // cout<<"Went backwards, found "<<before<<endl;
2316
    // return us to starting point
2317
    while (count--)
3,398✔
2318
      cursor.next(key, val);
1,994✔
2319
  }
1,404✔
2320
  //  cout<<"Now going forward"<<endl;
2321
  for (int count = 0;; ++count) {
7,255✔
2322
    if ((count && cursor.next(key, val)) || co.getDomainID(key.getNoStripHeader<StringView>()) != id) {
7,255✔
2323
      // cout <<"Hit end of database or zone, finding first hash then in zone "<<id<<endl;
2324
      if (cursor.lower_bound(co(id), key, val)) {
119!
2325
        // cout<<"hit end of zone find when we shouldn't"<<endl;
2326
        // means database is wrong, nothing we can do
2327
        return false;
×
2328
      }
×
2329
      for (;;) {
845✔
2330
        if (co.getQType(key.getNoStripHeader<StringView>()) == QType::NSEC3) {
845✔
2331
          serFromString(val.get<StringView>(), lrr);
285✔
2332
          if (!lrr.ttl)
285✔
2333
            break;
119✔
2334
        }
285✔
2335

2336
        if (cursor.next(key, val)) {
726!
2337
          // means database is wrong, nothing we can do
2338
          // cout<<"hit end of zone when we shouldn't 2"<<endl;
2339
          return false;
×
2340
        }
×
2341
        // cout << "Next.. "<<endl;
2342
      }
726✔
2343
      after = co.getQName(key.getNoStripHeader<StringView>());
119✔
2344

2345
      // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl;
2346
      return true;
119✔
2347
    }
119✔
2348

2349
    // cout<<"After "<<co.getQName(key.get<StringView>()) <<endl;
2350
    if (co.getQType(key.getNoStripHeader<StringView>()) == QType::NSEC3) {
7,136✔
2351
      serFromString(val.get<StringView>(), lrr);
4,205✔
2352
      if (!lrr.ttl) {
4,205✔
2353
        break;
3,005✔
2354
      }
3,005✔
2355
    }
4,205✔
2356
  }
7,136✔
2357
  after = co.getQName(key.getNoStripHeader<StringView>());
3,005✔
2358
  // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl;
2359
  return true;
3,005✔
2360
}
3,124✔
2361

2362
bool LMDBBackend::getBeforeAndAfterNames(uint32_t id, const DNSName& zonenameU, const DNSName& qname, DNSName& before, DNSName& after)
2363
{
1,373✔
2364
  DNSName zonename = zonenameU.makeLowerCase();
1,373✔
2365
  //  cout << __PRETTY_FUNCTION__<< ": "<<id <<", "<<zonename << ", '"<<qname<<"'"<<endl;
2366

2367
  auto txn = getRecordsROTransaction(id);
1,373✔
2368
  compoundOrdername co;
1,373✔
2369
  DNSName qname2 = qname.makeRelative(zonename);
1,373✔
2370
  string matchkey = co(id, qname2);
1,373✔
2371
  auto cursor = txn->txn->getCursor(txn->db->dbi);
1,373✔
2372
  MDBOutVal key, val;
1,373✔
2373
  // cout<<"Lower_bound for "<<qname2<<endl;
2374
  if (cursor.lower_bound(matchkey, key, val)) {
1,373✔
2375
    // cout << "Hit end of database, bummer"<<endl;
2376
    cursor.last(key, val);
195✔
2377
    if (co.getDomainID(key.getNoStripHeader<string_view>()) == id) {
195!
2378
      before = co.getQName(key.getNoStripHeader<string_view>()) + zonename;
195✔
2379
      after = zonename;
195✔
2380
    }
195✔
2381
    // else
2382
    // cout << "We were at end of database, but this zone is not there?!"<<endl;
2383
    return true;
195✔
2384
  }
195✔
2385
  // cout<<"Cursor is at "<<co.getQName(key.get<string_view>()) <<", in zone id "<<co.getDomainID(key.get<string_view>())<< endl;
2386

2387
  if (co.getQType(key.getNoStripHeader<string_view>()).getCode() && co.getDomainID(key.getNoStripHeader<string_view>()) == id && co.getQName(key.getNoStripHeader<string_view>()) == qname2) { // don't match ENTs
1,178!
2388
    // cout << "Had an exact match!"<<endl;
2389
    before = qname2 + zonename;
590✔
2390
    int rc;
590✔
2391
    for (;;) {
2,205✔
2392
      rc = cursor.next(key, val);
2,205✔
2393
      if (rc)
2,205✔
2394
        break;
24✔
2395

2396
      if (co.getDomainID(key.getNoStripHeader<string_view>()) == id && key.getNoStripHeader<StringView>().rfind(matchkey, 0) == 0)
2,181!
2397
        continue;
1,257✔
2398
      LMDBResourceRecord lrr;
924✔
2399
      serFromString(val.get<StringView>(), lrr);
924✔
2400
      if (co.getQType(key.getNoStripHeader<string_view>()).getCode() && (lrr.auth || co.getQType(key.getNoStripHeader<string_view>()).getCode() == QType::NS))
924✔
2401
        break;
566✔
2402
    }
924✔
2403
    if (rc || co.getDomainID(key.getNoStripHeader<string_view>()) != id) {
590!
2404
      // cout << "We hit the end of the zone or database. 'after' is apex" << endl;
2405
      after = zonename;
24✔
2406
      return false;
24✔
2407
    }
24✔
2408
    after = co.getQName(key.getNoStripHeader<string_view>()) + zonename;
566✔
2409
    return true;
566✔
2410
  }
590✔
2411

2412
  if (co.getDomainID(key.getNoStripHeader<string_view>()) != id) {
588!
2413
    // cout << "Ended up in next zone, 'after' is zonename" <<endl;
UNCOV
2414
    after = zonename;
×
2415
    // cout << "Now hunting for previous" << endl;
UNCOV
2416
    int rc;
×
UNCOV
2417
    for (;;) {
×
UNCOV
2418
      rc = cursor.prev(key, val);
×
UNCOV
2419
      if (rc) {
×
2420
        // cout<<"Reversed into zone, but got not found from lmdb" <<endl;
2421
        return false;
×
2422
      }
×
2423

UNCOV
2424
      if (co.getDomainID(key.getNoStripHeader<string_view>()) != id) {
×
2425
        // cout<<"Reversed into zone, but found wrong zone id " << co.getDomainID(key.getNoStripHeader<string_view>()) << " != "<<id<<endl;
2426
        // "this can't happen"
2427
        return false;
×
2428
      }
×
UNCOV
2429
      LMDBResourceRecord lrr;
×
UNCOV
2430
      serFromString(val.get<StringView>(), lrr);
×
UNCOV
2431
      if (co.getQType(key.getNoStripHeader<string_view>()).getCode() && (lrr.auth || co.getQType(key.getNoStripHeader<string_view>()).getCode() == QType::NS))
×
UNCOV
2432
        break;
×
UNCOV
2433
    }
×
2434

UNCOV
2435
    before = co.getQName(key.getNoStripHeader<string_view>()) + zonename;
×
2436
    // cout<<"Found: "<< before<<endl;
UNCOV
2437
    return true;
×
UNCOV
2438
  }
×
2439

2440
  // cout <<"We ended up after "<<qname<<", on "<<co.getQName(key.getNoStripHeader<string_view>())<<endl;
2441

2442
  int skips = 0;
588✔
2443
  for (;;) {
958✔
2444
    LMDBResourceRecord lrr;
958✔
2445
    serFromString(val.get<StringView>(), lrr);
958✔
2446
    if (co.getQType(key.getNoStripHeader<string_view>()).getCode() && (lrr.auth || co.getQType(key.getNoStripHeader<string_view>()).getCode() == QType::NS)) {
958!
2447
      after = co.getQName(key.getNoStripHeader<string_view>()) + zonename;
588✔
2448
      // cout <<"Found auth ("<<lrr.auth<<") or an NS record "<<after<<", type: "<<co.getQType(key.getNoStripHeader<string_view>()).toString()<<", ttl = "<<lrr.ttl<<endl;
2449
      // cout << makeHexDump(val.get<string>()) << endl;
2450
      break;
588✔
2451
    }
588✔
2452
    // cout <<"  oops, " << co.getQName(key.getNoStripHeader<string_view>()) << " was not auth "<<lrr.auth<< " type=" << lrr.qtype.toString()<<" or NS, so need to skip ahead a bit more" << endl;
2453
    int rc = cursor.next(key, val);
370✔
2454
    if (!rc)
370!
2455
      ++skips;
370✔
2456
    if (rc || co.getDomainID(key.getNoStripHeader<string_view>()) != id) {
370!
2457
      // cout << "  oops, hit end of database or zone. This means after is apex" <<endl;
2458
      after = zonename;
×
2459
      break;
×
2460
    }
×
2461
  }
370✔
2462
  // go back to where we were
2463
  while (skips--)
958✔
2464
    cursor.prev(key, val);
370✔
2465

2466
  for (;;) {
1,058✔
2467
    int rc = cursor.prev(key, val);
1,058✔
2468
    if (rc || co.getDomainID(key.getNoStripHeader<string_view>()) != id) {
1,058!
2469
      // XX I don't think this case can happen
2470
      // cout << "We hit the beginning of the zone or database.. now what" << endl;
2471
      return false;
×
2472
    }
×
2473
    before = co.getQName(key.getNoStripHeader<string_view>()) + zonename;
1,058✔
2474
    LMDBResourceRecord lrr;
1,058✔
2475
    serFromString(val.get<string_view>(), lrr);
1,058✔
2476
    // cout<<"And before to "<<before<<", auth = "<<rr.auth<<endl;
2477
    if (co.getQType(key.getNoStripHeader<string_view>()).getCode() && (lrr.auth || co.getQType(key.getNoStripHeader<string_view>()) == QType::NS))
1,058✔
2478
      break;
588✔
2479
    // cout << "Oops, that was wrong, go back one more"<<endl;
2480
  }
1,058✔
2481

2482
  return true;
588✔
2483
}
588✔
2484

2485
bool LMDBBackend::updateDNSSECOrderNameAndAuth(uint32_t domain_id, const DNSName& qname, const DNSName& ordername, bool auth, const uint16_t qtype)
2486
{
61,166✔
2487
  //  cout << __PRETTY_FUNCTION__<< ": "<< domain_id <<", '"<<qname <<"', '"<<ordername<<"', "<<auth<< ", " << qtype << endl;
2488
  shared_ptr<RecordsRWTransaction> txn;
61,166✔
2489
  bool needCommit = false;
61,166✔
2490
  if (d_rwtxn && d_transactiondomainid == domain_id) {
61,166!
2491
    txn = d_rwtxn;
61,166✔
2492
    //    cout<<"Reusing open transaction"<<endl;
2493
  }
61,166✔
2494
  else {
×
2495
    //    cout<<"Making a new RW txn for " << __PRETTY_FUNCTION__ <<endl;
2496
    txn = getRecordsRWTransaction(domain_id);
×
2497
    needCommit = true;
×
2498
  }
×
2499

2500
  DomainInfo info;
61,166✔
2501
  if (!findDomain(domain_id, info)) {
61,166!
2502
    //    cout<<"Could not find domain_id "<<domain_id <<endl;
2503
    return false;
×
2504
  }
×
2505

2506
  DNSName rel = qname.makeRelative(info.zone);
61,166✔
2507

2508
  compoundOrdername co;
61,166✔
2509
  string matchkey = co(domain_id, rel);
61,166✔
2510

2511
  auto cursor = txn->txn->getCursor(txn->db->dbi);
61,166✔
2512
  MDBOutVal key, val;
61,166✔
2513
  if (cursor.prefix(matchkey, key, val) != 0) {
61,166!
2514
    // cout << "Could not find anything"<<endl;
2515
    return false;
×
2516
  }
×
2517

2518
  bool hasOrderName = !ordername.empty();
61,166✔
2519
  bool needNSEC3 = hasOrderName;
61,166✔
2520

2521
  do {
61,656✔
2522
    vector<LMDBResourceRecord> lrrs;
61,656✔
2523

2524
    if (co.getQType(key.getNoStripHeader<StringView>()) != QType::NSEC3) {
61,656✔
2525
      serFromString(val.get<StringView>(), lrrs);
61,575✔
2526
      bool changed = false;
61,575✔
2527
      vector<LMDBResourceRecord> newRRs;
61,575✔
2528
      for (auto& lrr : lrrs) {
62,211✔
2529
        lrr.qtype = co.getQType(key.getNoStripHeader<StringView>());
62,211✔
2530
        if (!needNSEC3 && qtype != QType::ANY) {
62,211✔
2531
          needNSEC3 = (lrr.ordername && QType(qtype) != lrr.qtype);
364✔
2532
        }
364✔
2533

2534
        if ((qtype == QType::ANY || QType(qtype) == lrr.qtype) && (lrr.ordername != hasOrderName || lrr.auth != auth)) {
62,211✔
2535
          lrr.auth = auth;
61,185✔
2536
          lrr.ordername = hasOrderName;
61,185✔
2537
          changed = true;
61,185✔
2538
        }
61,185✔
2539
        newRRs.push_back(std::move(lrr));
62,211✔
2540
      }
62,211✔
2541
      if (changed) {
61,575✔
2542
        cursor.put(key, serToString(newRRs));
60,813✔
2543
      }
60,813✔
2544
    }
61,575✔
2545

2546
  } while (cursor.next(key, val) == 0);
61,656✔
2547

2548
  bool del = false;
61,166✔
2549
  LMDBResourceRecord lrr;
61,166✔
2550
  matchkey = co(domain_id, rel, QType::NSEC3);
61,166✔
2551
  // cerr<<"here qname="<<qname<<" ordername="<<ordername<<" qtype="<<qtype<<" matchkey="<<makeHexDump(matchkey)<<endl;
2552
  int txngetrc;
61,166✔
2553
  if (!(txngetrc = txn->txn->get(txn->db->dbi, matchkey, val))) {
61,166✔
2554
    serFromString(val.get<string_view>(), lrr);
81✔
2555

2556
    if (needNSEC3) {
81✔
2557
      if (hasOrderName && lrr.content != ordername.toDNSStringLC()) {
68✔
2558
        del = true;
1✔
2559
      }
1✔
2560
    }
68✔
2561
    else {
13✔
2562
      del = true;
13✔
2563
    }
13✔
2564
    if (del) {
81✔
2565
      txn->txn->del(txn->db->dbi, co(domain_id, DNSName(lrr.content.c_str(), lrr.content.size(), 0, false), QType::NSEC3));
14✔
2566
      txn->txn->del(txn->db->dbi, matchkey);
14✔
2567
    }
14✔
2568
  }
81✔
2569
  else {
61,085✔
2570
    del = true;
61,085✔
2571
  }
61,085✔
2572

2573
  if (hasOrderName && del) {
61,166✔
2574
    matchkey = co(domain_id, rel, QType::NSEC3);
60,548✔
2575

2576
    lrr.ttl = 0;
60,548✔
2577
    lrr.auth = 0;
60,548✔
2578
    lrr.content = rel.toDNSStringLC();
60,548✔
2579

2580
    string str = serToString(lrr);
60,548✔
2581
    txn->txn->put(txn->db->dbi, co(domain_id, ordername, QType::NSEC3), str);
60,548✔
2582
    lrr.ttl = 1;
60,548✔
2583
    lrr.content = ordername.toDNSStringLC();
60,548✔
2584
    str = serToString(lrr);
60,548✔
2585
    txn->txn->put(txn->db->dbi, matchkey, str); // 2
60,548✔
2586
  }
60,548✔
2587

2588
  if (needCommit)
61,166!
2589
    txn->txn->commit();
×
2590
  return false;
61,166✔
2591
}
61,166✔
2592

2593
bool LMDBBackend::updateEmptyNonTerminals(uint32_t domain_id, set<DNSName>& insert, set<DNSName>& erase, bool remove)
2594
{
35✔
2595
  // cout << __PRETTY_FUNCTION__<< ": "<< domain_id << ", insert.size() "<<insert.size()<<", "<<erase.size()<<", " <<remove<<endl;
2596

2597
  bool needCommit = false;
35✔
2598
  shared_ptr<RecordsRWTransaction> txn;
35✔
2599
  if (d_rwtxn && d_transactiondomainid == domain_id) {
35!
2600
    txn = d_rwtxn;
35✔
2601
    //    cout<<"Reusing open transaction"<<endl;
2602
  }
35✔
2603
  else {
×
2604
    //    cout<<"Making a new RW txn for delete domain"<<endl;
2605
    txn = getRecordsRWTransaction(domain_id);
×
2606
    needCommit = true;
×
2607
  }
×
2608

2609
  DomainInfo info;
35✔
2610
  if (!findDomain(domain_id, info)) {
35!
2611
    // cout <<"No such domain with id "<<domain_id<<endl;
2612
    return false;
×
2613
  }
×
2614

2615
  // if remove is set, all ENTs should be removed & nothing else should be done
2616
  if (remove) {
35!
2617
    deleteDomainRecords(*txn, domain_id, 0);
×
2618
  }
×
2619
  else {
35✔
2620
    compoundOrdername co;
35✔
2621
    for (const auto& n : insert) {
159✔
2622
      LMDBResourceRecord lrr;
159✔
2623
      lrr.qname = n.makeRelative(info.zone);
159✔
2624
      lrr.ttl = 0;
159✔
2625
      lrr.auth = true;
159✔
2626

2627
      std::string ser = serToString(lrr);
159✔
2628

2629
      txn->txn->put(txn->db->dbi, co(domain_id, lrr.qname, 0), ser);
159✔
2630

2631
      // cout <<" +"<<n<<endl;
2632
    }
159✔
2633
    for (auto n : erase) {
35✔
2634
      // cout <<" -"<<n<<endl;
2635
      n.makeUsRelative(info.zone);
1✔
2636
      txn->txn->del(txn->db->dbi, co(domain_id, n, 0));
1✔
2637
    }
1✔
2638
  }
35✔
2639
  if (needCommit)
35!
2640
    txn->txn->commit();
×
2641
  return false;
35✔
2642
}
35✔
2643

2644
/* TSIG */
2645
bool LMDBBackend::getTSIGKey(const DNSName& name, DNSName& algorithm, string& content)
2646
{
71✔
2647
  auto txn = d_ttsig->getROTransaction();
71✔
2648
  LMDBIDvec ids;
71✔
2649
  txn.get_multi<0>(name, ids);
71✔
2650

2651
  TSIGKey key;
71✔
2652
  for (auto id : ids) {
71✔
2653
    if (txn.get(id, key)) {
55!
2654
      if (algorithm.empty() || algorithm == DNSName(key.algorithm)) {
55!
2655
        algorithm = DNSName(key.algorithm);
55✔
2656
        content = key.key;
55✔
2657
      }
55✔
2658
    }
55✔
2659
  }
55✔
2660

2661
  return true;
71✔
2662
}
71✔
2663

2664
// this deletes an old key if it has the same algorithm
2665
bool LMDBBackend::setTSIGKey(const DNSName& name, const DNSName& algorithm, const string& content)
2666
{
22✔
2667
  auto txn = d_ttsig->getRWTransaction();
22✔
2668

2669
  LMDBIDvec ids;
22✔
2670
  txn.get_multi<0>(name, ids);
22✔
2671

2672
  TSIGKey key;
22✔
2673
  for (auto id : ids) {
22✔
2674
    if (txn.get(id, key)) {
2!
2675
      if (key.algorithm == algorithm) {
2✔
2676
        txn.del(id);
1✔
2677
      }
1✔
2678
    }
2✔
2679
  }
2✔
2680

2681
  TSIGKey tk;
22✔
2682
  tk.name = name;
22✔
2683
  tk.algorithm = algorithm;
22✔
2684
  tk.key = content;
22✔
2685

2686
  txn.put(tk, 0, d_random_ids);
22✔
2687
  txn.commit();
22✔
2688

2689
  return true;
22✔
2690
}
22✔
2691
bool LMDBBackend::deleteTSIGKey(const DNSName& name)
2692
{
2✔
2693
  auto txn = d_ttsig->getRWTransaction();
2✔
2694

2695
  LMDBIDvec ids;
2✔
2696
  txn.get_multi<0>(name, ids);
2✔
2697

2698
  TSIGKey key;
2✔
2699

2700
  for (auto id : ids) {
2✔
2701
    if (txn.get(id, key)) {
2!
2702
      txn.del(id);
2✔
2703
    }
2✔
2704
  }
2✔
2705
  txn.commit();
2✔
2706
  return true;
2✔
2707
}
2✔
2708
bool LMDBBackend::getTSIGKeys(std::vector<struct TSIGKey>& keys)
2709
{
1✔
2710
  auto txn = d_ttsig->getROTransaction();
1✔
2711

2712
  keys.clear();
1✔
2713
  for (auto iter = txn.begin(); iter != txn.end(); ++iter) {
11✔
2714
    keys.push_back(*iter);
10✔
2715
  }
10✔
2716
  return true;
1✔
2717
}
1✔
2718

2719
string LMDBBackend::directBackendCmd(const string& query)
2720
{
×
2721
  ostringstream ret, usage;
×
2722

2723
  usage << "info                               show some information about the database" << endl;
×
2724
  usage << "index check domains                check zone<>ID indexes" << endl;
×
2725
  usage << "index refresh domains <ID>         refresh index for zone with this ID" << endl;
×
2726
  usage << "index refresh-all domains          refresh index for all zones with disconnected indexes" << endl;
×
2727
  vector<string> argv;
×
2728
  stringtok(argv, query);
×
2729

2730
  if (argv.empty()) {
×
2731
    return usage.str();
×
2732
  }
×
2733

2734
  string& cmd = argv[0];
×
2735

2736
  if (cmd == "help") {
×
2737
    return usage.str();
×
2738
  }
×
2739

2740
  if (cmd == "info") {
×
2741
    ret << "shards: " << s_shards << endl;
×
2742
    ret << "schemaversion: " << SCHEMAVERSION << endl;
×
2743

2744
    return ret.str();
×
2745
  }
×
2746

2747
  if (cmd == "index") {
×
2748
    if (argv.size() < 2) {
×
2749
      return "need an index subcommand\n";
×
2750
    }
×
2751

2752
    string& subcmd = argv[1];
×
2753

2754
    if (subcmd == "check" || subcmd == "refresh-all") {
×
2755
      bool refresh = false;
×
2756

2757
      if (subcmd == "refresh-all") {
×
2758
        refresh = true;
×
2759
      }
×
2760

2761
      if (argv.size() < 3) {
×
2762
        return "need an index name\n";
×
2763
      }
×
2764

2765
      if (argv[2] != "domains") {
×
2766
        return "can only check the domains index\n";
×
2767
      }
×
2768

2769
      vector<uint32_t> refreshQueue;
×
2770

2771
      {
×
2772
        auto txn = d_tdomains->getROTransaction();
×
2773

2774
        for (auto iter = txn.begin(); iter != txn.end(); ++iter) {
×
2775
          DomainInfo di = *iter;
×
2776

2777
          auto id = iter.getID();
×
2778

2779
          LMDBIDvec ids;
×
2780
          txn.get_multi<0>(di.zone, ids);
×
2781

2782
          if (ids.size() != 1) {
×
2783
            ret << "ID->zone index has " << id << "->" << di.zone << ", ";
×
2784

2785
            if (ids.empty()) {
×
2786
              ret << "zone->ID index has no entry for " << di.zone << endl;
×
2787
              if (refresh) {
×
2788
                refreshQueue.push_back(id);
×
2789
              }
×
2790
              else {
×
2791
                ret << "  suggested remedy: index refresh domains " << id << endl;
×
2792
              }
×
2793
            }
×
2794
            else {
×
2795
              // ids.size() > 1
2796
              ret << "zone->ID index has multiple entries for " << di.zone << ": ";
×
2797
              for (auto id_ : ids) {
×
2798
                ret << id_ << " ";
×
2799
              }
×
2800
              ret << endl;
×
2801
            }
×
2802
          }
×
2803
        }
×
2804
      }
×
2805

2806
      if (refresh) {
×
2807
        for (const auto& id : refreshQueue) {
×
2808
          if (genChangeDomain(id, [](DomainInfo& /* di */) {})) {
×
2809
            ret << "refreshed " << id << endl;
×
2810
          }
×
2811
          else {
×
2812
            ret << "failed to refresh " << id << endl;
×
2813
          }
×
2814
        }
×
2815
      }
×
2816
      return ret.str();
×
2817
    }
×
2818
    if (subcmd == "refresh") {
×
2819
      // index refresh domains 12345
2820
      if (argv.size() < 4) {
×
2821
        return "usage: index refresh domains <ID>\n";
×
2822
      }
×
2823

2824
      if (argv[2] != "domains") {
×
2825
        return "can only refresh in the domains index\n";
×
2826
      }
×
2827

2828
      uint32_t id = 0;
×
2829

2830
      try {
×
2831
        id = pdns::checked_stoi<uint32_t>(argv[3]);
×
2832
      }
×
2833
      catch (const std::out_of_range& e) {
×
2834
        return "ID out of range\n";
×
2835
      }
×
2836

2837
      if (genChangeDomain(id, [](DomainInfo& /* di */) {})) {
×
2838
        ret << "refreshed" << endl;
×
2839
      }
×
2840
      else {
×
2841
        ret << "failed" << endl;
×
2842
      }
×
2843
      return ret.str();
×
2844
    }
×
2845
  }
×
2846

2847
  return "unknown lmdbbackend command\n";
×
2848
}
×
2849

2850
class LMDBFactory : public BackendFactory
2851
{
2852
public:
2853
  LMDBFactory() :
2854
    BackendFactory("lmdb") {}
3,230✔
2855
  void declareArguments(const string& suffix = "") override
2856
  {
651✔
2857
    declare(suffix, "filename", "Filename for lmdb", "./pdns.lmdb");
651✔
2858
    declare(suffix, "sync-mode", "Synchronisation mode: nosync, nometasync, sync", "sync");
651✔
2859
    // there just is no room for more on 32 bit
2860
    declare(suffix, "shards", "Records database will be split into this number of shards", (sizeof(void*) == 4) ? "2" : "64");
651✔
2861
    declare(suffix, "schema-version", "Maximum allowed schema version to run on this DB. If a lower version is found, auto update is performed", std::to_string(SCHEMAVERSION));
651✔
2862
    declare(suffix, "random-ids", "Numeric IDs inside the database are generated randomly instead of sequentially", "no");
651✔
2863
    declare(suffix, "map-size", "LMDB map size in megabytes", (sizeof(void*) == 4) ? "100" : "16000");
651✔
2864
    declare(suffix, "flag-deleted", "Flag entries on deletion instead of deleting them", "no");
651✔
2865
    declare(suffix, "write-notification-update", "Do not update domain table upon notification", "yes");
651✔
2866
    declare(suffix, "lightning-stream", "Run in Lightning Stream compatible mode", "no");
651✔
2867
  }
651✔
2868
  DNSBackend* make(const string& suffix = "") override
2869
  {
2,255✔
2870
    return new LMDBBackend(suffix);
2,255✔
2871
  }
2,255✔
2872
};
2873

2874
/* THIRD PART */
2875

2876
class LMDBLoader
2877
{
2878
public:
2879
  LMDBLoader()
2880
  {
3,230✔
2881
    BackendMakers().report(std::make_unique<LMDBFactory>());
3,230✔
2882
    g_log << Logger::Info << "[lmdbbackend] This is the lmdb backend version " VERSION
3,230✔
2883
#ifndef REPRODUCIBLE
3,230✔
2884
          << " (" __DATE__ " " __TIME__ ")"
3,230✔
2885
#endif
3,230✔
2886
          << " reporting" << endl;
3,230✔
2887
  }
3,230✔
2888
};
2889

2890
static LMDBLoader randomLoader;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc