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