]>
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 bool DNSFilterEngine :: getProcessingPolicy ( const DNSName
& qname
, const std :: unordered_map
< std :: string
, bool >& discardedPolicies
, Policy
& pol
) 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 ( z
-> getPriority () >= pol
. getPriority ()) {
130 else if ( discardedPolicies
. find ( zoneName
) != discardedPolicies
. end ()) {
134 if ( z
-> hasNSPolicies ()) {
142 zoneEnabled
[ count
] = enabled
;
150 /* prepare the wildcard-based names */
151 std :: vector
< DNSName
> wcNames
;
152 wcNames
. reserve ( qname
. countLabels ());
155 wcNames
. emplace_back ( g_wildcarddnsname
+ s
);
159 for ( const auto & z
: d_zones
) {
160 if (! zoneEnabled
[ count
]) {
164 if ( z
-> findExactNSPolicy ( qname
, pol
)) {
165 // cerr<<"Had a hit on the nameserver ("<<qname<<") used to process the query"<<endl;
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;
181 bool DNSFilterEngine :: getProcessingPolicy ( const ComboAddress
& address
, const std :: unordered_map
< std :: string
, bool >& discardedPolicies
, Policy
& pol
) const
183 // cout<<"Got question for nameserver IP "<<address.toString()<<endl;
184 for ( const auto & z
: d_zones
) {
185 if ( z
-> getPriority () >= pol
. getPriority ()) {
188 const auto & zoneName
= z
-> getName ();
189 if ( discardedPolicies
. find ( zoneName
) != discardedPolicies
. end ()) {
193 if ( z
-> findNSIPPolicy ( address
, pol
)) {
194 // cerr<<"Had a hit on the nameserver ("<<address.toString()<<") used to process the query"<<endl;
201 bool DNSFilterEngine :: getQueryPolicy ( const DNSName
& qname
, const ComboAddress
& ca
, const std :: unordered_map
< std :: string
, bool >& discardedPolicies
, Policy
& pol
) const
203 // cout<<"Got question for "<<qname<<" from "<<ca.toString()<<endl;
204 std :: vector
< bool > zoneEnabled ( d_zones
. size ());
206 bool allEmpty
= true ;
207 for ( const auto & z
: d_zones
) {
209 if ( z
-> getPriority () >= pol
. getPriority ()) {
212 const auto & zoneName
= z
-> getName ();
213 if ( discardedPolicies
. find ( zoneName
) != discardedPolicies
. end ()) {
217 if ( z
-> hasQNamePolicies () || z
-> hasClientPolicies ()) {
226 zoneEnabled
[ count
] = enabled
;
234 /* prepare the wildcard-based names */
235 std :: vector
< DNSName
> wcNames
;
236 wcNames
. reserve ( qname
. countLabels ());
239 wcNames
. emplace_back ( g_wildcarddnsname
+ s
);
243 for ( const auto & z
: d_zones
) {
244 if (! zoneEnabled
[ count
]) {
249 if ( z
-> findClientPolicy ( ca
, pol
)) {
250 // cerr<<"Had a hit on the IP address ("<<ca.toString()<<") of the client"<<endl;
254 if ( z
-> findExactQNamePolicy ( qname
, pol
)) {
255 // cerr<<"Had a hit on the name of the query"<<endl;
259 for ( const auto & wc
: wcNames
) {
260 if ( z
-> findExactQNamePolicy ( wc
, pol
)) {
261 // cerr<<"Had a hit on the name of the query"<<endl;
272 bool DNSFilterEngine :: getPostPolicy ( const vector
< DNSRecord
>& records
, const std :: unordered_map
< std :: string
, bool >& discardedPolicies
, Policy
& pol
) const
275 for ( const auto & r
: records
) {
276 if ( r
. d_place
!= DNSResourceRecord :: ANSWER
)
278 if ( r
. d_type
== QType :: A
) {
279 if ( auto rec
= getRR
< ARecordContent
>( r
)) {
283 else if ( r
. d_type
== QType :: AAAA
) {
284 if ( auto rec
= getRR
< AAAARecordContent
>( r
)) {
291 for ( const auto & z
: d_zones
) {
292 if ( z
-> getPriority () >= pol
. getPriority ()) {
295 const auto & zoneName
= z
-> getName ();
296 if ( discardedPolicies
. find ( zoneName
) != discardedPolicies
. end ()) {
300 if ( z
-> findResponsePolicy ( ca
, pol
)) {
308 void DNSFilterEngine :: assureZones ( size_t zone
)
310 if ( d_zones
. size () <= zone
)
311 d_zones
. resize ( zone
+ 1 );
314 void DNSFilterEngine :: Zone :: addClientTrigger ( const Netmask
& nm
, Policy
&& pol
)
316 pol
. d_zoneData
= d_zoneData
;
317 pol
. d_type
= PolicyType :: ClientIP
;
318 d_qpolAddr
. insert ( nm
). second
= std :: move ( pol
);
321 void DNSFilterEngine :: Zone :: addResponseTrigger ( const Netmask
& nm
, Policy
&& pol
)
323 pol
. d_zoneData
= d_zoneData
;
324 pol
. d_type
= PolicyType :: ResponseIP
;
325 d_postpolAddr
. insert ( nm
). second
= std :: move ( pol
);
328 void DNSFilterEngine :: Zone :: addQNameTrigger ( const DNSName
& n
, Policy
&& pol
, bool ignoreDuplicate
)
330 auto it
= d_qpolName
. find ( n
);
332 if ( it
!= d_qpolName
. end ()) {
333 auto & existingPol
= it
-> second
;
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 ());
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 ());
343 existingPol
. d_custom
. reserve ( existingPol
. d_custom
. size () + pol
. d_custom
. size ());
345 std :: move ( pol
. d_custom
. begin (), pol
. d_custom
. end (), std :: back_inserter ( existingPol
. d_custom
));
348 auto & qpol
= d_qpolName
. insert ({ n
, std :: move ( pol
)}). first
-> second
;
349 qpol
. d_zoneData
= d_zoneData
;
350 qpol
. d_type
= PolicyType :: QName
;
354 void DNSFilterEngine :: Zone :: addNSTrigger ( const DNSName
& n
, Policy
&& pol
)
356 pol
. d_zoneData
= d_zoneData
;
357 pol
. d_type
= PolicyType :: NSDName
;
358 d_propolName
. insert ({ n
, std :: move ( pol
)});
361 void DNSFilterEngine :: Zone :: addNSIPTrigger ( const Netmask
& nm
, Policy
&& pol
)
363 pol
. d_zoneData
= d_zoneData
;
364 pol
. d_type
= PolicyType :: NSIP
;
365 d_propolNSAddr
. insert ( nm
). second
= std :: move ( pol
);
368 bool DNSFilterEngine :: Zone :: rmClientTrigger ( const Netmask
& nm
, const Policy
& pol
)
370 d_qpolAddr
. erase ( nm
);
374 bool DNSFilterEngine :: Zone :: rmResponseTrigger ( const Netmask
& nm
, const Policy
& pol
)
376 d_postpolAddr
. erase ( nm
);
380 bool DNSFilterEngine :: Zone :: rmQNameTrigger ( const DNSName
& n
, const Policy
& pol
)
382 auto found
= d_qpolName
. find ( n
);
383 if ( found
== d_qpolName
. end ()) {
387 auto & existing
= found
-> second
;
388 if ( existing
. d_kind
!= DNSFilterEngine :: PolicyKind :: Custom
) {
389 d_qpolName
. erase ( found
);
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
);
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
);
414 bool DNSFilterEngine :: Zone :: rmNSTrigger ( const DNSName
& n
, const Policy
& pol
)
416 d_propolName
. erase ( n
); // XXX verify policy matched? =pol;
420 bool DNSFilterEngine :: Zone :: rmNSIPTrigger ( const Netmask
& nm
, const Policy
& pol
)
422 d_propolNSAddr
. erase ( nm
);
426 DNSRecord
DNSFilterEngine :: Policy :: getRecordFromCustom ( const DNSName
& qname
, const std :: shared_ptr
< DNSRecordContent
>& custom
) const
430 dr
. d_type
= custom
-> getType ();
432 dr
. d_class
= QClass :: IN
;
433 dr
. d_place
= DNSResourceRecord :: ANSWER
;
434 dr
. d_content
= custom
;
436 if ( dr
. d_type
== QType :: CNAME
) {
437 const auto content
= std :: dynamic_pointer_cast
< CNAMERecordContent
>( custom
);
439 DNSName target
= content
-> getTarget ();
440 if ( target
. isWildcard ()) {
442 dr
. d_content
= std :: make_shared
< CNAMERecordContent
>( qname
+ target
);
450 std :: vector
< DNSRecord
> DNSFilterEngine :: Policy :: getCustomRecords ( const DNSName
& qname
, uint16_t qtype
) const
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" );
456 std :: vector
< DNSRecord
> result
;
458 for ( const auto & custom
: d_custom
) {
459 if ( qtype
!= QType :: ANY
&& qtype
!= custom
-> getType () && custom
-> getType () != QType :: CNAME
) {
465 dr
. d_type
= custom
-> getType ();
467 dr
. d_class
= QClass :: IN
;
468 dr
. d_place
= DNSResourceRecord :: ANSWER
;
469 dr
. d_content
= custom
;
471 if ( dr
. d_type
== QType :: CNAME
) {
472 const auto content
= std :: dynamic_pointer_cast
< CNAMERecordContent
>( custom
);
474 DNSName target
= content
-> getTarget ();
475 if ( target
. isWildcard ()) {
477 dr
. d_content
= std :: make_shared
< CNAMERecordContent
>( qname
+ target
);
482 result
. emplace_back ( getRecordFromCustom ( qname
, custom
));
488 std :: string
DNSFilterEngine :: getKindToString ( DNSFilterEngine :: PolicyKind kind
)
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-" );
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 ();
507 throw std :: runtime_error ( "Unexpected DNSFilterEngine::Policy kind" );
511 std :: string
DNSFilterEngine :: getTypeToString ( DNSFilterEngine :: PolicyType type
)
514 case DNSFilterEngine :: PolicyType :: None
:
516 case DNSFilterEngine :: PolicyType :: QName
:
518 case DNSFilterEngine :: PolicyType :: ClientIP
:
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" ;
527 throw std :: runtime_error ( "Unexpected DNSFilterEngine::Policy type" );
531 std :: vector
< DNSRecord
> DNSFilterEngine :: Policy :: getRecords ( const DNSName
& qname
) const
533 std :: vector
< DNSRecord
> result
;
535 if ( d_kind
== PolicyKind :: Custom
) {
536 result
= getCustomRecords ( qname
, QType :: ANY
);
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
));
551 void DNSFilterEngine :: Zone :: dumpNamedPolicy ( FILE * fp
, const DNSName
& name
, const Policy
& pol
)
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 ());
559 DNSName
DNSFilterEngine :: Zone :: maskToRPZ ( const Netmask
& nm
)
561 int bits
= nm
. getBits ();
562 DNSName
res ( std :: to_string ( bits
));
563 const auto & addr
= nm
. getNetwork ();
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 ]));
571 const auto str
= addr
. toString ();
572 const auto len
= str
. size ();
573 std :: string :: size_type begin
= 0 ;
575 while ( begin
< len
) {
576 std :: string :: size_type end
= str
. find ( ":" , begin
);
578 if ( end
!= string :: npos
) {
579 sub
= str
. substr ( begin
, end
- begin
);
582 sub
= str
. substr ( begin
);
586 temp
= DNSName ( "zz" ) + temp
;
589 temp
= DNSName ( sub
) + temp
;
592 if ( end
== string :: npos
) {
604 void DNSFilterEngine :: Zone :: dumpAddrPolicy ( FILE * fp
, const Netmask
& nm
, const DNSName
& name
, const Policy
& pol
)
606 DNSName full
= maskToRPZ ( nm
);
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 ());
615 void DNSFilterEngine :: Zone :: dump ( FILE * fp
) const
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 ());
621 for ( const auto & pair
: d_qpolName
) {
622 dumpNamedPolicy ( fp
, pair
. first
+ d_domain
, pair
. second
);
625 for ( const auto & pair
: d_propolName
) {
626 dumpNamedPolicy ( fp
, pair
. first
+ DNSName ( "rpz-nsdname." ) + d_domain
, pair
. second
);
629 for ( const auto pair
: d_qpolAddr
) {
630 dumpAddrPolicy ( fp
, pair
. first
, DNSName ( "rpz-client-ip." ) + d_domain
, pair
. second
);
633 for ( const auto pair
: d_propolNSAddr
) {
634 dumpAddrPolicy ( fp
, pair
. first
, DNSName ( "rpz-nsip." ) + d_domain
, pair
. second
);
637 for ( const auto pair
: d_postpolAddr
) {
638 dumpAddrPolicy ( fp
, pair
. first
, DNSName ( "rpz-ip." ) + d_domain
, pair
. second
);
642 void mergePolicyTags ( std :: unordered_set
< std :: string
>& tags
, const std :: unordered_set
< std :: string
>& newTags
)
644 for ( const auto & tag
: newTags
) {