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

PowerDNS / pdns / 12595591960

03 Jan 2025 09:27AM UTC coverage: 62.774% (+2.5%) from 60.245%
12595591960

Pull #15008

github

web-flow
Merge c2a2749d3 into 788f396a7
Pull Request #15008: Do not follow CNAME records for ANY or CNAME queries

30393 of 78644 branches covered (38.65%)

Branch coverage included in aggregate %.

105822 of 138350 relevant lines covered (76.49%)

4613078.44 hits per line

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

87.58
/pdns/recursordist/filterpo.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
 * 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
#pragma once
23
#include "iputils.hh"
24
#include "dns.hh"
25
#include "dnsname.hh"
26
#include "dnsparser.hh"
27
#include "logging.hh"
28
#include <map>
29
#include <unordered_map>
30
#include <limits>
31
#include <utility>
32

33
/* This class implements a filtering policy that is able to fully implement RPZ, but is not bound to it.
34
   In other words, it is generic enough to support RPZ, but could get its data from other places.
35

36

37
   We know the following actions:
38

39
   No action - just pass it on
40
   Drop - drop a query, no response
41
   NXDOMAIN - fake up an NXDOMAIN for the query
42
   NODATA - just return no data for this qtype
43
   Truncate - set TC bit
44
   Modified - "we fake an answer for you"
45

46
   These actions can be caused by the following triggers:
47

48
   qname - the query name
49
   client-ip - the IP address of the requestor
50
   response-ip - an IP address in the response
51
   ns-name - the name of a server used in the delegation
52
   ns-ip - the IP address of a server used in the delegation
53

54
   This means we get several hook points:
55
   1) when the query comes in: qname & client-ip
56
   2) during processing: ns-name & ns-ip
57
   3) after processing: response-ip
58

59
   Triggers meanwhile can apply to:
60
   Verbatim domain names
61
   Wildcard versions (*.domain.com does NOT match domain.com)
62
   Netmasks (IPv4 and IPv6)
63
   Finally, triggers are grouped in different zones. The "first" zone that has a match
64
   is consulted. Then within that zone, rules again have precedences.
65
*/
66

