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

PowerDNS / pdns / 11212709231

07 Oct 2024 09:32AM UTC coverage: 64.717% (-0.02%) from 64.733%
11212709231

push

github

web-flow
Merge pull request #14694 from omoerbeek/rec-rpz-tweaks

rec: rpz tweaks

37108 of 88048 branches covered (42.15%)

Branch coverage included in aggregate %.

6 of 10 new or added lines in 3 files covered. (60.0%)

92 existing lines in 16 files now uncovered.

124989 of 162421 relevant lines covered (76.95%)

5075527.77 hits per line

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

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

51
#include <boost/iostreams/device/back_inserter.hpp>
52

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

57
#include <stdio.h>
58
#include <unistd.h>
59

60
#include "lmdbbackend.hh"
61

62
#define SCHEMAVERSION 5
1,356✔
63

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

68
static bool s_first = true;
69
static int s_shards = 0;
70
static std::mutex s_lmdbStartupLock;
71

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

76
  uint32_t schemaversion;
680✔
77

78
  MDB_env* tmpEnv = nullptr;
680✔
79

80
  if (mdb_env_create(&tmpEnv) != 0) {
680!
81
    throw std::runtime_error("mdb_env_create failed");
×
82
  }
×
83

84
  std::unique_ptr<MDB_env, decltype(&mdb_env_close)> env{tmpEnv, mdb_env_close};
680✔
85

86
  if (mdb_env_set_mapsize(tmpEnv, 0) != 0) {
680!
87
    throw std::runtime_error("mdb_env_set_mapsize failed");
×
88
  }
×
89

90
  if (mdb_env_set_maxdbs(tmpEnv, 20) != 0) { // we need 17: 1 {"pdns"} + 4 {"domains", "keydata", "tsig", "metadata"} * 2 {v4, v5} * 2 {main, index in _0}
680!
91
    throw std::runtime_error("mdb_env_set_maxdbs failed");
×
92
  }
×
93

94
  {
680✔
95
    int retCode = mdb_env_open(tmpEnv, filename.c_str(), MDB_NOSUBDIR | MDB_RDONLY, 0600);
680✔
96
    if (retCode != 0) {
680✔
97
      if (retCode == ENOENT) {
23!
98
        // we don't have a database yet! report schema 0, with 0 shards
99
        return {0U, 0U};
23✔
100
      }
23✔
101
      throw std::runtime_error("mdb_env_open failed");
×
102
    }
23✔
103
  }
680✔
104

105
  MDB_txn* txn = nullptr;
657✔
106

107
  if (mdb_txn_begin(tmpEnv, nullptr, MDB_RDONLY, &txn) != 0) {
657!
108
    throw std::runtime_error("mdb_txn_begin failed");
×
109
  }
×
110

111
  MDB_dbi dbi;
657✔
112

113
  {
657✔
114
    int retCode = mdb_dbi_open(txn, "pdns", 0, &dbi);
657✔
115
    if (retCode != 0) {
657!
116
      if (retCode == MDB_NOTFOUND) {
×
117
        // this means nothing has been inited yet
118
        // we pretend this means 5
119
        mdb_txn_abort(txn);
×
120
        return {5U, 0U};
×
121
      }
×
122
      mdb_txn_abort(txn);
×
123
      throw std::runtime_error("mdb_dbi_open failed");
×
124
    }
×
125
  }
657✔
126

127
  MDB_val key, data;
657✔
128

129
  key.mv_data = (char*)"schemaversion";
657✔
130
  key.mv_size = strlen((char*)key.mv_data);
657✔
131

132
  {
657✔
133
    int retCode = mdb_get(txn, dbi, &key, &data);
657✔
134
    if (retCode != 0) {
657!
135
      if (retCode == MDB_NOTFOUND) {
×
136
        // this means nothing has been inited yet
137
        // we pretend this means 5
138
        mdb_txn_abort(txn);
×
139
        return {5U, 0U};
×
140
      }
×
141

142
      throw std::runtime_error("mdb_get pdns.schemaversion failed");
×
143
    }
×
144
  }
657✔
145

146
  if (data.mv_size == 4) {
657✔
147
    // schemaversion is < 5 and is stored in 32 bits, in host order
148

149
    memcpy(&schemaversion, data.mv_data, data.mv_size);
4✔
150
  }
4✔
151
  else if (data.mv_size >= LMDBLS::LS_MIN_HEADER_SIZE + sizeof(schemaversion)) {
653!
152
    // schemaversion presumably is 5, stored in 32 bits, network order, after the LS header
153

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

163
  uint32_t shards;
657✔
164

165
  key.mv_data = (char*)"shards";
657✔
166
  key.mv_size = strlen((char*)key.mv_data);
657✔
167

168
  {
657✔
169
    int retCode = mdb_get(txn, dbi, &key, &data);
657✔
170
    if (retCode != 0) {
657!
171
      if (retCode == MDB_NOTFOUND) {
×
172
        cerr << "schemaversion was set, but shards was not. Dazed and confused, trying to exit." << endl;
×
173
        mdb_txn_abort(txn);
×
174
        // NOLINTNEXTLINE(concurrency-mt-unsafe)
175
        exit(1);
×
176
      }
×
177

178
      throw std::runtime_error("mdb_get pdns.shards failed");
×
179
    }
×
180
  }
657✔
181

182
  if (data.mv_size == 4) {
657✔
183
    // 'shards' is stored in 32 bits, in host order
184

185
    memcpy(&shards, data.mv_data, data.mv_size);
4✔
186
  }
4✔
187
  else if (data.mv_size >= LMDBLS::LS_MIN_HEADER_SIZE + sizeof(shards)) {
653!
188
    // FIXME: get actual header size (including extension blocks) instead of just reading from the back
189
    memcpy(&shards, (char*)data.mv_data + data.mv_size - sizeof(shards), sizeof(shards));
653✔
190
    shards = ntohl(shards);
653✔
191
  }
653✔
192
  else {
×
193
    throw std::runtime_error("pdns.shards had unexpected size");
×
194
  }
×
195

196
  mdb_txn_abort(txn);
657✔
197

198
  return {schemaversion, shards};
657✔
199
}
657✔
200

201
namespace
202
{
203
// copy sdbi to tdbi, prepending an empty LS header (24 bytes of '\0') to all values
204
void copyDBIAndAddLSHeader(MDB_txn* txn, MDB_dbi sdbi, MDB_dbi tdbi)
205
{
4✔
206
  // FIXME: clear out target dbi first
207

208
  std::string header(LMDBLS::LS_MIN_HEADER_SIZE, '\0');
4✔
209
  int rc;
4✔
210

211
  MDB_cursor* cur;
4✔
212

213
  if ((rc = mdb_cursor_open(txn, sdbi, &cur)) != 0) {
4!
214
    throw std::runtime_error("mdb_cursur_open failed");
×
215
  }
×
216

217
  MDB_val key, data;
4✔
218

219
  rc = mdb_cursor_get(cur, &key, &data, MDB_FIRST);
4✔
220

221
  while (rc == 0) {
40,448✔
222
    std::string skey(reinterpret_cast<const char*>(key.mv_data), key.mv_size);
40,444✔
223
    std::string sdata(reinterpret_cast<const char*>(data.mv_data), data.mv_size);
40,444✔
224

225
    std::string stdata = header + sdata;
40,444✔
226

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

229
    MDB_val tkey;
40,444✔
230
    MDB_val tdata;
40,444✔
231

232
    tkey.mv_data = const_cast<char*>(skey.c_str());
40,444✔
233
    tkey.mv_size = skey.size();
40,444✔
234
    tdata.mv_data = const_cast<char*>(stdata.c_str());
40,444✔
235
    tdata.mv_size = stdata.size();
40,444✔
236

237
    if ((rc = mdb_put(txn, tdbi, &tkey, &tdata, 0)) != 0) {
40,444!
238
      throw std::runtime_error("mdb_put failed");
×
239
    }
×
240

241
    rc = mdb_cursor_get(cur, &key, &data, MDB_NEXT);
40,444✔
242
  }
40,444✔
243
  if (rc != MDB_NOTFOUND) {
4!
244
    cerr << "rc=" << rc << endl;
×
245
    throw std::runtime_error("error while iterating dbi");
×
246
  }
×
247
}
4✔
248

249
// migrated a typed DBI:
250
// 1. change keys (uint32_t) from host to network order
251
// 2. prepend empty LS header to values
252
void copyTypedDBI(MDB_txn* txn, MDB_dbi sdbi, MDB_dbi tdbi)
253
{
8✔
254
  // FIXME: clear out target dbi first
255

256
  std::string header(LMDBLS::LS_MIN_HEADER_SIZE, '\0');
8✔
257
  int rc;
8✔
258

259
  MDB_cursor* cur;
8✔
260

261
  if ((rc = mdb_cursor_open(txn, sdbi, &cur)) != 0) {
8!
262
    throw std::runtime_error("mdb_cursur_open failed");
×
263
  }
×
264

265
  MDB_val key, data;
8✔
266

267
  rc = mdb_cursor_get(cur, &key, &data, MDB_FIRST);
8✔
268

269
  while (rc == 0) {
40✔
270
    // std::string skey((char*) key.mv_data, key.mv_size);
271
    std::string sdata(reinterpret_cast<const char*>(data.mv_data), data.mv_size);
32✔
272

273
    std::string stdata = header + sdata;
32✔
274

275
    uint32_t id;
32✔
276

277
    if (key.mv_size != sizeof(uint32_t)) {
32!
278
      throw std::runtime_error("got non-uint32_t key in TypedDBI");
×
279
    }
×
280

281
    memcpy(&id, key.mv_data, sizeof(uint32_t));
32✔
282

283
    id = htonl(id);
32✔
284

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

287
    MDB_val tkey;
32✔
288
    MDB_val tdata;
32✔
289

290
    tkey.mv_data = reinterpret_cast<char*>(&id);
32✔
291
    tkey.mv_size = sizeof(uint32_t);
32✔
292
    tdata.mv_data = const_cast<char*>(stdata.c_str());
32✔
293
    tdata.mv_size = stdata.size();
32✔
294

295
    if ((rc = mdb_put(txn, tdbi, &tkey, &tdata, 0)) != 0) {
32!
296
      throw std::runtime_error("mdb_put failed");
×
297
    }
×
298

299
    rc = mdb_cursor_get(cur, &key, &data, MDB_NEXT);
32✔
300
  }
32✔
301
  if (rc != MDB_NOTFOUND) {
8!
302
    cerr << "rc=" << rc << endl;
×
303
    throw std::runtime_error("error while iterating dbi");
×
304
  }
×
305
}
8✔
306

307
// migrating an index DBI:
308
// newkey = oldkey.len(), oldkey, htonl(oldvalue)
309
// newvalue = empty lsheader
310
void copyIndexDBI(MDB_txn* txn, MDB_dbi sdbi, MDB_dbi tdbi)
311
{
8✔
312
  // FIXME: clear out target dbi first
313

314
  std::string header(LMDBLS::LS_MIN_HEADER_SIZE, '\0');
8✔
315
  int rc;
8✔
316

317
  MDB_cursor* cur;
8✔
318

319
  if ((rc = mdb_cursor_open(txn, sdbi, &cur)) != 0) {
8!
320
    throw std::runtime_error("mdb_cursur_open failed");
×
321
  }
×
322

323
  MDB_val key, data;
8✔
324

325
  rc = mdb_cursor_get(cur, &key, &data, MDB_FIRST);
8✔
326

327
  while (rc == 0) {
40✔
328
    std::string lenprefix(sizeof(uint16_t), '\0');
32✔
329
    std::string skey((char*)key.mv_data, key.mv_size);
32✔
330

331
    uint32_t id;
32✔
332

333
    if (data.mv_size != sizeof(uint32_t)) {
32!
334
      throw std::runtime_error("got non-uint32_t ID value in IndexDBI");
×
335
    }
×
336

337
    memcpy((void*)&id, data.mv_data, sizeof(uint32_t));
32✔
338
    id = htonl(id);
32✔
339

340
    uint16_t len = htons(skey.size());
32✔
341
    memcpy((void*)lenprefix.data(), &len, sizeof(len));
32✔
342
    std::string stkey = lenprefix + skey + std::string((char*)&id, sizeof(uint32_t));
32✔
343

344
    MDB_val tkey;
32✔
345
    MDB_val tdata;
32✔
346

347
    tkey.mv_data = (char*)stkey.c_str();
32✔
348
    tkey.mv_size = stkey.size();
32✔
349
    tdata.mv_data = (char*)header.c_str();
32✔
350
    tdata.mv_size = header.size();
32✔
351

352
    if ((rc = mdb_put(txn, tdbi, &tkey, &tdata, 0)) != 0) {
32!
353
      throw std::runtime_error("mdb_put failed");
×
354
    }
×
355

356
    rc = mdb_cursor_get(cur, &key, &data, MDB_NEXT);
32✔
357
  }
32✔
358
  if (rc != MDB_NOTFOUND) {
8!
359
    throw std::runtime_error("error while iterating dbi");
×
360
  }
×
361
}
8✔
362

363
}
364

