VirtualBox

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

Last change on this file since 53448 was 53448, checked in by vboxsync, 10 years ago

NAT: trac ticket 13630 - use generation numbers as poor man's weak
references to prevent dnsproxy from using stale request::dns_server.

This commit has some extra LogRel() for the user to confirm the fix,
which are to be changed to Log2.

XXX: TOO: Clean up in dnsproxy_query() needs more investigation.
Existing code just punts with an "XXX" comment on sendto() failure,
which doesn't doesn't look right.

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