67
class DNSFilterEngine
68
{
69
public:
70
  enum class PolicyKind : uint8_t
71
  {
72
    NoAction,
73
    Drop,
74
    NXDOMAIN,
75
    NODATA,
76
    Truncate,
77
    Custom
78
  };
79
  enum class PolicyType : uint8_t
80
  {
81
    None,
82
    QName,
83
    ClientIP,
84
    ResponseIP,
85
    NSDName,
86
    NSIP
87
  };
88
  using Priority = uint16_t;
89
  static const Priority maximumPriority = std::numeric_limits<Priority>::max();
90

91
  static std::string getKindToString(PolicyKind kind);
92
  static std::string getTypeToString(PolicyType type);
93

94
  struct PolicyZoneData
95
  {
96
    /* shared by all the policies from a single zone */
97
    std::unordered_set<std::string> d_tags;
98
    std::string d_name;
99
    std::string d_extendedErrorExtra;
100
    DNSRecord d_soa{};
101
    boost::optional<uint16_t> d_extendedErrorCode{boost::none};
102
    Priority d_priority{maximumPriority};
103
    bool d_policyOverridesGettag{true};
104
    bool d_includeSOA{false};
105
    bool d_ignoreDuplicates{false};
106
  };
107

108
  struct Policy
109
  {
110
    Policy() :
111
      d_ttl(0), d_kind(PolicyKind::NoAction), d_type(PolicyType::None)
112
    {
8,213✔
113
    }
8,213✔
114

115
    Policy(PolicyKind kind, PolicyType type, int32_t ttl = 0, std::shared_ptr<PolicyZoneData> data = nullptr, const std::vector<std::shared_ptr<const DNSRecordContent>>& custom = {}) :
116
      d_zoneData(std::move(data)), d_custom(nullptr), d_ttl(ttl), d_kind(kind), d_type(type)
117
    {
92✔
118
      if (!custom.empty()) {
92✔
119
        setCustom(custom);
58✔
120
      }
58✔
121
    }
92✔
122

123
    ~Policy() = default;
8,490✔
124

125
    Policy(const Policy& rhs) :
126
      d_zoneData(rhs.d_zoneData),
127
      d_custom(rhs.d_custom ? make_unique<CustomData>(*rhs.d_custom) : nullptr),
128
      d_hitdata(rhs.d_hitdata ? make_unique<HitData>(*rhs.d_hitdata) : nullptr),
129
      d_ttl(rhs.d_ttl),
130
      d_kind(rhs.d_kind),
131
      d_type(rhs.d_type)
132
    {
29✔
133
    }
29✔
134

135
    Policy& operator=(const Policy& rhs)
136
    {
4,768✔
137
      if (this != &rhs) {
4,768!
138
        if (rhs.d_custom) {
4,768✔
139
          d_custom = make_unique<CustomData>(*rhs.d_custom);
162✔
140
        }
162✔
141
        d_zoneData = rhs.d_zoneData;
4,768✔
142
        if (rhs.d_hitdata) {
4,768✔
143
          d_hitdata = make_unique<HitData>(*rhs.d_hitdata);
94✔
144
        }
94✔
145
        else {
4,674✔
146
          d_hitdata = nullptr;
4,674✔
147
        }
4,674✔
148
        d_ttl = rhs.d_ttl;
4,768✔
149
        d_kind = rhs.d_kind;
4,768✔
150
        d_type = rhs.d_type;
4,768✔
151
      }
4,768✔
152
      return *this;
4,768✔
153
    }
4,768✔
154

155
    Policy(Policy&&) = default;
257✔
156
    Policy& operator=(Policy&&) = default;
58✔
157

158
    bool operator==(const Policy& rhs) const
159
    {
14✔
160
      return d_kind == rhs.d_kind && d_type == rhs.d_type && d_ttl == rhs.d_ttl && d_custom == rhs.d_custom;
14!
161
    }
14✔
162

163
    [[nodiscard]] const std::string& getName() const
164
    {
357✔
165
      static const std::string notSet;
357✔
166
      if (d_zoneData) {
357✔
167
        return d_zoneData->d_name;
222✔
168
      }
222✔
169
      return notSet;
135✔
170
    }
357✔
171

172
    void setName(const std::string& name)
173
    {
4✔
174
      /* until now the PolicyZoneData was shared,
175
         we now need to copy it, then write to it */
176
      std::shared_ptr<PolicyZoneData> newZoneData;
4✔
177
      if (d_zoneData) {
4!
178
        newZoneData = std::make_shared<PolicyZoneData>(*d_zoneData);
×
179
      }
×
180
      else {
4✔
181
        newZoneData = std::make_shared<PolicyZoneData>();
4✔
182
      }
4✔
183
      newZoneData->d_name = name;
4✔
184
      d_zoneData = std::move(newZoneData);
4✔
185
    }
4✔
186

187
    [[nodiscard]] const std::unordered_set<std::string>& getTags() const
188
    {
114✔
189
      static const std::unordered_set<std::string> notSet;
114✔
190
      if (d_zoneData) {
114!
191
        return d_zoneData->d_tags;
114✔
192
      }
114✔
193
      return notSet;
×
194
    }
114✔
195

196
    [[nodiscard]] Priority getPriority() const
197
    {
16,974✔
198
      static Priority notSet = maximumPriority;
16,974✔
199
      if (d_zoneData) {
16,974✔
200
        return d_zoneData->d_priority;
171✔
201
      }
171✔
202
      return notSet;
16,803✔
203
    }
16,974✔
204

205
    [[nodiscard]] bool policyOverridesGettag() const
206
    {
5✔
207
      if (d_zoneData) {
5!
208
        return d_zoneData->d_policyOverridesGettag;
×
209
      }
×
210
      return true;
5✔
211
    }
5✔
212

213
    [[nodiscard]] bool getSOA(DNSRecord& rec) const
214
    {
40✔
215
      if (d_zoneData && d_zoneData->d_soa.getContent()) {
40!
216
        rec = d_zoneData->d_soa;
40✔
217
        return true;
40✔
218
      }
40✔
219
      return false;
×
220
    }
40✔
221

222
    [[nodiscard]] bool includeSOA() const
223
    {
77✔
224
      if (d_zoneData) {
77✔
225
        return d_zoneData->d_includeSOA;
74✔
226
      }
74✔
227
      return false;
3✔
228
    }
77✔
229

230
    [[nodiscard]] bool wasHit() const
231
    {
71,218✔
232
      return (d_type != DNSFilterEngine::PolicyType::None && d_kind != DNSFilterEngine::PolicyKind::NoAction);
71,218✔
233
    }
71,218✔
234

235
    [[nodiscard]] std::string getLogString() const;
236
    void info(Logr::Priority prio, const std::shared_ptr<Logr::Logger>& log) const;
237
    [[nodiscard]] std::vector<DNSRecord> getCustomRecords(const DNSName& qname, uint16_t qtype) const;
238
    [[nodiscard]] std::vector<DNSRecord> getRecords(const DNSName& qname) const;
239

240
    std::shared_ptr<PolicyZoneData> d_zoneData{nullptr};
241

242
    using CustomData = std::vector<std::shared_ptr<const DNSRecordContent>>;
243
    std::unique_ptr<CustomData> d_custom;
244

245
    struct HitData
246
    {
247
      DNSName d_trigger;
248
      string d_hit;
249
    };
250
    std::unique_ptr<HitData> d_hitdata;
251
    /* Yup, we are currently using the same TTL for every record for a given name */
252
    int32_t d_ttl;
253
    PolicyKind d_kind;
254
    PolicyType d_type;
255

256
    void addSOAtoRPZResult(vector<DNSRecord>& ret) const
257
    {
77✔
258
      DNSRecord soa{};
77✔
259
      if (includeSOA() && getSOA(soa)) {
77!
260
        soa.d_place = DNSResourceRecord::ADDITIONAL;
40✔
261
        ret.emplace_back(soa);
40✔
262
      }
40✔
263
    }
77✔
264

265
    void setCustom(const CustomData& custom)
266
    {
58✔
267
      d_custom = make_unique<CustomData>(custom);
58✔
268
    }
58✔
269

270
    [[nodiscard]] size_t customRecordsSize() const
271
    {
139✔
272
      if (d_custom) {
139!
273
        return d_custom->size();
139✔
274
      }
139✔
275
      return 0;
×
276
    }
139✔
277

278
    void setHitData(const DNSName& name, const string& hit)
279
    {
192✔
280
      HitData hitdata{name, hit};
192✔
281
      d_hitdata = make_unique<HitData>(hitdata);
192✔
282
    }
192✔
283

284
    [[nodiscard]] DNSName getTrigger() const
285
    {
110✔
286
      return d_hitdata ? d_hitdata->d_trigger : DNSName();
110!
287
    }
110✔
288

289
    [[nodiscard]] std::string getHit() const
290
    {
110✔
291
      return d_hitdata ? d_hitdata->d_hit : "";
110!
292
    }
110✔
293

294
  private:
295
    [[nodiscard]] DNSRecord getRecordFromCustom(const DNSName& qname, const std::shared_ptr<const DNSRecordContent>& custom) const;
296
  };
297

298
  class Zone
299
  {
300
  public:
301
    Zone() :
302
      d_zoneData(std::make_shared<PolicyZoneData>())
303
    {
49✔
304
    }
49✔
305

306
    void clear()
307
    {
451✔
308
      d_qpolAddr.clear();
451✔
309
      d_postpolAddr.clear();
451✔
310
      d_propolName.clear();
451✔
311
      d_propolNSAddr.clear();
451✔
312
      d_qpolName.clear();
451✔
313
    }
451✔
314
    void reserve(size_t entriesCount)
315
    {
×
316
      d_qpolName.reserve(entriesCount);
×
317
    }
×
318
    void setName(const std::string& name)
319
    {
41✔
320
      d_zoneData->d_name = name;
41✔
321
    }
41✔
322
    void setDomain(const DNSName& domain)
323
    {
27✔
324
      d_domain = domain;
27✔
325
    }
27✔
326
    void setSerial(uint32_t serial)
327
    {
15✔
328
      d_serial = serial;
15✔
329
    }
15✔
330
    void setRefresh(uint32_t refresh)
331
    {
34✔
332
      d_refresh = refresh;
34✔
333
    }
34✔
334
    void setTags(std::unordered_set<std::string>&& tags)
335
    {
×
336
      d_zoneData->d_tags = std::move(tags);
×
337
    }
×
338
    void setTags(const std::unordered_set<std::string>& tags)
339
    {
1✔
340
      d_zoneData->d_tags = tags;
1✔
341
    }
1✔
342
    void setPolicyOverridesGettag(bool flag)
343
    {
17✔
344
      d_zoneData->d_policyOverridesGettag = flag;
17✔
345
    }
17✔
346
    void setExtendedErrorCode(uint16_t code)
347
    {
1✔
348
      d_zoneData->d_extendedErrorCode = code;
1✔
349
    }
1✔
350
    void setExtendedErrorExtra(const std::string& extra)
351
    {
1✔
352
      d_zoneData->d_extendedErrorExtra = extra;
1✔
353
    }
1✔
354
    void setSOA(DNSRecord soa)
355
    {
32✔
356
      d_zoneData->d_soa = std::move(soa);
32✔
357
    }
32✔
358
    [[nodiscard]] const std::string& getName() const
359
    {
17,040✔
360
      return d_zoneData->d_name;
17,040✔
361
    }
17,040✔
362

363
    [[nodiscard]] DNSName getDomain() const
364
    {
42✔
365
      return d_domain;
42✔
366
    }
42✔
367

368
    [[nodiscard]] uint32_t getRefresh() const
369
    {
2✔
370
      return d_refresh;
2✔
371
    }
2✔
372

373
    [[nodiscard]] uint32_t getSerial() const
374
    {
12✔
375
      return d_serial;
12✔
376
    }
12✔
377
    [[nodiscard]] const DNSRecord& getSOA() const
378
    {
10✔
379
      return d_zoneData->d_soa;
10✔
380
    }
10✔
381

382
    [[nodiscard]] size_t size() const
383
    {
78✔
384
      return d_qpolAddr.size() + d_postpolAddr.size() + d_propolName.size() + d_propolNSAddr.size() + d_qpolName.size();
78✔
385
    }
78✔
386

387
    void setIncludeSOA(bool flag)
388
    {
17✔
389
      d_zoneData->d_includeSOA = flag;
17✔
390
    }
17✔
391

392
    void setIgnoreDuplicates(bool flag)
393
    {
19✔
394
      d_zoneData->d_ignoreDuplicates = flag;
19✔
395
    }
19✔
396

397
    void dump(FILE* filePtr) const;
398

399
    void addClientTrigger(const Netmask& netmask, Policy&& pol, bool ignoreDuplicate = false);
400
    void addQNameTrigger(const DNSName& dnsname, Policy&& pol, bool ignoreDuplicate = false);
401
    void addNSTrigger(const DNSName& dnsname, Policy&& pol, bool ignoreDuplicate = false);
402
    void addNSIPTrigger(const Netmask& netmask, Policy&& pol, bool ignoreDuplicate = false);
403
    void addResponseTrigger(const Netmask& netmask, Policy&& pol, bool ignoreDuplicate = false);
404

405
    bool rmClientTrigger(const Netmask& netmask, const Policy& pol);
406
    bool rmQNameTrigger(const DNSName& dnsname, const Policy& pol);
407
    bool rmNSTrigger(const DNSName& dnsname, const Policy& pol);
408
    bool rmNSIPTrigger(const Netmask& netmask, const Policy& pol);
409
    bool rmResponseTrigger(const Netmask& netmask, const Policy& pol);
410

411
    bool findExactQNamePolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const;
412
    bool findExactNSPolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const;
413
    bool findNSIPPolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const;
414
    bool findResponsePolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const;
415
    bool findClientPolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const;
416

417
    [[nodiscard]] bool hasClientPolicies() const
418
    {
×
419
      return !d_qpolAddr.empty();
×
420
    }
×
421
    [[nodiscard]] bool hasQNamePolicies() const
422
    {
2,195✔
423
      return !d_qpolName.empty();
2,195✔
424
    }
2,195✔
425
    [[nodiscard]] bool hasNSPolicies() const
426
    {
10,911✔
427
      return !d_propolName.empty();
10,911✔
428
    }
10,911✔
429
    [[nodiscard]] bool hasNSIPPolicies() const
430
    {
×
431
      return !d_propolNSAddr.empty();
×
432
    }
×
433
    [[nodiscard]] bool hasResponsePolicies() const
434
    {
×
435
      return !d_postpolAddr.empty();
×
436
    }
×
437
    [[nodiscard]] Priority getPriority() const
438
    {
16,978✔
439
      return d_zoneData->d_priority;
16,978✔
440
    }
16,978✔
441
    void setPriority(Priority priority)
442
    {
54✔
443
      d_zoneData->d_priority = priority;
54✔
444
    }
54✔
445

446
    static DNSName maskToRPZ(const Netmask& netmask);
447

448
  private:
449
    void addNameTrigger(std::unordered_map<DNSName, Policy>& map, const DNSName& n, Policy&& pol, bool ignoreDuplicate, PolicyType ptype);
450
    void addNetmaskTrigger(NetmaskTree<Policy>& nmt, const Netmask& netmask, Policy&& pol, bool ignoreDuplicate, PolicyType ptype);
451
    static bool rmNameTrigger(std::unordered_map<DNSName, Policy>& map, const DNSName& n, const Policy& pol);
452
    static bool rmNetmaskTrigger(NetmaskTree<Policy>& nmt, const Netmask& netmask, const Policy& pol);
453

454
    static bool findExactNamedPolicy(const std::unordered_map<DNSName, DNSFilterEngine::Policy>& polmap, const DNSName& qname, DNSFilterEngine::Policy& pol);
455
    static bool findNamedPolicy(const std::unordered_map<DNSName, DNSFilterEngine::Policy>& polmap, const DNSName& qname, DNSFilterEngine::Policy& pol);
456
    static void dumpNamedPolicy(FILE* filePtr, const DNSName& name, const Policy& pol);
457
    static void dumpAddrPolicy(FILE* filePtr, const Netmask& netmask, const DNSName& name, const Policy& pol);
458

459
    std::unordered_map<DNSName, Policy> d_qpolName; // QNAME trigger (RPZ)
460
    NetmaskTree<Policy> d_qpolAddr; // Source address
461
    std::unordered_map<DNSName, Policy> d_propolName; // NSDNAME (RPZ)
462
    NetmaskTree<Policy> d_propolNSAddr; // NSIP (RPZ)
463
    NetmaskTree<Policy> d_postpolAddr; // IP trigger (RPZ)
464
    DNSName d_domain;
465
    std::shared_ptr<PolicyZoneData> d_zoneData{nullptr};
466
    uint32_t d_serial{0};
467
    uint32_t d_refresh{0};
468
  };
469

470
  DNSFilterEngine();
471
  void clear()
472
  {
600✔
473
    for (auto& zone : d_zones) {
600✔
474
      zone->clear();
448✔
475
    }
448✔
476
  }
600✔
477
  void clearZones()
478
  {
8✔
479
    d_zones.clear();
8✔
480
  }
8✔
481
  [[nodiscard]] std::shared_ptr<Zone> getZone(size_t zoneIdx) const
482
  {
31✔
483
    std::shared_ptr<Zone> result{nullptr};
31✔
484
    if (zoneIdx < d_zones.size()) {
31!
485
      result = d_zones[zoneIdx];
31✔
486
    }
31✔
487
    return result;
31✔
488
  }
31✔
489
  [[nodiscard]] std::shared_ptr<Zone> getZone(const std::string& name) const
490
  {
6✔
491
    for (const auto& zone : d_zones) {
6!
492
      const auto& zName = zone->getName();
6✔
493
      if (zName == name) {
6!
494
        return zone;
6✔
495
      }
6✔
496
    }
6✔
497
    return nullptr;
×
498
  }
6✔
499
  size_t addZone(const std::shared_ptr<Zone>& newZone)
500
  {
41✔
501
    newZone->setPriority(d_zones.size());
41✔
502
    d_zones.push_back(newZone);
41✔
503
    return (d_zones.size() - 1);
41✔
504
  }
41✔
505
  void setZone(size_t zoneIdx, const std::shared_ptr<Zone>& newZone)
506
  {
13✔
507
    if (newZone) {
13!
508
      assureZones(zoneIdx);
13✔
509
      newZone->setPriority(zoneIdx);
13✔
510
      d_zones[zoneIdx] = newZone;
13✔
511
    }
13✔
512
  }
13✔
513

514
  bool getQueryPolicy(const DNSName& qname, const std::unordered_map<std::string, bool>& discardedPolicies, Policy& policy) const;
515
  bool getClientPolicy(const ComboAddress& address, const std::unordered_map<std::string, bool>& discardedPolicies, Policy& policy) const;
516
  bool getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string, bool>& discardedPolicies, Policy& policy) const;