365
bool LMDBBackend::upgradeToSchemav5(std::string& filename)
366
{
2✔
367
  int rc;
2✔
368

369
  auto currentSchemaVersionAndShards = getSchemaVersionAndShards(filename);
2✔
370
  uint32_t currentSchemaVersion = currentSchemaVersionAndShards.first;
2✔
371
  uint32_t shards = currentSchemaVersionAndShards.second;
2✔
372

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

377
  MDB_env* env = nullptr;
2✔
378

379
  if ((rc = mdb_env_create(&env)) != 0) {
2!
380
    throw std::runtime_error("mdb_env_create failed");
×
381
  }
×
382

383
  if ((rc = mdb_env_set_maxdbs(env, 20)) != 0) {
2!
384
    mdb_env_close(env);
×
385
    throw std::runtime_error("mdb_env_set_maxdbs failed");
×
386
  }
×
387

388
  if ((rc = mdb_env_open(env, filename.c_str(), MDB_NOSUBDIR, 0600)) != 0) {
2!
389
    mdb_env_close(env);
×
390
    throw std::runtime_error("mdb_env_open failed");
×
391
  }
×
392

393
  MDB_txn* txn = nullptr;
2✔
394

395
  if ((rc = mdb_txn_begin(env, NULL, 0, &txn)) != 0) {
2!
396
    mdb_env_close(env);
×
397
    throw std::runtime_error("mdb_txn_begin failed");
×
398
  }
×
399

400
#ifdef HAVE_SYSTEMD
2✔
401
  /* A schema migration may take a long time. Extend the startup service timeout to 1 day,
402
   * but only if this is beyond the original maximum time of TimeoutStartSec=.
403
   */
404
  sd_notify(0, "EXTEND_TIMEOUT_USEC=86400000000");
2✔
405
#endif
2✔
406

407
  std::cerr << "migrating shards" << std::endl;
2✔
408
  for (uint32_t i = 0; i < shards; i++) {
6✔
409
    string shardfile = filename + "-" + std::to_string(i);
4✔
410
    if (access(shardfile.c_str(), F_OK) < 0) {
4!
411
      if (errno == ENOENT) {
×
412
        // apparently this shard doesn't exist yet, moving on
413
        std::cerr << "shard " << shardfile << " not found, continuing" << std::endl;
×
414
        continue;
×
415
      }
×
416
    }
×
417

418
    std::cerr << "migrating shard " << shardfile << std::endl;
4✔
419
    MDB_env* shenv = nullptr;
4✔
420

421
    if ((rc = mdb_env_create(&shenv)) != 0) {
4!
422
      throw std::runtime_error("mdb_env_create failed");
×
423
    }
×
424

425
    if ((rc = mdb_env_set_maxdbs(shenv, 8)) != 0) {
4!
426
      mdb_env_close(env);
×
427
      throw std::runtime_error("mdb_env_set_maxdbs failed");
×
428
    }
×
429

430
    if ((rc = mdb_env_open(shenv, shardfile.c_str(), MDB_NOSUBDIR, 0600)) != 0) {
4!
431
      mdb_env_close(env);
×
432
      throw std::runtime_error("mdb_env_open failed");
×
433
    }
×
434

435
    MDB_txn* shtxn = nullptr;
4✔
436

437
    if ((rc = mdb_txn_begin(shenv, NULL, 0, &shtxn)) != 0) {
4!
438
      mdb_env_close(env);
×
439
      throw std::runtime_error("mdb_txn_begin failed");
×
440
    }
×
441

442
    MDB_dbi shdbi;
4✔
443

444
    if ((rc = mdb_dbi_open(shtxn, "records", 0, &shdbi)) != 0) {
4!
445
      if (rc == MDB_NOTFOUND) {
×
446
        mdb_txn_abort(shtxn);
×
447
        mdb_env_close(shenv);
×
448
        continue;
×
449
      }
×
450
      mdb_txn_abort(shtxn);
×
451
      mdb_env_close(shenv);
×
452
      throw std::runtime_error("mdb_dbi_open shard records failed");
×
453
    }
×
454

455
    MDB_dbi shdbi2;
4✔
456

457
    if ((rc = mdb_dbi_open(shtxn, "records_v5", MDB_CREATE, &shdbi2)) != 0) {
4!
458
      mdb_dbi_close(shenv, shdbi);
×
459
      mdb_txn_abort(shtxn);
×
460
      mdb_env_close(shenv);
×
461
      throw std::runtime_error("mdb_dbi_open shard records_v5 failed");
×
462
    }
×
463

464
    try {
4✔
465
      copyDBIAndAddLSHeader(shtxn, shdbi, shdbi2);
4✔
466
    }
4✔
467
    catch (std::exception& e) {
4✔
468
      mdb_dbi_close(shenv, shdbi2);
×
469
      mdb_dbi_close(shenv, shdbi);
×
470
      mdb_txn_abort(shtxn);
×
471
      mdb_env_close(shenv);
×
472
      throw std::runtime_error("copyDBIAndAddLSHeader failed");
×
473
    }
×
474

475
    cerr << "shard mbd_drop=" << mdb_drop(shtxn, shdbi, 1) << endl;
4✔
476
    mdb_txn_commit(shtxn);
4✔
477
    mdb_dbi_close(shenv, shdbi2);
4✔
478
    mdb_env_close(shenv);
4✔
479
  }
4✔
480

481
  std::array<MDB_dbi, 4> fromtypeddbi;
2✔
482
  std::array<MDB_dbi, 4> totypeddbi;
2✔
483

484
  int index = 0;
2✔
485

486
  for (const std::string dbname : {"domains", "keydata", "tsig", "metadata"}) {
8✔
487
    std::cerr << "migrating " << dbname << std::endl;
8✔
488
    std::string tdbname = dbname + "_v5";
8✔
489

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

496
    if ((rc = mdb_dbi_open(txn, tdbname.c_str(), MDB_CREATE, &totypeddbi[index])) != 0) {
8!
497
      mdb_dbi_close(env, fromtypeddbi[index]);
×
498
      mdb_txn_abort(txn);
×
499
      mdb_env_close(env);
×
500
      throw std::runtime_error("mdb_dbi_open typeddbi target failed");
×
501
    }
×
502

503
    try {
8✔
504
      copyTypedDBI(txn, fromtypeddbi[index], totypeddbi[index]);
8✔
505
    }
8✔
506
    catch (std::exception& e) {
8✔
507
      mdb_dbi_close(env, totypeddbi[index]);
×
508
      mdb_dbi_close(env, fromtypeddbi[index]);
×
509
      mdb_txn_abort(txn);
×
510
      mdb_env_close(env);
×
511
      throw std::runtime_error("copyTypedDBI failed");
×
512
    }
×
513

514
    // mdb_dbi_close(env, dbi2);
515
    // mdb_dbi_close(env, dbi);
516
    std::cerr << "migrated " << dbname << std::endl;
8✔
517

518
    index++;
8✔
519
  }
8✔
520

521
  std::array<MDB_dbi, 4> fromindexdbi;
2✔
522
  std::array<MDB_dbi, 4> toindexdbi;
2✔
523

524
  index = 0;
2✔
525

526
  for (const std::string dbname : {"domains", "keydata", "tsig", "metadata"}) {
8✔
527
    std::string fdbname = dbname + "_0";
8✔
528
    std::cerr << "migrating " << dbname << std::endl;
8✔
529
    std::string tdbname = dbname + "_v5_0";
8✔
530

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

537
    if ((rc = mdb_dbi_open(txn, tdbname.c_str(), MDB_CREATE, &toindexdbi[index])) != 0) {
8!
538
      mdb_dbi_close(env, fromindexdbi[index]);
×
539
      mdb_txn_abort(txn);
×
540
      mdb_env_close(env);
×
541
      throw std::runtime_error("mdb_dbi_open indexdbi target failed");
×
542
    }
×
543

544
    try {
8✔
545
      copyIndexDBI(txn, fromindexdbi[index], toindexdbi[index]);
8✔
546
    }
8✔
547
    catch (std::exception& e) {
8✔
548
      mdb_dbi_close(env, toindexdbi[index]);
×
549
      mdb_dbi_close(env, fromindexdbi[index]);
×
550
      mdb_txn_abort(txn);
×
551
      mdb_env_close(env);
×
552
      throw std::runtime_error("copyIndexDBI failed");
×
553
    }
×
554

555
    // mdb_dbi_close(env, dbi2);
556
    // mdb_dbi_close(env, dbi);
557
    std::cerr << "migrated " << dbname << std::endl;
8✔
558

559
    index++;
8✔
560
  }
8✔
561

562
  MDB_dbi dbi;
2✔
563

564
  // finally, migrate the pdns db
565
  if ((rc = mdb_dbi_open(txn, "pdns", 0, &dbi)) != 0) {
2!
566
    mdb_txn_abort(txn);
×
567
    mdb_env_close(env);
×
568
    throw std::runtime_error("mdb_dbi_open pdns failed");
×
569
  }
×
570

571
  MDB_val key, data;
2✔
572

573
  std::string header(LMDBLS::LS_MIN_HEADER_SIZE, '\0');
2✔
574

575
  for (const std::string keyname : {"schemaversion", "shards"}) {
4✔
576
    cerr << "migrating pdns." << keyname << endl;
4✔
577

578
    key.mv_data = (char*)keyname.c_str();
4✔
579
    key.mv_size = keyname.size();
4✔
580

581
    if ((rc = mdb_get(txn, dbi, &key, &data))) {
4!
582
      throw std::runtime_error("mdb_get pdns.shards failed");
×
583
    }
×
584

585
    uint32_t value;
4✔
586

587
    if (data.mv_size != sizeof(uint32_t)) {
4!
588
      throw std::runtime_error("got non-uint32_t key");
×
589
    }
×
590

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

593
    value = htonl(value);
4✔
594
    if (keyname == "schemaversion") {
4✔
595
      value = htonl(5);
2✔
596
    }
2✔
597

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

600
    std::string stdata = header + std::string((char*)&value, sizeof(uint32_t));
4✔
601
    ;
4✔
602

603
    MDB_val tdata;
4✔
604

605
    tdata.mv_data = (char*)stdata.c_str();
4✔
606
    tdata.mv_size = stdata.size();
4✔
607

608
    if ((rc = mdb_put(txn, dbi, &key, &tdata, 0)) != 0) {
4!
609
      throw std::runtime_error("mdb_put failed");
×
610
    }
×
611
  }
4✔
612

613
  for (const std::string keyname : {"uuid"}) {
2✔
614
    cerr << "migrating pdns." << keyname << endl;
2✔
615

616
    key.mv_data = (char*)keyname.c_str();
2✔
617
    key.mv_size = keyname.size();
2✔
618

619
    if ((rc = mdb_get(txn, dbi, &key, &data))) {
2!
620
      throw std::runtime_error("mdb_get pdns.shards failed");
×
621
    }
×
622

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

625
    std::string stdata = header + sdata;
2✔
626

627
    MDB_val tdata;
2✔
628

629
    tdata.mv_data = (char*)stdata.c_str();
2✔
630
    tdata.mv_size = stdata.size();
2✔
631

632
    if ((rc = mdb_put(txn, dbi, &key, &tdata, 0)) != 0) {
2!
633
      throw std::runtime_error("mdb_put failed");
×
634
    }
×
635
  }
2✔
636

637
  for (int i = 0; i < 4; i++) {
10✔
638
    mdb_drop(txn, fromtypeddbi[i], 1);
8✔
639
    mdb_drop(txn, fromindexdbi[i], 1);
8✔
640
  }
8✔
641

642
  cerr << "txn commit=" << mdb_txn_commit(txn) << endl;
2✔
643

644
  for (int i = 0; i < 4; i++) {
10✔
645
    mdb_dbi_close(env, totypeddbi[i]);
8✔
646
    mdb_dbi_close(env, toindexdbi[i]);
8✔
647
  }
8✔
648
  mdb_env_close(env);
2✔
649

650
  // throw std::runtime_error("migration done");
651
  cerr << "migration done" << endl;
2✔
652
  // exit(1);
653
  return true;
2✔
654
}
2✔
655

656
LMDBBackend::LMDBBackend(const std::string& suffix)
657
{
2,404✔
658
  // overlapping domain ids in combination with relative names are a recipe for disaster
659
  if (!suffix.empty()) {
2,404!
660
    throw std::runtime_error("LMDB backend does not support multiple instances");
×
661
  }
×
662

663
  setArgPrefix("lmdb" + suffix);
2,404✔
664

665
  string syncMode = toLower(getArg("sync-mode"));
2,404✔
666

667
  d_random_ids = mustDo("random-ids");
2,404✔
668

669
  if (syncMode == "nosync")
2,404!
670
    d_asyncFlag = MDB_NOSYNC;
×
671
  else if (syncMode == "nometasync")
2,404!
672
    d_asyncFlag = MDB_NOMETASYNC;
×
673
  else if (syncMode.empty() || syncMode == "sync")
2,404!
674
    d_asyncFlag = 0;
2,404✔
675
  else
×
676
    throw std::runtime_error("Unknown sync mode " + syncMode + " requested for LMDB backend");
×
677

678
  uint64_t mapSize = 0;
2,404✔
679
  try {
2,404✔
680
    mapSize = std::stoll(getArg("map-size"));
2,404✔
681
  }
2,404✔
682
  catch (const std::exception& e) {
2,404✔
683
    throw std::runtime_error(std::string("Unable to parse the 'map-size' LMDB value: ") + e.what());
×
684
  }
×
685

686
  LMDBLS::s_flag_deleted = mustDo("flag-deleted");
2,403✔
687
  d_handle_dups = false;
2,403✔
688

689
  if (mustDo("lightning-stream")) {
2,403!
690
    d_random_ids = true;
×
691
    d_handle_dups = true;
×
692
    LMDBLS::s_flag_deleted = true;
×
693

694
    if (atoi(getArg("shards").c_str()) != 1) {
×
695
      throw std::runtime_error(std::string("running with Lightning Stream support requires shards=1"));
×
696
    }
×
697
  }
×
698

699
  bool opened = false;
2,403✔
700

701
  if (s_first) {
2,403✔
702
    std::lock_guard<std::mutex> l(s_lmdbStartupLock);
678✔
703
    if (s_first) {
678!
704
      auto filename = getArg("filename");
678✔
705

706
      auto currentSchemaVersionAndShards = getSchemaVersionAndShards(filename);
678✔
707
      uint32_t currentSchemaVersion = currentSchemaVersionAndShards.first;
678✔
708
      // std::cerr<<"current schema version: "<<currentSchemaVersion<<", shards="<<currentSchemaVersionAndShards.second<<std::endl;
709

710
      if (getArgAsNum("schema-version") != SCHEMAVERSION) {
678!
711
        throw std::runtime_error("This version of the lmdbbackend only supports schema version 5. Configuration demands a lower version. Not starting up.");
×
712
      }
×
713

714
      if (currentSchemaVersion > 0 && currentSchemaVersion < 3) {
678!
715
        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.");
×
716
      }
×
717

718
      if (currentSchemaVersion == 0) {
678✔
719
        // no database is present yet, we can just create them
720
        currentSchemaVersion = 5;
23✔
721
      }
23✔
722

723
      if (currentSchemaVersion == 3 || currentSchemaVersion == 4) {
678✔
724
        if (!upgradeToSchemav5(filename)) {
2!
725
          throw std::runtime_error("Failed to perform LMDB schema version upgrade from v4 to v5");
×
726
        }
×
727
        currentSchemaVersion = 5;
2✔
728
      }
2✔
729

730
      if (currentSchemaVersion != 5) {
678!
731
        throw std::runtime_error("Somehow, we are not at schema version 5. Giving up");
×
732
      }
×
733

734
      d_tdomains = std::make_shared<tdomains_t>(getMDBEnv(getArg("filename").c_str(), MDB_NOSUBDIR | d_asyncFlag, 0600, mapSize), "domains_v5");
678✔
735
      d_tmeta = std::make_shared<tmeta_t>(d_tdomains->getEnv(), "metadata_v5");
678✔
736
      d_tkdb = std::make_shared<tkdb_t>(d_tdomains->getEnv(), "keydata_v5");
678✔
737
      d_ttsig = std::make_shared<ttsig_t>(d_tdomains->getEnv(), "tsig_v5");
678✔
738

739
      auto pdnsdbi = d_tdomains->getEnv()->openDB("pdns", MDB_CREATE);
678✔
740

741
      opened = true;
678✔
742

743
      auto txn = d_tdomains->getEnv()->getRWTransaction();
678✔
744

745
      MDBOutVal shards;
678✔
746
      if (!txn->get(pdnsdbi, "shards", shards)) {
678✔
747
        s_shards = shards.get<uint32_t>();
655✔
748

749
        if (mustDo("lightning-stream") && s_shards != 1) {
655!
750
          throw std::runtime_error(std::string("running with Lightning Stream support enabled requires a database with exactly 1 shard"));
×
751
        }
×
752

753
        if (s_shards != atoi(getArg("shards").c_str())) {
655!
754
          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;
×
755
        }
×
756
      }
655✔
757
      else {
23✔
758
        s_shards = atoi(getArg("shards").c_str());
23✔
759
        txn->put(pdnsdbi, "shards", s_shards);
23✔
760
      }
23✔
761

762
      MDBOutVal gotuuid;
678✔
763
      if (txn->get(pdnsdbi, "uuid", gotuuid)) {
678✔
764
        const auto uuid = getUniqueID();
23✔
765
        const string uuids(uuid.begin(), uuid.end());
23✔
766
        txn->put(pdnsdbi, "uuid", uuids);
23✔
767
      }
23✔
768

769
      MDBOutVal _schemaversion;
678✔
770
      if (txn->get(pdnsdbi, "schemaversion", _schemaversion)) {
678✔
771
        // our DB is entirely new, so we need to write the schemaversion
772
        txn->put(pdnsdbi, "schemaversion", currentSchemaVersion);
23✔
773
      }
23✔
774
      txn->commit();
678✔
775

776
      s_first = false;
678✔
777
    }
678✔
778
  }
678✔
779

780
  if (!opened) {
2,403✔
781
    d_tdomains = std::make_shared<tdomains_t>(getMDBEnv(getArg("filename").c_str(), MDB_NOSUBDIR | d_asyncFlag, 0600, mapSize), "domains_v5");
1,726✔
782
    d_tmeta = std::make_shared<tmeta_t>(d_tdomains->getEnv(), "metadata_v5");
1,726✔
783
    d_tkdb = std::make_shared<tkdb_t>(d_tdomains->getEnv(), "keydata_v5");
1,726✔
784
    d_ttsig = std::make_shared<ttsig_t>(d_tdomains->getEnv(), "tsig_v5");
1,726✔
785
  }
1,726✔
786
  d_trecords.resize(s_shards);
2,403✔
787
  d_dolog = ::arg().mustDo("query-logging");
2,403✔
788
}
2,403✔
789

