]>
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> |
644dd1da | 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: | |
6791663c | 35 | |
644dd1da | 36 | No action - just pass it on |
37 | Drop - drop a query, no response | |
6791663c | 38 | NXDOMAIN - fake up an NXDOMAIN for the query |
644dd1da | 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: | |
6791663c | 44 | |
644dd1da | 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) | |
644dd1da | 60 | Finally, triggers are grouped in different zones. The "first" zone that has a match |
6791663c | 61 | is consulted. Then within that zone, rules again have precedences. |
644dd1da | 62 | */ |
63 | ||
64 | ||
65 | class DNSFilterEngine | |
66 | { | |
67 | public: | |
39ec5d29 | 68 | enum class PolicyKind { NoAction, Drop, NXDOMAIN, NODATA, Truncate, Custom}; |
f3da83fe RG |
69 | enum class PolicyType { None, QName, ClientIP, ResponseIP, NSDName, NSIP }; |
70 | ||
6da513b2 RG |
71 | static std::string getKindToString(PolicyKind kind); |
72 | static std::string getTypeToString(PolicyType type); | |
73 | ||
39ec5d29 | 74 | struct Policy |
75 | { | |
6da513b2 | 76 | Policy(): d_name(nullptr), d_kind(PolicyKind::NoAction), d_type(PolicyType::None), d_ttl(0) |
1f1ca368 RG |
77 | { |
78 | } | |
6da513b2 RG |
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 | ||
39ec5d29 | 84 | bool operator==(const Policy& rhs) const |
85 | { | |
6da513b2 | 86 | return d_kind == rhs.d_kind && d_type == rhs.d_type && d_ttl == rhs.d_ttl && d_custom == rhs.d_custom; |
39ec5d29 | 87 | } |
6da513b2 RG |
88 | std::vector<DNSRecord> getCustomRecords(const DNSName& qname, uint16_t qtype) const; |
89 | std::vector<DNSRecord> getRecords(const DNSName& qname) const; | |
6791663c | 90 | |
6da513b2 RG |
91 | std::vector<std::shared_ptr<DNSRecordContent>> d_custom; |
92 | std::shared_ptr<std::string> d_name; // the name of the policy | |
f3da83fe RG |
93 | PolicyKind d_kind; |
94 | PolicyType d_type; | |
6da513b2 | 95 | /* Yup, we are currently using the same TTL for every record for a given name */ |
8f618901 | 96 | int32_t d_ttl; |
6da513b2 RG |
97 | |
98 | private: | |
99 | DNSRecord getRecordFromCustom(const DNSName& qname, const std::shared_ptr<DNSRecordContent>& custom) const; | |
100 | }; | |
644dd1da | 101 | |
6b972d59 RG |
102 | class Zone { |
103 | public: | |
104 | void clear() | |
105 | { | |
106 | d_qpolAddr.clear(); | |
107 | d_postpolAddr.clear(); | |
108 | d_propolName.clear(); | |
598ac803 | 109 | d_propolNSAddr.clear(); |
6b972d59 RG |
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 | } | |
6791663c RG |
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 | } | |
6b972d59 RG |
132 | const std::shared_ptr<std::string> getName() const |
133 | { | |
134 | return d_name; | |
135 | } | |
4ba9d5dc RG |
136 | |
137 | DNSName getDomain() const | |
209955c7 | 138 | { |
139 | return d_domain; | |
140 | } | |
4ba9d5dc RG |
141 | |
142 | uint32_t getRefresh() const | |
209955c7 | 143 | { |
144 | return d_refresh; | |
145 | } | |
4ba9d5dc | 146 | |
6da513b2 RG |
147 | uint32_t getSerial() const |
148 | { | |
149 | return d_serial; | |
150 | } | |
151 | ||
4ba9d5dc RG |
152 | size_t size() const |
153 | { | |
154 | return d_qpolAddr.size() + d_postpolAddr.size() + d_propolName.size() + d_propolNSAddr.size() + d_qpolName.size(); | |
4ba9d5dc RG |
155 | } |
156 | ||
6791663c | 157 | void dump(FILE * fp) const; |
644dd1da | 158 | |
6da513b2 | 159 | void addClientTrigger(const Netmask& nm, Policy&& pol); |
d122dac0 | 160 | void addQNameTrigger(const DNSName& nm, Policy&& pol, bool ignoreDuplicate=false); |
6da513b2 RG |
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); | |
6b972d59 | 170 | |
272e9a00 | 171 | bool findExactQNamePolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const; |
272e9a00 | 172 | bool findExactNSPolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const; |
6791663c RG |
173 | bool findNSIPPolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const; |
174 | bool findResponsePolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const; | |
175 | bool findClientPolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const; | |
176 | ||
272e9a00 RG |
177 | bool hasClientPolicies() const |
178 | { | |
179 | return !d_qpolAddr.empty(); | |
180 | } | |
181 | bool hasQNamePolicies() const | |
182 | { | |
183 | return !d_qpolName.empty(); | |
184 | } | |
185 | bool hasNSPolicies() const | |
186 | { | |
187 | return !d_propolName.empty(); | |
188 | } | |
189 | bool hasNSIPPolicies() const | |
190 | { | |
191 | return !d_propolNSAddr.empty(); | |
192 | } | |
193 | bool hasResponsePolicies() const | |
194 | { | |
195 | return !d_postpolAddr.empty(); | |
196 | } | |
197 | ||
6791663c RG |
198 | private: |
199 | static DNSName maskToRPZ(const Netmask& nm); | |
272e9a00 RG |
200 | static bool findExactNamedPolicy(const std::unordered_map<DNSName, DNSFilterEngine::Policy>& polmap, const DNSName& qname, DNSFilterEngine::Policy& pol); |
201 | static bool findNamedPolicy(const std::unordered_map<DNSName, DNSFilterEngine::Policy>& polmap, const DNSName& qname, DNSFilterEngine::Policy& pol); | |
202 | static void dumpNamedPolicy(FILE* fp, const DNSName& name, const Policy& pol); | |
203 | static void dumpAddrPolicy(FILE* fp, const Netmask& nm, const DNSName& name, const Policy& pol); | |
6791663c | 204 | |
6b972d59 RG |
205 | std::unordered_map<DNSName, Policy> d_qpolName; // QNAME trigger (RPZ) |
206 | NetmaskTree<Policy> d_qpolAddr; // Source address | |
207 | std::unordered_map<DNSName, Policy> d_propolName; // NSDNAME (RPZ) | |
208 | NetmaskTree<Policy> d_propolNSAddr; // NSIP (RPZ) | |
209 | NetmaskTree<Policy> d_postpolAddr; // IP trigger (RPZ) | |
6791663c | 210 | DNSName d_domain; |
6b972d59 | 211 | std::shared_ptr<std::string> d_name; |
6791663c RG |
212 | uint32_t d_serial{0}; |
213 | uint32_t d_refresh{0}; | |
6b972d59 RG |
214 | }; |
215 | ||
216 | DNSFilterEngine(); | |
217 | void clear() | |
218 | { | |
219 | for(auto& z : d_zones) { | |
220 | z->clear(); | |
221 | } | |
222 | } | |
272e9a00 RG |
223 | void clearZones() |
224 | { | |
225 | d_zones.clear(); | |
226 | } | |
6b972d59 RG |
227 | const std::shared_ptr<Zone> getZone(size_t zoneIdx) const |
228 | { | |
229 | std::shared_ptr<Zone> result{nullptr}; | |
230 | if (zoneIdx < d_zones.size()) { | |
231 | result = d_zones[zoneIdx]; | |
232 | } | |
233 | return result; | |
234 | } | |
6791663c RG |
235 | const std::shared_ptr<Zone> getZone(const std::string& name) const |
236 | { | |
237 | for (const auto zone : d_zones) { | |
238 | const auto& zName = zone->getName(); | |
239 | if (zName && *zName == name) { | |
240 | return zone; | |
241 | } | |
242 | } | |
243 | return nullptr; | |
244 | } | |
6b972d59 RG |
245 | size_t addZone(std::shared_ptr<Zone> newZone) |
246 | { | |
247 | d_zones.push_back(newZone); | |
248 | return (d_zones.size() - 1); | |
249 | } | |
250 | void setZone(size_t zoneIdx, std::shared_ptr<Zone> newZone) | |
251 | { | |
252 | if (newZone) { | |
253 | assureZones(zoneIdx); | |
254 | d_zones[zoneIdx] = newZone; | |
255 | } | |
256 | } | |
39ec5d29 | 257 | |
0a273054 RG |
258 | Policy getQueryPolicy(const DNSName& qname, const ComboAddress& nm, const std::unordered_map<std::string,bool>& discardedPolicies) const; |
259 | Policy getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies) const; | |
260 | Policy getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string,bool>& discardedPolicies) const; | |
261 | Policy getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies) const; | |
644dd1da | 262 | |
6b972d59 | 263 | size_t size() const { |
0e760497 | 264 | return d_zones.size(); |
265 | } | |
644dd1da | 266 | private: |
0a273054 | 267 | void assureZones(size_t zone); |
6b972d59 | 268 | vector<std::shared_ptr<Zone>> d_zones; |
644dd1da | 269 | }; |