]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/resolver.cc
2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002 - 2011 PowerDNS.COM BV
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation.
9 Additionally, the license of this program contains a special
10 exception which allows to distribute the program in binary form when
11 it is linked against OpenSSL.
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 St, Fifth Floor, Boston, MA 02110-1301 USA
25 #include "packetcache.hh"
27 #include "resolver.hh"
29 #include <semaphore.h>
35 #include "dnsrecords.hh"
39 #include <boost/algorithm/string.hpp>
42 #include "tcpreceiver.hh"
43 #include "pdnsexception.hh"
45 #include "arguments.hh"
47 #include "dnswriter.hh"
48 #include "dnsparser.hh"
49 #include <boost/shared_ptr.hpp>
50 #include <boost/foreach.hpp>
51 #include "dns_random.hh"
53 #include "namespaces.hh"
55 int makeQuerySocket ( const ComboAddress
& local
, bool udpOrTCP
)
57 ComboAddress
ourLocal ( local
);
59 int sock
= socket ( ourLocal
. sin4
. sin_family
, udpOrTCP
? SOCK_DGRAM
: SOCK_STREAM
, 0 );
60 Utility :: setCloseOnExec ( sock
);
62 unixDie ( "Creating local resolver socket for " + ourLocal
. toString () + (( local
. sin4
. sin_family
== AF_INET6
) ? ", does your OS miss IPv6?" : "" ));
66 // udp, try hard to bind an unpredictable port
69 ourLocal
. sin4
. sin_port
= htons ( 10000 +( dns_random ( 10000 )));
71 if (:: bind ( sock
, ( struct sockaddr
*)& ourLocal
, ourLocal
. getSocklen ()) >= 0 )
74 // cerr<<"bound udp port "<<ourLocal.sin4.sin_port<<", "<<tries<<" tries left"<<endl;
77 Utility :: closesocket ( sock
);
78 throw PDNSException ( "Resolver binding to local UDP socket on " + ourLocal
. toString ()+ ": " + stringerror ());
82 // tcp, let the kernel figure out the port
83 // cerr<<"letting kernel pick TCP port"<<endl;
84 ourLocal
. sin4
. sin_port
= 0 ;
85 if (:: bind ( sock
, ( struct sockaddr
*)& ourLocal
, ourLocal
. getSocklen ()) < 0 )
86 throw PDNSException ( "Resolver binding to local TCP socket on " + ourLocal
. toString ()+ ": " + stringerror ());
94 locals
[ "default4" ] = makeQuerySocket ( ComboAddress (:: arg ()[ "query-local-address" ]), true );
95 if (!:: arg ()[ "query-local-address6" ]. empty ())
96 locals
[ "default6" ] = makeQuerySocket ( ComboAddress (:: arg ()[ "query-local-address6" ]), true );
98 locals
[ "default6" ] = - 1 ;
101 if ( locals
[ "default4" ]>= 0 )
102 close ( locals
[ "default4" ]);
106 Resolver ::~ Resolver ()
108 for ( std :: map
< std :: string
, int >:: iterator iter
= locals
. begin (); iter
!= locals
. end (); iter
++) {
109 if ( iter
-> second
>= 0 )
114 uint16_t Resolver :: sendResolve ( const ComboAddress
& remote
, const ComboAddress
& local
,
115 const char * domain
, int type
, bool dnssecOK
,
116 const string
& tsigkeyname
, const string
& tsigalgorithm
,
117 const string
& tsigsecret
)
120 vector
< uint8_t > packet
;
121 DNSPacketWriter
pw ( packet
, domain
, type
);
122 pw
. getHeader ()-> id
= randomid
= dns_random ( 0xffff );
125 pw
. addOpt ( 2800 , 0 , EDNSOpts :: DNSSECOK
);
129 if (! tsigkeyname
. empty ()) {
130 // cerr<<"Adding TSIG to notification, key name: '"<<tsigkeyname<<"', algo: '"<<tsigalgorithm<<"', secret: "<<Base64Encode(tsigsecret)<<endl;
131 TSIGRecordContent trc
;
132 if ( tsigalgorithm
== "hmac-md5" )
133 trc
. d_algoName
= tsigalgorithm
+ ".sig-alg.reg.int." ;
135 trc
. d_algoName
= tsigalgorithm
;
136 trc
. d_time
= time ( 0 );
138 trc
. d_origID
= ntohs ( randomid
);
140 addTSIG ( pw
, & trc
, tsigkeyname
, tsigsecret
, "" , false );
145 // choose socket based on local
146 if ( local
. sin4
. sin_family
== 0 ) {
148 sock
= remote
. sin4
. sin_family
== AF_INET
? locals
[ "default4" ] : locals
[ "default6" ];
150 std :: string lstr
= local
. toString ();
151 std :: map
< std :: string
, int >:: iterator lptr
;
152 // see if there is a local
154 if (( lptr
= locals
. find ( lstr
)) != locals
. end ()) {
157 // try to make socket
158 sock
= makeQuerySocket ( local
, true );
159 Utility :: setNonBlocking ( sock
);
164 if ( sendto ( sock
, & packet
[ 0 ], packet
. size (), 0 , ( struct sockaddr
*)(& remote
), remote
. getSocklen ()) < 0 ) {
165 throw ResolverException ( "Unable to ask query of " + remote
. toStringWithPort ()+ ": " + stringerror ());
170 uint16_t Resolver :: sendResolve ( const ComboAddress
& remote
, const char * domain
,
171 int type
, bool dnssecOK
,
172 const string
& tsigkeyname
, const string
& tsigalgorithm
,
173 const string
& tsigsecret
)
176 local
. sin4
. sin_family
= 0 ;
177 return this -> sendResolve ( remote
, local
, domain
, type
, dnssecOK
, tsigkeyname
, tsigalgorithm
, tsigsecret
);
180 static int parseResult ( MOADNSParser
& mdp
, const std :: string
& origQname
, uint16_t origQtype
, uint16_t id
, Resolver :: res_t
* result
)
184 if ( mdp
. d_header
. rcode
)
185 return mdp
. d_header
. rcode
;
187 if (! origQname
. empty ()) { // not AXFR
188 if ( mdp
. d_header
. id
!= id
)
189 throw ResolverException ( "Remote nameserver replied with wrong id" );
190 if ( mdp
. d_header
. qdcount
!= 1 )
191 throw ResolverException ( "resolver: received answer with wrong number of questions (" + itoa ( mdp
. d_header
. qdcount
)+ ")" );
192 if ( mdp
. d_qname
!= origQname
+ "." )
193 throw ResolverException ( string ( "resolver: received an answer to another question (" )+ mdp
. d_qname
+ "!=" + origQname
+ ".)" );
196 vector
< DNSResourceRecord
> ret
;
197 DNSResourceRecord rr
;
198 for ( MOADNSParser :: answers_t :: const_iterator i
= mdp
. d_answers
. begin (); i
!= mdp
. d_answers
. end (); ++ i
) {
199 rr
. qname
= i
-> first
. d_label
;
200 if (! rr
. qname
. empty ())
201 boost :: erase_tail ( rr
. qname
, 1 ); // strip .
202 rr
. qtype
= i
-> first
. d_type
;
203 rr
. ttl
= i
-> first
. d_ttl
;
204 rr
. content
= i
-> first
. d_content
-> getZoneRepresentation ();
205 switch ( rr
. qtype
. getCode ()) {
208 if ( rr
. content
. size () >= 2 && *( rr
. content
. rbegin ()+ 1 ) == ' ' )
212 if (! rr
. content
. empty ())
213 boost :: erase_tail ( rr
. content
, 1 );
215 result
-> push_back ( rr
);
221 bool Resolver :: tryGetSOASerial ( string
* domain
, uint32_t * theirSerial
, uint32_t * theirInception
, uint32_t * theirExpire
, uint16_t * id
)
223 struct pollfd
* fds
= new struct pollfd
[ locals
. size ()];
227 for ( std :: map
< string
, int >:: iterator iter
= locals
. begin (); iter
!= locals
. end (); iter
++, i
++) {
228 fds
[ i
]. fd
= iter
-> second
;
229 fds
[ i
]. events
= POLLIN
;
232 if ( poll ( fds
, i
, 250 ) < 1 ) { // wait for 0.25s
241 if (( fds
[ k
]. revents
& POLLIN
) == POLLIN
) {
249 if ( sock
< 0 ) return false ; // false alarm
252 ComboAddress fromaddr
;
253 socklen_t addrlen
= fromaddr
. getSocklen ();
255 err
= recvfrom ( sock
, buf
, sizeof ( buf
), 0 ,( struct sockaddr
*)(& fromaddr
), & addrlen
);
260 throw ResolverException ( "recvfrom error waiting for answer: " + stringerror ());
263 MOADNSParser
mdp (( char *) buf
, err
);
265 * domain
= stripDot ( mdp
. d_qname
);
267 if ( mdp
. d_answers
. empty ())
268 throw ResolverException ( "Query to '" + fromaddr
. toStringWithPort () + "' for SOA of '" + * domain
+ "' produced no results (RCode: " + RCode :: to_s ( mdp
. d_header
. rcode
) + ")" );
270 if ( mdp
. d_qtype
!= QType :: SOA
)
271 throw ResolverException ( "Query to '" + fromaddr
. toStringWithPort () + "' for SOA of '" + * domain
+ "' returned wrong record type" );
273 * theirInception
= * theirExpire
= 0 ;
275 BOOST_FOREACH ( const MOADNSParser :: answers_t :: value_type
& drc
, mdp
. d_answers
) {
276 if ( drc
. first
. d_type
== QType :: SOA
) {
277 shared_ptr
< SOARecordContent
> src
= boost :: dynamic_pointer_cast
< SOARecordContent
>( drc
. first
. d_content
);
278 * theirSerial
= src
-> d_st
. serial
;
281 if ( drc
. first
. d_type
== QType :: RRSIG
) {
282 shared_ptr
< RRSIGRecordContent
> rrc
= boost :: dynamic_pointer_cast
< RRSIGRecordContent
>( drc
. first
. d_content
);
283 if ( rrc
-> d_type
== QType :: SOA
) {
284 * theirInception
= std :: max (* theirInception
, rrc
-> d_siginception
);
285 * theirExpire
= std :: max (* theirExpire
, rrc
-> d_sigexpire
);
290 throw ResolverException ( "Query to '" + fromaddr
. toString () + "' for SOA of '" + * domain
+ "' did not return a SOA" );
294 int Resolver :: resolve ( const string
& ipport
, const char * domain
, int type
, Resolver :: res_t
* res
, const ComboAddress
& local
)
297 ComboAddress
to ( ipport
, 53 );
299 int id
= sendResolve ( to
, local
, domain
, type
);
302 // choose socket based on local
303 if ( local
. sin4
. sin_family
== 0 ) {
305 sock
= to
. sin4
. sin_family
== AF_INET
? locals
[ "default4" ] : locals
[ "default6" ];
307 std :: string lstr
= local
. toString ();
308 std :: map
< std :: string
, int >:: iterator lptr
;
309 // see if there is a local
311 if (( lptr
= locals
. find ( lstr
)) != locals
. end ()) sock
= lptr
-> second
;
312 else throw ResolverException ( "sendResolve did not create socket for " + lstr
);
315 int err
= waitForData ( sock
, 0 , 3000000 );
318 throw ResolverException ( "Timeout waiting for answer" );
321 throw ResolverException ( "Error waiting for answer: " + stringerror ());
324 socklen_t addrlen
= sizeof ( from
);
328 if (( len
= recvfrom ( sock
, buffer
, sizeof ( buffer
), 0 ,( struct sockaddr
*)(& from
), & addrlen
)) < 0 )
329 throw ResolverException ( "recvfrom error waiting for answer: " + stringerror ());
331 MOADNSParser
mdp ( buffer
, len
);
332 return parseResult ( mdp
, domain
, type
, id
, res
);
334 catch ( ResolverException
& re
) {
335 throw ResolverException ( re
. reason
+ " from " + ipport
);
340 int Resolver :: resolve ( const string
& ipport
, const char * domain
, int type
, Resolver :: res_t
* res
) {
342 local
. sin4
. sin_family
= 0 ;
343 return resolve ( ipport
, domain
, type
, res
, local
);
346 void Resolver :: getSoaSerial ( const string
& ipport
, const string
& domain
, uint32_t * serial
)
348 vector
< DNSResourceRecord
> res
;
349 int ret
= resolve ( ipport
, domain
. c_str (), QType :: SOA
, & res
);
351 if ( ret
|| res
. empty ())
352 throw ResolverException ( "Query to '" + ipport
+ "' for SOA of '" + domain
+ "' produced no answers" );
354 if ( res
[ 0 ]. qtype
. getCode () != QType :: SOA
)
355 throw ResolverException ( "Query to '" + ipport
+ "' for SOA of '" + domain
+ "' produced a " + res
[ 0 ]. qtype
. getName ()+ " record" );
358 stringtok ( parts
, res
[ 0 ]. content
);
360 throw ResolverException ( "Query to '" + ipport
+ "' for SOA of '" + domain
+ "' produced an unparseable response" );
362 * serial
=( uint32_t ) atol ( parts
[ 2 ]. c_str ());
365 AXFRRetriever :: AXFRRetriever ( const ComboAddress
& remote
,
366 const string
& domain
,
367 const string
& tsigkeyname
,
368 const string
& tsigalgorithm
,
369 const string
& tsigsecret
,
370 const ComboAddress
* laddr
)
371 : d_tsigkeyname ( tsigkeyname
), d_tsigsecret ( tsigsecret
), d_tsigPos ( 0 ), d_nonSignedMessages ( 0 )
375 local
= ( ComboAddress
) (* laddr
);
377 if ( remote
. sin4
. sin_family
== AF_INET
)
378 local
= ComboAddress (:: arg ()[ "query-local-address" ]);
379 else if (!:: arg ()[ "query-local-address6" ]. empty ())
380 local
= ComboAddress (:: arg ()[ "query-local-address6" ]);
382 local
= ComboAddress ( "::" );
386 d_sock
= makeQuerySocket ( local
, false ); // make a TCP socket
387 d_buf
= shared_array
< char >( new char [ 65536 ]);
388 d_remote
= remote
; // mostly for error reporting
392 vector
< uint8_t > packet
;
393 DNSPacketWriter
pw ( packet
, domain
, QType :: AXFR
);
394 pw
. getHeader ()-> id
= dns_random ( 0xffff );
396 if (! tsigkeyname
. empty ()) {
397 if ( tsigalgorithm
== "hmac-md5" )
398 d_trc
. d_algoName
= tsigalgorithm
+ ".sig-alg.reg.int." ;
400 d_trc
. d_algoName
= tsigalgorithm
;
401 d_trc
. d_time
= time ( 0 );
403 d_trc
. d_origID
= ntohs ( pw
. getHeader ()-> id
);
405 addTSIG ( pw
, & d_trc
, tsigkeyname
, tsigsecret
, "" , false );
408 uint16_t replen
= htons ( packet
. size ());
409 Utility :: iovec iov
[ 2 ];
410 iov
[ 0 ]. iov_base
=( char *)& replen
;
412 iov
[ 1 ]. iov_base
=( char *)& packet
[ 0 ];
413 iov
[ 1 ]. iov_len
= packet
. size ();
415 int ret
= Utility :: writev ( d_sock
, iov
, 2 );
417 throw ResolverException ( "Error sending question to " + d_remote
. toStringWithPort ()+ ": " + stringerror ());
418 if ( ret
!= ( int )( 2 + packet
. size ())) {
419 throw ResolverException ( "Partial write on AXFR request to " + d_remote
. toStringWithPort ());
422 int res
= waitForData ( d_sock
, 10 , 0 );
425 throw ResolverException ( "Timeout waiting for answer from " + d_remote
. toStringWithPort ()+ " during AXFR" );
427 throw ResolverException ( "Error waiting for answer from " + d_remote
. toStringWithPort ()+ ": " + stringerror ());
436 AXFRRetriever ::~ AXFRRetriever ()
443 int AXFRRetriever :: getChunk ( Resolver :: res_t
& res
) // Implementation is making sure RFC2845 4.4 is followed.
448 // d_sock is connected and is about to spit out a packet
451 throw ResolverException ( "EOF trying to read axfr chunk from remote TCP client" );
454 MOADNSParser
mdp ( d_buf
. get (), len
);
456 int err
= parseResult ( mdp
, "" , 0 , 0 , & res
);
458 throw ResolverException ( "AXFR chunk error: " + RCode :: to_s ( err
));
460 BOOST_FOREACH ( const MOADNSParser :: answers_t :: value_type
& answer
, mdp
. d_answers
)
461 if ( answer
. first
. d_type
== QType :: SOA
)
464 if (! d_tsigkeyname
. empty ()) { // TSIG verify message
465 // If we have multiple messages, we need to concatenate them together. We also need to make sure we know the location of
466 // the TSIG record so we can remove it in makeTSIGMessageFromTSIGPacket
467 d_signData
. append ( d_buf
. get (), len
);
468 if ( mdp
. getTSIGPos () == 0 )
471 d_tsigPos
+= mdp
. getTSIGPos ();
474 bool checkTSIG
= false ;
476 BOOST_FOREACH ( const MOADNSParser :: answers_t :: value_type
& answer
, mdp
. d_answers
) {
477 if ( answer
. first
. d_type
== QType :: SOA
) // A SOA is either the first or the last record. We need to check TSIG if that's the case.
480 if ( answer
. first
. d_type
== QType :: TSIG
) {
481 shared_ptr
< TSIGRecordContent
> trc
= boost :: dynamic_pointer_cast
< TSIGRecordContent
>( answer
. first
. d_content
);
482 theirMac
= trc
-> d_mac
;
483 d_trc
. d_time
= trc
-> d_time
;
488 if ( ! checkTSIG
&& d_nonSignedMessages
> 99 ) { // We're allowed to get 100 digest without a TSIG.
489 throw ResolverException ( "No TSIG message received in last 100 messages of AXFR transfer." );
493 if ( theirMac
. empty ())
494 throw ResolverException ( "No TSIG on AXFR response from " + d_remote
. toStringWithPort ()+ " , should be signed with TSIG key '" + d_tsigkeyname
+ "'" );
497 if (! d_prevMac
. empty ()) {
498 message
= makeTSIGMessageFromTSIGPacket ( d_signData
, d_tsigPos
, d_tsigkeyname
, d_trc
, d_prevMac
, true , d_signData
. size ()- len
);
500 message
= makeTSIGMessageFromTSIGPacket ( d_signData
, d_tsigPos
, d_tsigkeyname
, d_trc
, d_trc
. d_mac
, false );
504 if (! getTSIGHashEnum ( d_trc
. d_algoName
, algo
)) {
505 throw ResolverException ( "Unsupported TSIG HMAC algorithm " + d_trc
. d_algoName
);
508 string ourMac
= calculateHMAC ( d_tsigsecret
, message
, algo
);
510 // ourMac[0]++; // sabotage == for testing :-)
511 if ( ourMac
!= theirMac
) {
512 throw ResolverException ( "Signature failed to validate on AXFR response from " + d_remote
. toStringWithPort ()+ " signed with TSIG key '" + d_tsigkeyname
+ "'" );
515 // Reset and store some values for the next chunks.
516 d_prevMac
= theirMac
;
517 d_nonSignedMessages
= 0 ;
522 d_nonSignedMessages
++;
528 void AXFRRetriever :: timeoutReadn ( uint16_t bytes
)
530 time_t start
= time ( 0 );
534 int res
= waitForData ( d_sock
, 10 -( time ( 0 )- start
));
536 throw ResolverException ( "Reading data from remote nameserver over TCP: " + stringerror ());
538 throw ResolverException ( "Timeout while reading data from remote nameserver over TCP" );
540 numread
= recv ( d_sock
, d_buf
. get ()+ n
, bytes
- n
, 0 );
542 throw ResolverException ( "Reading data from remote nameserver over TCP: " + stringerror ());
544 throw ResolverException ( "Remote nameserver closed TCP connection" );
549 void AXFRRetriever :: connect ()
551 Utility :: setNonBlocking ( d_sock
);
555 if (( err
=:: connect ( d_sock
,( struct sockaddr
*)& d_remote
, d_remote
. getSocklen ()))< 0 && errno
!= EINPROGRESS
) {
556 Utility :: closesocket ( d_sock
);
558 throw ResolverException ( "connect: " + stringerror ());
564 err
= waitForRWData ( d_sock
, false , 10 , 0 ); // wait for writeability
567 Utility :: closesocket ( d_sock
); // timeout
571 throw ResolverException ( "Timeout connecting to server" );
574 throw ResolverException ( "Error connecting: " + string ( strerror ( err
)));
577 Utility :: socklen_t len
= sizeof ( err
);
578 if ( getsockopt ( d_sock
, SOL_SOCKET
, SO_ERROR
,( char *)& err
,& len
)< 0 )
579 throw ResolverException ( "Error connecting: " + stringerror ()); // Solaris
582 throw ResolverException ( "Error connecting: " + string ( strerror ( err
)));
586 Utility :: setBlocking ( d_sock
);
587 // d_sock now connected
590 int AXFRRetriever :: getLength ()
593 return ( unsigned char ) d_buf
[ 0 ]* 256 +( unsigned char ) d_buf
[ 1 ];