]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsproxy.cc
Standardize license text in all PDNS files
[thirdparty/pdns.git] / pdns / dnsproxy.cc
CommitLineData
12c86877 1/*
12471842
PL
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 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
379ab445 25#include "packetcache.hh"
b636533b 26#include "utility.hh"
12c86877 27#include "dnsproxy.hh"
5c409fa2 28#include "pdnsexception.hh"
12c86877
BH
29#include <sys/types.h>
30#include <errno.h>
31#include "dns.hh"
32#include "logger.hh"
33#include "statbag.hh"
fa8fd4d2 34
12c86877
BH
35
36extern StatBag S;
37extern PacketCache PC;
38
39DNSProxy::DNSProxy(const string &remote)
40{
12c86877
BH
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");
c0cc6559
BH
45 ComboAddress remaddr(remote, 53);
46
47 if((d_sock=socket(remaddr.sin4.sin_family, SOCK_DGRAM,0))<0)
3f81d239 48 throw PDNSException(string("socket: ")+strerror(errno));
12c86877 49
c0cc6559
BH
50 ComboAddress local;
51 if(remaddr.sin4.sin_family==AF_INET)
52 local = ComboAddress("0.0.0.0");
53 else
54 local = ComboAddress("::");
55
12c86877
BH
56 int n=0;
57 for(;n<10;n++) {
c0cc6559 58 local.sin4.sin_port = htons(10000+( Utility::random()%50000));
12c86877 59
c0cc6559 60 if(::bind(d_sock, (struct sockaddr *)&local, local.getSocklen()) >= 0)
12c86877
BH
61 break;
62 }
63 if(n==10) {
3897b9e1 64 closesocket(d_sock);
12c86877 65 d_sock=-1;
3f81d239 66 throw PDNSException(string("binding dnsproxy socket: ")+strerror(errno));
12c86877
BH
67 }
68
c0cc6559 69 if(connect(d_sock, (sockaddr *)&remaddr, remaddr.getSocklen())<0)
3f81d239 70 throw PDNSException("Unable to UDP connect to remote nameserver "+remaddr.toStringWithPort()+": "+stringerror());
12c86877
BH
71
72 d_xor=Utility::random()&0xffff;
c0cc6559 73 L<<Logger::Error<<"DNS Proxy launched, local port "<<ntohs(local.sin4.sin_port)<<", remote "<<remaddr.toStringWithPort()<<endl;
12c86877
BH
74}
75
76void DNSProxy::go()
77{
78 pthread_t tid;
79 pthread_create(&tid,0,&launchhelper,this);
80}
81
82
83void DNSProxy::onlyFrom(const string &ips)
84{
68b011bd 85 d_ng.toMasks(ips);
12c86877
BH
86}
87
b636533b
BH
88bool DNSProxy::recurseFor(DNSPacket* p)
89{
d06799d4 90 return d_ng.match((ComboAddress *)&p->d_remote);
b636533b
BH
91}
92
12c86877
BH
93/** returns false if p->remote is not allowed to recurse via us */
94bool DNSProxy::sendPacket(DNSPacket *p)
95{
b636533b 96 if(!recurseFor(p))
12c86877
BH
97 return false;
98
092f210a 99 uint16_t id;
12c86877
BH
100 {
101 Lock l(&d_lock);
102 id=getID_locked();
103
104 ConntrackEntry ce;
105 ce.id = p->d.id;
d06799d4 106 ce.remote = p->d_remote;
12c86877
BH
107 ce.outsock = p->getSocket();
108 ce.created = time( NULL );
7de6b0d5
BH
109 ce.qtype = p->qtype.getCode();
110 ce.qname = p->qdomain;
f5310162 111 ce.anyLocal = p->d_anyLocal;
d59b894d 112 ce.complete=0;
12c86877
BH
113 d_conntrack[id]=ce;
114 }
bb0bbdc2
BH
115 p->d.id=id^d_xor;
116 p->commitD();
3f45f34d
BH
117
118 const string& buffer = p->getString();
119
120 if(send(d_sock,buffer.c_str(), buffer.length() , 0)<0) { // zoom
12c86877
BH
121 L<<Logger::Error<<"Unable to send a packet to our recursing backend: "<<stringerror()<<endl;
122 }
123 (*d_resquestions)++;
124 return true;
125
126}
d59b894d 127
128//! look up qname aname with r->qtype, plonk it in the answer section of 'r' with name target
561434a6 129bool DNSProxy::completePacket(DNSPacket *r, const DNSName& target,const DNSName& aname)
d59b894d 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();
561434a6 142 ce.qname = target;
d59b894d 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
12c86877
BH
163/** This finds us an unused or stale ID. Does not actually clean the contents */
164int 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) {
d59b894d 173 if(i->second.created) {
4957a608 174 L<<Logger::Warning<<"Recursive query for remote "<<
85db02c5 175 i->second.remote.toStringWithPort()<<" with internal id "<<n<<
4957a608 176 " was not answered by backend within timeout, reusing id"<<endl;
d59b894d 177 delete i->second.complete;
bb6e54fe 178 S.inc("recursion-unanswered");
d59b894d 179 }
12c86877
BH
180 return n;
181 }
182 }
183}
184
185void DNSProxy::mainloop(void)
186{
187 try {
188 char buffer[1500];
a683e8bd 189 ssize_t len;
12c86877 190
65d8e171
KN
191 struct msghdr msgh;
192 struct iovec iov;
193 char cbuf[256];
194
12c86877
BH
195 for(;;) {
196 len=recv(d_sock, buffer, sizeof(buffer),0); // answer from our backend
a683e8bd 197 if(len<(ssize_t)sizeof(dnsheader)) {
4957a608
BH
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;
12c86877
BH
206 }
207 (*d_resanswers)++;
208 (*d_udpanswers)++;
88c1bc50 209 dnsheader d;
caa6eefa 210 memcpy(&d,buffer,sizeof(d));
12c86877 211 {
4957a608 212 Lock l(&d_lock);
bf549564 213#if BYTE_ORDER == BIG_ENDIAN
4957a608
BH
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];
a899e13a 216#endif
4957a608
BH
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)<<
abc1d928 220 ". Conntrack table size="<<d_conntrack.size()<<endl;
4957a608
BH
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 }
d59b894d 227
4957a608
BH
228 d.id=i->second.id;
229 memcpy(buffer,&d,sizeof(d)); // commit spoofed id
230
4957a608 231 DNSPacket p,q;
a683e8bd
RG
232 p.parse(buffer,(size_t)len);
233 q.parse(buffer,(size_t)len);
7de6b0d5
BH
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)<<
c348fec2 237 ", qname or qtype mismatch ("<<p.qtype.getCode()<<" v " <<i->second.qtype<<", "<<p.qdomain<<" v "<<i->second.qname<<")"<<endl;
7de6b0d5
BH
238 continue;
239 }
f5310162 240
65d8e171
KN
241 /* Set up iov and msgh structures. */
242 memset(&msgh, 0, sizeof(struct msghdr));
d59b894d 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;
e693ff5a 250 if(j->first.d_place == DNSResourceRecord::ANSWER || (j->first.d_place == DNSResourceRecord::AUTHORITY && j->first.d_type == QType::SOA)) {
d59b894d 251
252 DNSResourceRecord rr;
253
65d2032b 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))) {
d59b894d 255 rr.qname=i->second.aname;
256 rr.qtype = j->first.d_type;
257 rr.ttl=j->first.d_ttl;
e693ff5a 258 rr.d_place= j->first.d_place;
b9bafae0 259 rr.content=j->first.d_content->getZoneRepresentation();
d59b894d 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 }
65d8e171
KN
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();
3eae691f 279 msgh.msg_control=NULL;
65d8e171
KN
280
281 if(i->second.anyLocal) {
fbe2a2e0 282 addCMsgSrcAddr(&msgh, cbuf, i->second.anyLocal.get_ptr(), 0);
f5310162 283 }
76511d0d
KN
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;
4957a608 286
75fde355 287 PC.insert(&q, &p, true);
4957a608 288 i->second.created=0;
12c86877
BH
289 }
290 }
291 }
3f81d239 292 catch(PDNSException &ae) {
12c86877
BH
293 L<<Logger::Error<<"Fatal error in DNS proxy: "<<ae.reason<<endl;
294 }
adc10f99 295 catch(std::exception &e) {
12c86877
BH
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}
732d9faa
AT
305
306DNSProxy::~DNSProxy() {
307 if (d_sock>-1) closesocket(d_sock);
308 d_sock=-1;
309}