]>
Commit | Line | Data |
---|---|---|
12471842 PL |
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 | */ | |
644dd1da | 22 | #pragma once |
23 | #include "iputils.hh" | |
24 | #include "dns.hh" | |
6791663c | 25 | #include "dnsname.hh" |
39ec5d29 | 26 | #include "dnsparser.hh" |
644dd1da | 27 | #include <map> |
0a273054 | 28 | #include <unordered_map> |
e37e5795 | 29 | #include <limits> |
644dd1da | 30 | |
31 | /* This class implements a filtering policy that is able to fully implement RPZ, but is not bound to it. | |
32 | In other words, it is generic enough to support RPZ, but could get its data from other places. | |
33 | ||
34 | ||
35 | We know the following actions: | |
6791663c | 36 | |
644dd1da | 37 | No action - just pass it on |
38 | Drop - drop a query, no response | |
6791663c | 39 | NXDOMAIN - fake up an NXDOMAIN for the query |
644dd1da | 40 | NODATA - just return no data for this qtype |
41 | Truncate - set TC bit | |
42 | Modified - "we fake an answer for you" | |
43 | ||
44 | These actions can be caused by the following triggers: | |
6791663c | 45 | |
644dd1da | 46 | qname - the query name |
47 | client-ip - the IP address of the requestor | |
48 | response-ip - an IP address in the response | |
49 | ns-name - the name of a server used in the delegation | |
50 | ns-ip - the IP address of a server used in the delegation | |
51 | ||
52 | This means we get several hook points: | |
53 | 1) when the query comes in: qname & client-ip | |
54 | 2) during processing: ns-name & ns-ip | |
55 | 3) after processing: response-ip | |
56 | ||
57 | Triggers meanwhile can apply to: | |
58 | Verbatim domain names | |
59 | Wildcard versions (*.domain.com does NOT match domain.com) | |
60 | Netmasks (IPv4 and IPv6) | |
644dd1da | 61 | Finally, triggers are grouped in different zones. The "first" zone that has a match |
6791663c | 62 | is consulted. Then within that zone, rules again have precedences. |
644dd1da | 63 | */ |
64 | ||
65 | ||
66 | class DNSFilterEngine | |
67 | { | |
68 | public: | |
e37e5795 OM |
69 | enum class PolicyKind : uint8_t { NoAction, Drop, NXDOMAIN, NODATA, Truncate, Custom}; |
70 | enum class PolicyType : uint8_t { None, QName, ClientIP, ResponseIP, NSDName, NSIP }; | |
71 | typedef uint16_t Priority; | |
72 | static const Priority maximumPriority = std::numeric_limits<Priority>::max(); | |
73 | ||
6da513b2 RG |
74 | static std::string getKindToString(PolicyKind kind); |
75 | static std::string getTypeToString(PolicyType type); | |
76 | ||
39ec5d29 | 77 | struct Policy |
78 | { | |
e37e5795 | 79 | Policy(): d_name(nullptr), d_ttl(0), d_priority(maximumPriority), d_kind(PolicyKind::NoAction), d_type(PolicyType::None) |
1f1ca368 RG |
80 | { |
81 | } | |
6da513b2 | 82 | |
e37e5795 | 83 | 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_ttl(ttl), d_priority(maximumPriority), d_kind(kind), d_type(type) |
6da513b2 RG |
84 | { |
85 | } | |
86 | ||
39ec5d29 | 87 | bool operator==(const Policy& rhs) const |
88 | { | |
6da513b2 | 89 | return d_kind == rhs.d_kind && d_type == rhs.d_type && d_ttl == rhs.d_ttl && d_custom == rhs.d_custom; |
39ec5d29 | 90 | } |
6da513b2 RG |
91 | std::vector<DNSRecord> getCustomRecords(const DNSName& qname, uint16_t qtype) const; |
92 | std::vector<DNSRecord> getRecords(const DNSName& qname) const; | |
6791663c | 93 | |
6da513b2 RG |
94 | std::vector<std::shared_ptr<DNSRecordContent>> d_custom; |
95 | std::shared_ptr<std::string> d_name; // the name of the policy | |
6da513b2 | 96 | /* Yup, we are currently using the same TTL for every record for a given name */ |
8f618901 | 97 | int32_t d_ttl; |
e37e5795 OM |
98 | Priority d_priority; |
99 | PolicyKind d_kind; | |
100 | PolicyType d_type; | |
6da513b2 RG |
101 | |
102 | private: | |
103 | DNSRecord getRecordFromCustom(const DNSName& qname, const std::shared_ptr<DNSRecordContent>& custom) const; | |
e37e5795 | 104 | }; |
644dd1da | 105 | |
6b972d59 RG |
106 | class Zone { |
107 | public: | |
108 | void clear() | |
109 | { | |
110 | d_qpolAddr.clear(); | |
111 | d_postpolAddr.clear(); | |
112 | d_propolName.clear(); | |
598ac803 | 113 | d_propolNSAddr.clear(); |
6b972d59 RG |
114 | d_qpolName.clear(); |
115 | } | |
116 | void reserve(size_t entriesCount) | |
117 | { | |
118 | d_qpolName.reserve(entriesCount); | |
119 | } | |
120 | void setName(const std::string& name) | |
121 | { | |
122 | d_name = std::make_shared<std::string>(name); | |
123 | } | |
6791663c RG |
124 | void setDomain(const DNSName& domain) |
125 | { | |
126 | d_domain = domain; | |
127 | } | |
128 | void setSerial(uint32_t serial) | |
129 | { | |
130 | d_serial = serial; | |
131 | } | |
132 | void setRefresh(uint32_t refresh) | |
133 | { | |
134 | d_refresh = refresh; | |
135 | } | |
6b972d59 RG |
136 | const std::shared_ptr<std::string> getName() const |
137 | { | |
138 | return d_name; | |
139 | } | |
4ba9d5dc RG |
140 | |
141 | DNSName getDomain() const | |
209955c7 | 142 | { |
143 | return d_domain; | |
144 | } | |
4ba9d5dc RG |
145 | |
146 | uint32_t getRefresh() const | |
209955c7 | 147 | { |
148 | return d_refresh; | |
149 | } | |
4ba9d5dc | 150 | |
6da513b2 RG |
151 | uint32_t getSerial() const |
152 | { | |
153 | return d_serial; | |
154 | } | |
155 | ||
4ba9d5dc RG |
156 | size_t size() const |
157 | { | |
158 | return d_qpolAddr.size() + d_postpolAddr.size() + d_propolName.size() + d_propolNSAddr.size() + d_qpolName.size(); | |
4ba9d5dc RG |
159 | } |
160 | ||
6791663c | 161 | void dump(FILE * fp) const; |
644dd1da | 162 | |
6da513b2 | 163 | void addClientTrigger(const Netmask& nm, Policy&& pol); |
d122dac0 | 164 | void addQNameTrigger(const DNSName& nm, Policy&& pol, bool ignoreDuplicate=false); |
6da513b2 RG |
165 | void addNSTrigger(const DNSName& dn, Policy&& pol); |
166 | void addNSIPTrigger(const Netmask& nm, Policy&& pol); | |
167 | void addResponseTrigger(const Netmask& nm, Policy&& pol); | |
168 | ||
169 | bool rmClientTrigger(const Netmask& nm, const Policy& pol); | |
170 | bool rmQNameTrigger(const DNSName& nm, const Policy& pol); | |
171 | bool rmNSTrigger(const DNSName& dn, const Policy& pol); | |
172 | bool rmNSIPTrigger(const Netmask& nm, const Policy& pol); | |
173 | bool rmResponseTrigger(const Netmask& nm, const Policy& pol); | |
6b972d59 | 174 | |
272e9a00 | 175 | bool findExactQNamePolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const; |
272e9a00 | 176 | bool findExactNSPolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const; |
6791663c RG |
177 | bool findNSIPPolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const; |
178 | bool findResponsePolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const; | |
179 | bool findClientPolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const; | |
180 | ||
272e9a00 RG |
181 | bool hasClientPolicies() const |
182 | { | |
183 | return !d_qpolAddr.empty(); | |
184 | } | |
185 | bool hasQNamePolicies() const | |
186 | { | |
187 | return !d_qpolName.empty(); | |
188 | } | |
189 | bool hasNSPolicies() const | |
190 | { | |
191 | return !d_propolName.empty(); | |
192 | } | |
193 | bool hasNSIPPolicies() const | |
194 | { | |
195 | return !d_propolNSAddr.empty(); | |
196 | } | |
197 | bool hasResponsePolicies() const | |
198 | { | |
199 | return !d_postpolAddr.empty(); | |
200 | } | |
e37e5795 OM |
201 | Priority getPriority() const { |
202 | return d_priority; | |
203 | } | |
204 | void setPriority(Priority p) { | |
205 | d_priority = p; | |
63464fee OM |
206 | for (auto& pair : d_qpolName) { |
207 | pair.second.d_priority = p; | |
79f64c2b | 208 | } |
63464fee OM |
209 | for (auto& pair : d_propolName) { |
210 | pair.second.d_priority = p; | |
79f64c2b | 211 | } |
9bb0af03 OM |
212 | for (auto& pair : d_qpolAddr) { |
213 | pair.second.d_priority = p; | |
79f64c2b | 214 | } |
9bb0af03 OM |
215 | for (auto& pair : d_propolNSAddr) { |
216 | pair.second.d_priority = p; | |
79f64c2b | 217 | } |
9bb0af03 OM |
218 | for (auto& pair : d_postpolAddr) { |
219 | pair.second.d_priority = p; | |
79f64c2b | 220 | } |
e37e5795 | 221 | } |
6791663c RG |
222 | private: |
223 | static DNSName maskToRPZ(const Netmask& nm); | |
272e9a00 RG |
224 | static bool findExactNamedPolicy(const std::unordered_map<DNSName, DNSFilterEngine::Policy>& polmap, const DNSName& qname, DNSFilterEngine::Policy& pol); |
225 | static bool findNamedPolicy(const std::unordered_map<DNSName, DNSFilterEngine::Policy>& polmap, const DNSName& qname, DNSFilterEngine::Policy& pol); | |
226 | static void dumpNamedPolicy(FILE* fp, const DNSName& name, const Policy& pol); | |
227 | static void dumpAddrPolicy(FILE* fp, const Netmask& nm, const DNSName& name, const Policy& pol); | |
6791663c | 228 | |
6b972d59 RG |
229 | std::unordered_map<DNSName, Policy> d_qpolName; // QNAME trigger (RPZ) |
230 | NetmaskTree<Policy> d_qpolAddr; // Source address | |
231 | std::unordered_map<DNSName, Policy> d_propolName; // NSDNAME (RPZ) | |
232 | NetmaskTree<Policy> d_propolNSAddr; // NSIP (RPZ) | |
233 | NetmaskTree<Policy> d_postpolAddr; // IP trigger (RPZ) | |
6791663c | 234 | DNSName d_domain; |
6b972d59 | 235 | std::shared_ptr<std::string> d_name; |
6791663c RG |
236 | uint32_t d_serial{0}; |
237 | uint32_t d_refresh{0}; | |
e37e5795 | 238 | Priority d_priority; |
6b972d59 RG |
239 | }; |
240 | ||
241 | DNSFilterEngine(); | |
242 | void clear() | |
243 | { | |
244 | for(auto& z : d_zones) { | |
245 | z->clear(); | |
246 | } | |
247 | } | |
272e9a00 RG |
248 | void clearZones() |
249 | { | |
250 | d_zones.clear(); | |
251 | } | |
6b972d59 RG |
252 | const std::shared_ptr<Zone> getZone(size_t zoneIdx) const |
253 | { | |
254 | std::shared_ptr<Zone> result{nullptr}; | |
255 | if (zoneIdx < d_zones.size()) { | |
256 | result = d_zones[zoneIdx]; | |
257 | } | |
258 | return result; | |
259 | } | |
6791663c RG |
260 | const std::shared_ptr<Zone> getZone(const std::string& name) const |
261 | { | |
262 | for (const auto zone : d_zones) { | |
263 | const auto& zName = zone->getName(); | |
264 | if (zName && *zName == name) { | |
265 | return zone; | |
266 | } | |
267 | } | |
268 | return nullptr; | |
269 | } | |
6b972d59 RG |
270 | size_t addZone(std::shared_ptr<Zone> newZone) |
271 | { | |
e37e5795 | 272 | newZone->setPriority(d_zones.size()); |
6b972d59 RG |
273 | d_zones.push_back(newZone); |
274 | return (d_zones.size() - 1); | |
275 | } | |
276 | void setZone(size_t zoneIdx, std::shared_ptr<Zone> newZone) | |
277 | { | |
278 | if (newZone) { | |
279 | assureZones(zoneIdx); | |
e37e5795 | 280 | newZone->setPriority(zoneIdx); |
6b972d59 RG |
281 | d_zones[zoneIdx] = newZone; |
282 | } | |
283 | } | |
39ec5d29 | 284 | |
2996400c OM |
285 | bool getQueryPolicy(const DNSName& qname, const ComboAddress& nm, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& policy) const; |
286 | bool getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& policy) const; | |
287 | bool getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& policy) const; | |
288 | bool getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& policy) const; | |
289 | ||
290 | // A few convenience methods for the unit test code | |
291 | Policy getQueryPolicy(const DNSName& qname, const ComboAddress& nm, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const { | |
292 | Policy policy; | |
293 | policy.d_priority = p; | |
294 | getQueryPolicy(qname, nm, discardedPolicies, policy); | |
295 | return policy; | |
296 | } | |
297 | ||
298 | Policy getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const { | |
299 | Policy policy; | |
300 | policy.d_priority = p; | |
301 | getProcessingPolicy(qname, discardedPolicies, policy); | |
302 | return policy; | |
303 | } | |
304 | ||
305 | Policy getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const { | |
306 | Policy policy; | |
307 | policy.d_priority = p; | |
308 | getProcessingPolicy(address, discardedPolicies, policy); | |
309 | return policy; | |
310 | } | |
311 | ||
312 | Policy getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies, Priority p) const { | |
313 | Policy policy; | |
314 | policy.d_priority = p; | |
315 | getPostPolicy(records, discardedPolicies, policy); | |
316 | return policy; | |
317 | } | |
644dd1da | 318 | |
6b972d59 | 319 | size_t size() const { |
0e760497 | 320 | return d_zones.size(); |
321 | } | |
644dd1da | 322 | private: |
0a273054 | 323 | void assureZones(size_t zone); |
6b972d59 | 324 | vector<std::shared_ptr<Zone>> d_zones; |
644dd1da | 325 | }; |