790
namespace boost
791
{
792
namespace serialization
793
{
794

795
  template <class Archive>
796
  void save(Archive& ar, const DNSName& g, const unsigned int /* version */)
797
  {
2,344✔
798
    if (g.empty()) {
2,344✔
799
      ar& std::string();
632✔
800
    }
632✔
801
    else {
1,712✔
802
      ar& g.toDNSStringLC();
1,712✔
803
    }
1,712✔
804
  }
2,344✔
805

806
  template <class Archive>
807
  void load(Archive& ar, DNSName& g, const unsigned int /* version */)
808
  {
148,999✔
809
    string tmp;
148,999✔
810
    ar& tmp;
148,999✔
811
    if (tmp.empty()) {
148,999✔
812
      g = DNSName();
66,042✔
813
    }
66,042✔
814
    else {
82,957✔
815
      g = DNSName(tmp.c_str(), tmp.size(), 0, false);
82,957✔
816
    }
82,957✔
817
  }
148,999✔
818

819
  template <class Archive>
820
  void save(Archive& ar, const QType& g, const unsigned int /* version */)
821
  {
822
    ar& g.getCode();
823
  }
824

825
  template <class Archive>
826
  void load(Archive& ar, QType& g, const unsigned int /* version */)
827
  {
828
    uint16_t tmp;
829
    ar& tmp;
830
    g = QType(tmp);
831
  }
832

833
  template <class Archive>
834
  void save(Archive& ar, const DomainInfo& g, const unsigned int /* version */)
835
  {
910✔
836
    ar& g.zone;
910✔
837
    ar& g.last_check;
910✔
838
    ar& g.account;
910✔
839
    ar& g.primaries;
910✔
840
    ar& g.id;
910✔
841
    ar& g.notified_serial;
910✔
842
    ar& g.kind;
910✔
843
    ar& g.options;
910✔
844
    ar& g.catalog;
910✔
845
  }
910✔
846

847
  template <class Archive>
848
  void load(Archive& ar, DomainInfo& g, const unsigned int version)
849
  {
72,354✔
850
    ar& g.zone;
72,354✔
851
    ar& g.last_check;
72,354✔
852
    ar& g.account;
72,354✔
853
    ar& g.primaries;
72,354✔
854
    ar& g.id;
72,354✔
855
    ar& g.notified_serial;
72,354✔
856
    ar& g.kind;
72,354✔
857
    if (version >= 1) {
72,354✔
858
      ar& g.options;
72,290✔
859
      ar& g.catalog;
72,290✔
860
    }
72,290✔
861
    else {
64✔
862
      g.options.clear();
64✔
863
      g.catalog.clear();
64✔
864
    }
64✔
865
  }
72,354✔
866

867
  template <class Archive>
868
  void serialize(Archive& ar, LMDBBackend::DomainMeta& g, const unsigned int /* version */)
869
  {
4,112✔
870
    ar& g.domain& g.key& g.value;
4,112✔
871
  }
4,112✔
872

873
  template <class Archive>
874
  void save(Archive& ar, const LMDBBackend::KeyDataDB& g, const unsigned int /* version */)
875
  {
136✔
876
    ar& g.domain& g.content& g.flags& g.active& g.published;
136✔
877
  }
136✔
878

879
  template <class Archive>
880
  void load(Archive& ar, LMDBBackend::KeyDataDB& g, const unsigned int version)
881
  {
443✔
882
    ar& g.domain& g.content& g.flags& g.active;
443✔
883
    if (version >= 1) {
443!
884
      ar& g.published;
443✔
885
    }
443✔
886
    else {
×
887
      g.published = true;
×
888
    }
×
889
  }
443✔
890

891
  template <class Archive>
892
  void serialize(Archive& ar, TSIGKey& g, const unsigned int /* version */)
893
  {
94✔
894
    ar& g.name;
94✔
895
    ar& g.algorithm; // this is the ordername
94✔
896
    ar& g.key;
94✔
897
  }
94✔
898

899
} // namespace serialization
900
} // namespace boost
901

902
BOOST_SERIALIZATION_SPLIT_FREE(DNSName);
903
BOOST_SERIALIZATION_SPLIT_FREE(QType);
904
BOOST_SERIALIZATION_SPLIT_FREE(LMDBBackend::KeyDataDB);
905
BOOST_SERIALIZATION_SPLIT_FREE(DomainInfo);
906
BOOST_IS_BITWISE_SERIALIZABLE(ComboAddress);
907

908
template <>
909
std::string serializeToBuffer(const LMDBBackend::LMDBResourceRecord& value)
910
{
589,322✔
911
  std::string buffer;
589,322✔
912

913
  // Data size of the resource record.
914
  uint16_t len = value.content.length();
589,322✔
915

916
  // Reserve space to store the size of the resource record + the content of the resource
917
  // record + a few other things.
918
  buffer.reserve(sizeof(len) + len + sizeof(value.ttl) + sizeof(value.auth) + sizeof(value.disabled) + sizeof(value.ordername));
589,322✔
919

920
  // Store the size of the resource record.
921
  // NOLINTNEXTLINE.
922
  buffer.assign((const char*)&len, sizeof(len));
589,322✔
923

924
  // Store the contents of the resource record.
925
  buffer += value.content;
589,322✔
926

927
  // The few other things.
928
  // NOLINTNEXTLINE.
929
  buffer.append((const char*)&value.ttl, sizeof(value.ttl));
589,322✔
930
  buffer.append(1, (char)value.auth);
589,322✔
931
  buffer.append(1, (char)value.disabled);
589,322✔
932
  buffer.append(1, (char)value.ordername);
589,322✔
933

934
  return buffer;
589,322✔
935
}
589,322✔
936

937
template <>
938
std::string serializeToBuffer(const vector<LMDBBackend::LMDBResourceRecord>& value)
939
{
61,040✔
940
  std::string ret;
61,040✔
941
  for (const auto& lrr : value) {
61,407✔
942
    ret += serializeToBuffer(lrr);
61,407✔
943
  }
61,407✔
944
  return ret;
61,040✔
945
}
61,040✔
946

947
static inline size_t deserializeRRFromBuffer(const string_view& str, LMDBBackend::LMDBResourceRecord& lrr)
948
{
328,425✔
949
  uint16_t len;
328,425✔
950
  memcpy(&len, &str[0], 2);
328,425✔
951
  lrr.content.assign(&str[2], len); // len bytes
328,425✔
952
  memcpy(&lrr.ttl, &str[2] + len, 4);
328,425✔
953
  lrr.auth = str[2 + len + 4];
328,425✔
954
  lrr.disabled = str[2 + len + 4 + 1];
328,425✔
955
  lrr.ordername = str[2 + len + 4 + 2];
328,425✔
956
  lrr.wildcardname.clear();
328,425✔
957

958
  return 2 + len + 7;
328,425✔
959
}
328,425✔
960

961
template <>
962
void deserializeFromBuffer(const string_view& buffer, LMDBBackend::LMDBResourceRecord& value)
963
{
11,647✔
964
  deserializeRRFromBuffer(buffer, value);
11,647✔
965
}
11,647✔
966

967
template <>
968
void deserializeFromBuffer(const string_view& buffer, vector<LMDBBackend::LMDBResourceRecord>& value)
969
{
312,386✔
970
  auto str_copy = buffer;
312,386✔
971
  while (str_copy.size() >= 9) { // minimum length for a record is 10
629,164✔
972
    LMDBBackend::LMDBResourceRecord lrr;
316,778✔
973
    auto rrLength = deserializeRRFromBuffer(str_copy, lrr);
316,778✔
974
    value.emplace_back(lrr);
316,778✔
975
    str_copy.remove_prefix(rrLength);
316,778✔
976
  }
316,778✔
977
}
312,386✔
978

979
static std::string serializeContent(uint16_t qtype, const DNSName& domain, const std::string& content)
980
{
325,638✔
981
  auto drc = DNSRecordContent::make(qtype, QClass::IN, content);
325,638✔
982
  return drc->serialize(domain, false);
325,638✔
983
}
325,638✔
984

985
static std::shared_ptr<DNSRecordContent> deserializeContentZR(uint16_t qtype, const DNSName& qname, const std::string& content)
986
{
254,394✔
987
  if (qtype == QType::A && content.size() == 4) {
254,394!
988
    return std::make_shared<ARecordContent>(*((uint32_t*)content.c_str()));
243,987✔
989
  }
243,987✔
990
  return DNSRecordContent::deserialize(qname, qtype, content);
10,407✔
991
}
254,394✔
992

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

996
   The index we use is "zoneid,canonical relative name". This index is also used
997
   for AXFR.
998

999
   Note - domain_id, name and type are ONLY present on the index!
1000
*/
1001

1002
#if BOOST_VERSION >= 106100
1003
#define StringView string_view
1004
#else
1005
#define StringView string
1006
#endif
1007

1008
void LMDBBackend::deleteDomainRecords(RecordsRWTransaction& txn, uint32_t domain_id, uint16_t qtype)
1009
{
331✔
1010
  compoundOrdername co;
331✔
1011
  string match = co(domain_id);
331✔
1012

1013
  auto cursor = txn.txn->getCursor(txn.db->dbi);
331✔
1014
  MDBOutVal key, val;
331✔
1015
  //  cout<<"Match: "<<makeHexDump(match);
1016
  if (!cursor.lower_bound(match, key, val)) {
331✔
1017
    while (key.getNoStripHeader<StringView>().rfind(match, 0) == 0) {
639✔
1018
      if (qtype == QType::ANY || co.getQType(key.getNoStripHeader<StringView>()) == qtype)
633!
1019
        cursor.del();
633✔
1020
      if (cursor.next(key, val))
633✔
1021
        break;
15✔
1022
    }
633✔
1023
  }
21✔
1024
}
331✔
1025

1026
/* Here's the complicated story. Other backends have just one transaction, which is either
1027
   on or not.
1028

1029
   You can't call feedRecord without a transaction started with startTransaction.
1030

1031
   However, other functions can be called after startTransaction() or without startTransaction()
1032
     (like updateDNSSECOrderNameAndAuth)
1033

1034

1035

1036
*/
1037

1038
bool LMDBBackend::startTransaction(const DNSName& domain, int domain_id)
1039
{
533✔
1040
  // cout <<"startTransaction("<<domain<<", "<<domain_id<<")"<<endl;
1041
  int real_id = domain_id;
533✔
1042
  if (real_id < 0) {
533✔
1043
    auto rotxn = d_tdomains->getROTransaction();
202✔
1044
    DomainInfo di;
202✔
1045
    real_id = rotxn.get<0>(domain, di);
202✔
1046
    // cout<<"real_id = "<<real_id << endl;
1047
    if (!real_id)
202!
1048
      return false;
×
1049
  }
202✔
1050
  if (d_rwtxn) {
533!
1051
    throw DBException("Attempt to start a transaction while one was open already");
×
1052
  }
×
1053
  d_rwtxn = getRecordsRWTransaction(real_id);
533✔
1054

1055
  d_transactiondomain = domain;
533✔
1056
  d_transactiondomainid = real_id;
533✔
1057
  if (domain_id >= 0) {
533✔
1058
    deleteDomainRecords(*d_rwtxn, domain_id);
331✔
1059
  }
331✔
1060

1061
  return true;
533✔
1062
}
533✔
1063

1064
bool LMDBBackend::commitTransaction()
1065
{
494✔
1066
  // cout<<"Commit transaction" <<endl;
1067
  if (!d_rwtxn) {
494!
1068
    throw DBException("Attempt to commit a transaction while there isn't one open");
×
1069
  }
×
1070

1071
  d_rwtxn->txn->commit();
494✔
1072
  d_rwtxn.reset();
494✔
1073
  return true;
494✔
1074
}
494✔
1075

1076
bool LMDBBackend::abortTransaction()
1077
{
35✔
1078
  // cout<<"Abort transaction"<<endl;
1079
  if (!d_rwtxn) {
35!
1080
    throw DBException("Attempt to abort a transaction while there isn't one open");
×
1081
  }
×
1082

1083
  d_rwtxn->txn->abort();
35✔
1084
  d_rwtxn.reset();
35✔
1085

1086
  return true;
35✔
1087
}
35✔
1088

1089
// d_rwtxn must be set here
1090
bool LMDBBackend::feedRecord(const DNSResourceRecord& r, const DNSName& ordername, bool ordernameIsNSEC3)
1091
{
325,510✔
1092
  LMDBResourceRecord lrr(r);
325,510✔
1093
  lrr.qname.makeUsRelative(d_transactiondomain);
325,510✔
1094
  lrr.content = serializeContent(lrr.qtype.getCode(), r.qname, lrr.content);
325,510✔
1095

1096
  compoundOrdername co;
325,510✔
1097
  string matchName = co(lrr.domain_id, lrr.qname, lrr.qtype.getCode());
325,510✔
1098

1099
  string rrs;
325,510✔
1100
  MDBOutVal _rrs;
325,510✔
1101
  if (!d_rwtxn->txn->get(d_rwtxn->db->dbi, matchName, _rrs)) {
325,510✔
1102
    rrs = _rrs.get<string>();
21,768✔
1103
  }
21,768✔
1104

1105
  rrs += serializeToBuffer(lrr);
325,510✔
1106

1107
  d_rwtxn->txn->put(d_rwtxn->db->dbi, matchName, rrs);
325,510✔
1108

1109
  if (ordernameIsNSEC3 && !ordername.empty()) {
325,510!
1110
    MDBOutVal val;
40,765✔
1111
    if (d_rwtxn->txn->get(d_rwtxn->db->dbi, co(lrr.domain_id, lrr.qname, QType::NSEC3), val)) {
40,765✔
1112
      lrr.ttl = 0;
40,379✔
1113
      lrr.content = lrr.qname.toDNSStringLC();
40,379✔
1114
      lrr.auth = 0;
40,379✔
1115
      string ser = serializeToBuffer(lrr);
40,379✔
1116
      d_rwtxn->txn->put(d_rwtxn->db->dbi, co(lrr.domain_id, ordername, QType::NSEC3), ser);
40,379✔
1117

1118
      lrr.ttl = 1;
40,379✔
1119
      lrr.content = ordername.toDNSString();
40,379✔
1120
      ser = serializeToBuffer(lrr);
40,379✔
1121
      d_rwtxn->txn->put(d_rwtxn->db->dbi, co(lrr.domain_id, lrr.qname, QType::NSEC3), ser);
40,379✔
1122
    }
40,379✔
1123
  }
40,765✔
1124
  return true;
325,510✔
1125
}
325,510✔
1126

