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

PowerDNS / pdns / 19741624072

27 Nov 2025 03:45PM UTC coverage: 73.086% (+0.02%) from 73.065%
19741624072

Pull #16570

github

web-flow
Merge 08a2cdb1d into f94a3f63f
Pull Request #16570: rec: rewrite all unwrap calls in web.rs

38523 of 63408 branches covered (60.75%)

Branch coverage included in aggregate %.

128044 of 164496 relevant lines covered (77.84%)

6531485.83 hits per line

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

76.7
/pdns/dnsdistdist/dnsdist-kvs.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 "dnsdist-kvs.hh"
24
#include "dolog.hh"
25

26
#include <sys/stat.h>
27

28
std::vector<std::string> KeyValueLookupKeySourceIP::getKeys(const ComboAddress& addr)
29
{
68✔
30
  std::vector<std::string> result;
68✔
31
  ComboAddress truncated(addr);
68✔
32

33
  std::string key;
68✔
34
  if (truncated.isIPv4()) {
68✔
35
    truncated.truncate(d_v4Mask);
64✔
36
    key.reserve(sizeof(truncated.sin4.sin_addr.s_addr) + (d_includePort ? sizeof(truncated.sin4.sin_port) : 0));
64✔
37
    key.append(reinterpret_cast<const char*>(&truncated.sin4.sin_addr.s_addr), sizeof(truncated.sin4.sin_addr.s_addr));
64✔
38
  }
64✔
39
  else if (truncated.isIPv6()) {
4!
40
    truncated.truncate(d_v6Mask);
4✔
41
    key.reserve(sizeof(truncated.sin6.sin6_addr.s6_addr) + (d_includePort ? sizeof(truncated.sin4.sin_port) : 0));
4!
42
    key.append(reinterpret_cast<const char*>(&truncated.sin6.sin6_addr.s6_addr), sizeof(truncated.sin6.sin6_addr.s6_addr));
4✔
43
  }
4✔
44

45
  if (d_includePort) {
68✔
46
    key.append(reinterpret_cast<const char*>(&truncated.sin4.sin_port), sizeof(truncated.sin4.sin_port));
8✔
47
  }
8✔
48

49
  result.push_back(std::move(key));
68✔
50

51
  return result;
68✔
52
}
68✔
53

54
std::vector<std::string> KeyValueLookupKeySuffix::getKeys(const DNSName& qname)
55
{
68✔
56
  if (qname.empty() || qname.isRoot()) {
68!
57
    return {};
×
58
  }
×
59

60
  auto lowerQName = qname.makeLowerCase();
68✔
61
  size_t labelsCount = lowerQName.countLabels();
68✔
62
  if (d_minLabels != 0) {
68✔
63
    if (labelsCount < d_minLabels) {
8!
64
      return {};
×
65
    }
×
66
    labelsCount -= (d_minLabels - 1);
8✔
67
  }
8✔
68

69
  std::vector<std::string> result;
68✔
70
  result.reserve(labelsCount);
68✔
71

72
  while(!lowerQName.isRoot()) {
212!
73
    result.emplace_back(d_wireFormat ? lowerQName.toDNSString() : lowerQName.toStringRootDot());
212✔
74
    labelsCount--;
212✔
75
    if (!lowerQName.chopOff() || labelsCount == 0) {
212!
76
      break;
68✔
77
    }
68✔
78
  }
212✔
79

80
  return result;
68✔
81
}
68✔
82

83
#ifdef HAVE_LMDB
84

85
bool LMDBKVStore::getValue(const std::string& key, std::string& value)
86
{
164✔
87
  try {
164✔
88
    auto transaction = d_env->getROTransaction();
164✔
89
    MDBOutVal result;
164✔
90
    int rc = transaction->get(d_dbi, MDBInVal(key), result);
164✔
91
    if (rc == 0) {
164✔
92
      value = result.get<std::string>();
70✔
93
      return true;
70✔
94
    }
70✔
95
    else if (rc == MDB_NOTFOUND) {
94!
96
      return false;
94✔
97
    }
94✔
98
  }
164✔
99
  catch (const std::exception& e) {
164✔
100
    vinfolog("Error while looking up key '%s' from LMDB file '%s', database '%s': %s", key, d_fname, d_dbName, e.what());
×
101
  }
×
102
  return false;
×
103
}
164✔
104

105
bool LMDBKVStore::keyExists(const std::string& key)
106
{
10✔
107
  try {
10✔
108
    auto transaction = d_env->getROTransaction();
10✔
109
    MDBOutVal result;
10✔
110
    int rc = transaction->get(d_dbi, MDBInVal(key), result);
10✔
111
    if (rc == 0) {
10✔
112
      return true;
8✔
113
    }
8✔
114
    else if (rc == MDB_NOTFOUND) {
2!
115
      return false;
2✔
116
    }
2✔
117
  }
10✔
118
  catch (const std::exception& e) {
10✔
119
    vinfolog("Error while looking up key '%s' from LMDB file '%s', database '%s': %s", key, d_fname, d_dbName, e.what());
×
120
  }
×
121
  return false;
×
122
}
10✔
123

