VirtualBox

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

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

gcc-4.4 warning

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