1127
bool LMDBBackend::feedEnts(int domain_id, map<DNSName, bool>& nonterm)
1128
{
22✔
1129
  LMDBResourceRecord lrr;
22✔
1130
  lrr.ttl = 0;
22✔
1131
  compoundOrdername co;
22✔
1132
  for (const auto& nt : nonterm) {
96✔
1133
    lrr.qname = nt.first.makeRelative(d_transactiondomain);
96✔
1134
    lrr.auth = nt.second;
96✔
1135
    lrr.ordername = true;
96✔
1136

1137
    std::string ser = serializeToBuffer(lrr);
96✔
1138
    d_rwtxn->txn->put(d_rwtxn->db->dbi, co(domain_id, lrr.qname, QType::ENT), ser);
96✔
1139
  }
96✔
1140
  return true;
22✔
1141
}
22✔
1142

1143
bool LMDBBackend::feedEnts3(int domain_id, const DNSName& domain, map<DNSName, bool>& nonterm, const NSEC3PARAMRecordContent& ns3prc, bool narrow)
1144
{
18✔
1145
  string ser;
18✔
1146
  DNSName ordername;
18✔
1147
  LMDBResourceRecord lrr;
18✔
1148
  compoundOrdername co;
18✔
1149
  for (const auto& nt : nonterm) {
92✔
1150
    lrr.qname = nt.first.makeRelative(domain);
92✔
1151
    lrr.ttl = 0;
92✔
1152
    lrr.auth = nt.second;
92✔
1153
    lrr.ordername = nt.second;
92✔
1154
    ser = serializeToBuffer(lrr);
92✔
1155
    d_rwtxn->txn->put(d_rwtxn->db->dbi, co(domain_id, lrr.qname, QType::ENT), ser);
92✔
1156

1157
    if (!narrow && lrr.auth) {
92!
1158
      lrr.content = lrr.qname.toDNSString();
80✔
1159
      lrr.auth = false;
80✔
1160
      lrr.ordername = false;
80✔
1161
      ser = serializeToBuffer(lrr);
80✔
1162

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

1166
      lrr.ttl = 1;
80✔
1167
      lrr.content = ordername.toDNSString();
80✔
1168
      ser = serializeToBuffer(lrr);
80✔
1169
      d_rwtxn->txn->put(d_rwtxn->db->dbi, co(domain_id, lrr.qname, QType::NSEC3), ser);
80✔
1170
    }
80✔
1171
  }
92✔
1172
  return true;
18✔
1173
}
18✔
1174

1175
// might be called within a transaction, might also be called alone
1176
bool LMDBBackend::replaceRRSet(uint32_t domain_id, const DNSName& qname, const QType& qt, const vector<DNSResourceRecord>& rrset)
1177
{
129✔
1178
  // zonk qname/qtype within domain_id (go through qname, check domain_id && qtype)
1179
  shared_ptr<RecordsRWTransaction> txn;
129✔
1180
  bool needCommit = false;
129✔
1181
  if (d_rwtxn && d_transactiondomainid == domain_id) {
129!
1182
    txn = d_rwtxn;
129✔
1183
    //    cout<<"Reusing open transaction"<<endl;
1184
  }
129✔
1185
  else {
×
1186
    //    cout<<"Making a new RW txn for replace rrset"<<endl;
1187
    txn = getRecordsRWTransaction(domain_id);
×
1188
    needCommit = true;
×
1189
  }
×
1190

1191
  DomainInfo di;
129✔
1192
  if (!d_tdomains->getROTransaction().get(domain_id, di)) {
129!
1193
    return false;
×
1194
  }
×
1195

1196
  compoundOrdername co;
129✔
1197
  auto cursor = txn->txn->getCursor(txn->db->dbi);
129✔
1198
  MDBOutVal key, val;
129✔
1199
  string match = co(domain_id, qname.makeRelative(di.zone), qt.getCode());
129✔
1200
  if (!cursor.find(match, key, val)) {
129✔
1201
    cursor.del();
116✔
1202
  }
116✔
1203

1204
  if (!rrset.empty()) {
129✔
1205
    vector<LMDBResourceRecord> adjustedRRSet;
127✔
1206
    for (const auto& rr : rrset) {
128✔
1207
      LMDBResourceRecord lrr(rr);
128✔
1208
      lrr.content = serializeContent(lrr.qtype.getCode(), lrr.qname, lrr.content);
128✔
1209
      lrr.qname.makeUsRelative(di.zone);
128✔
1210

1211
      adjustedRRSet.emplace_back(lrr);
128✔
1212
    }
128✔
1213
    txn->txn->put(txn->db->dbi, match, serializeToBuffer(adjustedRRSet));
127✔
1214
  }
127✔
1215

1216
  if (needCommit)
129!
1217
    txn->txn->commit();
×
1218

1219
  return true;
129✔
1220
}
129✔
1221

1222
bool LMDBBackend::replaceComments([[maybe_unused]] const uint32_t domain_id, [[maybe_unused]] const DNSName& qname, [[maybe_unused]] const QType& qt, const vector<Comment>& comments)
1223
{
2✔
1224
  // if the vector is empty, good, that's what we do here (LMDB does not store comments)
1225
  // if it's not, report failure
1226
  return comments.empty();
2✔
1227
}
2✔
1228

1229
// tempting to templatize these two functions but the pain is not worth it
1230
std::shared_ptr<LMDBBackend::RecordsRWTransaction> LMDBBackend::getRecordsRWTransaction(uint32_t id)
1231
{
533✔
1232
  auto& shard = d_trecords[id % s_shards];
533✔
1233
  if (!shard.env) {
533✔
1234
    shard.env = getMDBEnv((getArg("filename") + "-" + std::to_string(id % s_shards)).c_str(),
69✔
1235
                          MDB_NOSUBDIR | d_asyncFlag, 0600);
69✔
1236
    shard.dbi = shard.env->openDB("records_v5", MDB_CREATE);
69✔
1237
  }
69✔
1238
  auto ret = std::make_shared<RecordsRWTransaction>(shard.env->getRWTransaction());
533✔
1239
  ret->db = std::make_shared<RecordsDB>(shard);
533✔
1240

1241
  return ret;
533✔
1242
}
533✔
1243

1244
std::shared_ptr<LMDBBackend::RecordsROTransaction> LMDBBackend::getRecordsROTransaction(uint32_t id, const std::shared_ptr<LMDBBackend::RecordsRWTransaction>& rwtxn)
1245
{
10,945✔
1246
  auto& shard = d_trecords[id % s_shards];
10,945✔
1247
  if (!shard.env) {
10,945✔
1248
    if (rwtxn) {
2,248!
1249
      throw DBException("attempting to start nested transaction without open parent env");
×
1250
    }
×
1251
    shard.env = getMDBEnv((getArg("filename") + "-" + std::to_string(id % s_shards)).c_str(),
2,248✔
1252
                          MDB_NOSUBDIR | d_asyncFlag, 0600);
2,248✔
1253
    shard.dbi = shard.env->openDB("records_v5", MDB_CREATE);
2,248✔
1254
  }
2,248✔
1255

1256
  if (rwtxn) {
10,945✔
1257
    auto ret = std::make_shared<RecordsROTransaction>(rwtxn->txn->getROTransaction());
392✔
1258
    ret->db = std::make_shared<RecordsDB>(shard);
392✔
1259
    return ret;
392✔
1260
  }
392✔
1261
  else {
10,553✔
1262
    auto ret = std::make_shared<RecordsROTransaction>(shard.env->getROTransaction());
10,553✔
1263
    ret->db = std::make_shared<RecordsDB>(shard);
10,553✔
1264
    return ret;
10,553✔
1265
  }
10,553✔
1266
}
10,945✔
1267

1268
#if 0
1269
// FIXME reinstate soon
1270
bool LMDBBackend::upgradeToSchemav3()
1271
{
1272
  g_log << Logger::Warning << "Upgrading LMDB schema" << endl;
1273

1274
  for (auto i = 0; i < s_shards; i++) {
1275
    string filename = getArg("filename") + "-" + std::to_string(i);
1276
    if (rename(filename.c_str(), (filename + "-old").c_str()) < 0) {
1277
      if (errno == ENOENT) {
1278
        // apparently this shard doesn't exist yet, moving on
1279
        continue;
1280
      }
1281
      unixDie("Rename failed during LMDB upgrade");
1282
    }
1283

1284
    LMDBBackend::RecordsDB oldShard, newShard;
1285

1286
    oldShard.env = getMDBEnv((filename + "-old").c_str(),
1287
                             MDB_NOSUBDIR | d_asyncFlag, 0600);
1288
    oldShard.dbi = oldShard.env->openDB("records", MDB_CREATE | MDB_DUPSORT);
1289
    auto txn = oldShard.env->getROTransaction();
1290
    auto cursor = txn->getROCursor(oldShard.dbi);
1291

1292
    newShard.env = getMDBEnv((filename).c_str(),
1293
                             MDB_NOSUBDIR | d_asyncFlag, 0600);
1294
    newShard.dbi = newShard.env->openDB("records", MDB_CREATE);
1295
    auto newTxn = newShard.env->getRWTransaction();
1296

1297
    MDBOutVal key, val;
1298
    if (cursor.first(key, val) != 0) {
1299
      cursor.close();
1300
      txn->abort();
1301
      newTxn->abort();
1302
      continue;
1303
    }
1304
    string_view currentKey;
1305
    string value;
1306
    for (;;) {
1307
      auto newKey = key.getNoStripHeader<string_view>();
1308
      if (currentKey.compare(newKey) != 0) {
1309
        if (value.size() > 0) {
1310
          newTxn->put(newShard.dbi, currentKey, value);
1311
        }
1312
        currentKey = newKey;
1313
        value = "";
1314
      }
1315
      value += val.get<string>();
1316
      if (cursor.next(key, val) != 0) {
1317
        if (value.size() > 0) {
1318
          newTxn->put(newShard.dbi, currentKey, value);
1319
        }
1320
        break;
1321
      }
1322
    }
1323

1324
    cursor.close();
1325
    txn->commit();
1326
    newTxn->commit();
1327
  }
1328

1329
  return true;
1330
}
1331
#endif
1332

1333
bool LMDBBackend::deleteDomain(const DNSName& domain)
1334
{
18✔
1335
  if (!d_rwtxn) {
18!
1336
    throw DBException(std::string(__PRETTY_FUNCTION__) + " called without a transaction");
×
1337
  }
×
1338

1339
  int transactionDomainId = d_transactiondomainid;
18✔
1340
  DNSName transactionDomain = d_transactiondomain;
18✔
1341

1342
  abortTransaction();
18✔
1343

1344
  LmdbIdVec idvec;
18✔
1345

1346
  if (!d_handle_dups) {
18!
1347
    // get domain id
1348
    auto txn = d_tdomains->getROTransaction();
18✔
1349

1350
    DomainInfo di;
18✔
1351
    idvec.push_back(txn.get<0>(domain, di));
18✔
1352
  }
18✔
1353
  else {
×
1354
    // this transaction used to be RO.
1355
    // it is now RW to narrow a race window between PowerDNS and Lightning Stream
1356
    // FIXME: turn the entire delete, including this ID scan, into one RW transaction
1357
    // when doing that, first do a short RO check to see if we actually have anything to delete
1358
    auto txn = d_tdomains->getRWTransaction();
×
1359

1360
    txn.get_multi<0>(domain, idvec);
×
1361
  }
×
1362

1363
  for (auto id : idvec) {
18✔
1364

1365
    startTransaction(domain, id);
18✔
1366

1367
    { // Remove metadata
18✔
1368
      auto txn = d_tmeta->getRWTransaction();
18✔
1369
      LmdbIdVec ids;
18✔
1370

1371
      txn.get_multi<0>(domain, ids);
18✔
1372

1373
      for (auto& _id : ids) {
20✔
1374
        txn.del(_id);
8✔
1375
      }
8✔
1376

1377
      txn.commit();
18✔
1378
    }
18✔
1379

1380
    { // Remove cryptokeys
18✔
1381
      auto txn = d_tkdb->getRWTransaction();
18✔
1382
      LmdbIdVec ids;
18✔
1383
      txn.get_multi<0>(domain, ids);
18✔
1384

1385
      for (auto _id : ids) {
18!
1386
        txn.del(_id);
×
1387
      }
×
1388

1389
      txn.commit();
18✔
1390
    }
18✔
1391

1392
    // Remove records
1393
    commitTransaction();
18✔
1394

1395
    // Remove zone
1396
    auto txn = d_tdomains->getRWTransaction();
18✔
1397
    txn.del(id);
18✔
1398
    txn.commit();
18✔
1399
  }
18✔
1400

1401
  startTransaction(transactionDomain, transactionDomainId);
18✔
1402

1403
  return true;
18✔
1404
}
18✔
1405

1406
bool LMDBBackend::list(const DNSName& target, int /* id */, bool include_disabled)
1407
{
613✔
1408
  d_includedisabled = include_disabled;
613✔
1409

1410
  DomainInfo di;
613✔
1411
  {
613✔
1412
    auto dtxn = d_tdomains->getROTransaction();
613✔
1413
    if ((di.id = dtxn.get<0>(target, di))) {
613!
1414
      // cerr << "Found domain " << target << " on domain_id " << di.id << ", list requested " << id << endl;
1415
    }
613✔
1416
    else {
×
1417
      // cerr << "Did not find " << target << endl;
1418
      return false;
×
1419
    }
×
1420
  }
613✔
1421

1422
  d_rotxn = getRecordsROTransaction(di.id, d_rwtxn);
613✔
1423
  d_getcursor = std::make_shared<MDBROCursor>(d_rotxn->txn->getCursor(d_rotxn->db->dbi));
613✔
1424

1425
  compoundOrdername co;
613✔
1426
  d_matchkey = co(di.id);
613✔
1427

1428
  MDBOutVal key, val;
613✔
1429
  auto a = d_getcursor->lower_bound(d_matchkey, key, val);
613✔
1430
  auto b0 = key.getNoStripHeader<StringView>();
613✔
1431
  auto b = b0.rfind(d_matchkey, 0);
613✔
1432
  if (a || b != 0) {
613!
1433
    d_getcursor.reset();
11✔
1434
  }
11✔
1435

1436
  d_lookupdomain = target;
613✔
1437

1438
  // Make sure we start with fresh data
1439
  d_currentrrset.clear();
613✔
1440
  d_currentrrsetpos = 0;
613✔
1441

1442
  return true;
613✔
1443
}
613✔
1444

