]>
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"
47 #include "query-local-address.hh"
49 #include "dns_random.hh"
51 #include "gss_context.hh"
52 #include "namespaces.hh"
54 using pdns :: resolver :: parseResult
;
56 int makeQuerySocket ( const ComboAddress
& local
, bool udpOrTCP
, bool nonLocalBind
)
58 ComboAddress
ourLocal ( local
);
60 int sock
= socket ( ourLocal
. sin4
. sin_family
, udpOrTCP
? SOCK_DGRAM
: SOCK_STREAM
, 0 );
62 if ( errno
== EAFNOSUPPORT
&& local
. sin4
. sin_family
== AF_INET6
) {
65 unixDie ( "Creating local resolver socket for address '" + ourLocal
. toString ()+ "'" );
71 Utility :: setBindAny ( local
. sin4
. sin_family
, sock
);
74 // udp, try hard to bind an unpredictable port
77 ourLocal
. sin4
. sin_port
= htons ( 10000 +( dns_random ( 10000 )));
79 if (:: bind ( sock
, ( struct sockaddr
*)& ourLocal
, ourLocal
. getSocklen ()) >= 0 )
82 // cerr<<"bound udp port "<<ourLocal.sin4.sin_port<<", "<<tries<<" tries left"<<endl;
86 throw PDNSException ( "Resolver binding to local UDP socket on '" + ourLocal
. toString ()+ "': " + stringerror ());
90 // tcp, let the kernel figure out the port
91 ourLocal
. sin4
. sin_port
= 0 ;
92 if (:: bind ( sock
, ( struct sockaddr
*)& ourLocal
, ourLocal
. getSocklen ()) < 0 ) {
94 throw PDNSException ( "Resolver binding to local TCP socket on '" + ourLocal
. toString ()+ "': " + stringerror ());
102 locals
[ "default4" ] = - 1 ;
103 locals
[ "default6" ] = - 1 ;
105 if ( pdns :: isQueryLocalAddressFamilyEnabled ( AF_INET
)) {
106 locals
[ "default4" ] = makeQuerySocket ( pdns :: getQueryLocalAddress ( AF_INET
, 0 ), true , :: arg (). mustDo ( "non-local-bind" ));
108 if ( pdns :: isQueryLocalAddressFamilyEnabled ( AF_INET6
)) {
109 locals
[ "default6" ] = makeQuerySocket ( pdns :: getQueryLocalAddress ( AF_INET6
, 0 ), true , :: arg (). mustDo ( "non-local-bind" ));
113 if ( locals
[ "default4" ]>= 0 )
114 close ( locals
[ "default4" ]);
115 if ( locals
[ "default6" ]>= 0 )
116 close ( locals
[ "default6" ]);
121 Resolver ::~ Resolver ()
123 for ( auto & iter
: locals
) {
124 if ( iter
. second
>= 0 )
129 uint16_t Resolver :: sendResolve ( const ComboAddress
& remote
, const ComboAddress
& local
,
130 const DNSName
& domain
, int type
, int * localsock
, bool dnssecOK
,
131 const DNSName
& tsigkeyname
, const DNSName
& tsigalgorithm
,
132 const string
& tsigsecret
)
135 vector
< uint8_t > packet
;
136 DNSPacketWriter
pw ( packet
, domain
, type
);
137 pw
. getHeader ()-> id
= randomid
= dns_random_uint16 ();
140 pw
. addOpt ( 2800 , 0 , EDNSOpts :: DNSSECOK
);
144 if (! tsigkeyname
. empty ()) {
145 // cerr<<"Adding TSIG to notification, key name: '"<<tsigkeyname<<"', algo: '"<<tsigalgorithm<<"', secret: "<<Base64Encode(tsigsecret)<<endl;
146 TSIGRecordContent trc
;
147 if ( tsigalgorithm
== DNSName ( "hmac-md5" ))
148 trc
. d_algoName
= tsigalgorithm
+ DNSName ( "sig-alg.reg.int" );
150 trc
. d_algoName
= tsigalgorithm
;
151 trc
. d_time
= time ( nullptr );
153 trc
. d_origID
= ntohs ( randomid
);
155 addTSIG ( pw
, trc
, tsigkeyname
, tsigsecret
, "" , false );
160 // choose socket based on local
161 if ( local
. sin4
. sin_family
== 0 ) {
163 if (! pdns :: isQueryLocalAddressFamilyEnabled ( remote
. sin4
. sin_family
)) {
164 string ipv
= remote
. sin4
. sin_family
== AF_INET
? "4" : "6" ;
165 throw ResolverException ( "No IPv" + ipv
+ " socket available, is such an address configured in query-local-address?" );
167 sock
= remote
. sin4
. sin_family
== AF_INET
? locals
[ "default4" ] : locals
[ "default6" ];
169 std :: string lstr
= local
. toString ();
170 std :: map
< std :: string
, int >:: iterator lptr
;
172 // reuse an existing local socket or make a new one
173 if (( lptr
= locals
. find ( lstr
)) != locals
. end ()) {
176 // try to make socket
177 sock
= makeQuerySocket ( local
, true );
179 throw ResolverException ( "Unable to create local socket on '" + lstr
+ "'' to '" + remote
. toStringWithPort ()+ "': " + stringerror ());
180 setNonBlocking ( sock
);
185 if ( localsock
!= nullptr ) {
188 if ( sendto ( sock
, & packet
[ 0 ], packet
. size (), 0 , ( struct sockaddr
*)(& remote
), remote
. getSocklen ()) < 0 ) {
189 throw ResolverException ( "Unable to ask query of '" + remote
. toStringWithPort ()+ "': " + stringerror ());
196 int parseResult ( MOADNSParser
& mdp
, const DNSName
& origQname
, uint16_t origQtype
, uint16_t id
, Resolver :: res_t
* result
)
200 if ( mdp
. d_header
. rcode
)
201 return mdp
. d_header
. rcode
;
203 if ( origQname
. countLabels ()) { // not AXFR
204 if ( mdp
. d_header
. id
!= id
)
205 throw ResolverException ( "Remote nameserver replied with wrong id" );
206 if ( mdp
. d_header
. qdcount
!= 1 )
207 throw ResolverException ( "resolver: received answer with wrong number of questions (" + std :: to_string ( mdp
. d_header
. qdcount
)+ ")" );
208 if ( mdp
. d_qname
!= origQname
)
209 throw ResolverException ( string ( "resolver: received an answer to another question (" )+ mdp
. d_qname
. toLogString ()+ "!=" + origQname
. toLogString ()+ ".)" );
212 vector
< DNSResourceRecord
> ret
;
213 DNSResourceRecord rr
;
214 result
-> reserve ( mdp
. d_answers
. size ());
216 for ( const auto & i
: mdp
. d_answers
) {
217 rr
. qname
= i
. first
. d_name
;
218 rr
. qtype
= i
. first
. d_type
;
219 rr
. ttl
= i
. first
. d_ttl
;
220 rr
. content
= i
. first
. d_content
-> getZoneRepresentation ( true );
221 result
-> push_back ( rr
);
227 } // namespace resolver
230 bool Resolver :: tryGetSOASerial ( DNSName
* domain
, ComboAddress
* remote
, uint32_t * theirSerial
, uint32_t * theirInception
, uint32_t * theirExpire
, uint16_t * id
)
232 auto fds
= std :: make_unique
< struct pollfd
[]>( locals
. size ());
236 for ( const auto & iter
: locals
) {
237 fds
[ i
]. fd
= iter
. second
;
238 fds
[ i
]. events
= POLLIN
;
242 if ( poll ( fds
. get (), i
, 250 ) < 1 ) { // wait for 0.25s
250 if (( fds
[ k
]. revents
& POLLIN
) == POLLIN
) {
256 if ( sock
< 0 ) return false ; // false alarm
259 remote
-> sin6
. sin6_family
= AF_INET6
; // make sure getSocklen() below returns a large enough value
260 socklen_t addrlen
= remote
-> getSocklen ();
262 err
= recvfrom ( sock
, buf
, sizeof ( buf
), 0 ,( struct sockaddr
*)( remote
), & addrlen
);
267 throw ResolverException ( "recvfrom error waiting for answer: " + stringerror ());
270 MOADNSParser
mdp ( false , ( char *) buf
, err
);
272 * domain
= mdp
. d_qname
;
275 throw ResolverException ( "SOA query to '" + remote
-> toStringWithPort () + "' produced response without domain name (RCode: " + RCode :: to_s ( mdp
. d_header
. rcode
) + ")" );
277 if ( mdp
. d_answers
. empty ())
278 throw ResolverException ( "Query to '" + remote
-> toStringWithPort () + "' for SOA of '" + domain
-> toLogString () + "' produced no results (RCode: " + RCode :: to_s ( mdp
. d_header
. rcode
) + ")" );
280 if ( mdp
. d_qtype
!= QType :: SOA
)
281 throw ResolverException ( "Query to '" + remote
-> toStringWithPort () + "' for SOA of '" + domain
-> toLogString () + "' returned wrong record type" );
283 if ( mdp
. d_header
. rcode
!= 0 )
284 throw ResolverException ( "Query to '" + remote
-> toStringWithPort () + "' for SOA of '" + domain
-> toLogString () + "' returned Rcode " + RCode :: to_s ( mdp
. d_header
. rcode
));
286 * theirInception
= * theirExpire
= 0 ;
288 for ( const MOADNSParser :: answers_t :: value_type
& drc
: mdp
. d_answers
) {
289 if ( drc
. first
. d_type
== QType :: SOA
&& drc
. first
. d_name
== * domain
) {
290 shared_ptr
< SOARecordContent
> src
= getRR
< SOARecordContent
>( drc
. first
);
292 * theirSerial
= src
-> d_st
. serial
;
296 if ( drc
. first
. d_type
== QType :: RRSIG
&& drc
. first
. d_name
== * domain
) {
297 shared_ptr
< RRSIGRecordContent
> rrc
= getRR
< RRSIGRecordContent
>( drc
. first
);
298 if ( rrc
&& rrc
-> d_type
== QType :: SOA
) {
299 * theirInception
= std :: max (* theirInception
, rrc
-> d_siginception
);
300 * theirExpire
= std :: max (* theirExpire
, rrc
-> d_sigexpire
);
305 throw ResolverException ( "Query to '" + remote
-> toString () + "' for SOA of '" + domain
-> toLogString () + "' did not return a SOA" );
309 int Resolver :: resolve ( const ComboAddress
& to
, const DNSName
& domain
, int type
, Resolver :: res_t
* res
, const ComboAddress
& local
)
313 int id
= sendResolve ( to
, local
, domain
, type
, & sock
);
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 ());
331 throw ResolverException ( "Got answer from the wrong peer while resolving ('" + from
. toStringWithPort ()+ "' instead of '" + to
. toStringWithPort ()+ "', discarding" );
334 MOADNSParser
mdp ( false , buffer
, len
);
335 return parseResult ( mdp
, domain
, type
, id
, res
);
337 catch ( ResolverException
& re
) {
338 throw ResolverException ( re
. reason
+ " from " + to
. toLogString ());
342 int Resolver :: resolve ( const ComboAddress
& ipport
, const DNSName
& domain
, int type
, Resolver :: res_t
* res
) {
344 local
. sin4
. sin_family
= 0 ;
345 return resolve ( ipport
, domain
, type
, res
, local
);
348 void Resolver :: getSoaSerial ( const ComboAddress
& ipport
, const DNSName
& domain
, uint32_t * serial
)
350 vector
< DNSResourceRecord
> res
;
351 int ret
= resolve ( ipport
, domain
, QType :: SOA
, & res
);
353 if ( ret
|| res
. empty ())
354 throw ResolverException ( "Query to '" + ipport
. toLogString () + "' for SOA of '" + domain
. toLogString () + "' produced no answers" );
356 if ( res
[ 0 ]. qtype
. getCode () != QType :: SOA
)
357 throw ResolverException ( "Query to '" + ipport
. toLogString () + "' for SOA of '" + domain
. toLogString () + "' produced a " + res
[ 0 ]. qtype
. toString ()+ " record" );
360 stringtok ( parts
, res
[ 0 ]. content
);
362 throw ResolverException ( "Query to '" + ipport
. toLogString () + "' for SOA of '" + domain
. toLogString () + "' produced an unparseable response" );
365 * serial
= pdns :: checked_stoi
< uint32_t >( parts
[ 2 ]);
367 catch ( const std :: out_of_range
& oor
) {
368 throw ResolverException ( "Query to '" + ipport
. toLogString () + "' for SOA of '" + domain
. toLogString () + "' produced an unparseable serial" );