]>
Commit | Line | Data |
---|---|---|
12c86877 BH |
1 | /* |
2 | PowerDNS Versatile Database Driven Nameserver | |
379ab445 | 3 | Copyright (C) 2004 - 2008 PowerDNS.COM BV |
12c86877 BH |
4 | |
5 | This program is free software; you can redistribute it and/or modify | |
a899e13a BH |
6 | it under the terms of the GNU General Public License version 2 as |
7 | published by the Free Software Foundation; | |
12c86877 | 8 | |
f782fe38 MH |
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 | ||
12c86877 BH |
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 | |
06bd9ccf | 20 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
12c86877 | 21 | */ |
379ab445 | 22 | #include "packetcache.hh" |
b636533b | 23 | #include "utility.hh" |
12c86877 | 24 | #include "dnsproxy.hh" |
5c409fa2 | 25 | #include "pdnsexception.hh" |
12c86877 BH |
26 | #include <sys/types.h> |
27 | #include <errno.h> | |
28 | #include "dns.hh" | |
29 | #include "logger.hh" | |
30 | #include "statbag.hh" | |
379ab445 | 31 | |
12c86877 BH |
32 | |
33 | extern StatBag S; | |
34 | extern PacketCache PC; | |
35 | ||
36 | DNSProxy::DNSProxy(const string &remote) | |
37 | { | |
12c86877 BH |
38 | pthread_mutex_init(&d_lock,0); |
39 | d_resanswers=S.getPointer("recursing-answers"); | |
40 | d_resquestions=S.getPointer("recursing-questions"); | |
41 | d_udpanswers=S.getPointer("udp-answers"); | |
c0cc6559 BH |
42 | ComboAddress remaddr(remote, 53); |
43 | ||
44 | if((d_sock=socket(remaddr.sin4.sin_family, SOCK_DGRAM,0))<0) | |
3f81d239 | 45 | throw PDNSException(string("socket: ")+strerror(errno)); |
12c86877 | 46 | |
c0cc6559 BH |
47 | ComboAddress local; |
48 | if(remaddr.sin4.sin_family==AF_INET) | |
49 | local = ComboAddress("0.0.0.0"); | |
50 | else | |
51 | local = ComboAddress("::"); | |
52 | ||
12c86877 BH |
53 | int n=0; |
54 | for(;n<10;n++) { | |
c0cc6559 | 55 | local.sin4.sin_port = htons(10000+( Utility::random()%50000)); |
12c86877 | 56 | |
c0cc6559 | 57 | if(::bind(d_sock, (struct sockaddr *)&local, local.getSocklen()) >= 0) |
12c86877 BH |
58 | break; |
59 | } | |
60 | if(n==10) { | |
61 | Utility::closesocket(d_sock); | |
62 | d_sock=-1; | |
3f81d239 | 63 | throw PDNSException(string("binding dnsproxy socket: ")+strerror(errno)); |
12c86877 BH |
64 | } |
65 | ||
c0cc6559 | 66 | if(connect(d_sock, (sockaddr *)&remaddr, remaddr.getSocklen())<0) |
3f81d239 | 67 | throw PDNSException("Unable to UDP connect to remote nameserver "+remaddr.toStringWithPort()+": "+stringerror()); |
12c86877 BH |
68 | |
69 | d_xor=Utility::random()&0xffff; | |
c0cc6559 | 70 | L<<Logger::Error<<"DNS Proxy launched, local port "<<ntohs(local.sin4.sin_port)<<", remote "<<remaddr.toStringWithPort()<<endl; |
12c86877 BH |
71 | } |
72 | ||
73 | void DNSProxy::go() | |
74 | { | |
75 | pthread_t tid; | |
76 | pthread_create(&tid,0,&launchhelper,this); | |
77 | } | |
78 | ||
79 | ||
80 | void DNSProxy::onlyFrom(const string &ips) | |
81 | { | |
82 | vector<string>parts; | |
83 | stringtok(parts,ips,", \t"); | |
84 | for(vector<string>::const_iterator i=parts.begin(); | |
85 | i!=parts.end();++i) | |
86 | d_ng.addMask(*i); | |
87 | ||
88 | } | |
89 | ||
b636533b BH |
90 | bool DNSProxy::recurseFor(DNSPacket* p) |
91 | { | |
d06799d4 | 92 | return d_ng.match((ComboAddress *)&p->d_remote); |
b636533b BH |
93 | } |
94 | ||
12c86877 BH |
95 | /** returns false if p->remote is not allowed to recurse via us */ |
96 | bool DNSProxy::sendPacket(DNSPacket *p) | |
97 | { | |
b636533b | 98 | if(!recurseFor(p)) |
12c86877 BH |
99 | return false; |
100 | ||
092f210a | 101 | uint16_t id; |
12c86877 BH |
102 | { |
103 | Lock l(&d_lock); | |
104 | id=getID_locked(); | |
105 | ||
106 | ConntrackEntry ce; | |
107 | ce.id = p->d.id; | |
d06799d4 | 108 | ce.remote = p->d_remote; |
12c86877 BH |
109 | ce.outsock = p->getSocket(); |
110 | ce.created = time( NULL ); | |
7de6b0d5 BH |
111 | ce.qtype = p->qtype.getCode(); |
112 | ce.qname = p->qdomain; | |
f5310162 | 113 | ce.anyLocal = p->d_anyLocal; |
12c86877 BH |
114 | d_conntrack[id]=ce; |
115 | } | |
bb0bbdc2 BH |
116 | p->d.id=id^d_xor; |
117 | p->commitD(); | |
3f45f34d BH |
118 | |
119 | const string& buffer = p->getString(); | |
120 | ||
121 | if(send(d_sock,buffer.c_str(), buffer.length() , 0)<0) { // zoom | |
12c86877 BH |
122 | L<<Logger::Error<<"Unable to send a packet to our recursing backend: "<<stringerror()<<endl; |
123 | } | |
124 | (*d_resquestions)++; | |
125 | return true; | |
126 | ||
127 | } | |
128 | /** This finds us an unused or stale ID. Does not actually clean the contents */ | |
129 | int DNSProxy::getID_locked() | |
130 | { | |
131 | map_t::iterator i; | |
132 | for(int n=0;;++n) { | |
133 | i=d_conntrack.find(n); | |
134 | if(i==d_conntrack.end()) { | |
135 | return n; | |
136 | } | |
137 | else if(i->second.created<time(0)-60) { | |
138 | if(i->second.created) | |
4957a608 | 139 | L<<Logger::Warning<<"Recursive query for remote "<< |
85db02c5 | 140 | i->second.remote.toStringWithPort()<<" with internal id "<<n<< |
4957a608 | 141 | " was not answered by backend within timeout, reusing id"<<endl; |
12c86877 BH |
142 | |
143 | return n; | |
144 | } | |
145 | } | |
146 | } | |
147 | ||
148 | void DNSProxy::mainloop(void) | |
149 | { | |
150 | try { | |
151 | char buffer[1500]; | |
152 | int len; | |
153 | ||
65d8e171 KN |
154 | struct msghdr msgh; |
155 | struct iovec iov; | |
156 | char cbuf[256]; | |
157 | ||
12c86877 BH |
158 | for(;;) { |
159 | len=recv(d_sock, buffer, sizeof(buffer),0); // answer from our backend | |
160 | if(len<12) { | |
4957a608 BH |
161 | if(len<0) |
162 | L<<Logger::Error<<"Error receiving packet from recursor backend: "<<stringerror()<<endl; | |
163 | else if(len==0) | |
164 | L<<Logger::Error<<"Error receiving packet from recursor backend, EOF"<<endl; | |
165 | else | |
166 | L<<Logger::Error<<"Short packet from recursor backend, "<<len<<" bytes"<<endl; | |
167 | ||
168 | continue; | |
12c86877 BH |
169 | } |
170 | (*d_resanswers)++; | |
171 | (*d_udpanswers)++; | |
88c1bc50 | 172 | dnsheader d; |
caa6eefa | 173 | memcpy(&d,buffer,sizeof(d)); |
12c86877 | 174 | { |
4957a608 | 175 | Lock l(&d_lock); |
a899e13a | 176 | #ifdef WORDS_BIGENDIAN |
4957a608 BH |
177 | // this is needed because spoof ID down below does not respect the native byteorder |
178 | d.id = ( 256 * (uint16_t)buffer[1] ) + (uint16_t)buffer[0]; | |
a899e13a | 179 | #endif |
4957a608 BH |
180 | map_t::iterator i=d_conntrack.find(d.id^d_xor); |
181 | if(i==d_conntrack.end()) { | |
182 | L<<Logger::Error<<"Discarding untracked packet from recursor backend with id "<<(d.id^d_xor)<< | |
abc1d928 | 183 | ". Conntrack table size="<<d_conntrack.size()<<endl; |
4957a608 BH |
184 | continue; |
185 | } | |
186 | else if(i->second.created==0) { | |
187 | L<<Logger::Error<<"Received packet from recursor backend with id "<<(d.id^d_xor)<<" which is a duplicate"<<endl; | |
188 | continue; | |
189 | } | |
190 | d.id=i->second.id; | |
191 | memcpy(buffer,&d,sizeof(d)); // commit spoofed id | |
192 | ||
4957a608 BH |
193 | DNSPacket p,q; |
194 | p.parse(buffer,len); | |
195 | q.parse(buffer,len); | |
7de6b0d5 BH |
196 | |
197 | if(p.qtype.getCode() != i->second.qtype || p.qdomain != i->second.qname) { | |
198 | L<<Logger::Error<<"Discarding packet from recursor backend with id "<<(d.id^d_xor)<< | |
199 | ", qname or qtype mismatch"<<endl; | |
200 | continue; | |
201 | } | |
f5310162 | 202 | |
65d8e171 KN |
203 | /* Set up iov and msgh structures. */ |
204 | memset(&msgh, 0, sizeof(struct msghdr)); | |
205 | iov.iov_base = buffer; | |
206 | iov.iov_len = len; | |
207 | msgh.msg_iov = &iov; | |
208 | msgh.msg_iovlen = 1; | |
209 | msgh.msg_name = (struct sockaddr*)&i->second.remote; | |
210 | msgh.msg_namelen = i->second.remote.getSocklen(); | |
211 | ||
212 | if(i->second.anyLocal) { | |
213 | addCMsgSrcAddr(&msgh, cbuf, i->second.anyLocal.get_ptr()); | |
f5310162 | 214 | } |
65d8e171 | 215 | sendmsg(i->second.outsock, &msgh, 0); |
4957a608 BH |
216 | |
217 | PC.insert(&q, &p); | |
218 | i->second.created=0; | |
12c86877 BH |
219 | } |
220 | } | |
221 | } | |
3f81d239 | 222 | catch(PDNSException &ae) { |
12c86877 BH |
223 | L<<Logger::Error<<"Fatal error in DNS proxy: "<<ae.reason<<endl; |
224 | } | |
adc10f99 | 225 | catch(std::exception &e) { |
12c86877 BH |
226 | L<<Logger::Error<<"Communicator thread died because of STL error: "<<e.what()<<endl; |
227 | } | |
228 | catch( ... ) | |
229 | { | |
230 | L << Logger::Error << "Caught unknown exception." << endl; | |
231 | } | |
232 | L<<Logger::Error<<"Exiting because DNS proxy failed"<<endl; | |
233 | exit(1); | |
234 | } |