]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/filterpo.cc
dnsbulktest: Explicitely check that find() returned 0
[thirdparty/pdns.git] / pdns / filterpo.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
23 #include <cinttypes>
24 #include <iostream>
25
26 #include "filterpo.hh"
27 #include "namespaces.hh"
28 #include "dnsrecords.hh"
29
30 DNSFilterEngine::DNSFilterEngine()
31 {
32 }
33
34 bool DNSFilterEngine::Zone::findQNamePolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const
35 {
36 return findNamedPolicy(d_qpolName, qname, pol);
37 }
38
39 bool DNSFilterEngine::Zone::findNSPolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const
40 {
41 return findNamedPolicy(d_propolName, qname, pol);
42 }
43
44 bool DNSFilterEngine::Zone::findNSIPPolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const
45 {
46 if (const auto fnd = d_propolNSAddr.lookup(addr)) {
47 pol = fnd->second;
48 return true;
49 }
50 return false;
51 }
52
53 bool DNSFilterEngine::Zone::findResponsePolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const
54 {
55 if (const auto fnd = d_postpolAddr.lookup(addr)) {
56 pol = fnd->second;
57 return true;
58 }
59 return false;
60 }
61
62 bool DNSFilterEngine::Zone::findClientPolicy(const ComboAddress& addr, DNSFilterEngine::Policy& pol) const
63 {
64 if (const auto fnd = d_qpolAddr.lookup(addr)) {
65 pol = fnd->second;
66 return true;
67 }
68 return false;
69 }
70
71 bool DNSFilterEngine::Zone::findNamedPolicy(const std::unordered_map<DNSName, DNSFilterEngine::Policy>& polmap, const DNSName& qname, DNSFilterEngine::Policy& pol) const
72 {
73 /* for www.powerdns.com, we need to check:
74 www.powerdns.com.
75 *.powerdns.com.
76 *.com.
77 *.
78 */
79
80 std::unordered_map<DNSName, DNSFilterEngine::Policy>::const_iterator iter;
81 iter = polmap.find(qname);
82
83 if(iter != polmap.end()) {
84 pol=iter->second;
85 return true;
86 }
87
88 DNSName s(qname);
89 while(s.chopOff()){
90 iter = polmap.find(g_wildcarddnsname+s);
91 if(iter != polmap.end()) {
92 pol=iter->second;
93 return true;
94 }
95 }
96 return false;
97 }
98
99 DNSFilterEngine::Policy DNSFilterEngine::getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies) const
100 {
101 // cout<<"Got question for nameserver name "<<qname<<endl;
102 Policy pol;
103 for(const auto& z : d_zones) {
104 const auto zoneName = z->getName();
105 if(zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
106 continue;
107 }
108
109 if(z->findNSPolicy(qname, pol)) {
110 // cerr<<"Had a hit on the nameserver ("<<qname<<") used to process the query"<<endl;
111 return pol;
112 }
113 }
114 return pol;
115 }
116
117 DNSFilterEngine::Policy DNSFilterEngine::getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string,bool>& discardedPolicies) const
118 {
119 Policy pol;
120 // cout<<"Got question for nameserver IP "<<address.toString()<<endl;
121 for(const auto& z : d_zones) {
122 const auto zoneName = z->getName();
123 if(zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
124 continue;
125 }
126
127 if(z->findNSIPPolicy(address, pol)) {
128 // cerr<<"Had a hit on the nameserver ("<<address.toString()<<") used to process the query"<<endl;
129 return pol;
130 }
131 }
132 return pol;
133 }
134
135 DNSFilterEngine::Policy DNSFilterEngine::getQueryPolicy(const DNSName& qname, const ComboAddress& ca, const std::unordered_map<std::string,bool>& discardedPolicies) const
136 {
137 // cout<<"Got question for "<<qname<<" from "<<ca.toString()<<endl;
138 Policy pol;
139 for(const auto& z : d_zones) {
140 const auto zoneName = z->getName();
141 if(zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
142 continue;
143 }
144
145 if(z->findQNamePolicy(qname, pol)) {
146 // cerr<<"Had a hit on the name of the query"<<endl;
147 return pol;
148 }
149
150 if(z->findClientPolicy(ca, pol)) {
151 // cerr<<"Had a hit on the IP address ("<<ca.toString()<<") of the client"<<endl;
152 return pol;
153 }
154 }
155
156 return pol;
157 }
158
159 DNSFilterEngine::Policy DNSFilterEngine::getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies) const
160 {
161 Policy pol;
162 ComboAddress ca;
163 for(const auto& r : records) {
164 if(r.d_place != DNSResourceRecord::ANSWER)
165 continue;
166 if(r.d_type == QType::A) {
167 if (auto rec = getRR<ARecordContent>(r)) {
168 ca = rec->getCA();
169 }
170 }
171 else if(r.d_type == QType::AAAA) {
172 if (auto rec = getRR<AAAARecordContent>(r)) {
173 ca = rec->getCA();
174 }
175 }
176 else
177 continue;
178
179 for(const auto& z : d_zones) {
180 const auto zoneName = z->getName();
181 if(zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
182 continue;
183 }
184
185 if(z->findResponsePolicy(ca, pol)) {
186 return pol;
187 }
188 }
189 }
190 return pol;
191 }
192
193 void DNSFilterEngine::assureZones(size_t zone)
194 {
195 if(d_zones.size() <= zone)
196 d_zones.resize(zone+1);
197 }
198
199 void DNSFilterEngine::Zone::addClientTrigger(const Netmask& nm, Policy&& pol)
200 {
201 pol.d_name = d_name;
202 pol.d_type = PolicyType::ClientIP;
203 d_qpolAddr.insert(nm).second=std::move(pol);
204 }
205
206 void DNSFilterEngine::Zone::addResponseTrigger(const Netmask& nm, Policy&& pol)
207 {
208 pol.d_name = d_name;
209 pol.d_type = PolicyType::ResponseIP;
210 d_postpolAddr.insert(nm).second=std::move(pol);
211 }
212
213 void DNSFilterEngine::Zone::addQNameTrigger(const DNSName& n, Policy&& pol, bool ignoreDuplicate)
214 {
215 auto it = d_qpolName.find(n);
216
217 if (it != d_qpolName.end()) {
218 auto& existingPol = it->second;
219
220 if (pol.d_kind != PolicyKind::Custom && !ignoreDuplicate) {
221 throw std::runtime_error("Adding a QName-based filter policy of kind " + getKindToString(pol.d_kind) + " but a policy of kind " + getKindToString(existingPol.d_kind) + " already exists for the following QName: " + n.toLogString());
222 }
223
224 if (existingPol.d_kind != PolicyKind::Custom && ignoreDuplicate) {
225 throw std::runtime_error("Adding a QName-based filter policy of kind " + getKindToString(existingPol.d_kind) + " but there was already an existing policy for the following QName: " + n.toLogString());
226 }
227
228 existingPol.d_custom.reserve(existingPol.d_custom.size() + pol.d_custom.size());
229
230 std::move(pol.d_custom.begin(), pol.d_custom.end(), std::back_inserter(existingPol.d_custom));
231 }
232 else {
233 auto& qpol = d_qpolName.insert({n, std::move(pol)}).first->second;
234 qpol.d_name = d_name;
235 qpol.d_type = PolicyType::QName;
236 }
237 }
238
239 void DNSFilterEngine::Zone::addNSTrigger(const DNSName& n, Policy&& pol)
240 {
241 pol.d_name = d_name;
242 pol.d_type = PolicyType::NSDName;
243 d_propolName.insert({n, std::move(pol)});
244 }
245
246 void DNSFilterEngine::Zone::addNSIPTrigger(const Netmask& nm, Policy&& pol)
247 {
248 pol.d_name = d_name;
249 pol.d_type = PolicyType::NSIP;
250 d_propolNSAddr.insert(nm).second = std::move(pol);
251 }
252
253 bool DNSFilterEngine::Zone::rmClientTrigger(const Netmask& nm, const Policy& pol)
254 {
255 d_qpolAddr.erase(nm);
256 return true;
257 }
258
259 bool DNSFilterEngine::Zone::rmResponseTrigger(const Netmask& nm, const Policy& pol)
260 {
261 d_postpolAddr.erase(nm);
262 return true;
263 }
264
265 bool DNSFilterEngine::Zone::rmQNameTrigger(const DNSName& n, const Policy& pol)
266 {
267 auto it = d_qpolName.find(n);
268 if (it == d_qpolName.end()) {
269 return false;
270 }
271
272 auto& existing = it->second;
273 if (existing.d_kind != DNSFilterEngine::PolicyKind::Custom) {
274 d_qpolName.erase(it);
275 return true;
276 }
277
278 /* for custom types, we might have more than one type,
279 and then we need to remove only the right ones. */
280 if (existing.d_custom.size() <= 1) {
281 d_qpolName.erase(it);
282 return true;
283 }
284
285 bool result = false;
286 for (auto& toRemove : pol.d_custom) {
287 for (auto it = existing.d_custom.begin(); it != existing.d_custom.end(); ++it) {
288 if (**it == *toRemove) {
289 existing.d_custom.erase(it);
290 result = true;
291 break;
292 }
293 }
294 }
295
296 return result;
297 }
298
299 bool DNSFilterEngine::Zone::rmNSTrigger(const DNSName& n, const Policy& pol)
300 {
301 d_propolName.erase(n); // XXX verify policy matched? =pol;
302 return true;
303 }
304
305 bool DNSFilterEngine::Zone::rmNSIPTrigger(const Netmask& nm, const Policy& pol)
306 {
307 d_propolNSAddr.erase(nm);
308 return true;
309 }
310
311 DNSRecord DNSFilterEngine::Policy::getRecordFromCustom(const DNSName& qname, const std::shared_ptr<DNSRecordContent>& custom) const
312 {
313 DNSRecord dr;
314 dr.d_name = qname;
315 dr.d_type = custom->getType();
316 dr.d_ttl = d_ttl;
317 dr.d_class = QClass::IN;
318 dr.d_place = DNSResourceRecord::ANSWER;
319 dr.d_content = custom;
320
321 if (dr.d_type == QType::CNAME) {
322 const auto content = std::dynamic_pointer_cast<CNAMERecordContent>(custom);
323 if (content) {
324 DNSName target = content->getTarget();
325 if (target.isWildcard()) {
326 target.chopOff();
327 dr.d_content = std::make_shared<CNAMERecordContent>(qname + target);
328 }
329 }
330 }
331
332 return dr;
333 }
334
335 std::vector<DNSRecord> DNSFilterEngine::Policy::getCustomRecords(const DNSName& qname, uint16_t qtype) const
336 {
337 if (d_kind != PolicyKind::Custom) {
338 throw std::runtime_error("Asking for a custom record from a filtering policy of a non-custom type");
339 }
340
341 std::vector<DNSRecord> result;
342
343 for (const auto& custom : d_custom) {
344 if (qtype != QType::ANY && qtype != custom->getType() && custom->getType() != QType::CNAME) {
345 continue;
346 }
347
348 DNSRecord dr;
349 dr.d_name = qname;
350 dr.d_type = custom->getType();
351 dr.d_ttl = d_ttl;
352 dr.d_class = QClass::IN;
353 dr.d_place = DNSResourceRecord::ANSWER;
354 dr.d_content = custom;
355
356 if (dr.d_type == QType::CNAME) {
357 const auto content = std::dynamic_pointer_cast<CNAMERecordContent>(custom);
358 if (content) {
359 DNSName target = content->getTarget();
360 if (target.isWildcard()) {
361 target.chopOff();
362 dr.d_content = std::make_shared<CNAMERecordContent>(qname + target);
363 }
364 }
365 }
366
367 result.emplace_back(getRecordFromCustom(qname, custom));
368 }
369
370 return result;
371 }
372
373 std::string DNSFilterEngine::getKindToString(DNSFilterEngine::PolicyKind kind)
374 {
375 static const DNSName drop("rpz-drop."), truncate("rpz-tcp-only."), noaction("rpz-passthru.");
376 static const DNSName rpzClientIP("rpz-client-ip"), rpzIP("rpz-ip"),
377 rpzNSDname("rpz-nsdname"), rpzNSIP("rpz-nsip.");
378 static const std::string rpzPrefix("rpz-");
379
380 switch(kind) {
381 case DNSFilterEngine::PolicyKind::NoAction:
382 return noaction.toString();
383 case DNSFilterEngine::PolicyKind::Drop:
384 return drop.toString();
385 case DNSFilterEngine::PolicyKind::NXDOMAIN:
386 return g_rootdnsname.toString();
387 case PolicyKind::NODATA:
388 return g_wildcarddnsname.toString();
389 case DNSFilterEngine::PolicyKind::Truncate:
390 return truncate.toString();
391 default:
392 throw std::runtime_error("Unexpected DNSFilterEngine::Policy kind");
393 }
394 }
395
396 std::string DNSFilterEngine::getTypeToString(DNSFilterEngine::PolicyType type)
397 {
398 switch(type) {
399 case DNSFilterEngine::PolicyType::None:
400 return "none";
401 case DNSFilterEngine::PolicyType::QName:
402 return "QName";
403 case DNSFilterEngine::PolicyType::ClientIP:
404 return "Client IP";
405 case DNSFilterEngine::PolicyType::ResponseIP:
406 return "Response IP";
407 case DNSFilterEngine::PolicyType::NSDName:
408 return "Name Server Name";
409 case DNSFilterEngine::PolicyType::NSIP:
410 return "Name Server IP";
411 default:
412 throw std::runtime_error("Unexpected DNSFilterEngine::Policy type");
413 }
414 }
415
416 std::vector<DNSRecord> DNSFilterEngine::Policy::getRecords(const DNSName& qname) const
417 {
418 std::vector<DNSRecord> result;
419
420 if (d_kind == PolicyKind::Custom) {
421 result = getCustomRecords(qname, QType::ANY);
422 }
423 else {
424 DNSRecord dr;
425 dr.d_name = qname;
426 dr.d_ttl = static_cast<uint32_t>(d_ttl);
427 dr.d_type = QType::CNAME;
428 dr.d_class = QClass::IN;
429 dr.d_content = DNSRecordContent::mastermake(QType::CNAME, QClass::IN, getKindToString(d_kind));
430 result.push_back(std::move(dr));
431 }
432
433 return result;
434 }
435
436 void DNSFilterEngine::Zone::dumpNamedPolicy(FILE* fp, const DNSName& name, const Policy& pol) const
437 {
438 auto records = pol.getRecords(name);
439 for (const auto& dr : records) {
440 fprintf(fp, "%s %" PRIu32 " IN %s %s\n", dr.d_name.toString().c_str(), dr.d_ttl, QType(dr.d_type).getName().c_str(), dr.d_content->getZoneRepresentation().c_str());
441 }
442 }
443
444 DNSName DNSFilterEngine::Zone::maskToRPZ(const Netmask& nm)
445 {
446 int bits = nm.getBits();
447 DNSName res(std::to_string(bits));
448 const auto& addr = nm.getNetwork();
449
450 if (addr.isIPv4()) {
451 const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&addr.sin4.sin_addr.s_addr);
452 res += DNSName(std::to_string(bytes[3]) + "." + std::to_string(bytes[2]) + "." + std::to_string(bytes[1]) + "." + std::to_string(bytes[0]));
453 }
454 else {
455 DNSName temp;
456 const auto str = addr.toString();
457 const auto len = str.size();
458 std::string::size_type begin = 0;
459
460 while (begin < len) {
461 std::string::size_type end = str.find(":", begin);
462 std::string sub;
463 if (end != string::npos) {
464 sub = str.substr(begin, end - begin);
465 }
466 else {
467 sub = str.substr(begin);
468 }
469
470 if (sub.empty()) {
471 temp = DNSName("zz") + temp;
472 }
473 else {
474 temp = DNSName(sub) + temp;
475 }
476
477 if (end == string::npos) {
478 break;
479 }
480 begin = end + 1;
481 }
482 res += temp;
483 }
484
485 return res;
486 }
487
488
489 void DNSFilterEngine::Zone::dumpAddrPolicy(FILE* fp, const Netmask& nm, const DNSName& name, const Policy& pol) const
490 {
491 DNSName full = maskToRPZ(nm);
492 full += name;
493
494 auto records = pol.getRecords(full);
495 for (const auto& dr : records) {
496 fprintf(fp, "%s %" PRIu32 " IN %s %s\n", dr.d_name.toString().c_str(), dr.d_ttl, QType(dr.d_type).getName().c_str(), dr.d_content->getZoneRepresentation().c_str());
497 }
498 }
499
500 void DNSFilterEngine::Zone::dump(FILE* fp) const
501 {
502 /* fake the SOA record */
503 auto soa = DNSRecordContent::mastermake(QType::SOA, QClass::IN, "fake.RPZ. hostmaster.fake.RPZ. " + std::to_string(d_serial) + " " + std::to_string(d_refresh) + " 600 3600000 604800");
504 fprintf(fp, "%s IN SOA %s\n", d_domain.toString().c_str(), soa->getZoneRepresentation().c_str());
505
506 for (const auto& pair : d_qpolName) {
507 dumpNamedPolicy(fp, pair.first + d_domain, pair.second);
508 }
509
510 for (const auto& pair : d_propolName) {
511 dumpNamedPolicy(fp, pair.first + DNSName("rpz-nsdname.") + d_domain, pair.second);
512 }
513
514 for (const auto pair : d_qpolAddr) {
515 dumpAddrPolicy(fp, pair->first, DNSName("rpz-client-ip.") + d_domain, pair->second);
516 }
517
518 for (const auto pair : d_propolNSAddr) {
519 dumpAddrPolicy(fp, pair->first, DNSName("rpz-nsip.") + d_domain, pair->second);
520 }
521
522 for (const auto pair : d_postpolAddr) {
523 dumpAddrPolicy(fp, pair->first, DNSName("rpz-ip.") + d_domain, pair->second);
524 }
525 }