VirtualBox

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

Last change on this file since 37808 was 37746, checked in by vboxsync, 14 years ago

NAT/debug: %R[naipv4] -> %RTnaipv4 and &IP -> IP.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.4 KB
Line 
1/* $Id: dnsproxy.c 37746 2011-07-04 06:07:37Z 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 /* VBOX */
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 = TAILQ_PREV(req->dns_server, dns_list_head, 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 = slirpDnsMbufAlloc(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 = RT_H2N_U32(RT_N2H_U32(pData->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 /* VBOX */
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\n", 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\n", inet_ntoa(fromaddr.sin_addr)));
237 ++dropped_queries;
238 return;
239 }
240
241#ifndef VBOX
242 /* allocate new request */
243 if ((req = calloc(1, sizeof(struct request))) == NULL) {
244 LogRel(("calloc failed\n"));
245 ++dropped_queries;
246 return;
247 }
248
249 req->id = QUERYID;
250 memcpy(&req->client, &fromaddr, sizeof(struct sockaddr_in));
251 memcpy(&req->clientid, &buf[0], 2);
252#else
253 /* allocate new request */
254 req = so->so_timeout_arg; /* in slirp we might re-send the query*/
255 if (req == NULL)
256 {
257 if ((req = RTMemAllocZ(sizeof(struct request) + byte)) == NULL) {
258 LogRel(("calloc failed\n"));
259 ++dropped_queries;
260 return;
261 }
262 }
263
264 /* fill the request structure */
265 if (so->so_timeout_arg == NULL)
266 {
267 req->id = QUERYID;
268 memcpy(&req->client, &fromaddr, sizeof(struct sockaddr_in));
269 memcpy(&req->clientid, &buf[0], 2);
270 req->dns_server = TAILQ_LAST(&pData->pDnsList, dns_list_head);
271 if (req->dns_server == NULL)
272 {
273 static int fail_counter = 0;
274 RTMemFree(req);
275 if (fail_counter == 0)
276 LogRel(("NAT/dnsproxy: Empty DNS entry (suppressed 100 times)\n"));
277 else
278 fail_counter = (fail_counter == 100 ? 0 : fail_counter + 1);
279 return;
280
281 }
282 retransmit = 0;
283 so->so_timeout = timeout;
284 so->so_timeout_arg = req;
285 req->nbyte = byte;
286 memcpy(req->byte, buf, byte); /* copying original request */
287 }
288 else
289 {
290 retransmit = 1;
291 }
292#endif
293
294#ifndef VBOX
295 /* where is this query coming from? */
296 if (is_internal(pData, fromaddr.sin_addr)) {
297 req->recursion = RD(buf);
298 DPRINTF(("Internal query RD=%d\n", req->recursion));
299 } else {
300 /* no recursion for foreigners */
301 req->recursion = 0;
302 DPRINTF(("External query RD=%d\n", RD(buf)));
303 }
304
305 /* insert it into the hash table */
306 hash_add_request(pData, req);
307#else
308 req->recursion = 0;
309 DPRINTF(("External query RD=%d\n", RD(buf)));
310 if (retransmit == 0)
311 hash_add_request(pData, req);
312#endif
313
314 /* overwrite the original query id */
315 memcpy(&buf[0], &req->id, 2);
316
317#ifndef VBOX
318 if (req->recursion) {
319
320 /* recursive queries timeout in 90s */
321 event_set(&req->timeout, -1, 0, timeout, req);
322 tv.tv_sec=recursive_timeout; tv.tv_usec=0;
323 event_add(&req->timeout, &tv);
324
325 /* send it to our recursive server */
326 if ((byte = sendto(sock_answer, buf, (unsigned int)byte, 0,
327 (struct sockaddr *)&recursive_addr,
328 sizeof(struct sockaddr_in))) == -1) {
329 LogRel(("sendto failed: %s\n", strerror(errno)));
330 ++dropped_queries;
331 return;
332 }
333
334 ++recursive_queries;
335
336 } else {
337
338 /* authoritative queries timeout in 10s */
339 event_set(&req->timeout, -1, 0, timeout, req);
340 tv.tv_sec=authoritative_timeout; tv.tv_usec=0;
341 event_add(&req->timeout, &tv);
342
343 /* send it to our authoritative server */
344 if ((byte = sendto(sock_answer, buf, (unsigned int)byte, 0,
345 (struct sockaddr *)&authoritative_addr,
346 sizeof(struct sockaddr_in))) == -1) {
347 LogRel(("sendto failed: %s\n", strerror(errno)));
348 ++dropped_queries;
349 return;
350 }
351
352#else
353 so->so_expire = curtime + recursive_timeout * 1000; /* let's slirp to care about expiration */
354 memset(&addr, 0, sizeof(struct sockaddr_in));
355 addr.sin_family = AF_INET;
356 addr.sin_addr.s_addr = req->dns_server->de_addr.s_addr;
357 addr.sin_port = htons(53);
358 so->so_expire = curtime + recursive_timeout * 1000; /* let's slirp to care about expiration */
359 /* send it to our authoritative server */
360 Log2(("NAT: request will be sent to %RTnaipv4 on %R[natsock]\n", addr.sin_addr, so));
361 if ((byte = sendto(so->s, buf, (unsigned int)byte, 0,
362 (struct sockaddr *)&addr,
363 sizeof(struct sockaddr_in))) == -1) {
364 LogRel(("sendto failed: %s\n", strerror(errno)));
365 ++dropped_queries;
366 return;
367 }
368 so->so_state = SS_ISFCONNECTED; /* now it's selected */
369 Log2(("NAT: request was sent to %RTnaipv4 on %R[natsock]\n", addr.sin_addr, so));
370#endif
371 ++authoritative_queries;
372#ifndef VBOX
373 }
374#endif
375}
376
377/* do_answer -- Process a packet coming from our authoritative or recursive
378 * server. Find the corresponding query and send answer back to querying
379 * host.
380 *
381 * Slirp: we call this from the routine from socrecvfrom routine handling UDP responses.
382 * So at the moment of call response already has been readed and packed into the mbuf
383 */
384
385/* ARGSUSED */
386#ifndef VBOX
387static void
388do_answer(int fd, short event, void *arg)
389#else
390void
391dnsproxy_answer(PNATState pData, struct socket *so, struct mbuf *m)
392#endif
393{
394#ifndef VBOX
395 char buf[MAX_BUFSPACE];
396 int byte = 0;
397 struct request *query = NULL;
398
399 /* Reschedule event */
400 event_add((struct event *)arg, NULL);
401
402 /* read packet from socket */
403 if ((byte = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL)) == -1) {
404 LogRel(("recvfrom failed: %s\n", strerror(errno)));
405 ++dropped_answers;
406 return;
407 }
408#else
409 char *buf;
410 int byte;
411 struct request *query = NULL;
412 byte = m->m_len;
413 buf = mtod(m, char *);
414#endif
415
416 /* check for minimum dns packet length */
417 if (byte < 12) {
418 LogRel(("answer too short\n"));
419 ++dropped_answers;
420 return;
421 }
422
423 /* find corresponding query */
424#ifdef VBOX
425 if ((query = hash_find_request(pData, *((unsigned short *)buf))) == NULL) {
426 ++late_answers;
427 /* Probably, this request wasn't serviced by
428 * dnsproxy so we won't care about it here*/
429 so->so_expire = curtime + SO_EXPIREFAST;
430 Log2(("NAT: query wasn't found\n"));
431 return;
432 }
433 so->so_timeout = NULL;
434 so->so_timeout_arg = NULL;
435#else
436 if ((query = hash_find_request(pData, *((unsigned short *)&buf))) == NULL) {
437 ++late_answers;
438 return;
439 }
440 event_del(&query->timeout);
441#endif
442 hash_remove_request(pData, query);
443
444 /* restore original query id */
445 memcpy(&buf[0], &query->clientid, 2);
446
447#ifndef VBOX
448 /* Slirp: will send mbuf to guest by itself */
449 /* send answer back to querying host */
450 if (sendto(sock_query, buf, (unsigned int)byte, 0,
451 (struct sockaddr *)&query->client,
452 sizeof(struct sockaddr_in)) == -1) {
453 LogRel(("sendto failed: %s\n", strerror(errno)));
454 ++dropped_answers;
455 } else
456 ++answered_queries;
457
458 free(query);
459#else
460 ++answered_queries;
461
462 RTMemFree(query);
463#endif
464}
465
466/* main -- dnsproxy main function
467 */
468#ifndef VBOX
469int
470main(int argc, char *argv[])
471{
472 int ch;
473 struct passwd *pw = NULL;
474 struct sockaddr_in addr;
475 struct event evq, eva;
476 const char *config = "/etc/dnsproxy.conf";
477 int daemonize = 0;
478
479 /* Process commandline arguments */
480 while ((ch = getopt(argc, argv, "c:dhV")) != -1) {
481 switch (ch) {
482 case 'c':
483 config = optarg;
484 break;
485 case 'd':
486 daemonize = 1;
487 break;
488 case 'V':
489 fprintf(stderr, PACKAGE_STRING "\n");
490 exit(0);
491 /* FALLTHROUGH */
492 case 'h':
493 default:
494 fprintf(stderr,
495 "usage: dnsproxy [-c file] [-dhV]\n" \
496 "\t-c file Read configuration from file\n" \
497 "\t-d Detach and run as a daemon\n" \
498 "\t-h This help text\n" \
499 "\t-V Show version information\n");
500 exit(1);
501 }
502 }
503
504 /* Parse configuration and check required parameters */
505 if (!parse(config))
506 fatal("unable to parse configuration");
507
508 if (!authoritative || !recursive)
509 fatal("No authoritative or recursive server defined");
510
511 if (!listenat)
512 listenat = strdup("0.0.0.0");
513
514 /* Create and bind query socket */
515 if ((sock_query = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
516 fatal("unable to create socket: %s", strerror(errno));
517
518 memset(&addr, 0, sizeof(struct sockaddr_in));
519 addr.sin_addr.s_addr = inet_addr(listenat);
520 addr.sin_port = htons(port);
521 addr.sin_family = AF_INET;
522
523 if (bind(sock_query, (struct sockaddr *)&addr, sizeof(addr)) != 0)
524 fatal("unable to bind socket: %s", strerror(errno));
525
526 /* Create and bind answer socket */
527 if ((sock_answer = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
528 fatal("unable to create socket: %s", strerror(errno));
529
530 memset(&addr, 0, sizeof(struct sockaddr_in));
531 addr.sin_family = AF_INET;
532
533 if (bind(sock_answer, (struct sockaddr *)&addr, sizeof(addr)) != 0)
534 fatal("unable to bind socket: %s", strerror(errno));
535
536 /* Fill sockaddr_in structs for both servers */
537 memset(&authoritative_addr, 0, sizeof(struct sockaddr_in));
538 authoritative_addr.sin_addr.s_addr = inet_addr(authoritative);
539 authoritative_addr.sin_port = htons(authoritative_port);
540 authoritative_addr.sin_family = AF_INET;
541
542 memset(&recursive_addr, 0, sizeof(struct sockaddr_in));
543 recursive_addr.sin_addr.s_addr = inet_addr(recursive);
544 recursive_addr.sin_port = htons(recursive_port);
545 recursive_addr.sin_family = AF_INET;
546
547 /* Daemonize if requested and switch to syslog */
548 if (daemonize) {
549 if (daemon(0, 0) == -1)
550 fatal("unable to daemonize");
551 log_syslog("dnsproxy");
552 }
553
554 /* Find less privileged user */
555 if (user) {
556 pw = getpwnam(user);
557 if (!pw)
558 fatal("unable to find user %s", user);
559 }
560
561 /* Do a chroot if requested */
562 if (chrootdir) {
563 if (chdir(chrootdir) || chroot(chrootdir))
564 fatal("unable to chroot to %s", chrootdir);
565 chdir("/");
566 }
567
568 /* Drop privileges */
569 if (user) {
570 if (setgroups(1, &pw->pw_gid) < 0)
571 fatal("setgroups: %s", strerror(errno));
572#if defined(HAVE_SETRESGID)
573 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0)
574 fatal("setresgid: %s", strerror(errno));
575#elif defined(HAVE_SETREGID)
576 if (setregid(pw->pw_gid, pw->pw_gid) < 0)
577 fatal("setregid: %s", strerror(errno));
578#else
579 if (setegid(pw->pw_gid) < 0)
580 fatal("setegid: %s", strerror(errno));
581 if (setgid(pw->pw_gid) < 0)
582 fatal("setgid: %s", strerror(errno));
583#endif
584#if defined(HAVE_SETRESUID)
585 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0)
586 fatal("setresuid: %s", strerror(errno));
587#elif defined(HAVE_SETREUID)
588 if (setreuid(pw->pw_uid, pw->pw_uid) < 0)
589 fatal("setreuid: %s", strerror(errno));
590#else
591 if (seteuid(pw->pw_uid) < 0)
592 fatal("seteuid: %s", strerror(errno));
593 if (setuid(pw->pw_uid) < 0)
594 fatal("setuid: %s", strerror(errno));
595#endif
596 }
597
598 /* Init event handling */
599 event_init();
600
601 event_set(&evq, sock_query, EV_READ, do_query, &evq);
602 event_add(&evq, NULL);
603
604 event_set(&eva, sock_answer, EV_READ, do_answer, &eva);
605 event_add(&eva, NULL);
606
607 /* Zero counters and start statistics timer */
608 statistics_start();
609
610 /* Take care of signals */
611 if (signal(SIGINT, signal_handler) == SIG_ERR)
612 fatal("unable to mask signal SIGINT: %s", strerror(errno));
613
614 if (signal(SIGTERM, signal_handler) == SIG_ERR)
615 fatal("unable to mask signal SIGTERM: %s", strerror(errno));
616
617 if (signal(SIGHUP, SIG_IGN) == SIG_ERR)
618 fatal("unable to mask signal SIGHUP: %s", strerror(errno));
619
620 event_sigcb = signal_event;
621
622 /* Start libevent main loop */
623 event_dispatch();
624
625 return 0;
626
627}
628#else
629int
630dnsproxy_init(PNATState pData)
631{
632 /* globals initialization */
633 authoritative_port = 53;
634 authoritative_timeout = 10;
635 recursive_port = 53;
636 recursive_timeout = 2;
637 stats_timeout = 3600;
638 dns_port = 53;
639 return 0;
640}
641#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