]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/reczones.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 "arguments.hh"
27 #include "zoneparser-tng.hh"
29 #include "dnsrecords.hh"
30 #include "root-addresses.hh"
35 static thread_local set
< DNSName
> t_rootNSZones
;
37 static void insertIntoRootNSZones ( const DNSName
& name
) {
38 // do not insert dot, wiping dot's NS records from the cache in primeRootNSZones()
39 // will cause infinite recursion
41 t_rootNSZones
. insert ( name
);
48 const vState validationState
= vState :: Insecure
;
49 vector
< DNSRecord
> nsset
;
50 t_rootNSZones
. clear ();
52 if (:: arg ()[ "hint-file" ]. empty ()) {
53 DNSRecord arr
, aaaarr
, nsrr
;
54 nsrr
. d_name
= g_rootdnsname
;
56 aaaarr
. d_type
= QType :: AAAA
;
57 nsrr
. d_type
= QType :: NS
;
58 arr
. d_ttl
= aaaarr
. d_ttl
= nsrr
. d_ttl
= time ( 0 )+ 3600000 ;
60 for ( char c
= 'a' ; c
<= 'm' ;++ c
) {
62 strncpy ( templ
, "a.root-servers.net." , sizeof ( templ
) - 1 );
63 templ
[ sizeof ( templ
)- 1 ] = '\0' ;
65 aaaarr
. d_name
= arr
. d_name
= DNSName ( templ
);
66 insertIntoRootNSZones ( arr
. d_name
. getLastLabel ());
67 nsrr
. d_content
= std :: make_shared
< NSRecordContent
>( DNSName ( templ
));
68 arr
. d_content
= std :: make_shared
< ARecordContent
>( ComboAddress ( rootIps4
[ c
- 'a' ]));
69 vector
< DNSRecord
> aset
;
71 s_RC
-> replace ( time ( 0 ), DNSName ( templ
), QType ( QType :: A
), aset
, vector
< std :: shared_ptr
< RRSIGRecordContent
>>(), vector
< std :: shared_ptr
< DNSRecord
>>(), true , boost :: none
, boost :: none
, validationState
); // auth, nuke it all
72 if ( rootIps6
[ c
- 'a' ] != NULL
) {
73 aaaarr
. d_content
= std :: make_shared
< AAAARecordContent
>( ComboAddress ( rootIps6
[ c
- 'a' ]));
75 vector
< DNSRecord
> aaaaset
;
76 aaaaset
. push_back ( aaaarr
);
77 s_RC
-> replace ( time ( 0 ), DNSName ( templ
), QType ( QType :: AAAA
), aaaaset
, vector
< std :: shared_ptr
< RRSIGRecordContent
>>(), vector
< std :: shared_ptr
< DNSRecord
>>(), true , boost :: none
, boost :: none
, validationState
);
80 nsset
. push_back ( nsrr
);
84 ZoneParserTNG
zpt (:: arg ()[ "hint-file" ]);
85 zpt
. setMaxGenerateSteps (:: arg (). asNum ( "max-generate-steps" ));
89 set
< DNSName
> seenAAAA
;
93 if ( rr
. qtype
. getCode ()== QType :: A
) {
94 seenA
. insert ( rr
. qname
);
95 vector
< DNSRecord
> aset
;
96 aset
. push_back ( DNSRecord ( rr
));
97 s_RC
-> replace ( time ( 0 ), rr
. qname
, QType ( QType :: A
), aset
, vector
< std :: shared_ptr
< RRSIGRecordContent
>>(), vector
< std :: shared_ptr
< DNSRecord
>>(), true , boost :: none
, boost :: none
, validationState
); // auth, etc see above
98 } else if ( rr
. qtype
. getCode ()== QType :: AAAA
) {
99 seenAAAA
. insert ( rr
. qname
);
100 vector
< DNSRecord
> aaaaset
;
101 aaaaset
. push_back ( DNSRecord ( rr
));
102 s_RC
-> replace ( time ( 0 ), rr
. qname
, QType ( QType :: AAAA
), aaaaset
, vector
< std :: shared_ptr
< RRSIGRecordContent
>>(), vector
< std :: shared_ptr
< DNSRecord
>>(), true , boost :: none
, boost :: none
, validationState
);
103 } else if ( rr
. qtype
. getCode ()== QType :: NS
) {
104 seenNS
. insert ( DNSName ( rr
. content
));
105 rr
. content
= toLower ( rr
. content
);
106 nsset
. push_back ( DNSRecord ( rr
));
108 insertIntoRootNSZones ( rr
. qname
. getLastLabel ());
111 // Check reachability of A and AAAA records
112 bool reachableA
= false , reachableAAAA
= false ;
113 for ( auto const & r
: seenA
) {
114 if ( seenNS
. count ( r
)) {
118 for ( auto const & r
: seenAAAA
) {
119 if ( seenNS
. count ( r
)) {
120 reachableAAAA
= true ;
123 if ( SyncRes :: s_doIPv4
&& ! SyncRes :: s_doIPv6
&& ! reachableA
) {
124 g_log
<< Logger :: Error
<< "Running IPv4 only but no IPv4 root hints" << endl
;
127 if (! SyncRes :: s_doIPv4
&& SyncRes :: s_doIPv6
&& ! reachableAAAA
) {
128 g_log
<< Logger :: Error
<< "Running IPv6 only but no IPv6 root hints" << endl
;
131 if ( SyncRes :: s_doIPv4
&& SyncRes :: s_doIPv6
&& ! reachableA
&& ! reachableAAAA
) {
132 g_log
<< Logger :: Error
<< "No valid root hints" << endl
;
137 s_RC
-> doWipeCache ( g_rootdnsname
, false , QType :: NS
);
138 s_RC
-> replace ( time ( 0 ), g_rootdnsname
, QType ( QType :: NS
), nsset
, vector
< std :: shared_ptr
< RRSIGRecordContent
>>(), vector
< std :: shared_ptr
< DNSRecord
>>(), false , boost :: none
, boost :: none
, validationState
); // and stuff in the cache
143 // Do not only put the root hints into the cache, but also make sure
144 // the NS records of the top level domains of the names of the root
145 // servers are in the cache. We need these to correctly determine the
146 // security status of that specific domain (normally
147 // root-servers.net). This is caused by the accident that the root
148 // servers are authoritative for root-servers.net, and some
149 // implementations reply not with a delegation on a root-servers.net
150 // DS query, but with a NODATA response (the domain is unsigned).
151 void primeRootNSZones ( bool dnssecmode
, unsigned int depth
)
154 gettimeofday (& now
, 0 );
158 sr
. setDoDNSSEC ( true );
159 sr
. setDNSSECValidationRequested ( true );
162 // beginResolve() can yield to another mthread that could trigger t_rootNSZones updates,
163 // so make a local copy
164 set
< DNSName
> copy ( t_rootNSZones
);
165 for ( const auto & qname
: copy
) {
166 s_RC
-> doWipeCache ( qname
, false , QType :: NS
);
167 vector
< DNSRecord
> ret
;
168 sr
. beginResolve ( qname
, QType ( QType :: NS
), QClass :: IN
, ret
, depth
+ 1 );
172 static void makeNameToIPZone ( std :: shared_ptr
< SyncRes :: domainmap_t
> newMap
, const DNSName
& hostname
, const string
& ip
)
174 SyncRes :: AuthDomain ad
;
175 ad
. d_rdForward
= false ;
179 dr
. d_place
= DNSResourceRecord :: ANSWER
;
181 dr
. d_type
= QType :: SOA
;
183 dr
. d_content
= DNSRecordContent :: mastermake ( QType :: SOA
, 1 , "localhost. root 1 604800 86400 2419200 604800" );
185 ad
. d_records
. insert ( dr
);
188 dr
. d_content
= std :: make_shared
< NSRecordContent
>( "localhost." );
190 ad
. d_records
. insert ( dr
);
193 dr
. d_content
= DNSRecordContent :: mastermake ( QType :: A
, 1 , ip
);
194 ad
. d_records
. insert ( dr
);
196 if ( newMap
-> count ( dr
. d_name
)) {
197 g_log
<< Logger :: Warning
<< "Hosts file will not overwrite zone '" << dr
. d_name
<< "' already loaded" << endl
;
200 g_log
<< Logger :: Warning
<< "Inserting forward zone '" << dr
. d_name
<< "' based on hosts file" << endl
;
202 (* newMap
)[ ad
. d_name
]= ad
;
206 //! parts[0] must be an IP address, the rest must be host names
207 static void makeIPToNamesZone ( std :: shared_ptr
< SyncRes :: domainmap_t
> newMap
, const vector
< string
>& parts
)
209 string address
= parts
[ 0 ];
210 vector
< string
> ipparts
;
211 stringtok ( ipparts
, address
, "." );
213 SyncRes :: AuthDomain ad
;
214 ad
. d_rdForward
= false ;
217 for ( int n
= ipparts
. size ()- 1 ; n
>= 0 ; -- n
) {
218 dr
. d_name
. appendRawLabel ( ipparts
[ n
]);
220 dr
. d_name
. appendRawLabel ( "in-addr" );
221 dr
. d_name
. appendRawLabel ( "arpa" );
223 dr
. d_place
= DNSResourceRecord :: ANSWER
;
225 dr
. d_type
= QType :: SOA
;
226 dr
. d_content
= DNSRecordContent :: mastermake ( QType :: SOA
, 1 , "localhost. root 1 604800 86400 2419200 604800" );
228 ad
. d_records
. insert ( dr
);
231 dr
. d_content
= std :: make_shared
< NSRecordContent
>( DNSName ( "localhost." ));
233 ad
. d_records
. insert ( dr
);
234 dr
. d_type
= QType :: PTR
;
236 if ( ipparts
. size ()== 4 ) // otherwise this is a partial zone
237 for ( unsigned int n
= 1 ; n
< parts
. size (); ++ n
) {
238 dr
. d_content
= DNSRecordContent :: mastermake ( QType :: PTR
, 1 , DNSName ( parts
[ n
]). toString ()); // XXX FIXME DNSNAME PAIN CAN THIS BE RIGHT?
239 ad
. d_records
. insert ( dr
);
242 if ( newMap
-> count ( dr
. d_name
)) {
243 g_log
<< Logger :: Warning
<< "Will not overwrite zone '" << dr
. d_name
<< "' already loaded" << endl
;
246 if ( ipparts
. size ()== 4 )
247 g_log
<< Logger :: Warning
<< "Inserting reverse zone '" << dr
. d_name
<< "' based on hosts file" << endl
;
248 ad
. d_name
= dr
. d_name
;
249 (* newMap
)[ ad
. d_name
]= ad
;
255 /* mission in life: parse three cases
262 ComboAddress
parseIPAndPort ( const std :: string
& input
, uint16_t port
)
264 if ( input
. find ( ':' ) == string :: npos
|| input
. empty ()) // common case
265 return ComboAddress ( input
, port
);
267 pair
< string
, string
> both
;
270 both
= splitField ( input
, ':' );
271 uint16_t newport
= static_cast < uint16_t >( pdns_stou ( both
. second
));
272 return ComboAddress ( both
. first
, newport
);
276 if ( input
[ 0 ]== '[' ) { // case 4
277 both
= splitField ( input
. substr ( 1 ), ']' );
278 return ComboAddress ( both
. first
, both
. second
. empty () ? port
: static_cast < uint16_t >( pdns_stou ( both
. second
. substr ( 1 ))));
281 return ComboAddress ( input
, port
); // case 3
285 static void convertServersForAD ( const std :: string
& input
, SyncRes :: AuthDomain
& ad
, const char * sepa
, bool verbose
= true )
287 vector
< string
> servers
;
288 stringtok ( servers
, input
, sepa
);
289 ad
. d_servers
. clear ();
291 for ( vector
< string
>:: const_iterator iter
= servers
. begin (); iter
!= servers
. end (); ++ iter
) {
292 if ( verbose
&& iter
!= servers
. begin ())
295 ComboAddress addr
= parseIPAndPort (* iter
, 53 );
297 g_log
<< addr
. toStringWithPort ();
298 ad
. d_servers
. push_back ( addr
);
304 static void * pleaseUseNewSDomainsMap ( std :: shared_ptr
< SyncRes :: domainmap_t
> newmap
)
306 SyncRes :: setDomainMap ( newmap
);
310 string
reloadAuthAndForwards ()
312 std :: shared_ptr
< SyncRes :: domainmap_t
> original
= SyncRes :: getDomainMap ();
315 g_log
<< Logger :: Warning
<< "Reloading zones, purging data from cache" << endl
;
317 string configname
=:: arg ()[ "config-dir" ]+ "/recursor.conf" ;
318 if (:: arg ()[ "config-name" ]!= "" ) {
319 configname
=:: arg ()[ "config-dir" ]+ "/recursor-" +:: arg ()[ "config-name" ]+ ".conf" ;
321 cleanSlashes ( configname
);
323 if (!:: arg (). preParseFile ( configname
. c_str (), "forward-zones" ))
324 throw runtime_error ( "Unable to re-parse configuration file '" + configname
+ "'" );
325 :: arg (). preParseFile ( configname
. c_str (), "forward-zones-file" );
326 :: arg (). preParseFile ( configname
. c_str (), "forward-zones-recurse" );
327 :: arg (). preParseFile ( configname
. c_str (), "auth-zones" );
328 :: arg (). preParseFile ( configname
. c_str (), "export-etc-hosts" , "off" );
329 :: arg (). preParseFile ( configname
. c_str (), "serve-rfc1918" );
330 :: arg (). preParseFile ( configname
. c_str (), "include-dir" );
331 :: arg (). preParse ( g_argc
, g_argv
, "include-dir" );
333 // then process includes
334 std :: vector
< std :: string
> extraConfigs
;
335 :: arg (). gatherIncludes ( extraConfigs
);
337 for ( const std :: string
& fn
: extraConfigs
) {
338 if (!:: arg (). preParseFile ( fn
. c_str (), "forward-zones" , :: arg ()[ "forward-zones" ]))
339 throw runtime_error ( "Unable to re-parse configuration file include '" + fn
+ "'" );
340 :: arg (). preParseFile ( fn
. c_str (), "forward-zones-file" , :: arg ()[ "forward-zones-file" ]);
341 :: arg (). preParseFile ( fn
. c_str (), "forward-zones-recurse" , :: arg ()[ "forward-zones-recurse" ]);
342 :: arg (). preParseFile ( fn
. c_str (), "auth-zones" ,:: arg ()[ "auth-zones" ]);
343 :: arg (). preParseFile ( fn
. c_str (), "export-etc-hosts" ,:: arg ()[ "export-etc-hosts" ]);
344 :: arg (). preParseFile ( fn
. c_str (), "serve-rfc1918" ,:: arg ()[ "serve-rfc1918" ]);
347 :: arg (). preParse ( g_argc
, g_argv
, "forward-zones" );
348 :: arg (). preParse ( g_argc
, g_argv
, "forward-zones-file" );
349 :: arg (). preParse ( g_argc
, g_argv
, "forward-zones-recurse" );
350 :: arg (). preParse ( g_argc
, g_argv
, "auth-zones" );
351 :: arg (). preParse ( g_argc
, g_argv
, "export-etc-hosts" );
352 :: arg (). preParse ( g_argc
, g_argv
, "serve-rfc1918" );
354 std :: shared_ptr
< SyncRes :: domainmap_t
> newDomainMap
= parseAuthAndForwards ();
356 // purge both original and new names
357 std :: set
< DNSName
> oldAndNewDomains
;
358 for ( const auto & i
: * newDomainMap
) {
359 oldAndNewDomains
. insert ( i
. first
);
363 for ( const auto & i
: * original
) {
364 oldAndNewDomains
. insert ( i
. first
);
368 for ( const auto & i
: oldAndNewDomains
) {
369 broadcastAccFunction
< uint64_t >([&]{ return pleaseWipeCache ( i
, true , 0xffff );});
370 broadcastAccFunction
< uint64_t >([&]{ return pleaseWipePacketCache ( i
, true , 0xffff );});
371 broadcastAccFunction
< uint64_t >([&]{ return pleaseWipeAndCountNegCache ( i
, true );});
374 broadcastFunction ([=]{ return pleaseUseNewSDomainsMap ( newDomainMap
);});
377 catch ( std :: exception
& e
) {
378 g_log
<< Logger :: Error
<< "Encountered error reloading zones, keeping original data: " << e
. what ()<< endl
;
380 catch ( PDNSException
& ae
) {
381 g_log
<< Logger :: Error
<< "Encountered error reloading zones, keeping original data: " << ae
. reason
<< endl
;
384 g_log
<< Logger :: Error
<< "Encountered unknown error reloading zones, keeping original data" << endl
;
386 return "reloading failed, see log \n " ;
389 std :: shared_ptr
< SyncRes :: domainmap_t
> parseAuthAndForwards ()
391 TXTRecordContent :: report ();
392 OPTRecordContent :: report ();
394 auto newMap
= std :: make_shared
< SyncRes :: domainmap_t
>();
396 typedef vector
< string
> parts_t
;
398 const char * option_names
[ 3 ]={ "auth-zones" , "forward-zones" , "forward-zones-recurse" };
399 for ( int n
= 0 ; n
< 3 ; ++ n
) {
401 stringtok ( parts
, :: arg ()[ option_names
[ n
]], " , \t\n\r " );
402 for ( parts_t :: const_iterator iter
= parts
. begin (); iter
!= parts
. end (); ++ iter
) {
403 SyncRes :: AuthDomain ad
;
404 if ((* iter
). find ( '=' ) == string :: npos
)
405 throw PDNSException ( "Error parsing '" + * iter
+ "', missing =" );
406 pair
< string
, string
> headers
= splitField (* iter
, '=' );
408 trim ( headers
. second
);
409 // headers.first=toCanonic("", headers.first);
411 ad
. d_rdForward
= false ;
412 g_log
<< Logger :: Error
<< "Parsing authoritative data for zone '" << headers
. first
<< "' from file '" << headers
. second
<< "'" << endl
;
413 ZoneParserTNG
zpt ( headers
. second
, DNSName ( headers
. first
));
414 zpt
. setMaxGenerateSteps (:: arg (). asNum ( "max-generate-steps" ));
415 DNSResourceRecord rr
;
420 dr
. d_place
= DNSResourceRecord :: ANSWER
;
422 catch ( std :: exception
& e
) {
423 throw PDNSException ( "Error parsing record '" + rr
. qname
. toLogString ()+ "' of type " + rr
. qtype
. getName ()+ " in zone '" + headers
. first
+ "' from file '" + headers
. second
+ "': " + e
. what ());
426 throw PDNSException ( "Error parsing record '" + rr
. qname
. toLogString ()+ "' of type " + rr
. qtype
. getName ()+ " in zone '" + headers
. first
+ "' from file '" + headers
. second
+ "'" );
429 ad
. d_records
. insert ( dr
);
433 g_log
<< Logger :: Error
<< "Redirecting queries for zone '" << headers
. first
<< "' " ;
435 g_log
<< "with recursion " ;
436 ad
. d_rdForward
= true ;
438 else ad
. d_rdForward
= false ;
441 convertServersForAD ( headers
. second
, ad
, ";" );
443 ad
. d_rdForward
= true ;
447 ad
. d_name
= DNSName ( headers
. first
);
448 (* newMap
)[ ad
. d_name
]= ad
;
452 if (!:: arg ()[ "forward-zones-file" ]. empty ()) {
453 g_log
<< Logger :: Warning
<< "Reading zone forwarding information from '" <<:: arg ()[ "forward-zones-file" ]<< "'" << endl
;
454 SyncRes :: AuthDomain ad
;
455 auto fp
= std :: unique_ptr
< FILE , int (*)( FILE *)>( fopen (:: arg ()[ "forward-zones-file" ]. c_str (), "r" ), fclose
);
457 throw PDNSException ( "Error opening forward-zones-file '" +:: arg ()[ "forward-zones-file" ]+ "': " + stringerror ());
462 uint64_t before
= newMap
-> size ();
463 while ( linenum
++, stringfgets ( fp
. get (), line
)) {
465 if ( line
[ 0 ] == '#' ) // Comment line, skip to the next line
467 string domain
, instructions
;
468 tie ( domain
, instructions
)= splitField ( line
, '=' );
469 instructions
= splitField ( instructions
, '#' ). first
; // Remove EOL comments
472 if ( domain
. empty () && instructions
. empty ()) { // empty line
475 if ( boost :: starts_with ( domain
, "+" )) {
476 domain
= domain
. c_str ()+ 1 ;
477 ad
. d_rdForward
= true ;
480 ad
. d_rdForward
= false ;
482 throw PDNSException ( "Error parsing line " + std :: to_string ( linenum
)+ " of " +:: arg ()[ "forward-zones-file" ]);
486 convertServersForAD ( instructions
, ad
, ",; " , false );
489 throw PDNSException ( "Conversion error parsing line " + std :: to_string ( linenum
)+ " of " +:: arg ()[ "forward-zones-file" ]);
492 ad
. d_name
= DNSName ( domain
);
493 (* newMap
)[ ad
. d_name
]= ad
;
495 g_log
<< Logger :: Warning
<< "Done parsing " << newMap
-> size () - before
<< " forwarding instructions from file '" <<:: arg ()[ "forward-zones-file" ]<< "'" << endl
;
498 if (:: arg (). mustDo ( "export-etc-hosts" )) {
500 string fname
=:: arg ()[ "etc-hosts-file" ];
502 ifstream
ifs ( fname
. c_str ());
504 g_log
<< Logger :: Warning
<< "Could not open " << fname
<< " for reading" << endl
;
507 string searchSuffix
= :: arg ()[ "export-etc-hosts-search-suffix" ];
508 string :: size_type pos
;
509 while ( getline ( ifs
, line
)) {
511 if ( pos
!= string :: npos
)
517 stringtok ( parts
, line
, " \t\r\n " );
518 if ( parts
[ 0 ]. find ( ':' )!= string :: npos
)
521 for ( unsigned int n
= 1 ; n
< parts
. size (); ++ n
) {
522 if ( searchSuffix
. empty () || parts
[ n
]. find ( '.' ) != string :: npos
)
523 makeNameToIPZone ( newMap
, DNSName ( parts
[ n
]), parts
[ 0 ]);
525 DNSName canonic
= toCanonic ( DNSName ( searchSuffix
), parts
[ n
]); /// XXXX DNSName pain
526 if ( canonic
!= DNSName ( parts
[ n
])) { // XXX further DNSName pain
527 makeNameToIPZone ( newMap
, canonic
, parts
[ 0 ]);
531 makeIPToNamesZone ( newMap
, parts
);
535 if (:: arg (). mustDo ( "serve-rfc1918" )) {
536 g_log
<< Logger :: Warning
<< "Inserting rfc 1918 private space zones" << endl
;
538 parts
. push_back ( "127" );
539 makeIPToNamesZone ( newMap
, parts
);
541 makeIPToNamesZone ( newMap
, parts
);
544 makeIPToNamesZone ( newMap
, parts
);
545 for ( int n
= 16 ; n
< 32 ; n
++) {
546 parts
[ 0 ]= "172." + std :: to_string ( n
);
547 makeIPToNamesZone ( newMap
, parts
);