]>
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 ourLocal
. sin4
. sin_port
= 0 ;
90 if (:: bind ( sock
, ( struct sockaddr
*)& ourLocal
, ourLocal
. getSocklen ()) < 0 ) {
92 throw PDNSException ( "Resolver binding to local TCP socket on " + ourLocal
. toString ()+ ": " + stringerror ());
100 locals
[ "default4" ] = - 1 ;
101 locals
[ "default6" ] = - 1 ;
103 if (!:: arg ()[ "query-local-address" ]. empty ())
104 locals
[ "default4" ] = makeQuerySocket ( ComboAddress (:: arg ()[ "query-local-address" ]), true , :: arg (). mustDo ( "non-local-bind" ));
105 if (!:: arg ()[ "query-local-address6" ]. empty ())
106 locals
[ "default6" ] = makeQuerySocket ( ComboAddress (:: arg ()[ "query-local-address6" ]), true , :: arg (). mustDo ( "non-local-bind" ));
109 if ( locals
[ "default4" ]>= 0 )
110 close ( locals
[ "default4" ]);
111 if ( locals
[ "default6" ]>= 0 )
112 close ( locals
[ "default6" ]);
117 Resolver ::~ Resolver ()
119 for ( auto & iter
: locals
) {
120 if ( iter
. second
>= 0 )
125 uint16_t Resolver :: sendResolve ( const ComboAddress
& remote
, const ComboAddress
& local
,
126 const DNSName
& domain
, int type
, int * localsock
, bool dnssecOK
,
127 const DNSName
& tsigkeyname
, const DNSName
& tsigalgorithm
,
128 const string
& tsigsecret
)
131 vector
< uint8_t > packet
;
132 DNSPacketWriter
pw ( packet
, domain
, type
);
133 pw
. getHeader ()-> id
= randomid
= dns_random ( 0xffff );
136 pw
. addOpt ( 2800 , 0 , EDNSOpts :: DNSSECOK
);
140 if (! tsigkeyname
. empty ()) {
141 // cerr<<"Adding TSIG to notification, key name: '"<<tsigkeyname<<"', algo: '"<<tsigalgorithm<<"', secret: "<<Base64Encode(tsigsecret)<<endl;
142 TSIGRecordContent trc
;
143 if ( tsigalgorithm
== DNSName ( "hmac-md5" ))
144 trc
. d_algoName
= tsigalgorithm
+ DNSName ( "sig-alg.reg.int" );
146 trc
. d_algoName
= tsigalgorithm
;
147 trc
. d_time
= time ( 0 );
149 trc
. d_origID
= ntohs ( randomid
);
151 addTSIG ( pw
, trc
, tsigkeyname
, tsigsecret
, "" , false );
156 // choose socket based on local
157 if ( local
. sin4
. sin_family
== 0 ) {
159 sock
= remote
. sin4
. sin_family
== AF_INET
? locals
[ "default4" ] : locals
[ "default6" ];
161 string ipv
= remote
. sin4
. sin_family
== AF_INET
? "4" : "6" ;
162 string qla
= remote
. sin4
. sin_family
== AF_INET
? "" : "6" ;
163 throw ResolverException ( "No IPv" + ipv
+ " socket available, is query-local-address" + qla
+ " unset?" );
166 std :: string lstr
= local
. toString ();
167 std :: map
< std :: string
, int >:: iterator lptr
;
169 // reuse an existing local socket or make a new one
170 if (( lptr
= locals
. find ( lstr
)) != locals
. end ()) {
173 // try to make socket
174 sock
= makeQuerySocket ( local
, true );
176 throw ResolverException ( "Unable to create local socket on " + lstr
+ " to " + remote
. toStringWithPort ()+ ": " + stringerror ());
177 setNonBlocking ( sock
);
182 if ( localsock
!= nullptr ) {
185 if ( sendto ( sock
, & packet
[ 0 ], packet
. size (), 0 , ( struct sockaddr
*)(& remote
), remote
. getSocklen ()) < 0 ) {
186 throw ResolverException ( "Unable to ask query of " + remote
. toStringWithPort ()+ ": " + stringerror ());
191 static int parseResult ( MOADNSParser
& mdp
, const DNSName
& origQname
, uint16_t origQtype
, uint16_t id
, Resolver :: res_t
* result
)
195 if ( mdp
. d_header
. rcode
)
196 return mdp
. d_header
. rcode
;
198 if ( origQname
. countLabels ()) { // not AXFR
199 if ( mdp
. d_header
. id
!= id
)
200 throw ResolverException ( "Remote nameserver replied with wrong id" );
201 if ( mdp
. d_header
. qdcount
!= 1 )
202 throw ResolverException ( "resolver: received answer with wrong number of questions (" + itoa ( mdp
. d_header
. qdcount
)+ ")" );
203 if ( mdp
. d_qname
!= origQname
)
204 throw ResolverException ( string ( "resolver: received an answer to another question (" )+ mdp
. d_qname
. toLogString ()+ "!=" + origQname
. toLogString ()+ ".)" );
207 vector
< DNSResourceRecord
> ret
;
208 DNSResourceRecord rr
;
209 result
-> reserve ( mdp
. d_answers
. size ());
211 for ( const auto & i
: mdp
. d_answers
) {
212 rr
. qname
= i
. first
. d_name
;
213 rr
. qtype
= i
. first
. d_type
;
214 rr
. ttl
= i
. first
. d_ttl
;
215 rr
. content
= i
. first
. d_content
-> getZoneRepresentation ( true );
216 result
-> push_back ( rr
);
222 bool Resolver :: tryGetSOASerial ( DNSName
* domain
, ComboAddress
* remote
, uint32_t * theirSerial
, uint32_t * theirInception
, uint32_t * theirExpire
, uint16_t * id
)
224 auto fds
= std :: unique_ptr
< struct pollfd
[]>( new struct pollfd
[ locals
. size ()]);
228 for ( const auto & iter
: locals
) {
229 fds
[ i
]. fd
= iter
. second
;
230 fds
[ i
]. events
= POLLIN
;
234 if ( poll ( fds
. get (), i
, 250 ) < 1 ) { // wait for 0.25s
242 if (( fds
[ k
]. revents
& POLLIN
) == POLLIN
) {
248 if ( sock
< 0 ) return false ; // false alarm
251 remote
-> sin6
. sin6_family
= AF_INET6
; // make sure getSocklen() below returns a large enough value
252 socklen_t addrlen
= remote
-> getSocklen ();
254 err
= recvfrom ( sock
, buf
, sizeof ( buf
), 0 ,( struct sockaddr
*)( remote
), & addrlen
);
259 throw ResolverException ( "recvfrom error waiting for answer: " + stringerror ());
262 MOADNSParser
mdp ( false , ( char *) buf
, err
);
264 * domain
= mdp
. d_qname
;
267 throw ResolverException ( "SOA query to '" + remote
-> toStringWithPort () + "' produced response without domain name (RCode: " + RCode :: to_s ( mdp
. d_header
. rcode
) + ")" );
269 if ( mdp
. d_answers
. empty ())
270 throw ResolverException ( "Query to '" + remote
-> toStringWithPort () + "' for SOA of '" + domain
-> toLogString () + "' produced no results (RCode: " + RCode :: to_s ( mdp
. d_header
. rcode
) + ")" );
272 if ( mdp
. d_qtype
!= QType :: SOA
)
273 throw ResolverException ( "Query to '" + remote
-> toStringWithPort () + "' for SOA of '" + domain
-> toLogString () + "' returned wrong record type" );
275 if ( mdp
. d_header
. rcode
!= 0 )
276 throw ResolverException ( "Query to '" + remote
-> toStringWithPort () + "' for SOA of '" + domain
-> toLogString () + "' returned Rcode " + RCode :: to_s ( mdp
. d_header
. rcode
));
278 * theirInception
= * theirExpire
= 0 ;
280 for ( const MOADNSParser :: answers_t :: value_type
& drc
: mdp
. d_answers
) {
281 if ( drc
. first
. d_type
== QType :: SOA
&& drc
. first
. d_name
== * domain
) {
282 shared_ptr
< SOARecordContent
> src
= getRR
< SOARecordContent
>( drc
. first
);
284 * theirSerial
= src
-> d_st
. serial
;
288 if ( drc
. first
. d_type
== QType :: RRSIG
&& drc
. first
. d_name
== * domain
) {
289 shared_ptr
< RRSIGRecordContent
> rrc
= getRR
< RRSIGRecordContent
>( drc
. first
);
290 if ( rrc
&& rrc
-> d_type
== QType :: SOA
) {
291 * theirInception
= std :: max (* theirInception
, rrc
-> d_siginception
);
292 * theirExpire
= std :: max (* theirExpire
, rrc
-> d_sigexpire
);
297 throw ResolverException ( "Query to '" + remote
-> toString () + "' for SOA of '" + domain
-> toLogString () + "' did not return a SOA" );
301 int Resolver :: resolve ( const ComboAddress
& to
, const DNSName
& domain
, int type
, Resolver :: res_t
* res
, const ComboAddress
& local
)
305 int id
= sendResolve ( to
, local
, domain
, type
, & sock
);
306 int err
= waitForData ( sock
, 0 , 3000000 );
309 throw ResolverException ( "Timeout waiting for answer" );
312 throw ResolverException ( "Error waiting for answer: " + stringerror ());
315 socklen_t addrlen
= sizeof ( from
);
319 if (( len
= recvfrom ( sock
, buffer
, sizeof ( buffer
), 0 ,( struct sockaddr
*)(& from
), & addrlen
)) < 0 )
320 throw ResolverException ( "recvfrom error waiting for answer: " + stringerror ());
323 throw ResolverException ( "Got answer from the wrong peer while resolving (" + from
. toStringWithPort ()+ " instead of " + to
. toStringWithPort ()+ ", discarding" );
326 MOADNSParser
mdp ( false , buffer
, len
);
327 return parseResult ( mdp
, domain
, type
, id
, res
);
329 catch ( ResolverException
& re
) {
330 throw ResolverException ( re
. reason
+ " from " + to
. toLogString ());
335 int Resolver :: resolve ( const ComboAddress
& ipport
, const DNSName
& domain
, int type
, Resolver :: res_t
* res
) {
337 local
. sin4
. sin_family
= 0 ;
338 return resolve ( ipport
, domain
, type
, res
, local
);
341 void Resolver :: getSoaSerial ( const ComboAddress
& ipport
, const DNSName
& domain
, uint32_t * serial
)
343 vector
< DNSResourceRecord
> res
;
344 int ret
= resolve ( ipport
, domain
, QType :: SOA
, & res
);
346 if ( ret
|| res
. empty ())
347 throw ResolverException ( "Query to '" + ipport
. toLogString () + "' for SOA of '" + domain
. toLogString () + "' produced no answers" );
349 if ( res
[ 0 ]. qtype
. getCode () != QType :: SOA
)
350 throw ResolverException ( "Query to '" + ipport
. toLogString () + "' for SOA of '" + domain
. toLogString () + "' produced a " + res
[ 0 ]. qtype
. getName ()+ " record" );
353 stringtok ( parts
, res
[ 0 ]. content
);
355 throw ResolverException ( "Query to '" + ipport
. toLogString () + "' for SOA of '" + domain
. toLogString () + "' produced an unparseable response" );
358 * serial
= pdns_stou ( parts
[ 2 ]);
360 catch ( const std :: out_of_range
& oor
) {
361 throw ResolverException ( "Query to '" + ipport
. toLogString () + "' for SOA of '" + domain
. toLogString () + "' produced an unparseable serial" );
365 AXFRRetriever :: AXFRRetriever ( const ComboAddress
& remote
,
366 const DNSName
& domain
,
367 const TSIGTriplet
& tt
,
368 const ComboAddress
* laddr
,
369 size_t maxReceivedBytes
,
371 : d_tsigVerifier ( tt
, remote
, d_trc
), d_receivedBytes ( 0 ), d_maxReceivedBytes ( maxReceivedBytes
)
374 if ( laddr
!= nullptr ) {
375 local
= ComboAddress (* laddr
);
377 string qlas
= remote
. sin4
. sin_family
== AF_INET
? "query-local-address" : "query-local-address6" ;
378 if (:: arg ()[ qlas
]. empty ()) {
379 throw ResolverException ( "Unable to determine source address for AXFR request to " + remote
. toStringWithPort () + " for " + domain
. toLogString () + ". " + qlas
+ " is unset" );
381 local
= ComboAddress (:: arg ()[ qlas
]);
385 d_sock
= makeQuerySocket ( local
, false ); // make a TCP socket
387 throw ResolverException ( "Error creating socket for AXFR request to " + d_remote
. toStringWithPort ());
388 d_buf
= shared_array
< char >( new char [ 65536 ]);
389 d_remote
= remote
; // mostly for error reporting
390 this -> connect ( timeout
);
393 vector
< uint8_t > packet
;
394 DNSPacketWriter
pw ( packet
, domain
, QType :: AXFR
);
395 pw
. getHeader ()-> id
= dns_random ( 0xffff );
397 if (! tt
. name
. empty ()) {
398 if ( tt
. algo
== DNSName ( "hmac-md5" ))
399 d_trc
. d_algoName
= tt
. algo
+ DNSName ( "sig-alg.reg.int" );
401 d_trc
. d_algoName
= tt
. algo
;
402 d_trc
. d_time
= time ( 0 );
404 d_trc
. d_origID
= ntohs ( pw
. getHeader ()-> id
);
406 addTSIG ( pw
, d_trc
, tt
. name
, tt
. secret
, "" , false );
409 uint16_t replen
= htons ( packet
. size ());
410 Utility :: iovec iov
[ 2 ];
411 iov
[ 0 ]. iov_base
= reinterpret_cast < char *>(& replen
);
413 iov
[ 1 ]. iov_base
= packet
. data ();
414 iov
[ 1 ]. iov_len
= packet
. size ();
416 int ret
= Utility :: writev ( d_sock
, iov
, 2 );
418 throw ResolverException ( "Error sending question to " + d_remote
. toStringWithPort ()+ ": " + stringerror ());
419 if ( ret
!= ( int )( 2 + packet
. size ())) {
420 throw ResolverException ( "Partial write on AXFR request to " + d_remote
. toStringWithPort ());
423 int res
= waitForData ( d_sock
, timeout
, 0 );
426 throw ResolverException ( "Timeout waiting for answer from " + d_remote
. toStringWithPort ()+ " during AXFR" );
428 throw ResolverException ( "Error waiting for answer from " + d_remote
. toStringWithPort ()+ ": " + stringerror ());
438 AXFRRetriever ::~ AXFRRetriever ()
445 int AXFRRetriever :: getChunk ( Resolver :: res_t
& res
, vector
< DNSRecord
>* records
, uint16_t timeout
) // Implementation is making sure RFC2845 4.4 is followed.
450 // d_sock is connected and is about to spit out a packet
451 int len
= getLength ( timeout
);
453 throw ResolverException ( "EOF trying to read axfr chunk from remote TCP client" );
455 if ( d_maxReceivedBytes
> 0 && ( d_maxReceivedBytes
- d_receivedBytes
) < ( size_t ) len
)
456 throw ResolverException ( "Reached the maximum number of received bytes during AXFR" );
458 timeoutReadn ( len
, timeout
);
460 d_receivedBytes
+= ( uint16_t ) len
;
462 MOADNSParser
mdp ( false , d_buf
. get (), len
);
464 int err
= mdp
. d_header
. rcode
;
467 throw ResolverException ( "AXFR chunk error: " + RCode :: to_s ( err
));
471 d_tsigVerifier
. check ( std :: string ( d_buf
. get (), len
), mdp
);
473 catch ( const std :: runtime_error
& re
) {
474 throw ResolverException ( re
. what ());
478 err
= parseResult ( mdp
, DNSName (), 0 , 0 , & res
);
481 for ( const auto & answer
: mdp
. d_answers
)
482 if ( answer
. first
. d_type
== QType :: SOA
)
488 records
-> reserve ( mdp
. d_answers
. size ());
490 for ( auto & r
: mdp
. d_answers
) {
491 if ( r
. first
. d_type
== QType :: SOA
) {
495 records
-> push_back ( std :: move ( r
. first
));
502 void AXFRRetriever :: timeoutReadn ( uint16_t bytes
, uint16_t timeoutsec
)
504 time_t start
= time ( nullptr );
508 int res
= waitForData ( d_sock
, timeoutsec
-( time ( nullptr )- start
));
510 throw ResolverException ( "Reading data from remote nameserver over TCP: " + stringerror ());
512 throw ResolverException ( "Timeout while reading data from remote nameserver over TCP" );
514 numread
= recv ( d_sock
, d_buf
. get ()+ n
, bytes
- n
, 0 );
516 throw ResolverException ( "Reading data from remote nameserver over TCP: " + stringerror ());
518 throw ResolverException ( "Remote nameserver closed TCP connection" );
523 void AXFRRetriever :: connect ( uint16_t timeout
)
525 setNonBlocking ( d_sock
);
529 if (( err
=:: connect ( d_sock
,( struct sockaddr
*)& d_remote
, d_remote
. getSocklen ()))< 0 && errno
!= EINPROGRESS
) {
533 catch ( const PDNSException
& e
) {
535 throw ResolverException ( "Error closing AXFR socket after connect() failed: " + e
. reason
);
538 throw ResolverException ( "connect: " + stringerror ());
544 err
= waitForRWData ( d_sock
, false , timeout
, 0 ); // wait for writeability
548 closesocket ( d_sock
); // timeout
550 catch ( const PDNSException
& e
) {
552 throw ResolverException ( "Error closing AXFR socket after timeout: " + e
. reason
);
558 throw ResolverException ( "Timeout connecting to server" );
561 throw ResolverException ( "Error connecting: " + string ( strerror ( errno
)));
564 Utility :: socklen_t len
= sizeof ( err
);
565 if ( getsockopt ( d_sock
, SOL_SOCKET
, SO_ERROR
,( char *)& err
,& len
)< 0 )
566 throw ResolverException ( "Error connecting: " + stringerror ()); // Solaris
569 throw ResolverException ( "Error connecting: " + string ( strerror ( err
)));
573 setBlocking ( d_sock
);
574 // d_sock now connected
577 int AXFRRetriever :: getLength ( uint16_t timeout
)
579 timeoutReadn ( 2 , timeout
);
580 return ( unsigned char ) d_buf
[ 0 ]* 256 +( unsigned char ) d_buf
[ 1 ];