]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/filterpo.cc
Replace boost's placeholders with the ones from the std namespace
[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::findExactQNamePolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const
35 {
36 return findExactNamedPolicy(d_qpolName, qname, pol);
37 }
38
39 bool DNSFilterEngine::Zone::findExactNSPolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const
40 {
41 return findExactNamedPolicy(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)
72 {
73 if (polmap.empty()) {
74 return false;
75 }
76
77 /* for www.powerdns.com, we need to check:
78 www.powerdns.com.
79 *.powerdns.com.
80 *.com.
81 *.
82 */
83
84 std::unordered_map<DNSName, DNSFilterEngine::Policy>::const_iterator iter;
85 iter = polmap.find(qname);
86
87 if(iter != polmap.end()) {
88 pol=iter->second;
89 return true;
90 }
91
92 DNSName s(qname);
93 while(s.chopOff()){
94 iter = polmap.find(g_wildcarddnsname+s);
95 if(iter != polmap.end()) {
96 pol=iter->second;
97 return true;
98 }
99 }
100 return false;
101 }
102
103 bool DNSFilterEngine::Zone::findExactNamedPolicy(const std::unordered_map<DNSName, DNSFilterEngine::Policy>& polmap, const DNSName& qname, DNSFilterEngine::Policy& pol)
104 {
105 if (polmap.empty()) {
106 return false;
107 }
108
109 const auto& it = polmap.find(qname);
110 if (it != polmap.end()) {
111 pol = it->second;
112 return true;
113 }
114
115 return false;
116 }
117
118 bool DNSFilterEngine::getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& pol) const
119 {
120 // cout<<"Got question for nameserver name "<<qname<<endl;
121 std::vector<bool> zoneEnabled(d_zones.size());
122 size_t count = 0;
123 bool allEmpty = true;
124 for (const auto& z : d_zones) {
125 bool enabled = true;
126 const auto& zoneName = z->getName();
127 if (z->getPriority() >= pol.getPriority()) {
128 enabled = false;
129 }
130 else if (discardedPolicies.find(zoneName) != discardedPolicies.end()) {
131 enabled = false;
132 }
133 else {
134 if (z->hasNSPolicies()) {
135 allEmpty = false;
136 }
137 else {
138 enabled = false;
139 }
140 }
141
142 zoneEnabled[count] = enabled;
143 ++count;
144 }
145
146 if (allEmpty) {
147 return false;
148 }
149
150 /* prepare the wildcard-based names */
151 std::vector<DNSName> wcNames;
152 wcNames.reserve(qname.countLabels());
153 DNSName s(qname);
154 while (s.chopOff()){
155 wcNames.emplace_back(g_wildcarddnsname+s);
156 }
157
158 count = 0;
159 for(const auto& z : d_zones) {
160 if (!zoneEnabled[count]) {
161 ++count;
162 continue;
163 }
164 if (z->findExactNSPolicy(qname, pol)) {
165 // cerr<<"Had a hit on the nameserver ("<<qname<<") used to process the query"<<endl;
166 return true;
167 }
168
169 for (const auto& wc : wcNames) {
170 if (z->findExactNSPolicy(wc, pol)) {
171 // cerr<<"Had a hit on the nameserver ("<<qname<<") used to process the query"<<endl;
172 return true;
173 }
174 }
175 ++count;
176 }
177
178 return false;
179 }
180
181 bool DNSFilterEngine::getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& pol) const
182 {
183 // cout<<"Got question for nameserver IP "<<address.toString()<<endl;
184 for(const auto& z : d_zones) {
185 if (z->getPriority() >= pol.getPriority()) {
186 break;
187 }
188 const auto& zoneName = z->getName();
189 if (discardedPolicies.find(zoneName) != discardedPolicies.end()) {
190 continue;
191 }
192
193 if(z->findNSIPPolicy(address, pol)) {
194 // cerr<<"Had a hit on the nameserver ("<<address.toString()<<") used to process the query"<<endl;
195 return true;
196 }
197 }
198 return false;
199 }
200
201 bool DNSFilterEngine::getQueryPolicy(const DNSName& qname, const ComboAddress& ca, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& pol) const
202 {
203 // cout<<"Got question for "<<qname<<" from "<<ca.toString()<<endl;
204 std::vector<bool> zoneEnabled(d_zones.size());
205 size_t count = 0;
206 bool allEmpty = true;
207 for (const auto& z : d_zones) {
208 bool enabled = true;
209 if (z->getPriority() >= pol.getPriority()) {
210 enabled = false;
211 } else {
212 const auto& zoneName = z->getName();
213 if (discardedPolicies.find(zoneName) != discardedPolicies.end()) {
214 enabled = false;
215 }
216 else {
217 if (z->hasQNamePolicies() || z->hasClientPolicies()) {
218 allEmpty = false;
219 }
220 else {
221 enabled = false;
222 }
223 }
224 }
225
226 zoneEnabled[count] = enabled;
227 ++count;
228 }
229
230 if (allEmpty) {
231 return false;
232 }
233
234 /* prepare the wildcard-based names */
235 std::vector<DNSName> wcNames;
236 wcNames.reserve(qname.countLabels());
237 DNSName s(qname);
238 while (s.chopOff()){
239 wcNames.emplace_back(g_wildcarddnsname+s);
240 }
241
242 count = 0;
243 for (const auto& z : d_zones) {
244 if (!zoneEnabled[count]) {
245 ++count;
246 continue;
247 }
248
249 if (z->findClientPolicy(ca, pol)) {
250 // cerr<<"Had a hit on the IP address ("<<ca.toString()<<") of the client"<<endl;
251 return true;
252 }
253
254 if (z->findExactQNamePolicy(qname, pol)) {
255 // cerr<<"Had a hit on the name of the query"<<endl;
256 return true;
257 }
258
259 for (const auto& wc : wcNames) {
260 if (z->findExactQNamePolicy(wc, pol)) {
261 // cerr<<"Had a hit on the name of the query"<<endl;
262 return true;
263 }
264 }
265
266 ++count;
267 }
268
269 return false;
270 }
271
272 bool DNSFilterEngine::getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies, Policy& pol) const
273 {
274 ComboAddress ca;
275 for (const auto& r : records) {
276 if (r.d_place != DNSResourceRecord::ANSWER)
277 continue;
278 if (r.d_type == QType::A) {
279 if (auto rec = getRR<ARecordContent>(r)) {
280 ca = rec->getCA();
281 }
282 }
283 else if(r.d_type == QType::AAAA) {
284 if (auto rec = getRR<AAAARecordContent>(r)) {
285 ca = rec->getCA();
286 }
287 }
288 else
289 continue;
290
291 for (const auto& z : d_zones) {
292 if (z->getPriority() >= pol.getPriority()) {
293 break;
294 }
295 const auto& zoneName = z->getName();
296 if (discardedPolicies.find(zoneName) != discardedPolicies.end()) {
297 continue;
298 }
299
300 if (z->findResponsePolicy(ca, pol)) {
301 return true;
302 }
303 }
304 }
305 return false;
306 }
307
308 void DNSFilterEngine::assureZones(size_t zone)
309 {
310 if(d_zones.size() <= zone)
311 d_zones.resize(zone+1);
312 }
313
314 void DNSFilterEngine::Zone::addClientTrigger(const Netmask& nm, Policy&& pol)
315 {
316 pol.d_zoneData = d_zoneData;
317 pol.d_type = PolicyType::ClientIP;
318 d_qpolAddr.insert(nm).second=std::move(pol);
319 }
320
321 void DNSFilterEngine::Zone::addResponseTrigger(const Netmask& nm, Policy&& pol)
322 {
323 pol.d_zoneData = d_zoneData;
324 pol.d_type = PolicyType::ResponseIP;
325 d_postpolAddr.insert(nm).second=std::move(pol);
326 }
327
328 void DNSFilterEngine::Zone::addQNameTrigger(const DNSName& n, Policy&& pol, bool ignoreDuplicate)
329 {
330 auto it = d_qpolName.find(n);
331
332 if (it != d_qpolName.end()) {
333 auto& existingPol = it->second;
334
335 if (pol.d_kind != PolicyKind::Custom && !ignoreDuplicate) {
336 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());
337 }
338
339 if (existingPol.d_kind != PolicyKind::Custom && ignoreDuplicate) {
340 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());
341 }
342
343 existingPol.d_custom.reserve(existingPol.d_custom.size() + pol.d_custom.size());
344
345 std::move(pol.d_custom.begin(), pol.d_custom.end(), std::back_inserter(existingPol.d_custom));
346 }
347 else {
348 auto& qpol = d_qpolName.insert({n, std::move(pol)}).first->second;
349 qpol.d_zoneData = d_zoneData;
350 qpol.d_type = PolicyType::QName;
351 }
352 }
353
354 void DNSFilterEngine::Zone::addNSTrigger(const DNSName& n, Policy&& pol)
355 {
356 pol.d_zoneData = d_zoneData;
357 pol.d_type = PolicyType::NSDName;
358 d_propolName.insert({n, std::move(pol)});
359 }
360
361 void DNSFilterEngine::Zone::addNSIPTrigger(const Netmask& nm, Policy&& pol)
362 {
363 pol.d_zoneData = d_zoneData;
364 pol.d_type = PolicyType::NSIP;
365 d_propolNSAddr.insert(nm).second = std::move(pol);
366 }
367
368 bool DNSFilterEngine::Zone::rmClientTrigger(const Netmask& nm, const Policy& pol)
369 {
370 d_qpolAddr.erase(nm);
371 return true;
372 }
373
374 bool DNSFilterEngine::Zone::rmResponseTrigger(const Netmask& nm, const Policy& pol)
375 {
376 d_postpolAddr.erase(nm);
377 return true;
378 }
379
380 bool DNSFilterEngine::Zone::rmQNameTrigger(const DNSName& n, const Policy& pol)
381 {
382 auto found = d_qpolName.find(n);
383 if (found == d_qpolName.end()) {
384 return false;
385 }
386
387 auto& existing = found->second;
388 if (existing.d_kind != DNSFilterEngine::PolicyKind::Custom) {
389 d_qpolName.erase(found);
390 return true;
391 }
392
393 /* for custom types, we might have more than one type,
394 and then we need to remove only the right ones. */
395 if (existing.d_custom.size() <= 1) {
396 d_qpolName.erase(found);
397 return true;
398 }
399
400 bool result = false;
401 for (auto& toRemove : pol.d_custom) {
402 for (auto it = existing.d_custom.begin(); it != existing.d_custom.end(); ++it) {
403 if (**it == *toRemove) {
404 existing.d_custom.erase(it);
405 result = true;
406 break;
407 }
408 }
409 }
410
411 return result;
412 }
413
414 bool DNSFilterEngine::Zone::rmNSTrigger(const DNSName& n, const Policy& pol)
415 {
416 d_propolName.erase(n); // XXX verify policy matched? =pol;
417 return true;
418 }
419
420 bool DNSFilterEngine::Zone::rmNSIPTrigger(const Netmask& nm, const Policy& pol)
421 {
422 d_propolNSAddr.erase(nm);
423 return true;
424 }
425
426 DNSRecord DNSFilterEngine::Policy::getRecordFromCustom(const DNSName& qname, const std::shared_ptr<DNSRecordContent>& custom) const
427 {
428 DNSRecord dr;
429 dr.d_name = qname;
430 dr.d_type = custom->getType();
431 dr.d_ttl = d_ttl;
432 dr.d_class = QClass::IN;
433 dr.d_place = DNSResourceRecord::ANSWER;
434 dr.d_content = custom;
435
436 if (dr.d_type == QType::CNAME) {
437 const auto content = std::dynamic_pointer_cast<CNAMERecordContent>(custom);
438 if (content) {
439 DNSName target = content->getTarget();
440 if (target.isWildcard()) {
441 target.chopOff();
442 dr.d_content = std::make_shared<CNAMERecordContent>(qname + target);
443 }
444 }
445 }
446
447 return dr;
448 }
449
450 std::vector<DNSRecord> DNSFilterEngine::Policy::getCustomRecords(const DNSName& qname, uint16_t qtype) const
451 {
452 if (d_kind != PolicyKind::Custom) {
453 throw std::runtime_error("Asking for a custom record from a filtering policy of a non-custom type");
454 }
455
456 std::vector<DNSRecord> result;
457
458 for (const auto& custom : d_custom) {
459 if (qtype != QType::ANY && qtype != custom->getType() && custom->getType() != QType::CNAME) {
460 continue;
461 }
462
463 DNSRecord dr;
464 dr.d_name = qname;
465 dr.d_type = custom->getType();
466 dr.d_ttl = d_ttl;
467 dr.d_class = QClass::IN;
468 dr.d_place = DNSResourceRecord::ANSWER;
469 dr.d_content = custom;
470
471 if (dr.d_type == QType::CNAME) {
472 const auto content = std::dynamic_pointer_cast<CNAMERecordContent>(custom);
473 if (content) {
474 DNSName target = content->getTarget();
475 if (target.isWildcard()) {
476 target.chopOff();
477 dr.d_content = std::make_shared<CNAMERecordContent>(qname + target);
478 }
479 }
480 }
481
482 result.emplace_back(getRecordFromCustom(qname, custom));
483 }
484
485 return result;
486 }
487
488 std::string DNSFilterEngine::getKindToString(DNSFilterEngine::PolicyKind kind)
489 {
490 static const DNSName drop("rpz-drop."), truncate("rpz-tcp-only."), noaction("rpz-passthru.");
491 static const DNSName rpzClientIP("rpz-client-ip"), rpzIP("rpz-ip"),
492 rpzNSDname("rpz-nsdname"), rpzNSIP("rpz-nsip.");
493 static const std::string rpzPrefix("rpz-");
494
495 switch(kind) {
496 case DNSFilterEngine::PolicyKind::NoAction:
497 return noaction.toString();
498 case DNSFilterEngine::PolicyKind::Drop:
499 return drop.toString();
500 case DNSFilterEngine::PolicyKind::NXDOMAIN:
501 return g_rootdnsname.toString();
502 case PolicyKind::NODATA:
503 return g_wildcarddnsname.toString();
504 case DNSFilterEngine::PolicyKind::Truncate:
505 return truncate.toString();
506 default:
507 throw std::runtime_error("Unexpected DNSFilterEngine::Policy kind");
508 }
509 }
510
511 std::string DNSFilterEngine::getTypeToString(DNSFilterEngine::PolicyType type)
512 {
513 switch(type) {
514 case DNSFilterEngine::PolicyType::None:
515 return "none";
516 case DNSFilterEngine::PolicyType::QName:
517 return "QName";
518 case DNSFilterEngine::PolicyType::ClientIP:
519 return "Client IP";
520 case DNSFilterEngine::PolicyType::ResponseIP:
521 return "Response IP";
522 case DNSFilterEngine::PolicyType::NSDName:
523 return "Name Server Name";
524 case DNSFilterEngine::PolicyType::NSIP:
525 return "Name Server IP";
526 default:
527 throw std::runtime_error("Unexpected DNSFilterEngine::Policy type");
528 }
529 }
530
531 std::vector<DNSRecord> DNSFilterEngine::Policy::getRecords(const DNSName& qname) const
532 {
533 std::vector<DNSRecord> result;
534
535 if (d_kind == PolicyKind::Custom) {
536 result = getCustomRecords(qname, QType::ANY);
537 }
538 else {
539 DNSRecord dr;
540 dr.d_name = qname;
541 dr.d_ttl = static_cast<uint32_t>(d_ttl);
542 dr.d_type = QType::CNAME;
543 dr.d_class = QClass::IN;
544 dr.d_content = DNSRecordContent::mastermake(QType::CNAME, QClass::IN, getKindToString(d_kind));
545 result.push_back(std::move(dr));
546 }
547
548 return result;
549 }
550
551 void DNSFilterEngine::Zone::dumpNamedPolicy(FILE* fp, const DNSName& name, const Policy& pol)
552 {
553 auto records = pol.getRecords(name);
554 for (const auto& dr : records) {
555 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());
556 }
557 }
558
559 DNSName DNSFilterEngine::Zone::maskToRPZ(const Netmask& nm)
560 {
561 int bits = nm.getBits();
562 DNSName res(std::to_string(bits));
563 const auto& addr = nm.getNetwork();
564
565 if (addr.isIPv4()) {
566 const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&addr.sin4.sin_addr.s_addr);
567 res += DNSName(std::to_string(bytes[3]) + "." + std::to_string(bytes[2]) + "." + std::to_string(bytes[1]) + "." + std::to_string(bytes[0]));
568 }
569 else {
570 DNSName temp;
571 const auto str = addr.toString();
572 const auto len = str.size();
573 std::string::size_type begin = 0;
574
575 while (begin < len) {
576 std::string::size_type end = str.find(":", begin);
577 std::string sub;
578 if (end != string::npos) {
579 sub = str.substr(begin, end - begin);
580 }
581 else {
582 sub = str.substr(begin);
583 }
584
585 if (sub.empty()) {
586 temp = DNSName("zz") + temp;
587 }
588 else {
589 temp = DNSName(sub) + temp;
590 }
591
592 if (end == string::npos) {
593 break;
594 }
595 begin = end + 1;
596 }
597 res += temp;
598 }
599
600 return res;
601 }
602
603
604 void DNSFilterEngine::Zone::dumpAddrPolicy(FILE* fp, const Netmask& nm, const DNSName& name, const Policy& pol)
605 {
606 DNSName full = maskToRPZ(nm);
607 full += name;
608
609 auto records = pol.getRecords(full);
610 for (const auto& dr : records) {
611 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());
612 }
613 }
614
615 void DNSFilterEngine::Zone::dump(FILE* fp) const
616 {
617 /* fake the SOA record */
618 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");
619 fprintf(fp, "%s IN SOA %s\n", d_domain.toString().c_str(), soa->getZoneRepresentation().c_str());
620
621 for (const auto& pair : d_qpolName) {
622 dumpNamedPolicy(fp, pair.first + d_domain, pair.second);
623 }
624
625 for (const auto& pair : d_propolName) {
626 dumpNamedPolicy(fp, pair.first + DNSName("rpz-nsdname.") + d_domain, pair.second);
627 }
628
629 for (const auto pair : d_qpolAddr) {
630 dumpAddrPolicy(fp, pair.first, DNSName("rpz-client-ip.") + d_domain, pair.second);
631 }
632
633 for (const auto pair : d_propolNSAddr) {
634 dumpAddrPolicy(fp, pair.first, DNSName("rpz-nsip.") + d_domain, pair.second);
635 }
636
637 for (const auto pair : d_postpolAddr) {
638 dumpAddrPolicy(fp, pair.first, DNSName("rpz-ip.") + d_domain, pair.second);
639 }
640 }
641
642 void mergePolicyTags(std::unordered_set<std::string>& tags, const std::unordered_set<std::string>& newTags)
643 {
644 for (const auto& tag : newTags) {
645 tags.insert(tag);
646 }
647 }