517
  bool getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string, bool>& discardedPolicies, Policy& policy) const;
518
  bool getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string, bool>& discardedPolicies, Policy& policy) const;
519
  bool getPostPolicy(const DNSRecord& record, const std::unordered_map<std::string, bool>& discardedPolicies, Policy& policy) const;
520

521
  // A few convenience methods for the unit test code
522
  [[nodiscard]] Policy getQueryPolicy(const DNSName& qname, const std::unordered_map<std::string, bool>& discardedPolicies, Priority priority) const
523
  {
46✔
524
    Policy policy;
46✔
525
    policy.d_zoneData = std::make_shared<PolicyZoneData>();
46✔
526
    policy.d_zoneData->d_priority = priority;
46✔
527
    getQueryPolicy(qname, discardedPolicies, policy);
46✔
528
    return policy;
46✔
529
  }
46✔
530

531
  [[nodiscard]] Policy getClientPolicy(const ComboAddress& address, const std::unordered_map<std::string, bool>& discardedPolicies, Priority priority) const
532
  {
20✔
533
    Policy policy;
20✔
534
    policy.d_zoneData = std::make_shared<PolicyZoneData>();
20✔
535
    policy.d_zoneData->d_priority = priority;
20✔
536
    getClientPolicy(address, discardedPolicies, policy);
20✔
537
    return policy;
20✔
538
  }
