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

PowerDNS / pdns / 10789862120

10 Sep 2024 09:38AM UTC coverage: 55.763% (-0.03%) from 55.792%
10789862120

push

github

web-flow
Merge pull request #14641 from rgacogne/ddist19-backport-14573

dnsdist-1.9.x: Backport 14573 - Stop reporting timeouts in `topSlow()`, add `topTimeouts()`

13812 of 45324 branches covered (30.47%)

Branch coverage included in aggregate %.

3 of 9 new or added lines in 1 file covered. (33.33%)

13 existing lines in 4 files now uncovered.

47994 of 65513 relevant lines covered (73.26%)

3755251.71 hits per line

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

42.14
/pdns/dnsdist-lua-inspection.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
#include <fcntl.h>
23

24
#include "dnsdist.hh"
25
#include "dnsdist-lua.hh"
26
#include "dnsdist-dynblocks.hh"
27
#include "dnsdist-nghttp2.hh"
28
#include "dnsdist-rings.hh"
29
#include "dnsdist-tcp.hh"
30

31
#include "statnode.hh"
32

33
#ifndef DISABLE_TOP_N_BINDINGS
34
static LuaArray<std::vector<boost::variant<string,double>>> getGenResponses(uint64_t top, boost::optional<int> labels, std::function<bool(const Rings::Response&)> pred)
35
{
×
36
  setLuaNoSideEffect();
×
37
  map<DNSName, unsigned int> counts;
×
38
  unsigned int total=0;
×
39
  {
×
40
    for (const auto& shard : g_rings.d_shards) {
×
41
      auto rl = shard->respRing.lock();
×
42
      if (!labels) {
×
43
        for(const auto& a : *rl) {
×
44
          if(!pred(a))
×
45
            continue;
×
46
          counts[a.name]++;
×
47
          total++;
×
48
        }
×
49
      }
×
50
      else {
×
51
        unsigned int lab = *labels;
×
52
        for(const auto& a : *rl) {
×
53
          if(!pred(a))
×
54
            continue;
×
55

56
          DNSName temp(a.name);
×
57
          temp.trimToLabels(lab);
×
58
          counts[temp]++;
×
59
          total++;
×
60
        }
×
61
      }
×
62
    }
×
63
  }
×
64
  //      cout<<"Looked at "<<total<<" responses, "<<counts.size()<<" different ones"<<endl;
65
  vector<pair<unsigned int, DNSName>> rcounts;
×
66
  rcounts.reserve(counts.size());
×
67
  for (const auto& c : counts)
×
68
    rcounts.emplace_back(c.second, c.first.makeLowerCase());
×
69

70
  sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
×
71
                                          const decltype(rcounts)::value_type& b) {
×
72
         return b.first < a.first;
×
73
       });
×
74

75
  LuaArray<vector<boost::variant<string,double>>> ret;
×
76
  ret.reserve(std::min(rcounts.size(), static_cast<size_t>(top + 1U)));
×
77
  int count = 1;
×
78
  unsigned int rest = 0;
