1145357Ssuz/*	$KAME: ftp.c,v 1.24 2005/03/16 05:05:48 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$
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>
45122679Sume#ifdef HAVE_POLL_H
46122679Sume#include <poll.h>
47122679Sume#endif
4856668Sshin#include <errno.h>
4956668Sshin#include <ctype.h>
5056668Sshin
5156668Sshin#include <netinet/in.h>
5256668Sshin#include <arpa/inet.h>
5356668Sshin#include <netdb.h>
5456668Sshin
5556668Sshin#include "faithd.h"
5656668Sshin
5756668Sshinstatic char rbuf[MSS];
5856668Sshinstatic char sbuf[MSS];
5956668Sshinstatic int passivemode = 0;
6056668Sshinstatic int wport4 = -1;			/* listen() to active */
6156668Sshinstatic int wport6 = -1;			/* listen() to passive */
6256668Sshinstatic int port4 = -1;			/* active: inbound  passive: outbound */
6356668Sshinstatic int port6 = -1;			/* active: outbound  passive: inbound */
6456668Sshinstatic struct sockaddr_storage data4;	/* server data address */
6556668Sshinstatic struct sockaddr_storage data6;	/* client data address */
6656668Sshinstatic int epsvall = 0;
6756668Sshin
6856668Sshinenum state { NONE, LPRT, EPRT, LPSV, EPSV };
6956668Sshin
70173412Skevlostatic int ftp_activeconn(void);
71173412Skevlostatic int ftp_passiveconn(void);
72173412Skevlostatic int ftp_copy(int, int);
73173412Skevlostatic int ftp_copyresult(int, int, enum state);
74173412Skevlostatic int ftp_copycommand(int, int, enum state *);
7556668Sshin
7656668Sshinvoid
7756668Sshinftp_relay(int ctl6, int ctl4)
7856668Sshin{
79122679Sume#ifdef HAVE_POLL_H
80122679Sume	struct pollfd pfd[6];
81122679Sume#else
8256668Sshin	fd_set readfds;
83122679Sume#endif
8456668Sshin	int error;
8556668Sshin	enum state state = NONE;
8656668Sshin	struct timeval tv;
8756668Sshin
8856668Sshin	syslog(LOG_INFO, "starting ftp control connection");
8956668Sshin
9056668Sshin	for (;;) {
91122679Sume#ifdef HAVE_POLL_H
92122679Sume		pfd[0].fd = ctl4;
93122679Sume		pfd[0].events = POLLIN;
94122679Sume		pfd[1].fd = ctl6;
95122679Sume		pfd[1].events = POLLIN;
96122679Sume		if (0 <= port4) {
97122679Sume			pfd[2].fd = port4;
98122679Sume			pfd[2].events = POLLIN;
99122679Sume		} else
100122679Sume			pfd[2].fd = -1;
101122679Sume		if (0 <= port6) {
102122679Sume			pfd[3].fd = port6;
103122679Sume			pfd[3].events = POLLIN;
104122679Sume		} else
105122679Sume			pfd[3].fd = -1;
106122679Sume#if 0
107122679Sume		if (0 <= wport4) {
108122679Sume			pfd[4].fd = wport4;
109122679Sume			pfd[4].events = POLLIN;
110122679Sume		} else
111122679Sume			pfd[4].fd = -1;
112122679Sume		if (0 <= wport6) {
113122679Sume			pfd[5].fd = wport4;
114122679Sume			pfd[5].events = POLLIN;
115122679Sume		} else
116122679Sume			pfd[5].fd = -1;
117122679Sume#else
118122679Sume		pfd[4].fd = pfd[5].fd = -1;
119122679Sume		pfd[4].events = pfd[5].events = 0;
120122679Sume#endif
121122679Sume#else
122122679Sume		int maxfd = 0;
123122679Sume
12456668Sshin		FD_ZERO(&readfds);
125122679Sume		if (ctl4 >= FD_SETSIZE)
126122679Sume			exit_failure("descriptor too big");
12756668Sshin		FD_SET(ctl4, &readfds);
128122679Sume		maxfd = ctl4;
129122679Sume		if (ctl6 >= FD_SETSIZE)
130122679Sume			exit_failure("descriptor too big");
13156668Sshin		FD_SET(ctl6, &readfds);
132122679Sume		maxfd = (ctl6 > maxfd) ? ctl6 : maxfd;
133122679Sume		if (0 <= port4) {
134122679Sume			if (port4 >= FD_SETSIZE)
135122679Sume				exit_failure("descriptor too big");
13656668Sshin			FD_SET(port4, &readfds);
137122679Sume			maxfd = (port4 > maxfd) ? port4 : maxfd;
138122679Sume		}
139122679Sume		if (0 <= port6) {
140122679Sume			if (port6 >= FD_SETSIZE)
141122679Sume				exit_failure("descriptor too big");
14256668Sshin			FD_SET(port6, &readfds);
143122679Sume			maxfd = (port6 > maxfd) ? port6 : maxfd;
144122679Sume		}
14556668Sshin#if 0
146122679Sume		if (0 <= wport4) {
147122679Sume			if (wport4 >= FD_SETSIZE)
148122679Sume				exit_failure("descriptor too big");
14956668Sshin			FD_SET(wport4, &readfds);
150122679Sume			maxfd = (wport4 > maxfd) ? wport4 : maxfd;
151122679Sume		}
152122679Sume		if (0 <= wport6) {
153122679Sume			if (wport6 >= FD_SETSIZE)
154122679Sume				exit_failure("descriptor too big");
15556668Sshin			FD_SET(wport6, &readfds);
156122679Sume			maxfd = (wport6 > maxfd) ? wport6 : maxfd;
157122679Sume		}
15856668Sshin#endif
159122679Sume#endif
16056668Sshin		tv.tv_sec = FAITH_TIMEOUT;
16156668Sshin		tv.tv_usec = 0;
16256668Sshin
163122679Sume#ifdef HAVE_POLL_H
164122679Sume		error = poll(pfd, sizeof(pfd)/sizeof(pfd[0]), tv.tv_sec * 1000);
165122679Sume#else
166122679Sume		error = select(maxfd + 1, &readfds, NULL, NULL, &tv);
167122679Sume#endif
168122679Sume		if (error == -1) {
169122679Sume#ifdef HAVE_POLL_H
170122679Sume			exit_failure("poll: %s", strerror(errno));
171122679Sume#else
17295023Ssuz			exit_failure("select: %s", strerror(errno));
173122679Sume#endif
174122679Sume		}
17556668Sshin		else if (error == 0)
17656668Sshin			exit_failure("connection timeout");
17756668Sshin
17856668Sshin		/*
17956668Sshin		 * The order of the following checks does (slightly) matter.
18056668Sshin		 * It is important to visit all checks (do not use "continue"),
18156668Sshin		 * otherwise some of the pipe may become full and we cannot
18256668Sshin		 * relay correctly.
18356668Sshin		 */
184122679Sume#ifdef HAVE_POLL_H
185122679Sume		if (pfd[1].revents & POLLIN)
186122679Sume#else
187122679Sume		if (FD_ISSET(ctl6, &readfds))
188122679Sume#endif
189122679Sume		{
19056668Sshin			/*
19156668Sshin			 * copy control connection from the client.
19256668Sshin			 * command translation is necessary.
19356668Sshin			 */
19456668Sshin			error = ftp_copycommand(ctl6, ctl4, &state);
19556668Sshin
19681981Sbrian			if (error < 0)
19756668Sshin				goto bad;
19881981Sbrian			else if (error == 0) {
19956668Sshin				close(ctl4);
20056668Sshin				close(ctl6);
20156668Sshin				exit_success("terminating ftp control connection");
202122679Sume				/*NOTREACHED*/
20356668Sshin			}
20456668Sshin		}
205122679Sume#ifdef HAVE_POLL_H
206122679Sume		if (pfd[0].revents & POLLIN)
207122679Sume#else
208122679Sume		if (FD_ISSET(ctl4, &readfds))
209122679Sume#endif
210122679Sume		{
21156668Sshin			/*
21256668Sshin			 * copy control connection from the server
21356668Sshin			 * translation of result code is necessary.
21456668Sshin			 */
21556668Sshin			error = ftp_copyresult(ctl4, ctl6, state);
21656668Sshin
21781981Sbrian			if (error < 0)
21856668Sshin				goto bad;
21981981Sbrian			else if (error == 0) {
22056668Sshin				close(ctl4);
22156668Sshin				close(ctl6);
22256668Sshin				exit_success("terminating ftp control connection");
223122679Sume				/*NOTREACHED*/
22456668Sshin			}
22556668Sshin		}
226122679Sume#ifdef HAVE_POLL_H
227122679Sume		if (0 <= port4 && 0 <= port6 && (pfd[2].revents & POLLIN))
228122679Sume#else
229122679Sume		if (0 <= port4 && 0 <= port6 && FD_ISSET(port4, &readfds))
230122679Sume#endif
231122679Sume		{
23256668Sshin			/*
23356668Sshin			 * copy data connection.
23456668Sshin			 * no special treatment necessary.
23556668Sshin			 */
236122679Sume#ifdef HAVE_POLL_H
237122679Sume			if (pfd[2].revents & POLLIN)
238122679Sume#else
23956668Sshin			if (FD_ISSET(port4, &readfds))
240122679Sume#endif
24156668Sshin				error = ftp_copy(port4, port6);
24256668Sshin			switch (error) {
24356668Sshin			case -1:
24456668Sshin				goto bad;
24556668Sshin			case 0:
24656668Sshin				close(port4);
24756668Sshin				close(port6);
24856668Sshin				port4 = port6 = -1;
24956668Sshin				syslog(LOG_INFO, "terminating data connection");
25056668Sshin				break;
25156668Sshin			default:
25256668Sshin				break;
25356668Sshin			}
25456668Sshin		}
255122679Sume#ifdef HAVE_POLL_H
256122679Sume		if (0 <= port4 && 0 <= port6 && (pfd[3].revents & POLLIN))
257122679Sume#else
258122679Sume		if (0 <= port4 && 0 <= port6 && FD_ISSET(port6, &readfds))
259122679Sume#endif
260122679Sume		{
26156668Sshin			/*
26256668Sshin			 * copy data connection.
26356668Sshin			 * no special treatment necessary.
26456668Sshin			 */
265122679Sume#ifdef HAVE_POLL_H
266122679Sume			if (pfd[3].revents & POLLIN)
267122679Sume#else
26856668Sshin			if (FD_ISSET(port6, &readfds))
269122679Sume#endif
27056668Sshin				error = ftp_copy(port6, port4);
27156668Sshin			switch (error) {
27256668Sshin			case -1:
27356668Sshin				goto bad;
27456668Sshin			case 0:
27556668Sshin				close(port4);
27656668Sshin				close(port6);
27756668Sshin				port4 = port6 = -1;
27856668Sshin				syslog(LOG_INFO, "terminating data connection");
27956668Sshin				break;
28056668Sshin			default:
28156668Sshin				break;
28256668Sshin			}
28356668Sshin		}
28456668Sshin#if 0
285122679Sume#ifdef HAVE_POLL_H
286122679Sume		if (wport4 && (pfd[4].revents & POLLIN))
287122679Sume#else
288122679Sume		if (wport4 && FD_ISSET(wport4, &readfds))
289122679Sume#endif
290122679Sume		{
29156668Sshin			/*
29256668Sshin			 * establish active data connection from the server.
29356668Sshin			 */
29456668Sshin			ftp_activeconn();
29556668Sshin		}
296122679Sume#ifdef HAVE_POLL_H
297122679Sume		if (wport4 && (pfd[5].revents & POLLIN))
298122679Sume#else
299122679Sume		if (wport6 && FD_ISSET(wport6, &readfds))
300122679Sume#endif
301122679Sume		{
30256668Sshin			/*
30356668Sshin			 * establish passive data connection from the client.
30456668Sshin			 */
30556668Sshin			ftp_passiveconn();
30656668Sshin		}
30756668Sshin#endif
30856668Sshin	}
30956668Sshin
31056668Sshin bad:
31195023Ssuz	exit_failure("%s", strerror(errno));
31256668Sshin}
31356668Sshin
31456668Sshinstatic int
31556668Sshinftp_activeconn()
31656668Sshin{
317122679Sume	socklen_t n;
31856668Sshin	int error;
319122679Sume#ifdef HAVE_POLL_H
320122679Sume	struct pollfd pfd[1];
321122679Sume#else
32256668Sshin	fd_set set;
323122679Sume#endif
32456668Sshin	struct timeval timeout;
32556668Sshin	struct sockaddr *sa;
32656668Sshin
32756668Sshin	/* get active connection from server */
328122679Sume#ifdef HAVE_POLL_H
329122679Sume	pfd[0].fd = wport4;
330122679Sume	pfd[0].events = POLLIN;
331122679Sume#else
33256668Sshin	FD_ZERO(&set);
333122679Sume	if (wport4 >= FD_SETSIZE)
334122679Sume		exit_failure("descriptor too big");
33556668Sshin	FD_SET(wport4, &set);
336122679Sume#endif
33756668Sshin	timeout.tv_sec = 120;
338122679Sume	timeout.tv_usec = 0;
33956668Sshin	n = sizeof(data4);
340122679Sume#ifdef HAVE_POLL_H
341122679Sume	if (poll(pfd, sizeof(pfd)/sizeof(pfd[0]), timeout.tv_sec * 1000) == 0 ||
342122679Sume	    (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0)
343122679Sume#else
344122679Sume	if (select(wport4 + 1, &set, NULL, NULL, &timeout) == 0 ||
345122679Sume	    (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0)
346122679Sume#endif
347122679Sume	{
34856668Sshin		close(wport4);
34956668Sshin		wport4 = -1;
35056668Sshin		syslog(LOG_INFO, "active mode data connection failed");
35156668Sshin		return -1;
35256668Sshin	}
35356668Sshin
35456668Sshin	/* ask active connection to client */
35556668Sshin	sa = (struct sockaddr *)&data6;
35656668Sshin	port6 = socket(sa->sa_family, SOCK_STREAM, 0);
35756668Sshin	if (port6 == -1) {
35856668Sshin		close(port4);
35956668Sshin		close(wport4);
36056668Sshin		port4 = wport4 = -1;
36156668Sshin		syslog(LOG_INFO, "active mode data connection failed");
36256668Sshin		return -1;
36356668Sshin	}
36456668Sshin	error = connect(port6, sa, sa->sa_len);
36595355Sume	if (error < 0) {
36656668Sshin		close(port6);
36756668Sshin		close(port4);
36856668Sshin		close(wport4);
36956668Sshin		port6 = port4 = wport4 = -1;
37056668Sshin		syslog(LOG_INFO, "active mode data connection failed");
37156668Sshin		return -1;
37256668Sshin	}
37356668Sshin
37456668Sshin	syslog(LOG_INFO, "active mode data connection established");
37556668Sshin	return 0;
37656668Sshin}
37756668Sshin
37856668Sshinstatic int
37956668Sshinftp_passiveconn()
38056668Sshin{
381122679Sume	socklen_t len;
38256668Sshin	int error;
383122679Sume#ifdef HAVE_POLL_H
384122679Sume	struct pollfd pfd[1];
385122679Sume#else
38656668Sshin	fd_set set;
387122679Sume#endif
38856668Sshin	struct timeval timeout;
38956668Sshin	struct sockaddr *sa;
39056668Sshin
39156668Sshin	/* get passive connection from client */
392122679Sume#ifdef HAVE_POLL_H
393122679Sume	pfd[0].fd = wport6;
394122679Sume	pfd[0].events = POLLIN;
395122679Sume#else
39656668Sshin	FD_ZERO(&set);
397122679Sume	if (wport6 >= FD_SETSIZE)
398122679Sume		exit_failure("descriptor too big");
39956668Sshin	FD_SET(wport6, &set);
400122679Sume#endif
40156668Sshin	timeout.tv_sec = 120;
40256668Sshin	timeout.tv_usec = 0;
403122679Sume	len = sizeof(data6);
404122679Sume#ifdef HAVE_POLL_H
405122679Sume	if (poll(pfd, sizeof(pfd)/sizeof(pfd[0]), timeout.tv_sec * 1000) == 0 ||
406122679Sume	    (port6 = accept(wport6, (struct sockaddr *)&data6, &len)) < 0)
407122679Sume#else
408122679Sume	if (select(wport6 + 1, &set, NULL, NULL, &timeout) == 0 ||
409122679Sume	    (port6 = accept(wport6, (struct sockaddr *)&data6, &len)) < 0)
410122679Sume#endif
411122679Sume	{
41256668Sshin		close(wport6);
41356668Sshin		wport6 = -1;
41456668Sshin		syslog(LOG_INFO, "passive mode data connection failed");
41556668Sshin		return -1;
41656668Sshin	}
41756668Sshin
41856668Sshin	/* ask passive connection to server */
41956668Sshin	sa = (struct sockaddr *)&data4;
42056668Sshin	port4 = socket(sa->sa_family, SOCK_STREAM, 0);
42156668Sshin	if (port4 == -1) {
42256668Sshin		close(wport6);
42356668Sshin		close(port6);
42456668Sshin		wport6 = port6 = -1;
42556668Sshin		syslog(LOG_INFO, "passive mode data connection failed");
42656668Sshin		return -1;
42756668Sshin	}
42856668Sshin	error = connect(port4, sa, sa->sa_len);
42995355Sume	if (error < 0) {
43056668Sshin		close(wport6);
43156668Sshin		close(port4);
43256668Sshin		close(port6);
43356668Sshin		wport6 = port4 = port6 = -1;
43456668Sshin		syslog(LOG_INFO, "passive mode data connection failed");
43556668Sshin		return -1;
43656668Sshin	}
43756668Sshin
43856668Sshin	syslog(LOG_INFO, "passive mode data connection established");
43956668Sshin	return 0;
44056668Sshin}
44156668Sshin
44256668Sshinstatic int
44356668Sshinftp_copy(int src, int dst)
44456668Sshin{
445122679Sume	int error, atmark, n;
44656668Sshin
44756668Sshin	/* OOB data handling */
44856668Sshin	error = ioctl(src, SIOCATMARK, &atmark);
44956668Sshin	if (error != -1 && atmark == 1) {
45056668Sshin		n = read(src, rbuf, 1);
45156668Sshin		if (n == -1)
45256668Sshin			goto bad;
45356668Sshin		send(dst, rbuf, n, MSG_OOB);
45456668Sshin#if 0
45556668Sshin		n = read(src, rbuf, sizeof(rbuf));
45656668Sshin		if (n == -1)
45756668Sshin			goto bad;
45856668Sshin		write(dst, rbuf, n);
45956668Sshin		return n;
46056668Sshin#endif
46156668Sshin	}
46256668Sshin
46356668Sshin	n = read(src, rbuf, sizeof(rbuf));
46456668Sshin	switch (n) {
46556668Sshin	case -1:
46656668Sshin	case 0:
46756668Sshin		return n;
46856668Sshin	default:
46956668Sshin		write(dst, rbuf, n);
47056668Sshin		return n;
47156668Sshin	}
47256668Sshin
47356668Sshin bad:
47495023Ssuz	exit_failure("%s", strerror(errno));
47556668Sshin	/*NOTREACHED*/
47656668Sshin	return 0;	/* to make gcc happy */
47756668Sshin}
47856668Sshin
47956668Sshinstatic int
48056668Sshinftp_copyresult(int src, int dst, enum state state)
48156668Sshin{
482122679Sume	int error, atmark, n;
483122679Sume	socklen_t len;
48456668Sshin	char *param;
48556668Sshin	int code;
486122679Sume	char *a, *p;
487122679Sume	int i;
48856668Sshin
48956668Sshin	/* OOB data handling */
49056668Sshin	error = ioctl(src, SIOCATMARK, &atmark);
49156668Sshin	if (error != -1 && atmark == 1) {
49256668Sshin		n = read(src, rbuf, 1);
49356668Sshin		if (n == -1)
49456668Sshin			goto bad;
49556668Sshin		send(dst, rbuf, n, MSG_OOB);
49656668Sshin#if 0
49756668Sshin		n = read(src, rbuf, sizeof(rbuf));
49856668Sshin		if (n == -1)
49956668Sshin			goto bad;
50056668Sshin		write(dst, rbuf, n);
50156668Sshin		return n;
50256668Sshin#endif
50356668Sshin	}
50456668Sshin
50556668Sshin	n = read(src, rbuf, sizeof(rbuf));
50656668Sshin	if (n <= 0)
50756668Sshin		return n;
50856668Sshin	rbuf[n] = '\0';
50956668Sshin
51056668Sshin	/*
51156668Sshin	 * parse argument
51256668Sshin	 */
51356668Sshin	p = rbuf;
51456668Sshin	for (i = 0; i < 3; i++) {
51556668Sshin		if (!isdigit(*p)) {
51656668Sshin			/* invalid reply */
51756668Sshin			write(dst, rbuf, n);
51856668Sshin			return n;
51956668Sshin		}
52056668Sshin		p++;
52156668Sshin	}
52256668Sshin	if (!isspace(*p)) {
52356668Sshin		/* invalid reply */
52456668Sshin		write(dst, rbuf, n);
52556668Sshin		return n;
52656668Sshin	}
52756668Sshin	code = atoi(rbuf);
52856668Sshin	param = p;
52956668Sshin	/* param points to first non-command token, if any */
53056668Sshin	while (*param && isspace(*param))
53156668Sshin		param++;
53256668Sshin	if (!*param)
53356668Sshin		param = NULL;
53456668Sshin
53556668Sshin	switch (state) {
53656668Sshin	case NONE:
53756668Sshin		if (!passivemode && rbuf[0] == '1') {
53856668Sshin			if (ftp_activeconn() < 0) {
53956668Sshin				n = snprintf(rbuf, sizeof(rbuf),
54062655Skris					"425 Cannot open data connetion\r\n");
541122679Sume				if (n < 0 || n >= sizeof(rbuf))
542122679Sume					n = 0;
54356668Sshin			}
54456668Sshin		}
545122679Sume		if (n)
546122679Sume			write(dst, rbuf, n);
547122679Sume		return n;
54856668Sshin	case LPRT:
54956668Sshin	case EPRT:
55056668Sshin		/* expecting "200 PORT command successful." */
55156668Sshin		if (code == 200) {
55256668Sshin			p = strstr(rbuf, "PORT");
55356668Sshin			if (p) {
55456668Sshin				p[0] = (state == LPRT) ? 'L' : 'E';
55556668Sshin				p[1] = 'P';
55656668Sshin			}
55756668Sshin		} else {
55856668Sshin			close(wport4);
55956668Sshin			wport4 = -1;
56056668Sshin		}
56156668Sshin		write(dst, rbuf, n);
56256668Sshin		return n;
56356668Sshin	case LPSV:
56456668Sshin	case EPSV:
56565994Sume		/*
56665994Sume		 * expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)"
56765994Sume		 * (in some cases result comes without paren)
56865994Sume		 */
56956668Sshin		if (code != 227) {
57056668Sshinpassivefail0:
57156668Sshin			close(wport6);
57256668Sshin			wport6 = -1;
57356668Sshin			write(dst, rbuf, n);
57456668Sshin			return n;
57556668Sshin		}
57656668Sshin
57756668Sshin	    {
57856668Sshin		unsigned int ho[4], po[2];
57956668Sshin		struct sockaddr_in *sin;
58056668Sshin		struct sockaddr_in6 *sin6;
58156668Sshin		u_short port;
58256668Sshin
58356668Sshin		/*
58456668Sshin		 * PASV result -> LPSV/EPSV result
58556668Sshin		 */
58656668Sshin		p = param;
58765994Sume		while (*p && *p != '(' && !isdigit(*p))	/*)*/
58856668Sshin			p++;
58956668Sshin		if (!*p)
59056668Sshin			goto passivefail0;	/*XXX*/
59165994Sume		if (*p == '(')	/*)*/
59265994Sume			p++;
59356668Sshin		n = sscanf(p, "%u,%u,%u,%u,%u,%u",
59456668Sshin			&ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
59556668Sshin		if (n != 6)
59656668Sshin			goto passivefail0;	/*XXX*/
59756668Sshin
59856668Sshin		/* keep PORT parameter */
59956668Sshin		memset(&data4, 0, sizeof(data4));
60056668Sshin		sin = (struct sockaddr_in *)&data4;
60156668Sshin		sin->sin_len = sizeof(*sin);
60256668Sshin		sin->sin_family = AF_INET;
60356668Sshin		sin->sin_addr.s_addr = 0;
60456668Sshin		for (n = 0; n < 4; n++) {
60556668Sshin			sin->sin_addr.s_addr |=
60656668Sshin				htonl((ho[n] & 0xff) << ((3 - n) * 8));
60756668Sshin		}
60856668Sshin		sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
60956668Sshin
61056668Sshin		/* get ready for passive data connection */
61156668Sshin		memset(&data6, 0, sizeof(data6));
61256668Sshin		sin6 = (struct sockaddr_in6 *)&data6;
61356668Sshin		sin6->sin6_len = sizeof(*sin6);
61456668Sshin		sin6->sin6_family = AF_INET6;
61556668Sshin		wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0);
61656668Sshin		if (wport6 == -1) {
61756668Sshinpassivefail:
61856668Sshin			n = snprintf(sbuf, sizeof(sbuf),
61956668Sshin				"500 could not translate from PASV\r\n");
620122679Sume			if (n < 0 || n >= sizeof(sbuf))
621122679Sume				n = 0;
622122679Sume			if (n)
623122679Sume				write(src, sbuf, n);
624122679Sume			return n;
62556668Sshin		}
62656668Sshin#ifdef IPV6_FAITH
62756668Sshin	    {
62856668Sshin		int on = 1;
62956668Sshin		error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH,
63056668Sshin			&on, sizeof(on));
63156668Sshin		if (error == -1)
63295023Ssuz			exit_failure("setsockopt(IPV6_FAITH): %s", strerror(errno));
63356668Sshin	    }
63456668Sshin#endif
63556668Sshin		error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len);
63656668Sshin		if (error == -1) {
63756668Sshin			close(wport6);
63856668Sshin			wport6 = -1;
63956668Sshin			goto passivefail;
64056668Sshin		}
64156668Sshin		error = listen(wport6, 1);
64256668Sshin		if (error == -1) {
64356668Sshin			close(wport6);
64456668Sshin			wport6 = -1;
64556668Sshin			goto passivefail;
64656668Sshin		}
64756668Sshin
64856668Sshin		/* transmit LPSV or EPSV */
64956668Sshin		/*
65056668Sshin		 * addr from dst, port from wport6
65156668Sshin		 */
652122679Sume		len = sizeof(data6);
653122679Sume		error = getsockname(wport6, (struct sockaddr *)&data6, &len);
65456668Sshin		if (error == -1) {
65556668Sshin			close(wport6);
65656668Sshin			wport6 = -1;
65756668Sshin			goto passivefail;
65856668Sshin		}
65956668Sshin		sin6 = (struct sockaddr_in6 *)&data6;
66056668Sshin		port = sin6->sin6_port;
66156668Sshin
662122679Sume		len = sizeof(data6);
663122679Sume		error = getsockname(dst, (struct sockaddr *)&data6, &len);
66456668Sshin		if (error == -1) {
66556668Sshin			close(wport6);
66656668Sshin			wport6 = -1;
66756668Sshin			goto passivefail;
66856668Sshin		}
66956668Sshin		sin6 = (struct sockaddr_in6 *)&data6;
67056668Sshin		sin6->sin6_port = port;
67156668Sshin
67256668Sshin		if (state == LPSV) {
67356668Sshin			a = (char *)&sin6->sin6_addr;
67456668Sshin			p = (char *)&sin6->sin6_port;
67556668Sshin			n = snprintf(sbuf, sizeof(sbuf),
67656668Sshin"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",
67762655Skris				6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
67862655Skris				UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
67962655Skris				UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
68062655Skris				UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
68156668Sshin				2, UC(p[0]), UC(p[1]));
682122679Sume			if (n < 0 || n >= sizeof(sbuf))
683122679Sume				n = 0;
684122679Sume			if (n)
685122679Sume				write(dst, sbuf, n);
68656668Sshin			passivemode = 1;
68756668Sshin			return n;
68856668Sshin		} else {
68956668Sshin			n = snprintf(sbuf, sizeof(sbuf),
69056668Sshin"229 Entering Extended Passive Mode (|||%d|)\r\n",
69156668Sshin				ntohs(sin6->sin6_port));
692122679Sume			if (n < 0 || n >= sizeof(sbuf))
693122679Sume				n = 0;
694122679Sume			if (n)
695122679Sume				write(dst, sbuf, n);
69656668Sshin			passivemode = 1;
69756668Sshin			return n;
69856668Sshin		}
69956668Sshin	    }
70056668Sshin	}
70156668Sshin
70256668Sshin bad:
70395023Ssuz	exit_failure("%s", strerror(errno));
70456668Sshin	/*NOTREACHED*/
70556668Sshin	return 0;	/* to make gcc happy */
70656668Sshin}
70756668Sshin
70856668Sshinstatic int
70956668Sshinftp_copycommand(int src, int dst, enum state *state)
71056668Sshin{
711122679Sume	int error, atmark, n;
712122679Sume	socklen_t len;
71356668Sshin	unsigned int af, hal, ho[16], pal, po[2];
714122679Sume	char *a, *p, *q;
71556668Sshin	char cmd[5], *param;
71656668Sshin	struct sockaddr_in *sin;
71756668Sshin	struct sockaddr_in6 *sin6;
71856668Sshin	enum state nstate;
71956668Sshin	char ch;
720122679Sume	int i;
72156668Sshin
72256668Sshin	/* OOB data handling */
72356668Sshin	error = ioctl(src, SIOCATMARK, &atmark);
72456668Sshin	if (error != -1 && atmark == 1) {
72556668Sshin		n = read(src, rbuf, 1);
72656668Sshin		if (n == -1)
72756668Sshin			goto bad;
72856668Sshin		send(dst, rbuf, n, MSG_OOB);
72956668Sshin#if 0
73056668Sshin		n = read(src, rbuf, sizeof(rbuf));
73156668Sshin		if (n == -1)
73256668Sshin			goto bad;
73356668Sshin		write(dst, rbuf, n);
73456668Sshin		return n;
73556668Sshin#endif
73656668Sshin	}
73756668Sshin
73856668Sshin	n = read(src, rbuf, sizeof(rbuf));
73956668Sshin	if (n <= 0)
74056668Sshin		return n;
74156668Sshin	rbuf[n] = '\0';
74256668Sshin
74356668Sshin	if (n < 4) {
74456668Sshin		write(dst, rbuf, n);
74556668Sshin		return n;
74656668Sshin	}
74756668Sshin
74856668Sshin	/*
74956668Sshin	 * parse argument
75056668Sshin	 */
75156668Sshin	p = rbuf;
75256668Sshin	q = cmd;
75356668Sshin	for (i = 0; i < 4; i++) {
75456668Sshin		if (!isalpha(*p)) {
75556668Sshin			/* invalid command */
75656668Sshin			write(dst, rbuf, n);
75756668Sshin			return n;
75856668Sshin		}
75956668Sshin		*q++ = islower(*p) ? toupper(*p) : *p;
76056668Sshin		p++;
76156668Sshin	}
76256668Sshin	if (!isspace(*p)) {
76356668Sshin		/* invalid command */
76456668Sshin		write(dst, rbuf, n);
76556668Sshin		return n;
76656668Sshin	}
76756668Sshin	*q = '\0';
76856668Sshin	param = p;
76956668Sshin	/* param points to first non-command token, if any */
77056668Sshin	while (*param && isspace(*param))
77156668Sshin		param++;
77256668Sshin	if (!*param)
77356668Sshin		param = NULL;
77456668Sshin
77556668Sshin	*state = NONE;
77656668Sshin
77756668Sshin	if (strcmp(cmd, "LPRT") == 0 && param) {
77856668Sshin		/*
77956668Sshin		 * LPRT -> PORT
78056668Sshin		 */
78156668Sshin		nstate = LPRT;
78256668Sshin
78356668Sshin		close(wport4);
78456668Sshin		close(wport6);
78556668Sshin		close(port4);
78656668Sshin		close(port6);
78756668Sshin		wport4 = wport6 = port4 = port6 = -1;
78856668Sshin
78956668Sshin		if (epsvall) {
79056668Sshin			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
79156668Sshin				cmd);
792122679Sume			if (n < 0 || n >= sizeof(sbuf))
793122679Sume				n = 0;
794122679Sume			if (n)
795122679Sume				write(src, sbuf, n);
796122679Sume			return n;
79756668Sshin		}
79856668Sshin
79956668Sshin		n = sscanf(param,
80056668Sshin"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u",
80156668Sshin			      &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3],
80256668Sshin			      &ho[4], &ho[5], &ho[6], &ho[7],
80356668Sshin			      &ho[8], &ho[9], &ho[10], &ho[11],
80456668Sshin			      &ho[12], &ho[13], &ho[14], &ho[15],
80556668Sshin			      &pal, &po[0], &po[1]);
80656668Sshin		if (n != 21 || af != 6 || hal != 16|| pal != 2) {
80756668Sshin			n = snprintf(sbuf, sizeof(sbuf),
80856668Sshin				"501 illegal parameter to LPRT\r\n");
809122679Sume			if (n < 0 || n >= sizeof(sbuf))
810122679Sume				n = 0;
811122679Sume			if (n)
812122679Sume				write(src, sbuf, n);
813122679Sume			return n;
81456668Sshin		}
81556668Sshin
81656668Sshin		/* keep LPRT parameter */
81756668Sshin		memset(&data6, 0, sizeof(data6));
81856668Sshin		sin6 = (struct sockaddr_in6 *)&data6;
81956668Sshin		sin6->sin6_len = sizeof(*sin6);
82056668Sshin		sin6->sin6_family = AF_INET6;
82156668Sshin		for (n = 0; n < 16; n++)
82256668Sshin			sin6->sin6_addr.s6_addr[n] = ho[n];
82356668Sshin		sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
82456668Sshin
82556668Sshinsendport:
82656668Sshin		/* get ready for active data connection */
827122679Sume		len = sizeof(data4);
828122679Sume		error = getsockname(dst, (struct sockaddr *)&data4, &len);
82956668Sshin		if (error == -1) {
83056668Sshinlprtfail:
83156668Sshin			n = snprintf(sbuf, sizeof(sbuf),
83256668Sshin				"500 could not translate to PORT\r\n");
833122679Sume			if (n < 0 || n >= sizeof(sbuf))
834122679Sume				n = 0;
835122679Sume			if (n)
836122679Sume				write(src, sbuf, n);
837122679Sume			return n;
83856668Sshin		}
83956668Sshin		if (((struct sockaddr *)&data4)->sa_family != AF_INET)
84056668Sshin			goto lprtfail;
84156668Sshin		sin = (struct sockaddr_in *)&data4;
84256668Sshin		sin->sin_port = 0;
84356668Sshin		wport4 = socket(sin->sin_family, SOCK_STREAM, 0);
84456668Sshin		if (wport4 == -1)
84556668Sshin			goto lprtfail;
84656668Sshin		error = bind(wport4, (struct sockaddr *)sin, sin->sin_len);
84756668Sshin		if (error == -1) {
84856668Sshin			close(wport4);
84956668Sshin			wport4 = -1;
85056668Sshin			goto lprtfail;
85156668Sshin		}
85256668Sshin		error = listen(wport4, 1);
85356668Sshin		if (error == -1) {
85456668Sshin			close(wport4);
85556668Sshin			wport4 = -1;
85656668Sshin			goto lprtfail;
85756668Sshin		}
85856668Sshin
85956668Sshin		/* transmit PORT */
860122679Sume		len = sizeof(data4);
861122679Sume		error = getsockname(wport4, (struct sockaddr *)&data4, &len);
86256668Sshin		if (error == -1) {
86356668Sshin			close(wport4);
86456668Sshin			wport4 = -1;
86556668Sshin			goto lprtfail;
86656668Sshin		}
86756668Sshin		if (((struct sockaddr *)&data4)->sa_family != AF_INET) {
86856668Sshin			close(wport4);
86956668Sshin			wport4 = -1;
87056668Sshin			goto lprtfail;
87156668Sshin		}
87256668Sshin		sin = (struct sockaddr_in *)&data4;
87356668Sshin		a = (char *)&sin->sin_addr;
87456668Sshin		p = (char *)&sin->sin_port;
87556668Sshin		n = snprintf(sbuf, sizeof(sbuf), "PORT %d,%d,%d,%d,%d,%d\r\n",
87656668Sshin				  UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
87756668Sshin				  UC(p[0]), UC(p[1]));
878122679Sume		if (n < 0 || n >= sizeof(sbuf))
879122679Sume			n = 0;
880122679Sume		if (n)
881122679Sume			write(dst, sbuf, n);
88256668Sshin		*state = nstate;
88356668Sshin		passivemode = 0;
88456668Sshin		return n;
88556668Sshin	} else if (strcmp(cmd, "EPRT") == 0 && param) {
88656668Sshin		/*
88756668Sshin		 * EPRT -> PORT
88856668Sshin		 */
88956668Sshin		char *afp, *hostp, *portp;
89056668Sshin		struct addrinfo hints, *res;
89156668Sshin
89256668Sshin		nstate = EPRT;
89356668Sshin
89456668Sshin		close(wport4);
89556668Sshin		close(wport6);
89656668Sshin		close(port4);
89756668Sshin		close(port6);
89856668Sshin		wport4 = wport6 = port4 = port6 = -1;
89956668Sshin
90056668Sshin		if (epsvall) {
90156668Sshin			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
90256668Sshin				cmd);
903122679Sume			if (n < 0 || n >= sizeof(sbuf))
904122679Sume				n = 0;
905122679Sume			if (n)
906122679Sume				write(src, sbuf, n);
907122679Sume			return n;
90856668Sshin		}
90956668Sshin
91056668Sshin		p = param;
91156668Sshin		ch = *p++;	/* boundary character */
91256668Sshin		afp = p;
91356668Sshin		while (*p && *p != ch)
91456668Sshin			p++;
91556668Sshin		if (!*p) {
91656668Sshineprtparamfail:
91756668Sshin			n = snprintf(sbuf, sizeof(sbuf),
91856668Sshin				"501 illegal parameter to EPRT\r\n");
919122679Sume			if (n < 0 || n >= sizeof(sbuf))
920122679Sume				n = 0;
921122679Sume			if (n)
922122679Sume				write(src, sbuf, n);
923122679Sume			return n;
92456668Sshin		}
92556668Sshin		*p++ = '\0';
92656668Sshin		hostp = p;
92756668Sshin		while (*p && *p != ch)
92856668Sshin			p++;
92956668Sshin		if (!*p)
93056668Sshin			goto eprtparamfail;
93156668Sshin		*p++ = '\0';
93256668Sshin		portp = p;
93356668Sshin		while (*p && *p != ch)
93456668Sshin			p++;
93556668Sshin		if (!*p)
93656668Sshin			goto eprtparamfail;
93756668Sshin		*p++ = '\0';
93856668Sshin
93956668Sshin		n = sscanf(afp, "%d", &af);
94056668Sshin		if (n != 1 || af != 2) {
94156668Sshin			n = snprintf(sbuf, sizeof(sbuf),
94256668Sshin				"501 unsupported address family to EPRT\r\n");
943122679Sume			if (n < 0 || n >= sizeof(sbuf))
944122679Sume				n = 0;
945122679Sume			if (n)
946122679Sume				write(src, sbuf, n);
947122679Sume			return n;
94856668Sshin		}
94956668Sshin		memset(&hints, 0, sizeof(hints));
95056668Sshin		hints.ai_family = AF_UNSPEC;
95178064Sume		hints.ai_socktype = SOCK_STREAM;
952122679Sume		hints.ai_protocol = IPPROTO_TCP;
95356668Sshin		error = getaddrinfo(hostp, portp, &hints, &res);
95456668Sshin		if (error) {
95556668Sshin			n = snprintf(sbuf, sizeof(sbuf),
95662655Skris				"501 EPRT: %s\r\n", gai_strerror(error));
957122679Sume			if (n < 0 || n >= sizeof(sbuf))
958122679Sume				n = 0;
959122679Sume			if (n)
960122679Sume				write(src, sbuf, n);
961122679Sume			return n;
96256668Sshin		}
96356668Sshin		if (res->ai_next) {
96456668Sshin			n = snprintf(sbuf, sizeof(sbuf),
96556668Sshin				"501 EPRT: %s resolved to multiple addresses\r\n", hostp);
966122679Sume			if (n < 0 || n >= sizeof(sbuf))
967122679Sume				n = 0;
968122679Sume			if (n)
969122679Sume				write(src, sbuf, n);
970145357Ssuz			freeaddrinfo(res);
971122679Sume			return n;
97256668Sshin		}
97356668Sshin
97456668Sshin		memcpy(&data6, res->ai_addr, res->ai_addrlen);
97556668Sshin
976145791Ssuz		freeaddrinfo(res);
97756668Sshin		goto sendport;
97856668Sshin	} else if (strcmp(cmd, "LPSV") == 0 && !param) {
97956668Sshin		/*
98056668Sshin		 * LPSV -> PASV
98156668Sshin		 */
98256668Sshin		nstate = LPSV;
98356668Sshin
98456668Sshin		close(wport4);
98556668Sshin		close(wport6);
98656668Sshin		close(port4);
98756668Sshin		close(port6);
98856668Sshin		wport4 = wport6 = port4 = port6 = -1;
98956668Sshin
99056668Sshin		if (epsvall) {
99156668Sshin			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
99256668Sshin				cmd);
993122679Sume			if (n < 0 || n >= sizeof(sbuf))
994122679Sume				n = 0;
995122679Sume			if (n)
996122679Sume				write(src, sbuf, n);
997122679Sume			return n;
99856668Sshin		}
99956668Sshin
100056668Sshin		/* transmit PASV */
100156668Sshin		n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
1002122679Sume		if (n < 0 || n >= sizeof(sbuf))
1003122679Sume			n = 0;
1004122679Sume		if (n)
1005122679Sume			write(dst, sbuf, n);
100656668Sshin		*state = LPSV;
100756668Sshin		passivemode = 0;	/* to be set to 1 later */
100856668Sshin		return n;
100956668Sshin	} else if (strcmp(cmd, "EPSV") == 0 && !param) {
101056668Sshin		/*
101156668Sshin		 * EPSV -> PASV
101256668Sshin		 */
101356668Sshin		close(wport4);
101456668Sshin		close(wport6);
101556668Sshin		close(port4);
101656668Sshin		close(port6);
101756668Sshin		wport4 = wport6 = port4 = port6 = -1;
101856668Sshin
101956668Sshin		n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
1020122679Sume		if (n < 0 || n >= sizeof(sbuf))
1021122679Sume			n = 0;
1022122679Sume		if (n)
1023122679Sume			write(dst, sbuf, n);
102456668Sshin		*state = EPSV;
102556668Sshin		passivemode = 0;	/* to be set to 1 later */
102656668Sshin		return n;
102756668Sshin	} else if (strcmp(cmd, "EPSV") == 0 && param
102856668Sshin	 && strncasecmp(param, "ALL", 3) == 0 && isspace(param[3])) {
102956668Sshin		/*
103056668Sshin		 * EPSV ALL
103156668Sshin		 */
103256668Sshin		epsvall = 1;
103356668Sshin		n = snprintf(sbuf, sizeof(sbuf), "200 EPSV ALL command successful.\r\n");
1034122679Sume		if (n < 0 || n >= sizeof(sbuf))
1035122679Sume			n = 0;
1036122679Sume		if (n)
1037122679Sume			write(src, sbuf, n);
103856668Sshin		return n;
103956668Sshin	} else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) {
104056668Sshin		/*
104156668Sshin		 * reject PORT/PASV
104256668Sshin		 */
104356668Sshin		n = snprintf(sbuf, sizeof(sbuf), "502 %s not implemented.\r\n", cmd);
1044122679Sume		if (n < 0 || n >= sizeof(sbuf))
1045122679Sume			n = 0;
1046122679Sume		if (n)
1047122679Sume			write(src, sbuf, n);
1048122679Sume		return n;
104956668Sshin	} else if (passivemode
105056668Sshin		&& (strcmp(cmd, "STOR") == 0
105156668Sshin		 || strcmp(cmd, "STOU") == 0
105256668Sshin		 || strcmp(cmd, "RETR") == 0
105356668Sshin		 || strcmp(cmd, "LIST") == 0
105456668Sshin		 || strcmp(cmd, "NLST") == 0
105556668Sshin		 || strcmp(cmd, "APPE") == 0)) {
105656668Sshin		/*
105756668Sshin		 * commands with data transfer.  need to care about passive
105856668Sshin		 * mode data connection.
105956668Sshin		 */
106056668Sshin
106156668Sshin		if (ftp_passiveconn() < 0) {
106256668Sshin			n = snprintf(sbuf, sizeof(sbuf), "425 Cannot open data connetion\r\n");
1063122679Sume			if (n < 0 || n >= sizeof(sbuf))
1064122679Sume				n = 0;
1065122679Sume			if (n)
1066122679Sume				write(src, sbuf, n);
106756668Sshin		} else {
106856668Sshin			/* simply relay the command */
106956668Sshin			write(dst, rbuf, n);
107056668Sshin		}
107156668Sshin
107256668Sshin		*state = NONE;
107356668Sshin		return n;
107456668Sshin	} else {
107556668Sshin		/* simply relay it */
107656668Sshin		*state = NONE;
1077122679Sume		write(dst, rbuf, n);
1078122679Sume		return n;
107956668Sshin	}
108056668Sshin
108156668Sshin bad:
108295023Ssuz	exit_failure("%s", strerror(errno));
108356668Sshin	/*NOTREACHED*/
108456668Sshin	return 0;	/* to make gcc happy */
108556668Sshin}
1086