VirtualBox

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

Last change on this file since 47624 was 47624, checked in by vboxsync, 11 years ago

NATState::pDnsList contains "internal" view of addresses, so localhost
is remapped to CTL_ALIAS there. Undo this in dnsproxy_query() so that
dnsproxy works with dnsmasq listening on localhost.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.9 KB
Line 
1/* $Id: dnsproxy.c 47624 2013-08-08 21:58:55Z 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 if (req->dns_server->de_addr.s_addr == pData->special_addr.s_addr | RT_H2N_U32_C(CTL_ALIAS)) {
399 /* undo loopback remapping done in get_dns_addr_domain() */
400 addr.sin_addr.s_addr = RT_N2H_U32_C(INADDR_LOOPBACK);
401 }
402 else {
403 addr.sin_addr.s_addr = req->dns_server->de_addr.s_addr;
404 }
405 addr.sin_port = htons(53);
406
407 /* send it to our authoritative server */
408 Log2(("NAT: request will be sent to %RTnaipv4 on %R[natsock]\n", addr.sin_addr, so));
409
410 byte = sendto(so->s, buf, (unsigned int)byte, 0,
411 (struct sockaddr *)&addr,
412 sizeof(struct sockaddr_in));
413 if (byte == -1)
414 {
415 /* XXX: is it really enough? */
416 LogRel(("sendto failed: %s\n", strerror(errno)));
417 ++dropped_queries;
418 return;
419 }
420
421 so->so_state = SS_ISFCONNECTED; /* now it's selected */
422 Log2(("NAT: request was sent to %RTnaipv4 on %R[natsock]\n", addr.sin_addr, so));
423
424 ++authoritative_queries;
425
426# if 0
427 /* XXX: this stuff for _debugging_ only,
428 * first enforce guest to send next request
429 * and second for faster getting timeout callback
430 * other option is adding couple entries in resolv.conf with
431 * invalid nameservers.
432 *
433 * For testing purposes could be used
434 * namebench -S -q 10000 -m random or -m chunk
435 */
436 /* RTThreadSleep(3000); */
437 /* curtime += 300; */
438# endif
439#endif /* VBOX */
440}
441
442/* do_answer -- Process a packet coming from our authoritative or recursive
443 * server. Find the corresponding query and send answer back to querying
444 * host.
445 *
446 * Slirp: we call this from the routine from socrecvfrom routine handling UDP responses.
447 * So at the moment of call response already has been readed and packed into the mbuf
448 */
449
450/* ARGSUSED */
451#ifndef VBOX
452static void
453do_answer(int fd, short event, void *arg)
454#else
455void
456dnsproxy_answer(PNATState pData, struct socket *so, struct mbuf *m)
457#endif
458{
459#ifndef VBOX
460 char buf[MAX_BUFSPACE];
461 int byte = 0;
462 struct request *query = NULL;
463
464 /* Reschedule event */
465 event_add((struct event *)arg, NULL);
466
467 /* read packet from socket */
468 if ((byte = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL)) == -1) {
469 LogRel(("recvfrom failed: %s\n", strerror(errno)));
470 ++dropped_answers;
471 return;
472 }
473
474 /* check for minimum dns packet length */
475 if (byte < 12) {
476 LogRel(("answer too short\n"));
477 ++dropped_answers;
478 return;
479 }
480
481 /* find corresponding query */
482 if ((query = hash_find_request(pData, *((unsigned short *)&buf))) == NULL) {
483 ++late_answers;
484 return;
485 }
486 event_del(&query->timeout);
487
488 hash_remove_request(pData, query);
489
490 /* restore original query id */
491 memcpy(&buf[0], &query->clientid, 2);
492
493 if (sendto(sock_query, buf, (unsigned int)byte, 0,
494 (struct sockaddr *)&query->client,
495 sizeof(struct sockaddr_in)) == -1) {
496 LogRel(("sendto failed: %s\n", strerror(errno)));
497 ++dropped_answers;
498 }
499 else
500 ++answered_queries;
501
502 free(query);
503#else /* VBOX */
504
505 char *buf = NULL;
506 int byte = 0;
507 struct request *query = NULL;
508
509 AssertPtr(pData);
510
511 /* XXX: mbuf->data points to ??? */
512 byte = m->m_len;
513 buf = mtod(m, char *);
514
515 /* check for minimum dns packet length */
516 if (byte < 12) {
517 LogRel(("answer too short\n"));
518 ++dropped_answers;
519 return;
520 }
521
522 query = hash_find_request(pData, *((unsigned short *)buf));
523
524 /* find corresponding query */
525 if (query == NULL)
526 {
527 /* XXX: if we haven't found anything for this request ...
528 * What we are expecting later?
529 */
530 ++late_answers;
531 so->so_expire = curtime + SO_EXPIREFAST;
532 Log2(("NAT: query wasn't found\n"));
533 return;
534 }
535
536 so->so_timeout = NULL;
537 so->so_timeout_arg = NULL;
538
539 hash_remove_request(pData, query);
540
541 /* restore original query id */
542 memcpy(&buf[0], &query->clientid, 2);
543
544 ++answered_queries;
545
546 RTMemFree(query);
547#endif /* VBOX */
548}
549
550
551#ifdef VBOX
552int
553dnsproxy_init(PNATState pData)
554{
555 /* globals initialization */
556 authoritative_port = 53;
557 authoritative_timeout = 10;
558 recursive_port = 53;
559 recursive_timeout = 2;
560 stats_timeout = 3600;
561 dns_port = 53;
562 return 0;
563}
564#else /* !VBOX */
565/* main -- dnsproxy main function
566 */
567int
568main(int argc, char *argv[])
569{
570 int ch;
571 struct passwd *pw = NULL;
572 struct sockaddr_in addr;
573 struct event evq, eva;
574 const char *config = "/etc/dnsproxy.conf";
575 int daemonize = 0;
576
577 /* Process commandline arguments */
578 while ((ch = getopt(argc, argv, "c:dhV")) != -1) {
579 switch (ch) {
580 case 'c':
581 config = optarg;
582 break;
583 case 'd':
584 daemonize = 1;
585 break;
586 case 'V':
587 fprintf(stderr, PACKAGE_STRING "\n");
588 exit(0);
589 /* FALLTHROUGH */
590 case 'h':
591 default:
592 fprintf(stderr,
593 "usage: dnsproxy [-c file] [-dhV]\n" \
594 "\t-c file Read configuration from file\n" \
595 "\t-d Detach and run as a daemon\n" \
596 "\t-h This help text\n" \
597 "\t-V Show version information\n");
598 exit(1);
599 }
600 }
601
602 /* Parse configuration and check required parameters */
603 if (!parse(config))
604 fatal("unable to parse configuration");
605
606 if (!authoritative || !recursive)
607 fatal("No authoritative or recursive server defined");
608
609 if (!listenat)
610 listenat = strdup("0.0.0.0");
611
612 /* Create and bind query socket */
613 if ((sock_query = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
614 fatal("unable to create socket: %s", strerror(errno));
615
616 memset(&addr, 0, sizeof(struct sockaddr_in));
617 addr.sin_addr.s_addr = inet_addr(listenat);
618 addr.sin_port = htons(port);
619 addr.sin_family = AF_INET;
620
621 if (bind(sock_query, (struct sockaddr *)&addr, sizeof(addr)) != 0)
622 fatal("unable to bind socket: %s", strerror(errno));
623
624 /* Create and bind answer socket */
625 if ((sock_answer = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
626 fatal("unable to create socket: %s", strerror(errno));
627
628 memset(&addr, 0, sizeof(struct sockaddr_in));
629 addr.sin_family = AF_INET;
630
631 if (bind(sock_answer, (struct sockaddr *)&addr, sizeof(addr)) != 0)
632 fatal("unable to bind socket: %s", strerror(errno));
633
634 /* Fill sockaddr_in structs for both servers */
635 memset(&authoritative_addr, 0, sizeof(struct sockaddr_in));
636 authoritative_addr.sin_addr.s_addr = inet_addr(authoritative);
637 authoritative_addr.sin_port = htons(authoritative_port);
638 authoritative_addr.sin_family = AF_INET;
639
640 memset(&recursive_addr, 0, sizeof(struct sockaddr_in));
641 recursive_addr.sin_addr.s_addr = inet_addr(recursive);
642 recursive_addr.sin_port = htons(recursive_port);
643 recursive_addr.sin_family = AF_INET;
644
645 /* Daemonize if requested and switch to syslog */
646 if (daemonize) {
647 if (daemon(0, 0) == -1)
648 fatal("unable to daemonize");
649 log_syslog("dnsproxy");
650 }
651
652 /* Find less privileged user */
653 if (user) {
654 pw = getpwnam(user);
655 if (!pw)
656 fatal("unable to find user %s", user);
657 }
658
659 /* Do a chroot if requested */
660 if (chrootdir) {
661 if (chdir(chrootdir) || chroot(chrootdir))
662 fatal("unable to chroot to %s", chrootdir);
663 chdir("/");
664 }
665
666 /* Drop privileges */
667 if (user) {
668 if (setgroups(1, &pw->pw_gid) < 0)
669 fatal("setgroups: %s", strerror(errno));
670#if defined(HAVE_SETRESGID)
671 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0)
672 fatal("setresgid: %s", strerror(errno));
673#elif defined(HAVE_SETREGID)
674 if (setregid(pw->pw_gid, pw->pw_gid) < 0)
675 fatal("setregid: %s", strerror(errno));
676#else
677 if (setegid(pw->pw_gid) < 0)
678 fatal("setegid: %s", strerror(errno));
679 if (setgid(pw->pw_gid) < 0)
680 fatal("setgid: %s", strerror(errno));
681#endif
682#if defined(HAVE_SETRESUID)
683 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0)
684 fatal("setresuid: %s", strerror(errno));
685#elif defined(HAVE_SETREUID)
686 if (setreuid(pw->pw_uid, pw->pw_uid) < 0)
687 fatal("setreuid: %s", strerror(errno));
688#else
689 if (seteuid(pw->pw_uid) < 0)
690 fatal("seteuid: %s", strerror(errno));
691 if (setuid(pw->pw_uid) < 0)
692 fatal("setuid: %s", strerror(errno));
693#endif
694 }
695
696 /* Init event handling */
697 event_init();
698
699 event_set(&evq, sock_query, EV_READ, do_query, &evq);
700 event_add(&evq, NULL);
701
702 event_set(&eva, sock_answer, EV_READ, do_answer, &eva);
703 event_add(&eva, NULL);
704
705 /* Zero counters and start statistics timer */
706 statistics_start();
707
708 /* Take care of signals */
709 if (signal(SIGINT, signal_handler) == SIG_ERR)
710 fatal("unable to mask signal SIGINT: %s", strerror(errno));
711
712 if (signal(SIGTERM, signal_handler) == SIG_ERR)
713 fatal("unable to mask signal SIGTERM: %s", strerror(errno));
714
715 if (signal(SIGHUP, SIG_IGN) == SIG_ERR)
716 fatal("unable to mask signal SIGHUP: %s", strerror(errno));
717
718 event_sigcb = signal_event;
719
720 /* Start libevent main loop */
721 event_dispatch();
722
723 return 0;
724
725}
726#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