VirtualBox

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

Last change on this file since 41967 was 40423, checked in by vboxsync, 13 years ago

NAT: warnings [-Wunused-macros]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.3 KB
Line 
1/* $Id: dnsproxy.c 40423 2012-03-11 03:22:22Z 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 /* VBOX */
82
83# define RD(x) (*(x + 2) & 0x01)
84
85# define QUERYID queryid++
86
87#endif
88/* timeout -- Called by the event loop when a query times out. Removes the
89 * query from the queue.
90 */
91/* ARGSUSED */
92#ifndef VBOX
93static void
94timeout(int fd, short event, void *arg)
95{
96 /* here we should check if we reached the end of the DNS server list */
97 hash_remove_request(pData, (struct request *)arg);
98 free((struct request *)arg);
99 ++removed_queries;
100}
101#else /* VBOX */
102static void
103timeout(PNATState pData, struct socket *so, void *arg)
104{
105 struct request *req = (struct request *)arg;
106 struct dns_entry *de;
107 de = TAILQ_PREV(req->dns_server, dns_list_head, de_list);
108 /* here we should check if we reached the end of the DNS server list */
109 if (de == NULL)
110 {
111 hash_remove_request(pData, req);
112 RTMemFree(req);
113 ++removed_queries;
114 }
115 else
116 {
117 struct ip *ip;
118 struct udphdr *udp;
119 int iphlen;
120 struct socket *so1 = socreate();
121 struct mbuf *m = NULL;
122 char *data;
123 if (so1 == NULL)
124 {
125 LogRel(("NAT: can't create DNS socket\n"));
126 return;
127 }
128 if(udp_attach(pData, so1) == -1)
129 {
130 LogRel(("NAT: can't attach udp socket\n"));
131 sofree(pData, so1);
132 return;
133 }
134 m = slirpDnsMbufAlloc(pData);
135 if (m == NULL)
136 {
137 LogRel(("NAT: Can't allocate mbuf\n"));
138 udp_detach(pData, so1);
139 return;
140 }
141 /* mbuf initialization */
142 m->m_data += if_maxlinkhdr;
143 ip = mtod(m, struct ip *);
144 udp = (struct udphdr *)&ip[1]; /* ip attributes */
145 data = (char *)&udp[1];
146 iphlen = sizeof(struct ip);
147 m->m_len += sizeof(struct ip);
148 m->m_len += sizeof(struct udphdr);
149 m->m_len += req->nbyte;
150 ip->ip_src.s_addr = so->so_laddr.s_addr;
151 ip->ip_dst.s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_DNS);
152 udp->uh_dport = ntohs(53);
153 udp->uh_sport = so->so_lport;
154 memcpy(data, req->byte, req->nbyte); /* coping initial req */
155
156 so1->so_laddr = so->so_laddr;
157 so1->so_lport = so->so_lport;
158 so1->so_faddr = so->so_faddr;
159 so1->so_fport = so->so_fport;
160 req->dns_server = de;
161 so1->so_timeout_arg = req;
162 so1->so_timeout = timeout;
163 dnsproxy_query(pData, so1, m, iphlen);
164 }
165}
166#endif /* VBOX */
167
168/* do_query -- Called by the event loop when a packet arrives at our
169 * listening socket. Read the packet, create a new query, append it to the
170 * queue and send it to the correct server.
171 *
172 * Slirp: this routine should be called from udp_input
173 * socket is Slirp's construction (here we should set expiration time for socket)
174 * mbuf points on ip header to easy fetch information about source and destination.
175 * iphlen - len of ip header
176 */
177
178/* ARGSUSED */
179#ifndef VBOX
180static void
181do_query(int fd, short event, void *arg)
182#else
183void
184dnsproxy_query(PNATState pData, struct socket *so, struct mbuf *m, int iphlen)
185#endif
186{
187#ifndef VBOX
188 char buf[MAX_BUFSPACE];
189 unsigned int fromlen = sizeof(fromaddr);
190 struct timeval tv;
191#else
192 struct ip *ip;
193 char *buf;
194 int retransmit;
195 struct udphdr *udp;
196#endif
197 struct sockaddr_in addr;
198 struct request *req = NULL;
199#ifndef VBOX
200 struct sockaddr_in fromaddr;
201#else
202 struct sockaddr_in fromaddr = { 0, };
203#endif
204 int byte = 0;
205
206 ++all_queries;
207
208#ifndef VBOX
209 /* Reschedule event */
210 event_add((struct event *)arg, NULL);
211
212 /* read packet from socket */
213 if ((byte = recvfrom(fd, buf, sizeof(buf), 0,
214 (struct sockaddr *)&fromaddr, &fromlen)) == -1) {
215 LogRel(("recvfrom failed: %s\n", strerror(errno)));
216 ++dropped_queries;
217 return;
218 }
219#else
220 ip = mtod(m, struct ip *);
221 udp = (struct udphdr *)(m->m_data + iphlen);
222
223 fromaddr.sin_addr.s_addr = ip->ip_src.s_addr;
224 fromaddr.sin_port = udp->uh_sport;
225 fromaddr.sin_family = AF_INET;
226
227 iphlen += sizeof (struct udphdr);
228 byte = m->m_len - iphlen; /* size of IP header + udp header size */
229 /* the validness of ip and udp header has been already checked so we shouldn't care if */
230 buf = m->m_data + iphlen;
231#endif
232
233 /* check for minimum dns packet length */
234 if (byte < 12) {
235 LogRel(("query too short from %s\n", inet_ntoa(fromaddr.sin_addr)));
236 ++dropped_queries;
237 return;
238 }
239
240#ifndef VBOX
241 /* allocate new request */
242 if ((req = calloc(1, sizeof(struct request))) == NULL) {
243 LogRel(("calloc failed\n"));
244 ++dropped_queries;
245 return;
246 }
247
248 req->id = QUERYID;
249 memcpy(&req->client, &fromaddr, sizeof(struct sockaddr_in));
250 memcpy(&req->clientid, &buf[0], 2);
251#else
252 /* allocate new request */
253 req = so->so_timeout_arg; /* in slirp we might re-send the query*/
254 if (req == NULL)
255 {
256 if ((req = RTMemAllocZ(sizeof(struct request) + byte)) == NULL) {
257 LogRel(("calloc failed\n"));
258 ++dropped_queries;
259 return;
260 }
261 }
262
263 /* fill the request structure */
264 if (so->so_timeout_arg == NULL)
265 {
266 req->id = QUERYID;
267 memcpy(&req->client, &fromaddr, sizeof(struct sockaddr_in));
268 memcpy(&req->clientid, &buf[0], 2);
269 req->dns_server = TAILQ_LAST(&pData->pDnsList, dns_list_head);
270 if (req->dns_server == NULL)
271 {
272 static int fail_counter = 0;
273 RTMemFree(req);
274 if (fail_counter == 0)
275 LogRel(("NAT/dnsproxy: Empty DNS entry (suppressed 100 times)\n"));
276 else
277 fail_counter = (fail_counter == 100 ? 0 : fail_counter + 1);
278 return;
279
280 }
281 retransmit = 0;
282 so->so_timeout = timeout;
283 so->so_timeout_arg = req;
284 req->nbyte = byte;
285 memcpy(req->byte, buf, byte); /* copying original request */
286 }
287 else
288 {
289 retransmit = 1;
290 }
291#endif
292
293#ifndef VBOX
294 /* where is this query coming from? */
295 if (is_internal(pData, fromaddr.sin_addr)) {
296 req->recursion = RD(buf);
297 DPRINTF(("Internal query RD=%d\n", req->recursion));
298 } else {
299 /* no recursion for foreigners */
300 req->recursion = 0;
301 DPRINTF(("External query RD=%d\n", RD(buf)));
302 }
303
304 /* insert it into the hash table */
305 hash_add_request(pData, req);
306#else
307 req->recursion = 0;
308 DPRINTF(("External query RD=%d\n", RD(buf)));
309 if (retransmit == 0)
310 hash_add_request(pData, req);
311#endif
312
313 /* overwrite the original query id */
314 memcpy(&buf[0], &req->id, 2);
315
316#ifndef VBOX
317 if (req->recursion) {
318
319 /* recursive queries timeout in 90s */
320 event_set(&req->timeout, -1, 0, timeout, req);
321 tv.tv_sec=recursive_timeout; tv.tv_usec=0;
322 event_add(&req->timeout, &tv);
323
324 /* send it to our recursive server */
325 if ((byte = sendto(sock_answer, buf, (unsigned int)byte, 0,
326 (struct sockaddr *)&recursive_addr,
327 sizeof(struct sockaddr_in))) == -1) {
328 LogRel(("sendto failed: %s\n", strerror(errno)));
329 ++dropped_queries;
330 return;
331 }
332
333 ++recursive_queries;
334
335 } else {
336
337 /* authoritative queries timeout in 10s */
338 event_set(&req->timeout, -1, 0, timeout, req);
339 tv.tv_sec=authoritative_timeout; tv.tv_usec=0;
340 event_add(&req->timeout, &tv);
341
342 /* send it to our authoritative server */
343 if ((byte = sendto(sock_answer, buf, (unsigned int)byte, 0,
344 (struct sockaddr *)&authoritative_addr,
345 sizeof(struct sockaddr_in))) == -1) {
346 LogRel(("sendto failed: %s\n", strerror(errno)));
347 ++dropped_queries;
348 return;
349 }
350
351#else
352 so->so_expire = curtime + recursive_timeout * 1000; /* let's slirp to care about expiration */
353 memset(&addr, 0, sizeof(struct sockaddr_in));
354 addr.sin_family = AF_INET;
355 addr.sin_addr.s_addr = req->dns_server->de_addr.s_addr;
356 addr.sin_port = htons(53);
357 so->so_expire = curtime + recursive_timeout * 1000; /* let's slirp to care about expiration */
358 /* send it to our authoritative server */
359 Log2(("NAT: request will be sent to %RTnaipv4 on %R[natsock]\n", addr.sin_addr, so));
360 if ((byte = sendto(so->s, buf, (unsigned int)byte, 0,
361 (struct sockaddr *)&addr,
362 sizeof(struct sockaddr_in))) == -1) {
363 LogRel(("sendto failed: %s\n", strerror(errno)));
364 ++dropped_queries;
365 return;
366 }
367 so->so_state = SS_ISFCONNECTED; /* now it's selected */
368 Log2(("NAT: request was sent to %RTnaipv4 on %R[natsock]\n", addr.sin_addr, so));
369#endif
370 ++authoritative_queries;
371#ifndef VBOX
372 }
373#endif
374}
375
376/* do_answer -- Process a packet coming from our authoritative or recursive
377 * server. Find the corresponding query and send answer back to querying
378 * host.
379 *
380 * Slirp: we call this from the routine from socrecvfrom routine handling UDP responses.
381 * So at the moment of call response already has been readed and packed into the mbuf
382 */
383
384/* ARGSUSED */
385#ifndef VBOX
386static void
387do_answer(int fd, short event, void *arg)
388#else
389void
390dnsproxy_answer(PNATState pData, struct socket *so, struct mbuf *m)
391#endif
392{
393#ifndef VBOX
394 char buf[MAX_BUFSPACE];
395 int byte = 0;
396 struct request *query = NULL;
397
398 /* Reschedule event */
399 event_add((struct event *)arg, NULL);
400
401 /* read packet from socket */
402 if ((byte = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL)) == -1) {
403 LogRel(("recvfrom failed: %s\n", strerror(errno)));
404 ++dropped_answers;
405 return;
406 }
407#else
408 char *buf;
409 int byte;
410 struct request *query = NULL;
411 byte = m->m_len;
412 buf = mtod(m, char *);
413#endif
414
415 /* check for minimum dns packet length */
416 if (byte < 12) {
417 LogRel(("answer too short\n"));
418 ++dropped_answers;
419 return;
420 }
421
422 /* find corresponding query */
423#ifdef VBOX
424 if ((query = hash_find_request(pData, *((unsigned short *)buf))) == NULL) {
425 ++late_answers;
426 /* Probably, this request wasn't serviced by
427 * dnsproxy so we won't care about it here*/
428 so->so_expire = curtime + SO_EXPIREFAST;
429 Log2(("NAT: query wasn't found\n"));
430 return;
431 }
432 so->so_timeout = NULL;
433 so->so_timeout_arg = NULL;
434#else
435 if ((query = hash_find_request(pData, *((unsigned short *)&buf))) == NULL) {
436 ++late_answers;
437 return;
438 }
439 event_del(&query->timeout);
440#endif
441 hash_remove_request(pData, query);
442
443 /* restore original query id */
444 memcpy(&buf[0], &query->clientid, 2);
445
446#ifndef VBOX
447 /* Slirp: will send mbuf to guest by itself */
448 /* send answer back to querying host */
449 if (sendto(sock_query, buf, (unsigned int)byte, 0,
450 (struct sockaddr *)&query->client,
451 sizeof(struct sockaddr_in)) == -1) {
452 LogRel(("sendto failed: %s\n", strerror(errno)));
453 ++dropped_answers;
454 } else
455 ++answered_queries;
456
457 free(query);
458#else
459 ++answered_queries;
460
461 RTMemFree(query);
462#endif
463}
464
465/* main -- dnsproxy main function
466 */
467#ifndef VBOX
468int
469main(int argc, char *argv[])
470{
471 int ch;
472 struct passwd *pw = NULL;
473 struct sockaddr_in addr;
474 struct event evq, eva;
475 const char *config = "/etc/dnsproxy.conf";
476 int daemonize = 0;
477
478 /* Process commandline arguments */
479 while ((ch = getopt(argc, argv, "c:dhV")) != -1) {
480 switch (ch) {
481 case 'c':
482 config = optarg;
483 break;
484 case 'd':
485 daemonize = 1;
486 break;
487 case 'V':
488 fprintf(stderr, PACKAGE_STRING "\n");
489 exit(0);
490 /* FALLTHROUGH */
491 case 'h':
492 default:
493 fprintf(stderr,
494 "usage: dnsproxy [-c file] [-dhV]\n" \
495 "\t-c file Read configuration from file\n" \
496 "\t-d Detach and run as a daemon\n" \
497 "\t-h This help text\n" \
498 "\t-V Show version information\n");
499 exit(1);
500 }
501 }
502
503 /* Parse configuration and check required parameters */
504 if (!parse(config))
505 fatal("unable to parse configuration");
506
507 if (!authoritative || !recursive)
508 fatal("No authoritative or recursive server defined");
509
510 if (!listenat)
511 listenat = strdup("0.0.0.0");
512
513 /* Create and bind query socket */
514 if ((sock_query = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
515 fatal("unable to create socket: %s", strerror(errno));
516
517 memset(&addr, 0, sizeof(struct sockaddr_in));
518 addr.sin_addr.s_addr = inet_addr(listenat);
519 addr.sin_port = htons(port);
520 addr.sin_family = AF_INET;
521
522 if (bind(sock_query, (struct sockaddr *)&addr, sizeof(addr)) != 0)
523 fatal("unable to bind socket: %s", strerror(errno));
524
525 /* Create and bind answer socket */
526 if ((sock_answer = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
527 fatal("unable to create socket: %s", strerror(errno));
528
529 memset(&addr, 0, sizeof(struct sockaddr_in));
530 addr.sin_family = AF_INET;
531
532 if (bind(sock_answer, (struct sockaddr *)&addr, sizeof(addr)) != 0)
533 fatal("unable to bind socket: %s", strerror(errno));
534
535 /* Fill sockaddr_in structs for both servers */
536 memset(&authoritative_addr, 0, sizeof(struct sockaddr_in));
537 authoritative_addr.sin_addr.s_addr = inet_addr(authoritative);
538 authoritative_addr.sin_port = htons(authoritative_port);
539 authoritative_addr.sin_family = AF_INET;
540
541 memset(&recursive_addr, 0, sizeof(struct sockaddr_in));
542 recursive_addr.sin_addr.s_addr = inet_addr(recursive);
543 recursive_addr.sin_port = htons(recursive_port);
544 recursive_addr.sin_family = AF_INET;
545
546 /* Daemonize if requested and switch to syslog */
547 if (daemonize) {
548 if (daemon(0, 0) == -1)
549 fatal("unable to daemonize");
550 log_syslog("dnsproxy");
551 }
552
553 /* Find less privileged user */
554 if (user) {
555 pw = getpwnam(user);
556 if (!pw)
557 fatal("unable to find user %s", user);
558 }
559
560 /* Do a chroot if requested */
561 if (chrootdir) {
562 if (chdir(chrootdir) || chroot(chrootdir))
563 fatal("unable to chroot to %s", chrootdir);
564 chdir("/");
565 }
566
567 /* Drop privileges */
568 if (user) {
569 if (setgroups(1, &pw->pw_gid) < 0)
570 fatal("setgroups: %s", strerror(errno));
571#if defined(HAVE_SETRESGID)
572 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0)
573 fatal("setresgid: %s", strerror(errno));
574#elif defined(HAVE_SETREGID)
575 if (setregid(pw->pw_gid, pw->pw_gid) < 0)
576 fatal("setregid: %s", strerror(errno));
577#else
578 if (setegid(pw->pw_gid) < 0)
579 fatal("setegid: %s", strerror(errno));
580 if (setgid(pw->pw_gid) < 0)
581 fatal("setgid: %s", strerror(errno));
582#endif
583#if defined(HAVE_SETRESUID)
584 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0)
585 fatal("setresuid: %s", strerror(errno));
586#elif defined(HAVE_SETREUID)
587 if (setreuid(pw->pw_uid, pw->pw_uid) < 0)
588 fatal("setreuid: %s", strerror(errno));
589#else
590 if (seteuid(pw->pw_uid) < 0)
591 fatal("seteuid: %s", strerror(errno));
592 if (setuid(pw->pw_uid) < 0)
593 fatal("setuid: %s", strerror(errno));
594#endif
595 }
596
597 /* Init event handling */
598 event_init();
599
600 event_set(&evq, sock_query, EV_READ, do_query, &evq);
601 event_add(&evq, NULL);
602
603 event_set(&eva, sock_answer, EV_READ, do_answer, &eva);
604 event_add(&eva, NULL);
605
606 /* Zero counters and start statistics timer */
607 statistics_start();
608
609 /* Take care of signals */
610 if (signal(SIGINT, signal_handler) == SIG_ERR)
611 fatal("unable to mask signal SIGINT: %s", strerror(errno));
612
613 if (signal(SIGTERM, signal_handler) == SIG_ERR)
614 fatal("unable to mask signal SIGTERM: %s", strerror(errno));
615
616 if (signal(SIGHUP, SIG_IGN) == SIG_ERR)
617 fatal("unable to mask signal SIGHUP: %s", strerror(errno));
618
619 event_sigcb = signal_event;
620
621 /* Start libevent main loop */
622 event_dispatch();
623
624 return 0;
625
626}
627#else
628int
629dnsproxy_init(PNATState pData)
630{
631 /* globals initialization */
632 authoritative_port = 53;
633 authoritative_timeout = 10;
634 recursive_port = 53;
635 recursive_timeout = 2;
636 stats_timeout = 3600;
637 dns_port = 53;
638 return 0;
639}
640#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