]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/filterpo.cc
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
26 #include "filterpo.hh"
27 #include "namespaces.hh"
28 #include "dnsrecords.hh"
30 DNSFilterEngine :: DNSFilterEngine ()
34 bool DNSFilterEngine :: Zone :: findExactQNamePolicy ( const DNSName
& qname
, DNSFilterEngine :: Policy
& pol
) const
36 return findExactNamedPolicy ( d_qpolName
, qname
, pol
);
39 bool DNSFilterEngine :: Zone :: findExactNSPolicy ( const DNSName
& qname
, DNSFilterEngine :: Policy
& pol
) const
41 return findExactNamedPolicy ( d_propolName
, qname
, pol
);
44 bool DNSFilterEngine :: Zone :: findNSIPPolicy ( const ComboAddress
& addr
, DNSFilterEngine :: Policy
& pol
) const
46 if ( const auto fnd
= d_propolNSAddr
. lookup ( addr
)) {
53 bool DNSFilterEngine :: Zone :: findResponsePolicy ( const ComboAddress
& addr
, DNSFilterEngine :: Policy
& pol
) const
55 if ( const auto fnd
= d_postpolAddr
. lookup ( addr
)) {
62 bool DNSFilterEngine :: Zone :: findClientPolicy ( const ComboAddress
& addr
, DNSFilterEngine :: Policy
& pol
) const
64 if ( const auto fnd
= d_qpolAddr
. lookup ( addr
)) {
71 bool DNSFilterEngine :: Zone :: findNamedPolicy ( const std :: unordered_map
< DNSName
, DNSFilterEngine :: Policy
>& polmap
, const DNSName
& qname
, DNSFilterEngine :: Policy
& pol
)
77 /* for www.powerdns.com, we need to check:
84 std :: unordered_map
< DNSName
, DNSFilterEngine :: Policy
>:: const_iterator iter
;
85 iter
= polmap
. find ( qname
);
87 if ( iter
!= polmap
. end ()) {
94 iter
= polmap
. find ( g_wildcarddnsname
+ s
);
95 if ( iter
!= polmap
. end ()) {
103 bool DNSFilterEngine :: Zone :: findExactNamedPolicy ( const std :: unordered_map
< DNSName
, DNSFilterEngine :: Policy
>& polmap
, const DNSName
& qname
, DNSFilterEngine :: Policy
& pol
)
105 if ( polmap
. empty ()) {
109 const auto & it
= polmap
. find ( qname
);
110 if ( it
!= polmap
. end ()) {
118 DNSFilterEngine :: Policy
DNSFilterEngine :: getProcessingPolicy ( const DNSName
& qname
, const std :: unordered_map
< std :: string
, bool >& discardedPolicies
) const
120 // cout<<"Got question for nameserver name "<<qname<<endl;
121 std :: vector
< bool > zoneEnabled ( d_zones
. size ());
123 bool allEmpty
= true ;
124 for ( const auto & z
: d_zones
) {
126 const auto zoneName
= z
-> getName ();
127 if ( zoneName
&& discardedPolicies
. find (* zoneName
) != discardedPolicies
. end ()) {
131 if ( z
-> hasNSPolicies ()) {
139 zoneEnabled
[ count
] = enabled
;
146 for ( const auto & z
: d_zones
) {
147 if ( zoneEnabled
[ count
] && z
-> findExactNSPolicy ( qname
, pol
)) {
148 // cerr<<"Had a hit on the nameserver ("<<qname<<") used to process the query"<<endl;
157 for ( const auto & z
: d_zones
) {
158 if ( zoneEnabled
[ count
] && z
-> findExactNSPolicy ( g_wildcarddnsname
+ s
, pol
)) {
159 // cerr<<"Had a hit on the nameserver ("<<qname<<") used to process the query"<<endl;
170 DNSFilterEngine :: Policy
DNSFilterEngine :: getProcessingPolicy ( const ComboAddress
& address
, const std :: unordered_map
< std :: string
, bool >& discardedPolicies
) const
173 // cout<<"Got question for nameserver IP "<<address.toString()<<endl;
174 for ( const auto & z
: d_zones
) {
175 const auto zoneName
= z
-> getName ();
176 if ( zoneName
&& discardedPolicies
. find (* zoneName
) != discardedPolicies
. end ()) {
180 if ( z
-> findNSIPPolicy ( address
, pol
)) {
181 // cerr<<"Had a hit on the nameserver ("<<address.toString()<<") used to process the query"<<endl;
188 DNSFilterEngine :: Policy
DNSFilterEngine :: getQueryPolicy ( const DNSName
& qname
, const ComboAddress
& ca
, const std :: unordered_map
< std :: string
, bool >& discardedPolicies
) const
190 // cout<<"Got question for "<<qname<<" from "<<ca.toString()<<endl;
191 std :: vector
< bool > zoneEnabled ( d_zones
. size ());
193 bool allEmpty
= true ;
194 for ( const auto & z
: d_zones
) {
196 const auto zoneName
= z
-> getName ();
197 if ( zoneName
&& discardedPolicies
. find (* zoneName
) != discardedPolicies
. end ()) {
201 if ( z
-> hasQNamePolicies ()) {
209 zoneEnabled
[ count
] = enabled
;
216 for ( const auto & z
: d_zones
) {
217 if ( zoneEnabled
[ count
] && z
-> findExactQNamePolicy ( qname
, pol
)) {
218 // cerr<<"Had a hit on the name of the query"<<endl;
227 for ( const auto & z
: d_zones
) {
228 if ( zoneEnabled
[ count
] && z
-> findExactQNamePolicy ( g_wildcarddnsname
+ s
, pol
)) {
229 // cerr<<"Had a hit on the name of the query"<<endl;
238 for ( const auto & z
: d_zones
) {
239 if ( zoneEnabled
[ count
] && z
-> findClientPolicy ( ca
, pol
)) {
240 // cerr<<"Had a hit on the IP address ("<<ca.toString()<<") of the client"<<endl;
248 DNSFilterEngine :: Policy
DNSFilterEngine :: getPostPolicy ( const vector
< DNSRecord
>& records
, const std :: unordered_map
< std :: string
, bool >& discardedPolicies
) const
252 for ( const auto & r
: records
) {
253 if ( r
. d_place
!= DNSResourceRecord :: ANSWER
)
255 if ( r
. d_type
== QType :: A
) {
256 if ( auto rec
= getRR
< ARecordContent
>( r
)) {
260 else if ( r
. d_type
== QType :: AAAA
) {
261 if ( auto rec
= getRR
< AAAARecordContent
>( r
)) {
268 for ( const auto & z
: d_zones
) {
269 const auto zoneName
= z
-> getName ();
270 if ( zoneName
&& discardedPolicies
. find (* zoneName
) != discardedPolicies
. end ()) {
274 if ( z
-> findResponsePolicy ( ca
, pol
)) {
282 void DNSFilterEngine :: assureZones ( size_t zone
)
284 if ( d_zones
. size () <= zone
)
285 d_zones
. resize ( zone
+ 1 );
288 void DNSFilterEngine :: Zone :: addClientTrigger ( const Netmask
& nm
, Policy
&& pol
)
291 pol
. d_type
= PolicyType :: ClientIP
;
292 d_qpolAddr
. insert ( nm
). second
= std :: move ( pol
);
295 void DNSFilterEngine :: Zone :: addResponseTrigger ( const Netmask
& nm
, Policy
&& pol
)
298 pol
. d_type
= PolicyType :: ResponseIP
;
299 d_postpolAddr
. insert ( nm
). second
= std :: move ( pol
);
302 void DNSFilterEngine :: Zone :: addQNameTrigger ( const DNSName
& n
, Policy
&& pol
, bool ignoreDuplicate
)
304 auto it
= d_qpolName
. find ( n
);
306 if ( it
!= d_qpolName
. end ()) {
307 auto & existingPol
= it
-> second
;
309 if ( pol
. d_kind
!= PolicyKind :: Custom
&& ! ignoreDuplicate
) {
310 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 ());
313 if ( existingPol
. d_kind
!= PolicyKind :: Custom
&& ignoreDuplicate
) {
314 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 ());
317 existingPol
. d_custom
. reserve ( existingPol
. d_custom
. size () + pol
. d_custom
. size ());
319 std :: move ( pol
. d_custom
. begin (), pol
. d_custom
. end (), std :: back_inserter ( existingPol
. d_custom
));
322 auto & qpol
= d_qpolName
. insert ({ n
, std :: move ( pol
)}). first
-> second
;
323 qpol
. d_name
= d_name
;
324 qpol
. d_type
= PolicyType :: QName
;
328 void DNSFilterEngine :: Zone :: addNSTrigger ( const DNSName
& n
, Policy
&& pol
)
331 pol
. d_type
= PolicyType :: NSDName
;
332 d_propolName
. insert ({ n
, std :: move ( pol
)});
335 void DNSFilterEngine :: Zone :: addNSIPTrigger ( const Netmask
& nm
, Policy
&& pol
)
338 pol
. d_type
= PolicyType :: NSIP
;
339 d_propolNSAddr
. insert ( nm
). second
= std :: move ( pol
);
342 bool DNSFilterEngine :: Zone :: rmClientTrigger ( const Netmask
& nm
, const Policy
& pol
)
344 d_qpolAddr
. erase ( nm
);
348 bool DNSFilterEngine :: Zone :: rmResponseTrigger ( const Netmask
& nm
, const Policy
& pol
)
350 d_postpolAddr
. erase ( nm
);
354 bool DNSFilterEngine :: Zone :: rmQNameTrigger ( const DNSName
& n
, const Policy
& pol
)
356 auto it
= d_qpolName
. find ( n
);
357 if ( it
== d_qpolName
. end ()) {
361 auto & existing
= it
-> second
;
362 if ( existing
. d_kind
!= DNSFilterEngine :: PolicyKind :: Custom
) {
363 d_qpolName
. erase ( it
);
367 /* for custom types, we might have more than one type,
368 and then we need to remove only the right ones. */
369 if ( existing
. d_custom
. size () <= 1 ) {
370 d_qpolName
. erase ( it
);
375 for ( auto & toRemove
: pol
. d_custom
) {
376 for ( auto it
= existing
. d_custom
. begin (); it
!= existing
. d_custom
. end (); ++ it
) {
377 if (** it
== * toRemove
) {
378 existing
. d_custom
. erase ( it
);
388 bool DNSFilterEngine :: Zone :: rmNSTrigger ( const DNSName
& n
, const Policy
& pol
)
390 d_propolName
. erase ( n
); // XXX verify policy matched? =pol;
394 bool DNSFilterEngine :: Zone :: rmNSIPTrigger ( const Netmask
& nm
, const Policy
& pol
)
396 d_propolNSAddr
. erase ( nm
);
400 DNSRecord
DNSFilterEngine :: Policy :: getRecordFromCustom ( const DNSName
& qname
, const std :: shared_ptr
< DNSRecordContent
>& custom
) const
404 dr
. d_type
= custom
-> getType ();
406 dr
. d_class
= QClass :: IN
;
407 dr
. d_place
= DNSResourceRecord :: ANSWER
;
408 dr
. d_content
= custom
;
410 if ( dr
. d_type
== QType :: CNAME
) {
411 const auto content
= std :: dynamic_pointer_cast
< CNAMERecordContent
>( custom
);
413 DNSName target
= content
-> getTarget ();
414 if ( target
. isWildcard ()) {
416 dr
. d_content
= std :: make_shared
< CNAMERecordContent
>( qname
+ target
);
424 std :: vector
< DNSRecord
> DNSFilterEngine :: Policy :: getCustomRecords ( const DNSName
& qname
, uint16_t qtype
) const
426 if ( d_kind
!= PolicyKind :: Custom
) {
427 throw std :: runtime_error ( "Asking for a custom record from a filtering policy of a non-custom type" );
430 std :: vector
< DNSRecord
> result
;
432 for ( const auto & custom
: d_custom
) {
433 if ( qtype
!= QType :: ANY
&& qtype
!= custom
-> getType () && custom
-> getType () != QType :: CNAME
) {
439 dr
. d_type
= custom
-> getType ();
441 dr
. d_class
= QClass :: IN
;
442 dr
. d_place
= DNSResourceRecord :: ANSWER
;
443 dr
. d_content
= custom
;
445 if ( dr
. d_type
== QType :: CNAME
) {
446 const auto content
= std :: dynamic_pointer_cast
< CNAMERecordContent
>( custom
);
448 DNSName target
= content
-> getTarget ();
449 if ( target
. isWildcard ()) {
451 dr
. d_content
= std :: make_shared
< CNAMERecordContent
>( qname
+ target
);
456 result
. emplace_back ( getRecordFromCustom ( qname
, custom
));
462 std :: string
DNSFilterEngine :: getKindToString ( DNSFilterEngine :: PolicyKind kind
)
464 static const DNSName
drop ( "rpz-drop." ), truncate ( "rpz-tcp-only." ), noaction ( "rpz-passthru." );
465 static const DNSName
rpzClientIP ( "rpz-client-ip" ), rpzIP ( "rpz-ip" ),
466 rpzNSDname ( "rpz-nsdname" ), rpzNSIP ( "rpz-nsip." );
467 static const std :: string
rpzPrefix ( "rpz-" );
470 case DNSFilterEngine :: PolicyKind :: NoAction
:
471 return noaction
. toString ();
472 case DNSFilterEngine :: PolicyKind :: Drop
:
473 return drop
. toString ();
474 case DNSFilterEngine :: PolicyKind :: NXDOMAIN
:
475 return g_rootdnsname
. toString ();
476 case PolicyKind :: NODATA
:
477 return g_wildcarddnsname
. toString ();
478 case DNSFilterEngine :: PolicyKind :: Truncate
:
479 return truncate
. toString ();
481 throw std :: runtime_error ( "Unexpected DNSFilterEngine::Policy kind" );
485 std :: string
DNSFilterEngine :: getTypeToString ( DNSFilterEngine :: PolicyType type
)
488 case DNSFilterEngine :: PolicyType :: None
:
490 case DNSFilterEngine :: PolicyType :: QName
:
492 case DNSFilterEngine :: PolicyType :: ClientIP
:
494 case DNSFilterEngine :: PolicyType :: ResponseIP
:
495 return "Response IP" ;
496 case DNSFilterEngine :: PolicyType :: NSDName
:
497 return "Name Server Name" ;
498 case DNSFilterEngine :: PolicyType :: NSIP
:
499 return "Name Server IP" ;
501 throw std :: runtime_error ( "Unexpected DNSFilterEngine::Policy type" );
505 std :: vector
< DNSRecord
> DNSFilterEngine :: Policy :: getRecords ( const DNSName
& qname
) const
507 std :: vector
< DNSRecord
> result
;
509 if ( d_kind
== PolicyKind :: Custom
) {
510 result
= getCustomRecords ( qname
, QType :: ANY
);
515 dr
. d_ttl
= static_cast < uint32_t >( d_ttl
);
516 dr
. d_type
= QType :: CNAME
;
517 dr
. d_class
= QClass :: IN
;
518 dr
. d_content
= DNSRecordContent :: mastermake ( QType :: CNAME
, QClass :: IN
, getKindToString ( d_kind
));
519 result
. push_back ( std :: move ( dr
));
525 void DNSFilterEngine :: Zone :: dumpNamedPolicy ( FILE * fp
, const DNSName
& name
, const Policy
& pol
)
527 auto records
= pol
. getRecords ( name
);
528 for ( const auto & dr
: records
) {
529 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 ());
533 DNSName
DNSFilterEngine :: Zone :: maskToRPZ ( const Netmask
& nm
)
535 int bits
= nm
. getBits ();
536 DNSName
res ( std :: to_string ( bits
));
537 const auto & addr
= nm
. getNetwork ();
540 const uint8_t * bytes
= reinterpret_cast < const uint8_t *>(& addr
. sin4
. sin_addr
. s_addr
);
541 res
+= DNSName ( std :: to_string ( bytes
[ 3 ]) + "." + std :: to_string ( bytes
[ 2 ]) + "." + std :: to_string ( bytes
[ 1 ]) + "." + std :: to_string ( bytes
[ 0 ]));
545 const auto str
= addr
. toString ();
546 const auto len
= str
. size ();
547 std :: string :: size_type begin
= 0 ;
549 while ( begin
< len
) {
550 std :: string :: size_type end
= str
. find ( ":" , begin
);
552 if ( end
!= string :: npos
) {
553 sub
= str
. substr ( begin
, end
- begin
);
556 sub
= str
. substr ( begin
);
560 temp
= DNSName ( "zz" ) + temp
;
563 temp
= DNSName ( sub
) + temp
;
566 if ( end
== string :: npos
) {
578 void DNSFilterEngine :: Zone :: dumpAddrPolicy ( FILE * fp
, const Netmask
& nm
, const DNSName
& name
, const Policy
& pol
)
580 DNSName full
= maskToRPZ ( nm
);
583 auto records
= pol
. getRecords ( full
);
584 for ( const auto & dr
: records
) {
585 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 ());
589 void DNSFilterEngine :: Zone :: dump ( FILE * fp
) const
591 /* fake the SOA record */
592 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" );
593 fprintf ( fp
, "%s IN SOA %s \n " , d_domain
. toString (). c_str (), soa
-> getZoneRepresentation (). c_str ());
595 for ( const auto & pair
: d_qpolName
) {
596 dumpNamedPolicy ( fp
, pair
. first
+ d_domain
, pair
. second
);
599 for ( const auto & pair
: d_propolName
) {
600 dumpNamedPolicy ( fp
, pair
. first
+ DNSName ( "rpz-nsdname." ) + d_domain
, pair
. second
);
603 for ( const auto pair
: d_qpolAddr
) {
604 dumpAddrPolicy ( fp
, pair
-> first
, DNSName ( "rpz-client-ip." ) + d_domain
, pair
-> second
);
607 for ( const auto pair
: d_propolNSAddr
) {
608 dumpAddrPolicy ( fp
, pair
-> first
, DNSName ( "rpz-nsip." ) + d_domain
, pair
-> second
);
611 for ( const auto pair
: d_postpolAddr
) {
612 dumpAddrPolicy ( fp
, pair
-> first
, DNSName ( "rpz-ip." ) + d_domain
, pair
-> second
);