]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/resolver.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 "resolver.hh"
28 #include <semaphore.h>
34 #include "dnsrecords.hh"
38 #include <boost/algorithm/string.hpp>
42 #include "pdnsexception.hh"
43 #include "arguments.hh"
45 #include "dnswriter.hh"
46 #include "dnsparser.hh"
49 #include "dns_random.hh"
51 #include "gss_context.hh"
52 #include "namespaces.hh"
54 int makeQuerySocket ( const ComboAddress
& local
, bool udpOrTCP
, bool nonLocalBind
)
56 ComboAddress
ourLocal ( local
);
58 int sock
= socket ( ourLocal
. sin4
. sin_family
, udpOrTCP
? SOCK_DGRAM
: SOCK_STREAM
, 0 );
60 if ( errno
== EAFNOSUPPORT
&& local
. sin4
. sin_family
== AF_INET6
) {
63 unixDie ( "Creating local resolver socket for " + ourLocal
. toString ());
69 Utility :: setBindAny ( local
. sin4
. sin_family
, sock
);
72 // udp, try hard to bind an unpredictable port
75 ourLocal
. sin4
. sin_port
= htons ( 10000 +( dns_random ( 10000 )));
77 if (:: bind ( sock
, ( struct sockaddr
*)& ourLocal
, ourLocal
. getSocklen ()) >= 0 )
80 // cerr<<"bound udp port "<<ourLocal.sin4.sin_port<<", "<<tries<<" tries left"<<endl;
84 throw PDNSException ( "Resolver binding to local UDP socket on " + ourLocal
. toString ()+ ": " + stringerror ());
88 // tcp, let the kernel figure out the port
89 // cerr<<"letting kernel pick TCP port"<<endl;
90 ourLocal
. sin4
. sin_port
= 0 ;
91 if (:: bind ( sock
, ( struct sockaddr
*)& ourLocal
, ourLocal
. getSocklen ()) < 0 )
92 throw PDNSException ( "Resolver binding to local TCP socket on " + ourLocal
. toString ()+ ": " + stringerror ());
99 locals
[ "default4" ] = - 1 ;
100 locals
[ "default6" ] = - 1 ;
102 locals
[ "default4" ] = makeQuerySocket ( ComboAddress (:: arg ()[ "query-local-address" ]), true , :: arg (). mustDo ( "non-local-bind" ));
103 if (!:: arg ()[ "query-local-address6" ]. empty ())
104 locals
[ "default6" ] = makeQuerySocket ( ComboAddress (:: arg ()[ "query-local-address6" ]), true , :: arg (). mustDo ( "non-local-bind" ));
107 if ( locals
[ "default4" ]>= 0 )
108 close ( locals
[ "default4" ]);
113 Resolver ::~ Resolver ()
115 for ( std :: map
< std :: string
, int >:: iterator iter
= locals
. begin (); iter
!= locals
. end (); iter
++) {
116 if ( iter
-> second
>= 0 )
121 uint16_t Resolver :: sendResolve ( const ComboAddress
& remote
, const ComboAddress
& local
,
122 const DNSName
& domain
, int type
, bool dnssecOK
,
123 const DNSName
& tsigkeyname
, const DNSName
& tsigalgorithm
,
124 const string
& tsigsecret
)
127 vector
< uint8_t > packet
;
128 DNSPacketWriter
pw ( packet
, domain
, type
);
129 pw
. getHeader ()-> id
= randomid
= dns_random ( 0xffff );
132 pw
. addOpt ( 2800 , 0 , EDNSOpts :: DNSSECOK
);
136 if (! tsigkeyname
. empty ()) {
137 // cerr<<"Adding TSIG to notification, key name: '"<<tsigkeyname<<"', algo: '"<<tsigalgorithm<<"', secret: "<<Base64Encode(tsigsecret)<<endl;
138 TSIGRecordContent trc
;
139 if ( tsigalgorithm
== DNSName ( "hmac-md5" ))
140 trc
. d_algoName
= tsigalgorithm
+ DNSName ( "sig-alg.reg.int" );
142 trc
. d_algoName
= tsigalgorithm
;
143 trc
. d_time
= time ( 0 );
145 trc
. d_origID
= ntohs ( randomid
);
147 addTSIG ( pw
, trc
, tsigkeyname
, tsigsecret
, "" , false );
152 // choose socket based on local
153 if ( local
. sin4
. sin_family
== 0 ) {
155 sock
= remote
. sin4
. sin_family
== AF_INET
? locals
[ "default4" ] : locals
[ "default6" ];
157 std :: string lstr
= local
. toString ();
158 std :: map
< std :: string
, int >:: iterator lptr
;
159 // see if there is a local
161 if (( lptr
= locals
. find ( lstr
)) != locals
. end ()) {
164 // try to make socket
165 sock
= makeQuerySocket ( local
, true );
167 throw ResolverException ( "Unable to create socket to " + remote
. toStringWithPort ()+ ": " + stringerror ());
168 setNonBlocking ( sock
);
173 if ( sendto ( sock
, & packet
[ 0 ], packet
. size (), 0 , ( struct sockaddr
*)(& remote
), remote
. getSocklen ()) < 0 ) {
174 throw ResolverException ( "Unable to ask query of " + remote
. toStringWithPort ()+ ": " + stringerror ());
179 uint16_t Resolver :: sendResolve ( const ComboAddress
& remote
, const DNSName
& domain
,
180 int type
, bool dnssecOK
,
181 const DNSName
& tsigkeyname
, const DNSName
& tsigalgorithm
,
182 const string
& tsigsecret
)
185 local
. sin4
. sin_family
= 0 ;
186 return this -> sendResolve ( remote
, local
, domain
, type
, dnssecOK
, tsigkeyname
, tsigalgorithm
, tsigsecret
);
189 static int parseResult ( MOADNSParser
& mdp
, const DNSName
& origQname
, uint16_t origQtype
, uint16_t id
, Resolver :: res_t
* result
)
193 if ( mdp
. d_header
. rcode
)
194 return mdp
. d_header
. rcode
;
196 if ( origQname
. countLabels ()) { // not AXFR
197 if ( mdp
. d_header
. id
!= id
)
198 throw ResolverException ( "Remote nameserver replied with wrong id" );
199 if ( mdp
. d_header
. qdcount
!= 1 )
200 throw ResolverException ( "resolver: received answer with wrong number of questions (" + itoa ( mdp
. d_header
. qdcount
)+ ")" );
201 if ( mdp
. d_qname
!= origQname
)
202 throw ResolverException ( string ( "resolver: received an answer to another question (" )+ mdp
. d_qname
. toString ()+ "!=" + origQname
. toString ()+ ".)" );
205 vector
< DNSResourceRecord
> ret
;
206 DNSResourceRecord rr
;
207 for ( MOADNSParser :: answers_t :: const_iterator i
= mdp
. d_answers
. begin (); i
!= mdp
. d_answers
. end (); ++ i
) {
208 rr
. qname
= i
-> first
. d_name
;
209 rr
. qtype
= i
-> first
. d_type
;
210 rr
. ttl
= i
-> first
. d_ttl
;
211 rr
. content
= i
-> first
. d_content
-> getZoneRepresentation ( true );
212 result
-> push_back ( rr
);
218 bool Resolver :: tryGetSOASerial ( DNSName
* domain
, uint32_t * theirSerial
, uint32_t * theirInception
, uint32_t * theirExpire
, uint16_t * id
)
220 struct pollfd
* fds
= new struct pollfd
[ locals
. size ()];
224 for ( std :: map
< string
, int >:: iterator iter
= locals
. begin (); iter
!= locals
. end (); iter
++, i
++) {
225 fds
[ i
]. fd
= iter
-> second
;
226 fds
[ i
]. events
= POLLIN
;
229 if ( poll ( fds
, i
, 250 ) < 1 ) { // wait for 0.25s
238 if (( fds
[ k
]. revents
& POLLIN
) == POLLIN
) {
246 if ( sock
< 0 ) return false ; // false alarm
249 ComboAddress fromaddr
;
250 socklen_t addrlen
= fromaddr
. getSocklen ();
252 err
= recvfrom ( sock
, buf
, sizeof ( buf
), 0 ,( struct sockaddr
*)(& fromaddr
), & addrlen
);
257 throw ResolverException ( "recvfrom error waiting for answer: " + stringerror ());
260 MOADNSParser
mdp ( false , ( char *) buf
, err
);
262 * domain
= mdp
. d_qname
;
264 if ( mdp
. d_answers
. empty ())
265 throw ResolverException ( "Query to '" + fromaddr
. toStringWithPort () + "' for SOA of '" + domain
-> toString () + "' produced no results (RCode: " + RCode :: to_s ( mdp
. d_header
. rcode
) + ")" );
267 if ( mdp
. d_qtype
!= QType :: SOA
)
268 throw ResolverException ( "Query to '" + fromaddr
. toStringWithPort () + "' for SOA of '" + domain
-> toString () + "' returned wrong record type" );
270 * theirInception
= * theirExpire
= 0 ;
272 for ( const MOADNSParser :: answers_t :: value_type
& drc
: mdp
. d_answers
) {
273 if ( drc
. first
. d_type
== QType :: SOA
) {
274 shared_ptr
< SOARecordContent
> src
= getRR
< SOARecordContent
>( drc
. first
);
276 * theirSerial
= src
-> d_st
. serial
;
280 if ( drc
. first
. d_type
== QType :: RRSIG
) {
281 shared_ptr
< RRSIGRecordContent
> rrc
= getRR
< RRSIGRecordContent
>( drc
. first
);
282 if ( rrc
&& rrc
-> d_type
== QType :: SOA
) {
283 * theirInception
= std :: max (* theirInception
, rrc
-> d_siginception
);
284 * theirExpire
= std :: max (* theirExpire
, rrc
-> d_sigexpire
);
289 throw ResolverException ( "Query to '" + fromaddr
. toString () + "' for SOA of '" + domain
-> toString () + "' did not return a SOA" );
293 int Resolver :: resolve ( const string
& ipport
, const DNSName
& domain
, int type
, Resolver :: res_t
* res
, const ComboAddress
& local
)
296 ComboAddress
to ( ipport
, 53 );
298 int id
= sendResolve ( to
, local
, domain
, type
);
301 // choose socket based on local
302 if ( local
. sin4
. sin_family
== 0 ) {
304 sock
= to
. sin4
. sin_family
== AF_INET
? locals
[ "default4" ] : locals
[ "default6" ];
306 std :: string lstr
= local
. toString ();
307 std :: map
< std :: string
, int >:: iterator lptr
;
308 // see if there is a local
310 if (( lptr
= locals
. find ( lstr
)) != locals
. end ()) sock
= lptr
-> second
;
311 else throw ResolverException ( "sendResolve did not create socket for " + lstr
);
314 int err
= waitForData ( sock
, 0 , 3000000 );
317 throw ResolverException ( "Timeout waiting for answer" );
320 throw ResolverException ( "Error waiting for answer: " + stringerror ());
323 socklen_t addrlen
= sizeof ( from
);
327 if (( len
= recvfrom ( sock
, buffer
, sizeof ( buffer
), 0 ,( struct sockaddr
*)(& from
), & addrlen
)) < 0 )
328 throw ResolverException ( "recvfrom error waiting for answer: " + stringerror ());
330 MOADNSParser
mdp ( false , buffer
, len
);
331 return parseResult ( mdp
, domain
, type
, id
, res
);
333 catch ( ResolverException
& re
) {
334 throw ResolverException ( re
. reason
+ " from " + ipport
);
339 int Resolver :: resolve ( const string
& ipport
, const DNSName
& domain
, int type
, Resolver :: res_t
* res
) {
341 local
. sin4
. sin_family
= 0 ;
342 return resolve ( ipport
, domain
, type
, res
, local
);
345 void Resolver :: getSoaSerial ( const string
& ipport
, const DNSName
& domain
, uint32_t * serial
)
347 vector
< DNSResourceRecord
> res
;
348 int ret
= resolve ( ipport
, domain
, QType :: SOA
, & res
);
350 if ( ret
|| res
. empty ())
351 throw ResolverException ( "Query to '" + ipport
+ "' for SOA of '" + domain
. toString () + "' produced no answers" );
353 if ( res
[ 0 ]. qtype
. getCode () != QType :: SOA
)
354 throw ResolverException ( "Query to '" + ipport
+ "' for SOA of '" + domain
. toString () + "' produced a " + res
[ 0 ]. qtype
. getName ()+ " record" );
357 stringtok ( parts
, res
[ 0 ]. content
);
359 throw ResolverException ( "Query to '" + ipport
+ "' for SOA of '" + domain
. toString () + "' produced an unparseable response" );
362 * serial
= pdns_stou ( parts
[ 2 ]);
364 catch ( const std :: out_of_range
& oor
) {
365 throw ResolverException ( "Query to '" + ipport
+ "' for SOA of '" + domain
. toString () + "' produced an unparseable serial" );
369 AXFRRetriever :: AXFRRetriever ( const ComboAddress
& remote
,
370 const DNSName
& domain
,
371 const TSIGTriplet
& tt
,
372 const ComboAddress
* laddr
,
373 size_t maxReceivedBytes
)
374 : d_tsigVerifier ( tt
, remote
, d_trc
), d_receivedBytes ( 0 ), d_maxReceivedBytes ( maxReceivedBytes
)
378 local
= ( ComboAddress
) (* laddr
);
380 if ( remote
. sin4
. sin_family
== AF_INET
)
381 local
= ComboAddress (:: arg ()[ "query-local-address" ]);
382 else if (!:: arg ()[ "query-local-address6" ]. empty ())
383 local
= ComboAddress (:: arg ()[ "query-local-address6" ]);
385 local
= ComboAddress ( "::" );
389 d_sock
= makeQuerySocket ( local
, false ); // make a TCP socket
391 throw ResolverException ( "Error creating socket for AXFR request to " + d_remote
. toStringWithPort ());
392 d_buf
= shared_array
< char >( new char [ 65536 ]);
393 d_remote
= remote
; // mostly for error reporting
397 vector
< uint8_t > packet
;
398 DNSPacketWriter
pw ( packet
, domain
, QType :: AXFR
);
399 pw
. getHeader ()-> id
= dns_random ( 0xffff );
401 if (! tt
. name
. empty ()) {
402 if ( tt
. algo
== DNSName ( "hmac-md5" ))
403 d_trc
. d_algoName
= tt
. algo
+ DNSName ( "sig-alg.reg.int" );
405 d_trc
. d_algoName
= tt
. algo
;
406 d_trc
. d_time
= time ( 0 );
408 d_trc
. d_origID
= ntohs ( pw
. getHeader ()-> id
);
410 addTSIG ( pw
, d_trc
, tt
. name
, tt
. secret
, "" , false );
413 uint16_t replen
= htons ( packet
. size ());
414 Utility :: iovec iov
[ 2 ];
415 iov
[ 0 ]. iov_base
= reinterpret_cast < char *>(& replen
);
417 iov
[ 1 ]. iov_base
= packet
. data ();
418 iov
[ 1 ]. iov_len
= packet
. size ();
420 int ret
= Utility :: writev ( d_sock
, iov
, 2 );
422 throw ResolverException ( "Error sending question to " + d_remote
. toStringWithPort ()+ ": " + stringerror ());
423 if ( ret
!= ( int )( 2 + packet
. size ())) {
424 throw ResolverException ( "Partial write on AXFR request to " + d_remote
. toStringWithPort ());
427 int res
= waitForData ( d_sock
, 10 , 0 );
430 throw ResolverException ( "Timeout waiting for answer from " + d_remote
. toStringWithPort ()+ " during AXFR" );
432 throw ResolverException ( "Error waiting for answer from " + d_remote
. toStringWithPort ()+ ": " + stringerror ());
442 AXFRRetriever ::~ AXFRRetriever ()
449 int AXFRRetriever :: getChunk ( Resolver :: res_t
& res
, vector
< DNSRecord
>* records
) // Implementation is making sure RFC2845 4.4 is followed.
454 // d_sock is connected and is about to spit out a packet
457 throw ResolverException ( "EOF trying to read axfr chunk from remote TCP client" );
459 if ( d_maxReceivedBytes
> 0 && ( d_maxReceivedBytes
- d_receivedBytes
) < ( size_t ) len
)
460 throw ResolverException ( "Reached the maximum number of received bytes during AXFR" );
464 d_receivedBytes
+= ( uint16_t ) len
;
466 MOADNSParser
mdp ( false , d_buf
. get (), len
);
470 err
= parseResult ( mdp
, DNSName (), 0 , 0 , & res
);
473 for ( const auto & r
: mdp
. d_answers
)
474 records
-> push_back ( r
. first
);
475 err
= mdp
. d_header
. rcode
;
479 throw ResolverException ( "AXFR chunk error: " + RCode :: to_s ( err
));
481 for ( const MOADNSParser :: answers_t :: value_type
& answer
: mdp
. d_answers
)
482 if ( answer
. first
. d_type
== QType :: SOA
)
486 d_tsigVerifier
. check ( std :: string ( d_buf
. get (), len
), mdp
);
488 catch ( const std :: runtime_error
& re
) {
489 throw ResolverException ( re
. what ());
495 void AXFRRetriever :: timeoutReadn ( uint16_t bytes
)
497 time_t start
= time ( 0 );
501 int res
= waitForData ( d_sock
, 10 -( time ( 0 )- start
));
503 throw ResolverException ( "Reading data from remote nameserver over TCP: " + stringerror ());
505 throw ResolverException ( "Timeout while reading data from remote nameserver over TCP" );
507 numread
= recv ( d_sock
, d_buf
. get ()+ n
, bytes
- n
, 0 );
509 throw ResolverException ( "Reading data from remote nameserver over TCP: " + stringerror ());
511 throw ResolverException ( "Remote nameserver closed TCP connection" );
516 void AXFRRetriever :: connect ()
518 setNonBlocking ( d_sock
);
522 if (( err
=:: connect ( d_sock
,( struct sockaddr
*)& d_remote
, d_remote
. getSocklen ()))< 0 && errno
!= EINPROGRESS
) {
526 catch ( const PDNSException
& e
) {
528 throw ResolverException ( "Error closing AXFR socket after connect() failed: " + e
. reason
);
531 throw ResolverException ( "connect: " + stringerror ());
537 err
= waitForRWData ( d_sock
, false , 10 , 0 ); // wait for writeability
541 closesocket ( d_sock
); // timeout
543 catch ( const PDNSException
& e
) {
545 throw ResolverException ( "Error closing AXFR socket after timeout: " + e
. reason
);
551 throw ResolverException ( "Timeout connecting to server" );
554 throw ResolverException ( "Error connecting: " + string ( strerror ( err
)));
557 Utility :: socklen_t len
= sizeof ( err
);
558 if ( getsockopt ( d_sock
, SOL_SOCKET
, SO_ERROR
,( char *)& err
,& len
)< 0 )
559 throw ResolverException ( "Error connecting: " + stringerror ()); // Solaris
562 throw ResolverException ( "Error connecting: " + string ( strerror ( err
)));
566 setBlocking ( d_sock
);
567 // d_sock now connected
570 int AXFRRetriever :: getLength ()
573 return ( unsigned char ) d_buf
[ 0 ]* 256 +( unsigned char ) d_buf
[ 1 ];