124
bool LMDBKVStore::getRangeValue(const std::string& key, std::string& value)
125
{
24✔
126
  try {
24✔
127
    auto transaction = d_env->getROTransaction();
24✔
128
    auto cursor = transaction->getROCursor(d_dbi);
24✔
129
    MDBOutVal actualKey;
24✔
130
    MDBOutVal result;
24✔
131
    // for range-based lookups, we expect the data in LMDB
132
    // to be stored with the last value of the range as key
133
    // and the first value of the range as data, sometimes
134
    // followed by any other content we don't care about
135
    // range-based lookups are mostly useful for network ranges,
136
    // for which we expect addresses to be stored in network byte
137
    // order
138

139
    // retrieve the first key greater or equal to our key
140
    int rc = cursor.lower_bound(MDBInVal(key), actualKey, result);
24✔
141

142
    if (rc == 0) {
24✔
143
      auto last = actualKey.get<std::string>();
20✔
144
      if (last.size() != key.size() || key > last) {
20!
145
        return false;
2✔
146
      }
2✔
147

148
      value = result.get<std::string>();
18✔
149
      if (value.size() < key.size()) {
18!
150
        return false;
×
151
      }
×
152

153
      // take the first part of the data, which should be
154
      // the first address of the range
155
      auto first = value.substr(0, key.size());
18✔
156
      if (first.size() != key.size() || key < first) {
18!
157
        return false;
4✔
158
      }
4✔
159

160
      return true;
14✔
161
    }
18✔
162
    else if (rc == MDB_NOTFOUND) {
4!
163
      return false;
4✔
164
    }
4✔
165
  }
24✔
166
  catch (const std::exception& e) {
24✔
167
    vinfolog("Error while looking up a range from LMDB file '%s', database '%s': %s", d_fname, d_dbName, e.what());
×
168
  }
×
169
  return false;
×
170
}
24✔
171

172
#endif /* HAVE_LMDB */
173

174
#ifdef HAVE_CDB
175

176
CDBKVStore::CDBKVStore(const std::string& fname, time_t refreshDelay): d_fname(fname), d_refreshDelay(refreshDelay)
7✔
177
{
7✔
178
  d_refreshing.clear();
7✔
179

180
  time_t now = time(nullptr);
7✔
181
  if (d_refreshDelay > 0) {
7✔
182
    d_nextCheck = now + d_refreshDelay;
5✔
183
  }
5✔
184

185
  refreshDBIfNeeded(now);
7✔
186
}
7✔
187

188
CDBKVStore::~CDBKVStore() {
4✔
189
}
4✔
190

191
bool CDBKVStore::reload(const struct stat& st)
192
{
13✔
193
  auto newCDB = std::make_unique<CDB>(d_fname);
13✔
194
  {
13✔
195
    *(d_cdb.write_lock()) = std::move(newCDB);
13✔
196
  }
13✔
197
  d_mtime = st.st_mtime;
13✔
198
  return true;
13✔
199
}
13✔
200

201
bool CDBKVStore::reload()
202
{
5✔
203
  struct stat st;
5✔
204
  if (stat(d_fname.c_str(), &st) == 0) {
5!
205
    return reload(st);
5✔
206
  }
5✔
207
  else {
×
208
    warnlog("Error while retrieving the last modification time of CDB database '%s': %s", d_fname, stringerror());
×
209
    return false;
×
210
  }
×
211
}
5✔
212

213
void CDBKVStore::refreshDBIfNeeded(time_t now)
214
{
78✔
215
  if (d_refreshing.test_and_set()) {
78!
216
    /* someone else is already refreshing */
217
    return;
×
218
  }
×
219

220
  try {
78✔
221
    struct stat st;
78✔
222
    if (stat(d_fname.c_str(), &st) == 0) {
78!
223
      if (st.st_mtime > d_mtime) {
78✔
224
        reload(st);
8✔
225
      }
8✔
226
    }
78✔
227
    else {
×
228
      warnlog("Error while retrieving the last modification time of CDB database '%s': %s", d_fname, stringerror());
×
229
    }
×
230
    d_nextCheck = now + d_refreshDelay;
78✔
231
    d_refreshing.clear();
78✔
232
  }
78✔
233
  catch (...) {
78✔
234
    d_refreshing.clear();
×
235
    throw;
×
236
  }
×
237
}
78✔
238

239
bool CDBKVStore::getValue(const std::string& key, std::string& value)
240
{
142✔
241
  time_t now = time(nullptr);
142✔
242

243
  try {
142✔
244
    if (d_nextCheck != 0 && now >= d_nextCheck) {
142!
245
      refreshDBIfNeeded(now);
65✔
246
    }
65✔
247

248
    {
142✔
249
      auto cdb = d_cdb.read_lock();
142✔
250
      if (*cdb && (*cdb)->findOne(key, value)) {
142!
251
        return true;
46✔
252
      }
46✔
253
    }
142✔
254
  }
142✔
255
  catch (const std::exception& e) {
142✔
256
    vinfolog("Error while looking up key '%s' from CDB file '%s': %s", key, d_fname, e.what());
×
257
  }
×
258
  return false;
96✔
259
}
142✔
260

261
bool CDBKVStore::keyExists(const std::string& key)
262
{
6✔
263
  time_t now = time(nullptr);
6✔
264

265
  try {
6✔
266
    if (d_nextCheck != 0 && now >= d_nextCheck) {
6!
267
      refreshDBIfNeeded(now);
6✔
268
    }
6✔
269

270
    {
6✔
271
      auto cdb = d_cdb.read_lock();
6✔
272
      if (!*cdb) {
6!
273
        return false;
×
274
      }
×
275

276
      return (*cdb)->keyExists(key);
6✔
277
    }
6✔
278
  }
6✔
279
  catch (const std::exception& e) {
6✔
280
    vinfolog("Error while looking up key '%s' from CDB file '%s': %s", key, d_fname, e.what());
×
281
  }
×
282
  return false;
×
283
}
6✔
284

285
#endif /* HAVE_CDB */
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

© 2025 Coveralls, Inc