×
79
  for (const auto& rc : rcounts) {
×
80
    if (count == static_cast<int>(top + 1)) {
×
81
      rest+=rc.first;
×
82
    }
×
83
    else {
×
84
      ret.push_back({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
×
85
    }
×
86
  }
×
87

88
  if (total > 0) {
×
89
    ret.push_back({count, {"Rest", rest, 100.0*rest/total}});
×
90
  }
×
91
  else {
×
92
    ret.push_back({count, {"Rest", rest, 100.0 }});
×
93
  }
×
94

95
  return ret;
×
96
}
×
97
#endif /* DISABLE_TOP_N_BINDINGS */
98

99
#ifndef DISABLE_DYNBLOCKS
100
#ifndef DISABLE_DEPRECATED_DYNBLOCK
101

102
typedef std::unordered_map<ComboAddress, unsigned int, ComboAddress::addressOnlyHash, ComboAddress::addressOnlyEqual> counts_t;
103

104
static counts_t filterScore(const counts_t& counts,
105
                        double delta, unsigned int rate)
106
{
133✔
107
  counts_t ret;
133✔
108

109
  double lim = delta*rate;
133✔
110
  for(const auto& c : counts) {
133✔
111
    if (c.second > lim) {
54✔
112
      ret[c.first] = c.second;
31✔
113
    }
31✔
114
  }
54✔
115

116
  return ret;
133✔
117
}
133✔
118

119
using statvisitor_t = std::function<void(const StatNode&, const StatNode::Stat&, const StatNode::Stat&)>;
120

121
static void statNodeRespRing(statvisitor_t visitor, uint64_t seconds)
122
{
5✔
123
  struct timespec cutoff, now;
5✔
124
  gettime(&now);
5✔
125
  cutoff = now;
5✔
126
  cutoff.tv_sec -= seconds;
5✔
127

128
  StatNode root;
5✔
129
  for (const auto& shard : g_rings.d_shards) {
50✔
130
    auto rl = shard->respRing.lock();
50✔
131

132
    for(const auto& c : *rl) {
50✔
133
      if (now < c.when){
5!
134
        continue;
×
135
      }
×
136

137
      if (seconds && c.when < cutoff) {
5✔
138
        continue;
1✔
139
      }
1✔
140

141
      const bool hit = c.isACacheHit();
4✔
142
      root.submit(c.name, ((c.dh.rcode == 0 && c.usec == std::numeric_limits<unsigned int>::max()) ? -1 : c.dh.rcode), c.size, hit, boost::none);
4!
143
    }
4✔
144
  }
50✔
145

146
  StatNode::Stat node;
5✔
147
  root.visit([visitor = std::move(visitor)](const StatNode* node_, const StatNode::Stat& self, const StatNode::Stat& children) {
25✔
148
      visitor(*node_, self, children);},  node);
25✔
149
}
5✔
150

151
static LuaArray<LuaAssociativeTable<std::string>> getRespRing(boost::optional<int> rcode)
152
{
×
153
  typedef LuaAssociativeTable<std::string> entry_t;
×
154
  LuaArray<entry_t> ret;
×
155

156
  for (const auto& shard : g_rings.d_shards) {
×
157
    auto rl = shard->respRing.lock();
×
158

159
    int count = 1;
×
160
    for (const auto& c : *rl) {
×
161
      if (rcode && (rcode.get() != c.dh.rcode)) {
×
162
        continue;
×
163
      }
×
164
      entry_t e;
×
165
      e["qname"] = c.name.toString();
×
166
      e["rcode"] = std::to_string(c.dh.rcode);
×
167
      ret.emplace_back(count, std::move(e));
×
168
      count++;
×
169
    }
×
170
  }
×
171

172
  return ret;
×
173
}
×
174

175
static counts_t exceedRespGen(unsigned int rate, int seconds, std::function<void(counts_t&, const Rings::Response&)> T)
176
{
60✔
177
  counts_t counts;
60✔
178
  struct timespec cutoff, mintime, now;
60✔
179
  gettime(&now);
60✔
180
  cutoff = mintime = now;
60✔
181
  cutoff.tv_sec -= seconds;
60✔
182

183
  counts.reserve(g_rings.getNumberOfResponseEntries());
60✔
184

185
  for (const auto& shard : g_rings.d_shards) {
600✔
186
    auto rl = shard->respRing.lock();
600✔
187
    for(const auto& c : *rl) {
2,071✔
188

189
      if(seconds && c.when < cutoff)
2,071!
190
        continue;
1,777✔
191
      if(now < c.when)
294!
192
        continue;
×
193

194
      T(counts, c);
294✔
195
      if(c.when < mintime)
294✔
196
        mintime = c.when;
34✔
197
    }
294✔
198
  }
600✔
199

200
  double delta = seconds ? seconds : DiffTime(now, mintime);
60!
201
  return filterScore(counts, delta, rate);
60✔
202
}
60✔
203

204
static counts_t exceedQueryGen(unsigned int rate, int seconds, std::function<void(counts_t&, const Rings::Query&)> T)
205
{
73✔
206
  counts_t counts;
73✔
207
  struct timespec cutoff, mintime, now;
73✔
208
  gettime(&now);
73✔
209
  cutoff = mintime = now;
73✔
210
  cutoff.tv_sec -= seconds;
73✔
211

212
  counts.reserve(g_rings.getNumberOfQueryEntries());
73✔
213

214
  for (const auto& shard : g_rings.d_shards) {
730✔
215
    auto rl = shard->queryRing.lock();
730✔
216
    for(const auto& c : *rl) {
2,327✔
217
      if(seconds && c.when < cutoff)
2,327!
218
        continue;
1,881✔
219
      if(now < c.when)
446!
220
        continue;
×
221
      T(counts, c);
446✔
222
      if(c.when < mintime)
446✔
223
        mintime = c.when;
47✔
224
    }
446✔
225
  }
730✔
226

227
  double delta = seconds ? seconds : DiffTime(now, mintime);
73!
228
  return filterScore(counts, delta, rate);
73✔
229
}
73✔
230

231

232
static counts_t exceedRCode(unsigned int rate, int seconds, int rcode)
233
{
43✔
234
  return exceedRespGen(rate, seconds, [rcode](counts_t& counts, const Rings::Response& r)
43✔
235
                   {
268✔
236
                     if(r.dh.rcode == rcode)
268✔
237
                       counts[r.requestor]++;
178✔
238
                   });
268✔
239
}
43✔
240

241
static counts_t exceedRespByterate(unsigned int rate, int seconds)
242
{
17✔
243
  return exceedRespGen(rate, seconds, [](counts_t& counts, const Rings::Response& r)
17✔
244
                   {
26✔
245
                     counts[r.requestor]+=r.size;
26✔
246
                   });
26✔
247
}
17✔
248

249
#endif /* DISABLE_DEPRECATED_DYNBLOCK */
250
#endif /* DISABLE_DYNBLOCKS */
251
// NOLINTNEXTLINE(readability-function-cognitive-complexity): this function declares Lua bindings, even with a good refactoring it will likely blow up the threshold
252
void setupLuaInspection(LuaContext& luaCtx)
253
{
631✔
254
#ifndef DISABLE_TOP_N_BINDINGS
631✔
255
  luaCtx.writeFunction("topClients", [](boost::optional<uint64_t> top_) {
631✔
256
      setLuaNoSideEffect();
×
257
      uint64_t top = top_ ? *top_ : 10U;
×
258
      map<ComboAddress, unsigned int,ComboAddress::addressOnlyLessThan > counts;
×
259
      unsigned int total=0;
×
260
      {
×
261
        for (const auto& shard : g_rings.d_shards) {
×
262
          auto rl = shard->queryRing.lock();
×
263
          for(const auto& c : *rl) {
×
264
            counts[c.requestor]++;
×
265
            total++;
×
266
          }
×
267
        }
×
268
      }
×
269
      vector<pair<unsigned int, ComboAddress>> rcounts;
×
270
      rcounts.reserve(counts.size());
×
271
      for(const auto& c : counts)
×
272
        rcounts.emplace_back(c.second, c.first);
×
273

274
      sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
×
275
                                              const decltype(rcounts)::value_type& b) {
×
276
             return b.first < a.first;
×
277
           });
×
278
      unsigned int count=1, rest=0;
×
279
      boost::format fmt("%4d  %-40s %4d %4.1f%%\n");
×
280
      for(const auto& rc : rcounts) {
×
281
        if(count==top+1)
×
282
          rest+=rc.first;
×
283
        else
×
284
          g_outputBuffer += (fmt % (count++) % rc.second.toString() % rc.first % (100.0*rc.first/total)).str();
×
285
      }
×
286
      g_outputBuffer += (fmt % (count) % "Rest" % rest % (total > 0 ? 100.0*rest/total : 100.0)).str();
×
287
    });
×
288