1445
void LMDBBackend::lookup(const QType& type, const DNSName& qdomain, int zoneId, DNSPacket* /* p */)
1446
{
3,740✔
1447
  if (d_dolog) {
3,740!
1448
    g_log << Logger::Warning << "Got lookup for " << qdomain << "|" << type.toString() << " in zone " << zoneId << endl;
×
1449
    d_dtime.set();
×
1450
  }
×
1451

1452
  d_includedisabled = false;
3,740✔
1453

1454
  DNSName hunt(qdomain);
3,740✔
1455
  DomainInfo di;
3,740✔
1456
  if (zoneId < 0) {
3,740✔
1457
    auto rotxn = d_tdomains->getROTransaction();
810✔
1458

1459
    do {
810✔
1460
      zoneId = rotxn.get<0>(hunt, di);
810✔
1461
    } while (!zoneId && type != QType::SOA && hunt.chopOff());
810!
1462
    if (zoneId <= 0) {
810!
1463
      //      cout << "Did not find zone for "<< qdomain<<endl;
1464
      d_getcursor.reset();
×
1465
      return;
×
1466
    }
×
1467
  }
810✔
1468
  else {
2,930✔
1469
    if (!d_tdomains->getROTransaction().get(zoneId, di)) {
2,930✔
1470
      // cout<<"Could not find a zone with id "<<zoneId<<endl;
1471
      d_getcursor.reset();
8✔
1472
      return;
8✔
1473
    }
8✔
1474
    hunt = di.zone;
2,922✔
1475
  }
2,922✔
1476

1477
  DNSName relqname = qdomain.makeRelative(hunt);
3,732✔
1478
  if (relqname.empty()) {
3,732!
1479
    return;
×
1480
  }
×
1481
  // cout<<"get will look for "<<relqname<< " in zone "<<hunt<<" with id "<<zoneId<<" and type "<<type.toString()<<endl;
1482
  d_rotxn = getRecordsROTransaction(zoneId, d_rwtxn);
3,732✔
1483

1484
  compoundOrdername co;
3,732✔
1485
  d_getcursor = std::make_shared<MDBROCursor>(d_rotxn->txn->getCursor(d_rotxn->db->dbi));
3,732✔
1486
  MDBOutVal key, val;
3,732✔
1487
  if (type.getCode() == QType::ANY) {
3,732✔
1488
    d_matchkey = co(zoneId, relqname);
2,917✔
1489
  }
2,917✔
1490
  else {
815✔
1491
    d_matchkey = co(zoneId, relqname, type.getCode());
815✔
1492
  }
815✔
1493

1494
  if (d_getcursor->lower_bound(d_matchkey, key, val) || key.getNoStripHeader<StringView>().rfind(d_matchkey, 0) != 0) {
3,732✔
1495
    d_getcursor.reset();
1,261✔
1496
    if (d_dolog) {
1,261!
1497
      g_log << Logger::Warning << "Query " << ((long)(void*)this) << ": " << d_dtime.udiffNoReset() << " us to execute (found nothing)" << endl;
×
1498
    }
×
1499
    return;
1,261✔
1500
  }
1,261✔
1501

1502
  if (d_dolog) {
2,471!
1503
    g_log << Logger::Warning << "Query " << ((long)(void*)this) << ": " << d_dtime.udiffNoReset() << " us to execute" << endl;
×
1504
  }
×
1505

1506
  d_lookupdomain = hunt;
2,471✔
1507

1508
  // Make sure we start with fresh data
1509
  d_currentrrset.clear();
2,471✔
1510
  d_currentrrsetpos = 0;
2,471✔
1511
}
2,471✔
1512

1513
bool LMDBBackend::get(DNSZoneRecord& zr)
1514
{
258,747✔
1515
  for (;;) {
362,967✔
1516
    // std::cerr<<"d_getcursor="<<d_getcursor<<std::endl;
1517
    if (!d_getcursor) {
362,967✔
1518
      d_rotxn.reset();
4,353✔
1519
      return false;
4,353✔
1520
    }
4,353✔
1521

1522
    string_view key;
358,614✔
1523

1524
    if (d_currentrrset.empty()) {
358,614✔
1525
      d_getcursor->current(d_currentKey, d_currentVal);
354,834✔
1526

1527
      key = d_currentKey.getNoStripHeader<string_view>();
354,834✔
1528
      zr.dr.d_type = compoundOrdername::getQType(key).getCode();
354,834✔
1529

1530
      if (zr.dr.d_type == QType::NSEC3) {
354,834✔
1531
        // Hit a magic NSEC3 skipping
1532
        if (d_getcursor->next(d_currentKey, d_currentVal) || d_currentKey.getNoStripHeader<StringView>().rfind(d_matchkey, 0) != 0) {
104,217✔
1533
          // cerr<<"resetting d_getcursor 1"<<endl;
1534
          d_getcursor.reset();
1,066✔
1535
        }
1,066✔
1536
        continue;
104,217✔
1537
      }
104,217✔
1538

1539
      deserializeFromBuffer(d_currentVal.get<string_view>(), d_currentrrset);
250,617✔
1540
      d_currentrrsetpos = 0;
250,617✔
1541
    }
250,617✔
1542
    else {
3,780✔
1543
      key = d_currentKey.getNoStripHeader<string_view>();
3,780✔
1544
    }
3,780✔
1545
    try {
254,397✔
1546
      const auto& lrr = d_currentrrset.at(d_currentrrsetpos++);
254,397✔
1547

1548
      zr.disabled = lrr.disabled;
254,397✔
1549
      if (!zr.disabled || d_includedisabled) {
254,397✔
1550
        zr.dr.d_name = compoundOrdername::getQName(key) + d_lookupdomain;
254,394✔
1551
        zr.domain_id = compoundOrdername::getDomainID(key);
254,394✔
1552
        zr.dr.d_type = compoundOrdername::getQType(key).getCode();
254,394✔
1553
        zr.dr.d_ttl = lrr.ttl;
254,394✔
1554
        zr.dr.setContent(deserializeContentZR(zr.dr.d_type, zr.dr.d_name, lrr.content));
254,394✔
1555
        zr.auth = lrr.auth;
254,394✔
1556
      }
254,394✔
1557

1558
      if (d_currentrrsetpos >= d_currentrrset.size()) {
254,397✔
1559
        d_currentrrset.clear(); // will invalidate lrr
250,617✔
1560
        if (d_getcursor->next(d_currentKey, d_currentVal) || d_currentKey.getNoStripHeader<StringView>().rfind(d_matchkey, 0) != 0) {
250,617✔
1561
          // cerr<<"resetting d_getcursor 2"<<endl;
1562
          d_getcursor.reset();
2,007✔
1563
        }
2,007✔
1564
      }
250,617✔
1565

1566
      if (zr.disabled && !d_includedisabled) {
254,397✔
1567
        continue;
3✔
1568
      }
3✔
1569
    }
254,397✔
1570
    catch (const std::exception& e) {
254,397✔
1571
      throw PDNSException(e.what());
×
1572
    }
×
1573

1574
    break;
254,394✔
1575
  }
254,397✔
1576

1577
  return true;
254,394✔
1578
}
258,747✔
1579

1580
bool LMDBBackend::get(DNSResourceRecord& rr)
1581
{
165,419✔
1582
  DNSZoneRecord zr;
165,419✔
1583
  if (!get(zr)) {
165,419✔
1584
    return false;
1,216✔
1585
  }
1,216✔
1586

1587
  rr.qname = zr.dr.d_name;
164,203✔
1588
  rr.ttl = zr.dr.d_ttl;
164,203✔
1589
  rr.qtype = zr.dr.d_type;
164,203✔
1590
  rr.content = zr.dr.getContent()->getZoneRepresentation(true);
164,203✔
1591
  rr.domain_id = zr.domain_id;
164,203✔
1592
  rr.auth = zr.auth;
164,203✔
1593
  rr.disabled = zr.disabled;
164,203✔
1594

1595
  return true;
164,203✔
1596
}
165,419✔
1597

1598
bool LMDBBackend::getSerial(DomainInfo& di)
1599
{
1,653✔
1600
  auto txn = getRecordsROTransaction(di.id);
1,653✔
1601
  compoundOrdername co;
1,653✔
1602
  MDBOutVal val;
1,653✔
1603
  if (!txn->txn->get(txn->db->dbi, co(di.id, g_rootdnsname, QType::SOA), val)) {
1,653✔
1604
    LMDBResourceRecord lrr;
1,147✔
1605
    deserializeFromBuffer(val.get<string_view>(), lrr);
1,147✔
1606
    if (lrr.content.size() >= 5 * sizeof(uint32_t)) {
1,147!
1607
      uint32_t serial;
1,147✔
1608
      // a SOA has five 32 bit fields, the first of which is the serial
1609
      // there are two variable length names before the serial, so we calculate from the back
1610
      memcpy(&serial, &lrr.content[lrr.content.size() - (5 * sizeof(uint32_t))], sizeof(serial));
1,147✔
1611
      di.serial = ntohl(serial);
1,147✔
1612
    }
1,147✔
1613
    return !lrr.disabled;
1,147✔
1614
  }
1,147✔
1615
  return false;
506✔
1616
}
1,653✔
1617

1618
bool LMDBBackend::getDomainInfo(const DNSName& domain, DomainInfo& di, bool getserial)
1619
{
1,621✔
1620
  {
1,621✔
1621
    auto txn = d_tdomains->getROTransaction();
1,621✔
1622
    // auto range = txn.prefix_range<0>(domain);
1623

1624
    // bool found = false;
1625

1626
    // for (auto& iter = range.first ; iter != range.second; ++iter) {
1627
    //   found = true;
1628
    //   di.id = iter.getID();
1629
    //   di.backend = this;
1630
    // }
1631

1632
    // if (!found) {
1633
    //   return false;
1634
    // }
1635
    if (!(di.id = txn.get<0>(domain, di))) {
1,621✔
1636
      return false;
303✔
1637
    }
303✔
1638

1639
    di.backend = this;
1,318✔
1640
  }
1,318✔
1641

1642
  if (getserial) {
1,318✔
1643
    getSerial(di);
1,021✔
1644
  }
1,021✔
1645

1646
  return true;
1,318✔
1647
}
1,621✔
1648

1649
int LMDBBackend::genChangeDomain(const DNSName& domain, const std::function<void(DomainInfo&)>& func)
1650
{
526✔
1651
  auto txn = d_tdomains->getRWTransaction();
526✔
1652

1653
  DomainInfo di;
526✔
1654

1655
  auto id = txn.get<0>(domain, di);
526✔
1656
  func(di);
526✔
1657
  txn.put(di, id);
526✔
1658

1659
  txn.commit();
526✔
1660
  return true;
526✔
1661
}
526✔
1662

1663
int LMDBBackend::genChangeDomain(uint32_t id, const std::function<void(DomainInfo&)>& func)
1664
{
91✔
1665
  DomainInfo di;
91✔
1666

1667
  auto txn = d_tdomains->getRWTransaction();
91✔
1668

1669
  if (!txn.get(id, di))
91!
1670
    return false;
×
1671

1672
  func(di);
91✔
1673

1674
  txn.put(di, id);
91✔
1675

1676
  txn.commit();
91✔
1677
  return true;
91✔
1678
}
91✔
1679

1680
bool LMDBBackend::setKind(const DNSName& domain, const DomainInfo::DomainKind kind)
1681
{
205✔
1682
  return genChangeDomain(domain, [kind](DomainInfo& di) {
205✔
1683
    di.kind = kind;
205✔
1684
  });
205✔
1685
}
205✔
1686

1687
bool LMDBBackend::setAccount(const DNSName& domain, const std::string& account)
1688
{
1✔
1689
  return genChangeDomain(domain, [account](DomainInfo& di) {
1✔
1690
    di.account = account;
1✔
1691
  });
1✔
1692
}
1✔
1693

1694
bool LMDBBackend::setPrimaries(const DNSName& domain, const vector<ComboAddress>& primaries)
1695
{
71✔
1696
  return genChangeDomain(domain, [&primaries](DomainInfo& di) {
71✔
1697
    di.primaries = primaries;
71✔
1698
  });
71✔
1699
}
71✔
1700

1701
bool LMDBBackend::createDomain(const DNSName& domain, const DomainInfo::DomainKind kind, const vector<ComboAddress>& primaries, const string& account)
1702
{
293✔
1703
  DomainInfo di;
293✔
1704

1705
  {
293✔
1706
    auto txn = d_tdomains->getRWTransaction();
293✔
1707
    if (txn.get<0>(domain, di)) {
293!
1708
      throw DBException("Domain '" + domain.toLogString() + "' exists already");
×
1709
    }
×
1710

1711
    di.zone = domain;
293✔
1712
    di.kind = kind;
293✔
1713
    di.primaries = primaries;
293✔
1714
    di.account = account;
293✔
1715

1716
    txn.put(di, 0, d_random_ids);
293✔
1717
    txn.commit();
293✔
1718
  }
293✔
1719

1720
  return true;
×
1721
}
293✔
1722

1723
void LMDBBackend::getAllDomainsFiltered(vector<DomainInfo>* domains, const std::function<bool(DomainInfo&)>& allow)
1724
{
77✔
1725
  auto txn = d_tdomains->getROTransaction();
77✔
1726
  if (d_handle_dups) {
77!
1727
    map<DNSName, DomainInfo> zonemap;
×
1728
    set<DNSName> dups;
×
1729

1730
    for (auto iter = txn.begin(); iter != txn.end(); ++iter) {
×
1731
      DomainInfo di = *iter;
×
1732
      di.id = iter.getID();
×
1733
      di.backend = this;
×
1734

1735
      if (!zonemap.emplace(di.zone, di).second) {
×
1736
        dups.insert(di.zone);
×
1737
      }
×
1738
    }
×
1739

1740
    for (const auto& zone : dups) {
×
1741
      DomainInfo di;
×
1742

1743
      // this get grabs the oldest item if there are duplicates
1744
      di.id = txn.get<0>(zone, di);
×
1745

1746
      if (di.id == 0) {
×
1747
        // .get actually found nothing for us
1748
        continue;
×
1749
      }
×
1750

1751
      di.backend = this;
×
1752
      zonemap[di.zone] = di;
×
1753
    }
×
1754

1755
    for (auto& [k, v] : zonemap) {
×
1756
      if (allow(v)) {
×
1757
        domains->push_back(std::move(v));
×
1758
      }
×
1759
    }
×
1760
  }
×
1761
  else {
77✔
1762
    for (auto iter = txn.begin(); iter != txn.end(); ++iter) {
1,024✔
1763
      DomainInfo di = *iter;
947✔
1764
      di.id = iter.getID();
947✔
1765
      di.backend = this;
947✔
1766

1767
      if (allow(di)) {
947✔
1768
        domains->push_back(di);
664✔
1769
      }
664✔
1770
    }
947✔
1771
  }
77✔
1772
}
77✔
1773

1774
void LMDBBackend::getAllDomains(vector<DomainInfo>* domains, bool /* doSerial */, bool include_disabled)
1775
{
50✔
1776
  domains->clear();
50✔
1777

1778
  getAllDomainsFiltered(domains, [this, include_disabled](DomainInfo& di) {
632✔
1779
    if (!getSerial(di) && !include_disabled) {
632!
1780
      return false;
×
1781
    }
×
1782

1783
    return true;
632✔
1784
  });
632✔
1785
}
50✔
1786

1787
void LMDBBackend::getUnfreshSecondaryInfos(vector<DomainInfo>* domains)
1788
{
11✔
1789
  uint32_t serial;
11✔
1790
  time_t now = time(0);
11✔
1791
  LMDBResourceRecord lrr;
11✔
1792
  soatimes st;
11✔
1793

1794
  getAllDomainsFiltered(domains, [this, &lrr, &st, &now, &serial](DomainInfo& di) {
83✔
1795
    if (!di.isSecondaryType()) {
83!
1796
      return false;
×
1797
    }
×
1798

1799
    auto txn2 = getRecordsROTransaction(di.id);
83✔
1800
    compoundOrdername co;
83✔
1801
    MDBOutVal val;
83✔
1802
    if (!txn2->txn->get(txn2->db->dbi, co(di.id, g_rootdnsname, QType::SOA), val)) {
83✔
1803
      deserializeFromBuffer(val.get<string_view>(), lrr);
51✔
1804
      memcpy(&st, &lrr.content[lrr.content.size() - sizeof(soatimes)], sizeof(soatimes));
51✔
1805
      if ((time_t)(di.last_check + ntohl(st.refresh)) > now) { // still fresh
51!
1806
        return false;
51✔
1807
      }
51✔
1808
      serial = ntohl(st.serial);
×
1809
    }
×
1810
    else {
32✔
1811
      serial = 0;
32✔
1812
    }
32✔
1813

1814
    return true;
32✔
1815
  });
83✔
1816
}
11✔
1817

1818
void LMDBBackend::setStale(uint32_t domain_id)
1819
{
2✔
1820
  genChangeDomain(domain_id, [](DomainInfo& di) {
2✔
1821
    di.last_check = 0;
2✔
1822
  });
2✔
1823
}
2✔
1824

