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