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