VirtualBox

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

Last change on this file since 106061 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.3 KB
Line 
1/* $Id: dnsproxy.c 106061 2024-09-16 14:03:52Z vboxsync $ */
2
3/*
4 * Copyright (C) 2009-2024 Oracle and/or its affiliates.
5 *
6 * This file is part of VirtualBox base platform packages, as
7 * available from https://www.virtualbox.org.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation, in version 3 of the
12 * License.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, see <https://www.gnu.org/licenses>.
21 *
22 * SPDX-License-Identifier: GPL-3.0-only
23 */
24
25/*
26 * Copyright (c) 2003,2004,2005 Armin Wolfermann
27 *
28 * Permission is hereby granted, free of charge, to any person obtaining a
29 * copy of this software and associated documentation files (the "Software"),
30 * to deal in the Software without restriction, including without limitation
31 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
32 * and/or sell copies of the Software, and to permit persons to whom the
33 * Software is furnished to do so, subject to the following conditions:
34 *
35 * The above copyright notice and this permission notice shall be included in
36 * all copies or substantial portions of the Software.
37 *
38 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
41 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
42 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
43 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
44 * DEALINGS IN THE SOFTWARE.
45 */
46
47#ifndef VBOX
48#include <config.h>
49#include <errno.h>
50#include <pwd.h>
51#include <signal.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55#include <unistd.h>
56#else
57#include "slirp.h"
58#endif
59
60#ifndef VBOX
61#define GLOBALS 1
62#include "dnsproxy.h"
63
64#define RD(x) (*(x + 2) & 0x01)
65#define MAX_BUFSPACE 512
66
67static unsigned short queryid = 0;
68#define QUERYID queryid++
69
70static struct sockaddr_in authoritative_addr;
71static struct sockaddr_in recursive_addr;
72static int sock_query;
73static int sock_answer;
74static int dnsproxy_sig;
75
76extern int event_gotsig;
77extern int (*event_sigcb)(void);
78
79#ifdef DEBUG
80char *malloc_options = "AGZ";
81#endif
82
83/* signal_handler -- Native signal handler. Set external flag for libevent
84 * and store type of signal. Real signal handling is done in signal_event.
85 */
86
87RETSIGTYPE
88signal_handler(int sig)
89{
90 event_gotsig = 1;
91 dnsproxy_sig = sig;
92}
93
94/* signal_event -- Called by libevent to deliver a signal.
95 */
96
97int
98signal_event(void)
99{
100 fatal("exiting on signal %d", dnsproxy_sig);
101 return 0;
102}
103
104#else /* VBOX */
105
106# define RD(x) (*(x + 2) & 0x01)
107
108# define QUERYID queryid++
109
110#endif
111/* timeout -- Called by the event loop when a query times out. Removes the
112 * query from the queue.
113 */
114/* ARGSUSED */
115#ifndef VBOX
116static void
117timeout(int fd, short event, void *arg)
118{
119 /* here we should check if we reached the end of the DNS server list */
120 hash_remove_request(pData, (struct request *)arg);
121 free((struct request *)arg);
122 ++removed_queries;
123}
124#else /* VBOX */
125static void
126timeout(PNATState pData, struct socket *so, void *arg)
127{
128 struct request *req = (struct request *)arg;
129 struct dns_entry *de;
130 /* be paranoid */
131 AssertPtrReturnVoid(arg);
132
133 if ( req->dnsgen != pData->dnsgen
134 || req->dns_server == NULL
135 || (de = TAILQ_PREV(req->dns_server, dns_list_head, de_list)) == NULL)
136 {
137 if (req->dnsgen != pData->dnsgen)
138 {
139 /* XXX: Log2 */
140 LogRel(("NAT: dnsproxy: timeout: req %p dnsgen %u != %u on %R[natsock]\n",
141 req, req->dnsgen, pData->dnsgen, so));
142 }
143 hash_remove_request(pData, req);
144 RTMemFree(req);
145 ++removed_queries;
146 /* the rest of clean up at the end of the method. */
147 }
148 else
149 {
150 struct ip *ip;
151 struct udphdr *udp;
152 int iphlen;
153 struct mbuf *m = NULL;
154 char *data;
155
156 m = slirpDnsMbufAlloc(pData);
157 if (m == NULL)
158 {
159 LogRel(("NAT: Can't allocate mbuf\n"));
160 goto socket_clean_up;
161 }
162
163 /* mbuf initialization */
164 m->m_data += if_maxlinkhdr;
165
166 ip = mtod(m, struct ip *);
167 udp = (struct udphdr *)&ip[1]; /* ip attributes */
168 data = (char *)&udp[1];
169 iphlen = sizeof(struct ip);
170
171 m->m_len += sizeof(struct ip);
172 m->m_len += sizeof(struct udphdr);
173 m->m_len += req->nbyte;
174
175 ip->ip_src.s_addr = so->so_laddr.s_addr;
176 ip->ip_dst.s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_DNS);
177 udp->uh_dport = ntohs(53);
178 udp->uh_sport = so->so_lport;
179
180 memcpy(data, req->byte, req->nbyte); /* coping initial req */
181
182 /* req points to so->so_timeout_arg */
183 req->dns_server = de;
184
185 /* expiration will be bumped in dnsproxy_query */
186
187 dnsproxy_query(pData, so, m, iphlen);
188 /* should we free so->so_m ? */
189 return;
190 }
191
192 socket_clean_up:
193 /* This socket (so) will be detached, so we need to remove timeout(&_arg) references
194 * before leave
195 */
196 so->so_timeout = NULL;
197 so->so_timeout_arg = NULL;
198 return;
199
200}
201#endif /* VBOX */
202
203/* do_query -- Called by the event loop when a packet arrives at our
204 * listening socket. Read the packet, create a new query, append it to the
205 * queue and send it to the correct server.
206 *
207 * Slirp: this routine should be called from udp_input
208 * socket is Slirp's construction (here we should set expiration time for socket)
209 * mbuf points on ip header to easy fetch information about source and destination.
210 * iphlen - len of ip header
211 */
212
213/* ARGSUSED */
214#ifndef VBOX
215static void
216do_query(int fd, short event, void *arg)
217#else
218void
219dnsproxy_query(PNATState pData, struct socket *so, struct mbuf *m, int iphlen)
220#endif
221{
222#ifndef VBOX
223 char buf[MAX_BUFSPACE];
224 unsigned int fromlen = sizeof(fromaddr);
225 struct timeval tv;
226#else
227 struct ip *ip;
228 char *buf;
229 int retransmit;
230 struct udphdr *udp;
231#endif
232 struct sockaddr_in addr;
233 struct request *req = NULL;
234#ifndef VBOX
235 struct sockaddr_in fromaddr;
236#else
237 struct sockaddr_in fromaddr = { 0, };
238#endif
239 int byte = 0;
240
241 ++all_queries;
242
243#ifndef VBOX
244 /* Reschedule event */
245 event_add((struct event *)arg, NULL);
246
247 /* read packet from socket */
248 if ((byte = recvfrom(fd, buf, sizeof(buf), 0,
249 (struct sockaddr *)&fromaddr, &fromlen)) == -1) {
250 LogRel(("recvfrom failed: %s\n", strerror(errno)));
251 ++dropped_queries;
252 return;
253 }
254
255 /* check for minimum dns packet length */
256 if (byte < 12) {
257 LogRel(("query too short from %s\n", inet_ntoa(fromaddr.sin_addr)));
258 ++dropped_queries;
259 return;
260 }
261
262 /* allocate new request */
263 if ((req = calloc(1, sizeof(struct request))) == NULL) {
264 LogRel(("calloc failed\n"));
265 ++dropped_queries;
266 return;
267 }
268
269 req->id = QUERYID;
270 memcpy(&req->client, &fromaddr, sizeof(struct sockaddr_in));
271 memcpy(&req->clientid, &buf[0], 2);
272
273 /* where is this query coming from? */
274 if (is_internal(pData, fromaddr.sin_addr)) {
275 req->recursion = RD(buf);
276 DPRINTF(("Internal query RD=%d\n", req->recursion));
277 } else {
278 /* no recursion for foreigners */
279 req->recursion = 0;
280 DPRINTF(("External query RD=%d\n", RD(buf)));
281 }
282
283 /* insert it into the hash table */
284 hash_add_request(pData, req);
285
286 /* overwrite the original query id */
287 memcpy(&buf[0], &req->id, 2);
288
289 if (req->recursion) {
290
291 /* recursive queries timeout in 90s */
292 event_set(&req->timeout, -1, 0, timeout, req);
293 tv.tv_sec=recursive_timeout; tv.tv_usec=0;
294 event_add(&req->timeout, &tv);
295
296 /* send it to our recursive server */
297 if ((byte = sendto(sock_answer, buf, (unsigned int)byte, 0,
298 (struct sockaddr *)&recursive_addr,
299 sizeof(struct sockaddr_in))) == -1) {
300 LogRel(("sendto failed: %s\n", strerror(errno)));
301 ++dropped_queries;
302 return;
303 }
304
305 ++recursive_queries;
306
307 } else {
308
309 /* authoritative queries timeout in 10s */
310 event_set(&req->timeout, -1, 0, timeout, req);
311 tv.tv_sec=authoritative_timeout; tv.tv_usec=0;
312 event_add(&req->timeout, &tv);
313
314 /* send it to our authoritative server */
315 if ((byte = sendto(sock_answer, buf, (unsigned int)byte, 0,
316 (struct sockaddr *)&authoritative_addr,
317 sizeof(struct sockaddr_in))) == -1) {
318 LogRel(("sendto failed: %s\n", strerror(errno)));
319 ++dropped_queries;
320 return;
321 }
322 ++authoritative_queries;
323 }
324
325#else /* VBOX */
326 AssertPtr(pData);
327
328 /* m->m_data points to IP header */
329#if 0
330 /* XXX: for some reason it make gdb ill,
331 * it good to have this assert here with assumption above.
332 */
333 M_ASSERTPKTHDR(m);
334#endif
335
336 ip = mtod(m, struct ip *);
337 udp = (struct udphdr *)(m->m_data + iphlen);
338
339 fromaddr.sin_addr.s_addr = ip->ip_src.s_addr;
340 fromaddr.sin_port = udp->uh_sport;
341 fromaddr.sin_family = AF_INET;
342
343 /* iphlen equals to lenght of ip header */
344 Assert(iphlen == sizeof(struct ip));
345 iphlen += sizeof (struct udphdr);
346
347 byte = m->m_len - iphlen;
348 buf = m->m_data + iphlen;
349
350 /* check for minimum dns packet length */
351 if (byte < 12) {
352 LogRel(("NAT: Query too short from %RTnaipv4\n", fromaddr.sin_addr));
353 ++dropped_queries;
354 return;
355 }
356
357 req = so->so_timeout_arg;
358
359 if (!req)
360 {
361
362 Assert(!so->so_timeout_arg);
363
364 if ((req = RTMemAllocZ(sizeof(struct request) + byte)) == NULL)
365 {
366 LogRel(("NAT: calloc failed\n"));
367 ++dropped_queries;
368 return;
369 }
370
371 req->id = QUERYID;
372 memcpy(&req->client, &fromaddr, sizeof(struct sockaddr_in));
373 memcpy(&req->clientid, &buf[0], 2);
374 req->dns_server = TAILQ_LAST(&pData->pDnsList, dns_list_head);
375 req->dnsgen = pData->dnsgen;
376 if (req->dns_server == NULL)
377 {
378 RTMemFree(req);
379 return;
380 }
381 retransmit = 0;
382 so->so_timeout = timeout;
383 so->so_timeout_arg = req;
384 req->nbyte = byte;
385 memcpy(req->byte, buf, byte); /* copying original request */
386 }
387 else
388 {
389 if (req->dnsgen != pData->dnsgen)
390 {
391 /* XXX: Log2 */
392 LogRel(("NAT: dnsproxy: query: req %p dnsgen %u != %u on %R[natsock]\n",
393 req, req->dnsgen, pData->dnsgen, so));
394 /*
395 * XXX: TODO: this probably requires more cleanup.
396 * Cf. XXX comment for sendto() failure below, but that
397 * error leg is probably untested since ~never taken.
398 */
399 ++dropped_queries;
400 return;
401 }
402 retransmit = 1;
403 }
404
405 req->recursion = 0;
406
407 DPRINTF(("External query RD=%d\n", RD(buf)));
408
409 if (retransmit == 0)
410 hash_add_request(pData, req);
411
412
413 /* overwrite the original query id */
414 memcpy(&buf[0], &req->id, 2);
415
416 /* let's slirp to care about expiration */
417 so->so_expire = curtime + recursive_timeout * 1000;
418
419 memset(&addr, 0, sizeof(struct sockaddr_in));
420 addr.sin_family = AF_INET;
421 if (req->dns_server->de_addr.s_addr == (pData->special_addr.s_addr | RT_H2N_U32_C(CTL_ALIAS))) {
422 /* undo loopback remapping done in get_dns_addr_domain() */
423 addr.sin_addr.s_addr = RT_N2H_U32_C(INADDR_LOOPBACK);
424 }
425 else {
426 addr.sin_addr.s_addr = req->dns_server->de_addr.s_addr;
427 }
428 addr.sin_port = htons(53);
429
430 /* send it to our authoritative server */
431 Log2(("NAT: request will be %ssent to %RTnaipv4 on %R[natsock]\n",
432 retransmit ? "re" : "", addr.sin_addr, so));
433
434 byte = sendto(so->s, buf, (unsigned int)byte, 0,
435 (struct sockaddr *)&addr,
436 sizeof(struct sockaddr_in));
437 if (byte == -1)
438 {
439 /* XXX: is it really enough? */
440 LogRel(("NAT: sendto failed: %s\n", strerror(errno)));
441 ++dropped_queries;
442 return;
443 }
444
445 so->so_state = SS_ISFCONNECTED; /* now it's selected */
446 Log2(("NAT: request was %ssent to %RTnaipv4 on %R[natsock]\n",
447 retransmit ? "re" : "", addr.sin_addr, so));
448
449 ++authoritative_queries;
450
451# if 0
452 /* XXX: this stuff for _debugging_ only,
453 * first enforce guest to send next request
454 * and second for faster getting timeout callback
455 * other option is adding couple entries in resolv.conf with
456 * invalid nameservers.
457 *
458 * For testing purposes could be used
459 * namebench -S -q 10000 -m random or -m chunk
460 */
461 /* RTThreadSleep(3000); */
462 /* curtime += 300; */
463# endif
464#endif /* VBOX */
465}
466
467/* do_answer -- Process a packet coming from our authoritative or recursive
468 * server. Find the corresponding query and send answer back to querying
469 * host.
470 *
471 * Slirp: we call this from the routine from socrecvfrom routine handling UDP responses.
472 * So at the moment of call response already has been readed and packed into the mbuf
473 */
474
475/* ARGSUSED */
476#ifndef VBOX
477static void
478do_answer(int fd, short event, void *arg)
479#else
480void
481dnsproxy_answer(PNATState pData, struct socket *so, struct mbuf *m)
482#endif
483{
484#ifndef VBOX
485 char buf[MAX_BUFSPACE];
486 int byte = 0;
487 struct request *query = NULL;
488
489 /* Reschedule event */
490 event_add((struct event *)arg, NULL);
491
492 /* read packet from socket */
493 if ((byte = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL)) == -1) {
494 LogRel(("recvfrom failed: %s\n", strerror(errno)));
495 ++dropped_answers;
496 return;
497 }
498
499 /* check for minimum dns packet length */
500 if (byte < 12) {
501 LogRel(("answer too short\n"));
502 ++dropped_answers;
503 return;
504 }
505
506 /* find corresponding query */
507 if ((query = hash_find_request(pData, *((unsigned short *)&buf))) == NULL) {
508 ++late_answers;
509 return;
510 }
511 event_del(&query->timeout);
512
513 hash_remove_request(pData, query);
514
515 /* restore original query id */
516 memcpy(&buf[0], &query->clientid, 2);
517
518 if (sendto(sock_query, buf, (unsigned int)byte, 0,
519 (struct sockaddr *)&query->client,
520 sizeof(struct sockaddr_in)) == -1) {
521 LogRel(("sendto failed: %s\n", strerror(errno)));
522 ++dropped_answers;
523 }
524 else
525 ++answered_queries;
526
527 free(query);
528#else /* VBOX */
529
530 char *buf = NULL;
531 int byte = 0;
532 struct request *query = NULL;
533
534 AssertPtr(pData);
535
536 /* XXX: mbuf->data points to ??? */
537 byte = m->m_len;
538 buf = mtod(m, char *);
539
540 /* check for minimum dns packet length */
541 if (byte < 12) {
542 LogRel(("NAT: Answer too short\n"));
543 ++dropped_answers;
544 return;
545 }
546
547 /* find corresponding query (XXX: but see below) */
548 query = hash_find_request(pData, *((unsigned short *)buf));
549
550 if (query == NULL)
551 {
552 /* XXX: if we haven't found anything for this request ...
553 * What we are expecting later?
554 */
555 ++late_answers;
556 so->so_expire = curtime + SO_EXPIREFAST;
557 Log2(("NAT: query wasn't found\n"));
558 return;
559 }
560
561 /*
562 * XXX: The whole hash thing is pretty meaningless right now since
563 * we use a separate socket for each request, so we already know
564 * the answer.
565 *
566 * If the answer is not what we expect it to be, then it's
567 * probably a stray or malicious reply and we'd better not free a
568 * query owned by some other socket - that would cause
569 * use-after-free later on.
570 */
571 if (query != so->so_timeout_arg)
572 return;
573
574 so->so_timeout = NULL;
575 so->so_timeout_arg = NULL;
576
577 hash_remove_request(pData, query);
578
579 /* restore original query id */
580 memcpy(&buf[0], &query->clientid, 2);
581
582 ++answered_queries;
583
584 RTMemFree(query);
585#endif /* VBOX */
586}
587
588
589#ifdef VBOX
590int
591dnsproxy_init(PNATState pData)
592{
593 /* globals initialization */
594 authoritative_port = 53;
595 authoritative_timeout = 10;
596 recursive_port = 53;
597 recursive_timeout = 2;
598 stats_timeout = 3600;
599 dns_port = 53;
600 return 0;
601}
602#else /* !VBOX */
603/* main -- dnsproxy main function
604 */
605int
606main(int argc, char *argv[])
607{
608 int ch;
609 struct passwd *pw = NULL;
610 struct sockaddr_in addr;
611 struct event evq, eva;
612 const char *config = "/etc/dnsproxy.conf";
613 int daemonize = 0;
614
615 /* Process commandline arguments */
616 while ((ch = getopt(argc, argv, "c:dhV")) != -1) {
617 switch (ch) {
618 case 'c':
619 config = optarg;
620 break;
621 case 'd':
622 daemonize = 1;
623 break;
624 case 'V':
625 fprintf(stderr, PACKAGE_STRING "\n");
626 exit(0);
627 RT_FALL_THRU();
628 case 'h':
629 default:
630 fprintf(stderr,
631 "usage: dnsproxy [-c file] [-dhV]\n" \
632 "\t-c file Read configuration from file\n" \
633 "\t-d Detach and run as a daemon\n" \
634 "\t-h This help text\n" \
635 "\t-V Show version information\n");
636 exit(1);
637 }
638 }
639
640 /* Parse configuration and check required parameters */
641 if (!parse(config))
642 fatal("unable to parse configuration");
643
644 if (!authoritative || !recursive)
645 fatal("No authoritative or recursive server defined");
646
647 if (!listenat)
648 listenat = strdup("0.0.0.0");
649
650 /* Create and bind query socket */
651 if ((sock_query = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
652 fatal("unable to create socket: %s", strerror(errno));
653
654 memset(&addr, 0, sizeof(struct sockaddr_in));
655 addr.sin_addr.s_addr = inet_addr(listenat);
656 addr.sin_port = htons(port);
657 addr.sin_family = AF_INET;
658
659 if (bind(sock_query, (struct sockaddr *)&addr, sizeof(addr)) != 0)
660 fatal("unable to bind socket: %s", strerror(errno));
661
662 /* Create and bind answer socket */
663 if ((sock_answer = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
664 fatal("unable to create socket: %s", strerror(errno));
665
666 memset(&addr, 0, sizeof(struct sockaddr_in));
667 addr.sin_family = AF_INET;
668
669 if (bind(sock_answer, (struct sockaddr *)&addr, sizeof(addr)) != 0)
670 fatal("unable to bind socket: %s", strerror(errno));
671
672 /* Fill sockaddr_in structs for both servers */
673 memset(&authoritative_addr, 0, sizeof(struct sockaddr_in));
674 authoritative_addr.sin_addr.s_addr = inet_addr(authoritative);
675 authoritative_addr.sin_port = htons(authoritative_port);
676 authoritative_addr.sin_family = AF_INET;
677
678 memset(&recursive_addr, 0, sizeof(struct sockaddr_in));
679 recursive_addr.sin_addr.s_addr = inet_addr(recursive);
680 recursive_addr.sin_port = htons(recursive_port);
681 recursive_addr.sin_family = AF_INET;
682
683 /* Daemonize if requested and switch to syslog */
684 if (daemonize) {
685 if (daemon(0, 0) == -1)
686 fatal("unable to daemonize");
687 log_syslog("dnsproxy");
688 }
689
690 /* Find less privileged user */
691 if (user) {
692 pw = getpwnam(user);
693 if (!pw)
694 fatal("unable to find user %s", user);
695 }
696
697 /* Do a chroot if requested */
698 if (chrootdir) {
699 if (chdir(chrootdir) || chroot(chrootdir))
700 fatal("unable to chroot to %s", chrootdir);
701 chdir("/");
702 }
703
704 /* Drop privileges */
705 if (user) {
706 if (setgroups(1, &pw->pw_gid) < 0)
707 fatal("setgroups: %s", strerror(errno));
708#if defined(HAVE_SETRESGID)
709 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0)
710 fatal("setresgid: %s", strerror(errno));
711#elif defined(HAVE_SETREGID)
712 if (setregid(pw->pw_gid, pw->pw_gid) < 0)
713 fatal("setregid: %s", strerror(errno));
714#else
715 if (setegid(pw->pw_gid) < 0)
716 fatal("setegid: %s", strerror(errno));
717 if (setgid(pw->pw_gid) < 0)
718 fatal("setgid: %s", strerror(errno));
719#endif
720#if defined(HAVE_SETRESUID)
721 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0)
722 fatal("setresuid: %s", strerror(errno));
723#elif defined(HAVE_SETREUID)
724 if (setreuid(pw->pw_uid, pw->pw_uid) < 0)
725 fatal("setreuid: %s", strerror(errno));
726#else
727 if (seteuid(pw->pw_uid) < 0)
728 fatal("seteuid: %s", strerror(errno));
729 if (setuid(pw->pw_uid) < 0)
730 fatal("setuid: %s", strerror(errno));
731#endif
732 }
733
734 /* Init event handling */
735 event_init();
736
737 event_set(&evq, sock_query, EV_READ, do_query, &evq);
738 event_add(&evq, NULL);
739
740 event_set(&eva, sock_answer, EV_READ, do_answer, &eva);
741 event_add(&eva, NULL);
742
743 /* Zero counters and start statistics timer */
744 statistics_start();
745
746 /* Take care of signals */
747 if (signal(SIGINT, signal_handler) == SIG_ERR)
748 fatal("unable to mask signal SIGINT: %s", strerror(errno));
749
750 if (signal(SIGTERM, signal_handler) == SIG_ERR)
751 fatal("unable to mask signal SIGTERM: %s", strerror(errno));
752
753 if (signal(SIGHUP, SIG_IGN) == SIG_ERR)
754 fatal("unable to mask signal SIGHUP: %s", strerror(errno));
755
756 event_sigcb = signal_event;
757
758 /* Start libevent main loop */
759 event_dispatch();
760
761 return 0;
762
763}
764#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