1825
void LMDBBackend::setFresh(uint32_t domain_id)
1826
{
88✔
1827
  genChangeDomain(domain_id, [](DomainInfo& di) {
88✔
1828
    di.last_check = time(nullptr);
88✔
1829
  });
88✔
1830
}
88✔
1831

1832
void LMDBBackend::getUpdatedPrimaries(vector<DomainInfo>& updatedDomains, std::unordered_set<DNSName>& catalogs, CatalogHashMap& catalogHashes)
1833
{
×
1834
  CatalogInfo ci;
×
1835

1836
  getAllDomainsFiltered(&(updatedDomains), [this, &catalogs, &catalogHashes, &ci](DomainInfo& di) {
×
1837
    if (!di.isPrimaryType()) {
×
1838
      return false;
×
1839
    }
×
1840

1841
    if (di.kind == DomainInfo::Producer) {
×
1842
      catalogs.insert(di.zone);
×
1843
      catalogHashes[di.zone].process("\0");
×
1844
      return false; // Producer fresness check is performed elsewhere
×
1845
    }
×
1846

1847
    if (!di.catalog.empty()) {
×
1848
      ci.fromJson(di.options, CatalogInfo::CatalogType::Producer);
×
1849
      ci.updateHash(catalogHashes, di);
×
1850
    }
×
1851

1852
    if (getSerial(di) && di.serial != di.notified_serial) {
×
1853
      di.backend = this;
×
1854
      return true;
×
1855
    }
×
1856

1857
    return false;
×
1858
  });
×
1859
}
×
1860

1861
void LMDBBackend::setNotified(uint32_t domain_id, uint32_t serial)
1862
{
1✔
1863
  genChangeDomain(domain_id, [serial](DomainInfo& di) {
1✔
1864
    di.notified_serial = serial;
1✔
1865
  });
1✔
1866
}
1✔
1867

1868
class getCatalogMembersReturnFalseException : std::runtime_error
1869
{
1870
public:
1871
  getCatalogMembersReturnFalseException() :
1872
    std::runtime_error("getCatalogMembers should return false") {}
×
1873
};
1874

1875
bool LMDBBackend::getCatalogMembers(const DNSName& catalog, vector<CatalogInfo>& members, CatalogInfo::CatalogType type)
1876
{
16✔
1877
  vector<DomainInfo> scratch;
16✔
1878

1879
  try {
16✔
1880
    getAllDomainsFiltered(&scratch, [&catalog, &members, &type](DomainInfo& di) {
232✔
1881
      if ((type == CatalogInfo::CatalogType::Producer && di.kind != DomainInfo::Primary) || (type == CatalogInfo::CatalogType::Consumer && di.kind != DomainInfo::Secondary) || di.catalog != catalog) {
232✔
1882
        return false;
28✔
1883
      }
28✔
1884

1885
      CatalogInfo ci;
204✔
1886
      ci.d_id = di.id;
204✔
1887
      ci.d_zone = di.zone;
204✔
1888
      ci.d_primaries = di.primaries;
204✔
1889
      try {
204✔
1890
        ci.fromJson(di.options, type);
204✔
1891
      }
204✔
1892
      catch (const std::runtime_error& e) {
204✔
1893
        g_log << Logger::Warning << __PRETTY_FUNCTION__ << " options '" << di.options << "' for zone '" << di.zone << "' is no valid JSON: " << e.what() << endl;
×
1894
        members.clear();
×
1895
        throw getCatalogMembersReturnFalseException();
×
1896
      }
×
1897
      members.emplace_back(ci);
204✔
1898

1899
      return false;
204✔
1900
    });
204✔
1901
  }
16✔
1902
  catch (const getCatalogMembersReturnFalseException& e) {
16✔
1903
    return false;
×
1904
  }
×
1905
  return true;
16✔
1906
}
16✔
1907

1908
bool LMDBBackend::setOptions(const DNSName& domain, const std::string& options)
1909
{
86✔
1910
  return genChangeDomain(domain, [options](DomainInfo& di) {
86✔
1911
    di.options = options;
86✔
1912
  });
86✔
1913
}
86✔
1914

1915
bool LMDBBackend::setCatalog(const DNSName& domain, const DNSName& catalog)
1916
{
163✔
1917
  return genChangeDomain(domain, [catalog](DomainInfo& di) {
163✔
1918
    di.catalog = catalog;
163✔
1919
  });
163✔
1920
}
163✔
1921

1922
bool LMDBBackend::getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string>>& meta)
1923
{
3,308✔
1924
  meta.clear();
3,308✔
1925
  auto txn = d_tmeta->getROTransaction();
3,308✔
1926
  LmdbIdVec ids;
3,308✔
1927
  txn.get_multi<0>(name, ids);
3,308✔
1928

1929
  DomainMeta dm;
3,308✔
1930
  // cerr<<"getAllDomainMetadata start"<<endl;
1931
  for (auto id : ids) {
3,659✔
1932
    if (txn.get(id, dm)) {
3,440!
1933
      meta[dm.key].push_back(dm.value);
3,440✔
1934
    }
3,440✔
1935
  }
3,440✔
1936
  return true;
3,308✔
1937
}
3,308✔
1938

1939
bool LMDBBackend::setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta)
1940
{
437✔
1941
  auto txn = d_tmeta->getRWTransaction();
437✔
1942

1943
  LmdbIdVec ids;
437✔
1944
  txn.get_multi<0>(name, ids);
437✔
1945

1946
  DomainMeta dmeta;
437✔
1947
  for (auto id : ids) {
437✔
1948
    if (txn.get(id, dmeta)) {
288!
1949
      if (dmeta.key == kind) {
288✔
1950
        // cerr<<"delete"<<endl;
1951
        txn.del(id);
32✔
1952
      }
32✔
1953
    }
288✔
1954
  }
288✔
1955

1956
  for (const auto& m : meta) {
437✔
1957
    DomainMeta dm{name, kind, m};
344✔
1958
    txn.put(dm, 0, d_random_ids);
344✔
1959
  }
344✔
1960
  txn.commit();
437✔
1961
  return true;
437✔
1962
}
437✔
1963

1964
bool LMDBBackend::getDomainKeys(const DNSName& name, std::vector<KeyData>& keys)
1965
{
728✔
1966
  auto txn = d_tkdb->getROTransaction();
728✔
1967
  LmdbIdVec ids;
728✔
1968
  txn.get_multi<0>(name, ids);
728✔
1969

1970
  KeyDataDB key;
728✔
1971

1972
  for (auto id : ids) {
728✔
1973
    if (txn.get(id, key)) {
337!
1974
      KeyData kd{key.content, id, key.flags, key.active, key.published};
337✔
1975
      keys.push_back(kd);
337✔
1976
    }
337✔
1977
  }
337✔
1978

1979
  return true;
728✔
1980
}
728✔
1981

1982
bool LMDBBackend::removeDomainKey(const DNSName& name, unsigned int id)
1983
{
29✔
1984
  auto txn = d_tkdb->getRWTransaction();
29✔
1985
  KeyDataDB kdb;
29✔
1986
  if (txn.get(id, kdb)) {
29✔
1987
    if (kdb.domain == name) {
20!
1988
      txn.del(id);
20✔
1989
      txn.commit();
20✔
1990
      return true;
20✔
1991
    }
20✔
1992
  }
20✔
1993
  // cout << "??? wanted to remove domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
1994
  return true;
9✔
1995
}
29✔
1996

1997
bool LMDBBackend::addDomainKey(const DNSName& name, const KeyData& key, int64_t& id)
1998
{
114✔
1999
  auto txn = d_tkdb->getRWTransaction();
114✔
2000
  KeyDataDB kdb{name, key.content, key.flags, key.active, key.published};
114✔
2001
  id = txn.put(kdb, 0, d_random_ids);
114✔
2002
  txn.commit();
114✔
2003

2004
  return true;
114✔
2005
}
114✔
2006

2007
bool LMDBBackend::activateDomainKey(const DNSName& name, unsigned int id)
2008
{
6✔
2009
  auto txn = d_tkdb->getRWTransaction();
6✔
2010
  KeyDataDB kdb;
6✔
2011
  if (txn.get(id, kdb)) {
6!
2012
    if (kdb.domain == name) {
6!
2013
      txn.modify(id, [](KeyDataDB& kdbarg) {
6✔
2014
        kdbarg.active = true;
6✔
2015
      });
6✔
2016
      txn.commit();
6✔
2017
      return true;
6✔
2018
    }
6✔
2019
  }
6✔
2020

2021
  // cout << "??? wanted to activate domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
2022
  return true;
×
2023
}
6✔
2024

2025
bool LMDBBackend::deactivateDomainKey(const DNSName& name, unsigned int id)
2026
{
4✔
2027
  auto txn = d_tkdb->getRWTransaction();
4✔
2028
  KeyDataDB kdb;
4✔
2029
  if (txn.get(id, kdb)) {
4!
2030
    if (kdb.domain == name) {
4!
2031
      txn.modify(id, [](KeyDataDB& kdbarg) {
4✔
2032
        kdbarg.active = false;
4✔
2033
      });
4✔
2034
      txn.commit();
4✔
2035
      return true;
4✔
2036
    }
4✔
2037
  }
4✔
2038
  // cout << "??? wanted to deactivate domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
2039
  return true;
×
2040
}
4✔
2041

2042
bool LMDBBackend::publishDomainKey(const DNSName& name, unsigned int id)
2043
{
6✔
2044
  auto txn = d_tkdb->getRWTransaction();
6✔
2045
  KeyDataDB kdb;
6✔
2046
  if (txn.get(id, kdb)) {
6!
2047
    if (kdb.domain == name) {
6!
2048
      txn.modify(id, [](KeyDataDB& kdbarg) {
6✔
2049
        kdbarg.published = true;
6✔
2050
      });
6✔
2051
      txn.commit();
6✔
2052
      return true;
6✔
2053
    }
6✔
2054
  }
6✔
2055

2056
  // cout << "??? wanted to hide domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
2057
  return true;
×
2058
}
6✔
2059

2060
bool LMDBBackend::unpublishDomainKey(const DNSName& name, unsigned int id)
2061
{
6✔
2062
  auto txn = d_tkdb->getRWTransaction();
6✔
2063
  KeyDataDB kdb;
6✔
2064
  if (txn.get(id, kdb)) {
6!
2065
    if (kdb.domain == name) {
6!
2066
      txn.modify(id, [](KeyDataDB& kdbarg) {
6✔
2067
        kdbarg.published = false;
6✔
2068
      });
6✔
2069
      txn.commit();
6✔
2070
      return true;
6✔
2071
    }
6✔
2072
  }
6✔
2073
  // cout << "??? wanted to unhide domain key for domain "<<name<<" with id "<<id<<", could not find it"<<endl;
2074
  return true;
×
2075
}
6✔
2076

