]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/filterpo.hh
rec: Optimize for large number of filtering policies, empty sections
[thirdparty/pdns.git] / pdns / 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 <map>
28 #include <unordered_map>
29
30 /* This class implements a filtering policy that is able to fully implement RPZ, but is not bound to it.
31 In other words, it is generic enough to support RPZ, but could get its data from other places.
32
33
34 We know the following actions:
35
36 No action - just pass it on
37 Drop - drop a query, no response
38 NXDOMAIN - fake up an NXDOMAIN for the query
39 NODATA - just return no data for this qtype
40 Truncate - set TC bit
41 Modified - "we fake an answer for you"
42
43 These actions can be caused by the following triggers:
44
45 qname - the query name
46 client-ip - the IP address of the requestor
47 response-ip - an IP address in the response
48 ns-name - the name of a server used in the delegation
49 ns-ip - the IP address of a server used in the delegation
50
51 This means we get several hook points:
52 1) when the query comes in: qname & client-ip
53 2) during processing: ns-name & ns-ip
54 3) after processing: response-ip
55
56 Triggers meanwhile can apply to:
57 Verbatim domain names
58 Wildcard versions (*.domain.com does NOT match domain.com)
59 Netmasks (IPv4 and IPv6)
60 Finally, triggers are grouped in different zones. The "first" zone that has a match
61 is consulted. Then within that zone, rules again have precedences.
62 */
63
64
65 class DNSFilterEngine
66 {
67 public:
68 enum class PolicyKind { NoAction, Drop, NXDOMAIN, NODATA, Truncate, Custom};
69 enum class PolicyType { None, QName, ClientIP, ResponseIP, NSDName, NSIP };
70
71 static std::string getKindToString(PolicyKind kind);
72 static std::string getTypeToString(PolicyType type);
73
74 struct Policy
75 {
76 Policy(): d_name(nullptr), d_kind(PolicyKind::NoAction), d_type(PolicyType::None), d_ttl(0)
77 {
78 }
79
80 Policy(PolicyKind kind, PolicyType type, int32_t ttl=0, std::shared_ptr<std::string> name=nullptr, const std::vector<std::shared_ptr<DNSRecordContent>>& custom={}): d_custom(custom), d_name(name), d_kind(kind), d_type(type), d_ttl(ttl)
81 {
82 }
83
84 bool operator==(const Policy& rhs) const
85 {
86 return d_kind == rhs.d_kind && d_type == rhs.d_type && d_ttl == rhs.d_ttl && d_custom == rhs.d_custom;
87 }
88 std::vector<DNSRecord> getCustomRecords(const DNSName& qname, uint16_t qtype) const;
89 std::vector<DNSRecord> getRecords(const DNSName& qname) const;
90
91 std::vector<std::shared_ptr<DNSRecordContent>> d_custom;
92 std::shared_ptr<std::string> d_name; // the name of the policy
93 PolicyKind d_kind;
94 PolicyType d_type;
95 /* Yup, we are currently using the same TTL for every record for a given name */
96 int32_t d_ttl;
97
98 private:
99 DNSRecord getRecordFromCustom(const DNSName& qname, const std::shared_ptr<DNSRecordContent>& custom) const;
100 };
101
102 class Zone {
103 public:
104 void clear()
105 {
106 d_qpolAddr.clear();
107 d_postpolAddr.clear();
108 d_propolName.clear();
109 d_propolNSAddr.clear();
110 d_qpolName.clear();
111 }
112 void reserve(size_t entriesCount)
113 {
114 d_qpolName.reserve(entriesCount);
115 }
116 void setName(const std::string& name)
117 {
118 d_name = std::make_shared<std::string>(name);
119 }
120 void setDomain(const DNSName& domain)
121 {
122 d_domain = domain;
123 }
124 void setSerial(uint32_t serial)
125 {
126 d_serial = serial;
127 }
128 void setRefresh(uint32_t refresh)
129 {
130 d_refresh = refresh;
131 }
132 const std::shared_ptr<std::string> getName() const
133 {
134 return d_name;
135 }
136
137 DNSName getDomain() const
138 {
139 return d_domain;
140 }
141
142 uint32_t getRefresh() const
143 {
144 return d_refresh;
145 }
146
147 uint32_t getSerial() const
148 {
149 return d_serial;
150 }
151
152 size_t size() const
153 {
154 return d_qpolAddr.size() + d_postpolAddr.size() + d_propolName.size() + d_propolNSAddr.size() + d_qpolName.size();
155 }
156
157 void dump(FILE * fp) const;
158
159 void addClientTrigger(const Netmask& nm, Policy&& pol);
160 void addQNameTrigger(const DNSName& nm, Policy&& pol, bool ignoreDuplicate=false);
161 void addNSTrigger(const DNSName& dn, Policy&& pol);
162 void addNSIPTrigger(const Netmask& nm, Policy&& pol);
163 void addResponseTrigger(const Netmask& nm, Policy&& pol);
164
165 bool rmClientTrigger(const Netmask& nm, const Policy& pol);
166 bool rmQNameTrigger(const DNSName& nm, const Policy& pol);
167 bool rmNSTrigger(const DNSName& dn, const Policy& pol);
168 bool rmNSIPTrigger(const Netmask& nm, const Policy& pol);
169 bool rmResponseTrigger(const Netmask& nm, const Policy& pol);
170
171 bool findQNamePolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const;
172 bool findExactQNamePolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const;
173 bool findNSPolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const;
174 bool findExactNSPolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const;
175 bool findNSIPPolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const;
176 bool findResponsePolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const;
177 bool findClientPolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const;
178
179 bool hasClientPolicies() const
180 {
181 return !d_qpolAddr.empty();
182 }
183 bool hasQNamePolicies() const
184 {
185 return !d_qpolName.empty();
186 }
187 bool hasNSPolicies() const
188 {
189 return !d_propolName.empty();
190 }
191 bool hasNSIPPolicies() const
192 {
193 return !d_propolNSAddr.empty();
194 }
195 bool hasResponsePolicies() const
196 {
197 return !d_postpolAddr.empty();
198 }
199
200 private:
201 static DNSName maskToRPZ(const Netmask& nm);
202 static bool findExactNamedPolicy(const std::unordered_map<DNSName, DNSFilterEngine::Policy>& polmap, const DNSName& qname, DNSFilterEngine::Policy& pol);
203 static bool findNamedPolicy(const std::unordered_map<DNSName, DNSFilterEngine::Policy>& polmap, const DNSName& qname, DNSFilterEngine::Policy& pol);
204 static void dumpNamedPolicy(FILE* fp, const DNSName& name, const Policy& pol);
205 static void dumpAddrPolicy(FILE* fp, const Netmask& nm, const DNSName& name, const Policy& pol);
206
207 std::unordered_map<DNSName, Policy> d_qpolName; // QNAME trigger (RPZ)
208 NetmaskTree<Policy> d_qpolAddr; // Source address
209 std::unordered_map<DNSName, Policy> d_propolName; // NSDNAME (RPZ)
210 NetmaskTree<Policy> d_propolNSAddr; // NSIP (RPZ)
211 NetmaskTree<Policy> d_postpolAddr; // IP trigger (RPZ)
212 DNSName d_domain;
213 std::shared_ptr<std::string> d_name;
214 uint32_t d_serial{0};
215 uint32_t d_refresh{0};
216 };
217
218 DNSFilterEngine();
219 void clear()
220 {
221 for(auto& z : d_zones) {
222 z->clear();
223 }
224 }
225 void clearZones()
226 {
227 d_zones.clear();
228 }
229 const std::shared_ptr<Zone> getZone(size_t zoneIdx) const
230 {
231 std::shared_ptr<Zone> result{nullptr};
232 if (zoneIdx < d_zones.size()) {
233 result = d_zones[zoneIdx];
234 }
235 return result;
236 }
237 const std::shared_ptr<Zone> getZone(const std::string& name) const
238 {
239 for (const auto zone : d_zones) {
240 const auto& zName = zone->getName();
241 if (zName && *zName == name) {
242 return zone;
243 }
244 }
245 return nullptr;
246 }
247 size_t addZone(std::shared_ptr<Zone> newZone)
248 {
249 d_zones.push_back(newZone);
250 return (d_zones.size() - 1);
251 }
252 void setZone(size_t zoneIdx, std::shared_ptr<Zone> newZone)
253 {
254 if (newZone) {
255 assureZones(zoneIdx);
256 d_zones[zoneIdx] = newZone;
257 }
258 }
259
260 Policy getQueryPolicy(const DNSName& qname, const ComboAddress& nm, const std::unordered_map<std::string,bool>& discardedPolicies) const;
261 Policy getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies) const;
262 Policy getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string,bool>& discardedPolicies) const;
263 Policy getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies) const;
264
265 size_t size() const {
266 return d_zones.size();
267 }
268 private:
269 void assureZones(size_t zone);
270 vector<std::shared_ptr<Zone>> d_zones;
271 };