VirtualBox

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

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

NAT/dnsproxy.c:timeout if DNS server list is empty just drop the request.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.8 KB
Line 
1/* $Id: dnsproxy.c 50673 2014-03-04 01:36:57Z 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 if ( req->dns_server == NULL
124 || (de = TAILQ_PREV(req->dns_server, dns_list_head, de_list)) == NULL)
125 {
126 hash_remove_request(pData, req);
127 RTMemFree(req);
128 ++removed_queries;
129 /* the rest of clean up at the end of the method. */
130 }
131 else
132 {
133 struct ip *ip;
134 struct udphdr *udp;
135 int iphlen;
136 struct mbuf *m = NULL;
137 char *data;
138
139 m = slirpDnsMbufAlloc(pData);
140 if (m == NULL)
141 {
142 LogRel(("NAT: Can't allocate mbuf\n"));
143 goto socket_clean_up;
144 }
145
146 /* mbuf initialization */
147 m->m_data += if_maxlinkhdr;
148
149 ip = mtod(m, struct ip *);
150 udp = (struct udphdr *)&ip[1]; /* ip attributes */
151 data = (char *)&udp[1];
152 iphlen = sizeof(struct ip);
153
154 m->m_len += sizeof(struct ip);
155 m->m_len += sizeof(struct udphdr);
156 m->m_len += req->nbyte;
157
158 ip->ip_src.s_addr = so->so_laddr.s_addr;
159 ip->ip_dst.s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_DNS);
160 udp->uh_dport = ntohs(53);
161 udp->uh_sport = so->so_lport;
162
163 memcpy(data, req->byte, req->nbyte); /* coping initial req */
164
165 /* req points to so->so_timeout_arg */
166 req->dns_server = de;
167
168 /* expiration will be bumped in dnsproxy_query */
169
170 dnsproxy_query(pData, so, m, iphlen);
171 /* should we free so->so_m ? */
172 return;
173 }
174
175 socket_clean_up:
176 /* This socket (so) will be detached, so we need to remove timeout(&_arg) references
177 * before leave
178 */
179 so->so_timeout = NULL;
180 so->so_timeout_arg = NULL;
181 return;
182
183}
184#endif /* VBOX */
185
186/* do_query -- Called by the event loop when a packet arrives at our
187 * listening socket. Read the packet, create a new query, append it to the
188 * queue and send it to the correct server.
189 *
190 * Slirp: this routine should be called from udp_input
191 * socket is Slirp's construction (here we should set expiration time for socket)
192 * mbuf points on ip header to easy fetch information about source and destination.
193 * iphlen - len of ip header
194 */
195
196/* ARGSUSED */
197#ifndef VBOX
198static void
199do_query(int fd, short event, void *arg)
200#else
201void
202dnsproxy_query(PNATState pData, struct socket *so, struct mbuf *m, int iphlen)
203#endif
204{
205#ifndef VBOX
206 char buf[MAX_BUFSPACE];
207 unsigned int fromlen = sizeof(fromaddr);
208 struct timeval tv;
209#else
210 struct ip *ip;
211 char *buf;
212 int retransmit;
213 struct udphdr *udp;
214#endif
215 struct sockaddr_in addr;
216 struct request *req = NULL;
217#ifndef VBOX
218 struct sockaddr_in fromaddr;
219#else
220 struct sockaddr_in fromaddr = { 0, };
221#endif
222 int byte = 0;
223
224 ++all_queries;
225
226#ifndef VBOX
227 /* Reschedule event */
228 event_add((struct event *)arg, NULL);
229
230 /* read packet from socket */
231 if ((byte = recvfrom(fd, buf, sizeof(buf), 0,
232 (struct sockaddr *)&fromaddr, &fromlen)) == -1) {
233 LogRel(("recvfrom failed: %s\n", strerror(errno)));
234 ++dropped_queries;
235 return;
236 }
237
238 /* check for minimum dns packet length */
239 if (byte < 12) {
240 LogRel(("query too short from %s\n", inet_ntoa(fromaddr.sin_addr)));
241 ++dropped_queries;
242 return;
243 }
244
245 /* allocate new request */
246 if ((req = calloc(1, sizeof(struct request))) == NULL) {
247 LogRel(("calloc failed\n"));
248 ++dropped_queries;
249 return;
250 }
251
252 req->id = QUERYID;
253 memcpy(&req->client, &fromaddr, sizeof(struct sockaddr_in));
254 memcpy(&req->clientid, &buf[0], 2);
255
256 /* where is this query coming from? */
257 if (is_internal(pData, fromaddr.sin_addr)) {
258 req->recursion = RD(buf);
259 DPRINTF(("Internal query RD=%d\n", req->recursion));
260 } else {
261 /* no recursion for foreigners */
262 req->recursion = 0;
263 DPRINTF(("External query RD=%d\n", RD(buf)));
264 }
265
266 /* insert it into the hash table */
267 hash_add_request(pData, req);
268
269 /* overwrite the original query id */
270 memcpy(&buf[0], &req->id, 2);
271
272 if (req->recursion) {
273
274 /* recursive queries timeout in 90s */
275 event_set(&req->timeout, -1, 0, timeout, req);
276 tv.tv_sec=recursive_timeout; tv.tv_usec=0;
277 event_add(&req->timeout, &tv);
278
279 /* send it to our recursive server */
280 if ((byte = sendto(sock_answer, buf, (unsigned int)byte, 0,
281 (struct sockaddr *)&recursive_addr,
282 sizeof(struct sockaddr_in))) == -1) {
283 LogRel(("sendto failed: %s\n", strerror(errno)));
284 ++dropped_queries;
285 return;
286 }
287
288 ++recursive_queries;
289
290 } else {
291
292 /* authoritative queries timeout in 10s */
293 event_set(&req->timeout, -1, 0, timeout, req);
294 tv.tv_sec=authoritative_timeout; tv.tv_usec=0;
295 event_add(&req->timeout, &tv);
296
297 /* send it to our authoritative server */
298 if ((byte = sendto(sock_answer, buf, (unsigned int)byte, 0,
299 (struct sockaddr *)&authoritative_addr,
300 sizeof(struct sockaddr_in))) == -1) {
301 LogRel(("sendto failed: %s\n", strerror(errno)));
302 ++dropped_queries;
303 return;
304 }
305 ++authoritative_queries;
306 }
307
308#else /* VBOX */
309 AssertPtr(pData);
310
311 /* m->m_data points to IP header */
312#if 0
313 /* XXX: for some reason it make gdb ill,
314 * it good to have this assert here with assumption above.
315 */
316 M_ASSERTPKTHDR(m);
317#endif
318
319 ip = mtod(m, struct ip *);
320 udp = (struct udphdr *)(m->m_data + iphlen);
321
322 fromaddr.sin_addr.s_addr = ip->ip_src.s_addr;
323 fromaddr.sin_port = udp->uh_sport;
324 fromaddr.sin_family = AF_INET;
325
326 /* iphlen equals to lenght of ip header */
327 Assert(iphlen == sizeof(struct ip));
328 iphlen += sizeof (struct udphdr);
329
330 byte = m->m_len - iphlen;
331 buf = m->m_data + iphlen;
332
333 /* check for minimum dns packet length */
334 if (byte < 12) {
335 LogRel(("query too short from %RTnaipv4\n", fromaddr.sin_addr));
336 ++dropped_queries;
337 return;
338 }
339
340 req = so->so_timeout_arg;
341
342 if (!req)
343 {
344
345 Assert(!so->so_timeout_arg);
346
347 if ((req = RTMemAllocZ(sizeof(struct request) + byte)) == NULL)
348 {
349 LogRel(("calloc failed\n"));
350 ++dropped_queries;
351 return;
352 }
353
354 req->id = QUERYID;
355 memcpy(&req->client, &fromaddr, sizeof(struct sockaddr_in));
356 memcpy(&req->clientid, &buf[0], 2);
357 req->dns_server = TAILQ_LAST(&pData->pDnsList, dns_list_head);
358 if (req->dns_server == NULL)
359 {
360 static int fail_counter = 0;
361 RTMemFree(req);
362
363 if (fail_counter++ % 100 == 0)
364 LogRel(("NAT/dnsproxy: Empty DNS entry (suppressed 100 times)\n"));
365
366 return;
367
368 }
369 retransmit = 0;
370 so->so_timeout = timeout;
371 so->so_timeout_arg = req;
372 req->nbyte = byte;
373 memcpy(req->byte, buf, byte); /* copying original request */
374 }
375 else
376 {
377 retransmit = 1;
378 }
379
380 req->recursion = 0;
381
382 DPRINTF(("External query RD=%d\n", RD(buf)));
383
384 if (retransmit == 0)
385 hash_add_request(pData, req);
386
387
388 /* overwrite the original query id */
389 memcpy(&buf[0], &req->id, 2);
390
391 /* let's slirp to care about expiration */
392 so->so_expire = curtime + recursive_timeout * 1000;
393
394 memset(&addr, 0, sizeof(struct sockaddr_in));
395 addr.sin_family = AF_INET;
396 if (req->dns_server->de_addr.s_addr == (pData->special_addr.s_addr | RT_H2N_U32_C(CTL_ALIAS))) {
397 /* undo loopback remapping done in get_dns_addr_domain() */
398 addr.sin_addr.s_addr = RT_N2H_U32_C(INADDR_LOOPBACK);
399 }
400 else {
401 addr.sin_addr.s_addr = req->dns_server->de_addr.s_addr;
402 }
403 addr.sin_port = htons(53);
404
405 /* send it to our authoritative server */
406 Log2(("NAT: request will be sent to %RTnaipv4 on %R[natsock]\n", addr.sin_addr, so));
407
408 byte = sendto(so->s, buf, (unsigned int)byte, 0,
409 (struct sockaddr *)&addr,
410 sizeof(struct sockaddr_in));
411 if (byte == -1)
412 {
413 /* XXX: is it really enough? */
414 LogRel(("sendto failed: %s\n", strerror(errno)));
415 ++dropped_queries;
416 return;
417 }
418
419 so->so_state = SS_ISFCONNECTED; /* now it's selected */
420 Log2(("NAT: request was sent to %RTnaipv4 on %R[natsock]\n", addr.sin_addr, so));
421
422 ++authoritative_queries;
423
424# if 0
425 /* XXX: this stuff for _debugging_ only,
426 * first enforce guest to send next request
427 * and second for faster getting timeout callback
428 * other option is adding couple entries in resolv.conf with
429 * invalid nameservers.
430 *
431 * For testing purposes could be used
432 * namebench -S -q 10000 -m random or -m chunk
433 */
434 /* RTThreadSleep(3000); */
435 /* curtime += 300; */
436# endif
437#endif /* VBOX */
438}
439
440/* do_answer -- Process a packet coming from our authoritative or recursive
441 * server. Find the corresponding query and send answer back to querying
442 * host.
443 *
444 * Slirp: we call this from the routine from socrecvfrom routine handling UDP responses.
445 * So at the moment of call response already has been readed and packed into the mbuf
446 */
447
448/* ARGSUSED */
449#ifndef VBOX
450static void
451do_answer(int fd, short event, void *arg)
452#else
453void
454dnsproxy_answer(PNATState pData, struct socket *so, struct mbuf *m)
455#endif
456{
457#ifndef VBOX
458 char buf[MAX_BUFSPACE];
459 int byte = 0;
460 struct request *query = NULL;
461
462 /* Reschedule event */
463 event_add((struct event *)arg, NULL);
464
465 /* read packet from socket */
466 if ((byte = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL)) == -1) {
467 LogRel(("recvfrom failed: %s\n", strerror(errno)));
468 ++dropped_answers;
469 return;
470 }
471
472 /* check for minimum dns packet length */
473 if (byte < 12) {
474 LogRel(("answer too short\n"));
475 ++dropped_answers;
476 return;
477 }
478
479 /* find corresponding query */
480 if ((query = hash_find_request(pData, *((unsigned short *)&buf))) == NULL) {
481 ++late_answers;
482 return;
483 }
484 event_del(&query->timeout);
485
486 hash_remove_request(pData, query);
487
488 /* restore original query id */
489 memcpy(&buf[0], &query->clientid, 2);
490
491 if (sendto(sock_query, buf, (unsigned int)byte, 0,
492 (struct sockaddr *)&query->client,
493 sizeof(struct sockaddr_in)) == -1) {
494 LogRel(("sendto failed: %s\n", strerror(errno)));
495 ++dropped_answers;
496 }
497 else
498 ++answered_queries;
499
500 free(query);
501#else /* VBOX */
502
503 char *buf = NULL;
504 int byte = 0;
505 struct request *query = NULL;
506
507 AssertPtr(pData);
508
509 /* XXX: mbuf->data points to ??? */
510 byte = m->m_len;
511 buf = mtod(m, char *);
512
513 /* check for minimum dns packet length */
514 if (byte < 12) {
515 LogRel(("answer too short\n"));
516 ++dropped_answers;
517 return;
518 }
519
520 query = hash_find_request(pData, *((unsigned short *)buf));
521
522 /* find corresponding query */
523 if (query == NULL)
524 {
525 /* XXX: if we haven't found anything for this request ...
526 * What we are expecting later?
527 */
528 ++late_answers;
529 so->so_expire = curtime + SO_EXPIREFAST;
530 Log2(("NAT: query wasn't found\n"));
531 return;
532 }
533
534 so->so_timeout = NULL;
535 so->so_timeout_arg = NULL;
536
537 hash_remove_request(pData, query);
538
539 /* restore original query id */
540 memcpy(&buf[0], &query->clientid, 2);
541
542 ++answered_queries;
543
544 RTMemFree(query);
545#endif /* VBOX */
546}
547
548
549#ifdef VBOX
550int
551dnsproxy_init(PNATState pData)
552{
553 /* globals initialization */
554 authoritative_port = 53;
555 authoritative_timeout = 10;
556 recursive_port = 53;
557 recursive_timeout = 2;
558 stats_timeout = 3600;
559 dns_port = 53;
560 return 0;
561}
562#else /* !VBOX */
563/* main -- dnsproxy main function
564 */
565int
566main(int argc, char *argv[])
567{
568 int ch;
569 struct passwd *pw = NULL;
570 struct sockaddr_in addr;
571 struct event evq, eva;
572 const char *config = "/etc/dnsproxy.conf";
573 int daemonize = 0;
574
575 /* Process commandline arguments */
576 while ((ch = getopt(argc, argv, "c:dhV")) != -1) {
577 switch (ch) {
578 case 'c':
579 config = optarg;
580 break;
581 case 'd':
582 daemonize = 1;
583 break;
584 case 'V':
585 fprintf(stderr, PACKAGE_STRING "\n");
586 exit(0);
587 /* FALLTHROUGH */
588 case 'h':
589 default:
590 fprintf(stderr,
591 "usage: dnsproxy [-c file] [-dhV]\n" \
592 "\t-c file Read configuration from file\n" \
593 "\t-d Detach and run as a daemon\n" \
594 "\t-h This help text\n" \
595 "\t-V Show version information\n");
596 exit(1);
597 }
598 }
599
600 /* Parse configuration and check required parameters */
601 if (!parse(config))
602 fatal("unable to parse configuration");
603
604 if (!authoritative || !recursive)
605 fatal("No authoritative or recursive server defined");
606
607 if (!listenat)
608 listenat = strdup("0.0.0.0");
609
610 /* Create and bind query socket */
611 if ((sock_query = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
612 fatal("unable to create socket: %s", strerror(errno));
613
614 memset(&addr, 0, sizeof(struct sockaddr_in));
615 addr.sin_addr.s_addr = inet_addr(listenat);
616 addr.sin_port = htons(port);
617 addr.sin_family = AF_INET;
618
619 if (bind(sock_query, (struct sockaddr *)&addr, sizeof(addr)) != 0)
620 fatal("unable to bind socket: %s", strerror(errno));
621
622 /* Create and bind answer socket */
623 if ((sock_answer = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
624 fatal("unable to create socket: %s", strerror(errno));
625
626 memset(&addr, 0, sizeof(struct sockaddr_in));
627 addr.sin_family = AF_INET;
628
629 if (bind(sock_answer, (struct sockaddr *)&addr, sizeof(addr)) != 0)
630 fatal("unable to bind socket: %s", strerror(errno));
631
632 /* Fill sockaddr_in structs for both servers */
633 memset(&authoritative_addr, 0, sizeof(struct sockaddr_in));
634 authoritative_addr.sin_addr.s_addr = inet_addr(authoritative);
635 authoritative_addr.sin_port = htons(authoritative_port);
636 authoritative_addr.sin_family = AF_INET;
637
638 memset(&recursive_addr, 0, sizeof(struct sockaddr_in));
639 recursive_addr.sin_addr.s_addr = inet_addr(recursive);
640 recursive_addr.sin_port = htons(recursive_port);
641 recursive_addr.sin_family = AF_INET;
642
643 /* Daemonize if requested and switch to syslog */
644 if (daemonize) {
645 if (daemon(0, 0) == -1)
646 fatal("unable to daemonize");
647 log_syslog("dnsproxy");
648 }
649
650 /* Find less privileged user */
651 if (user) {
652 pw = getpwnam(user);
653 if (!pw)
654 fatal("unable to find user %s", user);
655 }
656
657 /* Do a chroot if requested */
658 if (chrootdir) {
659 if (chdir(chrootdir) || chroot(chrootdir))
660 fatal("unable to chroot to %s", chrootdir);
661 chdir("/");
662 }
663
664 /* Drop privileges */
665 if (user) {
666 if (setgroups(1, &pw->pw_gid) < 0)
667 fatal("setgroups: %s", strerror(errno));
668#if defined(HAVE_SETRESGID)
669 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0)
670 fatal("setresgid: %s", strerror(errno));
671#elif defined(HAVE_SETREGID)
672 if (setregid(pw->pw_gid, pw->pw_gid) < 0)
673 fatal("setregid: %s", strerror(errno));
674#else
675 if (setegid(pw->pw_gid) < 0)
676 fatal("setegid: %s", strerror(errno));
677 if (setgid(pw->pw_gid) < 0)
678 fatal("setgid: %s", strerror(errno));
679#endif
680#if defined(HAVE_SETRESUID)
681 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0)
682 fatal("setresuid: %s", strerror(errno));
683#elif defined(HAVE_SETREUID)
684 if (setreuid(pw->pw_uid, pw->pw_uid) < 0)
685 fatal("setreuid: %s", strerror(errno));
686#else
687 if (seteuid(pw->pw_uid) < 0)
688 fatal("seteuid: %s", strerror(errno));
689 if (setuid(pw->pw_uid) < 0)
690 fatal("setuid: %s", strerror(errno));
691#endif
692 }
693
694 /* Init event handling */
695 event_init();
696
697 event_set(&evq, sock_query, EV_READ, do_query, &evq);
698 event_add(&evq, NULL);
699
700 event_set(&eva, sock_answer, EV_READ, do_answer, &eva);
701 event_add(&eva, NULL);
702
703 /* Zero counters and start statistics timer */
704 statistics_start();
705
706 /* Take care of signals */
707 if (signal(SIGINT, signal_handler) == SIG_ERR)
708 fatal("unable to mask signal SIGINT: %s", strerror(errno));
709
710 if (signal(SIGTERM, signal_handler) == SIG_ERR)
711 fatal("unable to mask signal SIGTERM: %s", strerror(errno));
712
713 if (signal(SIGHUP, SIG_IGN) == SIG_ERR)
714 fatal("unable to mask signal SIGHUP: %s", strerror(errno));
715
716 event_sigcb = signal_event;
717
718 /* Start libevent main loop */
719 event_dispatch();
720
721 return 0;
722
723}
724#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