]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsproxy.cc
auth: Convert the TCPNameserver to LockGuarded
[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
0ddde5fb
RG
25
26#include <sys/types.h>
27#include <thread>
28
379ab445 29#include "packetcache.hh"
b636533b 30#include "utility.hh"
12c86877 31#include "dnsproxy.hh"
5c409fa2 32#include "pdnsexception.hh"
12c86877
BH
33#include "dns.hh"
34#include "logger.hh"
35#include "statbag.hh"
d2116c15 36#include "dns_random.hh"
c4e084f2 37#include "stubresolver.hh"
c4e084f2 38#include "arguments.hh"
519f5484 39#include "threadname.hh"
12c86877
BH
40
41extern StatBag S;
12c86877
BH
42
43DNSProxy::DNSProxy(const string &remote)
44{
12c86877
BH
45 d_resanswers=S.getPointer("recursing-answers");
46 d_resquestions=S.getPointer("recursing-questions");
47 d_udpanswers=S.getPointer("udp-answers");
2b78726c
PL
48
49 vector<string> addresses;
50 stringtok(addresses, remote, " ,\t");
40c9a111
RG
51 d_remote = ComboAddress(addresses[0], 53);
52
53 if((d_sock=socket(d_remote.sin4.sin_family, SOCK_DGRAM,0))<0) {
a702a96c 54 throw PDNSException(string("socket: ")+stringerror());
40c9a111
RG
55 }
56
c0cc6559 57 ComboAddress local;
40c9a111 58 if(d_remote.sin4.sin_family==AF_INET) {
c0cc6559 59 local = ComboAddress("0.0.0.0");
40c9a111
RG
60 }
61 else {
c0cc6559 62 local = ComboAddress("::");
40c9a111 63 }
c0cc6559 64
40c9a111 65 unsigned int n=0;
12c86877 66 for(;n<10;n++) {
d2116c15 67 local.sin4.sin_port = htons(10000+dns_random(50000));
12c86877 68
c0cc6559 69 if(::bind(d_sock, (struct sockaddr *)&local, local.getSocklen()) >= 0)
12c86877
BH
70 break;
71 }
72 if(n==10) {
3897b9e1 73 closesocket(d_sock);
12c86877 74 d_sock=-1;
a702a96c 75 throw PDNSException(string("binding dnsproxy socket: ")+stringerror());
12c86877
BH
76 }
77
40c9a111
RG
78 if(connect(d_sock, (sockaddr *)&d_remote, d_remote.getSocklen())<0) {
79 throw PDNSException("Unable to UDP connect to remote nameserver "+d_remote.toStringWithPort()+": "+stringerror());
80 }
12c86877 81
a410b176 82 d_xor=dns_random_uint16();
e6a9dde5 83 g_log<<Logger::Error<<"DNS Proxy launched, local port "<<ntohs(local.sin4.sin_port)<<", remote "<<d_remote.toStringWithPort()<<endl;
12c86877
BH
84}
85
86void DNSProxy::go()
87{
969e4459 88 std::thread t([this](){mainloop();});
0ddde5fb 89 t.detach();
12c86877
BH
90}
91
713010a2 92//! look up qname target with r->qtype, plonk it in the answer section of 'r' with name aname
c2826d2e 93bool DNSProxy::completePacket(std::unique_ptr<DNSPacket>& r, const DNSName& target,const DNSName& aname, const uint8_t scopeMask)
d59b894d 94{
c4e084f2 95 if(r->d_tcp) {
c4e084f2
PD
96 vector<DNSZoneRecord> ips;
97 int ret1 = 0, ret2 = 0;
98
99 if(r->qtype == QType::A || r->qtype == QType::ANY)
100 ret1 = stubDoResolve(target, QType::A, ips);
101 if(r->qtype == QType::AAAA || r->qtype == QType::ANY)
102 ret2 = stubDoResolve(target, QType::AAAA, ips);
103
104 if(ret1 != RCode::NoError || ret2 != RCode::NoError) {
a3928acd 105 g_log<<Logger::Error<<"Error resolving for "<<aname<<" ALIAS "<<target<<" over UDP, original query came in over TCP";
b9c5ccc5
PL
106 if (ret1 != RCode::NoError) {
107 g_log<<Logger::Error<<", A-record query returned "<<RCode::to_s(ret1);
108 }
109 if (ret2 != RCode::NoError) {
110 g_log<<Logger::Error<<", AAAA-record query returned "<<RCode::to_s(ret2);
111 }
112 g_log<<Logger::Error<<", returning SERVFAIL"<<endl;
113 r->clearRecords();
114 r->setRcode(RCode::ServFail);
115 } else {
116 for (auto &ip : ips)
117 {
118 ip.dr.d_name = aname;
9bbcf03a 119 r->addRecord(std::move(ip));
b9c5ccc5 120 }
c4e084f2
PD
121 }
122
123 uint16_t len=htons(r->getString().length());
124 string buffer((const char*)&len, 2);
125 buffer.append(r->getString());
50111728 126 writen2WithTimeout(r->getSocket(), buffer.c_str(), buffer.length(), timeval{::arg().asNum("tcp-idle-timeout"),0});
c4e084f2
PD
127
128 return true;
129 }
130
d59b894d 131 uint16_t id;
c2826d2e 132 uint16_t qtype = r->qtype.getCode();
d59b894d 133 {
0ddde5fb 134 std::lock_guard<std::mutex> l(d_lock);
d59b894d 135 id=getID_locked();
136
137 ConntrackEntry ce;
138 ce.id = r->d.id;
139 ce.remote = r->d_remote;
140 ce.outsock = r->getSocket();
4646277d 141 ce.created = time( nullptr );
d59b894d 142 ce.qtype = r->qtype.getCode();
561434a6 143 ce.qname = target;
d59b894d 144 ce.anyLocal = r->d_anyLocal;
c2826d2e 145 ce.complete = std::move(r);
d59b894d 146 ce.aname=aname;
0abea1ca 147 ce.anameScopeMask = scopeMask;
c2826d2e 148 d_conntrack[id]=std::move(ce);
d59b894d 149 }
150
151 vector<uint8_t> packet;
c2826d2e 152 DNSPacketWriter pw(packet, target, qtype);
d59b894d 153 pw.getHeader()->rd=true;
154 pw.getHeader()->id=id ^ d_xor;
155
156 if(send(d_sock,&packet[0], packet.size() , 0)<0) { // zoom
e6a9dde5 157 g_log<<Logger::Error<<"Unable to send a packet to our recursing backend: "<<stringerror()<<endl;
d59b894d 158 }
159
160 return true;
161
162}
163
164
12c86877
BH
165/** This finds us an unused or stale ID. Does not actually clean the contents */
166int DNSProxy::getID_locked()
167{
168 map_t::iterator i;
169 for(int n=0;;++n) {
170 i=d_conntrack.find(n);
171 if(i==d_conntrack.end()) {
172 return n;
173 }
4646277d 174 else if(i->second.created<time(nullptr)-60) {
d59b894d 175 if(i->second.created) {
e6a9dde5 176 g_log<<Logger::Warning<<"Recursive query for remote "<<
85db02c5 177 i->second.remote.toStringWithPort()<<" with internal id "<<n<<
4957a608 178 " was not answered by backend within timeout, reusing id"<<endl;
c2826d2e 179 i->second.complete.reset();
bb6e54fe 180 S.inc("recursion-unanswered");
d59b894d 181 }
12c86877
BH
182 return n;
183 }
184 }
185}
186
e52fb6a4 187void DNSProxy::mainloop()
12c86877 188{
519f5484 189 setThreadName("pdns/dnsproxy");
12c86877
BH
190 try {
191 char buffer[1500];
a683e8bd 192 ssize_t len;
12c86877 193
65d8e171
KN
194 struct msghdr msgh;
195 struct iovec iov;
7bec330a 196 cmsgbuf_aligned cbuf;
40c9a111 197 ComboAddress fromaddr;
65d8e171 198
12c86877 199 for(;;) {
40c9a111
RG
200 socklen_t fromaddrSize = sizeof(fromaddr);
201 len=recvfrom(d_sock, buffer, sizeof(buffer),0, (struct sockaddr*) &fromaddr, &fromaddrSize); // answer from our backend
a683e8bd 202 if(len<(ssize_t)sizeof(dnsheader)) {
4957a608 203 if(len<0)
e6a9dde5 204 g_log<<Logger::Error<<"Error receiving packet from recursor backend: "<<stringerror()<<endl;
4957a608 205 else if(len==0)
e6a9dde5 206 g_log<<Logger::Error<<"Error receiving packet from recursor backend, EOF"<<endl;
4957a608 207 else
e6a9dde5 208 g_log<<Logger::Error<<"Short packet from recursor backend, "<<len<<" bytes"<<endl;
4957a608
BH
209
210 continue;
12c86877 211 }
40c9a111 212 if (fromaddr != d_remote) {
e6a9dde5 213 g_log<<Logger::Error<<"Got answer from unexpected host "<<fromaddr.toStringWithPort()<<" instead of our recursor backend "<<d_remote.toStringWithPort()<<endl;
40c9a111
RG
214 continue;
215 }
12c86877
BH
216 (*d_resanswers)++;
217 (*d_udpanswers)++;
88c1bc50 218 dnsheader d;
caa6eefa 219 memcpy(&d,buffer,sizeof(d));
12c86877 220 {
0ddde5fb 221 std::lock_guard<std::mutex> l(d_lock);
bf549564 222#if BYTE_ORDER == BIG_ENDIAN
4957a608
BH
223 // this is needed because spoof ID down below does not respect the native byteorder
224 d.id = ( 256 * (uint16_t)buffer[1] ) + (uint16_t)buffer[0];
a899e13a 225#endif
4957a608
BH
226 map_t::iterator i=d_conntrack.find(d.id^d_xor);
227 if(i==d_conntrack.end()) {
e6a9dde5 228 g_log<<Logger::Error<<"Discarding untracked packet from recursor backend with id "<<(d.id^d_xor)<<
abc1d928 229 ". Conntrack table size="<<d_conntrack.size()<<endl;
4957a608
BH
230 continue;
231 }
232 else if(i->second.created==0) {
e6a9dde5 233 g_log<<Logger::Error<<"Received packet from recursor backend with id "<<(d.id^d_xor)<<" which is a duplicate"<<endl;
4957a608
BH
234 continue;
235 }
d59b894d 236
4957a608
BH
237 d.id=i->second.id;
238 memcpy(buffer,&d,sizeof(d)); // commit spoofed id
239
27c0050c 240 DNSPacket p(false),q(false);
a683e8bd
RG
241 p.parse(buffer,(size_t)len);
242 q.parse(buffer,(size_t)len);
7de6b0d5
BH
243
244 if(p.qtype.getCode() != i->second.qtype || p.qdomain != i->second.qname) {
e6a9dde5 245 g_log<<Logger::Error<<"Discarding packet from recursor backend with id "<<(d.id^d_xor)<<
c348fec2 246 ", qname or qtype mismatch ("<<p.qtype.getCode()<<" v " <<i->second.qtype<<", "<<p.qdomain<<" v "<<i->second.qname<<")"<<endl;
7de6b0d5
BH
247 continue;
248 }
f5310162 249
65d8e171
KN
250 /* Set up iov and msgh structures. */
251 memset(&msgh, 0, sizeof(struct msghdr));
d808896e 252 string reply; // needs to be alive at time of sendmsg!
a3928acd
PL
253 MOADNSParser mdp(false, p.getString());
254 // cerr<<"Got completion, "<<mdp.d_answers.size()<<" answers, rcode: "<<mdp.d_header.rcode<<endl;
255 if (mdp.d_header.rcode == RCode::NoError) {
f80ebc05 256 for(const auto & answer : mdp.d_answers) {
a3928acd 257 // cerr<<"comp: "<<(int)j->first.d_place-1<<" "<<j->first.d_label<<" " << DNSRecordContent::NumberToType(j->first.d_type)<<" "<<j->first.d_content->getZoneRepresentation()<<endl;
f80ebc05 258 if(answer.first.d_place == DNSResourceRecord::ANSWER || (answer.first.d_place == DNSResourceRecord::AUTHORITY && answer.first.d_type == QType::SOA)) {
d808896e 259
f80ebc05 260 if(answer.first.d_type == i->second.qtype || (i->second.qtype == QType::ANY && (answer.first.d_type == QType::A || answer.first.d_type == QType::AAAA))) {
a3928acd
PL
261 DNSZoneRecord dzr;
262 dzr.dr.d_name=i->second.aname;
f80ebc05
O
263 dzr.dr.d_type = answer.first.d_type;
264 dzr.dr.d_ttl=answer.first.d_ttl;
265 dzr.dr.d_place= answer.first.d_place;
266 dzr.dr.d_content=answer.first.d_content;
9bbcf03a 267 i->second.complete->addRecord(std::move(dzr));
d808896e
PL
268 }
269 }
270 }
a3928acd
PL
271 i->second.complete->setRcode(mdp.d_header.rcode);
272 } else {
d5fcd583 273 g_log<<Logger::Error<<"Error resolving for "<<i->second.aname<<" ALIAS "<<i->second.qname<<" over UDP, "<<QType(i->second.qtype).toString()<<"-record query returned "<<RCode::to_s(mdp.d_header.rcode)<<", returning SERVFAIL"<<endl;
a3928acd
PL
274 i->second.complete->clearRecords();
275 i->second.complete->setRcode(RCode::ServFail);
d808896e 276 }
a3928acd
PL
277 reply=i->second.complete->getString();
278 iov.iov_base = (void*)reply.c_str();
279 iov.iov_len = reply.length();
c2826d2e 280 i->second.complete.reset();
65d8e171
KN
281 msgh.msg_iov = &iov;
282 msgh.msg_iovlen = 1;
283 msgh.msg_name = (struct sockaddr*)&i->second.remote;
284 msgh.msg_namelen = i->second.remote.getSocklen();
4646277d 285 msgh.msg_control=nullptr;
65d8e171
KN
286
287 if(i->second.anyLocal) {
7bec330a 288 addCMsgSrcAddr(&msgh, &cbuf, i->second.anyLocal.get_ptr(), 0);
f5310162 289 }
a702a96c
OM
290 if(sendmsg(i->second.outsock, &msgh, 0) < 0) {
291 int err = errno;
292 g_log<<Logger::Warning<<"dnsproxy.cc: Error sending reply with sendmsg (socket="<<i->second.outsock<<"): "<<stringerror(err)<<endl;
293 }
4957a608 294 i->second.created=0;
12c86877
BH
295 }
296 }
297 }
3f81d239 298 catch(PDNSException &ae) {
e6a9dde5 299 g_log<<Logger::Error<<"Fatal error in DNS proxy: "<<ae.reason<<endl;
12c86877 300 }
adc10f99 301 catch(std::exception &e) {
e6a9dde5 302 g_log<<Logger::Error<<"Communicator thread died because of STL error: "<<e.what()<<endl;
12c86877
BH
303 }
304 catch( ... )
305 {
e6a9dde5 306 g_log << Logger::Error << "Caught unknown exception." << endl;
12c86877 307 }
e6a9dde5 308 g_log<<Logger::Error<<"Exiting because DNS proxy failed"<<endl;
5bd2ea7b 309 _exit(1);
12c86877 310}
732d9faa
AT
311
312DNSProxy::~DNSProxy() {
a7b68ae7
RG
313 if (d_sock>-1) {
314 try {
315 closesocket(d_sock);
316 }
317 catch(const PDNSException& e) {
318 }
319 }
320
732d9faa
AT
321 d_sock=-1;
322}