Commit | Line | Data |
---|---|---|
dc0d77d7 CE |
1 | #include <unistd.h> |
2 | #include "env.h" | |
3 | #include "exit.h" | |
4 | #include "scan.h" | |
5 | #include "strerr.h" | |
6 | #include "error.h" | |
7 | #include "ip4.h" | |
8 | #include "uint16.h" | |
9 | #include "uint64.h" | |
10 | #include "socket.h" | |
11 | #include "dns.h" | |
12 | #include "taia.h" | |
13 | #include "byte.h" | |
14 | #include "roots.h" | |
15 | #include "fmt.h" | |
16 | #include "iopause.h" | |
17 | #include "query.h" | |
18 | #include "alloc.h" | |
19 | #include "response.h" | |
20 | #include "cache.h" | |
21 | #include "ndelay.h" | |
22 | #include "log.h" | |
23 | #include "okclient.h" | |
24 | #include "droproot.h" | |
25 | ||
26 | static int packetquery(char *buf,unsigned int len,char **q,char qtype[2],char qclass[2],char id[2]) | |
27 | { | |
28 | unsigned int pos; | |
29 | char header[12]; | |
30 | ||
31 | errno = error_proto; | |
32 | pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return 0; | |
33 | if (header[2] & 128) return 0; /* must not respond to responses */ | |
34 | if (!(header[2] & 1)) return 0; /* do not respond to non-recursive queries */ | |
35 | if (header[2] & 120) return 0; | |
36 | if (header[2] & 2) return 0; | |
37 | if (byte_diff(header + 4,2,"\0\1")) return 0; | |
38 | ||
39 | pos = dns_packet_getname(buf,len,pos,q); if (!pos) return 0; | |
40 | pos = dns_packet_copy(buf,len,pos,qtype,2); if (!pos) return 0; | |
41 | pos = dns_packet_copy(buf,len,pos,qclass,2); if (!pos) return 0; | |
42 | if (byte_diff(qclass,2,DNS_C_IN) && byte_diff(qclass,2,DNS_C_ANY)) return 0; | |
43 | ||
44 | byte_copy(id,2,header); | |
45 | return 1; | |
46 | } | |
47 | ||
48 | ||
49 | static char myipoutgoing[4]; | |
50 | static char myipincoming[4]; | |
51 | static char buf[1024]; | |
52 | uint64 numqueries = 0; | |
53 | ||
54 | ||
55 | static int udp53; | |
56 | ||
57 | #define MAXUDP 200 | |
58 | static struct udpclient { | |
59 | struct query q; | |
60 | struct taia start; | |
61 | uint64 active; /* query number, if active; otherwise 0 */ | |
62 | iopause_fd *io; | |
63 | char ip[4]; | |
64 | uint16 port; | |
65 | char id[2]; | |
66 | } u[MAXUDP]; | |
67 | int uactive = 0; | |
68 | ||
69 | void u_drop(int j) | |
70 | { | |
71 | if (!u[j].active) return; | |
72 | log_querydrop(&u[j].active); | |
73 | u[j].active = 0; --uactive; | |
74 | } | |
75 | ||
76 | void u_respond(int j) | |
77 | { | |
78 | if (!u[j].active) return; | |
79 | response_id(u[j].id); | |
80 | if (response_len > 512) response_tc(); | |
81 | socket_send4(udp53,response,response_len,u[j].ip,u[j].port); | |
82 | log_querydone(&u[j].active,response_len); | |
83 | u[j].active = 0; --uactive; | |
84 | } | |
85 | ||
86 | void u_new(void) | |
87 | { | |
88 | int j; | |
89 | int i; | |
90 | struct udpclient *x; | |
91 | int len; | |
92 | static char *q = 0; | |
93 | char qtype[2]; | |
94 | char qclass[2]; | |
95 | ||
96 | for (j = 0;j < MAXUDP;++j) | |
97 | if (!u[j].active) | |
98 | break; | |
99 | ||
100 | if (j >= MAXUDP) { | |
101 | j = 0; | |
102 | for (i = 1;i < MAXUDP;++i) | |
103 | if (taia_less(&u[i].start,&u[j].start)) | |
104 | j = i; | |
105 | errno = error_timeout; | |
106 | u_drop(j); | |
107 | } | |
108 | ||
109 | x = u + j; | |
110 | taia_now(&x->start); | |
111 | ||
112 | len = socket_recv4(udp53,buf,sizeof buf,x->ip,&x->port); | |
113 | if (len == -1) return; | |
114 | if (len >= sizeof buf) return; | |
115 | if (x->port < 1024) if (x->port != 53) return; | |
116 | if (!okclient(x->ip)) return; | |
117 | ||
118 | if (!packetquery(buf,len,&q,qtype,qclass,x->id)) return; | |
119 | ||
120 | x->active = ++numqueries; ++uactive; | |
121 | log_query(&x->active,x->ip,x->port,x->id,q,qtype); | |
122 | switch(query_start(&x->q,q,qtype,qclass,myipoutgoing)) { | |
123 | case -1: | |
124 | u_drop(j); | |
125 | return; | |
126 | case 1: | |
127 | u_respond(j); | |
128 | } | |
129 | } | |
130 | ||
131 | ||
132 | static int tcp53; | |
133 | ||
134 | #define MAXTCP 20 | |
135 | struct tcpclient { | |
136 | struct query q; | |
137 | struct taia start; | |
138 | struct taia timeout; | |
139 | uint64 active; /* query number or 1, if active; otherwise 0 */ | |
140 | iopause_fd *io; | |
141 | char ip[4]; /* send response to this address */ | |
142 | uint16 port; /* send response to this port */ | |
143 | char id[2]; | |
144 | int tcp; /* open TCP socket, if active */ | |
145 | int state; | |
146 | char *buf; /* 0, or dynamically allocated of length len */ | |
147 | unsigned int len; | |
148 | unsigned int pos; | |
149 | } t[MAXTCP]; | |
150 | int tactive = 0; | |
151 | ||
152 | /* | |
153 | state 1: buf 0; normal state at beginning of TCP connection | |
154 | state 2: buf 0; have read 1 byte of query packet length into len | |
155 | state 3: buf allocated; have read pos bytes of buf | |
156 | state 0: buf 0; handling query in q | |
157 | state -1: buf allocated; have written pos bytes | |
158 | */ | |
159 | ||
160 | void t_free(int j) | |
161 | { | |
162 | if (!t[j].buf) return; | |
163 | alloc_free(t[j].buf); | |
164 | t[j].buf = 0; | |
165 | } | |
166 | ||
167 | void t_timeout(int j) | |
168 | { | |
169 | struct taia now; | |
170 | if (!t[j].active) return; | |
171 | taia_now(&now); | |
172 | taia_uint(&t[j].timeout,10); | |
173 | taia_add(&t[j].timeout,&t[j].timeout,&now); | |
174 | } | |
175 | ||
176 | void t_close(int j) | |
177 | { | |
178 | if (!t[j].active) return; | |
179 | t_free(j); | |
180 | log_tcpclose(t[j].ip,t[j].port); | |
181 | close(t[j].tcp); | |
182 | t[j].active = 0; --tactive; | |
183 | } | |
184 | ||
185 | void t_drop(int j) | |
186 | { | |
187 | log_querydrop(&t[j].active); | |
188 | errno = error_pipe; | |
189 | t_close(j); | |
190 | } | |
191 | ||
192 | void t_respond(int j) | |
193 | { | |
194 | if (!t[j].active) return; | |
195 | log_querydone(&t[j].active,response_len); | |
196 | response_id(t[j].id); | |
197 | t[j].len = response_len + 2; | |
198 | t_free(j); | |
199 | t[j].buf = alloc(response_len + 2); | |
200 | if (!t[j].buf) { t_close(j); return; } | |
201 | uint16_pack_big(t[j].buf,response_len); | |
202 | byte_copy(t[j].buf + 2,response_len,response); | |
203 | t[j].pos = 0; | |
204 | t[j].state = -1; | |
205 | } | |
206 | ||
207 | void t_rw(int j) | |
208 | { | |
209 | struct tcpclient *x; | |
210 | char ch; | |
211 | static char *q = 0; | |
212 | char qtype[2]; | |
213 | char qclass[2]; | |
214 | int r; | |
215 | ||
216 | x = t + j; | |
217 | if (x->state == -1) { | |
218 | r = write(x->tcp,x->buf + x->pos,x->len - x->pos); | |
219 | if (r <= 0) { t_close(j); return; } | |
220 | x->pos += r; | |
221 | if (x->pos == x->len) { | |
222 | t_free(j); | |
223 | x->state = 1; /* could drop connection immediately */ | |
224 | } | |
225 | return; | |
226 | } | |
227 | ||
228 | r = read(x->tcp,&ch,1); | |
229 | if (r == 0) { errno = error_pipe; t_close(j); return; } | |
230 | if (r < 0) { t_close(j); return; } | |
231 | ||
232 | if (x->state == 1) { | |
233 | x->len = (unsigned char) ch; | |
234 | x->len <<= 8; | |
235 | x->state = 2; | |
236 | return; | |
237 | } | |
238 | if (x->state == 2) { | |
239 | x->len += (unsigned char) ch; | |
240 | if (!x->len) { errno = error_proto; t_close(j); return; } | |
241 | x->buf = alloc(x->len); | |
242 | if (!x->buf) { t_close(j); return; } | |
243 | x->pos = 0; | |
244 | x->state = 3; | |
245 | return; | |
246 | } | |
247 | ||
248 | if (x->state != 3) return; /* impossible */ | |
249 | ||
250 | x->buf[x->pos++] = ch; | |
251 | if (x->pos < x->len) return; | |
252 | ||
253 | if (!packetquery(x->buf,x->len,&q,qtype,qclass,x->id)) { t_close(j); return; } | |
254 | ||
255 | x->active = ++numqueries; | |
256 | log_query(&x->active,x->ip,x->port,x->id,q,qtype); | |
257 | switch(query_start(&x->q,q,qtype,qclass,myipoutgoing)) { | |
258 | case -1: | |
259 | t_drop(j); | |
260 | return; | |
261 | case 1: | |
262 | t_respond(j); | |
263 | return; | |
264 | } | |
265 | t_free(j); | |
266 | x->state = 0; | |
267 | } | |
268 | ||
269 | void t_new(void) | |
270 | { | |
271 | int i; | |
272 | int j; | |
273 | struct tcpclient *x; | |
274 | ||
275 | for (j = 0;j < MAXTCP;++j) | |
276 | if (!t[j].active) | |
277 | break; | |
278 | ||
279 | if (j >= MAXTCP) { | |
280 | j = 0; | |
281 | for (i = 1;i < MAXTCP;++i) | |
282 | if (taia_less(&t[i].start,&t[j].start)) | |
283 | j = i; | |
284 | errno = error_timeout; | |
285 | if (t[j].state == 0) | |
286 | t_drop(j); | |
287 | else | |
288 | t_close(j); | |
289 | } | |
290 | ||
291 | x = t + j; | |
292 | taia_now(&x->start); | |
293 | ||
294 | x->tcp = socket_accept4(tcp53,x->ip,&x->port); | |
295 | if (x->tcp == -1) return; | |
296 | if (x->port < 1024) if (x->port != 53) { close(x->tcp); return; } | |
297 | if (!okclient(x->ip)) { close(x->tcp); return; } | |
298 | if (ndelay_on(x->tcp) == -1) { close(x->tcp); return; } /* Linux bug */ | |
299 | ||
300 | x->active = 1; ++tactive; | |
301 | x->state = 1; | |
302 | t_timeout(j); | |
303 | ||
304 | log_tcpopen(x->ip,x->port); | |
305 | } | |
306 | ||
307 | ||
308 | iopause_fd io[3 + MAXUDP + MAXTCP]; | |
309 | iopause_fd *udp53io; | |
310 | iopause_fd *tcp53io; | |
311 | ||
312 | static void doit(void) | |
313 | { | |
314 | int j; | |
315 | struct taia deadline; | |
316 | struct taia stamp; | |
317 | int iolen; | |
318 | int r; | |
319 | ||
320 | for (;;) { | |
321 | taia_now(&stamp); | |
322 | taia_uint(&deadline,120); | |
323 | taia_add(&deadline,&deadline,&stamp); | |
324 | ||
325 | iolen = 0; | |
326 | ||
327 | udp53io = io + iolen++; | |
328 | udp53io->fd = udp53; | |
329 | udp53io->events = IOPAUSE_READ; | |
330 | ||
331 | tcp53io = io + iolen++; | |
332 | tcp53io->fd = tcp53; | |
333 | tcp53io->events = IOPAUSE_READ; | |
334 | ||
335 | for (j = 0;j < MAXUDP;++j) | |
336 | if (u[j].active) { | |
337 | u[j].io = io + iolen++; | |
338 | query_io(&u[j].q,u[j].io,&deadline); | |
339 | } | |
340 | for (j = 0;j < MAXTCP;++j) | |
341 | if (t[j].active) { | |
342 | t[j].io = io + iolen++; | |
343 | if (t[j].state == 0) | |
344 | query_io(&t[j].q,t[j].io,&deadline); | |
345 | else { | |
346 | if (taia_less(&t[j].timeout,&deadline)) deadline = t[j].timeout; | |
347 | t[j].io->fd = t[j].tcp; | |
348 | t[j].io->events = (t[j].state > 0) ? IOPAUSE_READ : IOPAUSE_WRITE; | |
349 | } | |
350 | } | |
351 | ||
352 | iopause(io,iolen,&deadline,&stamp); | |
353 | ||
354 | for (j = 0;j < MAXUDP;++j) | |
355 | if (u[j].active) { | |
356 | r = query_get(&u[j].q,u[j].io,&stamp); | |
357 | if (r == -1) u_drop(j); | |
358 | if (r == 1) u_respond(j); | |
359 | } | |
360 | ||
361 | for (j = 0;j < MAXTCP;++j) | |
362 | if (t[j].active) { | |
363 | if (t[j].io->revents) | |
364 | t_timeout(j); | |
365 | if (t[j].state == 0) { | |
366 | r = query_get(&t[j].q,t[j].io,&stamp); | |
367 | if (r == -1) t_drop(j); | |
368 | if (r == 1) t_respond(j); | |
369 | } | |
370 | else | |
371 | if (t[j].io->revents || taia_less(&t[j].timeout,&stamp)) | |
372 | t_rw(j); | |
373 | } | |
374 | ||
375 | if (udp53io) | |
376 | if (udp53io->revents) | |
377 | u_new(); | |
378 | ||
379 | if (tcp53io) | |
380 | if (tcp53io->revents) | |
381 | t_new(); | |
382 | } | |
383 | } | |
384 | ||
385 | #define FATAL "dnscache: fatal: " | |
386 | ||
387 | char seed[128]; | |
388 | ||
389 | int main() | |
390 | { | |
391 | char *x; | |
392 | unsigned long cachesize; | |
393 | ||
394 | x = env_get("IP"); | |
395 | if (!x) | |
396 | strerr_die2x(111,FATAL,"$IP not set"); | |
397 | if (!ip4_scan(x,myipincoming)) | |
398 | strerr_die3x(111,FATAL,"unable to parse IP address ",x); | |
399 | ||
400 | udp53 = socket_udp(); | |
401 | if (udp53 == -1) | |
402 | strerr_die2sys(111,FATAL,"unable to create UDP socket: "); | |
403 | if (socket_bind4_reuse(udp53,myipincoming,53) == -1) | |
404 | strerr_die2sys(111,FATAL,"unable to bind UDP socket: "); | |
405 | ||
406 | tcp53 = socket_tcp(); | |
407 | if (tcp53 == -1) | |
408 | strerr_die2sys(111,FATAL,"unable to create TCP socket: "); | |
409 | if (socket_bind4_reuse(tcp53,myipincoming,53) == -1) | |
410 | strerr_die2sys(111,FATAL,"unable to bind TCP socket: "); | |
411 | ||
412 | droproot(FATAL); | |
413 | ||
414 | socket_tryreservein(udp53,131072); | |
415 | ||
416 | byte_zero(seed,sizeof seed); | |
417 | read(0,seed,sizeof seed); | |
418 | dns_random_init(seed); | |
419 | close(0); | |
420 | ||
421 | x = env_get("IPSEND"); | |
422 | if (!x) | |
423 | strerr_die2x(111,FATAL,"$IPSEND not set"); | |
424 | if (!ip4_scan(x,myipoutgoing)) | |
425 | strerr_die3x(111,FATAL,"unable to parse IP address ",x); | |
426 | ||
427 | x = env_get("CACHESIZE"); | |
428 | if (!x) | |
429 | strerr_die2x(111,FATAL,"$CACHESIZE not set"); | |
430 | scan_ulong(x,&cachesize); | |
431 | if (!cache_init(cachesize)) | |
432 | strerr_die3x(111,FATAL,"not enough memory for cache of size ",x); | |
433 | ||
434 | if (env_get("HIDETTL")) | |
435 | response_hidettl(); | |
436 | if (env_get("FORWARDONLY")) | |
437 | query_forwardonly(); | |
438 | ||
439 | if (!roots_init()) | |
440 | strerr_die2sys(111,FATAL,"unable to read servers: "); | |
441 | ||
442 | if (socket_listen(tcp53,20) == -1) | |
443 | strerr_die2sys(111,FATAL,"unable to listen on TCP socket: "); | |
444 | ||
445 | log_startup(); | |
446 | doit(); | |
447 | } |