]>
Commit | Line | Data |
---|---|---|
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 | |
36 | extern StatBag S; | |
37 | extern PacketCache PC; | |
38 | ||
39 | DNSProxy::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 | ||
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 | { | |
68b011bd | 85 | d_ng.toMasks(ips); |
12c86877 BH |
86 | } |
87 | ||
b636533b BH |
88 | bool 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 */ |
94 | bool 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 | 129 | bool 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 */ |
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) { | |
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 | ||
185 | void 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 | |
306 | DNSProxy::~DNSProxy() { | |
307 | if (d_sock>-1) closesocket(d_sock); | |
308 | d_sock=-1; | |
309 | } |