20✔
539

540
  [[nodiscard]] Policy getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string, bool>& discardedPolicies, Priority priority) const
541
  {
14✔
542
    Policy policy;
14✔
543
    policy.d_zoneData = std::make_shared<PolicyZoneData>();
14✔
544
    policy.d_zoneData->d_priority = priority;
14✔
545
    getProcessingPolicy(qname, discardedPolicies, policy);
14✔
546
    return policy;
14✔
547
  }
14✔
548

549
  [[nodiscard]] Policy getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string, bool>& discardedPolicies, Priority priority) const
550
  {
8✔
551
    Policy policy;
8✔
552
    policy.d_zoneData = std::make_shared<PolicyZoneData>();
8✔
553
    policy.d_zoneData->d_priority = priority;
8✔
554
    getProcessingPolicy(address, discardedPolicies, policy);
8✔
555
    return policy;
8✔
556
  }
8✔
557

558
  [[nodiscard]] Policy getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string, bool>& discardedPolicies, Priority priority) const
559
  {
8✔
560
    Policy policy;
8✔
561
    policy.d_zoneData = std::make_shared<PolicyZoneData>();
8✔
562
    policy.d_zoneData->d_priority = priority;
8✔
563
    getPostPolicy(records, discardedPolicies, policy);
8✔
564
    return policy;
8✔
565
  }
8✔
566

567
  [[nodiscard]] size_t size() const
568
  {
19✔
569
    return d_zones.size();
19✔
570
  }
19✔
571

572
private:
573
  void assureZones(size_t zone);
574
  vector<std::shared_ptr<Zone>> d_zones;
575
};
576

577
void mergePolicyTags(std::unordered_set<std::string>& tags, const std::unordered_set<std::string>& newTags);
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