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

PowerDNS / pdns / 15254479060

26 May 2025 12:53PM UTC coverage: 63.691% (+0.1%) from 63.57%
15254479060

push

github

web-flow
Merge pull request #15512 from miodvallat/blinds

Bind-style views

42368 of 101390 branches covered (41.79%)

Branch coverage included in aggregate %.

1164 of 1378 new or added lines in 33 files covered. (84.47%)

39 existing lines in 10 files now uncovered.

130646 of 170256 relevant lines covered (76.74%)

4645349.81 hits per line

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

59.29
/modules/lua2backend/lua2api2.hh
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
 * MERCHANTAPILITY 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
#pragma once
23
#include "boost/algorithm/string/join.hpp"
24
#include "pdns/arguments.hh"
25

26
#include "pdns/dnsbackend.hh"
27
#include "pdns/lua-auth4.hh"
28

29
class Lua2BackendAPIv2 : public DNSBackend, AuthLua4
30
{
31
private:
32
  typedef std::function<void()> init_call_t;
33
  typedef std::function<void()> deinit_call_t;
34

35
  typedef std::vector<std::pair<string, string>> lookup_context_t;
36

37
  typedef std::vector<std::pair<int, std::vector<std::pair<string, boost::variant<bool, int, DNSName, string, QType>>>>> lookup_result_t;
38
  typedef std::function<lookup_result_t(const QType& qtype, const DNSName& qname, domainid_t domain_id, const lookup_context_t& ctx)> lookup_call_t;
39

40
  typedef boost::variant<bool, lookup_result_t> list_result_t;
41
  typedef std::function<list_result_t(const DNSName& qname, domainid_t domain_id)> list_call_t;
42

43
  typedef vector<pair<string, boost::variant<bool, long, string, vector<string>>>> domaininfo_result_t;
44
  typedef boost::variant<bool, domaininfo_result_t> get_domaininfo_result_t;
45
  typedef vector<pair<DNSName, domaininfo_result_t>> get_all_domains_result_t;
46
  typedef std::function<get_domaininfo_result_t(const DNSName& domain)> get_domaininfo_call_t;
47
  typedef std::function<get_all_domains_result_t()> get_all_domains_call_t;
48

49
  typedef vector<pair<int, string>> domain_metadata_result_t;
50
  typedef boost::variant<bool, domain_metadata_result_t> get_domain_metadata_result_t;
51
  typedef boost::variant<bool, vector<pair<string, domain_metadata_result_t>>> get_all_domain_metadata_result_t;
52
  typedef std::function<get_domain_metadata_result_t(const DNSName& domain, const string& kind)> get_domain_metadata_call_t;
53
  typedef std::function<get_all_domain_metadata_result_t(const DNSName& domain)> get_all_domain_metadata_call_t;
54

55
  typedef vector<pair<string, boost::variant<bool, int, string>>> keydata_result_t;
56
  typedef boost::variant<bool, vector<pair<int, keydata_result_t>>> get_domain_keys_result_t;
57
  typedef std::function<get_domain_keys_result_t(const DNSName& domain)> get_domain_keys_call_t;
58

59
  typedef std::vector<std::pair<string, boost::variant<string, DNSName>>> before_and_after_names_result_t;
60
  typedef boost::variant<bool, before_and_after_names_result_t> get_before_and_after_names_absolute_result_t;
61
  typedef std::function<get_before_and_after_names_absolute_result_t(domainid_t id, const DNSName& qname)> get_before_and_after_names_absolute_call_t;
62

63
  typedef std::function<void(domainid_t, long)> set_notified_call_t;
64

65
  typedef std::function<string(const string& cmd)> direct_backend_cmd_call_t;
66

67
public:
68
  Lua2BackendAPIv2(const string& suffix)
69
  {
21✔
70
    d_include_path = ::arg()["lua-global-include-dir"];
21✔
71
    setArgPrefix("lua2" + suffix);
21✔
72
    d_debug_log = mustDo("query-logging");
21✔
73
    prepareContext();
21✔
74
    loadFile(getArg("filename"));
21✔
75
  }
21✔
76

77
  ~Lua2BackendAPIv2() override;
78

79
#define logCall(func, var)                                                                               \
80
  {                                                                                                      \
48✔
81
    if (d_debug_log) {                                                                                   \
48✔
82
      g_log << Logger::Debug << "[" << getPrefix() << "] Calling " << func << "(" << var << ")" << endl; \
×
83
    }                                                                                                    \
×
84
  }
48✔
85
#define logResult(var)                                                \
86
  {                                                                   \
128✔
87
    if (d_debug_log) {                                                \
128✔
88
      g_log << Logger::Debug << "[" << getPrefix() << "] Got result " \
×
89
            << "'" << var << "'" << endl;                             \
×
90
    }                                                                 \
×
91
  }
128✔
92

93
  void postPrepareContext() override
94
  {
21✔
95
    AuthLua4::postPrepareContext();
21✔
96
  }
21✔
97

98
  void postLoad() override
99
  {
21✔
100
    f_lookup = d_lw->readVariable<boost::optional<lookup_call_t>>("dns_lookup").get_value_or(0);
21✔
101
    f_list = d_lw->readVariable<boost::optional<list_call_t>>("dns_list").get_value_or(0);
21✔
102
    f_get_all_domains = d_lw->readVariable<boost::optional<get_all_domains_call_t>>("dns_get_all_domains").get_value_or(0);
21✔
103
    f_get_domaininfo = d_lw->readVariable<boost::optional<get_domaininfo_call_t>>("dns_get_domaininfo").get_value_or(0);
21✔
104
    f_get_domain_metadata = d_lw->readVariable<boost::optional<get_domain_metadata_call_t>>("dns_get_domain_metadata").get_value_or(0);
21✔
105
    f_get_all_domain_metadata = d_lw->readVariable<boost::optional<get_all_domain_metadata_call_t>>("dns_get_all_domain_metadata").get_value_or(0);
21✔
106
    f_get_domain_keys = d_lw->readVariable<boost::optional<get_domain_keys_call_t>>("dns_get_domain_keys").get_value_or(0);
21✔
107
    f_get_before_and_after_names_absolute = d_lw->readVariable<boost::optional<get_before_and_after_names_absolute_call_t>>("dns_get_before_and_after_names_absolute").get_value_or(0);
21✔
108
    f_set_notified = d_lw->readVariable<boost::optional<set_notified_call_t>>("dns_set_notified").get_value_or(0);
21✔
109

110
    auto init = d_lw->readVariable<boost::optional<init_call_t>>("dns_init").get_value_or(0);
21✔
111
    if (init)
21!
112
      init();
×
113

114
    f_deinit = d_lw->readVariable<boost::optional<deinit_call_t>>("dns_deinit").get_value_or(0);
21✔
115

116
    if (f_lookup == nullptr)
21!
117
      throw PDNSException("dns_lookup missing");
×
118

119
    /* see if dnssec support is wanted */
120
    d_dnssec = d_lw->readVariable<boost::optional<bool>>("dns_dnssec").get_value_or(false);
21✔
121
    if (d_dnssec) {
21✔
122
      if (f_get_domain_metadata == nullptr)
15!
123
        throw PDNSException("dns_dnssec is true but dns_get_domain_metadata is missing");
×
124
      if (f_get_before_and_after_names_absolute == nullptr)
15!
125
        throw PDNSException("dns_dnssec is true but dns_get_before_and_after_names_absolute is missing");
×
126
      /* domain keys is not strictly speaking necessary for dnssec backend */
127
      if (f_get_domain_keys == nullptr)
15!
128
        g_log << Logger::Warning << "dns_get_domain_keys missing - cannot do live signing" << endl;
×
129
    }
15✔
130
  }
21✔
131

132
  unsigned int getCapabilities() override
133
  {
6✔
134
    unsigned int caps = CAP_DIRECT | CAP_LIST;
6✔
135
    if (d_dnssec) {
6!
136
      caps |= CAP_DNSSEC;
6✔
137
    }
6✔
138
    return caps;
6✔
139
  }
6✔
140

141
  void parseLookup(const lookup_result_t& result)
142
  {
29✔
143
    for (const auto& row : result) {
103✔
144
      DNSResourceRecord rec;
103✔
145
      for (const auto& item : row.second) {
437✔
146
        if (item.first == "type") {
437✔
147
          if (item.second.which() == 1)
103!
148
            rec.qtype = QType(boost::get<int>(item.second));
×
149
          else if (item.second.which() == 3)
103!
150
            rec.qtype = boost::get<string>(item.second);
×
151
          else if (item.second.which() == 4)
103!
152
            rec.qtype = boost::get<QType>(item.second);
103✔
153
          else
×
154
            throw PDNSException("Unsupported value for type");
×
155
        }
103✔
156
        else if (item.first == "name") {
334✔
157
          if (item.second.which() == 3)
103!
158
            rec.qname = DNSName(boost::get<string>(item.second));
×
159
          else if (item.second.which() == 2)
103!
160
            rec.qname = boost::get<DNSName>(item.second);
103✔
161
          else
×
162
            throw PDNSException("Unsupported value for name");
×
163
        }
103✔
164
        else if (item.first == "domain_id")
231✔
165
          rec.domain_id = boost::get<int>(item.second);
25✔
166
        else if (item.first == "auth")
206!
167
          rec.auth = boost::get<bool>(item.second);
×
168
        else if (item.first == "last_modified")
206!
169
          rec.last_modified = static_cast<time_t>(boost::get<int>(item.second));
×
170
        else if (item.first == "ttl")
206✔
171
          rec.ttl = boost::get<int>(item.second);
103✔
172
        else if (item.first == "content")
103!
173
          rec.setContent(boost::get<string>(item.second));
103✔
174
        else if (item.first == "scopeMask")
×
175
          rec.scopeMask = boost::get<int>(item.second);
×
176
        else
×
177
          g_log << Logger::Warning << "Unsupported key '" << item.first << "' in lookup or list result" << endl;
×
178
      }
437✔
179
      logResult(rec.qname << " IN " << rec.qtype.toString() << " " << rec.ttl << " " << rec.getZoneRepresentation());
103!
180
      d_result.push_back(rec);
103✔
181
    }
103✔
182
    if (d_result.empty() && d_debug_log)
29!
183
      g_log << Logger::Debug << "[" << getPrefix() << "] Got empty result" << endl;
×
184
  }
29✔
185

186
  bool list(const ZoneName& target, domainid_t domain_id, bool /* include_disabled */ = false) override
187
  {
6✔
188
    if (f_list == nullptr) {
6!
189
      g_log << Logger::Error << "[" << getPrefix() << "] dns_list missing - cannot do AXFR" << endl;
×
190
      return false;
×
191
    }
×
192

193
    if (d_result.size() != 0)
6!
194
      throw PDNSException("list attempted while another was running");
×
195

196
    logCall("list", "target=" << target << ",domain_id=" << domain_id);
6!
197
    list_result_t result = f_list(target.operator const DNSName&(), domain_id);
6✔
198

199
    if (result.which() == 0)
6!
200
      return false;
×
201

202
    parseLookup(boost::get<lookup_result_t>(result));
6✔
203

204
    return true;
6✔
205
  }
6✔
206

207
  void lookup(const QType& qtype, const DNSName& qname, domainid_t domain_id, DNSPacket* p = nullptr) override
208
  {
23✔
209
    if (d_result.size() != 0)
23!
210
      throw PDNSException("lookup attempted while another was running");
×
211

212
    lookup_context_t ctx;
23✔
213
    if (p != NULL) {
23✔
214
      ctx.emplace_back(lookup_context_t::value_type{"source_address", p->getInnerRemote().toString()});
5✔
215
      ctx.emplace_back(lookup_context_t::value_type{"real_source_address", p->getRealRemote().toString()});
5✔
216
    }
5✔
217

218
    logCall("lookup", "qtype=" << qtype.toString() << ",qname=" << qname << ",domain_id=" << domain_id);
23!
219
    lookup_result_t result = f_lookup(qtype, qname, domain_id, ctx);
23✔
220
    parseLookup(result);
23✔
221
  }
23✔
222

223
  bool get(DNSResourceRecord& rr) override
224
  {
132✔
225
    if (d_result.size() == 0)
132✔
226
      return false;
29✔
227
    rr = std::move(d_result.front());
103✔
228
    d_result.pop_front();
103✔
229
    return true;
103✔
230
  }
132✔
231

232
  string directBackendCmd(const string& querystr) override
233
  {
×
234
    string::size_type pos = querystr.find_first_of(" \t");
×
235
    string cmd = querystr;
×
236
    string par = "";
×
237
    if (pos != string::npos) {
×
238
      cmd = querystr.substr(0, pos);
×
239
      par = querystr.substr(pos + 1);
×
240
    }
×
241
    direct_backend_cmd_call_t f = d_lw->readVariable<boost::optional<direct_backend_cmd_call_t>>(cmd).get_value_or(0);
×
242
    if (f == nullptr) {
×
243
      return cmd + "not found";
×
244
    }
×
245
    logCall(cmd, "parameter=" << par);
×
246
    return f(par);
×
247
  }
×
248

249
  void setNotified(domainid_t id, uint32_t serial) override
250
  {
×
251
    if (f_set_notified == NULL)
×
252
      return;
×
253
    logCall("dns_set_notified", "id=" << id << ",serial=" << serial);
×
254
    f_set_notified(id, serial);
×
255
  }
×
256

257
  void parseDomainInfo(const domaininfo_result_t& row, DomainInfo& di)
258
  {
5✔
259
    for (const auto& item : row) {
10✔
260
      if (item.first == "account")
10!
261
        di.account = boost::get<string>(item.second);
×
262
      else if (item.first == "last_check")
10!
263
        di.last_check = static_cast<time_t>(boost::get<long>(item.second));
×
264
      else if (item.first == "masters")
10!
265
        for (const auto& primary : boost::get<vector<string>>(item.second))
×
266
          di.primaries.push_back(ComboAddress(primary, 53));
×
267
      else if (item.first == "id")
10✔
268
        di.id = static_cast<domainid_t>(boost::get<long>(item.second));
5✔
269
      else if (item.first == "notified_serial")
5!
270
        di.notified_serial = static_cast<unsigned int>(boost::get<long>(item.second));
×
271
      else if (item.first == "serial")
5!
272
        di.serial = static_cast<unsigned int>(boost::get<long>(item.second));
5✔
273
      else if (item.first == "kind")
×
274
        di.kind = DomainInfo::stringToKind(boost::get<string>(item.second));
×
275
      else
×
276
        g_log << Logger::Warning << "Unsupported key '" << item.first << "' in domaininfo result" << endl;
×
277
    }
10✔
278
    di.backend = this;
5✔
279
    logResult("zone=" << di.zone << ",serial=" << di.serial << ",kind=" << di.getKindString());
5!
280
  }
5✔
281

282
  bool getDomainInfo(const ZoneName& domain, DomainInfo& di, bool /* getSerial */ = true) override
283
  {
6✔
284
    if (f_get_domaininfo == nullptr) {
6✔
285
      // use getAuth instead... but getAuth wraps getSOA which will call
286
      // getDomainInfo if this is a domain variant, so protect against this
287
      // would-be infinite recursion.
288
      if (domain.hasVariant()) {
3!
NEW
289
        g_log << Logger::Info << "Unable to return domain information for '" << domain.toLogString() << "' due to unimplemented dns_get_domaininfo" << endl;
×
NEW
290
        return false;
×
NEW
291
      }
×
292
      SOAData sd;
3✔
293
      if (!getAuth(domain, &sd))
3!
294
        return false;
×
295

296
      di.zone = domain;
3✔
297
      di.backend = this;
3✔
298
      di.serial = sd.serial;
3✔
299
      return true;
3✔
300
    }
3✔
301

302
    logCall("get_domaininfo", "domain=" << domain);
3!
303
    get_domaininfo_result_t result = f_get_domaininfo(domain.operator const DNSName&());
3✔
304

305
    if (result.which() == 0)
3!
306
      return false;
×
307

308
    di.zone = domain;
3✔
309
    parseDomainInfo(boost::get<domaininfo_result_t>(result), di);
3✔
310

311
    return true;
3✔
312
  }
3✔
313

314
  void getAllDomains(vector<DomainInfo>* domains, bool /* getSerial */, bool /* include_disabled */) override
315
  {
1✔
316
    if (f_get_all_domains == nullptr)
1!
317
      return;
×
318

319
    logCall("get_all_domains", "");
1!
320
    for (const auto& row : f_get_all_domains()) {
2✔
321
      DomainInfo di;
2✔
322
      di.zone = ZoneName(row.first);
2✔
323
      logResult(di.zone);
2!
324
      parseDomainInfo(row.second, di);
2✔
325
      domains->push_back(di);
2✔
326
    }
2✔
327
  }
1✔
328

329
  bool getAllDomainMetadata(const ZoneName& name, std::map<std::string, std::vector<std::string>>& meta) override
330
  {
6✔
331
    if (f_get_all_domain_metadata == nullptr)
6!
332
      return false;
6✔
333

334
    logCall("get_all_domain_metadata", "name=" << name);
×
335
    get_all_domain_metadata_result_t result = f_get_all_domain_metadata(name.operator const DNSName&());
×
336
    if (result.which() == 0)
×
337
      return false;
×
338

339
    for (const auto& row : boost::get<vector<pair<string, domain_metadata_result_t>>>(result)) {
×
340
      meta[row.first].clear();
×
341
      for (const auto& item : row.second)
×
342
        meta[row.first].push_back(item.second);
×
343
      logResult("kind=" << row.first << ",value=" << boost::algorithm::join(meta[row.first], ", "));
×
344
    }
×
345

346
    return true;
×
347
  }
×
348

349
  bool getDomainMetadata(const ZoneName& name, const std::string& kind, std::vector<std::string>& meta) override
350
  {
×
351
    if (f_get_domain_metadata == nullptr)
×
352
      return false;
×
353

354
    logCall("get_domain_metadata", "name=" << name << ",kind=" << kind);
×
355
    get_domain_metadata_result_t result = f_get_domain_metadata(name.operator const DNSName&(), kind);
×
356
    if (result.which() == 0)
×
357
      return false;
×
358

359
    meta.clear();
×
360
    for (const auto& item : boost::get<domain_metadata_result_t>(result))
×
361
      meta.push_back(item.second);
×
362

363
    logResult("value=" << boost::algorithm::join(meta, ", "));
×
364
    return true;
×
365
  }
×
366

367
  bool getDomainKeys(const ZoneName& name, std::vector<DNSBackend::KeyData>& keys) override
368
  {
6✔
369
    if (f_get_domain_keys == nullptr)
6✔
370
      return false;
3✔
371

372
    logCall("get_domain_keys", "name=" << name);
3!
373
    get_domain_keys_result_t result = f_get_domain_keys(name.operator const DNSName&());
3✔
374

375
    if (result.which() == 0)
3!
376
      return false;
×
377

378
    for (const auto& row : boost::get<vector<pair<int, keydata_result_t>>>(result)) {
6✔
379
      DNSBackend::KeyData key;
6✔
380
      key.published = true;
6✔
381
      for (const auto& item : row.second) {
24✔
382
        if (item.first == "content")
24✔
383
          key.content = boost::get<string>(item.second);
6✔
384
        else if (item.first == "id")
18✔
385
          key.id = static_cast<unsigned int>(boost::get<int>(item.second));
6✔
386
        else if (item.first == "flags")
12✔
387
          key.flags = static_cast<unsigned int>(boost::get<int>(item.second));
6✔
388
        else if (item.first == "active")
6!
389
          key.active = boost::get<bool>(item.second);
6✔
390
        else if (item.first == "published")
×
391
          key.published = boost::get<bool>(item.second);
×
392
        else
×
393
          g_log << Logger::Warning << "[" << getPrefix() << "] Unsupported key '" << item.first << "' in keydata result" << endl;
×
394
      }
24✔
395
      logResult("id=" << key.id << ",flags=" << key.flags << ",active=" << (key.active ? "true" : "false") << ",published=" << (key.published ? "true" : "false"));
6!
396
      keys.emplace_back(std::move(key));
6✔
397
    }
6✔
398

399
    return true;
3✔
400
  }
3✔
401

402
  bool getBeforeAndAfterNamesAbsolute(domainid_t id, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after) override
403
  {
12✔
404
    if (f_get_before_and_after_names_absolute == nullptr)
12!
405
      return false;
×
406

407
    logCall("get_before_and_after_names_absolute", "id=<<" << id << ",qname=" << qname);
12!
408
    get_before_and_after_names_absolute_result_t result = f_get_before_and_after_names_absolute(id, qname);
12✔
409

410
    if (result.which() == 0)
12!
411
      return false;
×
412

413
    before_and_after_names_result_t row = boost::get<before_and_after_names_result_t>(result);
12✔
414
    if (row.size() != 3) {
12!
415
      g_log << Logger::Error << "Invalid result from dns_get_before_and_after_names_absolute, expected array with 3 items, got " << row.size() << "item(s)" << endl;
×
416
      return false;
×
417
    }
×
418
    for (const auto& item : row) {
36✔
419
      DNSName value;
36✔
420
      if (item.second.which() == 0)
36!
421
        value = DNSName(boost::get<string>(item.second));
×
422
      else
36✔
423
        value = DNSName(boost::get<DNSName>(item.second));
36✔
424
      if (item.first == "unhashed")
36✔
425
        unhashed = value;
12✔
426
      else if (item.first == "before")
24✔
427
        before = value;
12✔
428
      else if (item.first == "after")
12!
429
        after = value;
12✔
430
      else {
×
431
        g_log << Logger::Error << "Invalid result from dns_get_before_and_after_names_absolute, unexpected key " << item.first << endl;
×
432
        return false;
×
433
      }
×
434
    }
36✔
435

436
    logResult("unhashed=" << unhashed << ",before=" << before << ",after=" << after);
12!
437
    return true;
12✔
438
  }
12✔
439

440
private:
441
  std::list<DNSResourceRecord> d_result;
442
  bool d_debug_log{false};
443
  bool d_dnssec{false};
444

445
  lookup_call_t f_lookup;
446
  list_call_t f_list;
447

448
  get_domaininfo_call_t f_get_domaininfo;
449
  get_all_domains_call_t f_get_all_domains;
450

451
  get_domain_metadata_call_t f_get_domain_metadata;
452
  get_all_domain_metadata_call_t f_get_all_domain_metadata;
453

454
  get_domain_keys_call_t f_get_domain_keys;
455

456
  get_before_and_after_names_absolute_call_t f_get_before_and_after_names_absolute;
457

458
  set_notified_call_t f_set_notified;
459

460
  deinit_call_t f_deinit;
461
};
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