289
  luaCtx.writeFunction("getTopQueries", [](uint64_t top, boost::optional<int> labels) {
631✔
290
      setLuaNoSideEffect();
×
291
      map<DNSName, unsigned int> counts;
×
292
      unsigned int total=0;
×
293
      if(!labels) {
×
294
        for (const auto& shard : g_rings.d_shards) {
×
295
          auto rl = shard->queryRing.lock();
×
296
          for(const auto& a : *rl) {
×
297
            counts[a.name]++;
×
298
            total++;
×
299
          }
×
300
        }
×
301
      }
×
302
      else {
×
303
        unsigned int lab = *labels;
×
304
        for (const auto& shard : g_rings.d_shards) {
×
305
          auto rl = shard->queryRing.lock();
×
306
          // coverity[auto_causes_copy]
307
          for (auto a : *rl) {
×
308
            a.name.trimToLabels(lab);
×
309
            counts[a.name]++;
×
310
            total++;
×
311
          }
×
312
        }
×
313
      }
×
314
      // cout<<"Looked at "<<total<<" queries, "<<counts.size()<<" different ones"<<endl;
315
      vector<pair<unsigned int, DNSName>> rcounts;
×
316
      rcounts.reserve(counts.size());
×
317
      for(const auto& c : counts)
×
318
        rcounts.emplace_back(c.second, c.first.makeLowerCase());
×
319

320
      sort(rcounts.begin(), rcounts.end(), [](const decltype(rcounts)::value_type& a,
×
321
                                              const decltype(rcounts)::value_type& b) {
×
322
             return b.first < a.first;
×
323
           });
×
324

325
      std::unordered_map<unsigned int, vector<boost::variant<string,double>>> ret;
×
326
      unsigned int count=1, rest=0;
×
327
      for(const auto& rc : rcounts) {
×
328
        if(count==top+1)
×
329
          rest+=rc.first;
×
330
        else
×
331
          ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
×
332
      }
×
333

334
      if (total > 0) {
×
335
        ret.insert({count, {"Rest", rest, 100.0*rest/total}});
×
336
      }
×
337
      else {
×
338
        ret.insert({count, {"Rest", rest, 100.0}});
×
339
      }
×
340

341
      return ret;
×
342

343
    });
×
344

345
  luaCtx.executeCode(R"(function topQueries(top, labels) top = top or 10; for k,v in ipairs(getTopQueries(top,labels)) do show(string.format("%4d  %-40s %4d %4.1f%%",k,v[1],v[2], v[3])) end end)");
631✔
346

347
  luaCtx.writeFunction("getResponseRing", []() {
631✔
348
      setLuaNoSideEffect();
×
349
      size_t totalEntries = 0;
×
350
      std::vector<boost::circular_buffer<Rings::Response>> rings;
×
351
      rings.reserve(g_rings.getNumberOfShards());
×
352
      for (const auto& shard : g_rings.d_shards) {
×
353
        {
×
354
          auto rl = shard->respRing.lock();
×
355
          rings.push_back(*rl);
×
356
        }
×
357
        totalEntries += rings.back().size();
×
358
      }
×
359
      vector<std::unordered_map<string, boost::variant<string, unsigned int> > > ret;
×
360
      ret.reserve(totalEntries);
×
361
      decltype(ret)::value_type item;
×
362
      for (size_t idx = 0; idx < rings.size(); idx++) {
×
363
        for(const auto& r : rings[idx]) {
×
364
          item["name"]=r.name.toString();
×
365
          item["qtype"]=r.qtype;
×
366
          item["rcode"]=r.dh.rcode;
×
367
          item["usec"]=r.usec;
×
368
          ret.push_back(item);
×
369
        }
×
370
      }
×
371
      return ret;
×
372
    });
×
373

374
  luaCtx.writeFunction("getTopResponses", [](uint64_t top, uint64_t kind, boost::optional<int> labels) {
631✔
375
      return getGenResponses(top, labels, [kind](const Rings::Response& r) { return r.dh.rcode == kind; });
×
376
    });
×
377

