]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsproxy.cc
Merge pull request #6172 from cassvin/ixfr
[thirdparty/pdns.git] / pdns / dnsproxy.cc
1 /*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
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.
8 *
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.
12 *
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.
17 *
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.
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "packetcache.hh"
26 #include "utility.hh"
27 #include "dnsproxy.hh"
28 #include "pdnsexception.hh"
29 #include <sys/types.h>
30 #include <errno.h>
31 #include "dns.hh"
32 #include "logger.hh"
33 #include "statbag.hh"
34 #include "dns_random.hh"
35
36 extern StatBag S;
37
38 DNSProxy::DNSProxy(const string &remote)
39 {
40 pthread_mutex_init(&d_lock,0);
41 d_resanswers=S.getPointer("recursing-answers");
42 d_resquestions=S.getPointer("recursing-questions");
43 d_udpanswers=S.getPointer("udp-answers");
44
45 vector<string> addresses;
46 stringtok(addresses, remote, " ,\t");
47 d_remote = ComboAddress(addresses[0], 53);
48
49 if((d_sock=socket(d_remote.sin4.sin_family, SOCK_DGRAM,0))<0) {
50 throw PDNSException(string("socket: ")+strerror(errno));
51 }
52
53 ComboAddress local;
54 if(d_remote.sin4.sin_family==AF_INET) {
55 local = ComboAddress("0.0.0.0");
56 }
57 else {
58 local = ComboAddress("::");
59 }
60
61 unsigned int n=0;
62 for(;n<10;n++) {
63 local.sin4.sin_port = htons(10000+dns_random(50000));
64
65 if(::bind(d_sock, (struct sockaddr *)&local, local.getSocklen()) >= 0)
66 break;
67 }
68 if(n==10) {
69 closesocket(d_sock);
70 d_sock=-1;
71 throw PDNSException(string("binding dnsproxy socket: ")+strerror(errno));
72 }
73
74 if(connect(d_sock, (sockaddr *)&d_remote, d_remote.getSocklen())<0) {
75 throw PDNSException("Unable to UDP connect to remote nameserver "+d_remote.toStringWithPort()+": "+stringerror());
76 }
77
78 d_xor=dns_random(0xffff);
79 L<<Logger::Error<<"DNS Proxy launched, local port "<<ntohs(local.sin4.sin_port)<<", remote "<<d_remote.toStringWithPort()<<endl;
80 }
81
82 void DNSProxy::go()
83 {
84 pthread_t tid;
85 pthread_create(&tid,0,&launchhelper,this);
86 }
87
88 /** returns false if p->remote is not allowed to recurse via us */
89 bool DNSProxy::sendPacket(DNSPacket *p)
90 {
91 uint16_t id;
92 {
93 Lock l(&d_lock);
94 id=getID_locked();
95
96 ConntrackEntry ce;
97 ce.id = p->d.id;
98 ce.remote = p->d_remote;
99 ce.outsock = p->getSocket();
100 ce.created = time( NULL );
101 ce.qtype = p->qtype.getCode();
102 ce.qname = p->qdomain;
103 ce.anyLocal = p->d_anyLocal;
104 ce.complete=0;
105 d_conntrack[id]=ce;
106 }
107 p->d.id=id^d_xor;
108 p->commitD();
109
110 const string& buffer = p->getString();
111
112 if(send(d_sock,buffer.c_str(), buffer.length() , 0)<0) { // zoom
113 L<<Logger::Error<<"Unable to send a packet to our recursing backend: "<<stringerror()<<endl;
114 }
115 (*d_resquestions)++;
116 return true;
117
118 }
119
120 //! look up qname aname with r->qtype, plonk it in the answer section of 'r' with name target
121 bool DNSProxy::completePacket(DNSPacket *r, const DNSName& target,const DNSName& aname)
122 {
123 uint16_t id;
124 {
125 Lock l(&d_lock);
126 id=getID_locked();
127
128 ConntrackEntry ce;
129 ce.id = r->d.id;
130 ce.remote = r->d_remote;
131 ce.outsock = r->getSocket();
132 ce.created = time( NULL );
133 ce.qtype = r->qtype.getCode();
134 ce.qname = target;
135 ce.anyLocal = r->d_anyLocal;
136 ce.complete = r;
137 ce.aname=aname;
138 d_conntrack[id]=ce;
139 }
140
141 vector<uint8_t> packet;
142 DNSPacketWriter pw(packet, target, r->qtype.getCode());
143 pw.getHeader()->rd=true;
144 pw.getHeader()->id=id ^ d_xor;
145
146 if(send(d_sock,&packet[0], packet.size() , 0)<0) { // zoom
147 L<<Logger::Error<<"Unable to send a packet to our recursing backend: "<<stringerror()<<endl;
148 }
149
150 return true;
151
152 }
153
154
155 /** This finds us an unused or stale ID. Does not actually clean the contents */
156 int DNSProxy::getID_locked()
157 {
158 map_t::iterator i;
159 for(int n=0;;++n) {
160 i=d_conntrack.find(n);
161 if(i==d_conntrack.end()) {
162 return n;
163 }
164 else if(i->second.created<time(0)-60) {
165 if(i->second.created) {
166 L<<Logger::Warning<<"Recursive query for remote "<<
167 i->second.remote.toStringWithPort()<<" with internal id "<<n<<
168 " was not answered by backend within timeout, reusing id"<<endl;
169 delete i->second.complete;
170 S.inc("recursion-unanswered");
171 }
172 return n;
173 }
174 }
175 }
176
177 void DNSProxy::mainloop(void)
178 {
179 try {
180 char buffer[1500];
181 ssize_t len;
182
183 struct msghdr msgh;
184 struct iovec iov;
185 char cbuf[256];
186 ComboAddress fromaddr;
187
188 for(;;) {
189 socklen_t fromaddrSize = sizeof(fromaddr);
190 len=recvfrom(d_sock, buffer, sizeof(buffer),0, (struct sockaddr*) &fromaddr, &fromaddrSize); // answer from our backend
191 if(len<(ssize_t)sizeof(dnsheader)) {
192 if(len<0)
193 L<<Logger::Error<<"Error receiving packet from recursor backend: "<<stringerror()<<endl;
194 else if(len==0)
195 L<<Logger::Error<<"Error receiving packet from recursor backend, EOF"<<endl;
196 else
197 L<<Logger::Error<<"Short packet from recursor backend, "<<len<<" bytes"<<endl;
198
199 continue;
200 }
201 if (fromaddr != d_remote) {
202 L<<Logger::Error<<"Got answer from unexpected host "<<fromaddr.toStringWithPort()<<" instead of our recursor backend "<<d_remote.toStringWithPort()<<endl;
203 continue;
204 }
205 (*d_resanswers)++;
206 (*d_udpanswers)++;
207 dnsheader d;
208 memcpy(&d,buffer,sizeof(d));
209 {
210 Lock l(&d_lock);
211 #if BYTE_ORDER == BIG_ENDIAN
212 // this is needed because spoof ID down below does not respect the native byteorder
213 d.id = ( 256 * (uint16_t)buffer[1] ) + (uint16_t)buffer[0];
214 #endif
215 map_t::iterator i=d_conntrack.find(d.id^d_xor);
216 if(i==d_conntrack.end()) {
217 L<<Logger::Error<<"Discarding untracked packet from recursor backend with id "<<(d.id^d_xor)<<
218 ". Conntrack table size="<<d_conntrack.size()<<endl;
219 continue;
220 }
221 else if(i->second.created==0) {
222 L<<Logger::Error<<"Received packet from recursor backend with id "<<(d.id^d_xor)<<" which is a duplicate"<<endl;
223 continue;
224 }
225
226 d.id=i->second.id;
227 memcpy(buffer,&d,sizeof(d)); // commit spoofed id
228
229 DNSPacket p(false),q(false);
230 p.parse(buffer,(size_t)len);
231 q.parse(buffer,(size_t)len);
232
233 if(p.qtype.getCode() != i->second.qtype || p.qdomain != i->second.qname) {
234 L<<Logger::Error<<"Discarding packet from recursor backend with id "<<(d.id^d_xor)<<
235 ", qname or qtype mismatch ("<<p.qtype.getCode()<<" v " <<i->second.qtype<<", "<<p.qdomain<<" v "<<i->second.qname<<")"<<endl;
236 continue;
237 }
238
239 /* Set up iov and msgh structures. */
240 memset(&msgh, 0, sizeof(struct msghdr));
241 string reply; // needs to be alive at time of sendmsg!
242 if(i->second.complete) {
243
244 MOADNSParser mdp(false, p.getString());
245 // cerr<<"Got completion, "<<mdp.d_answers.size()<<" answers, rcode: "<<mdp.d_header.rcode<<endl;
246 for(MOADNSParser::answers_t::const_iterator j=mdp.d_answers.begin(); j!=mdp.d_answers.end(); ++j) {
247 // cerr<<"comp: "<<(int)j->first.d_place-1<<" "<<j->first.d_label<<" " << DNSRecordContent::NumberToType(j->first.d_type)<<" "<<j->first.d_content->getZoneRepresentation()<<endl;
248 if(j->first.d_place == DNSResourceRecord::ANSWER || (j->first.d_place == DNSResourceRecord::AUTHORITY && j->first.d_type == QType::SOA)) {
249
250 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))) {
251 DNSZoneRecord dzr;
252 dzr.dr.d_name=i->second.aname;
253 dzr.dr.d_type = j->first.d_type;
254 dzr.dr.d_ttl=j->first.d_ttl;
255 dzr.dr.d_place= j->first.d_place;
256 dzr.dr.d_content=j->first.d_content;
257 i->second.complete->addRecord(dzr);
258 }
259 }
260 }
261 i->second.complete->setRcode(mdp.d_header.rcode);
262 reply=i->second.complete->getString();
263 iov.iov_base = (void*)reply.c_str();
264 iov.iov_len = reply.length();
265 delete i->second.complete;
266 i->second.complete=0;
267 }
268 else {
269 iov.iov_base = buffer;
270 iov.iov_len = len;
271 }
272 msgh.msg_iov = &iov;
273 msgh.msg_iovlen = 1;
274 msgh.msg_name = (struct sockaddr*)&i->second.remote;
275 msgh.msg_namelen = i->second.remote.getSocklen();
276 msgh.msg_control=NULL;
277
278 if(i->second.anyLocal) {
279 addCMsgSrcAddr(&msgh, cbuf, i->second.anyLocal.get_ptr(), 0);
280 }
281 if(sendmsg(i->second.outsock, &msgh, 0) < 0)
282 L<<Logger::Warning<<"dnsproxy.cc: Error sending reply with sendmsg (socket="<<i->second.outsock<<"): "<<strerror(errno)<<endl;
283
284 i->second.created=0;
285 }
286 }
287 }
288 catch(PDNSException &ae) {
289 L<<Logger::Error<<"Fatal error in DNS proxy: "<<ae.reason<<endl;
290 }
291 catch(std::exception &e) {
292 L<<Logger::Error<<"Communicator thread died because of STL error: "<<e.what()<<endl;
293 }
294 catch( ... )
295 {
296 L << Logger::Error << "Caught unknown exception." << endl;
297 }
298 L<<Logger::Error<<"Exiting because DNS proxy failed"<<endl;
299 _exit(1);
300 }
301
302 DNSProxy::~DNSProxy() {
303 if (d_sock>-1) {
304 try {
305 closesocket(d_sock);
306 }
307 catch(const PDNSException& e) {
308 }
309 }
310
311 d_sock=-1;
312 }