]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/filterpo.cc
auth: switch circleci mssql image
[thirdparty/pdns.git] / pdns / filterpo.cc
CommitLineData
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 */
6791663c
RG
22
23#include <cinttypes>
644dd1da 24#include <iostream>
6791663c
RG
25
26#include "filterpo.hh"
644dd1da 27#include "namespaces.hh"
28#include "dnsrecords.hh"
29
30DNSFilterEngine::DNSFilterEngine()
31{
32}
33
6791663c
RG
34bool DNSFilterEngine::Zone::findQNamePolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const
35{
36 return findNamedPolicy(d_qpolName, qname, pol);
37}
38
39bool DNSFilterEngine::Zone::findNSPolicy(const DNSName& qname, DNSFilterEngine::Policy& pol) const
40{
41 return findNamedPolicy(d_propolName, qname, pol);
42}
43
44bool 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
53bool 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
62bool 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
71bool DNSFilterEngine::Zone::findNamedPolicy(const std::unordered_map<DNSName, DNSFilterEngine::Policy>& polmap, const DNSName& qname, DNSFilterEngine::Policy& pol) const
644dd1da 72{
5678437b
PL
73 /* for www.powerdns.com, we need to check:
74 www.powerdns.com.
75 *.powerdns.com.
76 *.com.
77 *.
78 */
0a273054 79
a2d0450e
RG
80 std::unordered_map<DNSName, DNSFilterEngine::Policy>::const_iterator iter;
81 iter = polmap.find(qname);
bef458b2
PL
82
83 if(iter != polmap.end()) {
84 pol=iter->second;
85 return true;
86 }
5678437b 87
a2d0450e 88 DNSName s(qname);
bef458b2 89 while(s.chopOff()){
12c06211 90 iter = polmap.find(g_wildcarddnsname+s);
5678437b
PL
91 if(iter != polmap.end()) {
92 pol=iter->second;
93 return true;
94 }
bef458b2 95 }
644dd1da 96 return false;
97}
98
0a273054 99DNSFilterEngine::Policy DNSFilterEngine::getProcessingPolicy(const DNSName& qname, const std::unordered_map<std::string,bool>& discardedPolicies) const
644dd1da 100{
39ec5d29 101 // cout<<"Got question for nameserver name "<<qname<<endl;
1f1ca368 102 Policy pol;
644dd1da 103 for(const auto& z : d_zones) {
6b972d59
RG
104 const auto zoneName = z->getName();
105 if(zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
0a273054
RG
106 continue;
107 }
108
6791663c 109 if(z->findNSPolicy(qname, pol)) {
de0f72ba 110 // cerr<<"Had a hit on the nameserver ("<<qname<<") used to process the query"<<endl;
644dd1da 111 return pol;
112 }
113 }
114 return pol;
b8470add 115}
644dd1da 116
0a273054 117DNSFilterEngine::Policy DNSFilterEngine::getProcessingPolicy(const ComboAddress& address, const std::unordered_map<std::string,bool>& discardedPolicies) const
b8470add 118{
6791663c 119 Policy pol;
b8470add
PL
120 // cout<<"Got question for nameserver IP "<<address.toString()<<endl;
121 for(const auto& z : d_zones) {
6b972d59
RG
122 const auto zoneName = z->getName();
123 if(zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
0a273054
RG
124 continue;
125 }
126
6791663c 127 if(z->findNSIPPolicy(address, pol)) {
b8470add 128 // cerr<<"Had a hit on the nameserver ("<<address.toString()<<") used to process the query"<<endl;
6791663c 129 return pol;
b8470add
PL
130 }
131 }
6791663c 132 return pol;
b8470add 133}
644dd1da 134
0a273054 135DNSFilterEngine::Policy DNSFilterEngine::getQueryPolicy(const DNSName& qname, const ComboAddress& ca, const std::unordered_map<std::string,bool>& discardedPolicies) const
644dd1da 136{
de0f72ba 137 // cout<<"Got question for "<<qname<<" from "<<ca.toString()<<endl;
1f1ca368 138 Policy pol;
644dd1da 139 for(const auto& z : d_zones) {
6b972d59
RG
140 const auto zoneName = z->getName();
141 if(zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
0a273054
RG
142 continue;
143 }
144
6791663c 145 if(z->findQNamePolicy(qname, pol)) {
de0f72ba 146 // cerr<<"Had a hit on the name of the query"<<endl;
644dd1da 147 return pol;
148 }
6791663c
RG
149
150 if(z->findClientPolicy(ca, pol)) {
0e760497 151 // cerr<<"Had a hit on the IP address ("<<ca.toString()<<") of the client"<<endl;
6791663c 152 return pol;
644dd1da 153 }
154 }
155
39ec5d29 156 return pol;
644dd1da 157}
158
0a273054 159DNSFilterEngine::Policy DNSFilterEngine::getPostPolicy(const vector<DNSRecord>& records, const std::unordered_map<std::string,bool>& discardedPolicies) const
644dd1da 160{
6791663c 161 Policy pol;
644dd1da 162 ComboAddress ca;
644dd1da 163 for(const auto& r : records) {
6791663c 164 if(r.d_place != DNSResourceRecord::ANSWER)
644dd1da 165 continue;
ba3c54cb
RG
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 }
644dd1da 176 else
177 continue;
178
179 for(const auto& z : d_zones) {
6b972d59
RG
180 const auto zoneName = z->getName();
181 if(zoneName && discardedPolicies.find(*zoneName) != discardedPolicies.end()) {
0a273054
RG
182 continue;
183 }
184
6791663c
RG
185 if(z->findResponsePolicy(ca, pol)) {
186 return pol;
187 }
644dd1da 188 }
189 }
6791663c 190 return pol;
644dd1da 191}
192
0a273054 193void DNSFilterEngine::assureZones(size_t zone)
644dd1da 194{
0a273054 195 if(d_zones.size() <= zone)
644dd1da 196 d_zones.resize(zone+1);
644dd1da 197}
198
6da513b2 199void DNSFilterEngine::Zone::addClientTrigger(const Netmask& nm, Policy&& pol)
7eafc52f 200{
6b972d59 201 pol.d_name = d_name;
f3da83fe 202 pol.d_type = PolicyType::ClientIP;
6da513b2 203 d_qpolAddr.insert(nm).second=std::move(pol);
7eafc52f 204}
205
6da513b2 206void DNSFilterEngine::Zone::addResponseTrigger(const Netmask& nm, Policy&& pol)
0a7ef1b8 207{
6b972d59 208 pol.d_name = d_name;
f3da83fe 209 pol.d_type = PolicyType::ResponseIP;
6da513b2 210 d_postpolAddr.insert(nm).second=std::move(pol);
644dd1da 211}
212
d122dac0 213void DNSFilterEngine::Zone::addQNameTrigger(const DNSName& n, Policy&& pol, bool ignoreDuplicate)
644dd1da 214{
6da513b2
RG
215 auto it = d_qpolName.find(n);
216
217 if (it != d_qpolName.end()) {
218 auto& existingPol = it->second;
219
d122dac0 220 if (pol.d_kind != PolicyKind::Custom && !ignoreDuplicate) {
fc38ec55 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());
6da513b2
RG
222 }
223
d122dac0 224 if (existingPol.d_kind != PolicyKind::Custom && ignoreDuplicate) {
fc38ec55 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());
6da513b2
RG
226 }
227
228 existingPol.d_custom.reserve(existingPol.d_custom.size() + pol.d_custom.size());
229
fc38ec55 230 std::move(pol.d_custom.begin(), pol.d_custom.end(), std::back_inserter(existingPol.d_custom));
6da513b2
RG
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 }
644dd1da 237}
238
6da513b2 239void DNSFilterEngine::Zone::addNSTrigger(const DNSName& n, Policy&& pol)
644dd1da 240{
6b972d59 241 pol.d_name = d_name;
f3da83fe 242 pol.d_type = PolicyType::NSDName;
6da513b2 243 d_propolName.insert({n, std::move(pol)});
644dd1da 244}
245
6da513b2 246void DNSFilterEngine::Zone::addNSIPTrigger(const Netmask& nm, Policy&& pol)
644dd1da 247{
6b972d59 248 pol.d_name = d_name;
f3da83fe 249 pol.d_type = PolicyType::NSIP;
6da513b2 250 d_propolNSAddr.insert(nm).second = std::move(pol);
644dd1da 251}
39ec5d29 252
6da513b2 253bool DNSFilterEngine::Zone::rmClientTrigger(const Netmask& nm, const Policy& pol)
b8470add 254{
6b972d59 255 d_qpolAddr.erase(nm);
39ec5d29 256 return true;
257}
258
6da513b2 259bool DNSFilterEngine::Zone::rmResponseTrigger(const Netmask& nm, const Policy& pol)
39ec5d29 260{
6b972d59 261 d_postpolAddr.erase(nm);
39ec5d29 262 return true;
263}
264
6da513b2 265bool DNSFilterEngine::Zone::rmQNameTrigger(const DNSName& n, const Policy& pol)
39ec5d29 266{
6da513b2
RG
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;
3b027d9e 286 for (auto& toRemove : pol.d_custom) {
6da513b2 287 for (auto it = existing.d_custom.begin(); it != existing.d_custom.end(); ++it) {
3b027d9e 288 if (**it == *toRemove) {
6da513b2
RG
289 existing.d_custom.erase(it);
290 result = true;
291 break;
292 }
293 }
294 }
295
296 return result;
39ec5d29 297}
298
6da513b2 299bool DNSFilterEngine::Zone::rmNSTrigger(const DNSName& n, const Policy& pol)
39ec5d29 300{
6b972d59 301 d_propolName.erase(n); // XXX verify policy matched? =pol;
39ec5d29 302 return true;
303}
b8470add 304
6da513b2 305bool DNSFilterEngine::Zone::rmNSIPTrigger(const Netmask& nm, const Policy& pol)
b8470add 306{
6b972d59 307 d_propolNSAddr.erase(nm);
b8470add
PL
308 return true;
309}
a9e029ee 310
6da513b2
RG
311DNSRecord 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
335std::vector<DNSRecord> DNSFilterEngine::Policy::getCustomRecords(const DNSName& qname, uint16_t qtype) const
a9e029ee
RG
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
6da513b2 341 std::vector<DNSRecord> result;
a9e029ee 342
6da513b2
RG
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 }
a9e029ee
RG
364 }
365 }
6da513b2
RG
366
367 result.emplace_back(getRecordFromCustom(qname, custom));
a9e029ee
RG
368 }
369
370 return result;
371}
6791663c 372
6da513b2 373std::string DNSFilterEngine::getKindToString(DNSFilterEngine::PolicyKind kind)
6791663c
RG
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
6da513b2 380 switch(kind) {
6791663c
RG
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
6da513b2 396std::string DNSFilterEngine::getTypeToString(DNSFilterEngine::PolicyType type)
6791663c 397{
6da513b2
RG
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
416std::vector<DNSRecord> DNSFilterEngine::Policy::getRecords(const DNSName& qname) const
417{
418 std::vector<DNSRecord> result;
6791663c
RG
419
420 if (d_kind == PolicyKind::Custom) {
6da513b2 421 result = getCustomRecords(qname, QType::ANY);
6791663c
RG
422 }
423 else {
6da513b2 424 DNSRecord dr;
6791663c
RG
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;
6da513b2
RG
429 dr.d_content = DNSRecordContent::mastermake(QType::CNAME, QClass::IN, getKindToString(d_kind));
430 result.push_back(std::move(dr));
6791663c
RG
431 }
432
6da513b2 433 return result;
6791663c
RG
434}
435
436void DNSFilterEngine::Zone::dumpNamedPolicy(FILE* fp, const DNSName& name, const Policy& pol) const
437{
6da513b2
RG
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 }
6791663c
RG
442}
443
444DNSName DNSFilterEngine::Zone::maskToRPZ(const Netmask& nm)
445{
446 int bits = nm.getBits();
447 DNSName res(std::to_string(bits));
cffabb3b 448 const auto& addr = nm.getNetwork();
6791663c
RG
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
489void 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
6da513b2
RG
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 }
6791663c
RG
498}
499
500void 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}