378
  luaCtx.executeCode(R"(function topResponses(top, kind, labels) top = top or 10; kind = kind or 0; for k,v in ipairs(getTopResponses(top, kind, labels)) do show(string.format("%4d  %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
631✔
379

380

381
  luaCtx.writeFunction("getSlowResponses", [](uint64_t top, uint64_t msec, boost::optional<int> labels, boost::optional<bool> timeouts) {
631✔
NEW
382
    return getGenResponses(top, labels, [msec, timeouts](const Rings::Response& resp) {
×
NEW
383
      if (timeouts && *timeouts) {
×
NEW
384
        return resp.usec == std::numeric_limits<unsigned int>::max();
×
NEW
385
      }
×
NEW
386
      return resp.usec > msec * 1000 && resp.usec != std::numeric_limits<unsigned int>::max();
×
UNCOV
387
    });
×
NEW
388
  });
×
389

390
  luaCtx.executeCode(R"(function topSlow(top, msec, labels) top = top or 10; msec = msec or 500; for k,v in ipairs(getSlowResponses(top, msec, labels, false)) do show(string.format("%4d  %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
631✔
391

392
  luaCtx.executeCode(R"(function topTimeouts(top, labels) top = top or 10; for k,v in ipairs(getSlowResponses(top, 0, labels, true)) do show(string.format("%4d  %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
631✔
393

394
  luaCtx.writeFunction("getTopBandwidth", [](uint64_t top) {
631✔
395
      setLuaNoSideEffect();
×
396
      return g_rings.getTopBandwidth(top);
×
397
    });
×
398

399
  luaCtx.executeCode(R"(function topBandwidth(top) top = top or 10; for k,v in ipairs(getTopBandwidth(top)) do show(string.format("%4d  %-40s %4d %4.1f%%",k,v[1],v[2],v[3])) end end)");
631✔
400
#endif /* DISABLE_TOP_N_BINDINGS */
631✔
401

402
  luaCtx.writeFunction("delta", []() {
631✔
403
      setLuaNoSideEffect();
×
404
      // we hold the lua lock already!
405
      for(const auto& d : g_confDelta) {
×
406
        struct tm tm;
×
407
        localtime_r(&d.first.tv_sec, &tm);
×
408
        char date[80];
×
409
        strftime(date, sizeof(date)-1, "-- %a %b %d %Y %H:%M:%S %Z\n", &tm);
×
410
        g_outputBuffer += date;
×
411
        g_outputBuffer += d.second + "\n";
×
412
      }
×
413
    });
×
414

415
  luaCtx.writeFunction("grepq", [](LuaTypeOrArrayOf<std::string> inp, boost::optional<unsigned int> limit, boost::optional<LuaAssociativeTable<std::string>> options) {
631✔
416
      setLuaNoSideEffect();
11✔
417
      boost::optional<Netmask>  nm;
11✔
418
      boost::optional<DNSName> dn;
11✔
419
      int msec = -1;
11✔
420
      pdns::UniqueFilePtr outputFile{nullptr};
11✔
421

422
      if (options) {
11!
423
        std::string outputFileName;
×
424
        if (getOptionalValue<std::string>(options, "outputFile", outputFileName) > 0) {
×
425
          int fd = open(outputFileName.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0600);
×
426
          if (fd < 0) {
×
427
            g_outputBuffer = "Error opening dump file for writing: " + stringerror() + "\n";
×
428
            return;
×
429
          }
×
430
          outputFile = pdns::UniqueFilePtr(fdopen(fd, "w"));
×
431
          if (outputFile == nullptr) {
×
432
            g_outputBuffer = "Error opening dump file for writing: " + stringerror() + "\n";
×
433
            close(fd);
×
434
            return;
×
435
          }
×
436
        }
×
437
        checkAllParametersConsumed("grepq", options);
×
438
      }
×
439

440
      vector<string> vec;
11✔
441
      auto str = boost::get<string>(&inp);
11✔
442
      if (str) {
11!
443
        vec.push_back(*str);
11✔
444
      }
11✔
445
      else {
×
446
        auto v = boost::get<LuaArray<std::string>>(inp);
×
447
        for (const auto& a: v) {
×
448
          vec.push_back(a.second);
×
449
        }
×
450
      }
×
451

452
      for (const auto& s : vec) {
11✔
453
        try {
11✔
454
            nm = Netmask(s);
11✔
455
        }
11✔
456
        catch (...) {
11✔
457
          if (boost::ends_with(s,"ms") && sscanf(s.c_str(), "%ums", &msec)) {
11!
458
            ;
×
459
          }
×
460
          else {
11✔
461
            try {
11✔
462
              dn = DNSName(s);
11✔
463
            }
11✔
464
            catch (...) {
11✔
465
              g_outputBuffer = "Could not parse '"+s+"' as domain name or netmask";
×
466
              return;
×
467
            }
×
468
          }
11✔
469
        }
11✔
470
      }
11✔
471

472
      std::vector<Rings::Query> qr;
11✔
473
      std::vector<Rings::Response> rr;
11✔
474
      qr.reserve(g_rings.getNumberOfQueryEntries());
11✔
475
      rr.reserve(g_rings.getNumberOfResponseEntries());
11✔
476
      for (const auto& shard : g_rings.d_shards) {
110✔
477
        {
110✔
478
          auto rl = shard->queryRing.lock();
110✔
479
          for (const auto& entry : *rl) {
110✔
480
            qr.push_back(entry);
58✔
481
          }
58✔
482
        }
110✔
483
        {
110✔
484
          auto rl = shard->respRing.lock();
110✔
485
          for (const auto& entry : *rl) {
110✔
486
            rr.push_back(entry);
52✔
487
          }
52✔
488
        }
110✔
489
      }
110✔
490

491
      sort(qr.begin(), qr.end(), [](const decltype(qr)::value_type& a, const decltype(qr)::value_type& b) {
159✔
492
        return b.when < a.when;
159✔
493
      });
159✔
494

495
      sort(rr.begin(), rr.end(), [](const decltype(rr)::value_type& a, const decltype(rr)::value_type& b) {
153✔
496
        return b.when < a.when;
153✔
497
      });
153✔
498

499
      unsigned int num=0;
11✔
500
      struct timespec now;
11✔
501
      gettime(&now);
11✔
502

503
      std::multimap<struct timespec, string> out;
11✔
504

505
      boost::format        fmt("%-7.1f %-47s %-12s %-12s %-5d %-25s %-5s %-6.1f %-2s %-2s %-2s %-s\n");
11✔
506
      const auto headLine = (fmt % "Time" % "Client" % "Protocol" % "Server" % "ID" % "Name" % "Type" % "Lat." % "TC" % "RD" % "AA" % "Rcode").str();
11✔
507
      if (!outputFile) {
11!
508
        g_outputBuffer += headLine;
11✔
509
      }
11✔
510
      else {
×
511
        fprintf(outputFile.get(), "%s", headLine.c_str());
×
512
      }
×
513

514
      if (msec == -1) {
11!
515
        for (const auto& c : qr) {
58✔
516
          bool nmmatch = true;
58✔
517
          bool dnmatch = true;
58✔
518
          if (nm) {
58!
519
            nmmatch = nm->match(c.requestor);
×
520
          }
×
521
          if (dn) {
58!
522
            if (c.name.empty()) {
58!
523
              dnmatch = false;
×
524
            }
×
525
            else {
58✔
526
              dnmatch = c.name.isPartOf(*dn);
58✔
527
            }
58✔
528
          }
58✔
529
          if (nmmatch && dnmatch) {
58!
530
            QType qt(c.qtype);
58✔
531
            std::string extra;
58✔
532
            if (c.dh.opcode != 0) {
58!
533
              extra = " (" + Opcode::to_s(c.dh.opcode) + ")";
×
534
            }
×
535
            out.emplace(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % dnsdist::Protocol(c.protocol).toString() % "" % htons(c.dh.id) % c.name.toString() % qt.toString() % "" % (c.dh.tc ? "TC" : "") % (c.dh.rd ? "RD" : "") % (c.dh.aa ? "AA" : "") % ("Question" + extra)).str());
58!
536

537
            if (limit && *limit == ++num) {
58!
538
              break;
×
539
            }
×
540
          }
58✔
541
        }
58✔
542
      }
11✔
543
      num = 0;
11✔
544

545
      string extra;
11✔
546
      for (const auto& c : rr) {
52✔
547
        bool nmmatch = true;
52✔
548
        bool dnmatch = true;
52✔
549
        bool msecmatch = true;
52✔
550
        if (nm) {
52!
551
          nmmatch = nm->match(c.requestor);
×
552
        }
×
553
        if (dn) {
52!
554
          if (c.name.empty()) {
52!
555
            dnmatch = false;
×
556
          }
×
557
          else {
52✔
558
            dnmatch = c.name.isPartOf(*dn);
52✔
559
          }
52✔
560
        }
52✔
561
        if (msec != -1) {
52!
562
          msecmatch = (c.usec/1000 > (unsigned int)msec);
×
563
        }
×
564

565
        if (nmmatch && dnmatch && msecmatch) {
52!
566
          QType qt(c.qtype);
52✔
567
          if (!c.dh.rcode) {
52!
568
            extra = ". " +std::to_string(htons(c.dh.ancount)) + " answers";
52✔
569
          }
52✔
570
          else {
×
571
            extra.clear();
×
572
          }
×
573

574
          std::string server = c.ds.toStringWithPort();
52✔
575
          std::string protocol = dnsdist::Protocol(c.protocol).toString();
52✔
576
          if (server == "0.0.0.0:0") {
52!
577
            server = "Cache";
×
578
            protocol = "-";
×
579
          }
×
580
          if (c.usec != std::numeric_limits<decltype(c.usec)>::max()) {
52✔
581
            out.emplace(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % protocol % server % htons(c.dh.id) % c.name.toString() % qt.toString() % (c.usec / 1000.0) % (c.dh.tc ? "TC" : "") % (c.dh.rd ? "RD" : "") % (c.dh.aa ? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str());
48!
582
          }
48✔
583
          else {
4✔
584
            out.emplace(c.when, (fmt % DiffTime(now, c.when) % c.requestor.toStringWithPort() % protocol % server % htons(c.dh.id) % c.name.toString() % qt.toString() % "T.O" % (c.dh.tc ? "TC" : "") % (c.dh.rd ? "RD" : "") % (c.dh.aa ? "AA" : "") % (RCode::to_s(c.dh.rcode) + extra)).str());
4!
585
          }
4✔
586

587
          if (limit && *limit == ++num) {
52!
588
            break;
×
589
          }
×
590
        }
52✔
591
      }
52✔
592

593
      for (const auto& p : out) {
110✔
594
        if (!outputFile) {
110!
595
          g_outputBuffer += p.second;
110✔
596
        }
110✔
597
        else {
×
598
          fprintf(outputFile.get(), "%s", p.second.c_str());
×
599
        }
×
600
      }
110✔
601
    });
11✔
602

603
  luaCtx.writeFunction("showResponseLatency", []() {
631✔
604
      setLuaNoSideEffect();
×
605
      map<double, unsigned int> histo;
×
606
      double bin=100;
×
607
      for(int i=0; i < 15; ++i) {
×
608
        histo[bin];
×
609
        bin*=2;
×
610
      }
×
611

612
      double totlat=0;
×
613
      unsigned int size=0;
×
614
      {
×
615
        for (const auto& shard : g_rings.d_shards) {
×
616
          auto rl = shard->respRing.lock();
×
617
          for(const auto& r : *rl) {
×
618
            /* skip actively discovered timeouts */
619
            if (r.usec == std::numeric_limits<unsigned int>::max())
×
620
              continue;
×
621

622
            ++size;
×
623
            auto iter = histo.lower_bound(r.usec);
×
624
            if(iter != histo.end())
×
625
              iter->second++;
×
626
            else
×
627
              histo.rbegin()++;
×
628
            totlat+=r.usec;
×
629
          }
×
630
        }
×
631
      }
×
632

633
      if (size == 0) {
×
634
        g_outputBuffer = "No traffic yet.\n";
×
635
        return;
×
636
      }
×
637

638
      g_outputBuffer = (boost::format("Average response latency: %.02f ms\n") % (0.001*totlat/size)).str();
×
639
      double highest=0;
×
640

641
      for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
×
642
        highest=std::max(highest, iter->second*1.0);
×
643
      }
×
644
      boost::format fmt("%7.2f\t%s\n");
×
645
      g_outputBuffer += (fmt % "ms" % "").str();
×
646

647
      for(auto iter = histo.cbegin(); iter != histo.cend(); ++iter) {
×
648
        int stars = (70.0 * iter->second/highest);
×
649
        char c='*';
×
650
        if(!stars && iter->second) {
×
651
          stars=1; // you get 1 . to show something is there..
×
652
          if(70.0*iter->second/highest > 0.5)
×
653
            c=':';
×
654
          else
×
655
            c='.';
×
656
        }
×
657
        g_outputBuffer += (fmt % (iter->first/1000.0) % string(stars, c)).str();
×
658
      }
×
659
    });
×
660

661
  luaCtx.writeFunction("showTCPStats", [] {
631✔
662
      setLuaNoSideEffect();
×
663
      ostringstream ret;
×
664
      boost::format fmt("%-12d %-12d %-12d %-12d");
×
665
      ret << (fmt % "Workers" % "Max Workers" % "Queued" % "Max Queued") << endl;
×
666
      ret << (fmt % g_tcpclientthreads->getThreadsCount() % (g_maxTCPClientThreads ? *g_maxTCPClientThreads : 0) % g_tcpclientthreads->getQueuedCount() % g_maxTCPQueuedConnections) << endl;
×
667
      ret << endl;
×
668

669
      ret << "Frontends:" << endl;
×
670
      fmt = boost::format("%-3d %-20.20s %-20d %-20d %-20d %-25d %-20d %-20d %-20d %-20f %-20f %-20d %-20d %-25d %-25d %-15d %-15d %-15d %-15d %-15d");
×
671
      ret << (fmt % "#" % "Address" % "Connections" % "Max concurrent conn" % "Died reading query" % "Died sending response" % "Gave up" % "Client timeouts" % "Downstream timeouts" % "Avg queries/conn" % "Avg duration" % "TLS new sessions" % "TLS Resumptions" % "TLS unknown ticket keys" % "TLS inactive ticket keys" % "TLS 1.0" % "TLS 1.1" % "TLS 1.2" % "TLS 1.3" % "TLS other") << endl;
×
672

673
      size_t counter = 0;
×
674
      for(const auto& f : g_frontends) {
×
675
        ret << (fmt % counter % f->local.toStringWithPort() % f->tcpCurrentConnections % f->tcpMaxConcurrentConnections % f->tcpDiedReadingQuery % f->tcpDiedSendingResponse % f->tcpGaveUp % f->tcpClientTimeouts % f->tcpDownstreamTimeouts % f->tcpAvgQueriesPerConnection % f->tcpAvgConnectionDuration % f->tlsNewSessions % f->tlsResumptions % f->tlsUnknownTicketKey % f->tlsInactiveTicketKey % f->tls10queries % f->tls11queries % f->tls12queries % f->tls13queries % f->tlsUnknownqueries) << endl;
×
676
        ++counter;
×
677
      }
×
678
      ret << endl;
×
679

680
      ret << "Backends:" << endl;
×
681
      fmt = boost::format("%-3d %-20.20s %-20.20s %-20d %-20d %-25d %-25d %-20d %-20d %-20d %-20d %-20d %-20d %-20d %-20d %-20f %-20f");
×
682
      ret << (fmt % "#" % "Name" % "Address" % "Connections" % "Max concurrent conn" % "Died sending query" % "Died reading response" % "Gave up" % "Read timeouts" % "Write timeouts" % "Connect timeouts" % "Too many conn" % "Total connections" % "Reused connections" % "TLS resumptions" % "Avg queries/conn" % "Avg duration") << endl;
×
683

684
      auto states = g_dstates.getLocal();
×
685
      counter = 0;
×
686
      for(const auto& s : *states) {
×
687
        ret << (fmt % counter % s->getName() % s->d_config.remote.toStringWithPort() % s->tcpCurrentConnections % s->tcpMaxConcurrentConnections % s->tcpDiedSendingQuery % s->tcpDiedReadingResponse % s->tcpGaveUp % s->tcpReadTimeouts % s->tcpWriteTimeouts % s->tcpConnectTimeouts % s->tcpTooManyConcurrentConnections % s->tcpNewConnections % s->tcpReusedConnections % s->tlsResumptions % s->tcpAvgQueriesPerConnection % s->tcpAvgConnectionDuration) << endl;
×
688
        ++counter;
×
689
      }
×
690

691
      g_outputBuffer=ret.str();
×
692
    });
×
693

694
  luaCtx.writeFunction("showTLSErrorCounters", [] {
631✔
695
      setLuaNoSideEffect();
×
696
      ostringstream ret;
×
697
      boost::format fmt("%-3d %-20.20s %-23d %-23d %-23d %-23d %-23d %-23d %-23d %-23d");
×
698

699
      ret << (fmt % "#" % "Address" % "DH key too small" % "Inappropriate fallback" % "No shared cipher" % "Unknown cipher type" % "Unknown exchange type" % "Unknown protocol" % "Unsupported EC" % "Unsupported protocol") << endl;
×
700

701
      size_t counter = 0;
×
702
      for(const auto& f : g_frontends) {
×
703
        if (!f->hasTLS()) {
×
704
          continue;
×
705
        }
×
706
        const TLSErrorCounters* errorCounters = nullptr;
×
707
        if (f->tlsFrontend != nullptr) {
×
708
          errorCounters = &f->tlsFrontend->d_tlsCounters;
×
709
        }
×
710
        else if (f->dohFrontend != nullptr) {
×
711
          errorCounters = &f->dohFrontend->d_tlsContext.d_tlsCounters;
×
712
        }
×
713
        if (errorCounters == nullptr) {
×
714
          continue;
×
715
        }
×
716

717
        ret << (fmt % counter % f->local.toStringWithPort() % errorCounters->d_dhKeyTooSmall % errorCounters->d_inappropriateFallBack % errorCounters->d_noSharedCipher % errorCounters->d_unknownCipherType % errorCounters->d_unknownKeyExchangeType % errorCounters->d_unknownProtocol % errorCounters->d_unsupportedEC % errorCounters->d_unsupportedProtocol) << endl;
×
718
        ++counter;
×
719
      }
×
720
      ret << endl;
×
721

722
      g_outputBuffer=ret.str();
×
723
    });
×
724

725
  luaCtx.writeFunction("requestTCPStatesDump", [] {
631✔
726
    setLuaNoSideEffect();
×
727
    extern std::atomic<uint64_t> g_tcpStatesDumpRequested;
×
728
    g_tcpStatesDumpRequested += g_tcpclientthreads->getThreadsCount();
×
729
  });
×
730

731
  luaCtx.writeFunction("requestDoHStatesDump", [] {
631✔
732
    setLuaNoSideEffect();
×
733
#if defined(HAVE_DNS_OVER_HTTPS) && defined(HAVE_NGHTTP2)
×
734
    g_dohStatesDumpRequested += g_dohClientThreads->getThreadsCount();
×
735
#endif
×
736
  });
×
737

738
  luaCtx.writeFunction("dumpStats", [] {
631✔
739
      setLuaNoSideEffect();
1✔
740
      vector<string> leftcolumn, rightcolumn;
1✔
741

742
      boost::format fmt("%-35s\t%+11s");
1✔
743
      g_outputBuffer.clear();
1✔
744
      auto entries = *dnsdist::metrics::g_stats.entries.read_lock();
1✔
745
      sort(entries.begin(), entries.end(),
1✔
746
           [](const decltype(entries)::value_type& a, const decltype(entries)::value_type& b) {
641✔
747
             return a.d_name < b.d_name;
641✔
748
           });
641✔
749
      boost::format flt("    %9.1f");
1✔
750
      for (const auto& entry : entries) {
86✔
751
        string second;
86✔
752
        if (const auto& val = std::get_if<pdns::stat_t*>(&entry.d_value)) {
86✔
753
          second = std::to_string((*val)->load());
42✔
754
        }
42✔
755
        else if (const auto& adval = std::get_if<pdns::stat_t_trait<double>*>(&entry.d_value)) {
44!
756
          second = (flt % (*adval)->load()).str();
×
757
        }
×
758
        else if (const auto& dval = std::get_if<double*>(&entry.d_value)) {
44✔
759
          second = (flt % (**dval)).str();
24✔
760
        }
24✔
761
        else if (const auto& func = std::get_if<dnsdist::metrics::Stats::statfunction_t>(&entry.d_value)) {
20!
762
          second = std::to_string((*func)(entry.d_name));
20✔
763
        }
20✔
764

765
        if (leftcolumn.size() < entries.size() / 2) {
86✔
766
          leftcolumn.push_back((fmt % entry.d_name % second).str());
43✔
767
        }
43✔
768
        else {
43✔
769
          rightcolumn.push_back((fmt % entry.d_name % second).str());
43✔
770
        }
43✔
771
      }
86✔
772

773
      auto leftiter=leftcolumn.begin(), rightiter=rightcolumn.begin();
1✔
774
      boost::format clmn("%|0t|%1% %|51t|%2%\n");
1✔
775

776
      for(;leftiter != leftcolumn.end() || rightiter != rightcolumn.end();) {
44!
777
        string lentry, rentry;
43✔
778
        if(leftiter!= leftcolumn.end()) {
43!
779
          lentry = *leftiter;
43✔
780
          leftiter++;
43✔
781
        }
43✔
782
        if(rightiter!= rightcolumn.end()) {
43!
783
          rentry = *rightiter;
43✔
784
          rightiter++;
43✔
785
        }
43✔
786
        g_outputBuffer += (clmn % lentry % rentry).str();
43✔
787
      }
43✔
788
    });
1✔
789

790
#ifndef DISABLE_DYNBLOCKS
631✔
791
#ifndef DISABLE_DEPRECATED_DYNBLOCK
631✔
792
  luaCtx.writeFunction("exceedServFails", [](unsigned int rate, int seconds) {
631✔
793
      setLuaNoSideEffect();
43✔
794
      return exceedRCode(rate, seconds, RCode::ServFail);
43✔
795
    });
43✔
796
  luaCtx.writeFunction("exceedNXDOMAINs", [](unsigned int rate, int seconds) {
631✔
797
      setLuaNoSideEffect();
×
798
      return exceedRCode(rate, seconds, RCode::NXDomain);
×
799
    });
×
800

801
  luaCtx.writeFunction("exceedRespByterate", [](unsigned int rate, int seconds) {
631✔
802
      setLuaNoSideEffect();
17✔
803
      return exceedRespByterate(rate, seconds);
17✔
804
    });
17✔
805

806
  luaCtx.writeFunction("exceedQTypeRate", [](uint16_t type, unsigned int rate, int seconds) {
631✔
807
      setLuaNoSideEffect();
×
808
      return exceedQueryGen(rate, seconds, [type](counts_t& counts, const Rings::Query& q) {
×
809
          if(q.qtype==type)
×
810
            counts[q.requestor]++;
×
811
        });
×
812
    });
×
813

814
  luaCtx.writeFunction("exceedQRate", [](unsigned int rate, int seconds) {
631✔
815
      setLuaNoSideEffect();
73✔
816
      return exceedQueryGen(rate, seconds, [](counts_t& counts, const Rings::Query& q) {
446✔
817
          counts[q.requestor]++;
446✔
818
        });
446✔
819
    });
73✔
820

821
  luaCtx.writeFunction("getRespRing", getRespRing);
631✔
822

823
  /* StatNode */
824
  luaCtx.registerFunction<unsigned int(StatNode::*)()const>("numChildren",
631✔
825
                                                            [](const StatNode& sn) -> unsigned int {
631✔
826
                                                              return sn.children.size();
×
827
                                                            } );
×
828
  luaCtx.registerMember("fullname", &StatNode::fullname);
631✔
829
  luaCtx.registerMember("labelsCount", &StatNode::labelsCount);
631✔
830
  luaCtx.registerMember("servfails", &StatNode::Stat::servfails);
631✔
831
  luaCtx.registerMember("nxdomains", &StatNode::Stat::nxdomains);
631✔
832
  luaCtx.registerMember("queries", &StatNode::Stat::queries);
631✔
833
  luaCtx.registerMember("noerrors", &StatNode::Stat::noerrors);
631✔
834
  luaCtx.registerMember("drops", &StatNode::Stat::drops);
631✔
835
  luaCtx.registerMember("bytes", &StatNode::Stat::bytes);
631✔
836
  luaCtx.registerMember("hits", &StatNode::Stat::hits);
631✔
837

838
  luaCtx.writeFunction("statNodeRespRing", [](statvisitor_t visitor, boost::optional<uint64_t> seconds) {
631✔
839
      statNodeRespRing(std::move(visitor), seconds ? *seconds : 0U);
5✔
840
    });
5✔
841
#endif /* DISABLE_DEPRECATED_DYNBLOCK */
631✔
842

843
  /* DynBlockRulesGroup */
844
  luaCtx.writeFunction("dynBlockRulesGroup", []() { return std::make_shared<DynBlockRulesGroup>(); });
631✔
845
  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setQueryRate", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
631✔
846
      if (group) {
18!
847
        group->setQueryRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
18✔
848
      }
18✔
849
    });
