VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/slirp/dnsproxy/dnsproxy.c@ 19700

Last change on this file since 19700 was 19700, checked in by vboxsync, 16 years ago

small fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.3 KB
Line 
1/* $Id: dnsproxy.c 19700 2009-05-14 15:19:48Z vboxsync $ */
2/*
3 * Copyright (c) 2003,2004,2005 Armin Wolfermann
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24#ifndef VBOX
25#include <config.h>
26#include <errno.h>
27#include <pwd.h>
28#include <signal.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <unistd.h>
33#else
34#include "slirp.h"
35#endif
36
37#ifndef VBOX
38#define GLOBALS 1
39#include "dnsproxy.h"
40
41#define RD(x) (*(x + 2) & 0x01)
42#define MAX_BUFSPACE 512
43
44static unsigned short queryid = 0;
45#define QUERYID queryid++
46
47static struct sockaddr_in authoritative_addr;
48static struct sockaddr_in recursive_addr;
49static int sock_query;
50static int sock_answer;
51static int dnsproxy_sig;
52
53extern int event_gotsig;
54extern int (*event_sigcb)(void);
55
56#ifdef DEBUG
57char *malloc_options = "AGZ";
58#endif
59
60/* signal_handler -- Native signal handler. Set external flag for libevent
61 * and store type of signal. Real signal handling is done in signal_event.
62 */
63
64RETSIGTYPE
65signal_handler(int sig)
66{
67 event_gotsig = 1;
68 dnsproxy_sig = sig;
69}
70
71/* signal_event -- Called by libevent to deliver a signal.
72 */
73
74int
75signal_event(void)
76{
77 fatal("exiting on signal %d", dnsproxy_sig);
78 return 0;
79}
80
81#else
82
83# define RD(x) (*(x + 2) & 0x01)
84# define MAX_BUFSPACE 512
85
86# define QUERYID queryid++
87
88#endif
89/* timeout -- Called by the event loop when a query times out. Removes the
90 * query from the queue.
91 */
92/* ARGSUSED */
93#ifndef VBOX
94static void
95timeout(int fd, short event, void *arg)
96{
97 /* here we should check if we reached the end of the DNS server list */
98 hash_remove_request(pData, (struct request *)arg);
99 free((struct request *)arg);
100 ++removed_queries;
101}
102#else
103static void
104timeout(PNATState pData, struct socket *so, void *arg)
105{
106 struct request *req = (struct request *)arg;
107 struct dns_entry *de;
108 de = LIST_NEXT(req->dns_server, de_list);
109 /* here we should check if we reached the end of the DNS server list */
110 if (de == NULL)
111 {
112 hash_remove_request(pData, req);
113 RTMemFree(req);
114 ++removed_queries;
115 }
116 else
117 {
118 struct ip *ip;
119 struct udphdr *udp;
120 int iphlen;
121 struct socket *so1 = socreate();
122 struct mbuf *m = NULL;
123 char *data;
124 if (so1 == NULL)
125 {
126 LogRel(("NAT: can't create DNS socket \n"));
127 return;
128 }
129 if(udp_attach(pData, so1, 0) == -1)
130 {
131 LogRel(("NAT: can't attach udp socket\n"));
132 sofree(pData, so1);
133 return;
134 }
135 m = m_get(pData);
136 if (m == NULL)
137 {
138 LogRel(("NAT: Can't allocate mbuf\n"));
139 udp_detach(pData, so1);
140 return;
141 }
142 /* mbuf initialization */
143 m->m_data += if_maxlinkhdr;
144 ip = mtod(m, struct ip *);
145 udp = (struct udphdr *)&ip[1]; /* ip attributes */
146 data = (char *)&udp[1];
147 iphlen = sizeof(struct ip);
148 m->m_len += sizeof(struct ip);
149 m->m_len += sizeof(struct udphdr);
150 m->m_len += req->nbyte;
151 ip->ip_src.s_addr = so->so_laddr.s_addr;
152 ip->ip_dst.s_addr = htonl(ntohl(special_addr.s_addr) | CTL_DNS);
153 udp->uh_dport = ntohs(53);
154 udp->uh_sport = so->so_lport;
155 memcpy(data, req->byte, req->nbyte); /* coping initial req */
156
157 so1->so_laddr = so->so_laddr;
158 so1->so_lport = so->so_lport;
159 so1->so_faddr = so->so_faddr;
160 so1->so_fport = so->so_fport;
161 req->dns_server = de;
162 so1->so_timeout_arg = req;
163 so1->so_timeout = timeout;
164 dnsproxy_query(pData, so1, m, iphlen);
165 }
166}
167#endif
168
169/* do_query -- Called by the event loop when a packet arrives at our
170 * listening socket. Read the packet, create a new query, append it to the
171 * queue and send it to the correct server.
172 *
173 * Slirp: this routine should be called from udp_input
174 * socket is Slirp's construction (here we should set expiration time for socket)
175 * mbuf points on ip header to easy fetch information about source and destination.
176 * iphlen - len of ip header
177 */
178
179/* ARGSUSED */
180#ifndef VBOX
181static void
182do_query(int fd, short event, void *arg)
183#else
184void
185dnsproxy_query(PNATState pData, struct socket *so, struct mbuf *m, int iphlen)
186#endif
187{
188#ifndef VBOX
189 char buf[MAX_BUFSPACE];
190 unsigned int fromlen = sizeof(fromaddr);
191 struct timeval tv;
192#else
193 struct ip *ip;
194 char *buf;
195 int retransmit;
196 struct udphdr *udp;
197#endif
198 struct sockaddr_in addr;
199 struct request *req = NULL;
200#ifndef VBOX
201 struct sockaddr_in fromaddr;
202#else
203 struct sockaddr_in fromaddr = { 0, };
204#endif
205 int byte = 0;
206
207 ++all_queries;
208
209#ifndef VBOX
210 /* Reschedule event */
211 event_add((struct event *)arg, NULL);
212
213 /* read packet from socket */
214 if ((byte = recvfrom(fd, buf, sizeof(buf), 0,
215 (struct sockaddr *)&fromaddr, &fromlen)) == -1) {
216 LogRel(("recvfrom failed: %s", strerror(errno)));
217 ++dropped_queries;
218 return;
219 }
220#else
221 ip = mtod(m, struct ip *);
222 udp = (struct udphdr *)(m->m_data + iphlen);
223
224 fromaddr.sin_addr.s_addr = ip->ip_src.s_addr;
225 fromaddr.sin_port = udp->uh_sport;
226 fromaddr.sin_family = AF_INET;
227
228 iphlen += sizeof (struct udphdr);
229 byte = m->m_len - iphlen; /* size of IP header + udp header size */
230 /* the validness of ip and udp header has been already checked so we shouldn't care if */
231 buf = m->m_data + iphlen;
232#endif
233
234 /* check for minimum dns packet length */
235 if (byte < 12) {
236 LogRel(("query too short from %s",
237 inet_ntoa(fromaddr.sin_addr)));
238 ++dropped_queries;
239 return;
240 }
241
242#ifndef VBOX
243 /* allocate new request */
244 if ((req = calloc(1, sizeof(struct request))) == NULL) {
245 LogRel(("calloc: %s", strerror(errno)));
246 ++dropped_queries;
247 return;
248 }
249
250 req->id = QUERYID;
251 memcpy(&req->client, &fromaddr, sizeof(struct sockaddr_in));
252 memcpy(&req->clientid, &buf[0], 2);
253#else
254 /* allocate new request */
255 req = so->so_timeout_arg; /* in slirp we might re-send the query*/
256 if (req == NULL)
257 {
258 if ((req = RTMemAllocZ(sizeof(struct request) + byte)) == NULL) {
259 LogRel(("calloc: %s", strerror(errno)));
260 ++dropped_queries;
261 return;
262 }
263 }
264
265 /* fill the request structure */
266 if (so->so_timeout_arg == NULL)
267 {
268 req->id = QUERYID;
269 memcpy(&req->client, &fromaddr, sizeof(struct sockaddr_in));
270 memcpy(&req->clientid, &buf[0], 2);
271 req->dns_server = LIST_FIRST(&pData->dns_list_head);
272 if (req->dns_server == NULL)
273 {
274 static int fail_counter = 0;
275 RTMemFree(req);
276 if (fail_counter == 0)
277 LogRel(("NAT/dnsproxy: Empty DNS entry (suppressed 100 times)\n"));
278 else
279 fail_counter = (fail_counter == 100 ? 0 : fail_counter + 1);
280 return;
281
282 }
283 retransmit = 0;
284 so->so_timeout = timeout;
285 so->so_timeout_arg = req;
286 req->nbyte = byte;
287 memcpy(req->byte, buf, byte); /* copying original request */
288 }
289 else
290 {
291 retransmit = 1;
292 }
293#endif
294
295#ifndef VBOX
296 /* where is this query coming from? */
297 if (is_internal(pData, fromaddr.sin_addr)) {
298 req->recursion = RD(buf);
299 DPRINTF(("Internal query RD=%d\n", req->recursion));
300 } else {
301 /* no recursion for foreigners */
302 req->recursion = 0;
303 DPRINTF(("External query RD=%d\n", RD(buf)));
304 }
305
306 /* insert it into the hash table */
307 hash_add_request(pData, req);
308#else
309 req->recursion = 0;
310 DPRINTF(("External query RD=%d\n", RD(buf)));
311 if (retransmit == 0)
312 hash_add_request(pData, req);
313#endif
314
315 /* overwrite the original query id */
316 memcpy(&buf[0], &req->id, 2);
317
318#ifndef VBOX
319 if (req->recursion) {
320
321 /* recursive queries timeout in 90s */
322 event_set(&req->timeout, -1, 0, timeout, req);
323 tv.tv_sec=recursive_timeout; tv.tv_usec=0;
324 event_add(&req->timeout, &tv);
325
326 /* send it to our recursive server */
327 if ((byte = sendto(sock_answer, buf, (unsigned int)byte, 0,
328 (struct sockaddr *)&recursive_addr,
329 sizeof(struct sockaddr_in))) == -1) {
330 LogRel(("sendto failed: %s", strerror(errno)));
331 ++dropped_queries;
332 return;
333 }
334
335 ++recursive_queries;
336
337 } else {
338
339 /* authoritative queries timeout in 10s */
340 event_set(&req->timeout, -1, 0, timeout, req);
341 tv.tv_sec=authoritative_timeout; tv.tv_usec=0;
342 event_add(&req->timeout, &tv);
343
344 /* send it to our authoritative server */
345 if ((byte = sendto(sock_answer, buf, (unsigned int)byte, 0,
346 (struct sockaddr *)&authoritative_addr,
347 sizeof(struct sockaddr_in))) == -1) {
348 LogRel(("sendto failed: %s", strerror(errno)));
349 ++dropped_queries;
350 return;
351 }
352
353#else
354 so->so_expire = curtime + recursive_timeout * 1000; /* let's slirp to care about expiration */
355 memset(&addr, 0, sizeof(struct sockaddr_in));
356 addr.sin_family = AF_INET;
357 addr.sin_addr.s_addr = req->dns_server->de_addr.s_addr;
358 addr.sin_port = htons(53);
359 so->so_expire = curtime + recursive_timeout * 1000; /* let's slirp to care about expiration */
360 /* send it to our authoritative server */
361 Log2(("NAT: request will be sent to %R[IP4] on %R[natsock]\n", &addr.sin_addr, so));
362 if ((byte = sendto(so->s, buf, (unsigned int)byte, 0,
363 (struct sockaddr *)&addr,
364 sizeof(struct sockaddr_in))) == -1) {
365 LogRel(("sendto failed: %s", strerror(errno)));
366 ++dropped_queries;
367 return;
368 }
369 so->so_state = SS_ISFCONNECTED; /* now it's selected */
370 Log2(("NAT: request was sent to %R[IP4] on %R[natsock]\n", &addr.sin_addr, so));
371#endif
372 ++authoritative_queries;
373#ifndef VBOX
374 }
375#endif
376}
377
378/* do_answer -- Process a packet coming from our authoritative or recursive
379 * server. Find the corresponding query and send answer back to querying
380 * host.
381 *
382 * Slirp: we call this from the routine from socrecvfrom routine handling UDP responses.
383 * So at the moment of call response already has been readed and packed into the mbuf
384 */
385
386/* ARGSUSED */
387#ifndef VBOX
388static void
389do_answer(int fd, short event, void *arg)
390#else
391void
392dnsproxy_answer(PNATState pData, struct socket *so, struct mbuf *m)
393#endif
394{
395#ifndef VBOX
396 char buf[MAX_BUFSPACE];
397 int byte = 0;
398 struct request *query = NULL;
399
400 /* Reschedule event */
401 event_add((struct event *)arg, NULL);
402
403 /* read packet from socket */
404 if ((byte = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL)) == -1) {
405 LogRel(("recvfrom failed: %s", strerror(errno)));
406 ++dropped_answers;
407 return;
408 }
409#else
410 char *buf;
411 int byte;
412 struct request *query = NULL;
413 byte = m->m_len;
414 buf = mtod(m, char *);
415#endif
416
417 /* check for minimum dns packet length */
418 if (byte < 12) {
419 LogRel(("answer too short"));
420 ++dropped_answers;
421 return;
422 }
423
424 /* find corresponding query */
425#ifdef VBOX
426 if ((query = hash_find_request(pData, *((unsigned short *)buf))) == NULL) {
427 ++late_answers;
428 /* Probably, this request wasn't serviced by
429 * dnsproxy so we won't care about it here*/
430 so->so_expire = curtime + SO_EXPIREFAST;
431 Log2(("NAT: query wasn't found\n"));
432 return;
433 }
434 so->so_timeout = NULL;
435 so->so_timeout_arg = NULL;
436#else
437 if ((query = hash_find_request(pData, *((unsigned short *)&buf))) == NULL) {
438 ++late_answers;
439 return;
440 }
441 event_del(&query->timeout);
442#endif
443 hash_remove_request(pData, query);
444
445 /* restore original query id */
446 memcpy(&buf[0], &query->clientid, 2);
447
448#ifndef VBOX
449 /* Slirp: will send mbuf to guest by itself */
450 /* send answer back to querying host */
451 if (sendto(sock_query, buf, (unsigned int)byte, 0,
452 (struct sockaddr *)&query->client,
453 sizeof(struct sockaddr_in)) == -1) {
454 LogRel(("sendto failed: %s", strerror(errno)));
455 ++dropped_answers;
456 } else
457 ++answered_queries;
458
459 free(query);
460#else
461 ++answered_queries;
462
463 RTMemFree(query);
464#endif
465}
466
467/* main -- dnsproxy main function
468 */
469#ifndef VBOX
470int
471main(int argc, char *argv[])
472{
473 int ch;
474 struct passwd *pw = NULL;
475 struct sockaddr_in addr;
476 struct event evq, eva;
477 const char *config = "/etc/dnsproxy.conf";
478 int daemonize = 0;
479
480 /* Process commandline arguments */
481 while ((ch = getopt(argc, argv, "c:dhV")) != -1) {
482 switch (ch) {
483 case 'c':
484 config = optarg;
485 break;
486 case 'd':
487 daemonize = 1;
488 break;
489 case 'V':
490 fprintf(stderr, PACKAGE_STRING "\n");
491 exit(0);
492 /* FALLTHROUGH */
493 case 'h':
494 default:
495 fprintf(stderr,
496 "usage: dnsproxy [-c file] [-dhV]\n" \
497 "\t-c file Read configuration from file\n" \
498 "\t-d Detach and run as a daemon\n" \
499 "\t-h This help text\n" \
500 "\t-V Show version information\n");
501 exit(1);
502 }
503 }
504
505 /* Parse configuration and check required parameters */
506 if (!parse(config))
507 fatal("unable to parse configuration");
508
509 if (!authoritative || !recursive)
510 fatal("No authoritative or recursive server defined");
511
512 if (!listenat)
513 listenat = strdup("0.0.0.0");
514
515 /* Create and bind query socket */
516 if ((sock_query = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
517 fatal("unable to create socket: %s", strerror(errno));
518
519 memset(&addr, 0, sizeof(struct sockaddr_in));
520 addr.sin_addr.s_addr = inet_addr(listenat);
521 addr.sin_port = htons(port);
522 addr.sin_family = AF_INET;
523
524 if (bind(sock_query, (struct sockaddr *)&addr, sizeof(addr)) != 0)
525 fatal("unable to bind socket: %s", strerror(errno));
526
527 /* Create and bind answer socket */
528 if ((sock_answer = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
529 fatal("unable to create socket: %s", strerror(errno));
530
531 memset(&addr, 0, sizeof(struct sockaddr_in));
532 addr.sin_family = AF_INET;
533
534 if (bind(sock_answer, (struct sockaddr *)&addr, sizeof(addr)) != 0)
535 fatal("unable to bind socket: %s", strerror(errno));
536
537 /* Fill sockaddr_in structs for both servers */
538 memset(&authoritative_addr, 0, sizeof(struct sockaddr_in));
539 authoritative_addr.sin_addr.s_addr = inet_addr(authoritative);
540 authoritative_addr.sin_port = htons(authoritative_port);
541 authoritative_addr.sin_family = AF_INET;
542
543 memset(&recursive_addr, 0, sizeof(struct sockaddr_in));
544 recursive_addr.sin_addr.s_addr = inet_addr(recursive);
545 recursive_addr.sin_port = htons(recursive_port);
546 recursive_addr.sin_family = AF_INET;
547
548 /* Daemonize if requested and switch to syslog */
549 if (daemonize) {
550 if (daemon(0, 0) == -1)
551 fatal("unable to daemonize");
552 log_syslog("dnsproxy");
553 }
554
555 /* Find less privileged user */
556 if (user) {
557 pw = getpwnam(user);
558 if (!pw)
559 fatal("unable to find user %s", user);
560 }
561
562 /* Do a chroot if requested */
563 if (chrootdir) {
564 if (chdir(chrootdir) || chroot(chrootdir))
565 fatal("unable to chroot to %s", chrootdir);
566 chdir("/");
567 }
568
569 /* Drop privileges */
570 if (user) {
571 if (setgroups(1, &pw->pw_gid) < 0)
572 fatal("setgroups: %s", strerror(errno));
573#if defined(HAVE_SETRESGID)
574 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0)
575 fatal("setresgid: %s", strerror(errno));
576#elif defined(HAVE_SETREGID)
577 if (setregid(pw->pw_gid, pw->pw_gid) < 0)
578 fatal("setregid: %s", strerror(errno));
579#else
580 if (setegid(pw->pw_gid) < 0)
581 fatal("setegid: %s", strerror(errno));
582 if (setgid(pw->pw_gid) < 0)
583 fatal("setgid: %s", strerror(errno));
584#endif
585#if defined(HAVE_SETRESUID)
586 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0)
587 fatal("setresuid: %s", strerror(errno));
588#elif defined(HAVE_SETREUID)
589 if (setreuid(pw->pw_uid, pw->pw_uid) < 0)
590 fatal("setreuid: %s", strerror(errno));
591#else
592 if (seteuid(pw->pw_uid) < 0)
593 fatal("seteuid: %s", strerror(errno));
594 if (setuid(pw->pw_uid) < 0)
595 fatal("setuid: %s", strerror(errno));
596#endif
597 }
598
599 /* Init event handling */
600 event_init();
601
602 event_set(&evq, sock_query, EV_READ, do_query, &evq);
603 event_add(&evq, NULL);
604
605 event_set(&eva, sock_answer, EV_READ, do_answer, &eva);
606 event_add(&eva, NULL);
607
608 /* Zero counters and start statistics timer */
609 statistics_start();
610
611 /* Take care of signals */
612 if (signal(SIGINT, signal_handler) == SIG_ERR)
613 fatal("unable to mask signal SIGINT: %s", strerror(errno));
614
615 if (signal(SIGTERM, signal_handler) == SIG_ERR)
616 fatal("unable to mask signal SIGTERM: %s", strerror(errno));
617
618 if (signal(SIGHUP, SIG_IGN) == SIG_ERR)
619 fatal("unable to mask signal SIGHUP: %s", strerror(errno));
620
621 event_sigcb = signal_event;
622
623 /* Start libevent main loop */
624 event_dispatch();
625
626 return 0;
627
628}
629#else
630int
631dnsproxy_init(PNATState pData)
632{
633 /* globals initialization */
634 authoritative_port = 53;
635 authoritative_timeout = 10;
636 recursive_port = 53;
637 recursive_timeout = 2;
638 stats_timeout = 3600;
639 dns_port = 53;
640 return 0;
641}
642#endif
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette