VirtualBox

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

Last change on this file since 47484 was 45463, checked in by vboxsync, 12 years ago

header fixes

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

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