18✔
850
  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setResponseByteRate", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
631✔
851
      if (group) {
2!
852
        group->setResponseByteRate(rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
2!
853
      }
2✔
854
    });
2✔
855
  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, DynBlockRulesGroup::smtVisitor_t)>("setSuffixMatchRule", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, DynBlockRulesGroup::smtVisitor_t visitor) {
631✔
856
      if (group) {
×
857
        group->setSuffixMatchRule(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, std::move(visitor));
×
858
      }
×
859
    });
×
860
  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, dnsdist_ffi_stat_node_visitor_t)>("setSuffixMatchRuleFFI", [](std::shared_ptr<DynBlockRulesGroup>& group, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, dnsdist_ffi_stat_node_visitor_t visitor) {
631✔
861
      if (group) {
×
862
        group->setSuffixMatchRuleFFI(seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, std::move(visitor));
×
863
      }
×
864
    });
×
865
  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(const dnsdist_ffi_dynamic_block_inserted_hook&)>("setNewBlockInsertedHook", [](std::shared_ptr<DynBlockRulesGroup>& group, const dnsdist_ffi_dynamic_block_inserted_hook& hook) {
631✔
866
      if (group) {
×
867
        group->setNewBlockHook(hook);
×
868
      }
×
869
    });
×
870
  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(uint8_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setRCodeRate", [](std::shared_ptr<DynBlockRulesGroup>& group, uint8_t rcode, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
631✔
871
      if (group) {
2!
872
        group->setRCodeRate(rcode, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
2!
873
      }
2✔
874
    });
