]>
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 :: findQNamePolicy ( const DNSName
& qname
, DNSFilterEngine :: Policy
& pol
) const
36 return findNamedPolicy ( d_qpolName
, qname
, pol
);
39 bool DNSFilterEngine :: Zone :: findNSPolicy ( const DNSName
& qname
, DNSFilterEngine :: Policy
& pol
) const
41 return findNamedPolicy ( 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
) const
73 /* for www.powerdns.com, we need to check:
80 std :: unordered_map
< DNSName
, DNSFilterEngine :: Policy
>:: const_iterator iter
;
81 iter
= polmap
. find ( qname
);
83 if ( iter
!= polmap
. end ()) {
90 iter
= polmap
. find ( g_wildcarddnsname
+ s
);
91 if ( iter
!= polmap
. end ()) {
99 DNSFilterEngine :: Policy
DNSFilterEngine :: getProcessingPolicy ( const DNSName
& qname
, const std :: unordered_map
< std :: string
, bool >& discardedPolicies
) const
101 // cout<<"Got question for nameserver name "<<qname<<endl;
103 for ( const auto & z
: d_zones
) {
104 const auto zoneName
= z
-> getName ();
105 if ( zoneName
&& discardedPolicies
. find (* zoneName
) != discardedPolicies
. end ()) {
109 if ( z
-> findNSPolicy ( qname
, pol
)) {
110 // cerr<<"Had a hit on the nameserver ("<<qname<<") used to process the query"<<endl;
117 DNSFilterEngine :: Policy
DNSFilterEngine :: getProcessingPolicy ( const ComboAddress
& address
, const std :: unordered_map
< std :: string
, bool >& discardedPolicies
) const
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 ()) {
127 if ( z
-> findNSIPPolicy ( address
, pol
)) {
128 // cerr<<"Had a hit on the nameserver ("<<address.toString()<<") used to process the query"<<endl;
135 DNSFilterEngine :: Policy
DNSFilterEngine :: getQueryPolicy ( const DNSName
& qname
, const ComboAddress
& ca
, const std :: unordered_map
< std :: string
, bool >& discardedPolicies
) const
137 // cout<<"Got question for "<<qname<<" from "<<ca.toString()<<endl;
139 for ( const auto & z
: d_zones
) {
140 const auto zoneName
= z
-> getName ();
141 if ( zoneName
&& discardedPolicies
. find (* zoneName
) != discardedPolicies
. end ()) {
145 if ( z
-> findQNamePolicy ( qname
, pol
)) {
146 // cerr<<"Had a hit on the name of the query"<<endl;
150 if ( z
-> findClientPolicy ( ca
, pol
)) {
151 // cerr<<"Had a hit on the IP address ("<<ca.toString()<<") of the client"<<endl;
159 DNSFilterEngine :: Policy
DNSFilterEngine :: getPostPolicy ( const vector
< DNSRecord
>& records
, const std :: unordered_map
< std :: string
, bool >& discardedPolicies
) const
163 for ( const auto & r
: records
) {
164 if ( r
. d_place
!= DNSResourceRecord :: ANSWER
)
166 if ( r
. d_type
== QType :: A
) {
167 if ( auto rec
= getRR
< ARecordContent
>( r
)) {
171 else if ( r
. d_type
== QType :: AAAA
) {
172 if ( auto rec
= getRR
< AAAARecordContent
>( r
)) {
179 for ( const auto & z
: d_zones
) {
180 const auto zoneName
= z
-> getName ();
181 if ( zoneName
&& discardedPolicies
. find (* zoneName
) != discardedPolicies
. end ()) {
185 if ( z
-> findResponsePolicy ( ca
, pol
)) {
193 void DNSFilterEngine :: assureZones ( size_t zone
)
195 if ( d_zones
. size () <= zone
)
196 d_zones
. resize ( zone
+ 1 );
199 void DNSFilterEngine :: Zone :: addClientTrigger ( const Netmask
& nm
, Policy
&& pol
)
202 pol
. d_type
= PolicyType :: ClientIP
;
203 d_qpolAddr
. insert ( nm
). second
= std :: move ( pol
);
206 void DNSFilterEngine :: Zone :: addResponseTrigger ( const Netmask
& nm
, Policy
&& pol
)
209 pol
. d_type
= PolicyType :: ResponseIP
;
210 d_postpolAddr
. insert ( nm
). second
= std :: move ( pol
);
213 void DNSFilterEngine :: Zone :: addQNameTrigger ( const DNSName
& n
, Policy
&& pol
, bool ignoreDuplicate
)
215 auto it
= d_qpolName
. find ( n
);
217 if ( it
!= d_qpolName
. end ()) {
218 auto & existingPol
= it
-> second
;
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 ());
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 ());
228 existingPol
. d_custom
. reserve ( existingPol
. d_custom
. size () + pol
. d_custom
. size ());
230 std :: move ( pol
. d_custom
. begin (), pol
. d_custom
. end (), std :: back_inserter ( existingPol
. d_custom
));
233 auto & qpol
= d_qpolName
. insert ({ n
, std :: move ( pol
)}). first
-> second
;
234 qpol
. d_name
= d_name
;
235 qpol
. d_type
= PolicyType :: QName
;
239 void DNSFilterEngine :: Zone :: addNSTrigger ( const DNSName
& n
, Policy
&& pol
)
242 pol
. d_type
= PolicyType :: NSDName
;
243 d_propolName
. insert ({ n
, std :: move ( pol
)});
246 void DNSFilterEngine :: Zone :: addNSIPTrigger ( const Netmask
& nm
, Policy
&& pol
)
249 pol
. d_type
= PolicyType :: NSIP
;
250 d_propolNSAddr
. insert ( nm
). second
= std :: move ( pol
);
253 bool DNSFilterEngine :: Zone :: rmClientTrigger ( const Netmask
& nm
, const Policy
& pol
)
255 d_qpolAddr
. erase ( nm
);
259 bool DNSFilterEngine :: Zone :: rmResponseTrigger ( const Netmask
& nm
, const Policy
& pol
)
261 d_postpolAddr
. erase ( nm
);
265 bool DNSFilterEngine :: Zone :: rmQNameTrigger ( const DNSName
& n
, const Policy
& pol
)
267 auto it
= d_qpolName
. find ( n
);
268 if ( it
== d_qpolName
. end ()) {
272 auto & existing
= it
-> second
;
273 if ( existing
. d_kind
!= DNSFilterEngine :: PolicyKind :: Custom
) {
274 d_qpolName
. erase ( it
);
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
);
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
);
299 bool DNSFilterEngine :: Zone :: rmNSTrigger ( const DNSName
& n
, const Policy
& pol
)
301 d_propolName
. erase ( n
); // XXX verify policy matched? =pol;
305 bool DNSFilterEngine :: Zone :: rmNSIPTrigger ( const Netmask
& nm
, const Policy
& pol
)
307 d_propolNSAddr
. erase ( nm
);
311 DNSRecord
DNSFilterEngine :: Policy :: getRecordFromCustom ( const DNSName
& qname
, const std :: shared_ptr
< DNSRecordContent
>& custom
) const
315 dr
. d_type
= custom
-> getType ();
317 dr
. d_class
= QClass :: IN
;
318 dr
. d_place
= DNSResourceRecord :: ANSWER
;
319 dr
. d_content
= custom
;
321 if ( dr
. d_type
== QType :: CNAME
) {
322 const auto content
= std :: dynamic_pointer_cast
< CNAMERecordContent
>( custom
);
324 DNSName target
= content
-> getTarget ();
325 if ( target
. isWildcard ()) {
327 dr
. d_content
= std :: make_shared
< CNAMERecordContent
>( qname
+ target
);
335 std :: vector
< DNSRecord
> DNSFilterEngine :: Policy :: getCustomRecords ( const DNSName
& qname
, uint16_t qtype
) const
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" );
341 std :: vector
< DNSRecord
> result
;
343 for ( const auto & custom
: d_custom
) {
344 if ( qtype
!= QType :: ANY
&& qtype
!= custom
-> getType () && custom
-> getType () != QType :: CNAME
) {
350 dr
. d_type
= custom
-> getType ();
352 dr
. d_class
= QClass :: IN
;
353 dr
. d_place
= DNSResourceRecord :: ANSWER
;
354 dr
. d_content
= custom
;
356 if ( dr
. d_type
== QType :: CNAME
) {
357 const auto content
= std :: dynamic_pointer_cast
< CNAMERecordContent
>( custom
);
359 DNSName target
= content
-> getTarget ();
360 if ( target
. isWildcard ()) {
362 dr
. d_content
= std :: make_shared
< CNAMERecordContent
>( qname
+ target
);
367 result
. emplace_back ( getRecordFromCustom ( qname
, custom
));
373 std :: string
DNSFilterEngine :: getKindToString ( DNSFilterEngine :: PolicyKind kind
)
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-" );
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 ();
392 throw std :: runtime_error ( "Unexpected DNSFilterEngine::Policy kind" );
396 std :: string
DNSFilterEngine :: getTypeToString ( DNSFilterEngine :: PolicyType type
)
399 case DNSFilterEngine :: PolicyType :: None
:
401 case DNSFilterEngine :: PolicyType :: QName
:
403 case DNSFilterEngine :: PolicyType :: ClientIP
:
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" ;
412 throw std :: runtime_error ( "Unexpected DNSFilterEngine::Policy type" );
416 std :: vector
< DNSRecord
> DNSFilterEngine :: Policy :: getRecords ( const DNSName
& qname
) const
418 std :: vector
< DNSRecord
> result
;
420 if ( d_kind
== PolicyKind :: Custom
) {
421 result
= getCustomRecords ( qname
, QType :: ANY
);
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
));
436 void DNSFilterEngine :: Zone :: dumpNamedPolicy ( FILE * fp
, const DNSName
& name
, const Policy
& pol
) const
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 ());
444 DNSName
DNSFilterEngine :: Zone :: maskToRPZ ( const Netmask
& nm
)
446 int bits
= nm
. getBits ();
447 DNSName
res ( std :: to_string ( bits
));
448 const auto & addr
= nm
. getNetwork ();
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 ]));
456 const auto str
= addr
. toString ();
457 const auto len
= str
. size ();
458 std :: string :: size_type begin
= 0 ;
460 while ( begin
< len
) {
461 std :: string :: size_type end
= str
. find ( ":" , begin
);
463 if ( end
!= string :: npos
) {
464 sub
= str
. substr ( begin
, end
- begin
);
467 sub
= str
. substr ( begin
);
471 temp
= DNSName ( "zz" ) + temp
;
474 temp
= DNSName ( sub
) + temp
;
477 if ( end
== string :: npos
) {
489 void DNSFilterEngine :: Zone :: dumpAddrPolicy ( FILE * fp
, const Netmask
& nm
, const DNSName
& name
, const Policy
& pol
) const
491 DNSName full
= maskToRPZ ( nm
);
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 ());
500 void DNSFilterEngine :: Zone :: dump ( FILE * fp
) const
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 ());
506 for ( const auto & pair
: d_qpolName
) {
507 dumpNamedPolicy ( fp
, pair
. first
+ d_domain
, pair
. second
);
510 for ( const auto & pair
: d_propolName
) {
511 dumpNamedPolicy ( fp
, pair
. first
+ DNSName ( "rpz-nsdname." ) + d_domain
, pair
. second
);
514 for ( const auto pair
: d_qpolAddr
) {
515 dumpAddrPolicy ( fp
, pair
-> first
, DNSName ( "rpz-client-ip." ) + d_domain
, pair
-> second
);
518 for ( const auto pair
: d_propolNSAddr
) {
519 dumpAddrPolicy ( fp
, pair
-> first
, DNSName ( "rpz-nsip." ) + d_domain
, pair
-> second
);
522 for ( const auto pair
: d_postpolAddr
) {
523 dumpAddrPolicy ( fp
, pair
-> first
, DNSName ( "rpz-ip." ) + d_domain
, pair
-> second
);