]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/resolver.cc
98d26fce7a57ebcc4205586b07129421c93a8beb
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 if (!:: arg ()[ "query-local-address" ]. empty ())
103 locals
[ "default4" ] = makeQuerySocket ( ComboAddress (:: arg ()[ "query-local-address" ]), true , :: arg (). mustDo ( "non-local-bind" ));
104 if (!:: arg ()[ "query-local-address6" ]. empty ())
105 locals
[ "default6" ] = makeQuerySocket ( ComboAddress (:: arg ()[ "query-local-address6" ]), true , :: arg (). mustDo ( "non-local-bind" ));
108 if ( locals
[ "default4" ]>= 0 )
109 close ( locals
[ "default4" ]);
110 if ( locals
[ "default6" ]>= 0 )
111 close ( locals
[ "default6" ]);
116 Resolver ::~ Resolver ()
118 for ( auto & iter
: locals
) {
119 if ( iter
. second
>= 0 )
124 uint16_t Resolver :: sendResolve ( const ComboAddress
& remote
, const ComboAddress
& local
,
125 const DNSName
& domain
, int type
, int * localsock
, bool dnssecOK
,
126 const DNSName
& tsigkeyname
, const DNSName
& tsigalgorithm
,
127 const string
& tsigsecret
)
130 vector
< uint8_t > packet
;
131 DNSPacketWriter
pw ( packet
, domain
, type
);
132 pw
. getHeader ()-> id
= randomid
= dns_random ( 0xffff );
135 pw
. addOpt ( 2800 , 0 , EDNSOpts :: DNSSECOK
);
139 if (! tsigkeyname
. empty ()) {
140 // cerr<<"Adding TSIG to notification, key name: '"<<tsigkeyname<<"', algo: '"<<tsigalgorithm<<"', secret: "<<Base64Encode(tsigsecret)<<endl;
141 TSIGRecordContent trc
;
142 if ( tsigalgorithm
== DNSName ( "hmac-md5" ))
143 trc
. d_algoName
= tsigalgorithm
+ DNSName ( "sig-alg.reg.int" );
145 trc
. d_algoName
= tsigalgorithm
;
146 trc
. d_time
= time ( 0 );
148 trc
. d_origID
= ntohs ( randomid
);
150 addTSIG ( pw
, trc
, tsigkeyname
, tsigsecret
, "" , false );
155 // choose socket based on local
156 if ( local
. sin4
. sin_family
== 0 ) {
158 sock
= remote
. sin4
. sin_family
== AF_INET
? locals
[ "default4" ] : locals
[ "default6" ];
160 string ipv
= remote
. sin4
. sin_family
== AF_INET
? "4" : "6" ;
161 string qla
= remote
. sin4
. sin_family
== AF_INET
? "" : "6" ;
162 throw ResolverException ( "No IPv" + ipv
+ " socket available, is query-local-address" + qla
+ " unset?" );
165 std :: string lstr
= local
. toString ();
166 std :: map
< std :: string
, int >:: iterator lptr
;
168 // reuse an existing local socket or make a new one
169 if (( lptr
= locals
. find ( lstr
)) != locals
. end ()) {
172 // try to make socket
173 sock
= makeQuerySocket ( local
, true );
175 throw ResolverException ( "Unable to create local socket on " + lstr
+ " to " + remote
. toStringWithPort ()+ ": " + stringerror ());
176 setNonBlocking ( sock
);
181 if ( localsock
!= nullptr ) {
184 if ( sendto ( sock
, & packet
[ 0 ], packet
. size (), 0 , ( struct sockaddr
*)(& remote
), remote
. getSocklen ()) < 0 ) {
185 throw ResolverException ( "Unable to ask query of " + remote
. toStringWithPort ()+ ": " + stringerror ());
190 static int parseResult ( MOADNSParser
& mdp
, const DNSName
& origQname
, uint16_t origQtype
, uint16_t id
, Resolver :: res_t
* result
)
194 if ( mdp
. d_header
. rcode
)
195 return mdp
. d_header
. rcode
;
197 if ( origQname
. countLabels ()) { // not AXFR
198 if ( mdp
. d_header
. id
!= id
)
199 throw ResolverException ( "Remote nameserver replied with wrong id" );
200 if ( mdp
. d_header
. qdcount
!= 1 )
201 throw ResolverException ( "resolver: received answer with wrong number of questions (" + itoa ( mdp
. d_header
. qdcount
)+ ")" );
202 if ( mdp
. d_qname
!= origQname
)
203 throw ResolverException ( string ( "resolver: received an answer to another question (" )+ mdp
. d_qname
. toLogString ()+ "!=" + origQname
. toLogString ()+ ".)" );
206 vector
< DNSResourceRecord
> ret
;
207 DNSResourceRecord rr
;
208 for ( const auto & i
: mdp
. d_answers
) {
209 rr
. qname
= i
. first
. d_name
;
210 rr
. qtype
= i
. first
. d_type
;
211 rr
. ttl
= i
. first
. d_ttl
;
212 rr
. content
= i
. first
. d_content
-> getZoneRepresentation ( true );
213 result
-> push_back ( rr
);
219 bool Resolver :: tryGetSOASerial ( DNSName
* domain
, ComboAddress
* remote
, uint32_t * theirSerial
, uint32_t * theirInception
, uint32_t * theirExpire
, uint16_t * id
)
221 auto fds
= std :: unique_ptr
< struct pollfd
[]>( new struct pollfd
[ locals
. size ()]);
225 for ( const auto & iter
: locals
) {
226 fds
[ i
]. fd
= iter
. second
;
227 fds
[ i
]. events
= POLLIN
;
231 if ( poll ( fds
. get (), i
, 250 ) < 1 ) { // wait for 0.25s
239 if (( fds
[ k
]. revents
& POLLIN
) == POLLIN
) {
245 if ( sock
< 0 ) return false ; // false alarm
248 remote
-> sin6
. sin6_family
= AF_INET6
; // make sure getSocklen() below returns a large enough value
249 socklen_t addrlen
= remote
-> getSocklen ();
251 err
= recvfrom ( sock
, buf
, sizeof ( buf
), 0 ,( struct sockaddr
*)( remote
), & addrlen
);
256 throw ResolverException ( "recvfrom error waiting for answer: " + stringerror ());
259 MOADNSParser
mdp ( false , ( char *) buf
, err
);
261 * domain
= mdp
. d_qname
;
264 throw ResolverException ( "SOA query to '" + remote
-> toStringWithPort () + "' produced response without domain name (RCode: " + RCode :: to_s ( mdp
. d_header
. rcode
) + ")" );
266 if ( mdp
. d_answers
. empty ())
267 throw ResolverException ( "Query to '" + remote
-> toStringWithPort () + "' for SOA of '" + domain
-> toLogString () + "' produced no results (RCode: " + RCode :: to_s ( mdp
. d_header
. rcode
) + ")" );
269 if ( mdp
. d_qtype
!= QType :: SOA
)
270 throw ResolverException ( "Query to '" + remote
-> toStringWithPort () + "' for SOA of '" + domain
-> toLogString () + "' returned wrong record type" );
272 * theirInception
= * theirExpire
= 0 ;
274 for ( const MOADNSParser :: answers_t :: value_type
& drc
: mdp
. d_answers
) {
275 if ( drc
. first
. d_type
== QType :: SOA
) {
276 shared_ptr
< SOARecordContent
> src
= getRR
< SOARecordContent
>( drc
. first
);
278 * theirSerial
= src
-> d_st
. serial
;
282 if ( drc
. first
. d_type
== QType :: RRSIG
) {
283 shared_ptr
< RRSIGRecordContent
> rrc
= getRR
< RRSIGRecordContent
>( drc
. first
);
284 if ( rrc
&& rrc
-> d_type
== QType :: SOA
) {
285 * theirInception
= std :: max (* theirInception
, rrc
-> d_siginception
);
286 * theirExpire
= std :: max (* theirExpire
, rrc
-> d_sigexpire
);
291 throw ResolverException ( "Query to '" + remote
-> toString () + "' for SOA of '" + domain
-> toLogString () + "' did not return a SOA" );
295 int Resolver :: resolve ( const ComboAddress
& to
, const DNSName
& domain
, int type
, Resolver :: res_t
* res
, const ComboAddress
& local
)
299 int id
= sendResolve ( to
, local
, domain
, type
, & sock
);
300 int err
= waitForData ( sock
, 0 , 3000000 );
303 throw ResolverException ( "Timeout waiting for answer" );
306 throw ResolverException ( "Error waiting for answer: " + stringerror ());
309 socklen_t addrlen
= sizeof ( from
);
313 if (( len
= recvfrom ( sock
, buffer
, sizeof ( buffer
), 0 ,( struct sockaddr
*)(& from
), & addrlen
)) < 0 )
314 throw ResolverException ( "recvfrom error waiting for answer: " + stringerror ());
317 throw ResolverException ( "Got answer from the wrong peer while resolving (" + from
. toStringWithPort ()+ " instead of " + to
. toStringWithPort ()+ ", discarding" );
320 MOADNSParser
mdp ( false , buffer
, len
);
321 return parseResult ( mdp
, domain
, type
, id
, res
);
323 catch ( ResolverException
& re
) {
324 throw ResolverException ( re
. reason
+ " from " + to
. toStringWithPortExcept ( 53 ));
329 int Resolver :: resolve ( const ComboAddress
& ipport
, const DNSName
& domain
, int type
, Resolver :: res_t
* res
) {
331 local
. sin4
. sin_family
= 0 ;
332 return resolve ( ipport
, domain
, type
, res
, local
);
335 void Resolver :: getSoaSerial ( const ComboAddress
& ipport
, const DNSName
& domain
, uint32_t * serial
)
337 vector
< DNSResourceRecord
> res
;
338 int ret
= resolve ( ipport
, domain
, QType :: SOA
, & res
);
340 if ( ret
|| res
. empty ())
341 throw ResolverException ( "Query to '" + ipport
. toStringWithPortExcept ( 53 ) + "' for SOA of '" + domain
. toString () + "' produced no answers" );
343 if ( res
[ 0 ]. qtype
. getCode () != QType :: SOA
)
344 throw ResolverException ( "Query to '" + ipport
. toStringWithPortExcept ( 53 ) + "' for SOA of '" + domain
. toString () + "' produced a " + res
[ 0 ]. qtype
. getName ()+ " record" );
347 stringtok ( parts
, res
[ 0 ]. content
);
349 throw ResolverException ( "Query to '" + ipport
. toStringWithPortExcept ( 53 ) + "' for SOA of '" + domain
. toString () + "' produced an unparseable response" );
352 * serial
= pdns_stou ( parts
[ 2 ]);
354 catch ( const std :: out_of_range
& oor
) {
355 throw ResolverException ( "Query to '" + ipport
. toStringWithPortExcept ( 53 ) + "' for SOA of '" + domain
. toString () + "' produced an unparseable serial" );
359 AXFRRetriever :: AXFRRetriever ( const ComboAddress
& remote
,
360 const DNSName
& domain
,
361 const TSIGTriplet
& tt
,
362 const ComboAddress
* laddr
,
363 size_t maxReceivedBytes
)
364 : d_tsigVerifier ( tt
, remote
, d_trc
), d_receivedBytes ( 0 ), d_maxReceivedBytes ( maxReceivedBytes
)
367 if ( laddr
!= nullptr ) {
368 local
= ComboAddress (* laddr
);
370 string qlas
= remote
. sin4
. sin_family
== AF_INET
? "query-local-address" : "query-local-address6" ;
371 if (:: arg ()[ qlas
]. empty ()) {
372 throw ResolverException ( "Unable to determine source address for AXFR request to " + remote
. toStringWithPort () + " for " + domain
. toLogString () + ". " + qlas
+ " is unset" );
374 local
= ComboAddress (:: arg ()[ qlas
]);
378 d_sock
= makeQuerySocket ( local
, false ); // make a TCP socket
380 throw ResolverException ( "Error creating socket for AXFR request to " + d_remote
. toStringWithPort ());
381 d_buf
= shared_array
< char >( new char [ 65536 ]);
382 d_remote
= remote
; // mostly for error reporting
386 vector
< uint8_t > packet
;
387 DNSPacketWriter
pw ( packet
, domain
, QType :: AXFR
);
388 pw
. getHeader ()-> id
= dns_random ( 0xffff );
390 if (! tt
. name
. empty ()) {
391 if ( tt
. algo
== DNSName ( "hmac-md5" ))
392 d_trc
. d_algoName
= tt
. algo
+ DNSName ( "sig-alg.reg.int" );
394 d_trc
. d_algoName
= tt
. algo
;
395 d_trc
. d_time
= time ( 0 );
397 d_trc
. d_origID
= ntohs ( pw
. getHeader ()-> id
);
399 addTSIG ( pw
, d_trc
, tt
. name
, tt
. secret
, "" , false );
402 uint16_t replen
= htons ( packet
. size ());
403 Utility :: iovec iov
[ 2 ];
404 iov
[ 0 ]. iov_base
= reinterpret_cast < char *>(& replen
);
406 iov
[ 1 ]. iov_base
= packet
. data ();
407 iov
[ 1 ]. iov_len
= packet
. size ();
409 int ret
= Utility :: writev ( d_sock
, iov
, 2 );
411 throw ResolverException ( "Error sending question to " + d_remote
. toStringWithPort ()+ ": " + stringerror ());
412 if ( ret
!= ( int )( 2 + packet
. size ())) {
413 throw ResolverException ( "Partial write on AXFR request to " + d_remote
. toStringWithPort ());
416 int res
= waitForData ( d_sock
, 10 , 0 );
419 throw ResolverException ( "Timeout waiting for answer from " + d_remote
. toStringWithPort ()+ " during AXFR" );
421 throw ResolverException ( "Error waiting for answer from " + d_remote
. toStringWithPort ()+ ": " + stringerror ());
431 AXFRRetriever ::~ AXFRRetriever ()
438 int AXFRRetriever :: getChunk ( Resolver :: res_t
& res
, vector
< DNSRecord
>* records
, uint16_t timeout
) // Implementation is making sure RFC2845 4.4 is followed.
443 // d_sock is connected and is about to spit out a packet
444 int len
= getLength ( timeout
);
446 throw ResolverException ( "EOF trying to read axfr chunk from remote TCP client" );
448 if ( d_maxReceivedBytes
> 0 && ( d_maxReceivedBytes
- d_receivedBytes
) < ( size_t ) len
)
449 throw ResolverException ( "Reached the maximum number of received bytes during AXFR" );
451 timeoutReadn ( len
, timeout
);
453 d_receivedBytes
+= ( uint16_t ) len
;
455 MOADNSParser
mdp ( false , d_buf
. get (), len
);
459 err
= parseResult ( mdp
, DNSName (), 0 , 0 , & res
);
462 for ( const auto & r
: mdp
. d_answers
)
463 records
-> push_back ( r
. first
);
464 err
= mdp
. d_header
. rcode
;
468 throw ResolverException ( "AXFR chunk error: " + RCode :: to_s ( err
));
470 for ( const MOADNSParser :: answers_t :: value_type
& answer
: mdp
. d_answers
)
471 if ( answer
. first
. d_type
== QType :: SOA
)
475 d_tsigVerifier
. check ( std :: string ( d_buf
. get (), len
), mdp
);
477 catch ( const std :: runtime_error
& re
) {
478 throw ResolverException ( re
. what ());
484 void AXFRRetriever :: timeoutReadn ( uint16_t bytes
, uint16_t timeoutsec
)
486 time_t start
= time ( nullptr );
490 int res
= waitForData ( d_sock
, timeoutsec
-( time ( nullptr )- start
));
492 throw ResolverException ( "Reading data from remote nameserver over TCP: " + stringerror ());
494 throw ResolverException ( "Timeout while reading data from remote nameserver over TCP" );
496 numread
= recv ( d_sock
, d_buf
. get ()+ n
, bytes
- n
, 0 );
498 throw ResolverException ( "Reading data from remote nameserver over TCP: " + stringerror ());
500 throw ResolverException ( "Remote nameserver closed TCP connection" );
505 void AXFRRetriever :: connect ()
507 setNonBlocking ( d_sock
);
511 if (( err
=:: connect ( d_sock
,( struct sockaddr
*)& d_remote
, d_remote
. getSocklen ()))< 0 && errno
!= EINPROGRESS
) {
515 catch ( const PDNSException
& e
) {
517 throw ResolverException ( "Error closing AXFR socket after connect() failed: " + e
. reason
);
520 throw ResolverException ( "connect: " + stringerror ());
526 err
= waitForRWData ( d_sock
, false , 10 , 0 ); // wait for writeability
530 closesocket ( d_sock
); // timeout
532 catch ( const PDNSException
& e
) {
534 throw ResolverException ( "Error closing AXFR socket after timeout: " + e
. reason
);
540 throw ResolverException ( "Timeout connecting to server" );
543 throw ResolverException ( "Error connecting: " + string ( strerror ( err
)));
546 Utility :: socklen_t len
= sizeof ( err
);
547 if ( getsockopt ( d_sock
, SOL_SOCKET
, SO_ERROR
,( char *)& err
,& len
)< 0 )
548 throw ResolverException ( "Error connecting: " + stringerror ()); // Solaris
551 throw ResolverException ( "Error connecting: " + string ( strerror ( err
)));
555 setBlocking ( d_sock
);
556 // d_sock now connected
559 int AXFRRetriever :: getLength ( uint16_t timeout
)
561 timeoutReadn ( 2 , timeout
);
562 return ( unsigned char ) d_buf
[ 0 ]* 256 +( unsigned char ) d_buf
[ 1 ];