2✔
875
  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(uint8_t, double, unsigned int, const std::string&, unsigned int, size_t, boost::optional<DNSAction::Action>, boost::optional<double>)>("setRCodeRatio", [](std::shared_ptr<DynBlockRulesGroup>& group, uint8_t rcode, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, boost::optional<DNSAction::Action> action, boost::optional<double> warningRatio) {
631✔
876
      if (group) {
2!
877
        group->setRCodeRatio(rcode, ratio, warningRatio ? *warningRatio : 0.0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, minimumNumberOfResponses);
2!
878
      }
2✔
879
    });
2✔
880
  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(uint16_t, unsigned int, unsigned int, const std::string&, unsigned int, boost::optional<DNSAction::Action>, boost::optional<unsigned int>)>("setQTypeRate", [](std::shared_ptr<DynBlockRulesGroup>& group, uint16_t qtype, unsigned int rate, unsigned int seconds, const std::string& reason, unsigned int blockDuration, boost::optional<DNSAction::Action> action, boost::optional<unsigned int> warningRate) {
631✔
881
      if (group) {
×
882
        group->setQTypeRate(qtype, rate, warningRate ? *warningRate : 0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None);
×
883
      }
×
884
    });
×
885
  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(double, unsigned int, const std::string&, unsigned int, size_t, double, boost::optional<DNSAction::Action>, boost::optional<double>)>("setCacheMissRatio", [](std::shared_ptr<DynBlockRulesGroup>& group, double ratio, unsigned int seconds, const std::string& reason, unsigned int blockDuration, size_t minimumNumberOfResponses, double minimumGlobalCacheHitRatio, boost::optional<DNSAction::Action> action, boost::optional<double> warningRatio) {
631✔
886
      if (group) {
×
887
        group->setCacheMissRatio(ratio, warningRatio ? *warningRatio : 0.0, seconds, reason, blockDuration, action ? *action : DNSAction::Action::None, minimumNumberOfResponses, minimumGlobalCacheHitRatio);
×
888
      }
×
889
    });
