VirtualBox

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

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

properly export dnsproxy

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