]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsproxy.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.
25 #include "packetcache.hh"
27 #include "dnsproxy.hh"
28 #include "pdnsexception.hh"
29 #include <sys/types.h>
33 #include "dns_random.hh"
34 #include "stubresolver.hh"
35 #include "arguments.hh"
36 #include "threadname.hh"
40 DNSProxy :: DNSProxy ( const string
& remote
)
42 pthread_mutex_init (& d_lock
, 0 );
43 d_resanswers
= S
. getPointer ( "recursing-answers" );
44 d_resquestions
= S
. getPointer ( "recursing-questions" );
45 d_udpanswers
= S
. getPointer ( "udp-answers" );
47 vector
< string
> addresses
;
48 stringtok ( addresses
, remote
, " , \t " );
49 d_remote
= ComboAddress ( addresses
[ 0 ], 53 );
51 if (( d_sock
= socket ( d_remote
. sin4
. sin_family
, SOCK_DGRAM
, 0 ))< 0 ) {
52 throw PDNSException ( string ( "socket: " )+ stringerror ());
56 if ( d_remote
. sin4
. sin_family
== AF_INET
) {
57 local
= ComboAddress ( "0.0.0.0" );
60 local
= ComboAddress ( "::" );
65 local
. sin4
. sin_port
= htons ( 10000 + dns_random ( 50000 ));
67 if (:: bind ( d_sock
, ( struct sockaddr
*)& local
, local
. getSocklen ()) >= 0 )
73 throw PDNSException ( string ( "binding dnsproxy socket: " )+ stringerror ());
76 if ( connect ( d_sock
, ( sockaddr
*)& d_remote
, d_remote
. getSocklen ())< 0 ) {
77 throw PDNSException ( "Unable to UDP connect to remote nameserver " + d_remote
. toStringWithPort ()+ ": " + stringerror ());
80 d_xor
= dns_random_uint16 ();
81 g_log
<< Logger :: Error
<< "DNS Proxy launched, local port " << ntohs ( local
. sin4
. sin_port
)<< ", remote " << d_remote
. toStringWithPort ()<< endl
;
87 pthread_create (& tid
, 0 ,& launchhelper
, this );
90 //! look up qname target with r->qtype, plonk it in the answer section of 'r' with name aname
91 bool DNSProxy :: completePacket ( std :: unique_ptr
< DNSPacket
>& r
, const DNSName
& target
, const DNSName
& aname
, const uint8_t scopeMask
)
94 vector
< DNSZoneRecord
> ips
;
95 int ret1
= 0 , ret2
= 0 ;
97 if ( r
-> qtype
== QType :: A
|| r
-> qtype
== QType :: ANY
)
98 ret1
= stubDoResolve ( target
, QType :: A
, ips
);
99 if ( r
-> qtype
== QType :: AAAA
|| r
-> qtype
== QType :: ANY
)
100 ret2
= stubDoResolve ( target
, QType :: AAAA
, ips
);
102 if ( ret1
!= RCode :: NoError
|| ret2
!= RCode :: NoError
) {
103 g_log
<< Logger :: Error
<< "Error resolving for " << aname
<< " ALIAS " << target
<< " over UDP, original query came in over TCP" ;
104 if ( ret1
!= RCode :: NoError
) {
105 g_log
<< Logger :: Error
<< ", A-record query returned " << RCode :: to_s ( ret1
);
107 if ( ret2
!= RCode :: NoError
) {
108 g_log
<< Logger :: Error
<< ", AAAA-record query returned " << RCode :: to_s ( ret2
);
110 g_log
<< Logger :: Error
<< ", returning SERVFAIL" << endl
;
112 r
-> setRcode ( RCode :: ServFail
);
116 ip
. dr
. d_name
= aname
;
121 uint16_t len
= htons ( r
-> getString (). length ());
122 string
buffer (( const char *)& len
, 2 );
123 buffer
. append ( r
-> getString ());
124 writen2WithTimeout ( r
-> getSocket (), buffer
. c_str (), buffer
. length (), :: arg (). asNum ( "tcp-idle-timeout" ));
130 uint16_t qtype
= r
-> qtype
. getCode ();
137 ce
. remote
= r
-> d_remote
;
138 ce
. outsock
= r
-> getSocket ();
139 ce
. created
= time ( NULL
);
140 ce
. qtype
= r
-> qtype
. getCode ();
142 ce
. anyLocal
= r
-> d_anyLocal
;
143 ce
. complete
= std :: move ( r
);
145 ce
. anameScopeMask
= scopeMask
;
146 d_conntrack
[ id
]= std :: move ( ce
);
149 vector
< uint8_t > packet
;
150 DNSPacketWriter
pw ( packet
, target
, qtype
);
151 pw
. getHeader ()-> rd
= true ;
152 pw
. getHeader ()-> id
= id
^ d_xor
;
154 if ( send ( d_sock
,& packet
[ 0 ], packet
. size () , 0 )< 0 ) { // zoom
155 g_log
<< Logger :: Error
<< "Unable to send a packet to our recursing backend: " << stringerror ()<< endl
;
163 /** This finds us an unused or stale ID. Does not actually clean the contents */
164 int DNSProxy :: getID_locked ()
168 i
= d_conntrack
. find ( n
);
169 if ( i
== d_conntrack
. end ()) {
172 else if ( i
-> second
. created
< time ( 0 )- 60 ) {
173 if ( i
-> second
. created
) {
174 g_log
<< Logger :: Warning
<< "Recursive query for remote " <<
175 i
-> second
. remote
. toStringWithPort ()<< " with internal id " << n
<<
176 " was not answered by backend within timeout, reusing id" << endl
;
177 i
-> second
. complete
. reset ();
178 S
. inc ( "recursion-unanswered" );
185 void DNSProxy :: mainloop ( void )
187 setThreadName ( "pdns/dnsproxy" );
194 cmsgbuf_aligned cbuf
;
195 ComboAddress fromaddr
;
198 socklen_t fromaddrSize
= sizeof ( fromaddr
);
199 len
= recvfrom ( d_sock
, buffer
, sizeof ( buffer
), 0 , ( struct sockaddr
*) & fromaddr
, & fromaddrSize
); // answer from our backend
200 if ( len
<( ssize_t
) sizeof ( dnsheader
)) {
202 g_log
<< Logger :: Error
<< "Error receiving packet from recursor backend: " << stringerror ()<< endl
;
204 g_log
<< Logger :: Error
<< "Error receiving packet from recursor backend, EOF" << endl
;
206 g_log
<< Logger :: Error
<< "Short packet from recursor backend, " << len
<< " bytes" << endl
;
210 if ( fromaddr
!= d_remote
) {
211 g_log
<< Logger :: Error
<< "Got answer from unexpected host " << fromaddr
. toStringWithPort ()<< " instead of our recursor backend " << d_remote
. toStringWithPort ()<< endl
;
217 memcpy (& d
, buffer
, sizeof ( d
));
220 #if BYTE_ORDER == BIG_ENDIAN
221 // this is needed because spoof ID down below does not respect the native byteorder
222 d
. id
= ( 256 * ( uint16_t ) buffer
[ 1 ] ) + ( uint16_t ) buffer
[ 0 ];
224 map_t :: iterator i
= d_conntrack
. find ( d
. id
^ d_xor
);
225 if ( i
== d_conntrack
. end ()) {
226 g_log
<< Logger :: Error
<< "Discarding untracked packet from recursor backend with id " <<( d
. id
^ d_xor
)<<
227 ". Conntrack table size=" << d_conntrack
. size ()<< endl
;
230 else if ( i
-> second
. created
== 0 ) {
231 g_log
<< Logger :: Error
<< "Received packet from recursor backend with id " <<( d
. id
^ d_xor
)<< " which is a duplicate" << endl
;
236 memcpy ( buffer
,& d
, sizeof ( d
)); // commit spoofed id
238 DNSPacket
p ( false ), q ( false );
239 p
. parse ( buffer
,( size_t ) len
);
240 q
. parse ( buffer
,( size_t ) len
);
242 if ( p
. qtype
. getCode () != i
-> second
. qtype
|| p
. qdomain
!= i
-> second
. qname
) {
243 g_log
<< Logger :: Error
<< "Discarding packet from recursor backend with id " <<( d
. id
^ d_xor
)<<
244 ", qname or qtype mismatch (" << p
. qtype
. getCode ()<< " v " << i
-> second
. qtype
<< ", " << p
. qdomain
<< " v " << i
-> second
. qname
<< ")" << endl
;
248 /* Set up iov and msgh structures. */
249 memset (& msgh
, 0 , sizeof ( struct msghdr
));
250 string reply
; // needs to be alive at time of sendmsg!
251 MOADNSParser
mdp ( false , p
. getString ());
252 // cerr<<"Got completion, "<<mdp.d_answers.size()<<" answers, rcode: "<<mdp.d_header.rcode<<endl;
253 if ( mdp
. d_header
. rcode
== RCode :: NoError
) {
254 for ( MOADNSParser :: answers_t :: const_iterator j
= mdp
. d_answers
. begin (); j
!= mdp
. d_answers
. end (); ++ j
) {
255 // cerr<<"comp: "<<(int)j->first.d_place-1<<" "<<j->first.d_label<<" " << DNSRecordContent::NumberToType(j->first.d_type)<<" "<<j->first.d_content->getZoneRepresentation()<<endl;
256 if ( j
-> first
. d_place
== DNSResourceRecord :: ANSWER
|| ( j
-> first
. d_place
== DNSResourceRecord :: AUTHORITY
&& j
-> first
. d_type
== QType :: SOA
)) {
258 if ( j
-> first
. d_type
== i
-> second
. qtype
|| ( i
-> second
. qtype
== QType :: ANY
&& ( j
-> first
. d_type
== QType :: A
|| j
-> first
. d_type
== QType :: AAAA
))) {
260 dzr
. dr
. d_name
= i
-> second
. aname
;
261 dzr
. dr
. d_type
= j
-> first
. d_type
;
262 dzr
. dr
. d_ttl
= j
-> first
. d_ttl
;
263 dzr
. dr
. d_place
= j
-> first
. d_place
;
264 dzr
. dr
. d_content
= j
-> first
. d_content
;
265 i
-> second
. complete
-> addRecord ( dzr
);
269 i
-> second
. complete
-> setRcode ( mdp
. d_header
. rcode
);
271 g_log
<< Logger :: Error
<< "Error resolving for " << i
-> second
. aname
<< " ALIAS " << i
-> second
. qname
<< " over UDP, " << QType ( i
-> second
. qtype
). getName ()<< "-record query returned " << RCode :: to_s ( mdp
. d_header
. rcode
)<< ", returning SERVFAIL" << endl
;
272 i
-> second
. complete
-> clearRecords ();
273 i
-> second
. complete
-> setRcode ( RCode :: ServFail
);
275 reply
= i
-> second
. complete
-> getString ();
276 iov
. iov_base
= ( void *) reply
. c_str ();
277 iov
. iov_len
= reply
. length ();
278 i
-> second
. complete
. reset ();
281 msgh
. msg_name
= ( struct sockaddr
*)& i
-> second
. remote
;
282 msgh
. msg_namelen
= i
-> second
. remote
. getSocklen ();
283 msgh
. msg_control
= NULL
;
285 if ( i
-> second
. anyLocal
) {
286 addCMsgSrcAddr (& msgh
, & cbuf
, i
-> second
. anyLocal
. get_ptr (), 0 );
288 if ( sendmsg ( i
-> second
. outsock
, & msgh
, 0 ) < 0 ) {
290 g_log
<< Logger :: Warning
<< "dnsproxy.cc: Error sending reply with sendmsg (socket=" << i
-> second
. outsock
<< "): " << stringerror ( err
)<< endl
;
296 catch ( PDNSException
& ae
) {
297 g_log
<< Logger :: Error
<< "Fatal error in DNS proxy: " << ae
. reason
<< endl
;
299 catch ( std :: exception
& e
) {
300 g_log
<< Logger :: Error
<< "Communicator thread died because of STL error: " << e
. what ()<< endl
;
304 g_log
<< Logger :: Error
<< "Caught unknown exception." << endl
;
306 g_log
<< Logger :: Error
<< "Exiting because DNS proxy failed" << endl
;
310 DNSProxy ::~ DNSProxy () {
315 catch ( const PDNSException
& e
) {