×
890
  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(uint8_t, uint8_t, uint8_t)>("setMasks", [](std::shared_ptr<DynBlockRulesGroup>& group, uint8_t v4, uint8_t v6, uint8_t port) {
631✔
891
      if (group) {
2!
892
        if (v4 > 32) {
2!
893
          throw std::runtime_error("Trying to set an invalid IPv4 mask (" + std::to_string(v4) + ") to a Dynamic Block object");
×
894
        }
×
895
        if (v6 > 128) {
2!
896
          throw std::runtime_error("Trying to set an invalid IPv6 mask (" + std::to_string(v6) + ") to a Dynamic Block object");
×
897
        }
×
898
        if (port > 16) {
2!
899
          throw std::runtime_error("Trying to set an invalid port mask (" + std::to_string(port) + ") to a Dynamic Block object");
×
900
        }
×
901
        if (port > 0 && v4 != 32) {
2!
902
          throw std::runtime_error("Setting a non-zero port mask for Dynamic Blocks while only considering parts of IPv4 addresses does not make sense");
×
903
        }
×
904
        group->setMasks(v4, v6, port);
2✔
905
      }
2✔
906
    });
2✔
907
  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, LuaArray<std::string>, NetmaskGroup>)>("excludeRange", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, LuaArray<std::string>, NetmaskGroup> ranges) {
631✔
908
      if (ranges.type() == typeid(LuaArray<std::string>)) {
4!
909
        for (const auto& range : *boost::get<LuaArray<std::string>>(&ranges)) {
×
910
          group->excludeRange(Netmask(range.second));
×
911
        }
×
912
      }
×
913
      else if (ranges.type() == typeid(NetmaskGroup)) {
4✔
914
        group->excludeRange(*boost::get<NetmaskGroup>(&ranges));
2✔
915
      }
2✔
916
      else {
2✔
917
        group->excludeRange(Netmask(*boost::get<std::string>(&ranges)));
2✔
918
      }
2✔
919
    });
