ftp.c revision 81981
178064Sume/*	$KAME: ftp.c,v 1.10 2000/09/14 00:23:39 itojun Exp $	*/
262655Skris
356668Sshin/*
456668Sshin * Copyright (C) 1997 and 1998 WIDE Project.
556668Sshin * All rights reserved.
662655Skris *
756668Sshin * Redistribution and use in source and binary forms, with or without
856668Sshin * modification, are permitted provided that the following conditions
956668Sshin * are met:
1056668Sshin * 1. Redistributions of source code must retain the above copyright
1156668Sshin *    notice, this list of conditions and the following disclaimer.
1256668Sshin * 2. Redistributions in binary form must reproduce the above copyright
1356668Sshin *    notice, this list of conditions and the following disclaimer in the
1456668Sshin *    documentation and/or other materials provided with the distribution.
1556668Sshin * 3. Neither the name of the project nor the names of its contributors
1656668Sshin *    may be used to endorse or promote products derived from this software
1756668Sshin *    without specific prior written permission.
1862655Skris *
1956668Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2056668Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2156668Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2256668Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2356668Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2456668Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2556668Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2656668Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2756668Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2856668Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2956668Sshin * SUCH DAMAGE.
3056668Sshin *
3156668Sshin * $FreeBSD: head/usr.sbin/faithd/ftp.c 81981 2001-08-20 15:01:06Z brian $
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
12381981Sbrian			if (error < 0)
12456668Sshin				goto bad;
12581981Sbrian			else if (error == 0) {
12656668Sshin				close(ctl4);
12756668Sshin				close(ctl6);
12856668Sshin				exit_success("terminating ftp control connection");
12956668Sshin			}
13056668Sshin		}
13156668Sshin		if (FD_ISSET(ctl4, &readfds)) {
13256668Sshin			/*
13356668Sshin			 * copy control connection from the server
13456668Sshin			 * translation of result code is necessary.
13556668Sshin			 */
13656668Sshin			error = ftp_copyresult(ctl4, ctl6, state);
13756668Sshin
13881981Sbrian			if (error < 0)
13956668Sshin				goto bad;
14081981Sbrian			else if (error == 0) {
14156668Sshin				close(ctl4);
14256668Sshin				close(ctl6);
14356668Sshin				exit_success("terminating ftp control connection");
14456668Sshin			}
14556668Sshin		}
14656668Sshin		if (0 <= port4 && 0 <= port6 && FD_ISSET(port4, &readfds)) {
14756668Sshin			/*
14856668Sshin			 * copy data connection.
14956668Sshin			 * no special treatment necessary.
15056668Sshin			 */
15156668Sshin			if (FD_ISSET(port4, &readfds))
15256668Sshin				error = ftp_copy(port4, port6);
15356668Sshin			switch (error) {
15456668Sshin			case -1:
15556668Sshin				goto bad;
15656668Sshin			case 0:
15756668Sshin				close(port4);
15856668Sshin				close(port6);
15956668Sshin				port4 = port6 = -1;
16056668Sshin				syslog(LOG_INFO, "terminating data connection");
16156668Sshin				break;
16256668Sshin			default:
16356668Sshin				break;
16456668Sshin			}
16556668Sshin		}
16656668Sshin		if (0 <= port4 && 0 <= port6 && FD_ISSET(port6, &readfds)) {
16756668Sshin			/*
16856668Sshin			 * copy data connection.
16956668Sshin			 * no special treatment necessary.
17056668Sshin			 */
17156668Sshin			if (FD_ISSET(port6, &readfds))
17256668Sshin				error = ftp_copy(port6, port4);
17356668Sshin			switch (error) {
17456668Sshin			case -1:
17556668Sshin				goto bad;
17656668Sshin			case 0:
17756668Sshin				close(port4);
17856668Sshin				close(port6);
17956668Sshin				port4 = port6 = -1;
18056668Sshin				syslog(LOG_INFO, "terminating data connection");
18156668Sshin				break;
18256668Sshin			default:
18356668Sshin				break;
18456668Sshin			}
18556668Sshin		}
18656668Sshin#if 0
18756668Sshin		if (wport4 && FD_ISSET(wport4, &readfds)) {
18856668Sshin			/*
18956668Sshin			 * establish active data connection from the server.
19056668Sshin			 */
19156668Sshin			ftp_activeconn();
19256668Sshin		}
19356668Sshin		if (wport6 && FD_ISSET(wport6, &readfds)) {
19456668Sshin			/*
19556668Sshin			 * establish passive data connection from the client.
19656668Sshin			 */
19756668Sshin			ftp_passiveconn();
19856668Sshin		}
19956668Sshin#endif
20056668Sshin	}
20156668Sshin
20256668Sshin bad:
20356668Sshin	exit_failure(ERRSTR);
20456668Sshin}
20556668Sshin
20656668Sshinstatic int
20756668Sshinftp_activeconn()
20856668Sshin{
20956668Sshin	int n;
21056668Sshin	int error;
21156668Sshin	fd_set set;
21256668Sshin	struct timeval timeout;
21356668Sshin	struct sockaddr *sa;
21456668Sshin
21556668Sshin	/* get active connection from server */
21656668Sshin	FD_ZERO(&set);
21756668Sshin	FD_SET(wport4, &set);
21856668Sshin	timeout.tv_sec = 120;
21956668Sshin	timeout.tv_usec = -1;
22056668Sshin	n = sizeof(data4);
22156668Sshin	if (select(wport4 + 1, &set, NULL, NULL, &timeout) == 0
22256668Sshin	 || (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0) {
22356668Sshin		close(wport4);
22456668Sshin		wport4 = -1;
22556668Sshin		syslog(LOG_INFO, "active mode data connection failed");
22656668Sshin		return -1;
22756668Sshin	}
22856668Sshin
22956668Sshin	/* ask active connection to client */
23056668Sshin	sa = (struct sockaddr *)&data6;
23156668Sshin	port6 = socket(sa->sa_family, SOCK_STREAM, 0);
23256668Sshin	if (port6 == -1) {
23356668Sshin		close(port4);
23456668Sshin		close(wport4);
23556668Sshin		port4 = wport4 = -1;
23656668Sshin		syslog(LOG_INFO, "active mode data connection failed");
23756668Sshin		return -1;
23856668Sshin	}
23956668Sshin	error = connect(port6, sa, sa->sa_len);
24056668Sshin	if (port6 == -1) {
24156668Sshin		close(port6);
24256668Sshin		close(port4);
24356668Sshin		close(wport4);
24456668Sshin		port6 = port4 = wport4 = -1;
24556668Sshin		syslog(LOG_INFO, "active mode data connection failed");
24656668Sshin		return -1;
24756668Sshin	}
24856668Sshin
24956668Sshin	syslog(LOG_INFO, "active mode data connection established");
25056668Sshin	return 0;
25156668Sshin}
25256668Sshin
25356668Sshinstatic int
25456668Sshinftp_passiveconn()
25556668Sshin{
25656668Sshin	int n;
25756668Sshin	int error;
25856668Sshin	fd_set set;
25956668Sshin	struct timeval timeout;
26056668Sshin	struct sockaddr *sa;
26156668Sshin
26256668Sshin	/* get passive connection from client */
26356668Sshin	FD_ZERO(&set);
26456668Sshin	FD_SET(wport6, &set);
26556668Sshin	timeout.tv_sec = 120;
26656668Sshin	timeout.tv_usec = 0;
26756668Sshin	n = sizeof(data6);
26856668Sshin	if (select(wport6 + 1, &set, NULL, NULL, &timeout) == 0
26956668Sshin	 || (port6 = accept(wport6, (struct sockaddr *)&data6, &n)) < 0) {
27056668Sshin		close(wport6);
27156668Sshin		wport6 = -1;
27256668Sshin		syslog(LOG_INFO, "passive mode data connection failed");
27356668Sshin		return -1;
27456668Sshin	}
27556668Sshin
27656668Sshin	/* ask passive connection to server */
27756668Sshin	sa = (struct sockaddr *)&data4;
27856668Sshin	port4 = socket(sa->sa_family, SOCK_STREAM, 0);
27956668Sshin	if (port4 == -1) {
28056668Sshin		close(wport6);
28156668Sshin		close(port6);
28256668Sshin		wport6 = port6 = -1;
28356668Sshin		syslog(LOG_INFO, "passive mode data connection failed");
28456668Sshin		return -1;
28556668Sshin	}
28656668Sshin	error = connect(port4, sa, sa->sa_len);
28756668Sshin	if (port4 == -1) {
28856668Sshin		close(wport6);
28956668Sshin		close(port4);
29056668Sshin		close(port6);
29156668Sshin		wport6 = port4 = port6 = -1;
29256668Sshin		syslog(LOG_INFO, "passive mode data connection failed");
29356668Sshin		return -1;
29456668Sshin	}
29556668Sshin
29656668Sshin	syslog(LOG_INFO, "passive mode data connection established");
29756668Sshin	return 0;
29856668Sshin}
29956668Sshin
30056668Sshinstatic int
30156668Sshinftp_copy(int src, int dst)
30256668Sshin{
30356668Sshin	int error, atmark;
30456668Sshin	int n;
30556668Sshin
30656668Sshin	/* OOB data handling */
30756668Sshin	error = ioctl(src, SIOCATMARK, &atmark);
30856668Sshin	if (error != -1 && atmark == 1) {
30956668Sshin		n = read(src, rbuf, 1);
31056668Sshin		if (n == -1)
31156668Sshin			goto bad;
31256668Sshin		send(dst, rbuf, n, MSG_OOB);
31356668Sshin#if 0
31456668Sshin		n = read(src, rbuf, sizeof(rbuf));
31556668Sshin		if (n == -1)
31656668Sshin			goto bad;
31756668Sshin		write(dst, rbuf, n);
31856668Sshin		return n;
31956668Sshin#endif
32056668Sshin	}
32156668Sshin
32256668Sshin	n = read(src, rbuf, sizeof(rbuf));
32356668Sshin	switch (n) {
32456668Sshin	case -1:
32556668Sshin	case 0:
32656668Sshin		return n;
32756668Sshin	default:
32856668Sshin		write(dst, rbuf, n);
32956668Sshin		return n;
33056668Sshin	}
33156668Sshin
33256668Sshin bad:
33356668Sshin	exit_failure(ERRSTR);
33456668Sshin	/*NOTREACHED*/
33556668Sshin	return 0;	/* to make gcc happy */
33656668Sshin}
33756668Sshin
33856668Sshinstatic int
33956668Sshinftp_copyresult(int src, int dst, enum state state)
34056668Sshin{
34156668Sshin	int error, atmark;
34256668Sshin	int n;
34356668Sshin	char *param;
34456668Sshin	int code;
34556668Sshin
34656668Sshin	/* OOB data handling */
34756668Sshin	error = ioctl(src, SIOCATMARK, &atmark);
34856668Sshin	if (error != -1 && atmark == 1) {
34956668Sshin		n = read(src, rbuf, 1);
35056668Sshin		if (n == -1)
35156668Sshin			goto bad;
35256668Sshin		send(dst, rbuf, n, MSG_OOB);
35356668Sshin#if 0
35456668Sshin		n = read(src, rbuf, sizeof(rbuf));
35556668Sshin		if (n == -1)
35656668Sshin			goto bad;
35756668Sshin		write(dst, rbuf, n);
35856668Sshin		return n;
35956668Sshin#endif
36056668Sshin	}
36156668Sshin
36256668Sshin	n = read(src, rbuf, sizeof(rbuf));
36356668Sshin	if (n <= 0)
36456668Sshin		return n;
36556668Sshin	rbuf[n] = '\0';
36656668Sshin
36756668Sshin	/*
36856668Sshin	 * parse argument
36956668Sshin	 */
37056668Sshin    {
37156668Sshin	char *p;
37256668Sshin	int i;
37356668Sshin
37456668Sshin	p = rbuf;
37556668Sshin	for (i = 0; i < 3; i++) {
37656668Sshin		if (!isdigit(*p)) {
37756668Sshin			/* invalid reply */
37856668Sshin			write(dst, rbuf, n);
37956668Sshin			return n;
38056668Sshin		}
38156668Sshin		p++;
38256668Sshin	}
38356668Sshin	if (!isspace(*p)) {
38456668Sshin		/* invalid reply */
38556668Sshin		write(dst, rbuf, n);
38656668Sshin		return n;
38756668Sshin	}
38856668Sshin	code = atoi(rbuf);
38956668Sshin	param = p;
39056668Sshin	/* param points to first non-command token, if any */
39156668Sshin	while (*param && isspace(*param))
39256668Sshin		param++;
39356668Sshin	if (!*param)
39456668Sshin		param = NULL;
39556668Sshin    }
39656668Sshin
39756668Sshin	switch (state) {
39856668Sshin	case NONE:
39956668Sshin		if (!passivemode && rbuf[0] == '1') {
40056668Sshin			if (ftp_activeconn() < 0) {
40156668Sshin				n = snprintf(rbuf, sizeof(rbuf),
40262655Skris					"425 Cannot open data connetion\r\n");
40356668Sshin			}
40456668Sshin		}
40581977Sbrian		return n > 0 ? write(dst, rbuf, n) : n;
40656668Sshin	case LPRT:
40756668Sshin	case EPRT:
40856668Sshin		/* expecting "200 PORT command successful." */
40956668Sshin		if (code == 200) {
41056668Sshin			char *p;
41156668Sshin
41256668Sshin			p = strstr(rbuf, "PORT");
41356668Sshin			if (p) {
41456668Sshin				p[0] = (state == LPRT) ? 'L' : 'E';
41556668Sshin				p[1] = 'P';
41656668Sshin			}
41756668Sshin		} else {
41856668Sshin			close(wport4);
41956668Sshin			wport4 = -1;
42056668Sshin		}
42156668Sshin		write(dst, rbuf, n);
42256668Sshin		return n;
42356668Sshin#ifdef FAITH4
42456668Sshin	case PORT:
42556668Sshin		/* expecting "200 EPRT command successful." */
42656668Sshin		if (code == 200) {
42756668Sshin			char *p;
42856668Sshin
42956668Sshin			p = strstr(rbuf, "EPRT");
43056668Sshin			if (p) {
43156668Sshin				p[0] = 'P';
43256668Sshin				p[1] = 'O';
43356668Sshin			}
43456668Sshin		} else {
43556668Sshin			close(wport4);
43656668Sshin			wport4 = -1;
43756668Sshin		}
43856668Sshin		write(dst, rbuf, n);
43956668Sshin		return n;
44056668Sshin#endif
44156668Sshin	case LPSV:
44256668Sshin	case EPSV:
44365994Sume		/*
44465994Sume		 * expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)"
44565994Sume		 * (in some cases result comes without paren)
44665994Sume		 */
44756668Sshin		if (code != 227) {
44856668Sshinpassivefail0:
44956668Sshin			close(wport6);
45056668Sshin			wport6 = -1;
45156668Sshin			write(dst, rbuf, n);
45256668Sshin			return n;
45356668Sshin		}
45456668Sshin
45556668Sshin	    {
45656668Sshin		unsigned int ho[4], po[2];
45756668Sshin		struct sockaddr_in *sin;
45856668Sshin		struct sockaddr_in6 *sin6;
45956668Sshin		u_short port;
46056668Sshin		char *p;
46156668Sshin
46256668Sshin		/*
46356668Sshin		 * PASV result -> LPSV/EPSV result
46456668Sshin		 */
46556668Sshin		p = param;
46665994Sume		while (*p && *p != '(' && !isdigit(*p))	/*)*/
46756668Sshin			p++;
46856668Sshin		if (!*p)
46956668Sshin			goto passivefail0;	/*XXX*/
47065994Sume		if (*p == '(')	/*)*/
47165994Sume			p++;
47256668Sshin		n = sscanf(p, "%u,%u,%u,%u,%u,%u",
47356668Sshin			&ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
47456668Sshin		if (n != 6)
47556668Sshin			goto passivefail0;	/*XXX*/
47656668Sshin
47756668Sshin		/* keep PORT parameter */
47856668Sshin		memset(&data4, 0, sizeof(data4));
47956668Sshin		sin = (struct sockaddr_in *)&data4;
48056668Sshin		sin->sin_len = sizeof(*sin);
48156668Sshin		sin->sin_family = AF_INET;
48256668Sshin		sin->sin_addr.s_addr = 0;
48356668Sshin		for (n = 0; n < 4; n++) {
48456668Sshin			sin->sin_addr.s_addr |=
48556668Sshin				htonl((ho[n] & 0xff) << ((3 - n) * 8));
48656668Sshin		}
48756668Sshin		sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
48856668Sshin
48956668Sshin		/* get ready for passive data connection */
49056668Sshin		memset(&data6, 0, sizeof(data6));
49156668Sshin		sin6 = (struct sockaddr_in6 *)&data6;
49256668Sshin		sin6->sin6_len = sizeof(*sin6);
49356668Sshin		sin6->sin6_family = AF_INET6;
49456668Sshin		wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0);
49556668Sshin		if (wport6 == -1) {
49656668Sshinpassivefail:
49756668Sshin			n = snprintf(sbuf, sizeof(sbuf),
49856668Sshin				"500 could not translate from PASV\r\n");
49981977Sbrian			return n > 0 ? write(src, sbuf, n) : n;
50056668Sshin		}
50156668Sshin#ifdef IPV6_FAITH
50256668Sshin	    {
50356668Sshin		int on = 1;
50456668Sshin		error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH,
50556668Sshin			&on, sizeof(on));
50656668Sshin		if (error == -1)
50778064Sume			exit_failure("setsockopt(IPV6_FAITH): %s", ERRSTR);
50856668Sshin	    }
50956668Sshin#endif
51056668Sshin		error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len);
51156668Sshin		if (error == -1) {
51256668Sshin			close(wport6);
51356668Sshin			wport6 = -1;
51456668Sshin			goto passivefail;
51556668Sshin		}
51656668Sshin		error = listen(wport6, 1);
51756668Sshin		if (error == -1) {
51856668Sshin			close(wport6);
51956668Sshin			wport6 = -1;
52056668Sshin			goto passivefail;
52156668Sshin		}
52256668Sshin
52356668Sshin		/* transmit LPSV or EPSV */
52456668Sshin		/*
52556668Sshin		 * addr from dst, port from wport6
52656668Sshin		 */
52756668Sshin		n = sizeof(data6);
52856668Sshin		error = getsockname(wport6, (struct sockaddr *)&data6, &n);
52956668Sshin		if (error == -1) {
53056668Sshin			close(wport6);
53156668Sshin			wport6 = -1;
53256668Sshin			goto passivefail;
53356668Sshin		}
53456668Sshin		sin6 = (struct sockaddr_in6 *)&data6;
53556668Sshin		port = sin6->sin6_port;
53656668Sshin
53756668Sshin		n = sizeof(data6);
53856668Sshin		error = getsockname(dst, (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		sin6->sin6_port = port;
54656668Sshin
54756668Sshin		if (state == LPSV) {
54856668Sshin			char *a, *p;
54956668Sshin
55056668Sshin			a = (char *)&sin6->sin6_addr;
55156668Sshin			p = (char *)&sin6->sin6_port;
55256668Sshin			n = snprintf(sbuf, sizeof(sbuf),
55356668Sshin"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",
55462655Skris				6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
55562655Skris				UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
55662655Skris				UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
55762655Skris				UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
55856668Sshin				2, UC(p[0]), UC(p[1]));
55981977Sbrian			if (n > 0)
56081977Sbrian				n = write(dst, sbuf, n);
56156668Sshin			passivemode = 1;
56256668Sshin			return n;
56356668Sshin		} else {
56456668Sshin			n = snprintf(sbuf, sizeof(sbuf),
56556668Sshin"229 Entering Extended Passive Mode (|||%d|)\r\n",
56656668Sshin				ntohs(sin6->sin6_port));
56781977Sbrian			if (n > 0)
56881977Sbrian				n = write(dst, sbuf, n);
56956668Sshin			passivemode = 1;
57056668Sshin			return n;
57156668Sshin		}
57256668Sshin	    }
57356668Sshin#ifdef FAITH4
57456668Sshin	case PASV:
57556668Sshin		/* expecting "229 Entering Extended Passive Mode (|||x|)" */
57656668Sshin		if (code != 229) {
57756668Sshinpassivefail1:
57856668Sshin			close(wport6);
57956668Sshin			wport6 = -1;
58056668Sshin			write(dst, rbuf, n);
58156668Sshin			return n;
58256668Sshin		}
58356668Sshin
58456668Sshin	    {
58556668Sshin		u_short port;
58656668Sshin		char *p;
58756668Sshin		struct sockaddr_in *sin;
58856668Sshin		struct sockaddr_in6 *sin6;
58956668Sshin
59056668Sshin		/*
59156668Sshin		 * EPSV result -> PORT result
59256668Sshin		 */
59356668Sshin		p = param;
59465994Sume		while (*p && *p != '(')	/*)*/
59556668Sshin			p++;
59656668Sshin		if (!*p)
59756668Sshin			goto passivefail1;	/*XXX*/
59856668Sshin		p++;
59956668Sshin		n = sscanf(p, "|||%hu|", &port);
60056668Sshin		if (n != 1)
60156668Sshin			goto passivefail1;	/*XXX*/
60256668Sshin
60356668Sshin		/* keep EPRT parameter */
60456668Sshin		n = sizeof(data4);
60556668Sshin		error = getpeername(src, (struct sockaddr *)&data4, &n);
60656668Sshin		if (error == -1)
60756668Sshin			goto passivefail1;	/*XXX*/
60856668Sshin		sin6 = (struct sockaddr_in6 *)&data4;
60956668Sshin		sin6->sin6_port = htons(port);
61056668Sshin
61156668Sshin		/* get ready for passive data connection */
61256668Sshin		memset(&data6, 0, sizeof(data6));
61356668Sshin		sin = (struct sockaddr_in *)&data6;
61456668Sshin		sin->sin_len = sizeof(*sin);
61556668Sshin		sin->sin_family = AF_INET;
61656668Sshin		wport6 = socket(sin->sin_family, SOCK_STREAM, 0);
61756668Sshin		if (wport6 == -1) {
61856668Sshinpassivefail2:
61956668Sshin			n = snprintf(sbuf, sizeof(sbuf),
62056668Sshin				"500 could not translate from EPSV\r\n");
62181977Sbrian			return n > 0 ? write(src, sbuf, n) : n;
62256668Sshin		}
62356668Sshin#ifdef IP_FAITH
62456668Sshin	    {
62556668Sshin		int on = 1;
62656668Sshin		error = setsockopt(wport6, IPPROTO_IP, IP_FAITH,
62756668Sshin			&on, sizeof(on));
62856668Sshin		if (error == -1)
62956668Sshin			exit_error("setsockopt(IP_FAITH): %s", ERRSTR);
63056668Sshin	    }
63156668Sshin#endif
63256668Sshin		error = bind(wport6, (struct sockaddr *)sin, sin->sin_len);
63356668Sshin		if (error == -1) {
63456668Sshin			close(wport6);
63556668Sshin			wport6 = -1;
63656668Sshin			goto passivefail2;
63756668Sshin		}
63856668Sshin		error = listen(wport6, 1);
63956668Sshin		if (error == -1) {
64056668Sshin			close(wport6);
64156668Sshin			wport6 = -1;
64256668Sshin			goto passivefail2;
64356668Sshin		}
64456668Sshin
64556668Sshin		/* transmit PORT */
64656668Sshin		/*
64756668Sshin		 * addr from dst, port from wport6
64856668Sshin		 */
64956668Sshin		n = sizeof(data6);
65056668Sshin		error = getsockname(wport6, (struct sockaddr *)&data6, &n);
65156668Sshin		if (error == -1) {
65256668Sshin			close(wport6);
65356668Sshin			wport6 = -1;
65456668Sshin			goto passivefail2;
65556668Sshin		}
65656668Sshin		sin = (struct sockaddr_in *)&data6;
65756668Sshin		port = sin->sin_port;
65856668Sshin
65956668Sshin		n = sizeof(data6);
66056668Sshin		error = getsockname(dst, (struct sockaddr *)&data6, &n);
66156668Sshin		if (error == -1) {
66256668Sshin			close(wport6);
66356668Sshin			wport6 = -1;
66456668Sshin			goto passivefail2;
66556668Sshin		}
66656668Sshin		sin = (struct sockaddr_in *)&data6;
66756668Sshin		sin->sin_port = port;
66856668Sshin
66956668Sshin		{
67056668Sshin			char *a, *p;
67156668Sshin
67256668Sshin			a = (char *)&sin->sin_addr;
67356668Sshin			p = (char *)&sin->sin_port;
67456668Sshin			n = snprintf(sbuf, sizeof(sbuf),
67556668Sshin"227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
67662655Skris				UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
67756668Sshin				UC(p[0]), UC(p[1]));
67881977Sbrian			if (n > 0)
67981977Sbrian				n = write(dst, sbuf, n);
68056668Sshin			passivemode = 1;
68156668Sshin			return n;
68256668Sshin		}
68356668Sshin	    }
68456668Sshin#endif /* FAITH4 */
68556668Sshin	}
68656668Sshin
68756668Sshin bad:
68856668Sshin	exit_failure(ERRSTR);
68956668Sshin	/*NOTREACHED*/
69056668Sshin	return 0;	/* to make gcc happy */
69156668Sshin}
69256668Sshin
69356668Sshinstatic int
69456668Sshinftp_copycommand(int src, int dst, enum state *state)
69556668Sshin{
69656668Sshin	int error, atmark;
69756668Sshin	int n;
69856668Sshin	unsigned int af, hal, ho[16], pal, po[2];
69956668Sshin	char *a, *p;
70056668Sshin	char cmd[5], *param;
70156668Sshin	struct sockaddr_in *sin;
70256668Sshin	struct sockaddr_in6 *sin6;
70356668Sshin	enum state nstate;
70456668Sshin	char ch;
70556668Sshin
70656668Sshin	/* OOB data handling */
70756668Sshin	error = ioctl(src, SIOCATMARK, &atmark);
70856668Sshin	if (error != -1 && atmark == 1) {
70956668Sshin		n = read(src, rbuf, 1);
71056668Sshin		if (n == -1)
71156668Sshin			goto bad;
71256668Sshin		send(dst, rbuf, n, MSG_OOB);
71356668Sshin#if 0
71456668Sshin		n = read(src, rbuf, sizeof(rbuf));
71556668Sshin		if (n == -1)
71656668Sshin			goto bad;
71756668Sshin		write(dst, rbuf, n);
71856668Sshin		return n;
71956668Sshin#endif
72056668Sshin	}
72156668Sshin
72256668Sshin	n = read(src, rbuf, sizeof(rbuf));
72356668Sshin	if (n <= 0)
72456668Sshin		return n;
72556668Sshin	rbuf[n] = '\0';
72656668Sshin
72756668Sshin	if (n < 4) {
72856668Sshin		write(dst, rbuf, n);
72956668Sshin		return n;
73056668Sshin	}
73156668Sshin
73256668Sshin	/*
73356668Sshin	 * parse argument
73456668Sshin	 */
73556668Sshin    {
73656668Sshin	char *p, *q;
73756668Sshin	int i;
73856668Sshin
73956668Sshin	p = rbuf;
74056668Sshin	q = cmd;
74156668Sshin	for (i = 0; i < 4; i++) {
74256668Sshin		if (!isalpha(*p)) {
74356668Sshin			/* invalid command */
74456668Sshin			write(dst, rbuf, n);
74556668Sshin			return n;
74656668Sshin		}
74756668Sshin		*q++ = islower(*p) ? toupper(*p) : *p;
74856668Sshin		p++;
74956668Sshin	}
75056668Sshin	if (!isspace(*p)) {
75156668Sshin		/* invalid command */
75256668Sshin		write(dst, rbuf, n);
75356668Sshin		return n;
75456668Sshin	}
75556668Sshin	*q = '\0';
75656668Sshin	param = p;
75756668Sshin	/* param points to first non-command token, if any */
75856668Sshin	while (*param && isspace(*param))
75956668Sshin		param++;
76056668Sshin	if (!*param)
76156668Sshin		param = NULL;
76256668Sshin    }
76356668Sshin
76456668Sshin	*state = NONE;
76556668Sshin
76656668Sshin	if (strcmp(cmd, "LPRT") == 0 && param) {
76756668Sshin		/*
76856668Sshin		 * LPRT -> PORT
76956668Sshin		 */
77056668Sshin		nstate = LPRT;
77156668Sshin
77256668Sshin		close(wport4);
77356668Sshin		close(wport6);
77456668Sshin		close(port4);
77556668Sshin		close(port6);
77656668Sshin		wport4 = wport6 = port4 = port6 = -1;
77756668Sshin
77856668Sshin		if (epsvall) {
77956668Sshin			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
78056668Sshin				cmd);
78181977Sbrian			return n > 0 ? write(src, sbuf, n) : n;
78256668Sshin		}
78356668Sshin
78456668Sshin		n = sscanf(param,
78556668Sshin"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u",
78656668Sshin			      &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3],
78756668Sshin			      &ho[4], &ho[5], &ho[6], &ho[7],
78856668Sshin			      &ho[8], &ho[9], &ho[10], &ho[11],
78956668Sshin			      &ho[12], &ho[13], &ho[14], &ho[15],
79056668Sshin			      &pal, &po[0], &po[1]);
79156668Sshin		if (n != 21 || af != 6 || hal != 16|| pal != 2) {
79256668Sshin			n = snprintf(sbuf, sizeof(sbuf),
79356668Sshin				"501 illegal parameter to LPRT\r\n");
79481977Sbrian			return n > 0 ? write(src, sbuf, n) : n;
79556668Sshin		}
79656668Sshin
79756668Sshin		/* keep LPRT parameter */
79856668Sshin		memset(&data6, 0, sizeof(data6));
79956668Sshin		sin6 = (struct sockaddr_in6 *)&data6;
80056668Sshin		sin6->sin6_len = sizeof(*sin6);
80156668Sshin		sin6->sin6_family = AF_INET6;
80256668Sshin		for (n = 0; n < 16; n++)
80356668Sshin			sin6->sin6_addr.s6_addr[n] = ho[n];
80456668Sshin		sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
80556668Sshin
80656668Sshinsendport:
80756668Sshin		/* get ready for active data connection */
80856668Sshin		n = sizeof(data4);
80956668Sshin		error = getsockname(dst, (struct sockaddr *)&data4, &n);
81056668Sshin		if (error == -1) {
81156668Sshinlprtfail:
81256668Sshin			n = snprintf(sbuf, sizeof(sbuf),
81356668Sshin				"500 could not translate to PORT\r\n");
81481977Sbrian			return n > 0 ? write(src, sbuf, n) : n;
81556668Sshin		}
81656668Sshin		if (((struct sockaddr *)&data4)->sa_family != AF_INET)
81756668Sshin			goto lprtfail;
81856668Sshin		sin = (struct sockaddr_in *)&data4;
81956668Sshin		sin->sin_port = 0;
82056668Sshin		wport4 = socket(sin->sin_family, SOCK_STREAM, 0);
82156668Sshin		if (wport4 == -1)
82256668Sshin			goto lprtfail;
82356668Sshin		error = bind(wport4, (struct sockaddr *)sin, sin->sin_len);
82456668Sshin		if (error == -1) {
82556668Sshin			close(wport4);
82656668Sshin			wport4 = -1;
82756668Sshin			goto lprtfail;
82856668Sshin		}
82956668Sshin		error = listen(wport4, 1);
83056668Sshin		if (error == -1) {
83156668Sshin			close(wport4);
83256668Sshin			wport4 = -1;
83356668Sshin			goto lprtfail;
83456668Sshin		}
83556668Sshin
83656668Sshin		/* transmit PORT */
83756668Sshin		n = sizeof(data4);
83856668Sshin		error = getsockname(wport4, (struct sockaddr *)&data4, &n);
83956668Sshin		if (error == -1) {
84056668Sshin			close(wport4);
84156668Sshin			wport4 = -1;
84256668Sshin			goto lprtfail;
84356668Sshin		}
84456668Sshin		if (((struct sockaddr *)&data4)->sa_family != AF_INET) {
84556668Sshin			close(wport4);
84656668Sshin			wport4 = -1;
84756668Sshin			goto lprtfail;
84856668Sshin		}
84956668Sshin		sin = (struct sockaddr_in *)&data4;
85056668Sshin		a = (char *)&sin->sin_addr;
85156668Sshin		p = (char *)&sin->sin_port;
85256668Sshin		n = snprintf(sbuf, sizeof(sbuf), "PORT %d,%d,%d,%d,%d,%d\r\n",
85356668Sshin				  UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
85456668Sshin				  UC(p[0]), UC(p[1]));
85581977Sbrian		if (n > 0)
85681977Sbrian			n = write(dst, sbuf, n);
85756668Sshin		*state = nstate;
85856668Sshin		passivemode = 0;
85956668Sshin		return n;
86056668Sshin	} else if (strcmp(cmd, "EPRT") == 0 && param) {
86156668Sshin		/*
86256668Sshin		 * EPRT -> PORT
86356668Sshin		 */
86456668Sshin		char *afp, *hostp, *portp;
86556668Sshin		struct addrinfo hints, *res;
86656668Sshin
86756668Sshin		nstate = EPRT;
86856668Sshin
86956668Sshin		close(wport4);
87056668Sshin		close(wport6);
87156668Sshin		close(port4);
87256668Sshin		close(port6);
87356668Sshin		wport4 = wport6 = port4 = port6 = -1;
87456668Sshin
87556668Sshin		if (epsvall) {
87656668Sshin			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
87756668Sshin				cmd);
87881977Sbrian			return n > 0 ? write(src, sbuf, n) : n;
87956668Sshin		}
88056668Sshin
88156668Sshin		p = param;
88256668Sshin		ch = *p++;	/* boundary character */
88356668Sshin		afp = p;
88456668Sshin		while (*p && *p != ch)
88556668Sshin			p++;
88656668Sshin		if (!*p) {
88756668Sshineprtparamfail:
88856668Sshin			n = snprintf(sbuf, sizeof(sbuf),
88956668Sshin				"501 illegal parameter to EPRT\r\n");
89081977Sbrian			return n > 0 ? write(src, sbuf, n) : n;
89156668Sshin		}
89256668Sshin		*p++ = '\0';
89356668Sshin		hostp = p;
89456668Sshin		while (*p && *p != ch)
89556668Sshin			p++;
89656668Sshin		if (!*p)
89756668Sshin			goto eprtparamfail;
89856668Sshin		*p++ = '\0';
89956668Sshin		portp = p;
90056668Sshin		while (*p && *p != ch)
90156668Sshin			p++;
90256668Sshin		if (!*p)
90356668Sshin			goto eprtparamfail;
90456668Sshin		*p++ = '\0';
90556668Sshin
90656668Sshin		n = sscanf(afp, "%d", &af);
90756668Sshin		if (n != 1 || af != 2) {
90856668Sshin			n = snprintf(sbuf, sizeof(sbuf),
90956668Sshin				"501 unsupported address family to EPRT\r\n");
91081977Sbrian			return n > 0 ? write(src, sbuf, n) : n;
91156668Sshin		}
91256668Sshin		memset(&hints, 0, sizeof(hints));
91356668Sshin		hints.ai_family = AF_UNSPEC;
91478064Sume		hints.ai_socktype = SOCK_STREAM;
91556668Sshin		error = getaddrinfo(hostp, portp, &hints, &res);
91656668Sshin		if (error) {
91756668Sshin			n = snprintf(sbuf, sizeof(sbuf),
91862655Skris				"501 EPRT: %s\r\n", gai_strerror(error));
91981977Sbrian			return n > 0 ? write(src, sbuf, n) : n;
92056668Sshin		}
92156668Sshin		if (res->ai_next) {
92256668Sshin			n = snprintf(sbuf, sizeof(sbuf),
92356668Sshin				"501 EPRT: %s resolved to multiple addresses\r\n", hostp);
92481977Sbrian			return n > 0 ? write(src, sbuf, n) : n;
92556668Sshin		}
92656668Sshin
92756668Sshin		memcpy(&data6, res->ai_addr, res->ai_addrlen);
92856668Sshin
92956668Sshin		goto sendport;
93056668Sshin	} else if (strcmp(cmd, "LPSV") == 0 && !param) {
93156668Sshin		/*
93256668Sshin		 * LPSV -> PASV
93356668Sshin		 */
93456668Sshin		nstate = LPSV;
93556668Sshin
93656668Sshin		close(wport4);
93756668Sshin		close(wport6);
93856668Sshin		close(port4);
93956668Sshin		close(port6);
94056668Sshin		wport4 = wport6 = port4 = port6 = -1;
94156668Sshin
94256668Sshin		if (epsvall) {
94356668Sshin			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
94456668Sshin				cmd);
94581977Sbrian			return n > 0 ? write(src, sbuf, n) : n;
94656668Sshin		}
94756668Sshin
94856668Sshin		/* transmit PASV */
94956668Sshin		n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
95081977Sbrian		if (n > 0)
95181977Sbrian			n = write(dst, sbuf, n);
95256668Sshin		*state = LPSV;
95356668Sshin		passivemode = 0;	/* to be set to 1 later */
95456668Sshin		return n;
95556668Sshin	} else if (strcmp(cmd, "EPSV") == 0 && !param) {
95656668Sshin		/*
95756668Sshin		 * EPSV -> PASV
95856668Sshin		 */
95956668Sshin		close(wport4);
96056668Sshin		close(wport6);
96156668Sshin		close(port4);
96256668Sshin		close(port6);
96356668Sshin		wport4 = wport6 = port4 = port6 = -1;
96456668Sshin
96556668Sshin		n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
96681977Sbrian		if (n > 0)
96781977Sbrian			n = write(dst, sbuf, n);
96856668Sshin		*state = EPSV;
96956668Sshin		passivemode = 0;	/* to be set to 1 later */
97056668Sshin		return n;
97156668Sshin	} else if (strcmp(cmd, "EPSV") == 0 && param
97256668Sshin	 && strncasecmp(param, "ALL", 3) == 0 && isspace(param[3])) {
97356668Sshin		/*
97456668Sshin		 * EPSV ALL
97556668Sshin		 */
97656668Sshin		epsvall = 1;
97756668Sshin		n = snprintf(sbuf, sizeof(sbuf), "200 EPSV ALL command successful.\r\n");
97881977Sbrian		return n > 0 ? write(src, sbuf, n) : n;
97956668Sshin#ifdef FAITH4
98056668Sshin	} else if (strcmp(cmd, "PORT") == 0 && param) {
98156668Sshin		/*
98256668Sshin		 * PORT -> EPRT
98356668Sshin		 */
98456668Sshin		char host[NI_MAXHOST], serv[NI_MAXSERV];
98556668Sshin
98656668Sshin		nstate = PORT;
98756668Sshin
98856668Sshin		close(wport4);
98956668Sshin		close(wport6);
99056668Sshin		close(port4);
99156668Sshin		close(port6);
99256668Sshin		wport4 = wport6 = port4 = port6 = -1;
99356668Sshin
99456668Sshin		p = param;
99556668Sshin		n = sscanf(p, "%u,%u,%u,%u,%u,%u",
99656668Sshin			&ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
99756668Sshin		if (n != 6) {
99856668Sshin			n = snprintf(sbuf, sizeof(sbuf),
99956668Sshin				"501 illegal parameter to PORT\r\n");
100081977Sbrian			return n > 0 ? write(src, sbuf, n) : n;
100156668Sshin		}
100256668Sshin
100356668Sshin		memset(&data6, 0, sizeof(data6));
100456668Sshin		sin = (struct sockaddr_in *)&data6;
100556668Sshin		sin->sin_len = sizeof(*sin);
100656668Sshin		sin->sin_family = AF_INET;
100756668Sshin		sin->sin_addr.s_addr = htonl(
100856668Sshin			((ho[0] & 0xff) << 24) | ((ho[1] & 0xff) << 16) |
100956668Sshin			((ho[2] & 0xff) << 8) | (ho[3] & 0xff));
101056668Sshin		sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
101156668Sshin
101256668Sshin		/* get ready for active data connection */
101356668Sshin		n = sizeof(data4);
101456668Sshin		error = getsockname(dst, (struct sockaddr *)&data4, &n);
101556668Sshin		if (error == -1) {
101656668Sshinportfail:
101756668Sshin			n = snprintf(sbuf, sizeof(sbuf),
101856668Sshin				"500 could not translate to EPRT\r\n");
101981977Sbrian			return n > 0 ? write(src, sbuf, n) : n;
102056668Sshin		}
102156668Sshin		if (((struct sockaddr *)&data4)->sa_family != AF_INET6)
102256668Sshin			goto portfail;
102356668Sshin
102456668Sshin		((struct sockaddr_in6 *)&data4)->sin6_port = 0;
102556668Sshin		sa = (struct sockaddr *)&data4;
102656668Sshin		wport4 = socket(sa->sa_family, SOCK_STREAM, 0);
102756668Sshin		if (wport4 == -1)
102856668Sshin			goto portfail;
102956668Sshin		error = bind(wport4, sa, sa->sa_len);
103056668Sshin		if (error == -1) {
103156668Sshin			close(wport4);
103256668Sshin			wport4 = -1;
103356668Sshin			goto portfail;
103456668Sshin		}
103556668Sshin		error = listen(wport4, 1);
103656668Sshin		if (error == -1) {
103756668Sshin			close(wport4);
103856668Sshin			wport4 = -1;
103956668Sshin			goto portfail;
104056668Sshin		}
104156668Sshin
104256668Sshin		/* transmit EPRT */
104356668Sshin		n = sizeof(data4);
104456668Sshin		error = getsockname(wport4, (struct sockaddr *)&data4, &n);
104556668Sshin		if (error == -1) {
104656668Sshin			close(wport4);
104756668Sshin			wport4 = -1;
104856668Sshin			goto portfail;
104956668Sshin		}
105056668Sshin		af = 2;
105156668Sshin		sa = (struct sockaddr *)&data4;
105256668Sshin		if (getnameinfo(sa, sa->sa_len, host, sizeof(host),
105356668Sshin			serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV)) {
105456668Sshin			close(wport4);
105556668Sshin			wport4 = -1;
105656668Sshin			goto portfail;
105756668Sshin		}
105856668Sshin		n = snprintf(sbuf, sizeof(sbuf), "EPRT |%d|%s|%s|\r\n", af, host, serv);
105981977Sbrian		if (n > 0)
106081977Sbrian			n = write(dst, sbuf, n);
106156668Sshin		*state = nstate;
106256668Sshin		passivemode = 0;
106356668Sshin		return n;
106456668Sshin	} else if (strcmp(cmd, "PASV") == 0 && !param) {
106556668Sshin		/*
106656668Sshin		 * PASV -> EPSV
106756668Sshin		 */
106856668Sshin
106956668Sshin		nstate = PASV;
107056668Sshin
107156668Sshin		close(wport4);
107256668Sshin		close(wport6);
107356668Sshin		close(port4);
107456668Sshin		close(port6);
107556668Sshin		wport4 = wport6 = port4 = port6 = -1;
107656668Sshin
107756668Sshin		/* transmit EPSV */
107856668Sshin		n = snprintf(sbuf, sizeof(sbuf), "EPSV\r\n");
107981977Sbrian		if (n > 0)
108081977Sbrian			n = write(dst, sbuf, n);
108156668Sshin		*state = PASV;
108256668Sshin		passivemode = 0;	/* to be set to 1 later */
108356668Sshin		return n;
108456668Sshin#else /* FAITH4 */
108556668Sshin	} else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) {
108656668Sshin		/*
108756668Sshin		 * reject PORT/PASV
108856668Sshin		 */
108956668Sshin		n = snprintf(sbuf, sizeof(sbuf), "502 %s not implemented.\r\n", cmd);
109081977Sbrian		return n > 0 ? write(src, sbuf, n) : n;
109156668Sshin#endif /* FAITH4 */
109256668Sshin	} else if (passivemode
109356668Sshin		&& (strcmp(cmd, "STOR") == 0
109456668Sshin		 || strcmp(cmd, "STOU") == 0
109556668Sshin		 || strcmp(cmd, "RETR") == 0
109656668Sshin		 || strcmp(cmd, "LIST") == 0
109756668Sshin		 || strcmp(cmd, "NLST") == 0
109856668Sshin		 || strcmp(cmd, "APPE") == 0)) {
109956668Sshin		/*
110056668Sshin		 * commands with data transfer.  need to care about passive
110156668Sshin		 * mode data connection.
110256668Sshin		 */
110356668Sshin
110456668Sshin		if (ftp_passiveconn() < 0) {
110556668Sshin			n = snprintf(sbuf, sizeof(sbuf), "425 Cannot open data connetion\r\n");
110681977Sbrian			if (n > 0)
110781977Sbrian				n = write(src, sbuf, n);
110856668Sshin		} else {
110956668Sshin			/* simply relay the command */
111056668Sshin			write(dst, rbuf, n);
111156668Sshin		}
111256668Sshin
111356668Sshin		*state = NONE;
111456668Sshin		return n;
111556668Sshin	} else {
111656668Sshin		/* simply relay it */
111756668Sshin		*state = NONE;
111881977Sbrian		return write(dst, rbuf, n);
111956668Sshin	}
112056668Sshin
112156668Sshin bad:
112256668Sshin	exit_failure(ERRSTR);
112356668Sshin	/*NOTREACHED*/
112456668Sshin	return 0;	/* to make gcc happy */
112556668Sshin}
1126