summaryrefslogtreecommitdiffstats
path: root/usr.sbin/faithd
diff options
context:
space:
mode:
authorshin <shin@FreeBSD.org>2000-01-27 09:28:38 +0000
committershin <shin@FreeBSD.org>2000-01-27 09:28:38 +0000
commitce15efb7c04858f00b57c16093d4a3043809048e (patch)
tree8b3d00f78a4a5a34cc3b17e29c28b4472d93a35c /usr.sbin/faithd
parentdcbae417f8f4365a5eea807290162acd308b720d (diff)
downloadFreeBSD-src-ce15efb7c04858f00b57c16093d4a3043809048e.zip
FreeBSD-src-ce15efb7c04858f00b57c16093d4a3043809048e.tar.gz
another tcp apps IPv6 updates.(should be make world safe)
ftp, telnet, ftpd, faithd also telnet related sync with crypto, secure, kerberosIV Obtained from: KAME project
Diffstat (limited to 'usr.sbin/faithd')
-rw-r--r--usr.sbin/faithd/Makefile24
-rw-r--r--usr.sbin/faithd/README140
-rw-r--r--usr.sbin/faithd/faithd.8256
-rw-r--r--usr.sbin/faithd/faithd.c837
-rw-r--r--usr.sbin/faithd/faithd.h70
-rw-r--r--usr.sbin/faithd/ftp.c1139
-rw-r--r--usr.sbin/faithd/rsh.c210
-rw-r--r--usr.sbin/faithd/tcp.c300
-rw-r--r--usr.sbin/faithd/test/faithd.rb312
9 files changed, 3288 insertions, 0 deletions
diff --git a/usr.sbin/faithd/Makefile b/usr.sbin/faithd/Makefile
new file mode 100644
index 0000000..c40c21d
--- /dev/null
+++ b/usr.sbin/faithd/Makefile
@@ -0,0 +1,24 @@
+# Copyright (c) 1996 WIDE Project. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modifications, are permitted provided that the above copyright notice
+# and this paragraph are duplicated in all such forms and that any
+# documentation, advertising materials, and other materials related to
+# such distribution and use acknowledge that the software was developed
+# by the WIDE Project, Japan. The name of the Project may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS''
+# AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+# LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE.
+# $FreeBSD$
+
+PROG= faithd
+SRCS= faithd.c tcp.c ftp.c rsh.c
+MAN8= faithd.8
+#CFLAGS+= -DFAITH4
+CFLAGS+= -Wall
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/faithd/README b/usr.sbin/faithd/README
new file mode 100644
index 0000000..f8e4753
--- /dev/null
+++ b/usr.sbin/faithd/README
@@ -0,0 +1,140 @@
+Configuring FAITH IPv6-to-IPv4 TCP relay
+
+Kazu Yamamoto and Jun-ichiro itojun Hagino
+$Id: README,v 1.1.1.1 1999/08/08 23:29:27 itojun Exp $
+$FreeBSD$
+
+
+Introduction
+============
+
+FAITH is a IPv6-to-IPv4 TCP relay. It performs tcp relay just as some of
+firewall-oriented gateway does, but between IPv6 and IPv4 with address
+translation.
+TCP connections has to be made from IPv6 node to IPv4 node. FAITH will
+not relay connections for the opposite direction.
+To perform relays, FAITH daemon needs to be executed on a router between
+your local IPv6 site and outside IPv4 network. The daemon needs to be
+invoked per each TCP services (TCP port number).
+
+ IPv4 node "dest" = 123.4.5.6
+ |
+ [[[[ outside IPv4 ocean ]]]]
+ |
+ node that runs FAITH-daemon (usually a router)
+ |
+ ==+=====+===+==== IPv6, or IPv4/v6 network in your site ^
+ | | | connection
+ clients IPv6 node "src" |
+
+You will have to allocate an IPv6 address prefix to map IPv4 addresses into.
+The following description uses 3ffe:0501:1234:ffff:: as example.
+Please use a prefix which belongs to your site.
+FAITH will make it possible to make a IPv6 TCP connection From IPv6 node
+"src", toward IPv4 node "dest", by specifying FAITH-mapped address
+3ffe:0501:1234:ffff::123.4.5.6
+(which is, 3ffe:0501:1234:ffff:0000:0000:7b04:0506).
+The address mapping can be performed by hand:-), by speical nameserver on
+the network, or by special resolver on the source node.
+
+
+Setup
+=====
+
+The following example assumes:
+- You have assigned 3ffe:0501:1234:ffff:: as FAITH adderss prefix.
+- You are willing to provide IPv6-to IPv4 TCP relay for telnet.
+
+<<On the translating router on which faithd runs>>
+
+(1) If you have IPv6 TCP server for the "telnet" service, i.e. telnetd via
+ inet6d, disable that daemon. Comment out the line from "inet6d.conf"
+ and send the HUP signal to "inet6d".
+
+(2) Execute sysctl as root to enable FAITH support in the kernel.
+
+ # sysctl -w net.inet6.ip6.keepfaith=1
+
+(3) Route packets toward FAITH prefix into "faith0" interface.
+
+ # ifconfig faith0 up
+ # route add -inet6 3ffe:0501:1234:ffff:: -prefixlen 64 -interface faith0
+
+ or, on platforms that has problem with "-interface":
+ # ifconfig faith0 up
+ # route add -inet6 3ffe:0501:1234:ffff:: -prefixlen 64 \
+ fe80:q::xxxx:yyyy:zzzz:wwww
+ (the last one is link-local address assigned for faith0)
+
+(4) Execute "faithd" by root as follows:
+
+ # faithd telnet /usr/local/v6/libexec/telnetd telnetd
+
+ 1st argument is a service name you are willing to provide TCP relay.
+ (it can be specified either by number "23" or by string "telnet")
+ 2nd argument is a path name for local IPv6 TCP server. If there is a
+ connection toward the router itself, this program will be invoked.
+ 3rd and the following arguments are arguments for the local IPv6 TCP
+ server. (3rd argument is typically the program name without its path.)
+
+ More examples:
+
+ # faithd login /usr/local/v6/libexec/rlogin rlogind
+ # faithd shell /usr/local/v6/libexec/rshd rshd
+ # faithd ftpd /usr/local/v6/libexec/ftpd ftpd -l
+ # faithd ssh
+
+
+<<Routing>>
+
+(4) Make sure that packets whose destinations match the prefix can
+reach from the IPv6 host to the translating router.
+
+<<On the IPv6 host>>
+
+There are two ways to translate IPv4 address to IPv6 address:
+ (a) Faked by DNS
+ (b) Faked by /etc/hosts.
+
+(5.a) Install "newbie" and set up FAITH mode. See kit/ports/newbie of
+ KAME package. KAME package is obtained from www.kame.net.
+
+(5.b) Add an entry into /etc/hosts so that you can resolve hostname into
+faked IPv6 addrss. For example, add the following line for www.freebsd.org:
+
+ 3ffe:0501:1234:ffff::204.216.27.21 www.freebsd.org
+
+<<On the translating router on which faithd runs.>>
+
+(6) To see if "faithd" works, watch "/var/log/daemon". Note: please
+setup "/etc/syslog.conf" so that LOG_DAEMON messages are to be stored
+in "/var/log/daemon".
+
+ <e.g.>
+ daemon.* /var/log/daemon
+
+
+Advanced configuration
+======================
+
+If you would like to restrict IPv4 destination for translation, you may
+want to do the following:
+
+ # route add -inet6 3ffe:0501:1234:ffff::123.0.0.0 -prefixlen 104 \
+ -interface faith0
+
+By this way, you can restrict IPv4 destination to 123.0.0.0/8.
+You may also want to reject packets toward 3ffe:0501:1234:ffff::/64 which
+is not in 3ffe:0501:1234:ffff::123.0.0.0/104. This will be left as excerside
+for the reader.
+
+By doing this, you will be able to provide your IPv4 web server to outside
+IPv6 customers, without risks of unwanted open relays.
+
+ [[[[ IPv6 network outside ]]]] |
+ | | connection
+ node that runs FAITH-daemon (usually a router) v
+ |
+ ========+======== IPv4/v6 network in your site
+ | (123.0.0.0/8)
+ IPv4 web server
diff --git a/usr.sbin/faithd/faithd.8 b/usr.sbin/faithd/faithd.8
new file mode 100644
index 0000000..2f62ed3
--- /dev/null
+++ b/usr.sbin/faithd/faithd.8
@@ -0,0 +1,256 @@
+.\" Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the project nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: faithd.8,v 1.3 1999/10/07 04:22:14 itojun Exp $
+.\" $FreeBSD$
+.\"
+.Dd January 27, 2000
+.Dt FAITHD 8
+.Os KAME
+.Sh NAME
+.Nm faithd
+.Nd FAITH IPv6/v4 translator daemon
+.Sh SYNOPSIS
+.Nm faithd
+.Op Fl dp
+.Oo
+.Ar service
+.Oo
+.Ar serverpath
+.Op Ar serverargs
+.Oc
+.Oc
+.Sh DESCRIPTION
+.Nm
+provides IPv6/v4 TCP relay for the specified
+.Ar service .
+.Pp
+.Nm
+must be invoked on IPv4/v6 dual stack router.
+The router must be configured to capture all the TCP traffic
+toward reserved IPv6 address prefix, by using
+.Xr route 8
+and
+.Xr sysctl 8
+commands.
+.Nm
+will daemonize itself on invocation.
+.Pp
+.Nm
+will listen to TCPv6 port
+.Ar service .
+If TCPv6 traffic to port
+.Ar service
+is found,
+.Nm
+will relay the TCPv6 traffic to TCPv4.
+Destination for relayed TCPv4 connection will be determined by the
+last 4 octets of the original IPv6 destination.
+For example, if
+.Li 3ffe:0501:4819:ffff::
+is reserved for
+.Nm faithd ,
+and the TCPv6 destination address is
+.Li 3ffe:0501:4819:ffff::0a01:0101 ,
+the traffic will be relayed to IPv4 destination
+.Li 10.1.1.1 .
+.Pp
+If
+.Ar service
+is not given,
+.Li telnet
+is assumed, and
+.Nm
+will relay TCP traffic on TCP port
+.Li telnet .
+With
+.Ar service ,
+.Nm
+will work as TCP relaying daemon for specified
+.Ar service
+as described above.
+.Pp
+Since
+.Nm
+listens to TCP port
+.Ar service ,
+it is not possible to run local TCP daemons for port
+.Ar service
+on the router, using
+.Xr inetd 8
+or other standard mechanisms.
+By specifying
+.Ar serverpath
+to
+.Nm faithd ,
+you can run local daemons on the router.
+.Nm
+will invoke local daemon at
+.Ar serverpath
+if the destination address is local interface address,
+and will perform translation to IPv4 TCP in other cases.
+You can also specify
+.Ar serverargs
+for the arguments for the local daemon.
+.Pp
+To use
+.Nm
+translation service,
+an IPv6 address prefix must be reserved for mapping IPv4 addresses into.
+Kernel must be properly configured to route all the TCP connection
+toward the reserved IPv6 address prefix into the
+.Dv faith
+pseudo interface, by using
+.Xr route 8
+command.
+Also,
+.Xr sysctl 8
+should be used to configure
+.Dv net.inet6.ip6.keepfaith
+to
+.Dv 1 .
+.Pp
+If
+.Fl d
+is given, debugging information will be generated using
+.Xr syslog 3 .
+If
+.Fl p
+is given,
+.Nm
+will use privileged TCP port number as source port,
+for IPv4 TCP connection toward final destination.
+For relaying
+.Xr ftp 1
+and
+.Xr rlogin 1 ,
+.Fl p
+is not necessary as special program code is supplied.
+.Pp
+.Nm
+will relay both normal and out-of-band TCP data.
+It is capable of emulating TCP half close as well.
+.Nm
+includes special support for protocols used by
+.Xr ftp 1
+and
+.Xr rlogin 1 .
+When translating FTP protocol,
+.Nm
+translates network level addresses in
+.Li PORT/LPRT/EPRT
+and
+.Li PASV/LPSV/EPSV
+commands.
+For RLOGIN protocol,
+.Nm
+will relay back connection from
+.Xr rlogind 8
+on the server to
+.Xr rlogin 1
+on client.
+.Pp
+Inactive sessions will be disconnected in 30 minutes,
+to avoid stale sessions from chewing up resources.
+This may be inappropriate for some of the services
+.Po
+should this be configurable?
+.Pc .
+.\"
+.Sh EXAMPLES
+Before invoking
+.Nm faithd ,
+.Xr faith 4
+interface has to be configured properly.
+.Pp
+To translate
+.Li telnet
+service, and provide no local telnet service, invoke
+.Nm
+as either of the following:
+.Bd -literal -offset
+# faithd
+# faithd telnet
+.Ed
+.Pp
+If you would like to provide local telnet service via
+.Xr telnetd 8
+on
+.Pa /usr/local/v6/libexec/telnetd ,
+user the following command line:
+.Bd -literal -offset
+# faithd telnet /usr/local/v6/libexec/telnetd telnetd
+.Ed
+.Pp
+If you would like to pass extra arguments to the local daemon:
+.Bd -literal -offset
+# faithd ftpd /usr/local/v6/libexec/ftpd ftpd -l
+.Ed
+.Pp
+Here are some other examples:
+.Bd -literal -offset
+# faithd login /usr/local/v6/libexec/rlogin rlogind
+# faithd shell /usr/local/v6/libexec/rshd rshd
+# faithd sshd
+.Ed
+.\"
+.Sh RETURN VALUES
+.Nm
+exits with
+.Dv EXIT_SUCCESS
+.Pq 0
+on success, and
+.Dv EXIT_FAILURE
+.Pq 1
+on error.
+.\"
+.Sh SEE ALSO
+.Xr faith 4 ,
+.Xr route 8 ,
+.Xr sysctl 8
+.Rs
+.%A Jun-ichiro itojun Hagino
+.%A Kazu Yamamoto
+.%T "An IPv6-to-IPv4 transport relay translator"
+.%R internet draft
+.%N draft-ietf-ngtrans-tcpudp-relay-00.txt
+.%O work in progress material
+.Re
+.\"
+.Sh SECURITY NOTICE
+It is very insecure to use
+.Xr rhosts 5
+and other IP-address based authentication, for connections relayed by
+.Nm
+.Po
+and any other TCP relaying services
+.Pc .
+.\"
+.Sh HISTORY
+The
+.Nm
+command first appeared in WIDE Hydrangea IPv6 protocol stack kit.
diff --git a/usr.sbin/faithd/faithd.c b/usr.sbin/faithd/faithd.c
new file mode 100644
index 0000000..ee6c8df
--- /dev/null
+++ b/usr.sbin/faithd/faithd.c
@@ -0,0 +1,837 @@
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * User level translator from IPv6 to IPv4.
+ *
+ * Usage: faithd [<port> <progpath> <arg1(progname)> <arg2> ...]
+ * e.g. faithd telnet /usr/local/v6/sbin/telnetd telnetd
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <libutil.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <termios.h>
+
+#include <net/if_types.h>
+#ifdef IFT_FAITH
+# define USE_ROUTE
+# include <net/if.h>
+# include <net/route.h>
+# include <net/if_dl.h>
+#endif
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#ifdef FAITH4
+#include <resolv.h>
+#include <arpa/nameser.h>
+#ifndef FAITH_NS
+#define FAITH_NS "FAITH_NS"
+#endif
+#endif
+
+#include "faithd.h"
+
+char *serverpath = NULL;
+char *serverarg[MAXARGV + 1];
+static char *faithdname = NULL;
+char logname[BUFSIZ];
+char procname[BUFSIZ];
+struct myaddrs {
+ struct myaddrs *next;
+ struct sockaddr *addr;
+};
+struct myaddrs *myaddrs = NULL;
+static char *service;
+#ifdef USE_ROUTE
+static int sockfd = 0;
+#endif
+int dflag = 0;
+static int pflag = 0;
+
+int main __P((int, char **));
+static void play_service __P((int));
+static void play_child __P((int, struct sockaddr *));
+static int faith_prefix __P((struct sockaddr *));
+static int map6to4 __P((struct sockaddr_in6 *, struct sockaddr_in *));
+#ifdef FAITH4
+static int map4to6 __P((struct sockaddr_in *, struct sockaddr_in6 *));
+#endif
+static void sig_child __P((int));
+static void sig_terminate __P((int));
+static void start_daemon __P((void));
+static unsigned int if_maxindex __P((void));
+static void grab_myaddrs __P((void));
+static void free_myaddrs __P((void));
+static void update_myaddrs __P((void));
+static void usage __P((void));
+
+int
+main(int argc, char *argv[])
+{
+ struct addrinfo hints, *res;
+ int s_wld, error, i, serverargc, on = 1;
+ int family = AF_INET6;
+ int c;
+#ifdef FAITH_NS
+ char *ns;
+#endif /* FAITH_NS */
+ extern int optind;
+ extern char *optarg;
+
+ /*
+ * Initializing stuff
+ */
+
+ faithdname = strrchr(argv[0], '/');
+ if (faithdname)
+ faithdname++;
+ else
+ faithdname = argv[0];
+
+ while ((c = getopt(argc, argv, "dp46")) != -1) {
+ switch (c) {
+ case 'd':
+ dflag++;
+ break;
+ case 'p':
+ pflag++;
+ break;
+#ifdef FAITH4
+ case '4':
+ family = AF_INET;
+ break;
+ case '6':
+ family = AF_INET6;
+ break;
+#endif
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+#ifdef FAITH_NS
+ if ((ns = getenv(FAITH_NS)) != NULL) {
+ struct sockaddr_storage ss;
+ struct addrinfo hints, *res;
+ char serv[NI_MAXSERV];
+
+ memset(&ss, 0, sizeof(ss));
+ memset(&hints, 0, sizeof(hints));
+ snprintf(serv, sizeof(serv), "%u", NAMESERVER_PORT);
+ hints.ai_flags = AI_NUMERICHOST;
+ if (getaddrinfo(ns, serv, &hints, &res) == 0) {
+ res_init();
+ memcpy(&_res_ext.nsaddr, res->ai_addr, res->ai_addrlen);
+ _res.nscount = 1;
+ }
+ }
+#endif /* FAITH_NS */
+
+#ifdef USE_ROUTE
+ grab_myaddrs();
+#endif
+
+ switch (argc) {
+ case 0:
+ serverpath = DEFAULT_PATH;
+ serverarg[0] = DEFAULT_NAME;
+ serverarg[1] = NULL;
+ service = DEFAULT_PORT_NAME;
+ break;
+ default:
+ serverargc = argc - NUMARG;
+ if (serverargc > MAXARGV)
+ exit_error("too many augments");
+
+ serverpath = malloc(strlen(argv[NUMPRG]));
+ strcpy(serverpath, argv[NUMPRG]);
+ for (i = 0; i < serverargc; i++) {
+ serverarg[i] = malloc(strlen(argv[i + NUMARG]));
+ strcpy(serverarg[i], argv[i + NUMARG]);
+ }
+ serverarg[i] = NULL;
+ /* fall throuth */
+ case 1: /* no local service */
+ service = argv[NUMPRT];
+ break;
+ }
+
+ /*
+ * Opening wild card socket for this service.
+ */
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ error = getaddrinfo(NULL, service, &hints, &res);
+ if (error) {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error));
+ if (error == EAI_SYSTEM)
+ exit_error("getaddrinfo: %s\n", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ s_wld = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s_wld == -1)
+ exit_error("socket: %s", ERRSTR);
+
+#ifdef IPV6_FAITH
+ if (res->ai_family == AF_INET6) {
+ error = setsockopt(s_wld, IPPROTO_IPV6, IPV6_FAITH, &on, sizeof(on));
+ if (error == -1)
+ exit_error("setsockopt(IPV6_FAITH): %s", ERRSTR);
+ }
+#endif
+#ifdef FAITH4
+#ifdef IP_FAITH
+ if (res->ai_family == AF_INET) {
+ error = setsockopt(s_wld, IPPROTO_IP, IP_FAITH, &on, sizeof(on));
+ if (error == -1)
+ exit_error("setsockopt(IP_FAITH): %s", ERRSTR);
+ }
+#endif
+#endif /* FAITH4 */
+
+ error = setsockopt(s_wld, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (error == -1)
+ exit_error("setsockopt(SO_REUSEADDR): %s", ERRSTR);
+
+ error = setsockopt(s_wld, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on));
+ if (error == -1)
+ exit_error("setsockopt(SO_OOBINLINE): %s", ERRSTR);
+
+ error = bind(s_wld, (struct sockaddr *)res->ai_addr, res->ai_addrlen);
+ if (error == -1)
+ exit_error("bind: %s", ERRSTR);
+
+ error = listen(s_wld, 5);
+ if (error == -1)
+ exit_error("listen: %s", ERRSTR);
+
+#ifdef USE_ROUTE
+ sockfd = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC);
+ if (sockfd < 0) {
+ exit_error("socket(PF_ROUTE): %s", ERRSTR);
+ /*NOTREACHED*/
+ }
+#endif
+
+ /*
+ * Everything is OK.
+ */
+
+ start_daemon();
+
+ snprintf(logname, sizeof(logname), "faithd %s", service);
+ snprintf(procname, sizeof(procname), "accepting port %s", service);
+ openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+ syslog(LOG_INFO, "Staring faith daemon for %s port", service);
+
+ play_service(s_wld);
+ /*NOTRECHED*/
+ exit(1); /*pacify gcc*/
+}
+
+static void
+play_service(int s_wld)
+{
+ struct sockaddr_storage srcaddr;
+ int len;
+ int s_src;
+ pid_t child_pid;
+ fd_set rfds;
+ int error;
+ int maxfd;
+
+ /*
+ * Wait, accept, fork, faith....
+ */
+again:
+ setproctitle(procname);
+
+ FD_ZERO(&rfds);
+ FD_SET(s_wld, &rfds);
+ maxfd = s_wld;
+#ifdef USE_ROUTE
+ if (sockfd) {
+ FD_SET(sockfd, &rfds);
+ maxfd = (maxfd < sockfd) ? sockfd : maxfd;
+ }
+#endif
+
+ error = select(maxfd + 1, &rfds, NULL, NULL, NULL);
+ if (error < 0) {
+ if (errno == EINTR)
+ goto again;
+ exit_failure("select: %s", ERRSTR);
+ /*NOTREACHED*/
+ }
+
+#ifdef USE_ROUTE
+ if (FD_ISSET(sockfd, &rfds)) {
+ update_myaddrs();
+ }
+#endif
+ if (FD_ISSET(s_wld, &rfds)) {
+ len = sizeof(srcaddr);
+ s_src = accept(s_wld, (struct sockaddr *)&srcaddr,
+ &len);
+ if (s_src == -1)
+ exit_failure("socket: %s", ERRSTR);
+
+ child_pid = fork();
+
+ if (child_pid == 0) {
+ /* child process */
+ close(s_wld);
+ closelog();
+ openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+ play_child(s_src, (struct sockaddr *)&srcaddr);
+ exit_failure("should never reach here");
+ } else {
+ /* parent process */
+ close(s_src);
+ if (child_pid == -1)
+ syslog(LOG_ERR, "can't fork");
+ }
+ }
+ goto again;
+}
+
+static void
+play_child(int s_src, struct sockaddr *srcaddr)
+{
+ struct sockaddr_storage dstaddr6;
+ struct sockaddr_storage dstaddr4;
+ char src[MAXHOSTNAMELEN];
+ char dst6[MAXHOSTNAMELEN];
+ char dst4[MAXHOSTNAMELEN];
+ int len = sizeof(dstaddr6);
+ int s_dst, error, hport, nresvport, on = 1;
+ struct timeval tv;
+ struct sockaddr *sa4;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ getnameinfo(srcaddr, srcaddr->sa_len,
+ src, sizeof(src), NULL, 0, NI_NUMERICHOST);
+ syslog(LOG_INFO, "accepted a client from %s", src);
+
+ error = getsockname(s_src, (struct sockaddr *)&dstaddr6, &len);
+ if (error == -1)
+ exit_failure("getsockname: %s", ERRSTR);
+
+ getnameinfo((struct sockaddr *)&dstaddr6, len,
+ dst6, sizeof(dst6), NULL, 0, NI_NUMERICHOST);
+ syslog(LOG_INFO, "the client is connecting to %s", dst6);
+
+ if (!faith_prefix((struct sockaddr *)&dstaddr6)) {
+ if (serverpath) {
+ /*
+ * Local service
+ */
+ syslog(LOG_INFO, "executing local %s", serverpath);
+ dup2(s_src, 0);
+ close(s_src);
+ dup2(0, 1);
+ dup2(0, 2);
+ execv(serverpath, serverarg);
+ syslog(LOG_ERR, "execv %s: %s", serverpath, ERRSTR);
+ _exit(EXIT_FAILURE);
+ } else {
+ close(s_src);
+ exit_success("no local service for %s", service);
+ }
+ }
+
+ /*
+ * Act as a translator
+ */
+
+ switch (((struct sockaddr *)&dstaddr6)->sa_family) {
+ case AF_INET6:
+ if (!map6to4((struct sockaddr_in6 *)&dstaddr6,
+ (struct sockaddr_in *)&dstaddr4)) {
+ close(s_src);
+ exit_error("map6to4 failed");
+ }
+ syslog(LOG_INFO, "translating from v6 to v4");
+ break;
+#ifdef FAITH4
+ case AF_INET:
+ if (!map4to6((struct sockaddr_in *)&dstaddr6,
+ (struct sockaddr_in6 *)&dstaddr4)) {
+ close(s_src);
+ exit_error("map4to6 failed");
+ }
+ syslog(LOG_INFO, "translating from v4 to v6");
+ break;
+#endif
+ default:
+ close(s_src);
+ exit_error("family not supported");
+ /*NOTREACHED*/
+ }
+
+ sa4 = (struct sockaddr *)&dstaddr4;
+ getnameinfo(sa4, sa4->sa_len,
+ dst4, sizeof(dst4), NULL, 0, NI_NUMERICHOST);
+ syslog(LOG_INFO, "the translator is connecting to %s", dst4);
+
+ setproctitle("port %s, %s -> %s", service, src, dst4);
+
+ if (sa4->sa_family == AF_INET6)
+ hport = ntohs(((struct sockaddr_in6 *)&dstaddr4)->sin6_port);
+ else /* AF_INET */
+ hport = ntohs(((struct sockaddr_in *)&dstaddr4)->sin_port);
+
+ switch (hport) {
+ case RLOGIN_PORT:
+ case RSH_PORT:
+ s_dst = rresvport_af(&nresvport, sa4->sa_family);
+ break;
+ default:
+ if (pflag)
+ s_dst = rresvport_af(&nresvport, sa4->sa_family);
+ else
+ s_dst = socket(sa4->sa_family, SOCK_STREAM, 0);
+ break;
+ }
+ if (s_dst == -1)
+ exit_failure("socket: %s", ERRSTR);
+
+ error = setsockopt(s_dst, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on));
+ if (error == -1)
+ exit_error("setsockopt(SO_OOBINLINE): %s", ERRSTR);
+
+ error = setsockopt(s_src, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+ if (error == -1)
+ exit_error("setsockopt(SO_SNDTIMEO): %s", ERRSTR);
+ error = setsockopt(s_dst, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+ if (error == -1)
+ exit_error("setsockopt(SO_SNDTIMEO): %s", ERRSTR);
+
+ error = connect(s_dst, sa4, sa4->sa_len);
+ if (error == -1)
+ exit_failure("connect: %s", ERRSTR);
+
+ switch (hport) {
+ case FTP_PORT:
+ ftp_relay(s_src, s_dst);
+ break;
+ case RSH_PORT:
+ rsh_relay(s_src, s_dst);
+ break;
+ default:
+ tcp_relay(s_src, s_dst, service);
+ break;
+ }
+
+ /* NOTREACHED */
+}
+
+/* 0: non faith, 1: faith */
+static int
+faith_prefix(struct sockaddr *dst)
+{
+#ifndef USE_ROUTE
+ int mib[4], size;
+ struct in6_addr faith_prefix;
+ struct sockaddr_in6 *dst6 = (struct sockaddr_in *)dst;
+
+ if (dst->sa_family != AF_INET6)
+ return 0;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_INET6;
+ mib[2] = IPPROTO_IPV6;
+ mib[3] = IPV6CTL_FAITH_PREFIX;
+ size = sizeof(struct in6_addr);
+ if (sysctl(mib, 4, &faith_prefix, &size, NULL, 0) < 0)
+ exit_error("sysctl: %s", ERRSTR);
+
+ if (memcmp(dst, &faith_prefix,
+ sizeof(struct in6_addr) - sizeof(struct in_addr) == 0) {
+ return 1;
+ }
+ return 0;
+#else
+ struct myaddrs *p;
+ struct sockaddr_in6 *sin6;
+ struct sockaddr_in *sin4;
+ struct sockaddr_in6 *dst6;
+ struct sockaddr_in *dst4;
+ struct sockaddr_in dstmap;
+
+ dst6 = (struct sockaddr_in6 *)dst;
+ if (dst->sa_family == AF_INET6
+ && IN6_IS_ADDR_V4MAPPED(&dst6->sin6_addr)) {
+ /* ugly... */
+ memset(&dstmap, 0, sizeof(dstmap));
+ dstmap.sin_family = AF_INET;
+ dstmap.sin_len = sizeof(dstmap);
+ memcpy(&dstmap.sin_addr, &dst6->sin6_addr.s6_addr[12],
+ sizeof(dstmap.sin_addr));
+ dst = (struct sockaddr *)&dstmap;
+ }
+
+ dst6 = (struct sockaddr_in6 *)dst;
+ dst4 = (struct sockaddr_in *)dst;
+
+ for (p = myaddrs; p; p = p->next) {
+ sin6 = (struct sockaddr_in6 *)p->addr;
+ sin4 = (struct sockaddr_in *)p->addr;
+
+ if (p->addr->sa_len != dst->sa_len
+ || p->addr->sa_family != dst->sa_family)
+ continue;
+
+ switch (dst->sa_family) {
+ case AF_INET6:
+ if (sin6->sin6_scope_id == dst6->sin6_scope_id
+ && IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &dst6->sin6_addr))
+ return 0;
+ break;
+ case AF_INET:
+ if (sin4->sin_addr.s_addr == dst4->sin_addr.s_addr)
+ return 0;
+ break;
+ }
+ }
+ return 1;
+#endif
+}
+
+/* 0: non faith, 1: faith */
+static int
+map6to4(struct sockaddr_in6 *dst6, struct sockaddr_in *dst4)
+{
+ memset(dst4, 0, sizeof(*dst4));
+ dst4->sin_len = sizeof(*dst4);
+ dst4->sin_family = AF_INET;
+ dst4->sin_port = dst6->sin6_port;
+ memcpy(&dst4->sin_addr, &dst6->sin6_addr.s6_addr[12],
+ sizeof(dst4->sin_addr));
+
+ if (dst4->sin_addr.s_addr == INADDR_ANY
+ || dst4->sin_addr.s_addr == INADDR_BROADCAST
+ || IN_MULTICAST(dst4->sin_addr.s_addr))
+ return 0;
+
+ return 1;
+}
+
+#ifdef FAITH4
+/* 0: non faith, 1: faith */
+static int
+map4to6(struct sockaddr_in *dst4, struct sockaddr_in6 *dst6)
+{
+ char host[NI_MAXHOST];
+ char serv[NI_MAXSERV];
+ struct addrinfo hints, *res;
+ int ai_errno;
+
+ if (getnameinfo((struct sockaddr *)dst4, dst4->sin_len, host, sizeof(host),
+ serv, sizeof(serv), NI_NAMEREQD|NI_NUMERICSERV) != 0)
+ return 0;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = 0;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+
+ if ((ai_errno = getaddrinfo(host, serv, &hints, &res)) != 0) {
+ syslog(LOG_INFO, "%s %s: %s", host, serv, gai_strerror(ai_errno));
+ if (ai_errno == EAI_SYSTEM)
+ syslog(LOG_INFO, "%s %s: %s", host, serv,
+ strerror(errno));
+ return 0;
+ }
+
+ memcpy(dst6, res->ai_addr, res->ai_addrlen);
+
+ freeaddrinfo(res);
+
+ return 1;
+}
+#endif /* FAITH4 */
+
+static void
+sig_child(int sig)
+{
+ int status;
+ pid_t pid;
+
+ pid = wait3(&status, WNOHANG, (struct rusage *)0);
+ if (pid && status)
+ syslog(LOG_WARNING, "child %d exit status 0x%x", pid, status);
+}
+
+void
+sig_terminate(int sig)
+{
+ syslog(LOG_INFO, "Terminating faith daemon");
+ exit(EXIT_SUCCESS);
+}
+
+static void
+start_daemon(void)
+{
+ if (daemon(0, 0) == -1)
+ exit_error("daemon: %s", ERRSTR);
+
+ if (signal(SIGCHLD, sig_child) == SIG_ERR)
+ exit_failure("signal CHLD: %s", ERRSTR);
+
+ if (signal(SIGTERM, sig_terminate) == SIG_ERR)
+ exit_failure("signal TERM: %s", ERRSTR);
+}
+
+void
+exit_error(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[BUFSIZ];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "%s\n", buf);
+ exit(EXIT_FAILURE);
+}
+
+void
+exit_failure(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[BUFSIZ];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ syslog(LOG_ERR, buf);
+ exit(EXIT_FAILURE);
+}
+
+void
+exit_success(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[BUFSIZ];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ syslog(LOG_INFO, buf);
+ exit(EXIT_SUCCESS);
+}
+
+#ifdef USE_ROUTE
+static unsigned int
+if_maxindex()
+{
+ struct if_nameindex *p, *p0;
+ unsigned int max = 0;
+
+ p0 = if_nameindex();
+ for (p = p0; p && p->if_index && p->if_name; p++) {
+ if (max < p->if_index)
+ max = p->if_index;
+ }
+ if_freenameindex(p0);
+ return max;
+}
+
+static void
+grab_myaddrs()
+{
+ int s;
+ unsigned int maxif;
+ struct ifreq *iflist;
+ struct ifconf ifconf;
+ struct ifreq *ifr, *ifr_end;
+ struct myaddrs *p;
+ struct sockaddr_in6 *sin6;
+
+ maxif = if_maxindex() + 1;
+ iflist = (struct ifreq *)malloc(maxif * BUFSIZ); /* XXX */
+ if (!iflist) {
+ exit_failure("not enough core");
+ /*NOTREACHED*/
+ }
+
+ if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+ exit_failure("socket(SOCK_DGRAM)");
+ /*NOTREACHED*/
+ }
+ memset(&ifconf, 0, sizeof(ifconf));
+ ifconf.ifc_req = iflist;
+ ifconf.ifc_len = maxif * BUFSIZ; /* XXX */
+ if (ioctl(s, SIOCGIFCONF, &ifconf) < 0) {
+ exit_failure("ioctl(SIOCGIFCONF)");
+ /*NOTREACHED*/
+ }
+ close(s);
+
+ /* Look for this interface in the list */
+ ifr_end = (struct ifreq *) (ifconf.ifc_buf + ifconf.ifc_len);
+ for (ifr = ifconf.ifc_req;
+ ifr < ifr_end;
+ ifr = (struct ifreq *) ((char *) &ifr->ifr_addr
+ + ifr->ifr_addr.sa_len)) {
+ switch (ifr->ifr_addr.sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ p = (struct myaddrs *)malloc(sizeof(struct myaddrs)
+ + ifr->ifr_addr.sa_len);
+ if (!p) {
+ exit_failure("not enough core");
+ /*NOTREACHED*/
+ }
+ memcpy(p + 1, &ifr->ifr_addr, ifr->ifr_addr.sa_len);
+ p->next = myaddrs;
+ p->addr = (struct sockaddr *)(p + 1);
+#ifdef __KAME__
+ if (ifr->ifr_addr.sa_family == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)p->addr;
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)
+ || IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) {
+ sin6->sin6_scope_id =
+ ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
+ sin6->sin6_addr.s6_addr[2] = 0;
+ sin6->sin6_addr.s6_addr[3] = 0;
+ }
+ }
+#endif
+ myaddrs = p;
+ if (dflag) {
+ char hbuf[NI_MAXHOST];
+ getnameinfo(p->addr, p->addr->sa_len,
+ hbuf, sizeof(hbuf), NULL, 0,
+ NI_NUMERICHOST);
+ syslog(LOG_INFO, "my interface: %s %s", hbuf, ifr->ifr_name);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ free(iflist);
+}
+
+static void
+free_myaddrs()
+{
+ struct myaddrs *p, *q;
+
+ p = myaddrs;
+ while (p) {
+ q = p->next;
+ free(p);
+ p = q;
+ }
+ myaddrs = NULL;
+}
+
+static void
+update_myaddrs()
+{
+ char msg[BUFSIZ];
+ int len;
+ struct rt_msghdr *rtm;
+
+ len = read(sockfd, msg, sizeof(msg));
+ if (len < 0) {
+ syslog(LOG_ERR, "read(PF_ROUTE) failed");
+ return;
+ }
+ rtm = (struct rt_msghdr *)msg;
+ if (len < 4 || len < rtm->rtm_msglen) {
+ syslog(LOG_ERR, "read(PF_ROUTE) short read");
+ return;
+ }
+ if (rtm->rtm_version != RTM_VERSION) {
+ syslog(LOG_ERR, "routing socket version mismatch");
+ close(sockfd);
+ sockfd = 0;
+ return;
+ }
+ switch (rtm->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ case RTM_IFINFO:
+ break;
+ default:
+ return;
+ }
+ /* XXX more filters here? */
+
+ syslog(LOG_INFO, "update interface address list");
+ free_myaddrs();
+ grab_myaddrs();
+}
+#endif /*USE_ROUTE*/
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: %s [-dp] [service [serverpath [serverargs]]]\n",
+ faithdname);
+ exit(0);
+}
diff --git a/usr.sbin/faithd/faithd.h b/usr.sbin/faithd/faithd.h
new file mode 100644
index 0000000..55566ed
--- /dev/null
+++ b/usr.sbin/faithd/faithd.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+extern char logname[];
+extern int dflag;
+
+extern void tcp_relay __P((int, int, const char *));
+extern void ftp_relay __P((int, int));
+extern int ftp_active __P((int, int, int *, int *));
+extern int ftp_passive __P((int, int, int *, int *));
+extern void rsh_relay __P((int, int));
+extern void rsh_dual_relay __P((int, int));
+extern void exit_error __P((const char *fmt, ...));
+extern void exit_success __P((const char *fmt, ...));
+extern void exit_failure __P((const char *fmt, ...));
+
+#define DEFAULT_PORT_NAME "telnet"
+#define DEFAULT_PATH "/usr/local/v6/libexec/telnetd"
+#define DEFAULT_NAME "telnetd"
+
+#define FTP_PORT 21
+#define RLOGIN_PORT 513
+#define RSH_PORT 514
+
+#define RETURN_SUCCESS 0
+#define RETURN_FAILURE 1
+
+#define YES 1
+#define NO 0
+
+#define MSS 2048
+#define MAXARGV 20
+
+#define NUMPRT 0
+#define NUMPRG 1
+#define NUMARG 2
+
+#define UC(b) (((int)b)&0xff)
+
+#define ERRSTR strerror(errno)
+
+#define FAITH_TIMEOUT (30 * 60) /*second*/
diff --git a/usr.sbin/faithd/ftp.c b/usr.sbin/faithd/ftp.c
new file mode 100644
index 0000000..fcb25e3
--- /dev/null
+++ b/usr.sbin/faithd/ftp.c
@@ -0,0 +1,1139 @@
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "faithd.h"
+
+static char rbuf[MSS];
+static char sbuf[MSS];
+static int passivemode = 0;
+static int wport4 = -1; /* listen() to active */
+static int wport6 = -1; /* listen() to passive */
+static int port4 = -1; /* active: inbound passive: outbound */
+static int port6 = -1; /* active: outbound passive: inbound */
+static struct sockaddr_storage data4; /* server data address */
+static struct sockaddr_storage data6; /* client data address */
+static int epsvall = 0;
+
+#ifdef FAITH4
+enum state { NONE, LPRT, EPRT, PORT, LPSV, EPSV, PASV };
+#else
+enum state { NONE, LPRT, EPRT, LPSV, EPSV };
+#endif
+
+static int ftp_activeconn __P((void));
+static int ftp_passiveconn __P((void));
+static int ftp_copy __P((int, int));
+static int ftp_copyresult __P((int, int, enum state));
+static int ftp_copycommand __P((int, int, enum state *));
+
+void
+ftp_relay(int ctl6, int ctl4)
+{
+ fd_set readfds;
+ int error;
+ enum state state = NONE;
+ struct timeval tv;
+
+ syslog(LOG_INFO, "starting ftp control connection");
+
+ for (;;) {
+ FD_ZERO(&readfds);
+ FD_SET(ctl4, &readfds);
+ FD_SET(ctl6, &readfds);
+ if (0 <= port4)
+ FD_SET(port4, &readfds);
+ if (0 <= port6)
+ FD_SET(port6, &readfds);
+#if 0
+ if (0 <= wport4)
+ FD_SET(wport4, &readfds);
+ if (0 <= wport6)
+ FD_SET(wport6, &readfds);
+#endif
+ tv.tv_sec = FAITH_TIMEOUT;
+ tv.tv_usec = 0;
+
+ error = select(256, &readfds, NULL, NULL, &tv);
+ if (error == -1)
+ exit_failure("select: %s", ERRSTR);
+ else if (error == 0)
+ exit_failure("connection timeout");
+
+ /*
+ * The order of the following checks does (slightly) matter.
+ * It is important to visit all checks (do not use "continue"),
+ * otherwise some of the pipe may become full and we cannot
+ * relay correctly.
+ */
+ if (FD_ISSET(ctl6, &readfds)) {
+ /*
+ * copy control connection from the client.
+ * command translation is necessary.
+ */
+ error = ftp_copycommand(ctl6, ctl4, &state);
+
+ switch (error) {
+ case -1:
+ goto bad;
+ case 0:
+ close(ctl4);
+ close(ctl6);
+ exit_success("terminating ftp control connection");
+ /*NOTREACHED*/
+ default:
+ break;
+ }
+ }
+ if (FD_ISSET(ctl4, &readfds)) {
+ /*
+ * copy control connection from the server
+ * translation of result code is necessary.
+ */
+ error = ftp_copyresult(ctl4, ctl6, state);
+
+ switch (error) {
+ case -1:
+ goto bad;
+ case 0:
+ close(ctl4);
+ close(ctl6);
+ exit_success("terminating ftp control connection");
+ /*NOTREACHED*/
+ default:
+ break;
+ }
+ }
+ if (0 <= port4 && 0 <= port6 && FD_ISSET(port4, &readfds)) {
+ /*
+ * copy data connection.
+ * no special treatment necessary.
+ */
+ if (FD_ISSET(port4, &readfds))
+ error = ftp_copy(port4, port6);
+ switch (error) {
+ case -1:
+ goto bad;
+ case 0:
+ close(port4);
+ close(port6);
+ port4 = port6 = -1;
+ syslog(LOG_INFO, "terminating data connection");
+ break;
+ default:
+ break;
+ }
+ }
+ if (0 <= port4 && 0 <= port6 && FD_ISSET(port6, &readfds)) {
+ /*
+ * copy data connection.
+ * no special treatment necessary.
+ */
+ if (FD_ISSET(port6, &readfds))
+ error = ftp_copy(port6, port4);
+ switch (error) {
+ case -1:
+ goto bad;
+ case 0:
+ close(port4);
+ close(port6);
+ port4 = port6 = -1;
+ syslog(LOG_INFO, "terminating data connection");
+ break;
+ default:
+ break;
+ }
+ }
+#if 0
+ if (wport4 && FD_ISSET(wport4, &readfds)) {
+ /*
+ * establish active data connection from the server.
+ */
+ ftp_activeconn();
+ }
+ if (wport6 && FD_ISSET(wport6, &readfds)) {
+ /*
+ * establish passive data connection from the client.
+ */
+ ftp_passiveconn();
+ }
+#endif
+ }
+
+ bad:
+ exit_failure(ERRSTR);
+}
+
+static int
+ftp_activeconn()
+{
+ int n;
+ int error;
+ fd_set set;
+ struct timeval timeout;
+ struct sockaddr *sa;
+
+ /* get active connection from server */
+ FD_ZERO(&set);
+ FD_SET(wport4, &set);
+ timeout.tv_sec = 120;
+ timeout.tv_usec = -1;
+ n = sizeof(data4);
+ if (select(wport4 + 1, &set, NULL, NULL, &timeout) == 0
+ || (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0) {
+ close(wport4);
+ wport4 = -1;
+ syslog(LOG_INFO, "active mode data connection failed");
+ return -1;
+ }
+
+ /* ask active connection to client */
+ sa = (struct sockaddr *)&data6;
+ port6 = socket(sa->sa_family, SOCK_STREAM, 0);
+ if (port6 == -1) {
+ close(port4);
+ close(wport4);
+ port4 = wport4 = -1;
+ syslog(LOG_INFO, "active mode data connection failed");
+ return -1;
+ }
+ error = connect(port6, sa, sa->sa_len);
+ if (port6 == -1) {
+ close(port6);
+ close(port4);
+ close(wport4);
+ port6 = port4 = wport4 = -1;
+ syslog(LOG_INFO, "active mode data connection failed");
+ return -1;
+ }
+
+ syslog(LOG_INFO, "active mode data connection established");
+ return 0;
+}
+
+static int
+ftp_passiveconn()
+{
+ int n;
+ int error;
+ fd_set set;
+ struct timeval timeout;
+ struct sockaddr *sa;
+
+ /* get passive connection from client */
+ FD_ZERO(&set);
+ FD_SET(wport6, &set);
+ timeout.tv_sec = 120;
+ timeout.tv_usec = 0;
+ n = sizeof(data6);
+ if (select(wport6 + 1, &set, NULL, NULL, &timeout) == 0
+ || (port6 = accept(wport6, (struct sockaddr *)&data6, &n)) < 0) {
+ close(wport6);
+ wport6 = -1;
+ syslog(LOG_INFO, "passive mode data connection failed");
+ return -1;
+ }
+
+ /* ask passive connection to server */
+ sa = (struct sockaddr *)&data4;
+ port4 = socket(sa->sa_family, SOCK_STREAM, 0);
+ if (port4 == -1) {
+ close(wport6);
+ close(port6);
+ wport6 = port6 = -1;
+ syslog(LOG_INFO, "passive mode data connection failed");
+ return -1;
+ }
+ error = connect(port4, sa, sa->sa_len);
+ if (port4 == -1) {
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport6 = port4 = port6 = -1;
+ syslog(LOG_INFO, "passive mode data connection failed");
+ return -1;
+ }
+
+ syslog(LOG_INFO, "passive mode data connection established");
+ return 0;
+}
+
+static int
+ftp_copy(int src, int dst)
+{
+ int error, atmark;
+ int n;
+
+ /* OOB data handling */
+ error = ioctl(src, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ n = read(src, rbuf, 1);
+ if (n == -1)
+ goto bad;
+ send(dst, rbuf, n, MSG_OOB);
+#if 0
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n == -1)
+ goto bad;
+ write(dst, rbuf, n);
+ return n;
+#endif
+ }
+
+ n = read(src, rbuf, sizeof(rbuf));
+ switch (n) {
+ case -1:
+ case 0:
+ return n;
+ default:
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ bad:
+ exit_failure(ERRSTR);
+ /*NOTREACHED*/
+ return 0; /* to make gcc happy */
+}
+
+static int
+ftp_copyresult(int src, int dst, enum state state)
+{
+ int error, atmark;
+ int n;
+ char *param;
+ int code;
+
+ /* OOB data handling */
+ error = ioctl(src, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ n = read(src, rbuf, 1);
+ if (n == -1)
+ goto bad;
+ send(dst, rbuf, n, MSG_OOB);
+#if 0
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n == -1)
+ goto bad;
+ write(dst, rbuf, n);
+ return n;
+#endif
+ }
+
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n <= 0)
+ return n;
+ rbuf[n] = '\0';
+
+ /*
+ * parse argument
+ */
+ {
+ char *p;
+ int i;
+
+ p = rbuf;
+ for (i = 0; i < 3; i++) {
+ if (!isdigit(*p)) {
+ /* invalid reply */
+ write(dst, rbuf, n);
+ return n;
+ }
+ p++;
+ }
+ if (!isspace(*p)) {
+ /* invalid reply */
+ write(dst, rbuf, n);
+ return n;
+ }
+ code = atoi(rbuf);
+ param = p;
+ /* param points to first non-command token, if any */
+ while (*param && isspace(*param))
+ param++;
+ if (!*param)
+ param = NULL;
+ }
+
+ switch (state) {
+ case NONE:
+ if (!passivemode && rbuf[0] == '1') {
+ if (ftp_activeconn() < 0) {
+ n = snprintf(rbuf, sizeof(rbuf),
+ "425 Cannot open data connetion\r\n");
+ }
+ }
+ write(dst, rbuf, n);
+ return n;
+ case LPRT:
+ case EPRT:
+ /* expecting "200 PORT command successful." */
+ if (code == 200) {
+ char *p;
+
+ p = strstr(rbuf, "PORT");
+ if (p) {
+ p[0] = (state == LPRT) ? 'L' : 'E';
+ p[1] = 'P';
+ }
+ } else {
+ close(wport4);
+ wport4 = -1;
+ }
+ write(dst, rbuf, n);
+ return n;
+#ifdef FAITH4
+ case PORT:
+ /* expecting "200 EPRT command successful." */
+ if (code == 200) {
+ char *p;
+
+ p = strstr(rbuf, "EPRT");
+ if (p) {
+ p[0] = 'P';
+ p[1] = 'O';
+ }
+ } else {
+ close(wport4);
+ wport4 = -1;
+ }
+ write(dst, rbuf, n);
+ return n;
+#endif
+ case LPSV:
+ case EPSV:
+ /* expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)" */
+ if (code != 227) {
+passivefail0:
+ close(wport6);
+ wport6 = -1;
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ {
+ unsigned int ho[4], po[2];
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ u_short port;
+ char *p;
+
+ /*
+ * PASV result -> LPSV/EPSV result
+ */
+ p = param;
+ while (*p && *p != '(')
+ p++;
+ if (!*p)
+ goto passivefail0; /*XXX*/
+ p++;
+ n = sscanf(p, "%u,%u,%u,%u,%u,%u",
+ &ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
+ if (n != 6)
+ goto passivefail0; /*XXX*/
+
+ /* keep PORT parameter */
+ memset(&data4, 0, sizeof(data4));
+ sin = (struct sockaddr_in *)&data4;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = 0;
+ for (n = 0; n < 4; n++) {
+ sin->sin_addr.s_addr |=
+ htonl((ho[n] & 0xff) << ((3 - n) * 8));
+ }
+ sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
+
+ /* get ready for passive data connection */
+ memset(&data6, 0, sizeof(data6));
+ sin6 = (struct sockaddr_in6 *)&data6;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0);
+ if (wport6 == -1) {
+passivefail:
+ n = snprintf(sbuf, sizeof(sbuf),
+ "500 could not translate from PASV\r\n");
+ write(src, sbuf, n);
+ return n;
+ }
+#ifdef IPV6_FAITH
+ {
+ int on = 1;
+ error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH,
+ &on, sizeof(on));
+ if (error == -1)
+ exit_error("setsockopt(IPV6_FAITH): %s", ERRSTR);
+ }
+#endif
+ error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail;
+ }
+ error = listen(wport6, 1);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail;
+ }
+
+ /* transmit LPSV or EPSV */
+ /*
+ * addr from dst, port from wport6
+ */
+ n = sizeof(data6);
+ error = getsockname(wport6, (struct sockaddr *)&data6, &n);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail;
+ }
+ sin6 = (struct sockaddr_in6 *)&data6;
+ port = sin6->sin6_port;
+
+ n = sizeof(data6);
+ error = getsockname(dst, (struct sockaddr *)&data6, &n);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail;
+ }
+ sin6 = (struct sockaddr_in6 *)&data6;
+ sin6->sin6_port = port;
+
+ if (state == LPSV) {
+ char *a, *p;
+
+ a = (char *)&sin6->sin6_addr;
+ p = (char *)&sin6->sin6_port;
+ n = snprintf(sbuf, sizeof(sbuf),
+"228 Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)\r\n",
+ 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
+ UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
+ UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
+ 2, UC(p[0]), UC(p[1]));
+ write(dst, sbuf, n);
+ passivemode = 1;
+ return n;
+ } else {
+ n = snprintf(sbuf, sizeof(sbuf),
+"229 Entering Extended Passive Mode (|||%d|)\r\n",
+ ntohs(sin6->sin6_port));
+ write(dst, sbuf, n);
+ passivemode = 1;
+ return n;
+ }
+ }
+#ifdef FAITH4
+ case PASV:
+ /* expecting "229 Entering Extended Passive Mode (|||x|)" */
+ if (code != 229) {
+passivefail1:
+ close(wport6);
+ wport6 = -1;
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ {
+ u_short port;
+ char *p;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+
+ /*
+ * EPSV result -> PORT result
+ */
+ p = param;
+ while (*p && *p != '(')
+ p++;
+ if (!*p)
+ goto passivefail1; /*XXX*/
+ p++;
+ n = sscanf(p, "|||%hu|", &port);
+ if (n != 1)
+ goto passivefail1; /*XXX*/
+
+ /* keep EPRT parameter */
+ n = sizeof(data4);
+ error = getpeername(src, (struct sockaddr *)&data4, &n);
+ if (error == -1)
+ goto passivefail1; /*XXX*/
+ sin6 = (struct sockaddr_in6 *)&data4;
+ sin6->sin6_port = htons(port);
+
+ /* get ready for passive data connection */
+ memset(&data6, 0, sizeof(data6));
+ sin = (struct sockaddr_in *)&data6;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ wport6 = socket(sin->sin_family, SOCK_STREAM, 0);
+ if (wport6 == -1) {
+passivefail2:
+ n = snprintf(sbuf, sizeof(sbuf),
+ "500 could not translate from EPSV\r\n");
+ write(src, sbuf, n);
+ return n;
+ }
+#ifdef IP_FAITH
+ {
+ int on = 1;
+ error = setsockopt(wport6, IPPROTO_IP, IP_FAITH,
+ &on, sizeof(on));
+ if (error == -1)
+ exit_error("setsockopt(IP_FAITH): %s", ERRSTR);
+ }
+#endif
+ error = bind(wport6, (struct sockaddr *)sin, sin->sin_len);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail2;
+ }
+ error = listen(wport6, 1);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail2;
+ }
+
+ /* transmit PORT */
+ /*
+ * addr from dst, port from wport6
+ */
+ n = sizeof(data6);
+ error = getsockname(wport6, (struct sockaddr *)&data6, &n);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail2;
+ }
+ sin = (struct sockaddr_in *)&data6;
+ port = sin->sin_port;
+
+ n = sizeof(data6);
+ error = getsockname(dst, (struct sockaddr *)&data6, &n);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail2;
+ }
+ sin = (struct sockaddr_in *)&data6;
+ sin->sin_port = port;
+
+ {
+ char *a, *p;
+
+ a = (char *)&sin->sin_addr;
+ p = (char *)&sin->sin_port;
+ n = snprintf(sbuf, sizeof(sbuf),
+"227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(p[0]), UC(p[1]));
+ write(dst, sbuf, n);
+ passivemode = 1;
+ return n;
+ }
+ }
+#endif /* FAITH4 */
+ }
+
+ bad:
+ exit_failure(ERRSTR);
+ /*NOTREACHED*/
+ return 0; /* to make gcc happy */
+}
+
+static int
+ftp_copycommand(int src, int dst, enum state *state)
+{
+ int error, atmark;
+ int n;
+ unsigned int af, hal, ho[16], pal, po[2];
+ char *a, *p;
+ char cmd[5], *param;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ enum state nstate;
+ char ch;
+
+ /* OOB data handling */
+ error = ioctl(src, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ n = read(src, rbuf, 1);
+ if (n == -1)
+ goto bad;
+ send(dst, rbuf, n, MSG_OOB);
+#if 0
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n == -1)
+ goto bad;
+ write(dst, rbuf, n);
+ return n;
+#endif
+ }
+
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n <= 0)
+ return n;
+ rbuf[n] = '\0';
+
+ if (n < 4) {
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ /*
+ * parse argument
+ */
+ {
+ char *p, *q;
+ int i;
+
+ p = rbuf;
+ q = cmd;
+ for (i = 0; i < 4; i++) {
+ if (!isalpha(*p)) {
+ /* invalid command */
+ write(dst, rbuf, n);
+ return n;
+ }
+ *q++ = islower(*p) ? toupper(*p) : *p;
+ p++;
+ }
+ if (!isspace(*p)) {
+ /* invalid command */
+ write(dst, rbuf, n);
+ return n;
+ }
+ *q = '\0';
+ param = p;
+ /* param points to first non-command token, if any */
+ while (*param && isspace(*param))
+ param++;
+ if (!*param)
+ param = NULL;
+ }
+
+ *state = NONE;
+
+ if (strcmp(cmd, "LPRT") == 0 && param) {
+ /*
+ * LPRT -> PORT
+ */
+ nstate = LPRT;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ if (epsvall) {
+ n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
+ cmd);
+ write(src, sbuf, n);
+ return n;
+ }
+
+ n = sscanf(param,
+"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u",
+ &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3],
+ &ho[4], &ho[5], &ho[6], &ho[7],
+ &ho[8], &ho[9], &ho[10], &ho[11],
+ &ho[12], &ho[13], &ho[14], &ho[15],
+ &pal, &po[0], &po[1]);
+ if (n != 21 || af != 6 || hal != 16|| pal != 2) {
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 illegal parameter to LPRT\r\n");
+ write(src, sbuf, n);
+ return n;
+ }
+
+ /* keep LPRT parameter */
+ memset(&data6, 0, sizeof(data6));
+ sin6 = (struct sockaddr_in6 *)&data6;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ for (n = 0; n < 16; n++)
+ sin6->sin6_addr.s6_addr[n] = ho[n];
+ sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
+
+sendport:
+ /* get ready for active data connection */
+ n = sizeof(data4);
+ error = getsockname(dst, (struct sockaddr *)&data4, &n);
+ if (error == -1) {
+lprtfail:
+ n = snprintf(sbuf, sizeof(sbuf),
+ "500 could not translate to PORT\r\n");
+ write(src, sbuf, n);
+ return n;
+ }
+ if (((struct sockaddr *)&data4)->sa_family != AF_INET)
+ goto lprtfail;
+ sin = (struct sockaddr_in *)&data4;
+ sin->sin_port = 0;
+ wport4 = socket(sin->sin_family, SOCK_STREAM, 0);
+ if (wport4 == -1)
+ goto lprtfail;
+ error = bind(wport4, (struct sockaddr *)sin, sin->sin_len);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto lprtfail;
+ }
+ error = listen(wport4, 1);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto lprtfail;
+ }
+
+ /* transmit PORT */
+ n = sizeof(data4);
+ error = getsockname(wport4, (struct sockaddr *)&data4, &n);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto lprtfail;
+ }
+ if (((struct sockaddr *)&data4)->sa_family != AF_INET) {
+ close(wport4);
+ wport4 = -1;
+ goto lprtfail;
+ }
+ sin = (struct sockaddr_in *)&data4;
+ a = (char *)&sin->sin_addr;
+ p = (char *)&sin->sin_port;
+ n = snprintf(sbuf, sizeof(sbuf), "PORT %d,%d,%d,%d,%d,%d\r\n",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(p[0]), UC(p[1]));
+ write(dst, sbuf, n);
+ *state = nstate;
+ passivemode = 0;
+ return n;
+ } else if (strcmp(cmd, "EPRT") == 0 && param) {
+ /*
+ * EPRT -> PORT
+ */
+ char *afp, *hostp, *portp;
+ struct addrinfo hints, *res;
+
+ nstate = EPRT;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ if (epsvall) {
+ n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
+ cmd);
+ write(src, sbuf, n);
+ return n;
+ }
+
+ p = param;
+ ch = *p++; /* boundary character */
+ afp = p;
+ while (*p && *p != ch)
+ p++;
+ if (!*p) {
+eprtparamfail:
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 illegal parameter to EPRT\r\n");
+ write(src, sbuf, n);
+ return n;
+ }
+ *p++ = '\0';
+ hostp = p;
+ while (*p && *p != ch)
+ p++;
+ if (!*p)
+ goto eprtparamfail;
+ *p++ = '\0';
+ portp = p;
+ while (*p && *p != ch)
+ p++;
+ if (!*p)
+ goto eprtparamfail;
+ *p++ = '\0';
+
+ n = sscanf(afp, "%d", &af);
+ if (n != 1 || af != 2) {
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 unsupported address family to EPRT\r\n");
+ write(src, sbuf, n);
+ return n;
+ }
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ error = getaddrinfo(hostp, portp, &hints, &res);
+ if (error) {
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 EPRT: %s", gai_strerror(error));
+ if (error == EAI_SYSTEM)
+ n += snprintf(sbuf, sizeof(sbuf),
+ ": %s", strerror(errno));
+ n += snprintf(sbuf, sizeof(sbuf), "\r\n");
+
+ write(src, sbuf, n);
+ return n;
+ }
+ if (res->ai_next) {
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 EPRT: %s resolved to multiple addresses\r\n", hostp);
+ write(src, sbuf, n);
+ return n;
+ }
+
+ memcpy(&data6, res->ai_addr, res->ai_addrlen);
+
+ goto sendport;
+ } else if (strcmp(cmd, "LPSV") == 0 && !param) {
+ /*
+ * LPSV -> PASV
+ */
+ nstate = LPSV;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ if (epsvall) {
+ n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
+ cmd);
+ write(src, sbuf, n);
+ return n;
+ }
+
+ /* transmit PASV */
+ n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
+ write(dst, sbuf, n);
+ *state = LPSV;
+ passivemode = 0; /* to be set to 1 later */
+ return n;
+ } else if (strcmp(cmd, "EPSV") == 0 && !param) {
+ /*
+ * EPSV -> PASV
+ */
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
+ write(dst, sbuf, n);
+ *state = EPSV;
+ passivemode = 0; /* to be set to 1 later */
+ return n;
+ } else if (strcmp(cmd, "EPSV") == 0 && param
+ && strncasecmp(param, "ALL", 3) == 0 && isspace(param[3])) {
+ /*
+ * EPSV ALL
+ */
+ epsvall = 1;
+ n = snprintf(sbuf, sizeof(sbuf), "200 EPSV ALL command successful.\r\n");
+ write(src, sbuf, n);
+ return n;
+#ifdef FAITH4
+ } else if (strcmp(cmd, "PORT") == 0 && param) {
+ /*
+ * PORT -> EPRT
+ */
+ char host[NI_MAXHOST], serv[NI_MAXSERV];
+
+ nstate = PORT;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ p = param;
+ n = sscanf(p, "%u,%u,%u,%u,%u,%u",
+ &ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
+ if (n != 6) {
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 illegal parameter to PORT\r\n");
+ write(src, sbuf, n);
+ return n;
+ }
+
+ memset(&data6, 0, sizeof(data6));
+ sin = (struct sockaddr_in *)&data6;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = htonl(
+ ((ho[0] & 0xff) << 24) | ((ho[1] & 0xff) << 16) |
+ ((ho[2] & 0xff) << 8) | (ho[3] & 0xff));
+ sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
+
+ /* get ready for active data connection */
+ n = sizeof(data4);
+ error = getsockname(dst, (struct sockaddr *)&data4, &n);
+ if (error == -1) {
+portfail:
+ n = snprintf(sbuf, sizeof(sbuf),
+ "500 could not translate to EPRT\r\n");
+ write(src, sbuf, n);
+ return n;
+ }
+ if (((struct sockaddr *)&data4)->sa_family != AF_INET6)
+ goto portfail;
+
+ ((struct sockaddr_in6 *)&data4)->sin6_port = 0;
+ sa = (struct sockaddr *)&data4;
+ wport4 = socket(sa->sa_family, SOCK_STREAM, 0);
+ if (wport4 == -1)
+ goto portfail;
+ error = bind(wport4, sa, sa->sa_len);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto portfail;
+ }
+ error = listen(wport4, 1);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto portfail;
+ }
+
+ /* transmit EPRT */
+ n = sizeof(data4);
+ error = getsockname(wport4, (struct sockaddr *)&data4, &n);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto portfail;
+ }
+ af = 2;
+ sa = (struct sockaddr *)&data4;
+ if (getnameinfo(sa, sa->sa_len, host, sizeof(host),
+ serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV)) {
+ close(wport4);
+ wport4 = -1;
+ goto portfail;
+ }
+ n = snprintf(sbuf, sizeof(sbuf), "EPRT |%d|%s|%s|\r\n", af, host, serv);
+ write(dst, sbuf, n);
+ *state = nstate;
+ passivemode = 0;
+ return n;
+ } else if (strcmp(cmd, "PASV") == 0 && !param) {
+ /*
+ * PASV -> EPSV
+ */
+
+ nstate = PASV;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ /* transmit EPSV */
+ n = snprintf(sbuf, sizeof(sbuf), "EPSV\r\n");
+ write(dst, sbuf, n);
+ *state = PASV;
+ passivemode = 0; /* to be set to 1 later */
+ return n;
+#else /* FAITH4 */
+ } else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) {
+ /*
+ * reject PORT/PASV
+ */
+ n = snprintf(sbuf, sizeof(sbuf), "502 %s not implemented.\r\n", cmd);
+ write(src, sbuf, n);
+ return n;
+#endif /* FAITH4 */
+ } else if (passivemode
+ && (strcmp(cmd, "STOR") == 0
+ || strcmp(cmd, "STOU") == 0
+ || strcmp(cmd, "RETR") == 0
+ || strcmp(cmd, "LIST") == 0
+ || strcmp(cmd, "NLST") == 0
+ || strcmp(cmd, "APPE") == 0)) {
+ /*
+ * commands with data transfer. need to care about passive
+ * mode data connection.
+ */
+
+ if (ftp_passiveconn() < 0) {
+ n = snprintf(sbuf, sizeof(sbuf), "425 Cannot open data connetion\r\n");
+ write(src, sbuf, n);
+ } else {
+ /* simply relay the command */
+ write(dst, rbuf, n);
+ }
+
+ *state = NONE;
+ return n;
+ } else {
+ /* simply relay it */
+ *state = NONE;
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ bad:
+ exit_failure(ERRSTR);
+ /*NOTREACHED*/
+ return 0; /* to make gcc happy */
+}
diff --git a/usr.sbin/faithd/rsh.c b/usr.sbin/faithd/rsh.c
new file mode 100644
index 0000000..c6e8357
--- /dev/null
+++ b/usr.sbin/faithd/rsh.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "faithd.h"
+
+char rshbuf[MSS];
+
+int s_ctl, s_ctl6, s_rcv, s_snd;
+int half;
+
+void
+rsh_relay(int s_src, int s_dst)
+{
+ ssize_t n;
+ fd_set readfds;
+ int error;
+ struct timeval tv;
+
+ FD_ZERO(&readfds);
+ FD_SET(s_src, &readfds);
+ tv.tv_sec = FAITH_TIMEOUT;
+ tv.tv_usec = 0;
+ error = select(256, &readfds, NULL, NULL, &tv);
+ if (error == -1)
+ exit_failure("select %d: %s", s_src, ERRSTR);
+ else if (error == 0)
+ exit_failure("connecion timeout");
+
+ n = read(s_src, rshbuf, sizeof(rshbuf));
+ if (rshbuf[0] != 0) {
+ rsh_dual_relay(s_src, s_dst);
+ /* NOTREACHED */
+ }
+ write(s_dst, rshbuf, n);
+ tcp_relay(s_src, s_dst, "rsh");
+ /* NOTREACHED */
+}
+
+static void
+relay(int src, int dst)
+{
+ int error;
+ ssize_t n;
+ int atmark;
+
+ error = ioctl(s_rcv, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ n = read(s_rcv, rshbuf, 1);
+ if (n == 1)
+ send(s_snd, rshbuf, 1, MSG_OOB);
+ return;
+ }
+
+ n = read(s_rcv, rshbuf, sizeof(rshbuf));
+
+ switch (n) {
+ case -1:
+ exit_failure(ERRSTR);
+ case 0:
+ if (s_rcv == src) {
+ /* half close */
+ shutdown(dst, 1);
+ half = YES;
+ break;
+ }
+ close(src);
+ close(dst);
+ close(s_ctl);
+ close(s_ctl6);
+ exit_success("terminating rsh/contorol connections");
+ break;
+ default:
+ write(s_snd, rshbuf, n);
+ }
+}
+
+void
+rsh_dual_relay(int s_src, int s_dst)
+{
+ fd_set readfds;
+ int len, s_wld, error;
+ struct sockaddr_storage ctladdr6;
+ struct sockaddr_storage ctladdr;
+ int port6 = 0, lport, lport6;
+ char *p;
+ struct timeval tv;
+ struct sockaddr *sa;
+
+ half = NO;
+ s_rcv = s_src;
+ s_snd = s_dst;
+ syslog(LOG_INFO, "starting rsh connection");
+
+ for (p = rshbuf; *p; p++)
+ port6 = port6 * 10 + *p - '0';
+
+ len = sizeof(ctladdr6);
+ getpeername(s_src, (struct sockaddr *)&ctladdr6, &len);
+ if (((struct sockaddr *)&ctladdr6)->sa_family == AF_INET6)
+ ((struct sockaddr_in6 *)&ctladdr6)->sin6_port = htons(port6);
+ else
+ ((struct sockaddr_in *)&ctladdr6)->sin_port = htons(port6);
+
+ s_wld = rresvport(&lport);
+ if (s_wld == -1) goto bad;
+ error = listen(s_wld, 1);
+ if (error == -1) goto bad;
+ snprintf(rshbuf, sizeof(rshbuf), "%d", lport);
+ write(s_dst, rshbuf, strlen(rshbuf)+1);
+
+ len = sizeof(ctladdr);
+ s_ctl = accept(s_wld, (struct sockaddr *)&ctladdr, &len);
+ if (s_ctl == -1) goto bad;
+ close(s_wld);
+
+ sa = (struct sockaddr *)&ctladdr6;
+ s_ctl6 = rresvport_af(&lport6, sa->sa_family);
+ if (s_ctl6 == -1) goto bad;
+ error = connect(s_ctl6, sa, sa->sa_len);
+ if (error == -1) goto bad;
+
+ syslog(LOG_INFO, "starting rsh control connection");
+
+ for (;;) {
+ FD_ZERO(&readfds);
+ if (half == NO)
+ FD_SET(s_src, &readfds);
+ FD_SET(s_dst, &readfds);
+ FD_SET(s_ctl, &readfds);
+ FD_SET(s_ctl6, &readfds);
+ tv.tv_sec = FAITH_TIMEOUT;
+ tv.tv_usec = 0;
+
+ error = select(256, &readfds, NULL, NULL, &tv);
+ if (error == -1)
+ exit_failure("select 4 sockets: %s", ERRSTR);
+ else if (error == 0)
+ exit_failure("connecion timeout");
+
+ if (half == NO && FD_ISSET(s_src, &readfds)) {
+ s_rcv = s_src;
+ s_snd = s_dst;
+ relay(s_src, s_dst);
+ }
+ if (FD_ISSET(s_dst, &readfds)) {
+ s_rcv = s_dst;
+ s_snd = s_src;
+ relay(s_src, s_dst);
+ }
+ if (FD_ISSET(s_ctl, &readfds)) {
+ s_rcv = s_ctl;
+ s_snd = s_ctl6;
+ relay(s_src, s_dst);
+ }
+ if (FD_ISSET(s_ctl6, &readfds)) {
+ s_rcv = s_ctl6;
+ s_snd = s_ctl;
+ relay(s_src, s_dst);
+ }
+ }
+ /* NOTREACHED */
+
+ bad:
+ exit_failure(ERRSTR);
+}
diff --git a/usr.sbin/faithd/tcp.c b/usr.sbin/faithd/tcp.c
new file mode 100644
index 0000000..e1e1b32
--- /dev/null
+++ b/usr.sbin/faithd/tcp.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "faithd.h"
+
+static char tcpbuf[16*1024];
+ /* bigger than MSS and may be lesser than window size */
+static int tblen, tboff, oob_exists;
+static fd_set readfds, writefds, exceptfds;
+static char atmark_buf[2];
+static pid_t cpid = (pid_t)0;
+static pid_t ppid = (pid_t)0;
+static time_t child_lastactive = (time_t)0;
+static time_t parent_lastactive = (time_t)0;
+
+static void sig_ctimeout __P((int));
+static void sig_child __P((int));
+static void notify_inactive __P((void));
+static void notify_active __P((void));
+static void send_data __P((int, int, const char *, int));
+static void relay __P((int, int, const char *, int));
+
+/*
+ * Inactivity timer:
+ * - child side (ppid != 0) will send SIGUSR1 to parent every (FAITH_TIMEOUT/4)
+ * second if traffic is active. if traffic is inactive, don't send SIGUSR1.
+ * - parent side (ppid == 0) will check the last SIGUSR1 it have seen.
+ */
+static void
+sig_ctimeout(int sig)
+{
+ /* parent side: record notification from the child */
+ if (dflag)
+ syslog(LOG_DEBUG, "activity timer from child");
+ child_lastactive = time(NULL);
+}
+
+/* parent will terminate if child dies. */
+static void
+sig_child(int sig)
+{
+ int status;
+ pid_t pid;
+
+ pid = wait3(&status, WNOHANG, (struct rusage *)0);
+ if (pid && status)
+ syslog(LOG_WARNING, "child %d exit status 0x%x", pid, status);
+ exit_failure("terminate connection due to child termination");
+}
+
+static void
+notify_inactive()
+{
+ time_t t;
+
+ /* only on parent side... */
+ if (ppid)
+ return;
+
+ /* parent side should check for timeout. */
+ t = time(NULL);
+ if (dflag) {
+ syslog(LOG_DEBUG, "parent side %sactive, child side %sactive",
+ (FAITH_TIMEOUT < t - parent_lastactive) ? "in" : "",
+ (FAITH_TIMEOUT < t - child_lastactive) ? "in" : "");
+ }
+
+ if (FAITH_TIMEOUT < t - child_lastactive
+ && FAITH_TIMEOUT < t - parent_lastactive) {
+ /* both side timeouted */
+ signal(SIGCHLD, SIG_DFL);
+ kill(cpid, SIGTERM);
+ wait(NULL);
+ exit_failure("connection timeout");
+ /* NOTREACHED */
+ }
+}
+
+static void
+notify_active()
+{
+ if (ppid) {
+ /* child side: notify parent of active traffic */
+ time_t t;
+ t = time(NULL);
+ if (FAITH_TIMEOUT / 4 < t - child_lastactive) {
+ if (kill(ppid, SIGUSR1) < 0) {
+ exit_failure("terminate connection due to parent termination");
+ /* NOTREACHED */
+ }
+ child_lastactive = t;
+ }
+ } else {
+ /* parent side */
+ parent_lastactive = time(NULL);
+ }
+}
+
+static void
+send_data(int s_rcv, int s_snd, const char *service, int direction)
+{
+ int cc;
+
+ if (oob_exists) {
+ cc = send(s_snd, atmark_buf, 1, MSG_OOB);
+ if (cc == -1)
+ goto retry_or_err;
+ oob_exists = 0;
+ FD_SET(s_rcv, &exceptfds);
+ }
+
+ for (; tboff < tblen; tboff += cc) {
+ cc = write(s_snd, tcpbuf + tboff, tblen - tboff);
+ if (cc < 0)
+ goto retry_or_err;
+ }
+#ifdef DEBUG
+ if (tblen) {
+ if (tblen >= sizeof(tcpbuf))
+ tblen = sizeof(tcpbuf) - 1;
+ tcpbuf[tblen] = '\0';
+ syslog(LOG_DEBUG, "from %s (%dbytes): %s",
+ direction == 1 ? "client" : "server", tblen, tcpbuf);
+ }
+#endif /* DEBUG */
+ tblen = 0; tboff = 0;
+ FD_CLR(s_snd, &writefds);
+ FD_SET(s_rcv, &readfds);
+ return;
+ retry_or_err:
+ if (errno != EAGAIN)
+ exit_failure("writing relay data failed: %s", ERRSTR);
+ FD_SET(s_snd, &writefds);
+}
+
+static void
+relay(int s_rcv, int s_snd, const char *service, int direction)
+{
+ int atmark, error, maxfd;
+ struct timeval tv;
+ fd_set oreadfds, owritefds, oexceptfds;
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
+ fcntl(s_snd, F_SETFD, O_NONBLOCK);
+ oreadfds = readfds; owritefds = writefds; oexceptfds = exceptfds;
+ FD_SET(s_rcv, &readfds); FD_SET(s_rcv, &exceptfds);
+ oob_exists = 0;
+ maxfd = (s_rcv > s_snd) ? s_rcv : s_snd;
+
+ for (;;) {
+ tv.tv_sec = FAITH_TIMEOUT / 4;
+ tv.tv_usec = 0;
+ oreadfds = readfds;
+ owritefds = writefds;
+ oexceptfds = exceptfds;
+ error = select(maxfd + 1, &readfds, &writefds, &exceptfds, &tv);
+ if (error == -1) {
+ if (errno == EINTR)
+ continue;
+ exit_failure("select: %s", ERRSTR);
+ } else if (error == 0) {
+ readfds = oreadfds;
+ writefds = owritefds;
+ exceptfds = oexceptfds;
+ notify_inactive();
+ continue;
+ }
+
+ /* activity notification */
+ notify_active();
+
+ if (FD_ISSET(s_rcv, &exceptfds)) {
+ error = ioctl(s_rcv, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ int cc;
+ oob_read_retry:
+ cc = read(s_rcv, atmark_buf, 1);
+ if (cc == 1) {
+ FD_CLR(s_rcv, &exceptfds);
+ FD_SET(s_snd, &writefds);
+ oob_exists = 1;
+ } else if (cc == -1) {
+ if (errno == EINTR)
+ goto oob_read_retry;
+ exit_failure("reading oob data failed"
+ ": %s",
+ ERRSTR);
+ }
+ }
+ }
+ if (FD_ISSET(s_rcv, &readfds)) {
+ relaydata_read_retry:
+ tblen = read(s_rcv, tcpbuf, sizeof(tcpbuf));
+ tboff = 0;
+
+ switch (tblen) {
+ case -1:
+ if (errno == EINTR)
+ goto relaydata_read_retry;
+ exit_failure("reading relay data failed: %s",
+ ERRSTR);
+ /* NOTREACHED */
+ case 0:
+ /* to close opposite-direction relay process */
+ shutdown(s_snd, 0);
+
+ close(s_rcv);
+ close(s_snd);
+ exit_success("terminating %s relay", service);
+ /* NOTREACHED */
+ default:
+ FD_CLR(s_rcv, &readfds);
+ FD_SET(s_snd, &writefds);
+ break;
+ }
+ }
+ if (FD_ISSET(s_snd, &writefds))
+ send_data(s_rcv, s_snd, service, direction);
+ }
+}
+
+void
+tcp_relay(int s_src, int s_dst, const char *service)
+{
+ syslog(LOG_INFO, "starting %s relay", service);
+
+ child_lastactive = parent_lastactive = time(NULL);
+
+ cpid = fork();
+ switch (cpid) {
+ case -1:
+ exit_failure("tcp_relay: can't fork grand child: %s", ERRSTR);
+ /* NOTREACHED */
+ case 0:
+ /* child process: relay going traffic */
+ ppid = getppid();
+ /* this is child so reopen log */
+ closelog();
+ openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+ relay(s_src, s_dst, service, 1);
+ /* NOTREACHED */
+ default:
+ /* parent process: relay coming traffic */
+ ppid = (pid_t)0;
+ signal(SIGUSR1, sig_ctimeout);
+ signal(SIGCHLD, sig_child);
+ relay(s_dst, s_src, service, 0);
+ /* NOTREACHED */
+ }
+}
diff --git a/usr.sbin/faithd/test/faithd.rb b/usr.sbin/faithd/test/faithd.rb
new file mode 100644
index 0000000..683b504
--- /dev/null
+++ b/usr.sbin/faithd/test/faithd.rb
@@ -0,0 +1,312 @@
+# faithd, ruby version. requires v6-enabled ruby.
+#
+# highly experimental (not working right at all) and very limited
+# functionality.
+#
+# $Id: faithd.rb,v 1.1.1.1 1999/08/08 23:29:31 itojun Exp $
+# $FreeBSD$
+
+require "socket"
+require "thread"
+
+# XXX should be derived from system headers
+IPPROTO_IPV6 = 41
+IPV6_FAITH = 29
+DEBUG = true
+DEBUG_LOOPBACK = true
+
+# TODO: OOB data handling
+def tcpcopy(s1, s2, m)
+ STDERR.print "tcpcopy #{s1} #{s2}\n" if DEBUG
+ buf = ""
+ while TRUE
+ begin
+ buf = s1.sysread(100)
+ s2.syswrite(buf)
+ rescue EOFError
+ break
+ rescue IOError
+ break
+ end
+ end
+ STDERR.print "tcpcopy #{s1} #{s2} finished\n" if DEBUG
+ s1.shutdown(0)
+ s2.shutdown(1)
+end
+
+def relay_ftp_passiveconn(s6, s4, dport6, dport4)
+ Thread.start do
+ d6 = TCPserver.open("::", dport6).accept
+ d4 = TCPsocket.open(s4.getpeer[3], dport4)
+ t = []
+ t[0] = Thread.start do
+ tcpcopy(d6, d4)
+ end
+ t[1] = Thread.start do
+ tcpcopy(d4, d6)
+ end
+ for i in t
+ i.join
+ end
+ d4.close
+ d6.close
+ end
+end
+
+def ftp_parse_2428(line)
+ if (line[0] != line[line.length - 1])
+ return nil
+ end
+ t = line.split(line[0 .. 0]) # as string
+ if (t.size != 4 || t[1] !~ /^[12]$/ || t[3] !~ /^\d+$/)
+ return nil
+ end
+ return t[1 .. 3]
+end
+
+def relay_ftp_command(s6, s4, state)
+ STDERR.print "relay_ftp_command start\n" if DEBUG
+ while TRUE
+ begin
+ STDERR.print "s6.gets\n" if DEBUG
+ line = s6.gets
+ STDERR.print "line is #{line}\n" if DEBUG
+ if line == nil
+ return nil
+ end
+
+ # translate then copy
+ STDERR.print "line is #{line}\n" if DEBUG
+ if (line =~ /^EPSV\r\n/i)
+ STDERR.print "EPSV -> PASV\n" if DEBUG
+ line = "PASV\n"
+ state = "EPSV"
+ elsif (line =~ /^EPRT\s+(.+)\r\n/i)
+ t = ftp_parse_2428($1)
+ if t == nil
+ s6.puts "501 illegal parameter to EPRT\r\n"
+ next
+ end
+
+ # some tricks should be here
+ s6.puts "501 illegal parameter to EPRT\r\n"
+ next
+ end
+ STDERR.print "fail: send #{line} as is\n" if DEBUG
+ s4.puts(line)
+ break
+ rescue EOFError
+ return nil
+ rescue IOError
+ return nil
+ end
+ end
+ STDERR.print "relay_ftp_command finish\n" if DEBUG
+ return state
+end
+
+def relay_ftp_status(s4, s6, state)
+ STDERR.print "relay_ftp_status start\n" if DEBUG
+ while TRUE
+ begin
+ line = s4.gets
+ if line == nil
+ return nil
+ end
+
+ # translate then copy
+ s6.puts(line)
+
+ next if line =~ /^\d\d\d-/
+ next if line !~ /^\d/
+
+ # special post-processing
+ case line
+ when /^221 / # result to QUIT
+ s4.shutdown(0)
+ s6.shutdown(1)
+ end
+
+ break if (line =~ /^\d\d\d /)
+ rescue EOFError
+ return nil
+ rescue IOError
+ return nil
+ end
+ end
+ STDERR.print "relay_ftp_status finish\n" if DEBUG
+ return state
+end
+
+def relay_ftp(sock, name)
+ STDERR.print "relay_ftp(#{sock}, #{name})\n" if DEBUG
+ while TRUE
+ STDERR.print "relay_ftp(#{sock}, #{name}) accepting\n" if DEBUG
+ s = sock.accept
+ STDERR.print "relay_ftp(#{sock}, #{name}) accepted #{s}\n" if DEBUG
+ Thread.start do
+ threads = []
+ STDERR.print "accepted #{s} -> #{Thread.current}\n" if DEBUG
+ s6 = s
+ dest6 = s.addr[3]
+ if !DEBUG_LOOPBACK
+ t = s.getsockname.unpack("x8 x12 C4")
+ dest4 = "#{t[0]}.#{t[1]}.#{t[2]}.#{t[3]}"
+ port4 = s.addr[1]
+ else
+ dest4 = "127.0.0.1"
+ port4 = "ftp"
+ end
+ if DEBUG
+ STDERR.print "IPv6 dest: #{dest6} IPv4 dest: #{dest4}\n" if DEBUG
+ end
+ STDERR.print "connect to #{dest4} #{port4}\n" if DEBUG
+ s4 = TCPsocket.open(dest4, port4)
+ STDERR.print "connected to #{dest4} #{port4}, #{s4.addr[1]}\n" if DEBUG
+ state = 0
+ while TRUE
+ # translate status line
+ state = relay_ftp_status(s4, s6, state)
+ break if state == nil
+ # translate command line
+ state = relay_ftp_command(s6, s4, state)
+ break if state == nil
+ end
+ STDERR.print "relay_ftp(#{sock}, #{name}) closing s4\n" if DEBUG
+ s4.close
+ STDERR.print "relay_ftp(#{sock}, #{name}) closing s6\n" if DEBUG
+ s6.close
+ STDERR.print "relay_ftp(#{sock}, #{name}) done\n" if DEBUG
+ end
+ end
+ STDERR.print "relay_ftp(#{sock}, #{name}) finished\n" if DEBUG
+end
+
+def relay_tcp(sock, name)
+ STDERR.print "relay_tcp(#{sock}, #{name})\n" if DEBUG
+ while TRUE
+ STDERR.print "relay_tcp(#{sock}, #{name}) accepting\n" if DEBUG
+ s = sock.accept
+ STDERR.print "relay_tcp(#{sock}, #{name}) accepted #{s}\n" if DEBUG
+ Thread.start do
+ threads = []
+ STDERR.print "accepted #{s} -> #{Thread.current}\n" if DEBUG
+ s6 = s
+ dest6 = s.addr[3]
+ if !DEBUG_LOOPBACK
+ t = s.getsockname.unpack("x8 x12 C4")
+ dest4 = "#{t[0]}.#{t[1]}.#{t[2]}.#{t[3]}"
+ port4 = s.addr[1]
+ else
+ dest4 = "127.0.0.1"
+ port4 = "telnet"
+ end
+ if DEBUG
+ STDERR.print "IPv6 dest: #{dest6} IPv4 dest: #{dest4}\n" if DEBUG
+ end
+ STDERR.print "connect to #{dest4} #{port4}\n" if DEBUG
+ s4 = TCPsocket.open(dest4, port4)
+ STDERR.print "connected to #{dest4} #{port4}, #{s4.addr[1]}\n" if DEBUG
+ [0, 1].each do |i|
+ threads[i] = Thread.start do
+ if (i == 0)
+ tcpcopy(s6, s4)
+ else
+ tcpcopy(s4, s6)
+ end
+ end
+ end
+ STDERR.print "relay_tcp(#{sock}, #{name}) wait\n" if DEBUG
+ for i in threads
+ STDERR.print "relay_tcp(#{sock}, #{name}) wait #{i}\n" if DEBUG
+ i.join
+ STDERR.print "relay_tcp(#{sock}, #{name}) wait #{i} done\n" if DEBUG
+ end
+ STDERR.print "relay_tcp(#{sock}, #{name}) closing s4\n" if DEBUG
+ s4.close
+ STDERR.print "relay_tcp(#{sock}, #{name}) closing s6\n" if DEBUG
+ s6.close
+ STDERR.print "relay_tcp(#{sock}, #{name}) done\n" if DEBUG
+ end
+ end
+ STDERR.print "relay_tcp(#{sock}, #{name}) finished\n" if DEBUG
+end
+
+def usage()
+ STDERR.print "usage: #{$0} [-f] port...\n"
+end
+
+#------------------------------------------------------------
+
+$mode = "tcp"
+
+while ARGV[0] =~ /^-/ do
+ case ARGV[0]
+ when /^-f/
+ $mode = "ftp"
+ else
+ usage()
+ exit 0
+ end
+ ARGV.shift
+end
+
+if ARGV.length == 0
+ usage()
+ exit 1
+end
+
+ftpport = Socket.getservbyname("ftp")
+
+res = []
+for port in ARGV
+ t = Socket.getaddrinfo(nil, port, Socket::PF_INET6, Socket::SOCK_STREAM,
+ nil, Socket::AI_PASSIVE)
+ if (t.size <= 0)
+ STDERR.print "FATAL: getaddrinfo failed (port=#{port})\n"
+ exit 1
+ end
+ res += t
+end
+
+sockpool = []
+names = []
+listenthreads = []
+
+res.each do |i|
+ s = TCPserver.new(i[3], i[1])
+ n = Socket.getnameinfo(s.getsockname, Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV).join(" port ")
+ if i[6] == IPPROTO_IPV6
+ s.setsockopt(i[6], IPV6_FAITH, 1)
+ end
+ s.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
+ sockpool.push s
+ names.push n
+end
+
+if DEBUG
+ (0 .. sockpool.size - 1).each do |i|
+ STDERR.print "listen[#{i}]: #{sockpool[i]} #{names[i]}\n" if DEBUG
+ end
+end
+
+(0 .. sockpool.size - 1).each do |i|
+ listenthreads[i] = Thread.start do
+ if DEBUG
+ STDERR.print "listen[#{i}]: thread #{Thread.current}\n" if DEBUG
+ end
+ STDERR.print "listen[#{i}]: thread #{Thread.current}\n" if DEBUG
+ case $mode
+ when "tcp"
+ relay_tcp(sockpool[i], names[i])
+ when "ftp"
+ relay_ftp(sockpool[i], names[i])
+ end
+ end
+end
+
+for i in listenthreads
+ i.join
+end
+
+exit 0
OpenPOWER on IntegriCloud