Generally use NULL instead of explicitly casting 0 to some pointer type.
[dragonfly.git] / usr.sbin / faithd / faithd.c
1 /*      $KAME: faithd.c,v 1.46 2002/01/24 16:40:42 sumikawa Exp $       */
2
3 /*
4  * Copyright (C) 1997 and 1998 WIDE Project.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the project nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * $FreeBSD: src/usr.sbin/faithd/faithd.c,v 1.2.2.7 2002/04/28 08:01:39 ume Exp $
32  * $DragonFly: src/usr.sbin/faithd/faithd.c,v 1.5 2004/02/10 02:59:41 rob Exp $
33  */
34
35 /*
36  * User level translator from IPv6 to IPv4.
37  *
38  * Usage: faithd [<port> <progpath> <arg1(progname)> <arg2> ...]
39  *   e.g. faithd telnet /usr/libexec/telnetd telnetd
40  */
41
42 #include <sys/param.h>
43 #include <sys/types.h>
44 #include <sys/sysctl.h>
45 #include <sys/socket.h>
46 #include <sys/wait.h>
47 #include <sys/stat.h>
48 #include <sys/time.h>
49 #include <sys/ioctl.h>
50 #ifdef __DragonFly__
51 #include <libutil.h>
52 #endif
53
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <stdarg.h>
57 #include <string.h>
58 #include <syslog.h>
59 #include <unistd.h>
60 #include <errno.h>
61 #include <signal.h>
62 #include <fcntl.h>
63 #include <termios.h>
64
65 #include <net/if_types.h>
66 #ifdef IFT_FAITH
67 # define USE_ROUTE
68 # include <net/if.h>
69 # include <net/route.h>
70 # include <net/if_dl.h>
71 #endif
72
73 #include <netinet/in.h>
74 #include <arpa/inet.h>
75 #include <netdb.h>
76 #include <ifaddrs.h>
77
78 #ifdef FAITH4
79 #include <resolv.h>
80 #include <arpa/nameser.h>
81 #ifndef FAITH_NS
82 #define FAITH_NS "FAITH_NS"
83 #endif
84 #endif
85
86 #include "faithd.h"
87 #include "prefix.h"
88
89 char *serverpath = NULL;
90 char *serverarg[MAXARGV + 1];
91 static char *faithdname = NULL;
92 char logname[BUFSIZ];
93 char procname[BUFSIZ];
94 struct myaddrs {
95         struct myaddrs *next;
96         struct sockaddr *addr;
97 };
98 struct myaddrs *myaddrs = NULL;
99 static const char *service;
100 #ifdef USE_ROUTE
101 static int sockfd = 0;
102 #endif
103 int dflag = 0;
104 static int pflag = 0;
105 static int inetd = 0;
106 static char *configfile = NULL;
107
108 int main(int, char **);
109 static int inetd_main(int, char **);
110 static int daemon_main(int, char **);
111 static void play_service(int);
112 static void play_child(int, struct sockaddr *);
113 static int faith_prefix(struct sockaddr *);
114 static int map6to4(struct sockaddr_in6 *, struct sockaddr_in *);
115 #ifdef FAITH4
116 static int map4to6(struct sockaddr_in *, struct sockaddr_in6 *);
117 #endif
118 static void sig_child(int);
119 static void sig_terminate(int);
120 static void start_daemon(void);
121 static void exit_stderr(const char *, ...)
122         __attribute__((__format__(__printf__, 1, 2)));
123 static void grab_myaddrs(void);
124 static void free_myaddrs(void);
125 static void update_myaddrs(void);
126 static void usage(void);
127
128 int
129 main(int argc, char **argv)
130 {
131
132         /*
133          * Initializing stuff
134          */
135
136         faithdname = strrchr(argv[0], '/');
137         if (faithdname)
138                 faithdname++;
139         else
140                 faithdname = argv[0];
141
142         if (strcmp(faithdname, "faithd") != 0) {
143                 inetd = 1;
144                 return inetd_main(argc, argv);
145         } else
146                 return daemon_main(argc, argv);
147 }
148
149 static int
150 inetd_main(int argc, char **argv)
151 {
152         char path[MAXPATHLEN];
153         struct sockaddr_storage me;
154         struct sockaddr_storage from;
155         int melen, fromlen;
156         int i;
157         int error;
158         const int on = 1;
159         char sbuf[NI_MAXSERV], snum[NI_MAXSERV];
160
161         if (config_load(configfile) < 0 && configfile) {
162                 exit_failure("could not load config file");
163                 /*NOTREACHED*/
164         }
165
166         if (strrchr(argv[0], '/') == NULL)
167                 snprintf(path, sizeof(path), "%s/%s", DEFAULT_DIR, argv[0]);
168         else
169                 snprintf(path, sizeof(path), "%s", argv[0]);
170
171 #ifdef USE_ROUTE
172         grab_myaddrs();
173
174         sockfd = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC);
175         if (sockfd < 0) {
176                 exit_failure("socket(PF_ROUTE): %s", strerror(errno));
177                 /*NOTREACHED*/
178         }
179 #endif
180
181         melen = sizeof(me);
182         if (getsockname(STDIN_FILENO, (struct sockaddr *)&me, &melen) < 0) {
183                 exit_failure("getsockname: %s", strerror(errno));
184                 /*NOTREACHED*/
185         }
186         fromlen = sizeof(from);
187         if (getpeername(STDIN_FILENO, (struct sockaddr *)&from, &fromlen) < 0) {
188                 exit_failure("getpeername: %s", strerror(errno));
189                 /*NOTREACHED*/
190         }
191         if (getnameinfo((struct sockaddr *)&me, melen, NULL, 0,
192             sbuf, sizeof(sbuf), NI_NUMERICHOST) == 0)
193                 service = sbuf;
194         else
195                 service = DEFAULT_PORT_NAME;
196         if (getnameinfo((struct sockaddr *)&me, melen, NULL, 0,
197             snum, sizeof(snum), NI_NUMERICHOST) != 0)
198                 snprintf(snum, sizeof(snum), "?");
199
200         snprintf(logname, sizeof(logname), "faithd %s", snum);
201         snprintf(procname, sizeof(procname), "accepting port %s", snum);
202         openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
203
204         if (argc >= MAXARGV) {
205                 exit_failure("too many arguments");
206                 /*NOTREACHED*/
207         }
208         serverarg[0] = serverpath = path;
209         for (i = 1; i < argc; i++)
210                 serverarg[i] = argv[i];
211         serverarg[i] = NULL;
212
213         error = setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &on,
214             sizeof(on));
215         if (error < 0) {
216                 exit_failure("setsockopt(SO_OOBINLINE): %s", strerror(errno));
217                 /*NOTREACHED*/
218         }
219
220         play_child(STDIN_FILENO, (struct sockaddr *)&from);
221         exit_failure("should not reach here");
222         return 0;       /*dummy!*/
223 }
224
225 static int
226 daemon_main(int argc, char **argv)
227 {
228         struct addrinfo hints, *res;
229         int s_wld, error, i, serverargc, on = 1;
230         int family = AF_INET6;
231         int c;
232 #ifdef FAITH_NS
233         char *ns;
234 #endif /* FAITH_NS */
235
236         while ((c = getopt(argc, argv, "df:p46")) != -1) {
237                 switch (c) {
238                 case 'd':
239                         dflag++;
240                         break;
241                 case 'f':
242                         configfile = optarg;
243                         break;
244                 case 'p':
245                         pflag++;
246                         break;
247 #ifdef FAITH4
248                 case '4':
249                         family = AF_INET;
250                         break;
251                 case '6':
252                         family = AF_INET6;
253                         break;
254 #endif
255                 default:
256                         usage();
257                         /*NOTREACHED*/
258                 }
259         }
260         argc -= optind;
261         argv += optind;
262
263         if (config_load(configfile) < 0 && configfile) {
264                 exit_failure("could not load config file");
265                 /*NOTREACHED*/
266         }
267
268 #ifdef FAITH_NS
269         if ((ns = getenv(FAITH_NS)) != NULL) {
270                 struct sockaddr_storage ss;
271                 struct addrinfo hints, *res;
272                 char serv[NI_MAXSERV];
273
274                 memset(&ss, 0, sizeof(ss));
275                 memset(&hints, 0, sizeof(hints));
276                 snprintf(serv, sizeof(serv), "%u", NAMESERVER_PORT);
277                 hints.ai_flags = AI_NUMERICHOST;
278                 if (getaddrinfo(ns, serv, &hints, &res) ==  0) {
279                         res_init();
280                         memcpy(&_res_ext.nsaddr, res->ai_addr, res->ai_addrlen);
281                         _res.nscount = 1;
282                 }
283         }
284 #endif /* FAITH_NS */
285
286 #ifdef USE_ROUTE
287         grab_myaddrs();
288 #endif
289
290         switch (argc) {
291         case 0:
292                 usage();
293                 /*NOTREACHED*/
294         default:
295                 serverargc = argc - NUMARG;
296                 if (serverargc >= MAXARGV)
297                         exit_stderr("too many arguments");
298
299                 serverpath = malloc(strlen(argv[NUMPRG]) + 1);
300                 strcpy(serverpath, argv[NUMPRG]);
301                 for (i = 0; i < serverargc; i++) {
302                         serverarg[i] = malloc(strlen(argv[i + NUMARG]) + 1);
303                         strcpy(serverarg[i], argv[i + NUMARG]);
304                 }
305                 serverarg[i] = NULL;
306                 /* fall throuth */
307         case 1: /* no local service */
308                 service = argv[NUMPRT];
309                 break;
310         }
311
312         /*
313          * Opening wild card socket for this service.
314          */
315
316         memset(&hints, 0, sizeof(hints));
317         hints.ai_flags = AI_PASSIVE;
318         hints.ai_family = family;
319         hints.ai_socktype = SOCK_STREAM;
320         hints.ai_protocol = 0;
321         error = getaddrinfo(NULL, service, &hints, &res);
322         if (error)
323                 exit_failure("getaddrinfo: %s", gai_strerror(error));
324
325         s_wld = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
326         if (s_wld == -1)
327                 exit_failure("socket: %s", strerror(errno));
328
329 #ifdef IPV6_FAITH
330         if (res->ai_family == AF_INET6) {
331                 error = setsockopt(s_wld, IPPROTO_IPV6, IPV6_FAITH, &on, sizeof(on));
332                 if (error == -1)
333                         exit_failure("setsockopt(IPV6_FAITH): %s",
334                             strerror(errno));
335         }
336 #endif
337 #ifdef FAITH4
338 #ifdef IP_FAITH
339         if (res->ai_family == AF_INET) {
340                 error = setsockopt(s_wld, IPPROTO_IP, IP_FAITH, &on, sizeof(on));
341                 if (error == -1)
342                         exit_failure("setsockopt(IP_FAITH): %s",
343                             strerror(errno));
344         }
345 #endif
346 #endif /* FAITH4 */
347
348         error = setsockopt(s_wld, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
349         if (error == -1)
350                 exit_failure("setsockopt(SO_REUSEADDR): %s", strerror(errno));
351         
352         error = setsockopt(s_wld, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on));
353         if (error == -1)
354                 exit_failure("setsockopt(SO_OOBINLINE): %s", strerror(errno));
355
356         error = bind(s_wld, (struct sockaddr *)res->ai_addr, res->ai_addrlen);
357         if (error == -1)
358                 exit_failure("bind: %s", strerror(errno));
359
360         error = listen(s_wld, 5);
361         if (error == -1)
362                 exit_failure("listen: %s", strerror(errno));
363
364 #ifdef USE_ROUTE
365         sockfd = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC);
366         if (sockfd < 0) {
367                 exit_failure("socket(PF_ROUTE): %s", strerror(errno));
368                 /*NOTREACHED*/
369         }
370 #endif
371
372         /*
373          * Everything is OK.
374          */
375
376         start_daemon();
377
378         snprintf(logname, sizeof(logname), "faithd %s", service);
379         snprintf(procname, sizeof(procname), "accepting port %s", service);
380         openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
381         syslog(LOG_INFO, "Staring faith daemon for %s port", service);
382
383         play_service(s_wld);
384         /* NOTREACHED */
385         exit(1);        /*pacify gcc*/
386 }
387
388 static void
389 play_service(int s_wld)
390 {
391         struct sockaddr_storage srcaddr;
392         int len;
393         int s_src;
394         pid_t child_pid;
395         fd_set rfds;
396         int error;
397         int maxfd;
398
399         /*
400          * Wait, accept, fork, faith....
401          */
402 again:
403         setproctitle("%s", procname);
404
405         FD_ZERO(&rfds);
406         FD_SET(s_wld, &rfds);
407         maxfd = s_wld;
408 #ifdef USE_ROUTE
409         if (sockfd) {
410                 FD_SET(sockfd, &rfds);
411                 maxfd = (maxfd < sockfd) ? sockfd : maxfd;
412         }
413 #endif
414
415         error = select(maxfd + 1, &rfds, NULL, NULL, NULL);
416         if (error < 0) {
417                 if (errno == EINTR)
418                         goto again;
419                 exit_failure("select: %s", strerror(errno));
420                 /*NOTREACHED*/
421         }
422
423 #ifdef USE_ROUTE
424         if (FD_ISSET(sockfd, &rfds)) {
425                 update_myaddrs();
426         }
427 #endif
428         if (FD_ISSET(s_wld, &rfds)) {
429                 len = sizeof(srcaddr);
430                 s_src = accept(s_wld, (struct sockaddr *)&srcaddr,
431                         &len);
432                 if (s_src < 0) {
433                         if (errno == ECONNABORTED)
434                                 goto again;
435                         exit_failure("socket: %s", strerror(errno));
436                         /*NOTREACHED*/
437                 }
438
439                 child_pid = fork();
440                 
441                 if (child_pid == 0) {
442                         /* child process */
443                         close(s_wld);
444                         closelog();
445                         openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
446                         play_child(s_src, (struct sockaddr *)&srcaddr);
447                         exit_failure("should never reach here");
448                         /*NOTREACHED*/
449                 } else {
450                         /* parent process */
451                         close(s_src);
452                         if (child_pid == -1)
453                                 syslog(LOG_ERR, "can't fork");
454                 }
455         }
456         goto again;
457 }
458
459 static void
460 play_child(int s_src, struct sockaddr *srcaddr)
461 {
462         struct sockaddr_storage dstaddr6;
463         struct sockaddr_storage dstaddr4;
464         char src[NI_MAXHOST];
465         char dst6[NI_MAXHOST];
466         char dst4[NI_MAXHOST];
467         int len = sizeof(dstaddr6);
468         int s_dst, error, hport, nresvport, on = 1;
469         struct timeval tv;
470         struct sockaddr *sa4;
471         const struct config *conf;
472         
473         tv.tv_sec = 1;
474         tv.tv_usec = 0;
475
476         getnameinfo(srcaddr, srcaddr->sa_len,
477                 src, sizeof(src), NULL, 0, NI_NUMERICHOST);
478         syslog(LOG_INFO, "accepted a client from %s", src);
479
480         error = getsockname(s_src, (struct sockaddr *)&dstaddr6, &len);
481         if (error == -1) {
482                 exit_failure("getsockname: %s", strerror(errno));
483                 /*NOTREACHED*/
484         }
485
486         getnameinfo((struct sockaddr *)&dstaddr6, len,
487                 dst6, sizeof(dst6), NULL, 0, NI_NUMERICHOST);
488         syslog(LOG_INFO, "the client is connecting to %s", dst6);
489         
490         if (!faith_prefix((struct sockaddr *)&dstaddr6)) {
491                 if (serverpath) {
492                         /*
493                          * Local service
494                          */
495                         syslog(LOG_INFO, "executing local %s", serverpath);
496                         if (!inetd) {
497                                 dup2(s_src, 0);
498                                 close(s_src);
499                                 dup2(0, 1);
500                                 dup2(0, 2);
501                         }
502                         execv(serverpath, serverarg);
503                         syslog(LOG_ERR, "execv %s: %s", serverpath,
504                             strerror(errno));
505                         _exit(EXIT_FAILURE);
506                 } else {
507                         close(s_src);
508                         exit_success("no local service for %s", service);
509                 }
510         }
511
512         /*
513          * Act as a translator
514          */
515
516         switch (((struct sockaddr *)&dstaddr6)->sa_family) {
517         case AF_INET6:
518                 if (!map6to4((struct sockaddr_in6 *)&dstaddr6,
519                     (struct sockaddr_in *)&dstaddr4)) {
520                         close(s_src);
521                         exit_failure("map6to4 failed");
522                         /*NOTREACHED*/
523                 }
524                 syslog(LOG_INFO, "translating from v6 to v4");
525                 break;
526 #ifdef FAITH4
527         case AF_INET:
528                 if (!map4to6((struct sockaddr_in *)&dstaddr6,
529                     (struct sockaddr_in6 *)&dstaddr4)) {
530                         close(s_src);
531                         exit_failure("map4to6 failed");
532                         /*NOTREACHED*/
533                 }
534                 syslog(LOG_INFO, "translating from v4 to v6");
535                 break;
536 #endif
537         default:
538                 close(s_src);
539                 exit_failure("family not supported");
540                 /*NOTREACHED*/
541         }
542
543         sa4 = (struct sockaddr *)&dstaddr4;
544         getnameinfo(sa4, sa4->sa_len,
545                 dst4, sizeof(dst4), NULL, 0, NI_NUMERICHOST);
546
547         conf = config_match(srcaddr, sa4);
548         if (!conf || !conf->permit) {
549                 close(s_src);
550                 if (conf) {
551                         exit_failure("translation to %s not permitted for %s",
552                             dst4, prefix_string(&conf->match));
553                         /*NOTREACHED*/
554                 } else {
555                         exit_failure("translation to %s not permitted", dst4);
556                         /*NOTREACHED*/
557                 }
558         }
559
560         syslog(LOG_INFO, "the translator is connecting to %s", dst4);
561
562         setproctitle("port %s, %s -> %s", service, src, dst4);
563
564         if (sa4->sa_family == AF_INET6)
565                 hport = ntohs(((struct sockaddr_in6 *)&dstaddr4)->sin6_port);
566         else /* AF_INET */
567                 hport = ntohs(((struct sockaddr_in *)&dstaddr4)->sin_port);
568
569         switch (hport) {
570         case RLOGIN_PORT:
571         case RSH_PORT:
572                 s_dst = rresvport_af(&nresvport, sa4->sa_family);
573                 break;
574         default:
575                 if (pflag)
576                         s_dst = rresvport_af(&nresvport, sa4->sa_family);
577                 else
578                         s_dst = socket(sa4->sa_family, SOCK_STREAM, 0);
579                 break;
580         }
581         if (s_dst < 0) {
582                 exit_failure("socket: %s", strerror(errno));
583                 /*NOTREACHED*/
584         }
585
586         if (conf->src.a.ss_family) {
587                 if (bind(s_dst, (const struct sockaddr *)&conf->src.a,
588                     conf->src.a.ss_len) < 0) {
589                         exit_failure("bind: %s", strerror(errno));
590                         /*NOTREACHED*/
591                 }
592         }
593
594         error = setsockopt(s_dst, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on));
595         if (error < 0) {
596                 exit_failure("setsockopt(SO_OOBINLINE): %s", strerror(errno));
597                 /*NOTREACHED*/
598         }
599
600         error = setsockopt(s_src, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
601         if (error < 0) {
602                 exit_failure("setsockopt(SO_SNDTIMEO): %s", strerror(errno));
603                 /*NOTREACHED*/
604         }
605         error = setsockopt(s_dst, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
606         if (error < 0) {
607                 exit_failure("setsockopt(SO_SNDTIMEO): %s", strerror(errno));
608                 /*NOTREACHED*/
609         }
610
611         error = connect(s_dst, sa4, sa4->sa_len);
612         if (error < 0) {
613                 exit_failure("connect: %s", strerror(errno));
614                 /*NOTREACHED*/
615         }
616
617         switch (hport) {
618         case FTP_PORT:
619                 ftp_relay(s_src, s_dst);
620                 break;
621         case RSH_PORT:
622                 syslog(LOG_WARNING,
623                     "WARINNG: it is insecure to relay rsh port");
624                 rsh_relay(s_src, s_dst);
625                 break;
626         case RLOGIN_PORT:
627                 syslog(LOG_WARNING,
628                     "WARINNG: it is insecure to relay rlogin port");
629                 /*FALLTHROUGH*/
630         default:
631                 tcp_relay(s_src, s_dst, service);
632                 break;
633         }
634
635         /* NOTREACHED */
636 }
637
638 /* 0: non faith, 1: faith */
639 static int
640 faith_prefix(struct sockaddr *dst)
641 {
642 #ifndef USE_ROUTE
643         int mib[4], size;
644         struct in6_addr faith_prefix;
645         struct sockaddr_in6 *dst6 = (struct sockaddr_in *)dst;
646
647         if (dst->sa_family != AF_INET6)
648                 return 0;
649
650         mib[0] = CTL_NET;
651         mib[1] = PF_INET6;
652         mib[2] = IPPROTO_IPV6;
653         mib[3] = IPV6CTL_FAITH_PREFIX;
654         size = sizeof(struct in6_addr);
655         if (sysctl(mib, 4, &faith_prefix, &size, NULL, 0) < 0) {
656                 exit_failure("sysctl: %s", strerror(errno));
657                 /*NOTREACHED*/
658         }
659
660         if (memcmp(dst, &faith_prefix,
661                         sizeof(struct in6_addr) - sizeof(struct in_addr) == 0) {
662                 return 1;
663         }
664         return 0;
665 #else
666         struct myaddrs *p;
667         struct sockaddr_in6 *sin6;
668         struct sockaddr_in *sin4;
669         struct sockaddr_in6 *dst6;
670         struct sockaddr_in *dst4;
671         struct sockaddr_in dstmap;
672
673         dst6 = (struct sockaddr_in6 *)dst;
674         if (dst->sa_family == AF_INET6
675          && IN6_IS_ADDR_V4MAPPED(&dst6->sin6_addr)) {
676                 /* ugly... */
677                 memset(&dstmap, 0, sizeof(dstmap));
678                 dstmap.sin_family = AF_INET;
679                 dstmap.sin_len = sizeof(dstmap);
680                 memcpy(&dstmap.sin_addr, &dst6->sin6_addr.s6_addr[12],
681                         sizeof(dstmap.sin_addr));
682                 dst = (struct sockaddr *)&dstmap;
683         }
684
685         dst6 = (struct sockaddr_in6 *)dst;
686         dst4 = (struct sockaddr_in *)dst;
687
688         for (p = myaddrs; p; p = p->next) {
689                 sin6 = (struct sockaddr_in6 *)p->addr;
690                 sin4 = (struct sockaddr_in *)p->addr;
691
692                 if (p->addr->sa_len != dst->sa_len
693                  || p->addr->sa_family != dst->sa_family)
694                         continue;
695
696                 switch (dst->sa_family) {
697                 case AF_INET6:
698                         if (sin6->sin6_scope_id == dst6->sin6_scope_id
699                          && IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &dst6->sin6_addr))
700                                 return 0;
701                         break;
702                 case AF_INET:
703                         if (sin4->sin_addr.s_addr == dst4->sin_addr.s_addr)
704                                 return 0;
705                         break;
706                 }
707         }
708         return 1;
709 #endif
710 }
711
712 /* 0: non faith, 1: faith */
713 static int
714 map6to4(struct sockaddr_in6 *dst6, struct sockaddr_in *dst4)
715 {
716         memset(dst4, 0, sizeof(*dst4));
717         dst4->sin_len = sizeof(*dst4);
718         dst4->sin_family = AF_INET;
719         dst4->sin_port = dst6->sin6_port;
720         memcpy(&dst4->sin_addr, &dst6->sin6_addr.s6_addr[12],
721                 sizeof(dst4->sin_addr));
722
723         if (dst4->sin_addr.s_addr == INADDR_ANY
724          || dst4->sin_addr.s_addr == INADDR_BROADCAST
725          || IN_MULTICAST(ntohl(dst4->sin_addr.s_addr)))
726                 return 0;
727
728         return 1;
729 }
730
731 #ifdef FAITH4
732 /* 0: non faith, 1: faith */
733 static int
734 map4to6(struct sockaddr_in *dst4, struct sockaddr_in6 *dst6)
735 {
736         char host[NI_MAXHOST];
737         char serv[NI_MAXSERV];
738         struct addrinfo hints, *res;
739         int ai_errno;
740
741         if (getnameinfo((struct sockaddr *)dst4, dst4->sin_len, host, sizeof(host),
742                         serv, sizeof(serv), NI_NAMEREQD|NI_NUMERICSERV) != 0)
743                 return 0;
744
745         memset(&hints, 0, sizeof(hints));
746         hints.ai_flags = 0;
747         hints.ai_family = AF_INET6;
748         hints.ai_socktype = SOCK_STREAM;
749         hints.ai_protocol = 0;
750
751         if ((ai_errno = getaddrinfo(host, serv, &hints, &res)) != 0) {
752                 syslog(LOG_INFO, "%s %s: %s", host, serv,
753                     gai_strerror(ai_errno));
754                 return 0;
755         }
756
757         memcpy(dst6, res->ai_addr, res->ai_addrlen);
758
759         freeaddrinfo(res);
760
761         return 1;
762 }
763 #endif /* FAITH4 */
764
765 static void
766 sig_child(int sig)
767 {
768         int status;
769         pid_t pid;
770
771         pid = wait3(&status, WNOHANG, NULL);
772         if (pid && WEXITSTATUS(status))
773                 syslog(LOG_WARNING, "child %d exit status 0x%x", pid, status);
774 }
775
776 void
777 sig_terminate(int sig)
778 {
779         syslog(LOG_INFO, "Terminating faith daemon");   
780         exit(EXIT_SUCCESS);
781 }
782
783 static void
784 start_daemon(void)
785 {
786 #ifdef SA_NOCLDWAIT
787         struct sigaction sa;
788 #endif
789
790         if (daemon(0, 0) == -1)
791                 exit_stderr("daemon: %s", strerror(errno));
792
793 #ifdef SA_NOCLDWAIT
794         memset(&sa, 0, sizeof(sa));
795         sa.sa_handler = sig_child;
796         sa.sa_flags = SA_NOCLDWAIT;
797         sigemptyset(&sa.sa_mask);
798         sigaction(SIGCHLD, &sa, NULL);
799 #else
800         if (signal(SIGCHLD, sig_child) == SIG_ERR) {
801                 exit_failure("signal CHLD: %s", strerror(errno));
802                 /*NOTREACHED*/
803         }
804 #endif
805
806         if (signal(SIGTERM, sig_terminate) == SIG_ERR) {
807                 exit_failure("signal TERM: %s", strerror(errno));
808                 /*NOTREACHED*/
809         }
810 }
811
812 static void
813 exit_stderr(const char *fmt, ...)
814 {
815         va_list ap;
816         char buf[BUFSIZ];
817
818         va_start(ap, fmt);
819         vsnprintf(buf, sizeof(buf), fmt, ap);
820         va_end(ap);
821         fprintf(stderr, "%s\n", buf);
822         exit(EXIT_FAILURE);
823 }
824
825 void
826 exit_failure(const char *fmt, ...)
827 {
828         va_list ap;
829         char buf[BUFSIZ];
830
831         va_start(ap, fmt);
832         vsnprintf(buf, sizeof(buf), fmt, ap);
833         va_end(ap);
834         syslog(LOG_ERR, "%s", buf);
835         exit(EXIT_FAILURE);
836 }
837
838 void
839 exit_success(const char *fmt, ...)
840 {
841         va_list ap;
842         char buf[BUFSIZ];
843
844         va_start(ap, fmt);
845         vsnprintf(buf, sizeof(buf), fmt, ap);
846         va_end(ap);
847         syslog(LOG_INFO, "%s", buf);
848         exit(EXIT_SUCCESS);
849 }
850
851 #ifdef USE_ROUTE
852 static void
853 grab_myaddrs(void)
854 {
855         struct ifaddrs *ifap, *ifa;
856         struct myaddrs *p;
857         struct sockaddr_in6 *sin6;
858
859         if (getifaddrs(&ifap) != 0) {
860                 exit_failure("getifaddrs");
861                 /*NOTREACHED*/
862         }
863
864         for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
865                 switch (ifa->ifa_addr->sa_family) {
866                 case AF_INET:
867                 case AF_INET6:
868                         break;
869                 default:
870                         continue;
871                 }
872
873                 p = (struct myaddrs *)malloc(sizeof(struct myaddrs) +
874                     ifa->ifa_addr->sa_len);
875                 if (!p) {
876                         exit_failure("not enough core");
877                         /*NOTREACHED*/
878                 }
879                 memcpy(p + 1, ifa->ifa_addr, ifa->ifa_addr->sa_len);
880                 p->next = myaddrs;
881                 p->addr = (struct sockaddr *)(p + 1);
882 #ifdef __KAME__
883                 if (ifa->ifa_addr->sa_family == AF_INET6) {
884                         sin6 = (struct sockaddr_in6 *)p->addr;
885                         if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)
886                          || IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) {
887                                 sin6->sin6_scope_id =
888                                         ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
889                                 sin6->sin6_addr.s6_addr[2] = 0;
890                                 sin6->sin6_addr.s6_addr[3] = 0;
891                         }
892                 }
893 #endif
894                 myaddrs = p;
895                 if (dflag) {
896                         char hbuf[NI_MAXHOST];
897                         getnameinfo(p->addr, p->addr->sa_len,
898                                 hbuf, sizeof(hbuf), NULL, 0,
899                                 NI_NUMERICHOST);
900                         syslog(LOG_INFO, "my interface: %s %s", hbuf,
901                             ifa->ifa_name);
902                 }
903         }
904
905         freeifaddrs(ifap);
906 }
907
908 static void
909 free_myaddrs(void)
910 {
911         struct myaddrs *p, *q;
912
913         p = myaddrs;
914         while (p) {
915                 q = p->next;
916                 free(p);
917                 p = q;
918         }
919         myaddrs = NULL;
920 }
921
922 static void
923 update_myaddrs(void)
924 {
925         char msg[BUFSIZ];
926         int len;
927         struct rt_msghdr *rtm;
928
929         len = read(sockfd, msg, sizeof(msg));
930         if (len < 0) {
931                 syslog(LOG_ERR, "read(PF_ROUTE) failed");
932                 return;
933         }
934         rtm = (struct rt_msghdr *)msg;
935         if (len < 4 || len < rtm->rtm_msglen) {
936                 syslog(LOG_ERR, "read(PF_ROUTE) short read");
937                 return;
938         }
939         if (rtm->rtm_version != RTM_VERSION) {
940                 syslog(LOG_ERR, "routing socket version mismatch");
941                 close(sockfd);
942                 sockfd = 0;
943                 return;
944         }
945         switch (rtm->rtm_type) {
946         case RTM_NEWADDR:
947         case RTM_DELADDR:
948         case RTM_IFINFO:
949                 break;
950         default:
951                 return;
952         }
953         /* XXX more filters here? */
954
955         syslog(LOG_INFO, "update interface address list");
956         free_myaddrs();
957         grab_myaddrs();
958 }
959 #endif /*USE_ROUTE*/
960
961 static void
962 usage(void)
963 {
964         fprintf(stderr, "usage: %s [-dp] [-f conf] service [serverpath [serverargs]]\n",
965                 faithdname);
966         exit(0);
967 }