2077
bool LMDBBackend::getBeforeAndAfterNamesAbsolute(uint32_t id, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after)
2078
{
3,441✔
2079
  //  cout << __PRETTY_FUNCTION__<< ": "<<id <<", "<<qname << " " << unhashed<<endl;
2080

2081
  DomainInfo di;
3,441✔
2082
  if (!d_tdomains->getROTransaction().get(id, di)) {
3,441!
2083
    // domain does not exist, tough luck
2084
    return false;
×
2085
  }
×
2086
  // cout <<"Zone: "<<di.zone<<endl;
2087

2088
  compoundOrdername co;
3,441✔
2089
  auto txn = getRecordsROTransaction(id);
3,441✔
2090

2091
  auto cursor = txn->txn->getCursor(txn->db->dbi);
3,441✔
2092
  MDBOutVal key, val;
3,441✔
2093

2094
  LMDBResourceRecord lrr;
3,441✔
2095

2096
  string matchkey = co(id, qname, QType::NSEC3);
3,441✔
2097
  if (cursor.lower_bound(matchkey, key, val)) {
3,441✔
2098
    // this is beyond the end of the database
2099
    // cout << "Beyond end of database!" << endl;
2100
    cursor.last(key, val);
120✔
2101

2102
    for (;;) {
539✔
2103
      if (co.getDomainID(key.getNoStripHeader<StringView>()) != id) {
539!
2104
        // cout<<"Last record also not part of this zone!"<<endl;
2105
        //  this implies something is wrong in the database, nothing we can do
2106
        return false;
×
2107
      }
×
2108

2109
      if (co.getQType(key.getNoStripHeader<StringView>()) == QType::NSEC3) {
539✔
2110
        deserializeFromBuffer(val.get<StringView>(), lrr);
194✔
2111
        if (!lrr.ttl) // the kind of NSEC3 we need
194✔
2112
          break;
120✔
2113
      }
194✔
2114
      if (cursor.prev(key, val)) {
419!
2115
        // hit beginning of database, again means something is wrong with it
2116
        return false;
×
2117
      }
×
2118
    }
419✔
2119
    before = co.getQName(key.getNoStripHeader<StringView>());
120✔
2120
    unhashed = DNSName(lrr.content.c_str(), lrr.content.size(), 0, false) + di.zone;
120✔
2121

2122
    // now to find after .. at the beginning of the zone
2123
    if (cursor.lower_bound(co(id), key, val)) {
120!
2124
      // cout<<"hit end of zone find when we shouldn't"<<endl;
2125
      return false;
×
2126
    }
×
2127
    for (;;) {
690✔
2128
      if (co.getQType(key.getNoStripHeader<StringView>()) == QType::NSEC3) {
690✔
2129
        deserializeFromBuffer(val.get<StringView>(), lrr);
253✔
2130
        if (!lrr.ttl)
253✔
2131
          break;
120✔
2132
      }
253✔
2133

2134
      if (cursor.next(key, val) || co.getDomainID(key.getNoStripHeader<StringView>()) != id) {
570!
2135
        // cout<<"hit end of zone or database when we shouldn't"<<endl;
2136
        return false;
×
2137
      }
×
2138
    }
570✔
2139
    after = co.getQName(key.getNoStripHeader<StringView>());
120✔
2140
    // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl;
2141
    return true;
120✔
2142
  }
120✔
2143

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

2146
  before = co.getQName(key.getNoStripHeader<StringView>());
3,321✔
2147
  if (before == qname) {
3,321✔
2148
    // cout << "Ended up on exact right node" << endl;
2149
    before = co.getQName(key.getNoStripHeader<StringView>());
1,853✔
2150
    // unhashed should be correct now, maybe check?
2151
    if (cursor.next(key, val)) {
1,853✔
2152
      // xxx should find first hash now
2153

2154
      if (cursor.lower_bound(co(id), key, val)) {
80!
2155
        // cout<<"hit end of zone find when we shouldn't for id "<<id<< __LINE__<<endl;
2156
        return false;
×
2157
      }
×
2158
      for (;;) {
438✔
2159
        if (co.getQType(key.getNoStripHeader<StringView>()) == QType::NSEC3) {
438✔
2160
          deserializeFromBuffer(val.get<StringView>(), lrr);
160✔
2161
          if (!lrr.ttl)
160✔
2162
            break;
80✔
2163
        }
160✔
2164

2165
        if (cursor.next(key, val) || co.getDomainID(key.getNoStripHeader<StringView>()) != id) {
358!
2166
          // cout<<"hit end of zone or database when we shouldn't" << __LINE__<<endl;
2167
          return false;
×
2168
        }
×
2169
      }
358✔
2170
      after = co.getQName(key.getNoStripHeader<StringView>());
80✔
2171
      // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl;
2172
      return true;
80✔
2173
    }
80✔
2174
  }
1,853✔
2175
  else {
1,468✔
2176
    // cout <<"Going backwards to find 'before'"<<endl;
2177
    int count = 0;
1,468✔
2178
    for (;;) {
3,814✔
2179
      if (co.getQName(key.getNoStripHeader<StringView>()).canonCompare(qname) && co.getQType(key.getNoStripHeader<StringView>()) == QType::NSEC3) {
3,814✔
2180
        // cout<<"Potentially stopping traverse at "<< co.getQName(key.get<StringView>()) <<", " << (co.getQName(key.get<StringView>()).canonCompare(qname))<<endl;
2181
        // cout<<"qname = "<<qname<<endl;
2182
        // cout<<"here  = "<<co.getQName(key.get<StringView>())<<endl;
2183
        deserializeFromBuffer(val.get<StringView>(), lrr);
1,708✔
2184
        if (!lrr.ttl)
1,708✔
2185
          break;
1,405✔
2186
      }
1,708✔
2187

2188
      if (cursor.prev(key, val) || co.getDomainID(key.getNoStripHeader<StringView>()) != id) {
2,409!
2189
        // cout <<"XXX Hit *beginning* of zone or database"<<endl;
2190
        // this can happen, must deal with it
2191
        // should now find the last hash of the zone
2192

2193
        if (cursor.lower_bound(co(id + 1), key, val)) {
63!
2194
          // cout << "Could not find the next higher zone, going to the end of the database then"<<endl;
2195
          cursor.last(key, val);
63✔
2196
        }
63✔
UNCOV
2197
        else
×
UNCOV
2198
          cursor.prev(key, val);
×
2199

2200
        for (;;) {
566✔
2201
          if (co.getDomainID(key.getNoStripHeader<StringView>()) != id) {
566!
2202
            // cout<<"Last record also not part of this zone!"<<endl;
2203
            //  this implies something is wrong in the database, nothing we can do
2204
            return false;
×
2205
          }
×
2206

2207
          if (co.getQType(key.getNoStripHeader<StringView>()) == QType::NSEC3) {
566✔
2208
            deserializeFromBuffer(val.get<StringView>(), lrr);
136✔
2209
            if (!lrr.ttl) // the kind of NSEC3 we need
136✔
2210
              break;
63✔
2211
          }
136✔
2212
          if (cursor.prev(key, val)) {
503!
2213
            // hit beginning of database, again means something is wrong with it
2214
            return false;
×
2215
          }
×
2216
        }
503✔
2217
        before = co.getQName(key.getNoStripHeader<StringView>());
63✔
2218
        unhashed = DNSName(lrr.content.c_str(), lrr.content.size(), 0, false) + di.zone;
63✔
2219
        // cout <<"Should still find 'after'!"<<endl;
2220
        // for 'after', we need to find the first hash of this zone
2221

2222
        if (cursor.lower_bound(co(id), key, val)) {
63!
2223
          // cout<<"hit end of zone find when we shouldn't"<<endl;
2224
          // means database is wrong, nothing we can do
2225
          return false;
×
2226
        }
×
2227
        for (;;) {
405✔
2228
          if (co.getQType(key.getNoStripHeader<StringView>()) == QType::NSEC3) {
405✔
2229
            deserializeFromBuffer(val.get<StringView>(), lrr);
146✔
2230
            if (!lrr.ttl)
146✔
2231
              break;
63✔
2232
          }
146✔
2233

2234
          if (cursor.next(key, val)) {
342!
2235
            // means database is wrong, nothing we can do
2236
            // cout<<"hit end of zone when we shouldn't 2"<<endl;
2237
            return false;
×
2238
          }
×
2239
        }
342✔
2240
        after = co.getQName(key.getNoStripHeader<StringView>());
63✔
2241

2242
        // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl;
2243
        return true;
63✔
2244
      }
63✔
2245
      ++count;
2,346✔
2246
    }
2,346✔
2247
    before = co.getQName(key.getNoStripHeader<StringView>());
1,405✔
2248
    unhashed = DNSName(lrr.content.c_str(), lrr.content.size(), 0, false) + di.zone;
1,405✔
2249
    // cout<<"Went backwards, found "<<before<<endl;
2250
    // return us to starting point
2251
    while (count--)
3,430✔
2252
      cursor.next(key, val);
2,025✔
2253
  }
1,405✔
2254
  //  cout<<"Now going forward"<<endl;
2255
  for (int count = 0;; ++count) {
7,327✔
2256
    if ((count && cursor.next(key, val)) || co.getDomainID(key.getNoStripHeader<StringView>()) != id) {
7,327✔
2257
      // cout <<"Hit end of database or zone, finding first hash then in zone "<<id<<endl;
2258
      if (cursor.lower_bound(co(id), key, val)) {
102!
2259
        // cout<<"hit end of zone find when we shouldn't"<<endl;
2260
        // means database is wrong, nothing we can do
2261
        return false;
×
2262
      }
×
2263
      for (;;) {
801✔
2264
        if (co.getQType(key.getNoStripHeader<StringView>()) == QType::NSEC3) {
801✔
2265
          deserializeFromBuffer(val.get<StringView>(), lrr);
256✔
2266
          if (!lrr.ttl)
256✔
2267
            break;
102✔
2268
        }
256✔
2269

2270
        if (cursor.next(key, val)) {
699!
2271
          // means database is wrong, nothing we can do
2272
          // cout<<"hit end of zone when we shouldn't 2"<<endl;
2273
          return false;
×
2274
        }
×
2275
        // cout << "Next.. "<<endl;
2276
      }
699✔
2277
      after = co.getQName(key.getNoStripHeader<StringView>());
102✔
2278

2279
      // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl;
2280
      return true;
102✔
2281
    }
102✔
2282

2283
    // cout<<"After "<<co.getQName(key.get<StringView>()) <<endl;
2284
    if (co.getQType(key.getNoStripHeader<StringView>()) == QType::NSEC3) {
7,225✔
2285
      deserializeFromBuffer(val.get<StringView>(), lrr);
4,276✔
2286
      if (!lrr.ttl) {
4,276✔
2287
        break;
3,076✔
2288
      }
3,076✔
2289
    }
4,276✔
2290
  }
7,225✔
2291
  after = co.getQName(key.getNoStripHeader<StringView>());
3,076✔
2292
  // cout<<"returning: before="<<before<<", after="<<after<<", unhashed: "<<unhashed<<endl;
2293
  return true;
3,076✔
2294
}
3,178✔
2295

2296
bool LMDBBackend::getBeforeAndAfterNames(uint32_t id, const DNSName& zonenameU, const DNSName& qname, DNSName& before, DNSName& after)
2297
{
1,423✔
2298
  DNSName zonename = zonenameU.makeLowerCase();
1,423✔
2299
  //  cout << __PRETTY_FUNCTION__<< ": "<<id <<", "<<zonename << ", '"<<qname<<"'"<<endl;
2300

2301
  auto txn = getRecordsROTransaction(id);
1,423✔
2302
  compoundOrdername co;
1,423✔
2303
  DNSName qname2 = qname.makeRelative(zonename);
1,423✔
2304
  string matchkey = co(id, qname2);
1,423✔
2305
  auto cursor = txn->txn->getCursor(txn->db->dbi);
1,423✔
2306
  MDBOutVal key, val;
1,423✔
2307
  // cout<<"Lower_bound for "<<qname2<<endl;
2308
  if (cursor.lower_bound(matchkey, key, val)) {
1,423✔
2309
    // cout << "Hit end of database, bummer"<<endl;
2310
    cursor.last(key, val);
121✔
2311
    if (co.getDomainID(key.getNoStripHeader<string_view>()) == id) {
121!
2312
      before = co.getQName(key.getNoStripHeader<string_view>()) + zonename;
121✔
2313
      after = zonename;
121✔
2314
    }
121✔
2315
    // else
2316
    // cout << "We were at end of database, but this zone is not there?!"<<endl;
2317
    return true;
121✔
2318
  }
121✔
2319
  // cout<<"Cursor is at "<<co.getQName(key.get<string_view>()) <<", in zone id "<<co.getDomainID(key.get<string_view>())<< endl;
2320

2321
  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,302✔
2322
    // cout << "Had an exact match!"<<endl;
2323
    before = qname2 + zonename;
600✔
2324
    int rc;
600✔
2325
    for (;;) {
2,238✔
2326
      rc = cursor.next(key, val);
2,238✔
2327
      if (rc)
2,238✔
2328
        break;
20✔
2329

2330
      if (co.getDomainID(key.getNoStripHeader<string_view>()) == id && key.getNoStripHeader<StringView>().rfind(matchkey, 0) == 0)
2,218✔
2331
        continue;
1,280✔
2332
      LMDBResourceRecord lrr;
938✔
2333
      deserializeFromBuffer(val.get<StringView>(), lrr);
938✔
2334
      if (co.getQType(key.getNoStripHeader<string_view>()).getCode() && (lrr.auth || co.getQType(key.getNoStripHeader<string_view>()).getCode() == QType::NS))
938✔
2335
        break;
580✔
2336
    }
938✔
2337
    if (rc || co.getDomainID(key.getNoStripHeader<string_view>()) != id) {
600✔
2338
      // cout << "We hit the end of the zone or database. 'after' is apex" << endl;
2339
      after = zonename;
24✔
2340
      return false;
24✔
2341
    }
24✔
2342
    after = co.getQName(key.getNoStripHeader<string_view>()) + zonename;
576✔
2343
    return true;
576✔
2344
  }
600✔
2345

2346
  if (co.getDomainID(key.getNoStripHeader<string_view>()) != id) {
702✔
2347
    // cout << "Ended up in next zone, 'after' is zonename" <<endl;
2348
    after = zonename;
74✔
2349
    // cout << "Now hunting for previous" << endl;
2350
    int rc;
74✔
2351
    for (;;) {
148✔
2352
      rc = cursor.prev(key, val);
148✔
2353
      if (rc) {
148!
2354
        // cout<<"Reversed into zone, but got not found from lmdb" <<endl;
2355
        return false;
×
2356
      }
×
2357

2358
      if (co.getDomainID(key.getNoStripHeader<string_view>()) != id) {
148!
2359
        // cout<<"Reversed into zone, but found wrong zone id " << co.getDomainID(key.getNoStripHeader<string_view>()) << " != "<<id<<endl;
2360
        // "this can't happen"
2361
        return false;
×
2362
      }
×
2363
      LMDBResourceRecord lrr;
148✔
2364
      deserializeFromBuffer(val.get<StringView>(), lrr);
148✔
2365
      if (co.getQType(key.getNoStripHeader<string_view>()).getCode() && (lrr.auth || co.getQType(key.getNoStripHeader<string_view>()).getCode() == QType::NS))
148!
2366
        break;
74✔
2367
    }
148✔
2368

2369
    before = co.getQName(key.getNoStripHeader<string_view>()) + zonename;
74✔
2370
    // cout<<"Found: "<< before<<endl;
2371
    return true;
74✔
2372
  }
74✔
2373

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

2376
  int skips = 0;
628✔
2377
  for (;;) {
998✔
2378
    LMDBResourceRecord lrr;
998✔
2379
    deserializeFromBuffer(val.get<StringView>(), lrr);
998✔
2380
    if (co.getQType(key.getNoStripHeader<string_view>()).getCode() && (lrr.auth || co.getQType(key.getNoStripHeader<string_view>()).getCode() == QType::NS)) {
998!
2381
      after = co.getQName(key.getNoStripHeader<string_view>()) + zonename;
628✔
2382
      // cout <<"Found auth ("<<lrr.auth<<") or an NS record "<<after<<", type: "<<co.getQType(key.getNoStripHeader<string_view>()).toString()<<", ttl = "<<lrr.ttl<<endl;
2383
      // cout << makeHexDump(val.get<string>()) << endl;
2384
      break;
628✔
2385
    }
628✔
2386
    // 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;
2387
    int rc = cursor.next(key, val);
370✔
2388
    if (!rc)
370!
2389
      ++skips;
370✔
2390
    if (rc || co.getDomainID(key.getNoStripHeader<string_view>()) != id) {
370!
2391
      // cout << "  oops, hit end of database or zone. This means after is apex" <<endl;
2392
      after = zonename;
×
2393
      break;
×
2394
    }
×
2395
  }
370✔
2396
  // go back to where we were
2397
  while (skips--)
998✔
2398
    cursor.prev(key, val);
370✔
2399

2400
  for (;;) {
1,126✔
2401
    int rc = cursor.prev(key, val);
1,126✔
2402
    if (rc || co.getDomainID(key.getNoStripHeader<string_view>()) != id) {
1,126!
2403
      // XX I don't think this case can happen
2404
      // cout << "We hit the beginning of the zone or database.. now what" << endl;
2405
      return false;
×
2406
    }
×
2407
    before = co.getQName(key.getNoStripHeader<string_view>()) + zonename;
1,126✔
2408
    LMDBResourceRecord lrr;
1,126✔
2409
    deserializeFromBuffer(val.get<string_view>(), lrr);
1,126✔
2410
    // cout<<"And before to "<<before<<", auth = "<<rr.auth<<endl;
2411
    if (co.getQType(key.getNoStripHeader<string_view>()).getCode() && (lrr.auth || co.getQType(key.getNoStripHeader<string_view>()) == QType::NS))
1,126✔
2412
      break;
628✔
2413
    // cout << "Oops, that was wrong, go back one more"<<endl;
2414
  }
1,126✔
2415

2416
  return true;
628✔
2417
}
628✔
2418

