ftp.c revision 65994
162655Skris/*	$KAME$	*/
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 * $FreeBSD: head/usr.sbin/faithd/ftp.c 65994 2000-09-17 16:44:51Z ume $
3256668Sshin */
3356668Sshin
3456668Sshin#include <sys/param.h>
3556668Sshin#include <sys/types.h>
3656668Sshin#include <sys/socket.h>
3756668Sshin#include <sys/ioctl.h>
3856668Sshin#include <sys/time.h>
3956668Sshin
4056668Sshin#include <stdio.h>
4156668Sshin#include <stdlib.h>
4256668Sshin#include <string.h>
4356668Sshin#include <syslog.h>
4456668Sshin#include <unistd.h>
4556668Sshin#include <errno.h>
4656668Sshin#include <ctype.h>
4756668Sshin
4856668Sshin#include <netinet/in.h>
4956668Sshin#include <arpa/inet.h>
5056668Sshin#include <netdb.h>
5156668Sshin
5256668Sshin#include "faithd.h"
5356668Sshin
5456668Sshinstatic char rbuf[MSS];
5556668Sshinstatic char sbuf[MSS];
5656668Sshinstatic int passivemode = 0;
5756668Sshinstatic int wport4 = -1;			/* listen() to active */
5856668Sshinstatic int wport6 = -1;			/* listen() to passive */
5956668Sshinstatic int port4 = -1;			/* active: inbound  passive: outbound */
6056668Sshinstatic int port6 = -1;			/* active: outbound  passive: inbound */
6156668Sshinstatic struct sockaddr_storage data4;	/* server data address */
6256668Sshinstatic struct sockaddr_storage data6;	/* client data address */
6356668Sshinstatic int epsvall = 0;
6456668Sshin
6556668Sshin#ifdef FAITH4
6656668Sshinenum state { NONE, LPRT, EPRT, PORT, LPSV, EPSV, PASV };
6756668Sshin#else
6856668Sshinenum state { NONE, LPRT, EPRT, LPSV, EPSV };
6956668Sshin#endif
7056668Sshin
7156668Sshinstatic int ftp_activeconn __P((void));
7256668Sshinstatic int ftp_passiveconn __P((void));
7356668Sshinstatic int ftp_copy __P((int, int));
7456668Sshinstatic int ftp_copyresult __P((int, int, enum state));
7556668Sshinstatic int ftp_copycommand __P((int, int, enum state *));
7656668Sshin
7756668Sshinvoid
7856668Sshinftp_relay(int ctl6, int ctl4)
7956668Sshin{
8056668Sshin	fd_set readfds;
8156668Sshin	int error;
8256668Sshin	enum state state = NONE;
8356668Sshin	struct timeval tv;
8456668Sshin
8556668Sshin	syslog(LOG_INFO, "starting ftp control connection");
8656668Sshin
8756668Sshin	for (;;) {
8856668Sshin		FD_ZERO(&readfds);
8956668Sshin		FD_SET(ctl4, &readfds);
9056668Sshin		FD_SET(ctl6, &readfds);
9156668Sshin		if (0 <= port4)
9256668Sshin			FD_SET(port4, &readfds);
9356668Sshin		if (0 <= port6)
9456668Sshin			FD_SET(port6, &readfds);
9556668Sshin#if 0
9656668Sshin		if (0 <= wport4)
9756668Sshin			FD_SET(wport4, &readfds);
9856668Sshin		if (0 <= wport6)
9956668Sshin			FD_SET(wport6, &readfds);
10056668Sshin#endif
10156668Sshin		tv.tv_sec = FAITH_TIMEOUT;
10256668Sshin		tv.tv_usec = 0;
10356668Sshin
10456668Sshin		error = select(256, &readfds, NULL, NULL, &tv);
10556668Sshin		if (error == -1)
10656668Sshin			exit_failure("select: %s", ERRSTR);
10756668Sshin		else if (error == 0)
10856668Sshin			exit_failure("connection timeout");
10956668Sshin
11056668Sshin		/*
11156668Sshin		 * The order of the following checks does (slightly) matter.
11256668Sshin		 * It is important to visit all checks (do not use "continue"),
11356668Sshin		 * otherwise some of the pipe may become full and we cannot
11456668Sshin		 * relay correctly.
11556668Sshin		 */
11656668Sshin		if (FD_ISSET(ctl6, &readfds)) {
11756668Sshin			/*
11856668Sshin			 * copy control connection from the client.
11956668Sshin			 * command translation is necessary.
12056668Sshin			 */
12156668Sshin			error = ftp_copycommand(ctl6, ctl4, &state);
12256668Sshin
12356668Sshin			switch (error) {
12456668Sshin			case -1:
12556668Sshin				goto bad;
12656668Sshin			case 0:
12756668Sshin				close(ctl4);
12856668Sshin				close(ctl6);
12956668Sshin				exit_success("terminating ftp control connection");
13056668Sshin				/*NOTREACHED*/
13156668Sshin			default:
13256668Sshin				break;
13356668Sshin			}
13456668Sshin		}
13556668Sshin		if (FD_ISSET(ctl4, &readfds)) {
13656668Sshin			/*
13756668Sshin			 * copy control connection from the server
13856668Sshin			 * translation of result code is necessary.
13956668Sshin			 */
14056668Sshin			error = ftp_copyresult(ctl4, ctl6, state);
14156668Sshin
14256668Sshin			switch (error) {
14356668Sshin			case -1:
14456668Sshin				goto bad;
14556668Sshin			case 0:
14656668Sshin				close(ctl4);
14756668Sshin				close(ctl6);
14856668Sshin				exit_success("terminating ftp control connection");
14956668Sshin				/*NOTREACHED*/
15056668Sshin			default:
15156668Sshin				break;
15256668Sshin			}
15356668Sshin		}
15456668Sshin		if (0 <= port4 && 0 <= port6 && FD_ISSET(port4, &readfds)) {
15556668Sshin			/*
15656668Sshin			 * copy data connection.
15756668Sshin			 * no special treatment necessary.
15856668Sshin			 */
15956668Sshin			if (FD_ISSET(port4, &readfds))
16056668Sshin				error = ftp_copy(port4, port6);
16156668Sshin			switch (error) {
16256668Sshin			case -1:
16356668Sshin				goto bad;
16456668Sshin			case 0:
16556668Sshin				close(port4);
16656668Sshin				close(port6);
16756668Sshin				port4 = port6 = -1;
16856668Sshin				syslog(LOG_INFO, "terminating data connection");
16956668Sshin				break;
17056668Sshin			default:
17156668Sshin				break;
17256668Sshin			}
17356668Sshin		}
17456668Sshin		if (0 <= port4 && 0 <= port6 && FD_ISSET(port6, &readfds)) {
17556668Sshin			/*
17656668Sshin			 * copy data connection.
17756668Sshin			 * no special treatment necessary.
17856668Sshin			 */
17956668Sshin			if (FD_ISSET(port6, &readfds))
18056668Sshin				error = ftp_copy(port6, port4);
18156668Sshin			switch (error) {
18256668Sshin			case -1:
18356668Sshin				goto bad;
18456668Sshin			case 0:
18556668Sshin				close(port4);
18656668Sshin				close(port6);
18756668Sshin				port4 = port6 = -1;
18856668Sshin				syslog(LOG_INFO, "terminating data connection");
18956668Sshin				break;
19056668Sshin			default:
19156668Sshin				break;
19256668Sshin			}
19356668Sshin		}
19456668Sshin#if 0
19556668Sshin		if (wport4 && FD_ISSET(wport4, &readfds)) {
19656668Sshin			/*
19756668Sshin			 * establish active data connection from the server.
19856668Sshin			 */
19956668Sshin			ftp_activeconn();
20056668Sshin		}
20156668Sshin		if (wport6 && FD_ISSET(wport6, &readfds)) {
20256668Sshin			/*
20356668Sshin			 * establish passive data connection from the client.
20456668Sshin			 */
20556668Sshin			ftp_passiveconn();
20656668Sshin		}
20756668Sshin#endif
20856668Sshin	}
20956668Sshin
21056668Sshin bad:
21156668Sshin	exit_failure(ERRSTR);
21256668Sshin}
21356668Sshin
21456668Sshinstatic int
21556668Sshinftp_activeconn()
21656668Sshin{
21756668Sshin	int n;
21856668Sshin	int error;
21956668Sshin	fd_set set;
22056668Sshin	struct timeval timeout;
22156668Sshin	struct sockaddr *sa;
22256668Sshin
22356668Sshin	/* get active connection from server */
22456668Sshin	FD_ZERO(&set);
22556668Sshin	FD_SET(wport4, &set);
22656668Sshin	timeout.tv_sec = 120;
22756668Sshin	timeout.tv_usec = -1;
22856668Sshin	n = sizeof(data4);
22956668Sshin	if (select(wport4 + 1, &set, NULL, NULL, &timeout) == 0
23056668Sshin	 || (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0) {
23156668Sshin		close(wport4);
23256668Sshin		wport4 = -1;
23356668Sshin		syslog(LOG_INFO, "active mode data connection failed");
23456668Sshin		return -1;
23556668Sshin	}
23656668Sshin
23756668Sshin	/* ask active connection to client */
23856668Sshin	sa = (struct sockaddr *)&data6;
23956668Sshin	port6 = socket(sa->sa_family, SOCK_STREAM, 0);
24056668Sshin	if (port6 == -1) {
24156668Sshin		close(port4);
24256668Sshin		close(wport4);
24356668Sshin		port4 = wport4 = -1;
24456668Sshin		syslog(LOG_INFO, "active mode data connection failed");
24556668Sshin		return -1;
24656668Sshin	}
24756668Sshin	error = connect(port6, sa, sa->sa_len);
24856668Sshin	if (port6 == -1) {
24956668Sshin		close(port6);
25056668Sshin		close(port4);
25156668Sshin		close(wport4);
25256668Sshin		port6 = port4 = wport4 = -1;
25356668Sshin		syslog(LOG_INFO, "active mode data connection failed");
25456668Sshin		return -1;
25556668Sshin	}
25656668Sshin
25756668Sshin	syslog(LOG_INFO, "active mode data connection established");
25856668Sshin	return 0;
25956668Sshin}
26056668Sshin
26156668Sshinstatic int
26256668Sshinftp_passiveconn()
26356668Sshin{
26456668Sshin	int n;
26556668Sshin	int error;
26656668Sshin	fd_set set;
26756668Sshin	struct timeval timeout;
26856668Sshin	struct sockaddr *sa;
26956668Sshin
27056668Sshin	/* get passive connection from client */
27156668Sshin	FD_ZERO(&set);
27256668Sshin	FD_SET(wport6, &set);
27356668Sshin	timeout.tv_sec = 120;
27456668Sshin	timeout.tv_usec = 0;
27556668Sshin	n = sizeof(data6);
27656668Sshin	if (select(wport6 + 1, &set, NULL, NULL, &timeout) == 0
27756668Sshin	 || (port6 = accept(wport6, (struct sockaddr *)&data6, &n)) < 0) {
27856668Sshin		close(wport6);
27956668Sshin		wport6 = -1;
28056668Sshin		syslog(LOG_INFO, "passive mode data connection failed");
28156668Sshin		return -1;
28256668Sshin	}
28356668Sshin
28456668Sshin	/* ask passive connection to server */
28556668Sshin	sa = (struct sockaddr *)&data4;
28656668Sshin	port4 = socket(sa->sa_family, SOCK_STREAM, 0);
28756668Sshin	if (port4 == -1) {
28856668Sshin		close(wport6);
28956668Sshin		close(port6);
29056668Sshin		wport6 = port6 = -1;
29156668Sshin		syslog(LOG_INFO, "passive mode data connection failed");
29256668Sshin		return -1;
29356668Sshin	}
29456668Sshin	error = connect(port4, sa, sa->sa_len);
29556668Sshin	if (port4 == -1) {
29656668Sshin		close(wport6);
29756668Sshin		close(port4);
29856668Sshin		close(port6);
29956668Sshin		wport6 = port4 = port6 = -1;
30056668Sshin		syslog(LOG_INFO, "passive mode data connection failed");
30156668Sshin		return -1;
30256668Sshin	}
30356668Sshin
30456668Sshin	syslog(LOG_INFO, "passive mode data connection established");
30556668Sshin	return 0;
30656668Sshin}
30756668Sshin
30856668Sshinstatic int
30956668Sshinftp_copy(int src, int dst)
31056668Sshin{
31156668Sshin	int error, atmark;
31256668Sshin	int n;
31356668Sshin
31456668Sshin	/* OOB data handling */
31556668Sshin	error = ioctl(src, SIOCATMARK, &atmark);
31656668Sshin	if (error != -1 && atmark == 1) {
31756668Sshin		n = read(src, rbuf, 1);
31856668Sshin		if (n == -1)
31956668Sshin			goto bad;
32056668Sshin		send(dst, rbuf, n, MSG_OOB);
32156668Sshin#if 0
32256668Sshin		n = read(src, rbuf, sizeof(rbuf));
32356668Sshin		if (n == -1)
32456668Sshin			goto bad;
32556668Sshin		write(dst, rbuf, n);
32656668Sshin		return n;
32756668Sshin#endif
32856668Sshin	}
32956668Sshin
33056668Sshin	n = read(src, rbuf, sizeof(rbuf));
33156668Sshin	switch (n) {
33256668Sshin	case -1:
33356668Sshin	case 0:
33456668Sshin		return n;
33556668Sshin	default:
33656668Sshin		write(dst, rbuf, n);
33756668Sshin		return n;
33856668Sshin	}
33956668Sshin
34056668Sshin bad:
34156668Sshin	exit_failure(ERRSTR);
34256668Sshin	/*NOTREACHED*/
34356668Sshin	return 0;	/* to make gcc happy */
34456668Sshin}
34556668Sshin
34656668Sshinstatic int
34756668Sshinftp_copyresult(int src, int dst, enum state state)
34856668Sshin{
34956668Sshin	int error, atmark;
35056668Sshin	int n;
35156668Sshin	char *param;
35256668Sshin	int code;
35356668Sshin
35456668Sshin	/* OOB data handling */
35556668Sshin	error = ioctl(src, SIOCATMARK, &atmark);
35656668Sshin	if (error != -1 && atmark == 1) {
35756668Sshin		n = read(src, rbuf, 1);
35856668Sshin		if (n == -1)
35956668Sshin			goto bad;
36056668Sshin		send(dst, rbuf, n, MSG_OOB);
36156668Sshin#if 0
36256668Sshin		n = read(src, rbuf, sizeof(rbuf));
36356668Sshin		if (n == -1)
36456668Sshin			goto bad;
36556668Sshin		write(dst, rbuf, n);
36656668Sshin		return n;
36756668Sshin#endif
36856668Sshin	}
36956668Sshin
37056668Sshin	n = read(src, rbuf, sizeof(rbuf));
37156668Sshin	if (n <= 0)
37256668Sshin		return n;
37356668Sshin	rbuf[n] = '\0';
37456668Sshin
37556668Sshin	/*
37656668Sshin	 * parse argument
37756668Sshin	 */
37856668Sshin    {
37956668Sshin	char *p;
38056668Sshin	int i;
38156668Sshin
38256668Sshin	p = rbuf;
38356668Sshin	for (i = 0; i < 3; i++) {
38456668Sshin		if (!isdigit(*p)) {
38556668Sshin			/* invalid reply */
38656668Sshin			write(dst, rbuf, n);
38756668Sshin			return n;
38856668Sshin		}
38956668Sshin		p++;
39056668Sshin	}
39156668Sshin	if (!isspace(*p)) {
39256668Sshin		/* invalid reply */
39356668Sshin		write(dst, rbuf, n);
39456668Sshin		return n;
39556668Sshin	}
39656668Sshin	code = atoi(rbuf);
39756668Sshin	param = p;
39856668Sshin	/* param points to first non-command token, if any */
39956668Sshin	while (*param && isspace(*param))
40056668Sshin		param++;
40156668Sshin	if (!*param)
40256668Sshin		param = NULL;
40356668Sshin    }
40456668Sshin
40556668Sshin	switch (state) {
40656668Sshin	case NONE:
40756668Sshin		if (!passivemode && rbuf[0] == '1') {
40856668Sshin			if (ftp_activeconn() < 0) {
40956668Sshin				n = snprintf(rbuf, sizeof(rbuf),
41062655Skris					"425 Cannot open data connetion\r\n");
41156668Sshin			}
41256668Sshin		}
41356668Sshin		write(dst, rbuf, n);
41456668Sshin		return n;
41556668Sshin	case LPRT:
41656668Sshin	case EPRT:
41756668Sshin		/* expecting "200 PORT command successful." */
41856668Sshin		if (code == 200) {
41956668Sshin			char *p;
42056668Sshin
42156668Sshin			p = strstr(rbuf, "PORT");
42256668Sshin			if (p) {
42356668Sshin				p[0] = (state == LPRT) ? 'L' : 'E';
42456668Sshin				p[1] = 'P';
42556668Sshin			}
42656668Sshin		} else {
42756668Sshin			close(wport4);
42856668Sshin			wport4 = -1;
42956668Sshin		}
43056668Sshin		write(dst, rbuf, n);
43156668Sshin		return n;
43256668Sshin#ifdef FAITH4
43356668Sshin	case PORT:
43456668Sshin		/* expecting "200 EPRT command successful." */
43556668Sshin		if (code == 200) {
43656668Sshin			char *p;
43756668Sshin
43856668Sshin			p = strstr(rbuf, "EPRT");
43956668Sshin			if (p) {
44056668Sshin				p[0] = 'P';
44156668Sshin				p[1] = 'O';
44256668Sshin			}
44356668Sshin		} else {
44456668Sshin			close(wport4);
44556668Sshin			wport4 = -1;
44656668Sshin		}
44756668Sshin		write(dst, rbuf, n);
44856668Sshin		return n;
44956668Sshin#endif
45056668Sshin	case LPSV:
45156668Sshin	case EPSV:
45265994Sume		/*
45365994Sume		 * expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)"
45465994Sume		 * (in some cases result comes without paren)
45565994Sume		 */
45656668Sshin		if (code != 227) {
45756668Sshinpassivefail0:
45856668Sshin			close(wport6);
45956668Sshin			wport6 = -1;
46056668Sshin			write(dst, rbuf, n);
46156668Sshin			return n;
46256668Sshin		}
46356668Sshin
46456668Sshin	    {
46556668Sshin		unsigned int ho[4], po[2];
46656668Sshin		struct sockaddr_in *sin;
46756668Sshin		struct sockaddr_in6 *sin6;
46856668Sshin		u_short port;
46956668Sshin		char *p;
47056668Sshin
47156668Sshin		/*
47256668Sshin		 * PASV result -> LPSV/EPSV result
47356668Sshin		 */
47456668Sshin		p = param;
47565994Sume		while (*p && *p != '(' && !isdigit(*p))	/*)*/
47656668Sshin			p++;
47756668Sshin		if (!*p)
47856668Sshin			goto passivefail0;	/*XXX*/
47965994Sume		if (*p == '(')	/*)*/
48065994Sume			p++;
48156668Sshin		n = sscanf(p, "%u,%u,%u,%u,%u,%u",
48256668Sshin			&ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
48356668Sshin		if (n != 6)
48456668Sshin			goto passivefail0;	/*XXX*/
48556668Sshin
48656668Sshin		/* keep PORT parameter */
48756668Sshin		memset(&data4, 0, sizeof(data4));
48856668Sshin		sin = (struct sockaddr_in *)&data4;
48956668Sshin		sin->sin_len = sizeof(*sin);
49056668Sshin		sin->sin_family = AF_INET;
49156668Sshin		sin->sin_addr.s_addr = 0;
49256668Sshin		for (n = 0; n < 4; n++) {
49356668Sshin			sin->sin_addr.s_addr |=
49456668Sshin				htonl((ho[n] & 0xff) << ((3 - n) * 8));
49556668Sshin		}
49656668Sshin		sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
49756668Sshin
49856668Sshin		/* get ready for passive data connection */
49956668Sshin		memset(&data6, 0, sizeof(data6));
50056668Sshin		sin6 = (struct sockaddr_in6 *)&data6;
50156668Sshin		sin6->sin6_len = sizeof(*sin6);
50256668Sshin		sin6->sin6_family = AF_INET6;
50356668Sshin		wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0);
50456668Sshin		if (wport6 == -1) {
50556668Sshinpassivefail:
50656668Sshin			n = snprintf(sbuf, sizeof(sbuf),
50756668Sshin				"500 could not translate from PASV\r\n");
50856668Sshin			write(src, sbuf, n);
50956668Sshin			return n;
51056668Sshin		}
51156668Sshin#ifdef IPV6_FAITH
51256668Sshin	    {
51356668Sshin		int on = 1;
51456668Sshin		error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH,
51556668Sshin			&on, sizeof(on));
51656668Sshin		if (error == -1)
51756668Sshin			exit_error("setsockopt(IPV6_FAITH): %s", ERRSTR);
51856668Sshin	    }
51956668Sshin#endif
52056668Sshin		error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len);
52156668Sshin		if (error == -1) {
52256668Sshin			close(wport6);
52356668Sshin			wport6 = -1;
52456668Sshin			goto passivefail;
52556668Sshin		}
52656668Sshin		error = listen(wport6, 1);
52756668Sshin		if (error == -1) {
52856668Sshin			close(wport6);
52956668Sshin			wport6 = -1;
53056668Sshin			goto passivefail;
53156668Sshin		}
53256668Sshin
53356668Sshin		/* transmit LPSV or EPSV */
53456668Sshin		/*
53556668Sshin		 * addr from dst, port from wport6
53656668Sshin		 */
53756668Sshin		n = sizeof(data6);
53856668Sshin		error = getsockname(wport6, (struct sockaddr *)&data6, &n);
53956668Sshin		if (error == -1) {
54056668Sshin			close(wport6);
54156668Sshin			wport6 = -1;
54256668Sshin			goto passivefail;
54356668Sshin		}
54456668Sshin		sin6 = (struct sockaddr_in6 *)&data6;
54556668Sshin		port = sin6->sin6_port;
54656668Sshin
54756668Sshin		n = sizeof(data6);
54856668Sshin		error = getsockname(dst, (struct sockaddr *)&data6, &n);
54956668Sshin		if (error == -1) {
55056668Sshin			close(wport6);
55156668Sshin			wport6 = -1;
55256668Sshin			goto passivefail;
55356668Sshin		}
55456668Sshin		sin6 = (struct sockaddr_in6 *)&data6;
55556668Sshin		sin6->sin6_port = port;
55656668Sshin
55756668Sshin		if (state == LPSV) {
55856668Sshin			char *a, *p;
55956668Sshin
56056668Sshin			a = (char *)&sin6->sin6_addr;
56156668Sshin			p = (char *)&sin6->sin6_port;
56256668Sshin			n = snprintf(sbuf, sizeof(sbuf),
56356668Sshin"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",
56462655Skris				6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
56562655Skris				UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
56662655Skris				UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
56762655Skris				UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
56856668Sshin				2, UC(p[0]), UC(p[1]));
56956668Sshin			write(dst, sbuf, n);
57056668Sshin			passivemode = 1;
57156668Sshin			return n;
57256668Sshin		} else {
57356668Sshin			n = snprintf(sbuf, sizeof(sbuf),
57456668Sshin"229 Entering Extended Passive Mode (|||%d|)\r\n",
57556668Sshin				ntohs(sin6->sin6_port));
57656668Sshin			write(dst, sbuf, n);
57756668Sshin			passivemode = 1;
57856668Sshin			return n;
57956668Sshin		}
58056668Sshin	    }
58156668Sshin#ifdef FAITH4
58256668Sshin	case PASV:
58356668Sshin		/* expecting "229 Entering Extended Passive Mode (|||x|)" */
58456668Sshin		if (code != 229) {
58556668Sshinpassivefail1:
58656668Sshin			close(wport6);
58756668Sshin			wport6 = -1;
58856668Sshin			write(dst, rbuf, n);
58956668Sshin			return n;
59056668Sshin		}
59156668Sshin
59256668Sshin	    {
59356668Sshin		u_short port;
59456668Sshin		char *p;
59556668Sshin		struct sockaddr_in *sin;
59656668Sshin		struct sockaddr_in6 *sin6;
59756668Sshin
59856668Sshin		/*
59956668Sshin		 * EPSV result -> PORT result
60056668Sshin		 */
60156668Sshin		p = param;
60265994Sume		while (*p && *p != '(')	/*)*/
60356668Sshin			p++;
60456668Sshin		if (!*p)
60556668Sshin			goto passivefail1;	/*XXX*/
60656668Sshin		p++;
60756668Sshin		n = sscanf(p, "|||%hu|", &port);
60856668Sshin		if (n != 1)
60956668Sshin			goto passivefail1;	/*XXX*/
61056668Sshin
61156668Sshin		/* keep EPRT parameter */
61256668Sshin		n = sizeof(data4);
61356668Sshin		error = getpeername(src, (struct sockaddr *)&data4, &n);
61456668Sshin		if (error == -1)
61556668Sshin			goto passivefail1;	/*XXX*/
61656668Sshin		sin6 = (struct sockaddr_in6 *)&data4;
61756668Sshin		sin6->sin6_port = htons(port);
61856668Sshin
61956668Sshin		/* get ready for passive data connection */
62056668Sshin		memset(&data6, 0, sizeof(data6));
62156668Sshin		sin = (struct sockaddr_in *)&data6;
62256668Sshin		sin->sin_len = sizeof(*sin);
62356668Sshin		sin->sin_family = AF_INET;
62456668Sshin		wport6 = socket(sin->sin_family, SOCK_STREAM, 0);
62556668Sshin		if (wport6 == -1) {
62656668Sshinpassivefail2:
62756668Sshin			n = snprintf(sbuf, sizeof(sbuf),
62856668Sshin				"500 could not translate from EPSV\r\n");
62956668Sshin			write(src, sbuf, n);
63056668Sshin			return n;
63156668Sshin		}
63256668Sshin#ifdef IP_FAITH
63356668Sshin	    {
63456668Sshin		int on = 1;
63556668Sshin		error = setsockopt(wport6, IPPROTO_IP, IP_FAITH,
63656668Sshin			&on, sizeof(on));
63756668Sshin		if (error == -1)
63856668Sshin			exit_error("setsockopt(IP_FAITH): %s", ERRSTR);
63956668Sshin	    }
64056668Sshin#endif
64156668Sshin		error = bind(wport6, (struct sockaddr *)sin, sin->sin_len);
64256668Sshin		if (error == -1) {
64356668Sshin			close(wport6);
64456668Sshin			wport6 = -1;
64556668Sshin			goto passivefail2;
64656668Sshin		}
64756668Sshin		error = listen(wport6, 1);
64856668Sshin		if (error == -1) {
64956668Sshin			close(wport6);
65056668Sshin			wport6 = -1;
65156668Sshin			goto passivefail2;
65256668Sshin		}
65356668Sshin
65456668Sshin		/* transmit PORT */
65556668Sshin		/*
65656668Sshin		 * addr from dst, port from wport6
65756668Sshin		 */
65856668Sshin		n = sizeof(data6);
65956668Sshin		error = getsockname(wport6, (struct sockaddr *)&data6, &n);
66056668Sshin		if (error == -1) {
66156668Sshin			close(wport6);
66256668Sshin			wport6 = -1;
66356668Sshin			goto passivefail2;
66456668Sshin		}
66556668Sshin		sin = (struct sockaddr_in *)&data6;
66656668Sshin		port = sin->sin_port;
66756668Sshin
66856668Sshin		n = sizeof(data6);
66956668Sshin		error = getsockname(dst, (struct sockaddr *)&data6, &n);
67056668Sshin		if (error == -1) {
67156668Sshin			close(wport6);
67256668Sshin			wport6 = -1;
67356668Sshin			goto passivefail2;
67456668Sshin		}
67556668Sshin		sin = (struct sockaddr_in *)&data6;
67656668Sshin		sin->sin_port = port;
67756668Sshin
67856668Sshin		{
67956668Sshin			char *a, *p;
68056668Sshin
68156668Sshin			a = (char *)&sin->sin_addr;
68256668Sshin			p = (char *)&sin->sin_port;
68356668Sshin			n = snprintf(sbuf, sizeof(sbuf),
68456668Sshin"227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
68562655Skris				UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
68656668Sshin				UC(p[0]), UC(p[1]));
68756668Sshin			write(dst, sbuf, n);
68856668Sshin			passivemode = 1;
68956668Sshin			return n;
69056668Sshin		}
69156668Sshin	    }
69256668Sshin#endif /* FAITH4 */
69356668Sshin	}
69456668Sshin
69556668Sshin bad:
69656668Sshin	exit_failure(ERRSTR);
69756668Sshin	/*NOTREACHED*/
69856668Sshin	return 0;	/* to make gcc happy */
69956668Sshin}
70056668Sshin
70156668Sshinstatic int
70256668Sshinftp_copycommand(int src, int dst, enum state *state)
70356668Sshin{
70456668Sshin	int error, atmark;
70556668Sshin	int n;
70656668Sshin	unsigned int af, hal, ho[16], pal, po[2];
70756668Sshin	char *a, *p;
70856668Sshin	char cmd[5], *param;
70956668Sshin	struct sockaddr_in *sin;
71056668Sshin	struct sockaddr_in6 *sin6;
71156668Sshin	enum state nstate;
71256668Sshin	char ch;
71356668Sshin
71456668Sshin	/* OOB data handling */
71556668Sshin	error = ioctl(src, SIOCATMARK, &atmark);
71656668Sshin	if (error != -1 && atmark == 1) {
71756668Sshin		n = read(src, rbuf, 1);
71856668Sshin		if (n == -1)
71956668Sshin			goto bad;
72056668Sshin		send(dst, rbuf, n, MSG_OOB);
72156668Sshin#if 0
72256668Sshin		n = read(src, rbuf, sizeof(rbuf));
72356668Sshin		if (n == -1)
72456668Sshin			goto bad;
72556668Sshin		write(dst, rbuf, n);
72656668Sshin		return n;
72756668Sshin#endif
72856668Sshin	}
72956668Sshin
73056668Sshin	n = read(src, rbuf, sizeof(rbuf));
73156668Sshin	if (n <= 0)
73256668Sshin		return n;
73356668Sshin	rbuf[n] = '\0';
73456668Sshin
73556668Sshin	if (n < 4) {
73656668Sshin		write(dst, rbuf, n);
73756668Sshin		return n;
73856668Sshin	}
73956668Sshin
74056668Sshin	/*
74156668Sshin	 * parse argument
74256668Sshin	 */
74356668Sshin    {
74456668Sshin	char *p, *q;
74556668Sshin	int i;
74656668Sshin
74756668Sshin	p = rbuf;
74856668Sshin	q = cmd;
74956668Sshin	for (i = 0; i < 4; i++) {
75056668Sshin		if (!isalpha(*p)) {
75156668Sshin			/* invalid command */
75256668Sshin			write(dst, rbuf, n);
75356668Sshin			return n;
75456668Sshin		}
75556668Sshin		*q++ = islower(*p) ? toupper(*p) : *p;
75656668Sshin		p++;
75756668Sshin	}
75856668Sshin	if (!isspace(*p)) {
75956668Sshin		/* invalid command */
76056668Sshin		write(dst, rbuf, n);
76156668Sshin		return n;
76256668Sshin	}
76356668Sshin	*q = '\0';
76456668Sshin	param = p;
76556668Sshin	/* param points to first non-command token, if any */
76656668Sshin	while (*param && isspace(*param))
76756668Sshin		param++;
76856668Sshin	if (!*param)
76956668Sshin		param = NULL;
77056668Sshin    }
77156668Sshin
77256668Sshin	*state = NONE;
77356668Sshin
77456668Sshin	if (strcmp(cmd, "LPRT") == 0 && param) {
77556668Sshin		/*
77656668Sshin		 * LPRT -> PORT
77756668Sshin		 */
77856668Sshin		nstate = LPRT;
77956668Sshin
78056668Sshin		close(wport4);
78156668Sshin		close(wport6);
78256668Sshin		close(port4);
78356668Sshin		close(port6);
78456668Sshin		wport4 = wport6 = port4 = port6 = -1;
78556668Sshin
78656668Sshin		if (epsvall) {
78756668Sshin			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
78856668Sshin				cmd);
78956668Sshin			write(src, sbuf, n);
79056668Sshin			return n;
79156668Sshin		}
79256668Sshin
79356668Sshin		n = sscanf(param,
79456668Sshin"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u",
79556668Sshin			      &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3],
79656668Sshin			      &ho[4], &ho[5], &ho[6], &ho[7],
79756668Sshin			      &ho[8], &ho[9], &ho[10], &ho[11],
79856668Sshin			      &ho[12], &ho[13], &ho[14], &ho[15],
79956668Sshin			      &pal, &po[0], &po[1]);
80056668Sshin		if (n != 21 || af != 6 || hal != 16|| pal != 2) {
80156668Sshin			n = snprintf(sbuf, sizeof(sbuf),
80256668Sshin				"501 illegal parameter to LPRT\r\n");
80356668Sshin			write(src, sbuf, n);
80456668Sshin			return n;
80556668Sshin		}
80656668Sshin
80756668Sshin		/* keep LPRT parameter */
80856668Sshin		memset(&data6, 0, sizeof(data6));
80956668Sshin		sin6 = (struct sockaddr_in6 *)&data6;
81056668Sshin		sin6->sin6_len = sizeof(*sin6);
81156668Sshin		sin6->sin6_family = AF_INET6;
81256668Sshin		for (n = 0; n < 16; n++)
81356668Sshin			sin6->sin6_addr.s6_addr[n] = ho[n];
81456668Sshin		sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
81556668Sshin
81656668Sshinsendport:
81756668Sshin		/* get ready for active data connection */
81856668Sshin		n = sizeof(data4);
81956668Sshin		error = getsockname(dst, (struct sockaddr *)&data4, &n);
82056668Sshin		if (error == -1) {
82156668Sshinlprtfail:
82256668Sshin			n = snprintf(sbuf, sizeof(sbuf),
82356668Sshin				"500 could not translate to PORT\r\n");
82456668Sshin			write(src, sbuf, n);
82556668Sshin			return n;
82656668Sshin		}
82756668Sshin		if (((struct sockaddr *)&data4)->sa_family != AF_INET)
82856668Sshin			goto lprtfail;
82956668Sshin		sin = (struct sockaddr_in *)&data4;
83056668Sshin		sin->sin_port = 0;
83156668Sshin		wport4 = socket(sin->sin_family, SOCK_STREAM, 0);
83256668Sshin		if (wport4 == -1)
83356668Sshin			goto lprtfail;
83456668Sshin		error = bind(wport4, (struct sockaddr *)sin, sin->sin_len);
83556668Sshin		if (error == -1) {
83656668Sshin			close(wport4);
83756668Sshin			wport4 = -1;
83856668Sshin			goto lprtfail;
83956668Sshin		}
84056668Sshin		error = listen(wport4, 1);
84156668Sshin		if (error == -1) {
84256668Sshin			close(wport4);
84356668Sshin			wport4 = -1;
84456668Sshin			goto lprtfail;
84556668Sshin		}
84656668Sshin
84756668Sshin		/* transmit PORT */
84856668Sshin		n = sizeof(data4);
84956668Sshin		error = getsockname(wport4, (struct sockaddr *)&data4, &n);
85056668Sshin		if (error == -1) {
85156668Sshin			close(wport4);
85256668Sshin			wport4 = -1;
85356668Sshin			goto lprtfail;
85456668Sshin		}
85556668Sshin		if (((struct sockaddr *)&data4)->sa_family != AF_INET) {
85656668Sshin			close(wport4);
85756668Sshin			wport4 = -1;
85856668Sshin			goto lprtfail;
85956668Sshin		}
86056668Sshin		sin = (struct sockaddr_in *)&data4;
86156668Sshin		a = (char *)&sin->sin_addr;
86256668Sshin		p = (char *)&sin->sin_port;
86356668Sshin		n = snprintf(sbuf, sizeof(sbuf), "PORT %d,%d,%d,%d,%d,%d\r\n",
86456668Sshin				  UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
86556668Sshin				  UC(p[0]), UC(p[1]));
86656668Sshin		write(dst, sbuf, n);
86756668Sshin		*state = nstate;
86856668Sshin		passivemode = 0;
86956668Sshin		return n;
87056668Sshin	} else if (strcmp(cmd, "EPRT") == 0 && param) {
87156668Sshin		/*
87256668Sshin		 * EPRT -> PORT
87356668Sshin		 */
87456668Sshin		char *afp, *hostp, *portp;
87556668Sshin		struct addrinfo hints, *res;
87656668Sshin
87756668Sshin		nstate = EPRT;
87856668Sshin
87956668Sshin		close(wport4);
88056668Sshin		close(wport6);
88156668Sshin		close(port4);
88256668Sshin		close(port6);
88356668Sshin		wport4 = wport6 = port4 = port6 = -1;
88456668Sshin
88556668Sshin		if (epsvall) {
88656668Sshin			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
88756668Sshin				cmd);
88856668Sshin			write(src, sbuf, n);
88956668Sshin			return n;
89056668Sshin		}
89156668Sshin
89256668Sshin		p = param;
89356668Sshin		ch = *p++;	/* boundary character */
89456668Sshin		afp = p;
89556668Sshin		while (*p && *p != ch)
89656668Sshin			p++;
89756668Sshin		if (!*p) {
89856668Sshineprtparamfail:
89956668Sshin			n = snprintf(sbuf, sizeof(sbuf),
90056668Sshin				"501 illegal parameter to EPRT\r\n");
90156668Sshin			write(src, sbuf, n);
90256668Sshin			return n;
90356668Sshin		}
90456668Sshin		*p++ = '\0';
90556668Sshin		hostp = p;
90656668Sshin		while (*p && *p != ch)
90756668Sshin			p++;
90856668Sshin		if (!*p)
90956668Sshin			goto eprtparamfail;
91056668Sshin		*p++ = '\0';
91156668Sshin		portp = p;
91256668Sshin		while (*p && *p != ch)
91356668Sshin			p++;
91456668Sshin		if (!*p)
91556668Sshin			goto eprtparamfail;
91656668Sshin		*p++ = '\0';
91756668Sshin
91856668Sshin		n = sscanf(afp, "%d", &af);
91956668Sshin		if (n != 1 || af != 2) {
92056668Sshin			n = snprintf(sbuf, sizeof(sbuf),
92156668Sshin				"501 unsupported address family to EPRT\r\n");
92256668Sshin			write(src, sbuf, n);
92356668Sshin			return n;
92456668Sshin		}
92556668Sshin		memset(&hints, 0, sizeof(hints));
92656668Sshin		hints.ai_family = AF_UNSPEC;
92756668Sshin		error = getaddrinfo(hostp, portp, &hints, &res);
92856668Sshin		if (error) {
92956668Sshin			n = snprintf(sbuf, sizeof(sbuf),
93062655Skris				"501 EPRT: %s\r\n", gai_strerror(error));
93156668Sshin			write(src, sbuf, n);
93256668Sshin			return n;
93356668Sshin		}
93456668Sshin		if (res->ai_next) {
93556668Sshin			n = snprintf(sbuf, sizeof(sbuf),
93656668Sshin				"501 EPRT: %s resolved to multiple addresses\r\n", hostp);
93756668Sshin			write(src, sbuf, n);
93856668Sshin			return n;
93956668Sshin		}
94056668Sshin
94156668Sshin		memcpy(&data6, res->ai_addr, res->ai_addrlen);
94256668Sshin
94356668Sshin		goto sendport;
94456668Sshin	} else if (strcmp(cmd, "LPSV") == 0 && !param) {
94556668Sshin		/*
94656668Sshin		 * LPSV -> PASV
94756668Sshin		 */
94856668Sshin		nstate = LPSV;
94956668Sshin
95056668Sshin		close(wport4);
95156668Sshin		close(wport6);
95256668Sshin		close(port4);
95356668Sshin		close(port6);
95456668Sshin		wport4 = wport6 = port4 = port6 = -1;
95556668Sshin
95656668Sshin		if (epsvall) {
95756668Sshin			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
95856668Sshin				cmd);
95956668Sshin			write(src, sbuf, n);
96056668Sshin			return n;
96156668Sshin		}
96256668Sshin
96356668Sshin		/* transmit PASV */
96456668Sshin		n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
96556668Sshin		write(dst, sbuf, n);
96656668Sshin		*state = LPSV;
96756668Sshin		passivemode = 0;	/* to be set to 1 later */
96856668Sshin		return n;
96956668Sshin	} else if (strcmp(cmd, "EPSV") == 0 && !param) {
97056668Sshin		/*
97156668Sshin		 * EPSV -> PASV
97256668Sshin		 */
97356668Sshin		close(wport4);
97456668Sshin		close(wport6);
97556668Sshin		close(port4);
97656668Sshin		close(port6);
97756668Sshin		wport4 = wport6 = port4 = port6 = -1;
97856668Sshin
97956668Sshin		n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
98056668Sshin		write(dst, sbuf, n);
98156668Sshin		*state = EPSV;
98256668Sshin		passivemode = 0;	/* to be set to 1 later */
98356668Sshin		return n;
98456668Sshin	} else if (strcmp(cmd, "EPSV") == 0 && param
98556668Sshin	 && strncasecmp(param, "ALL", 3) == 0 && isspace(param[3])) {
98656668Sshin		/*
98756668Sshin		 * EPSV ALL
98856668Sshin		 */
98956668Sshin		epsvall = 1;
99056668Sshin		n = snprintf(sbuf, sizeof(sbuf), "200 EPSV ALL command successful.\r\n");
99156668Sshin		write(src, sbuf, n);
99256668Sshin		return n;
99356668Sshin#ifdef FAITH4
99456668Sshin	} else if (strcmp(cmd, "PORT") == 0 && param) {
99556668Sshin		/*
99656668Sshin		 * PORT -> EPRT
99756668Sshin		 */
99856668Sshin		char host[NI_MAXHOST], serv[NI_MAXSERV];
99956668Sshin
100056668Sshin		nstate = PORT;
100156668Sshin
100256668Sshin		close(wport4);
100356668Sshin		close(wport6);
100456668Sshin		close(port4);
100556668Sshin		close(port6);
100656668Sshin		wport4 = wport6 = port4 = port6 = -1;
100756668Sshin
100856668Sshin		p = param;
100956668Sshin		n = sscanf(p, "%u,%u,%u,%u,%u,%u",
101056668Sshin			&ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
101156668Sshin		if (n != 6) {
101256668Sshin			n = snprintf(sbuf, sizeof(sbuf),
101356668Sshin				"501 illegal parameter to PORT\r\n");
101456668Sshin			write(src, sbuf, n);
101556668Sshin			return n;
101656668Sshin		}
101756668Sshin
101856668Sshin		memset(&data6, 0, sizeof(data6));
101956668Sshin		sin = (struct sockaddr_in *)&data6;
102056668Sshin		sin->sin_len = sizeof(*sin);
102156668Sshin		sin->sin_family = AF_INET;
102256668Sshin		sin->sin_addr.s_addr = htonl(
102356668Sshin			((ho[0] & 0xff) << 24) | ((ho[1] & 0xff) << 16) |
102456668Sshin			((ho[2] & 0xff) << 8) | (ho[3] & 0xff));
102556668Sshin		sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
102656668Sshin
102756668Sshin		/* get ready for active data connection */
102856668Sshin		n = sizeof(data4);
102956668Sshin		error = getsockname(dst, (struct sockaddr *)&data4, &n);
103056668Sshin		if (error == -1) {
103156668Sshinportfail:
103256668Sshin			n = snprintf(sbuf, sizeof(sbuf),
103356668Sshin				"500 could not translate to EPRT\r\n");
103456668Sshin			write(src, sbuf, n);
103556668Sshin			return n;
103656668Sshin		}
103756668Sshin		if (((struct sockaddr *)&data4)->sa_family != AF_INET6)
103856668Sshin			goto portfail;
103956668Sshin
104056668Sshin		((struct sockaddr_in6 *)&data4)->sin6_port = 0;
104156668Sshin		sa = (struct sockaddr *)&data4;
104256668Sshin		wport4 = socket(sa->sa_family, SOCK_STREAM, 0);
104356668Sshin		if (wport4 == -1)
104456668Sshin			goto portfail;
104556668Sshin		error = bind(wport4, sa, sa->sa_len);
104656668Sshin		if (error == -1) {
104756668Sshin			close(wport4);
104856668Sshin			wport4 = -1;
104956668Sshin			goto portfail;
105056668Sshin		}
105156668Sshin		error = listen(wport4, 1);
105256668Sshin		if (error == -1) {
105356668Sshin			close(wport4);
105456668Sshin			wport4 = -1;
105556668Sshin			goto portfail;
105656668Sshin		}
105756668Sshin
105856668Sshin		/* transmit EPRT */
105956668Sshin		n = sizeof(data4);
106056668Sshin		error = getsockname(wport4, (struct sockaddr *)&data4, &n);
106156668Sshin		if (error == -1) {
106256668Sshin			close(wport4);
106356668Sshin			wport4 = -1;
106456668Sshin			goto portfail;
106556668Sshin		}
106656668Sshin		af = 2;
106756668Sshin		sa = (struct sockaddr *)&data4;
106856668Sshin		if (getnameinfo(sa, sa->sa_len, host, sizeof(host),
106956668Sshin			serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV)) {
107056668Sshin			close(wport4);
107156668Sshin			wport4 = -1;
107256668Sshin			goto portfail;
107356668Sshin		}
107456668Sshin		n = snprintf(sbuf, sizeof(sbuf), "EPRT |%d|%s|%s|\r\n", af, host, serv);
107556668Sshin		write(dst, sbuf, n);
107656668Sshin		*state = nstate;
107756668Sshin		passivemode = 0;
107856668Sshin		return n;
107956668Sshin	} else if (strcmp(cmd, "PASV") == 0 && !param) {
108056668Sshin		/*
108156668Sshin		 * PASV -> EPSV
108256668Sshin		 */
108356668Sshin
108456668Sshin		nstate = PASV;
108556668Sshin
108656668Sshin		close(wport4);
108756668Sshin		close(wport6);
108856668Sshin		close(port4);
108956668Sshin		close(port6);
109056668Sshin		wport4 = wport6 = port4 = port6 = -1;
109156668Sshin
109256668Sshin		/* transmit EPSV */
109356668Sshin		n = snprintf(sbuf, sizeof(sbuf), "EPSV\r\n");
109456668Sshin		write(dst, sbuf, n);
109556668Sshin		*state = PASV;
109656668Sshin		passivemode = 0;	/* to be set to 1 later */
109756668Sshin		return n;
109856668Sshin#else /* FAITH4 */
109956668Sshin	} else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) {
110056668Sshin		/*
110156668Sshin		 * reject PORT/PASV
110256668Sshin		 */
110356668Sshin		n = snprintf(sbuf, sizeof(sbuf), "502 %s not implemented.\r\n", cmd);
110456668Sshin		write(src, sbuf, n);
110556668Sshin		return n;
110656668Sshin#endif /* FAITH4 */
110756668Sshin	} else if (passivemode
110856668Sshin		&& (strcmp(cmd, "STOR") == 0
110956668Sshin		 || strcmp(cmd, "STOU") == 0
111056668Sshin		 || strcmp(cmd, "RETR") == 0
111156668Sshin		 || strcmp(cmd, "LIST") == 0
111256668Sshin		 || strcmp(cmd, "NLST") == 0
111356668Sshin		 || strcmp(cmd, "APPE") == 0)) {
111456668Sshin		/*
111556668Sshin		 * commands with data transfer.  need to care about passive
111656668Sshin		 * mode data connection.
111756668Sshin		 */
111856668Sshin
111956668Sshin		if (ftp_passiveconn() < 0) {
112056668Sshin			n = snprintf(sbuf, sizeof(sbuf), "425 Cannot open data connetion\r\n");
112156668Sshin			write(src, sbuf, n);
112256668Sshin		} else {
112356668Sshin			/* simply relay the command */
112456668Sshin			write(dst, rbuf, n);
112556668Sshin		}
112656668Sshin
112756668Sshin		*state = NONE;
112856668Sshin		return n;
112956668Sshin	} else {
113056668Sshin		/* simply relay it */
113156668Sshin		*state = NONE;
113256668Sshin		write(dst, rbuf, n);
113356668Sshin		return n;
113456668Sshin	}
113556668Sshin
113656668Sshin bad:
113756668Sshin	exit_failure(ERRSTR);
113856668Sshin	/*NOTREACHED*/
113956668Sshin	return 0;	/* to make gcc happy */
114056668Sshin}
1141