]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsproxy.cc
auth: switch circleci mssql image
[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"
d2116c15 34#include "dns_random.hh"
c4e084f2 35#include "stubresolver.hh"
c4e084f2 36#include "arguments.hh"
519f5484 37#include "threadname.hh"
12c86877
BH
38
39extern StatBag S;
12c86877
BH
40
41DNSProxy::DNSProxy(const string &remote)
42{
12c86877
BH
43 pthread_mutex_init(&d_lock,0);
44 d_resanswers=S.getPointer("recursing-answers");
45 d_resquestions=S.getPointer("recursing-questions");
46 d_udpanswers=S.getPointer("udp-answers");
2b78726c
PL
47
48 vector<string> addresses;
49 stringtok(addresses, remote, " ,\t");
40c9a111
RG
50 d_remote = ComboAddress(addresses[0], 53);
51
52 if((d_sock=socket(d_remote.sin4.sin_family, SOCK_DGRAM,0))<0) {
3f81d239 53 throw PDNSException(string("socket: ")+strerror(errno));
40c9a111
RG
54 }
55
c0cc6559 56 ComboAddress local;
40c9a111 57 if(d_remote.sin4.sin_family==AF_INET) {
c0cc6559 58 local = ComboAddress("0.0.0.0");
40c9a111
RG
59 }
60 else {
c0cc6559 61 local = ComboAddress("::");
40c9a111 62 }
c0cc6559 63
40c9a111 64 unsigned int n=0;
12c86877 65 for(;n<10;n++) {
d2116c15 66 local.sin4.sin_port = htons(10000+dns_random(50000));
12c86877 67
c0cc6559 68 if(::bind(d_sock, (struct sockaddr *)&local, local.getSocklen()) >= 0)
12c86877
BH
69 break;
70 }
71 if(n==10) {
3897b9e1 72 closesocket(d_sock);
12c86877 73 d_sock=-1;
3f81d239 74 throw PDNSException(string("binding dnsproxy socket: ")+strerror(errno));
12c86877
BH
75 }
76
40c9a111
RG
77 if(connect(d_sock, (sockaddr *)&d_remote, d_remote.getSocklen())<0) {
78 throw PDNSException("Unable to UDP connect to remote nameserver "+d_remote.toStringWithPort()+": "+stringerror());
79 }
12c86877 80
d2116c15 81 d_xor=dns_random(0xffff);
e6a9dde5 82 g_log<<Logger::Error<<"DNS Proxy launched, local port "<<ntohs(local.sin4.sin_port)<<", remote "<<d_remote.toStringWithPort()<<endl;
12c86877
BH
83}
84
85void DNSProxy::go()
86{
87 pthread_t tid;
88 pthread_create(&tid,0,&launchhelper,this);
89}
90
713010a2 91//! look up qname target with r->qtype, plonk it in the answer section of 'r' with name aname
0abea1ca 92bool DNSProxy::completePacket(DNSPacket *r, const DNSName& target,const DNSName& aname, const uint8_t scopeMask)
d59b894d 93{
c4e084f2 94 if(r->d_tcp) {
c4e084f2
PD
95 vector<DNSZoneRecord> ips;
96 int ret1 = 0, ret2 = 0;
97
98 if(r->qtype == QType::A || r->qtype == QType::ANY)
99 ret1 = stubDoResolve(target, QType::A, ips);
100 if(r->qtype == QType::AAAA || r->qtype == QType::ANY)
101 ret2 = stubDoResolve(target, QType::AAAA, ips);
102
103 if(ret1 != RCode::NoError || ret2 != RCode::NoError) {
a3928acd 104 g_log<<Logger::Error<<"Error resolving for "<<aname<<" ALIAS "<<target<<" over UDP, original query came in over TCP";
b9c5ccc5
PL
105 if (ret1 != RCode::NoError) {
106 g_log<<Logger::Error<<", A-record query returned "<<RCode::to_s(ret1);
107 }
108 if (ret2 != RCode::NoError) {
109 g_log<<Logger::Error<<", AAAA-record query returned "<<RCode::to_s(ret2);
110 }
111 g_log<<Logger::Error<<", returning SERVFAIL"<<endl;
112 r->clearRecords();
113 r->setRcode(RCode::ServFail);
114 } else {
115 for (auto &ip : ips)
116 {
117 ip.dr.d_name = aname;
118 r->addRecord(ip);
119 }
c4e084f2
PD
120 }
121
122 uint16_t len=htons(r->getString().length());
123 string buffer((const char*)&len, 2);
124 buffer.append(r->getString());
125 writen2WithTimeout(r->getSocket(), buffer.c_str(), buffer.length(), ::arg().asNum("tcp-idle-timeout"));
126
127 return true;
128 }
129
d59b894d 130 uint16_t id;
131 {
132 Lock l(&d_lock);
133 id=getID_locked();
134
135 ConntrackEntry ce;
136 ce.id = r->d.id;
137 ce.remote = r->d_remote;
138 ce.outsock = r->getSocket();
139 ce.created = time( NULL );
140 ce.qtype = r->qtype.getCode();
561434a6 141 ce.qname = target;
d59b894d 142 ce.anyLocal = r->d_anyLocal;
143 ce.complete = r;
144 ce.aname=aname;
0abea1ca 145 ce.anameScopeMask = scopeMask;
d59b894d 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
e6a9dde5 155 g_log<<Logger::Error<<"Unable to send a packet to our recursing backend: "<<stringerror()<<endl;
d59b894d 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) {
e6a9dde5 174 g_log<<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{
519f5484 187 setThreadName("pdns/dnsproxy");
12c86877
BH
188 try {
189 char buffer[1500];
a683e8bd 190 ssize_t len;
12c86877 191
65d8e171
KN
192 struct msghdr msgh;
193 struct iovec iov;
194 char cbuf[256];
40c9a111 195 ComboAddress fromaddr;
65d8e171 196
12c86877 197 for(;;) {
40c9a111
RG
198 socklen_t fromaddrSize = sizeof(fromaddr);
199 len=recvfrom(d_sock, buffer, sizeof(buffer),0, (struct sockaddr*) &fromaddr, &fromaddrSize); // answer from our backend
a683e8bd 200 if(len<(ssize_t)sizeof(dnsheader)) {
4957a608 201 if(len<0)
e6a9dde5 202 g_log<<Logger::Error<<"Error receiving packet from recursor backend: "<<stringerror()<<endl;
4957a608 203 else if(len==0)
e6a9dde5 204 g_log<<Logger::Error<<"Error receiving packet from recursor backend, EOF"<<endl;
4957a608 205 else
e6a9dde5 206 g_log<<Logger::Error<<"Short packet from recursor backend, "<<len<<" bytes"<<endl;
4957a608
BH
207
208 continue;
12c86877 209 }
40c9a111 210 if (fromaddr != d_remote) {
e6a9dde5 211 g_log<<Logger::Error<<"Got answer from unexpected host "<<fromaddr.toStringWithPort()<<" instead of our recursor backend "<<d_remote.toStringWithPort()<<endl;
40c9a111
RG
212 continue;
213 }
12c86877
BH
214 (*d_resanswers)++;
215 (*d_udpanswers)++;
88c1bc50 216 dnsheader d;
caa6eefa 217 memcpy(&d,buffer,sizeof(d));
12c86877 218 {
4957a608 219 Lock l(&d_lock);
bf549564 220#if BYTE_ORDER == BIG_ENDIAN
4957a608
BH
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];
a899e13a 223#endif
4957a608
BH
224 map_t::iterator i=d_conntrack.find(d.id^d_xor);
225 if(i==d_conntrack.end()) {
e6a9dde5 226 g_log<<Logger::Error<<"Discarding untracked packet from recursor backend with id "<<(d.id^d_xor)<<
abc1d928 227 ". Conntrack table size="<<d_conntrack.size()<<endl;
4957a608
BH
228 continue;
229 }
230 else if(i->second.created==0) {
e6a9dde5 231 g_log<<Logger::Error<<"Received packet from recursor backend with id "<<(d.id^d_xor)<<" which is a duplicate"<<endl;
4957a608
BH
232 continue;
233 }
d59b894d 234
4957a608
BH
235 d.id=i->second.id;
236 memcpy(buffer,&d,sizeof(d)); // commit spoofed id
237
27c0050c 238 DNSPacket p(false),q(false);
a683e8bd
RG
239 p.parse(buffer,(size_t)len);
240 q.parse(buffer,(size_t)len);
7de6b0d5
BH
241
242 if(p.qtype.getCode() != i->second.qtype || p.qdomain != i->second.qname) {
e6a9dde5 243 g_log<<Logger::Error<<"Discarding packet from recursor backend with id "<<(d.id^d_xor)<<
c348fec2 244 ", qname or qtype mismatch ("<<p.qtype.getCode()<<" v " <<i->second.qtype<<", "<<p.qdomain<<" v "<<i->second.qname<<")"<<endl;
7de6b0d5
BH
245 continue;
246 }
f5310162 247
65d8e171
KN
248 /* Set up iov and msgh structures. */
249 memset(&msgh, 0, sizeof(struct msghdr));
d808896e 250 string reply; // needs to be alive at time of sendmsg!
a3928acd
PL
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)) {
d808896e 257
a3928acd
PL
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))) {
259 DNSZoneRecord dzr;
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);
d808896e
PL
266 }
267 }
268 }
a3928acd
PL
269 i->second.complete->setRcode(mdp.d_header.rcode);
270 } else {
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);
d808896e 274 }
a3928acd
PL
275 reply=i->second.complete->getString();
276 iov.iov_base = (void*)reply.c_str();
277 iov.iov_len = reply.length();
278 delete i->second.complete;
279 i->second.complete=0;
65d8e171
KN
280 msgh.msg_iov = &iov;
281 msgh.msg_iovlen = 1;
282 msgh.msg_name = (struct sockaddr*)&i->second.remote;
283 msgh.msg_namelen = i->second.remote.getSocklen();
3eae691f 284 msgh.msg_control=NULL;
65d8e171
KN
285
286 if(i->second.anyLocal) {
fbe2a2e0 287 addCMsgSrcAddr(&msgh, cbuf, i->second.anyLocal.get_ptr(), 0);
f5310162 288 }
76511d0d 289 if(sendmsg(i->second.outsock, &msgh, 0) < 0)
e6a9dde5 290 g_log<<Logger::Warning<<"dnsproxy.cc: Error sending reply with sendmsg (socket="<<i->second.outsock<<"): "<<strerror(errno)<<endl;
d808896e 291
4957a608 292 i->second.created=0;
12c86877
BH
293 }
294 }
295 }
3f81d239 296 catch(PDNSException &ae) {
e6a9dde5 297 g_log<<Logger::Error<<"Fatal error in DNS proxy: "<<ae.reason<<endl;
12c86877 298 }
adc10f99 299 catch(std::exception &e) {
e6a9dde5 300 g_log<<Logger::Error<<"Communicator thread died because of STL error: "<<e.what()<<endl;
12c86877
BH
301 }
302 catch( ... )
303 {
e6a9dde5 304 g_log << Logger::Error << "Caught unknown exception." << endl;
12c86877 305 }
e6a9dde5 306 g_log<<Logger::Error<<"Exiting because DNS proxy failed"<<endl;
5bd2ea7b 307 _exit(1);
12c86877 308}
732d9faa
AT
309
310DNSProxy::~DNSProxy() {
a7b68ae7
RG
311 if (d_sock>-1) {
312 try {
313 closesocket(d_sock);
314 }
315 catch(const PDNSException& e) {
316 }
317 }
318
732d9faa
AT
319 d_sock=-1;
320}