4✔
920
  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, LuaArray<std::string>, NetmaskGroup>)>("includeRange", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, LuaArray<std::string>, NetmaskGroup> ranges) {
631✔
921
      if (ranges.type() == typeid(LuaArray<std::string>)) {
×
922
        for (const auto& range : *boost::get<LuaArray<std::string>>(&ranges)) {
×
923
          group->includeRange(Netmask(range.second));
×
924
        }
×
925
      }
×
926
      else if (ranges.type() == typeid(NetmaskGroup)) {
×
927
        group->includeRange(*boost::get<NetmaskGroup>(&ranges));
×
928
      }
×
929
      else {
×
930
        group->includeRange(Netmask(*boost::get<std::string>(&ranges)));
×
931
      }
×
932
    });
×
933
  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(boost::variant<std::string, LuaArray<std::string>, NetmaskGroup>)>("removeRange", [](std::shared_ptr<DynBlockRulesGroup>& group, boost::variant<std::string, LuaArray<std::string>, NetmaskGroup> ranges) {
631✔
934
      if (ranges.type() == typeid(LuaArray<std::string>)) {
×
935
        for (const auto& range : *boost::get<LuaArray<std::string>>(&ranges)) {
×
936
          group->removeRange(Netmask(range.second));
×
937
        }
×
938
      }
×
939
      else if (ranges.type() == typeid(NetmaskGroup)) {
×
940
        group->removeRange(*boost::get<NetmaskGroup>(&ranges));
×
941
      }
×
942
      else {
×
943
        group->removeRange(Netmask(*boost::get<std::string>(&ranges)));
×
944
      }
×
945
    });
×
946
  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)(LuaTypeOrArrayOf<std::string>)>("excludeDomains", [](std::shared_ptr<DynBlockRulesGroup>& group, LuaTypeOrArrayOf<std::string> domains) {
631✔
947
      if (domains.type() == typeid(LuaArray<std::string>)) {
×
948
        for (const auto& range : *boost::get<LuaArray<std::string>>(&domains)) {
×
949
          group->excludeDomain(DNSName(range.second));
×
950
        }
×
951
      }
×
952
      else {
×
953
        group->excludeDomain(DNSName(*boost::get<std::string>(&domains)));
×
954
      }
×
955
    });
×
956
  luaCtx.registerFunction<void(std::shared_ptr<DynBlockRulesGroup>::*)()>("apply", [](std::shared_ptr<DynBlockRulesGroup>& group) {
631✔
957
    group->apply();
160✔
958
  });
160✔
959
  luaCtx.registerFunction("setQuiet", &DynBlockRulesGroup::setQuiet);
631✔
960
  luaCtx.registerFunction("toString", &DynBlockRulesGroup::toString);
631✔
961

962
  /* DynBlock object accessors */
963
  luaCtx.registerMember("reason", &DynBlock::reason);
631✔
964
  luaCtx.registerMember("domain", &DynBlock::domain);
631✔
965
  luaCtx.registerMember("until", &DynBlock::until);
631✔
966
  luaCtx.registerMember<DynBlock, unsigned int>("blocks", [](const DynBlock& block) { return block.blocks.load(); }, [](DynBlock& block, [[maybe_unused]] unsigned int blocks) { });
631✔
967
  luaCtx.registerMember("action", &DynBlock::action);
631✔
968
  luaCtx.registerMember("warning", &DynBlock::warning);
631✔
969
  luaCtx.registerMember("bpf", &DynBlock::bpf);
631✔
970
#endif /* DISABLE_DYNBLOCKS */
631✔
971
}
631✔
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