2419
bool LMDBBackend::updateDNSSECOrderNameAndAuth(uint32_t domain_id, const DNSName& qname, const DNSName& ordername, bool auth, const uint16_t qtype)
2420
{
61,280✔
2421
  //  cout << __PRETTY_FUNCTION__<< ": "<< domain_id <<", '"<<qname <<"', '"<<ordername<<"', "<<auth<< ", " << qtype << endl;
2422
  shared_ptr<RecordsRWTransaction> txn;
61,280✔
2423
  bool needCommit = false;
61,280✔
2424
  if (d_rwtxn && d_transactiondomainid == domain_id) {
61,280!
2425
    txn = d_rwtxn;
61,280✔
2426
    //    cout<<"Reusing open transaction"<<endl;
2427
  }
61,280✔
2428
  else {
×
2429
    //    cout<<"Making a new RW txn for " << __PRETTY_FUNCTION__ <<endl;
2430
    txn = getRecordsRWTransaction(domain_id);
×
2431
    needCommit = true;
×
2432
  }
×
2433

2434
  DomainInfo di;
61,280✔
2435
  if (!d_tdomains->getROTransaction().get(domain_id, di)) {
61,280!
2436
    //    cout<<"Could not find domain_id "<<domain_id <<endl;
2437
    return false;
×
2438
  }
×
2439

2440
  DNSName rel = qname.makeRelative(di.zone);
61,280✔
2441

2442
  compoundOrdername co;
61,280✔
2443
  string matchkey = co(domain_id, rel);
61,280✔
2444

2445
  auto cursor = txn->txn->getCursor(txn->db->dbi);
61,280✔
2446
  MDBOutVal key, val;
61,280✔
2447
  if (cursor.lower_bound(matchkey, key, val)) {
61,280!
2448
    // cout << "Could not find anything"<<endl;
2449
    return false;
×
2450
  }
×
2451

2452
  bool hasOrderName = !ordername.empty();
61,280✔
2453
  bool needNSEC3 = hasOrderName;
61,280✔
2454

2455
  for (; key.getNoStripHeader<StringView>().rfind(matchkey, 0) == 0;) {
122,966✔
2456
    vector<LMDBResourceRecord> lrrs;
61,879✔
2457

2458
    if (co.getQType(key.getNoStripHeader<StringView>()) != QType::NSEC3) {
61,879✔
2459
      deserializeFromBuffer(val.get<StringView>(), lrrs);
61,769✔
2460
      bool changed = false;
61,769✔
2461
      vector<LMDBResourceRecord> newRRs;
61,769✔
2462
      for (auto& lrr : lrrs) {
62,381✔
2463
        lrr.qtype = co.getQType(key.getNoStripHeader<StringView>());
62,381✔
2464
        if (!needNSEC3 && qtype != QType::ANY) {
62,381✔
2465
          needNSEC3 = (lrr.ordername && QType(qtype) != lrr.qtype);
474✔
2466
        }
474✔
2467

2468
        if ((qtype == QType::ANY || QType(qtype) == lrr.qtype) && (lrr.ordername != hasOrderName || lrr.auth != auth)) {
62,381✔
2469
          lrr.auth = auth;
61,279✔
2470
          lrr.ordername = hasOrderName;
61,279✔
2471
          changed = true;
61,279✔
2472
        }
61,279✔
2473
        newRRs.push_back(std::move(lrr));
62,381✔
2474
      }
62,381✔
2475
      if (changed) {
61,769✔
2476
        cursor.put(key, serializeToBuffer(newRRs));
60,913✔
2477
      }
60,913✔
2478
    }
61,769✔
2479

2480
    if (cursor.next(key, val))
61,879✔
2481
      break;
193✔
2482
  }
61,879✔
2483

2484
  bool del = false;
61,280✔
2485
  LMDBResourceRecord lrr;
61,280✔
2486
  matchkey = co(domain_id, rel, QType::NSEC3);
61,280✔
2487
  // cerr<<"here qname="<<qname<<" ordername="<<ordername<<" qtype="<<qtype<<" matchkey="<<makeHexDump(matchkey)<<endl;
2488
  int txngetrc;
61,280✔
2489
  if (!(txngetrc = txn->txn->get(txn->db->dbi, matchkey, val))) {
61,280✔
2490
    deserializeFromBuffer(val.get<string_view>(), lrr);
110✔
2491

2492
    if (needNSEC3) {
110✔
2493
      if (hasOrderName && lrr.content != ordername.toDNSStringLC()) {
93✔
2494
        del = true;
1✔
2495
      }
1✔
2496
    }
93✔
2497
    else {
17✔
2498
      del = true;
17✔
2499
    }
17✔
2500
    if (del) {
110✔
2501
      txn->txn->del(txn->db->dbi, co(domain_id, DNSName(lrr.content.c_str(), lrr.content.size(), 0, false), QType::NSEC3));
18✔
2502
      txn->txn->del(txn->db->dbi, matchkey);
18✔
2503
    }
18✔
2504
  }
110✔
2505
  else {
61,170✔
2506
    del = true;
61,170✔
2507
  }
61,170✔
2508

2509
  if (hasOrderName && del) {
61,280✔
2510
    matchkey = co(domain_id, rel, QType::NSEC3);
60,563✔
2511

2512
    lrr.ttl = 0;
60,563✔
2513
    lrr.auth = 0;
60,563✔
2514
    lrr.content = rel.toDNSStringLC();
60,563✔
2515

2516
    string str = serializeToBuffer(lrr);
60,563✔
2517
    txn->txn->put(txn->db->dbi, co(domain_id, ordername, QType::NSEC3), str);
60,563✔
2518
    lrr.ttl = 1;
60,563✔
2519
    lrr.content = ordername.toDNSStringLC();
60,563✔
2520
    str = serializeToBuffer(lrr);
60,563✔
2521
    txn->txn->put(txn->db->dbi, matchkey, str); // 2
60,563✔
2522
  }
60,563✔
2523

2524
  if (needCommit)
61,280!
2525
    txn->txn->commit();
×
2526
  return false;
61,280✔
2527
}
61,280✔
2528

2529
bool LMDBBackend::updateEmptyNonTerminals(uint32_t domain_id, set<DNSName>& insert, set<DNSName>& erase, bool remove)
2530
{
39✔
2531
  // cout << __PRETTY_FUNCTION__<< ": "<< domain_id << ", insert.size() "<<insert.size()<<", "<<erase.size()<<", " <<remove<<endl;
2532

2533
  bool needCommit = false;
39✔
2534
  shared_ptr<RecordsRWTransaction> txn;
39✔
2535
  if (d_rwtxn && d_transactiondomainid == domain_id) {
39!
2536
    txn = d_rwtxn;
39✔
2537
    //    cout<<"Reusing open transaction"<<endl;
2538
  }
39✔
2539
  else {
×
2540
    //    cout<<"Making a new RW txn for delete domain"<<endl;
2541
    txn = getRecordsRWTransaction(domain_id);
×
2542
    needCommit = true;
×
2543
  }
×
2544

2545
  // if remove is set, all ENTs should be removed & nothing else should be done
2546
  if (remove) {
39!
2547
    deleteDomainRecords(*txn, domain_id, 0);
×
2548
  }
×
2549
  else {
39✔
2550
    DomainInfo di;
39✔
2551
    auto rotxn = d_tdomains->getROTransaction();
39✔
2552
    if (!rotxn.get(domain_id, di)) {
39!
2553
      // cout <<"No such domain with id "<<domain_id<<endl;
2554
      return false;
×
2555
    }
×
2556
    compoundOrdername co;
39✔
2557
    for (const auto& n : insert) {
174✔
2558
      LMDBResourceRecord lrr;
173✔
2559
      lrr.qname = n.makeRelative(di.zone);
173✔
2560
      lrr.ttl = 0;
173✔
2561
      lrr.auth = true;
173✔
2562

2563
      std::string ser = serializeToBuffer(lrr);
173✔
2564

2565
      txn->txn->put(txn->db->dbi, co(domain_id, lrr.qname, 0), ser);
173✔
2566

2567
      // cout <<" +"<<n<<endl;
2568
    }
173✔
2569
    for (auto n : erase) {
39✔
2570
      // cout <<" -"<<n<<endl;
2571
      n.makeUsRelative(di.zone);
1✔
2572
      txn->txn->del(txn->db->dbi, co(domain_id, n, 0));
1✔
2573
    }
1✔
2574
  }
39✔
2575
  if (needCommit)
39!
2576
    txn->txn->commit();
×
2577
  return false;
39✔
2578
}
39✔
2579

2580
/* TSIG */
2581
bool LMDBBackend::getTSIGKey(const DNSName& name, DNSName& algorithm, string& content)
2582
{
71✔
2583
  auto txn = d_ttsig->getROTransaction();
71✔
2584
  LmdbIdVec ids;
71✔
2585
  txn.get_multi<0>(name, ids);
71✔
2586

2587
  TSIGKey key;
71✔
2588
  for (auto id : ids) {
71✔
2589
    if (txn.get(id, key)) {
55!
2590
      if (algorithm.empty() || algorithm == DNSName(key.algorithm)) {
55!
2591
        algorithm = DNSName(key.algorithm);
55✔
2592
        content = key.key;
55✔
2593
      }
55✔
2594
    }
55✔
2595
  }
55✔
2596

2597
  return true;
71✔
2598
}
71✔
2599

2600
// this deletes an old key if it has the same algorithm
2601
bool LMDBBackend::setTSIGKey(const DNSName& name, const DNSName& algorithm, const string& content)
2602
{
22✔
2603
  auto txn = d_ttsig->getRWTransaction();
22✔
2604

2605
  LmdbIdVec ids;
22✔
2606
  txn.get_multi<0>(name, ids);
22✔
2607

2608
  TSIGKey key;
22✔
2609
  for (auto id : ids) {
22✔
2610
    if (txn.get(id, key)) {
2!
2611
      if (key.algorithm == algorithm) {
2✔
2612
        txn.del(id);
1✔
2613
      }
1✔
2614
    }
2✔
2615
  }
2✔
2616

2617
  TSIGKey tk;
22✔
2618
  tk.name = name;
22✔
2619
  tk.algorithm = algorithm;
22✔
2620
  tk.key = content;
22✔
2621

2622
  txn.put(tk, 0, d_random_ids);
22✔
2623
  txn.commit();
22✔
2624

2625
  return true;
22✔
2626
}
22✔
2627
bool LMDBBackend::deleteTSIGKey(const DNSName& name)
2628
{
2✔
2629
  auto txn = d_ttsig->getRWTransaction();
2✔
2630

2631
  LmdbIdVec ids;
2✔
2632
  txn.get_multi<0>(name, ids);
2✔
2633

2634
  TSIGKey key;
2✔
2635

2636
  for (auto id : ids) {
2✔
2637
    if (txn.get(id, key)) {
2!
2638
      txn.del(id);
2✔
2639
    }
2✔
2640
  }
2✔
2641
  txn.commit();
2✔
2642
  return true;
2✔
2643
}
2✔
2644
bool LMDBBackend::getTSIGKeys(std::vector<struct TSIGKey>& keys)
2645
{
1✔
2646
  auto txn = d_ttsig->getROTransaction();
1✔
2647

2648
  keys.clear();
1✔
2649
  for (auto iter = txn.begin(); iter != txn.end(); ++iter) {
11✔
2650
    keys.push_back(*iter);
10✔
2651
  }
10✔
2652
  return true;
1✔
2653
}
1✔
2654

2655
string LMDBBackend::directBackendCmd(const string& query)
2656
{
×
2657
  ostringstream ret, usage;
×
2658

2659
  usage << "info                               show some information about the database" << endl;
×
2660
  usage << "index check domains                check zone<>ID indexes" << endl;
×
2661
  usage << "index refresh domains <ID>         refresh index for zone with this ID" << endl;
×
2662
  usage << "index refresh-all domains          refresh index for all zones with disconnected indexes" << endl;
×
2663
  vector<string> argv;
×
2664
  stringtok(argv, query);
×
2665

2666
  if (argv.empty()) {
×
2667
    return usage.str();
×
2668
  }
×
2669

2670
  string& cmd = argv[0];
×
2671

2672
  if (cmd == "help") {
×
2673
    return usage.str();
×
2674
  }
×
2675

2676
  if (cmd == "info") {
×
2677
    ret << "shards: " << s_shards << endl;
×
2678
    ret << "schemaversion: " << SCHEMAVERSION << endl;
×
2679

2680
    return ret.str();
×
2681
  }
×
2682

2683
  if (cmd == "index") {
×
2684
    if (argv.size() < 2) {
×
2685
      return "need an index subcommand\n";
×
2686
    }
×
2687

2688
    string& subcmd = argv[1];
×
2689

2690
    if (subcmd == "check" || subcmd == "refresh-all") {
×
2691
      bool refresh = false;
×
2692

2693
      if (subcmd == "refresh-all") {
×
2694
        refresh = true;
×
2695
      }
×
2696

2697
      if (argv.size() < 3) {
×
2698
        return "need an index name\n";
×
2699
      }
×
2700

2701
      if (argv[2] != "domains") {
×
2702
        return "can only check the domains index\n";
×
2703
      }
×
2704

2705
      vector<uint32_t> refreshQueue;
×
2706

2707
      {
×
2708
        auto txn = d_tdomains->getROTransaction();
×
2709

2710
        for (auto iter = txn.begin(); iter != txn.end(); ++iter) {
×
2711
          DomainInfo di = *iter;
×
2712

2713
          auto id = iter.getID();
×
2714

2715
          LmdbIdVec ids;
×
2716
          txn.get_multi<0>(di.zone, ids);
×
2717

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

2721
            if (ids.empty()) {
×
2722
              ret << "zone->ID index has no entry for " << di.zone << endl;
×
2723
              if (refresh) {
×
2724
                refreshQueue.push_back(id);
×
2725
              }
×
2726
              else {
×
2727
                ret << "  suggested remedy: index refresh domains " << id << endl;
×
2728
              }
×
2729
            }
×
2730
            else {
×
2731
              // ids.size() > 1
2732
              ret << "zone->ID index has multiple entries for " << di.zone << ": ";
×
2733
              for (auto id_ : ids) {
×
2734
                ret << id_ << " ";
×
2735
              }
×
2736
              ret << endl;
×
2737
            }
×
2738
          }
×
2739
        }
×
2740
      }
×
2741

2742
      if (refresh) {
×
2743
        for (const auto& id : refreshQueue) {
×
2744
          if (genChangeDomain(id, [](DomainInfo& /* di */) {})) {
×
2745
            ret << "refreshed " << id << endl;
×
2746
          }
×
2747
          else {
×
2748
            ret << "failed to refresh " << id << endl;
×
2749
          }
×
2750
        }
×
2751
      }
×
2752
      return ret.str();
×
2753
    }
×
2754
    if (subcmd == "refresh") {
×
2755
      // index refresh domains 12345
2756
      if (argv.size() < 4) {
×
2757
        return "usage: index refresh domains <ID>\n";
×
2758
      }
×
2759

2760
      if (argv[2] != "domains") {
×
2761
        return "can only refresh in the domains index\n";
×
2762
      }
×
2763

2764
      uint32_t id = 0;
×
2765

2766
      try {
×
2767
        id = pdns::checked_stoi<uint32_t>(argv[3]);
×
2768
      }
×
2769
      catch (const std::out_of_range& e) {
×
2770
        return "ID out of range\n";
×
2771
      }
×
2772

2773
      if (genChangeDomain(id, [](DomainInfo& /* di */) {})) {
×
2774
        ret << "refreshed" << endl;
×
2775
      }
×
2776
      else {
×
2777
        ret << "failed" << endl;
×
2778
      }
×
2779
      return ret.str();
×
2780
    }
×
2781
  }
×
2782

2783
  return "unknown lmdbbackend command\n";
×
2784
}
×
2785

2786
class LMDBFactory : public BackendFactory
2787
{
2788
public:
2789
  LMDBFactory() :
2790
    BackendFactory("lmdb") {}
3,469✔
2791
  void declareArguments(const string& suffix = "") override
2792
  {
678✔
2793
    declare(suffix, "filename", "Filename for lmdb", "./pdns.lmdb");
678✔
2794
    declare(suffix, "sync-mode", "Synchronisation mode: nosync, nometasync, sync", "sync");
678✔
2795
    // there just is no room for more on 32 bit
2796
    declare(suffix, "shards", "Records database will be split into this number of shards", (sizeof(void*) == 4) ? "2" : "64");
678✔
2797
    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));
678✔
2798
    declare(suffix, "random-ids", "Numeric IDs inside the database are generated randomly instead of sequentially", "no");
678✔
2799
    declare(suffix, "map-size", "LMDB map size in megabytes", (sizeof(void*) == 4) ? "100" : "16000");
678✔
2800
    declare(suffix, "flag-deleted", "Flag entries on deletion instead of deleting them", "no");
678✔
2801
    declare(suffix, "lightning-stream", "Run in Lightning Stream compatible mode", "no");
678✔
2802
  }
678✔
2803
  DNSBackend* make(const string& suffix = "") override
2804
  {
2,404✔
2805
    return new LMDBBackend(suffix);
2,404✔
2806
  }
2,404✔
2807
};
2808

2809
/* THIRD PART */
2810

2811
class LMDBLoader
2812
{
2813
public:
2814
  LMDBLoader()
2815
  {
3,469✔
2816
    BackendMakers().report(std::make_unique<LMDBFactory>());
3,469✔
2817
    g_log << Logger::Info << "[lmdbbackend] This is the lmdb backend version " VERSION
3,469✔
2818
#ifndef REPRODUCIBLE
3,469✔
2819
          << " (" __DATE__ " " __TIME__ ")"
3,469✔
2820
#endif
3,469✔
2821
          << " reporting" << endl;
3,469✔
2822
  }
3,469✔
2823
};
2824

2825
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