1122679Sume/*	$KAME: faithd.c,v 1.67 2003/10/16 05:26:21 itojun Exp $	*/
262655Skris
356668Sshin/*
456668Sshin * Copyright (C) 1997 and 1998 WIDE Project.
556668Sshin * All rights reserved.
662655Skris *
756668Sshin * Redistribution and use in source and binary forms, with or without
856668Sshin * modification, are permitted provided that the following conditions
956668Sshin * are met:
1056668Sshin * 1. Redistributions of source code must retain the above copyright
1156668Sshin *    notice, this list of conditions and the following disclaimer.
1256668Sshin * 2. Redistributions in binary form must reproduce the above copyright
1356668Sshin *    notice, this list of conditions and the following disclaimer in the
1456668Sshin *    documentation and/or other materials provided with the distribution.
1556668Sshin * 3. Neither the name of the project nor the names of its contributors
1656668Sshin *    may be used to endorse or promote products derived from this software
1756668Sshin *    without specific prior written permission.
1862655Skris *
1956668Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2056668Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2156668Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2256668Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2356668Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2456668Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2556668Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2656668Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2756668Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2856668Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2956668Sshin * SUCH DAMAGE.
3056668Sshin */
3156668Sshin
3256668Sshin/*
3356668Sshin * User level translator from IPv6 to IPv4.
3456668Sshin *
3556668Sshin * Usage: faithd [<port> <progpath> <arg1(progname)> <arg2> ...]
3695023Ssuz *   e.g. faithd telnet /usr/libexec/telnetd telnetd
3756668Sshin */
3856668Sshin
39173298Scharnier#include <sys/cdefs.h>
40173298Scharnier__FBSDID("$FreeBSD$");
41173298Scharnier
4256668Sshin#include <sys/param.h>
4356668Sshin#include <sys/types.h>
4456668Sshin#include <sys/sysctl.h>
4556668Sshin#include <sys/socket.h>
4656668Sshin#include <sys/wait.h>
4756668Sshin#include <sys/stat.h>
4856668Sshin#include <sys/time.h>
4956668Sshin#include <sys/ioctl.h>
5078064Sume#include <libutil.h>
51122679Sume
52122679Sume#ifdef HAVE_POLL_H
53122679Sume#include <poll.h>
5478064Sume#endif
5556668Sshin#include <stdio.h>
5656668Sshin#include <stdlib.h>
5756668Sshin#include <stdarg.h>
5856668Sshin#include <string.h>
5956668Sshin#include <syslog.h>
6056668Sshin#include <unistd.h>
6156668Sshin#include <errno.h>
6256668Sshin#include <signal.h>
6356668Sshin#include <fcntl.h>
6456668Sshin#include <termios.h>
6556668Sshin
6656668Sshin#include <net/if_types.h>
6756668Sshin#ifdef IFT_FAITH
6856668Sshin# define USE_ROUTE
6956668Sshin# include <net/if.h>
7056668Sshin# include <net/route.h>
7156668Sshin# include <net/if_dl.h>
7256668Sshin#endif
7356668Sshin
7456668Sshin#include <netinet/in.h>
7556668Sshin#include <arpa/inet.h>
7656668Sshin#include <netdb.h>
7762655Skris#include <ifaddrs.h>
7856668Sshin
7956668Sshin#include "faithd.h"
8078064Sume#include "prefix.h"
8156668Sshin
8256668Sshinchar *serverpath = NULL;
8356668Sshinchar *serverarg[MAXARGV + 1];
8456668Sshinstatic char *faithdname = NULL;
8556668Sshinchar logname[BUFSIZ];
8656668Sshinchar procname[BUFSIZ];
87122679Sume
8856668Sshinstruct myaddrs {
8956668Sshin	struct myaddrs *next;
9056668Sshin	struct sockaddr *addr;
9156668Sshin};
9256668Sshinstruct myaddrs *myaddrs = NULL;
93122679Sume
9495023Ssuzstatic const char *service;
9556668Sshin#ifdef USE_ROUTE
9656668Sshinstatic int sockfd = 0;
9756668Sshin#endif
9856668Sshinint dflag = 0;
9956668Sshinstatic int pflag = 0;
10062655Skrisstatic int inetd = 0;
10178064Sumestatic char *configfile = NULL;
10256668Sshin
103173412Skevloint main(int, char **);
104173412Skevlostatic int inetd_main(int, char **);
105173412Skevlostatic int daemon_main(int, char **);
106173412Skevlostatic void play_service(int);
107173412Skevlostatic void play_child(int, struct sockaddr *);
108173412Skevlostatic int faith_prefix(struct sockaddr *);
109173412Skevlostatic int map6to4(struct sockaddr_in6 *, struct sockaddr_in *);
110173412Skevlostatic void sig_child(int);
111173412Skevlostatic void sig_terminate(int);
112173412Skevlostatic void start_daemon(void);
113173412Skevlostatic void exit_stderr(const char *, ...)
11478064Sume	__attribute__((__format__(__printf__, 1, 2)));
115173412Skevlostatic void grab_myaddrs(void);
116173412Skevlostatic void free_myaddrs(void);
117173412Skevlostatic void update_myaddrs(void);
118173412Skevlostatic void usage(void);
11956668Sshin
12056668Sshinint
12162655Skrismain(int argc, char **argv)
12256668Sshin{
12356668Sshin
12456668Sshin	/*
12556668Sshin	 * Initializing stuff
12656668Sshin	 */
12756668Sshin
12856668Sshin	faithdname = strrchr(argv[0], '/');
12956668Sshin	if (faithdname)
13056668Sshin		faithdname++;
13156668Sshin	else
13256668Sshin		faithdname = argv[0];
13356668Sshin
13462655Skris	if (strcmp(faithdname, "faithd") != 0) {
13562655Skris		inetd = 1;
13662655Skris		return inetd_main(argc, argv);
13762655Skris	} else
13862655Skris		return daemon_main(argc, argv);
13962655Skris}
14062655Skris
14162655Skrisstatic int
14262655Skrisinetd_main(int argc, char **argv)
14362655Skris{
14462655Skris	char path[MAXPATHLEN];
14562655Skris	struct sockaddr_storage me;
14662655Skris	struct sockaddr_storage from;
147122679Sume	socklen_t melen, fromlen;
14862655Skris	int i;
14962655Skris	int error;
15062655Skris	const int on = 1;
15162655Skris	char sbuf[NI_MAXSERV], snum[NI_MAXSERV];
15262655Skris
15378064Sume	if (config_load(configfile) < 0 && configfile) {
15478064Sume		exit_failure("could not load config file");
15578064Sume		/*NOTREACHED*/
15678064Sume	}
15778064Sume
15862655Skris	if (strrchr(argv[0], '/') == NULL)
15962655Skris		snprintf(path, sizeof(path), "%s/%s", DEFAULT_DIR, argv[0]);
16062655Skris	else
16162655Skris		snprintf(path, sizeof(path), "%s", argv[0]);
16262655Skris
16362655Skris#ifdef USE_ROUTE
16462655Skris	grab_myaddrs();
16562655Skris
16662655Skris	sockfd = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC);
16762655Skris	if (sockfd < 0) {
16895023Ssuz		exit_failure("socket(PF_ROUTE): %s", strerror(errno));
16962655Skris		/*NOTREACHED*/
17062655Skris	}
17162655Skris#endif
17262655Skris
17362655Skris	melen = sizeof(me);
17478064Sume	if (getsockname(STDIN_FILENO, (struct sockaddr *)&me, &melen) < 0) {
17595023Ssuz		exit_failure("getsockname: %s", strerror(errno));
17678064Sume		/*NOTREACHED*/
17778064Sume	}
17862655Skris	fromlen = sizeof(from);
17978064Sume	if (getpeername(STDIN_FILENO, (struct sockaddr *)&from, &fromlen) < 0) {
18095023Ssuz		exit_failure("getpeername: %s", strerror(errno));
18178064Sume		/*NOTREACHED*/
18278064Sume	}
18362655Skris	if (getnameinfo((struct sockaddr *)&me, melen, NULL, 0,
18462655Skris	    sbuf, sizeof(sbuf), NI_NUMERICHOST) == 0)
18562655Skris		service = sbuf;
18662655Skris	else
18762655Skris		service = DEFAULT_PORT_NAME;
18862655Skris	if (getnameinfo((struct sockaddr *)&me, melen, NULL, 0,
18962655Skris	    snum, sizeof(snum), NI_NUMERICHOST) != 0)
19062655Skris		snprintf(snum, sizeof(snum), "?");
19162655Skris
19262655Skris	snprintf(logname, sizeof(logname), "faithd %s", snum);
19362655Skris	snprintf(procname, sizeof(procname), "accepting port %s", snum);
19462655Skris	openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
19562655Skris
19678064Sume	if (argc >= MAXARGV) {
19769312Scharnier		exit_failure("too many arguments");
19878064Sume		/*NOTREACHED*/
19978064Sume	}
20062655Skris	serverarg[0] = serverpath = path;
20162655Skris	for (i = 1; i < argc; i++)
20262655Skris		serverarg[i] = argv[i];
20362655Skris	serverarg[i] = NULL;
20462655Skris
20562655Skris	error = setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &on,
20662655Skris	    sizeof(on));
20778064Sume	if (error < 0) {
20895023Ssuz		exit_failure("setsockopt(SO_OOBINLINE): %s", strerror(errno));
20978064Sume		/*NOTREACHED*/
21078064Sume	}
21162655Skris
21262655Skris	play_child(STDIN_FILENO, (struct sockaddr *)&from);
21362655Skris	exit_failure("should not reach here");
21462655Skris	return 0;	/*dummy!*/
21562655Skris}
21662655Skris
21762655Skrisstatic int
21862655Skrisdaemon_main(int argc, char **argv)
21962655Skris{
22062655Skris	struct addrinfo hints, *res;
22162655Skris	int s_wld, error, i, serverargc, on = 1;
22262655Skris	int family = AF_INET6;
22362655Skris	int c;
22462655Skris
225122679Sume	while ((c = getopt(argc, argv, "df:p")) != -1) {
22656668Sshin		switch (c) {
22756668Sshin		case 'd':
22856668Sshin			dflag++;
22956668Sshin			break;
23078064Sume		case 'f':
23178064Sume			configfile = optarg;
23278064Sume			break;
23356668Sshin		case 'p':
23456668Sshin			pflag++;
23556668Sshin			break;
23656668Sshin		default:
23756668Sshin			usage();
23878064Sume			/*NOTREACHED*/
23956668Sshin		}
24056668Sshin	}
24156668Sshin	argc -= optind;
24256668Sshin	argv += optind;
24356668Sshin
24478064Sume	if (config_load(configfile) < 0 && configfile) {
24578064Sume		exit_failure("could not load config file");
24678064Sume		/*NOTREACHED*/
24778064Sume	}
24878064Sume
24956668Sshin
25056668Sshin#ifdef USE_ROUTE
25156668Sshin	grab_myaddrs();
25256668Sshin#endif
25356668Sshin
25456668Sshin	switch (argc) {
25556668Sshin	case 0:
25678064Sume		usage();
25778064Sume		/*NOTREACHED*/
25856668Sshin	default:
25956668Sshin		serverargc = argc - NUMARG;
26062655Skris		if (serverargc >= MAXARGV)
26178064Sume			exit_stderr("too many arguments");
26256668Sshin
263122679Sume		serverpath = strdup(argv[NUMPRG]);
264122679Sume		if (!serverpath)
265122679Sume			exit_stderr("not enough core");
26656668Sshin		for (i = 0; i < serverargc; i++) {
267122679Sume			serverarg[i] = strdup(argv[i + NUMARG]);
268122679Sume			if (!serverarg[i])
269122679Sume				exit_stderr("not enough core");
27056668Sshin		}
27156668Sshin		serverarg[i] = NULL;
27262655Skris		/* fall throuth */
27356668Sshin	case 1:	/* no local service */
27456668Sshin		service = argv[NUMPRT];
27556668Sshin		break;
27656668Sshin	}
27756668Sshin
278122679Sume	start_daemon();
279122679Sume
28056668Sshin	/*
28156668Sshin	 * Opening wild card socket for this service.
28256668Sshin	 */
28356668Sshin
28456668Sshin	memset(&hints, 0, sizeof(hints));
28556668Sshin	hints.ai_flags = AI_PASSIVE;
28656668Sshin	hints.ai_family = family;
28756668Sshin	hints.ai_socktype = SOCK_STREAM;
288122679Sume	hints.ai_protocol = IPPROTO_TCP;	/* SCTP? */
28956668Sshin	error = getaddrinfo(NULL, service, &hints, &res);
29062655Skris	if (error)
29195023Ssuz		exit_failure("getaddrinfo: %s", gai_strerror(error));
29256668Sshin
29356668Sshin	s_wld = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
29456668Sshin	if (s_wld == -1)
29595023Ssuz		exit_failure("socket: %s", strerror(errno));
29656668Sshin
29756668Sshin#ifdef IPV6_FAITH
29856668Sshin	if (res->ai_family == AF_INET6) {
29956668Sshin		error = setsockopt(s_wld, IPPROTO_IPV6, IPV6_FAITH, &on, sizeof(on));
30056668Sshin		if (error == -1)
30195023Ssuz			exit_failure("setsockopt(IPV6_FAITH): %s",
30295023Ssuz			    strerror(errno));
30356668Sshin	}
30456668Sshin#endif
30556668Sshin
30656668Sshin	error = setsockopt(s_wld, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
30756668Sshin	if (error == -1)
30895023Ssuz		exit_failure("setsockopt(SO_REUSEADDR): %s", strerror(errno));
30956668Sshin
31056668Sshin	error = setsockopt(s_wld, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on));
31156668Sshin	if (error == -1)
31295023Ssuz		exit_failure("setsockopt(SO_OOBINLINE): %s", strerror(errno));
31356668Sshin
314122679Sume#ifdef IPV6_V6ONLY
315122679Sume	error = setsockopt(s_wld, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
316122679Sume	if (error == -1)
317122679Sume		exit_failure("setsockopt(IPV6_V6ONLY): %s", strerror(errno));
318122679Sume#endif
319122679Sume
32056668Sshin	error = bind(s_wld, (struct sockaddr *)res->ai_addr, res->ai_addrlen);
32156668Sshin	if (error == -1)
32295023Ssuz		exit_failure("bind: %s", strerror(errno));
32356668Sshin
32456668Sshin	error = listen(s_wld, 5);
32556668Sshin	if (error == -1)
32695023Ssuz		exit_failure("listen: %s", strerror(errno));
32756668Sshin
32856668Sshin#ifdef USE_ROUTE
32956668Sshin	sockfd = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC);
33056668Sshin	if (sockfd < 0) {
33195023Ssuz		exit_failure("socket(PF_ROUTE): %s", strerror(errno));
33256668Sshin		/*NOTREACHED*/
33356668Sshin	}
33456668Sshin#endif
33556668Sshin
33656668Sshin	/*
33756668Sshin	 * Everything is OK.
33856668Sshin	 */
33956668Sshin
34056668Sshin	snprintf(logname, sizeof(logname), "faithd %s", service);
34156668Sshin	snprintf(procname, sizeof(procname), "accepting port %s", service);
34256668Sshin	openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
343219158Sdelphij	syslog(LOG_INFO, "Starting faith daemon for %s port", service);
34456668Sshin
34556668Sshin	play_service(s_wld);
34678064Sume	/* NOTREACHED */
34756668Sshin	exit(1);	/*pacify gcc*/
34856668Sshin}
34956668Sshin
35056668Sshinstatic void
35156668Sshinplay_service(int s_wld)
35256668Sshin{
35356668Sshin	struct sockaddr_storage srcaddr;
354122679Sume	socklen_t len;
35556668Sshin	int s_src;
35656668Sshin	pid_t child_pid;
357122679Sume#ifdef HAVE_POLL_H
358122679Sume	struct pollfd pfd[2];
359122679Sume#else
36056668Sshin	fd_set rfds;
361122679Sume	int maxfd;
362122679Sume#endif
36356668Sshin	int error;
36456668Sshin
36556668Sshin	/*
36656668Sshin	 * Wait, accept, fork, faith....
36756668Sshin	 */
36856668Sshinagain:
36962683Sbrian	setproctitle("%s", procname);
37056668Sshin
371122679Sume#ifdef HAVE_POLL_H
372122679Sume	pfd[0].fd = s_wld;
373122679Sume	pfd[0].events = POLLIN;
374122679Sume	pfd[1].fd = -1;
375122679Sume	pfd[1].revents = 0;
376122679Sume#else
37756668Sshin	FD_ZERO(&rfds);
378122679Sume	if (s_wld >= FD_SETSIZE)
379122679Sume		exit_failure("descriptor too big");
38056668Sshin	FD_SET(s_wld, &rfds);
38156668Sshin	maxfd = s_wld;
382122679Sume#endif
38356668Sshin#ifdef USE_ROUTE
38456668Sshin	if (sockfd) {
385122679Sume#ifdef HAVE_POLL_H
386122679Sume		pfd[1].fd = sockfd;
387122679Sume		pfd[1].events = POLLIN;
388122679Sume#else
389122679Sume		if (sockfd >= FD_SETSIZE)
390122679Sume			exit_failure("descriptor too big");
39156668Sshin		FD_SET(sockfd, &rfds);
39256668Sshin		maxfd = (maxfd < sockfd) ? sockfd : maxfd;
393122679Sume#endif
39456668Sshin	}
39556668Sshin#endif
39656668Sshin
397122679Sume#ifdef HAVE_POLL_H
398122679Sume	error = poll(pfd, sizeof(pfd)/sizeof(pfd[0]), INFTIM);
399122679Sume#else
40056668Sshin	error = select(maxfd + 1, &rfds, NULL, NULL, NULL);
401122679Sume#endif
40256668Sshin	if (error < 0) {
40356668Sshin		if (errno == EINTR)
40456668Sshin			goto again;
40595023Ssuz		exit_failure("select: %s", strerror(errno));
40656668Sshin		/*NOTREACHED*/
40756668Sshin	}
40856668Sshin
40956668Sshin#ifdef USE_ROUTE
410122679Sume#ifdef HAVE_POLL_H
411122679Sume	if (pfd[1].revents & POLLIN)
412122679Sume#else
413122679Sume	if (FD_ISSET(sockfd, &rfds))
414122679Sume#endif
415122679Sume	{
41656668Sshin		update_myaddrs();
41756668Sshin	}
41856668Sshin#endif
419122679Sume#ifdef HAVE_POLL_H
420122679Sume	if (pfd[0].revents & POLLIN)
421122679Sume#else
422122679Sume	if (FD_ISSET(s_wld, &rfds))
423122679Sume#endif
424122679Sume	{
42556668Sshin		len = sizeof(srcaddr);
426122679Sume		s_src = accept(s_wld, (struct sockaddr *)&srcaddr, &len);
42795359Sume		if (s_src < 0) {
42895359Sume			if (errno == ECONNABORTED)
42995359Sume				goto again;
43095023Ssuz			exit_failure("socket: %s", strerror(errno));
43178064Sume			/*NOTREACHED*/
43278064Sume		}
433122679Sume		if (srcaddr.ss_family == AF_INET6 &&
434122679Sume		    IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&srcaddr)->sin6_addr)) {
435122679Sume			close(s_src);
436122679Sume			syslog(LOG_ERR, "connection from IPv4 mapped address?");
437122679Sume			goto again;
438122679Sume		}
43956668Sshin
44056668Sshin		child_pid = fork();
441122679Sume
44256668Sshin		if (child_pid == 0) {
44356668Sshin			/* child process */
44456668Sshin			close(s_wld);
44556668Sshin			closelog();
44656668Sshin			openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
44756668Sshin			play_child(s_src, (struct sockaddr *)&srcaddr);
44856668Sshin			exit_failure("should never reach here");
44978064Sume			/*NOTREACHED*/
45056668Sshin		} else {
45156668Sshin			/* parent process */
45256668Sshin			close(s_src);
45356668Sshin			if (child_pid == -1)
45456668Sshin				syslog(LOG_ERR, "can't fork");
45556668Sshin		}
45656668Sshin	}
45756668Sshin	goto again;
45856668Sshin}
45956668Sshin
46056668Sshinstatic void
46156668Sshinplay_child(int s_src, struct sockaddr *srcaddr)
46256668Sshin{
46362655Skris	struct sockaddr_storage dstaddr6;
46456668Sshin	struct sockaddr_storage dstaddr4;
46595023Ssuz	char src[NI_MAXHOST];
46695023Ssuz	char dst6[NI_MAXHOST];
46795023Ssuz	char dst4[NI_MAXHOST];
468122679Sume	socklen_t len = sizeof(dstaddr6);
46956668Sshin	int s_dst, error, hport, nresvport, on = 1;
47056668Sshin	struct timeval tv;
47156668Sshin	struct sockaddr *sa4;
47278064Sume	const struct config *conf;
47356668Sshin
47456668Sshin	tv.tv_sec = 1;
47556668Sshin	tv.tv_usec = 0;
47656668Sshin
47756668Sshin	getnameinfo(srcaddr, srcaddr->sa_len,
478122679Sume	    src, sizeof(src), NULL, 0, NI_NUMERICHOST);
47956668Sshin	syslog(LOG_INFO, "accepted a client from %s", src);
48056668Sshin
48156668Sshin	error = getsockname(s_src, (struct sockaddr *)&dstaddr6, &len);
48278064Sume	if (error == -1) {
48395023Ssuz		exit_failure("getsockname: %s", strerror(errno));
48478064Sume		/*NOTREACHED*/
48578064Sume	}
48656668Sshin
48756668Sshin	getnameinfo((struct sockaddr *)&dstaddr6, len,
488122679Sume	    dst6, sizeof(dst6), NULL, 0, NI_NUMERICHOST);
48956668Sshin	syslog(LOG_INFO, "the client is connecting to %s", dst6);
49056668Sshin
49156668Sshin	if (!faith_prefix((struct sockaddr *)&dstaddr6)) {
49256668Sshin		if (serverpath) {
49356668Sshin			/*
49456668Sshin			 * Local service
49556668Sshin			 */
49656668Sshin			syslog(LOG_INFO, "executing local %s", serverpath);
49762655Skris			if (!inetd) {
49862655Skris				dup2(s_src, 0);
49962655Skris				close(s_src);
50062655Skris				dup2(0, 1);
50162655Skris				dup2(0, 2);
50262655Skris			}
50356668Sshin			execv(serverpath, serverarg);
50495023Ssuz			syslog(LOG_ERR, "execv %s: %s", serverpath,
50595023Ssuz			    strerror(errno));
50656668Sshin			_exit(EXIT_FAILURE);
50756668Sshin		} else {
50856668Sshin			close(s_src);
50956668Sshin			exit_success("no local service for %s", service);
51056668Sshin		}
51156668Sshin	}
51256668Sshin
51356668Sshin	/*
51456668Sshin	 * Act as a translator
51556668Sshin	 */
51656668Sshin
51756668Sshin	switch (((struct sockaddr *)&dstaddr6)->sa_family) {
51856668Sshin	case AF_INET6:
51956668Sshin		if (!map6to4((struct sockaddr_in6 *)&dstaddr6,
52056668Sshin		    (struct sockaddr_in *)&dstaddr4)) {
52156668Sshin			close(s_src);
52278064Sume			exit_failure("map6to4 failed");
52378064Sume			/*NOTREACHED*/
52456668Sshin		}
52556668Sshin		syslog(LOG_INFO, "translating from v6 to v4");
52656668Sshin		break;
52756668Sshin	default:
52856668Sshin		close(s_src);
52978064Sume		exit_failure("family not supported");
53056668Sshin		/*NOTREACHED*/
53156668Sshin	}
53256668Sshin
53356668Sshin	sa4 = (struct sockaddr *)&dstaddr4;
53456668Sshin	getnameinfo(sa4, sa4->sa_len,
535122679Sume	    dst4, sizeof(dst4), NULL, 0, NI_NUMERICHOST);
53678064Sume
53778064Sume	conf = config_match(srcaddr, sa4);
53878064Sume	if (!conf || !conf->permit) {
53978064Sume		close(s_src);
54078064Sume		if (conf) {
54178064Sume			exit_failure("translation to %s not permitted for %s",
54278064Sume			    dst4, prefix_string(&conf->match));
54378064Sume			/*NOTREACHED*/
54478064Sume		} else {
54578064Sume			exit_failure("translation to %s not permitted", dst4);
54678064Sume			/*NOTREACHED*/
54778064Sume		}
54878064Sume	}
54978064Sume
55056668Sshin	syslog(LOG_INFO, "the translator is connecting to %s", dst4);
55156668Sshin
55256668Sshin	setproctitle("port %s, %s -> %s", service, src, dst4);
55356668Sshin
55456668Sshin	if (sa4->sa_family == AF_INET6)
55556668Sshin		hport = ntohs(((struct sockaddr_in6 *)&dstaddr4)->sin6_port);
55656668Sshin	else /* AF_INET */
55756668Sshin		hport = ntohs(((struct sockaddr_in *)&dstaddr4)->sin_port);
55856668Sshin
559122679Sume	if (pflag)
56056668Sshin		s_dst = rresvport_af(&nresvport, sa4->sa_family);
561122679Sume	else
562122679Sume		s_dst = socket(sa4->sa_family, SOCK_STREAM, 0);
56378064Sume	if (s_dst < 0) {
56495023Ssuz		exit_failure("socket: %s", strerror(errno));
56578064Sume		/*NOTREACHED*/
56678064Sume	}
56756668Sshin
56878064Sume	if (conf->src.a.ss_family) {
56995023Ssuz		if (bind(s_dst, (const struct sockaddr *)&conf->src.a,
57078064Sume		    conf->src.a.ss_len) < 0) {
57195023Ssuz			exit_failure("bind: %s", strerror(errno));
57278064Sume			/*NOTREACHED*/
57378064Sume		}
57478064Sume	}
57578064Sume
57656668Sshin	error = setsockopt(s_dst, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on));
57778064Sume	if (error < 0) {
57895023Ssuz		exit_failure("setsockopt(SO_OOBINLINE): %s", strerror(errno));
57978064Sume		/*NOTREACHED*/
58078064Sume	}
58156668Sshin
58256668Sshin	error = setsockopt(s_src, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
58378064Sume	if (error < 0) {
58495023Ssuz		exit_failure("setsockopt(SO_SNDTIMEO): %s", strerror(errno));
58578064Sume		/*NOTREACHED*/
58678064Sume	}
58756668Sshin	error = setsockopt(s_dst, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
58878064Sume	if (error < 0) {
58995023Ssuz		exit_failure("setsockopt(SO_SNDTIMEO): %s", strerror(errno));
59078064Sume		/*NOTREACHED*/
59178064Sume	}
59256668Sshin
59356668Sshin	error = connect(s_dst, sa4, sa4->sa_len);
59478064Sume	if (error < 0) {
59595023Ssuz		exit_failure("connect: %s", strerror(errno));
59678064Sume		/*NOTREACHED*/
59778064Sume	}
59856668Sshin
59956668Sshin	switch (hport) {
60056668Sshin	case FTP_PORT:
60156668Sshin		ftp_relay(s_src, s_dst);
60256668Sshin		break;
60356668Sshin	default:
60456668Sshin		tcp_relay(s_src, s_dst, service);
60556668Sshin		break;
60656668Sshin	}
60756668Sshin
60856668Sshin	/* NOTREACHED */
60956668Sshin}
61056668Sshin
61156668Sshin/* 0: non faith, 1: faith */
61256668Sshinstatic int
61356668Sshinfaith_prefix(struct sockaddr *dst)
61456668Sshin{
61556668Sshin#ifndef USE_ROUTE
61656668Sshin	int mib[4], size;
61756668Sshin	struct in6_addr faith_prefix;
61856668Sshin	struct sockaddr_in6 *dst6 = (struct sockaddr_in *)dst;
61956668Sshin
62056668Sshin	if (dst->sa_family != AF_INET6)
62156668Sshin		return 0;
62256668Sshin
62356668Sshin	mib[0] = CTL_NET;
62456668Sshin	mib[1] = PF_INET6;
62556668Sshin	mib[2] = IPPROTO_IPV6;
62656668Sshin	mib[3] = IPV6CTL_FAITH_PREFIX;
62756668Sshin	size = sizeof(struct in6_addr);
62878064Sume	if (sysctl(mib, 4, &faith_prefix, &size, NULL, 0) < 0) {
62995023Ssuz		exit_failure("sysctl: %s", strerror(errno));
63078064Sume		/*NOTREACHED*/
63178064Sume	}
63256668Sshin
63356668Sshin	if (memcmp(dst, &faith_prefix,
634122679Sume	    sizeof(struct in6_addr) - sizeof(struct in_addr) == 0) {
63556668Sshin		return 1;
63656668Sshin	}
63756668Sshin	return 0;
63856668Sshin#else
63956668Sshin	struct myaddrs *p;
64056668Sshin	struct sockaddr_in6 *sin6;
64156668Sshin	struct sockaddr_in *sin4;
64256668Sshin	struct sockaddr_in6 *dst6;
64356668Sshin	struct sockaddr_in *dst4;
64456668Sshin	struct sockaddr_in dstmap;
64556668Sshin
64656668Sshin	dst6 = (struct sockaddr_in6 *)dst;
64756668Sshin	if (dst->sa_family == AF_INET6
64856668Sshin	 && IN6_IS_ADDR_V4MAPPED(&dst6->sin6_addr)) {
64956668Sshin		/* ugly... */
65056668Sshin		memset(&dstmap, 0, sizeof(dstmap));
65156668Sshin		dstmap.sin_family = AF_INET;
65256668Sshin		dstmap.sin_len = sizeof(dstmap);
65356668Sshin		memcpy(&dstmap.sin_addr, &dst6->sin6_addr.s6_addr[12],
65456668Sshin			sizeof(dstmap.sin_addr));
65556668Sshin		dst = (struct sockaddr *)&dstmap;
65656668Sshin	}
65756668Sshin
65856668Sshin	dst6 = (struct sockaddr_in6 *)dst;
65956668Sshin	dst4 = (struct sockaddr_in *)dst;
66056668Sshin
66156668Sshin	for (p = myaddrs; p; p = p->next) {
66256668Sshin		sin6 = (struct sockaddr_in6 *)p->addr;
66356668Sshin		sin4 = (struct sockaddr_in *)p->addr;
66456668Sshin
66556668Sshin		if (p->addr->sa_len != dst->sa_len
66656668Sshin		 || p->addr->sa_family != dst->sa_family)
66756668Sshin			continue;
66856668Sshin
66956668Sshin		switch (dst->sa_family) {
67056668Sshin		case AF_INET6:
67156668Sshin			if (sin6->sin6_scope_id == dst6->sin6_scope_id
67256668Sshin			 && IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &dst6->sin6_addr))
67356668Sshin				return 0;
67456668Sshin			break;
67556668Sshin		case AF_INET:
67656668Sshin			if (sin4->sin_addr.s_addr == dst4->sin_addr.s_addr)
67756668Sshin				return 0;
67856668Sshin			break;
67956668Sshin		}
68056668Sshin	}
68156668Sshin	return 1;
68256668Sshin#endif
68356668Sshin}
68456668Sshin
68556668Sshin/* 0: non faith, 1: faith */
68656668Sshinstatic int
68756668Sshinmap6to4(struct sockaddr_in6 *dst6, struct sockaddr_in *dst4)
68856668Sshin{
68956668Sshin	memset(dst4, 0, sizeof(*dst4));
69056668Sshin	dst4->sin_len = sizeof(*dst4);
69156668Sshin	dst4->sin_family = AF_INET;
69256668Sshin	dst4->sin_port = dst6->sin6_port;
69356668Sshin	memcpy(&dst4->sin_addr, &dst6->sin6_addr.s6_addr[12],
69456668Sshin		sizeof(dst4->sin_addr));
69556668Sshin
69656668Sshin	if (dst4->sin_addr.s_addr == INADDR_ANY
69756668Sshin	 || dst4->sin_addr.s_addr == INADDR_BROADCAST
69878064Sume	 || IN_MULTICAST(ntohl(dst4->sin_addr.s_addr)))
69956668Sshin		return 0;
70056668Sshin
70156668Sshin	return 1;
70256668Sshin}
70356668Sshin
70456668Sshin
70556668Sshinstatic void
706173298Scharniersig_child(int sig __unused)
70756668Sshin{
70856668Sshin	int status;
70956668Sshin	pid_t pid;
71056668Sshin
711122679Sume	while ((pid = wait3(&status, WNOHANG, (struct rusage *)0)) > 0)
712122679Sume		if (WEXITSTATUS(status))
713122679Sume			syslog(LOG_WARNING, "child %ld exit status 0x%x",
714122679Sume			    (long)pid, status);
71556668Sshin}
71656668Sshin
71756668Sshinvoid
718173298Scharniersig_terminate(int sig __unused)
71956668Sshin{
72062655Skris	syslog(LOG_INFO, "Terminating faith daemon");
72156668Sshin	exit(EXIT_SUCCESS);
72256668Sshin}
72356668Sshin
72456668Sshinstatic void
72556668Sshinstart_daemon(void)
72656668Sshin{
72778064Sume#ifdef SA_NOCLDWAIT
72878064Sume	struct sigaction sa;
72978064Sume#endif
73078064Sume
73156668Sshin	if (daemon(0, 0) == -1)
73295023Ssuz		exit_stderr("daemon: %s", strerror(errno));
73356668Sshin
73478064Sume#ifdef SA_NOCLDWAIT
73578064Sume	memset(&sa, 0, sizeof(sa));
73678064Sume	sa.sa_handler = sig_child;
73778064Sume	sa.sa_flags = SA_NOCLDWAIT;
73878064Sume	sigemptyset(&sa.sa_mask);
73978064Sume	sigaction(SIGCHLD, &sa, (struct sigaction *)0);
74078064Sume#else
74178064Sume	if (signal(SIGCHLD, sig_child) == SIG_ERR) {
74295023Ssuz		exit_failure("signal CHLD: %s", strerror(errno));
74378064Sume		/*NOTREACHED*/
74478064Sume	}
74578064Sume#endif
74656668Sshin
74778064Sume	if (signal(SIGTERM, sig_terminate) == SIG_ERR) {
74895023Ssuz		exit_failure("signal TERM: %s", strerror(errno));
74978064Sume		/*NOTREACHED*/
75078064Sume	}
75156668Sshin}
75256668Sshin
75378064Sumestatic void
75478064Sumeexit_stderr(const char *fmt, ...)
75556668Sshin{
75656668Sshin	va_list ap;
75756668Sshin	char buf[BUFSIZ];
75856668Sshin
75956668Sshin	va_start(ap, fmt);
76056668Sshin	vsnprintf(buf, sizeof(buf), fmt, ap);
76156668Sshin	va_end(ap);
76256668Sshin	fprintf(stderr, "%s\n", buf);
76356668Sshin	exit(EXIT_FAILURE);
76456668Sshin}
76556668Sshin
76656668Sshinvoid
76756668Sshinexit_failure(const char *fmt, ...)
76856668Sshin{
76956668Sshin	va_list ap;
77056668Sshin	char buf[BUFSIZ];
77156668Sshin
77256668Sshin	va_start(ap, fmt);
77356668Sshin	vsnprintf(buf, sizeof(buf), fmt, ap);
77456668Sshin	va_end(ap);
77562655Skris	syslog(LOG_ERR, "%s", buf);
77656668Sshin	exit(EXIT_FAILURE);
77756668Sshin}
77856668Sshin
77956668Sshinvoid
78056668Sshinexit_success(const char *fmt, ...)
78156668Sshin{
78256668Sshin	va_list ap;
78356668Sshin	char buf[BUFSIZ];
78456668Sshin
78556668Sshin	va_start(ap, fmt);
78656668Sshin	vsnprintf(buf, sizeof(buf), fmt, ap);
78756668Sshin	va_end(ap);
78862655Skris	syslog(LOG_INFO, "%s", buf);
78956668Sshin	exit(EXIT_SUCCESS);
79056668Sshin}
79156668Sshin
79256668Sshin#ifdef USE_ROUTE
79356668Sshinstatic void
794201387Sedgrab_myaddrs(void)
79556668Sshin{
79662655Skris	struct ifaddrs *ifap, *ifa;
79762655Skris	struct myaddrs *p;
79862655Skris	struct sockaddr_in6 *sin6;
79962655Skris
80062655Skris	if (getifaddrs(&ifap) != 0) {
80162655Skris		exit_failure("getifaddrs");
80262655Skris		/*NOTREACHED*/
80362655Skris	}
80462655Skris
80562655Skris	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
80662655Skris		switch (ifa->ifa_addr->sa_family) {
80762655Skris		case AF_INET:
80862655Skris		case AF_INET6:
80962655Skris			break;
81062655Skris		default:
81162655Skris			continue;
81262655Skris		}
81362655Skris
81462655Skris		p = (struct myaddrs *)malloc(sizeof(struct myaddrs) +
81562655Skris		    ifa->ifa_addr->sa_len);
81662655Skris		if (!p) {
81762655Skris			exit_failure("not enough core");
81862655Skris			/*NOTREACHED*/
81962655Skris		}
82062655Skris		memcpy(p + 1, ifa->ifa_addr, ifa->ifa_addr->sa_len);
82162655Skris		p->next = myaddrs;
82262655Skris		p->addr = (struct sockaddr *)(p + 1);
82362655Skris#ifdef __KAME__
82462655Skris		if (ifa->ifa_addr->sa_family == AF_INET6) {
82562655Skris			sin6 = (struct sockaddr_in6 *)p->addr;
82662655Skris			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)
82762655Skris			 || IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) {
82862655Skris				sin6->sin6_scope_id =
82962655Skris					ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
83062655Skris				sin6->sin6_addr.s6_addr[2] = 0;
83162655Skris				sin6->sin6_addr.s6_addr[3] = 0;
83262655Skris			}
83362655Skris		}
83462655Skris#endif
83562655Skris		myaddrs = p;
83662655Skris		if (dflag) {
83762655Skris			char hbuf[NI_MAXHOST];
83862655Skris			getnameinfo(p->addr, p->addr->sa_len,
839122679Sume			    hbuf, sizeof(hbuf), NULL, 0,
840122679Sume			    NI_NUMERICHOST);
84162655Skris			syslog(LOG_INFO, "my interface: %s %s", hbuf,
84262655Skris			    ifa->ifa_name);
84362655Skris		}
84462655Skris	}
84562655Skris
84662655Skris	freeifaddrs(ifap);
84756668Sshin}
84856668Sshin
84956668Sshinstatic void
850201387Sedfree_myaddrs(void)
85156668Sshin{
85256668Sshin	struct myaddrs *p, *q;
85356668Sshin
85456668Sshin	p = myaddrs;
85556668Sshin	while (p) {
85656668Sshin		q = p->next;
85756668Sshin		free(p);
85856668Sshin		p = q;
85956668Sshin	}
86056668Sshin	myaddrs = NULL;
86156668Sshin}
86256668Sshin
86356668Sshinstatic void
864201387Sedupdate_myaddrs(void)
86556668Sshin{
86656668Sshin	char msg[BUFSIZ];
86756668Sshin	int len;
86856668Sshin	struct rt_msghdr *rtm;
86956668Sshin
87056668Sshin	len = read(sockfd, msg, sizeof(msg));
87156668Sshin	if (len < 0) {
87256668Sshin		syslog(LOG_ERR, "read(PF_ROUTE) failed");
87356668Sshin		return;
87456668Sshin	}
87556668Sshin	rtm = (struct rt_msghdr *)msg;
87656668Sshin	if (len < 4 || len < rtm->rtm_msglen) {
87756668Sshin		syslog(LOG_ERR, "read(PF_ROUTE) short read");
87856668Sshin		return;
87956668Sshin	}
88056668Sshin	if (rtm->rtm_version != RTM_VERSION) {
88156668Sshin		syslog(LOG_ERR, "routing socket version mismatch");
88256668Sshin		close(sockfd);
88356668Sshin		sockfd = 0;
88456668Sshin		return;
88556668Sshin	}
88656668Sshin	switch (rtm->rtm_type) {
88756668Sshin	case RTM_NEWADDR:
88856668Sshin	case RTM_DELADDR:
88956668Sshin	case RTM_IFINFO:
89056668Sshin		break;
89156668Sshin	default:
89256668Sshin		return;
89356668Sshin	}
89456668Sshin	/* XXX more filters here? */
89556668Sshin
89656668Sshin	syslog(LOG_INFO, "update interface address list");
89756668Sshin	free_myaddrs();
89856668Sshin	grab_myaddrs();
89956668Sshin}
90056668Sshin#endif /*USE_ROUTE*/
90156668Sshin
90256668Sshinstatic void
903201387Sedusage(void)
90456668Sshin{
90578064Sume	fprintf(stderr, "usage: %s [-dp] [-f conf] service [serverpath [serverargs]]\n",
90656668Sshin		faithdname);
90756668Sshin	exit(0);
90856668Sshin}
909