xref: /netbsd-src/usr.sbin/faithd/ftp.c (revision 749ce5c9e67932dcbaa1a7317f70b533653cedcb)
1*749ce5c9Sandvar /*	$NetBSD: ftp.c,v 1.20 2024/02/09 20:55:15 andvar Exp $	*/
2ecf55737Sitojun /*	$KAME: ftp.c,v 1.23 2003/08/19 21:20:33 itojun Exp $	*/
3e5db40b6Sitojun 
4e5db40b6Sitojun /*
5e5db40b6Sitojun  * Copyright (C) 1997 and 1998 WIDE Project.
6e5db40b6Sitojun  * All rights reserved.
7e5db40b6Sitojun  *
8e5db40b6Sitojun  * Redistribution and use in source and binary forms, with or without
9e5db40b6Sitojun  * modification, are permitted provided that the following conditions
10e5db40b6Sitojun  * are met:
11e5db40b6Sitojun  * 1. Redistributions of source code must retain the above copyright
12e5db40b6Sitojun  *    notice, this list of conditions and the following disclaimer.
13e5db40b6Sitojun  * 2. Redistributions in binary form must reproduce the above copyright
14e5db40b6Sitojun  *    notice, this list of conditions and the following disclaimer in the
15e5db40b6Sitojun  *    documentation and/or other materials provided with the distribution.
16e5db40b6Sitojun  * 3. Neither the name of the project nor the names of its contributors
17e5db40b6Sitojun  *    may be used to endorse or promote products derived from this software
18e5db40b6Sitojun  *    without specific prior written permission.
19e5db40b6Sitojun  *
20e5db40b6Sitojun  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21e5db40b6Sitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22e5db40b6Sitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23e5db40b6Sitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24e5db40b6Sitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25e5db40b6Sitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26e5db40b6Sitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27e5db40b6Sitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28e5db40b6Sitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29e5db40b6Sitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30e5db40b6Sitojun  * SUCH DAMAGE.
31e5db40b6Sitojun  */
32e5db40b6Sitojun 
33e5db40b6Sitojun #include <sys/param.h>
34e5db40b6Sitojun #include <sys/types.h>
35e5db40b6Sitojun #include <sys/socket.h>
36e5db40b6Sitojun #include <sys/ioctl.h>
37e5db40b6Sitojun #include <sys/time.h>
38e5db40b6Sitojun 
39e5db40b6Sitojun #include <stdio.h>
40e5db40b6Sitojun #include <stdlib.h>
41e5db40b6Sitojun #include <string.h>
42e5db40b6Sitojun #include <syslog.h>
43e5db40b6Sitojun #include <unistd.h>
44ecf55737Sitojun #include <poll.h>
45e5db40b6Sitojun #include <errno.h>
46e5db40b6Sitojun #include <ctype.h>
47e5db40b6Sitojun 
48e5db40b6Sitojun #include <netinet/in.h>
49e5db40b6Sitojun #include <arpa/inet.h>
50e5db40b6Sitojun #include <netdb.h>
51e5db40b6Sitojun 
52e5db40b6Sitojun #include "faithd.h"
53e5db40b6Sitojun 
54e5db40b6Sitojun static char rbuf[MSS];
55e5db40b6Sitojun static int passivemode = 0;
56e5db40b6Sitojun static int wport4 = -1;			/* listen() to active */
57e5db40b6Sitojun static int wport6 = -1;			/* listen() to passive */
58e5db40b6Sitojun static int port4 = -1;			/* active: inbound  passive: outbound */
59e5db40b6Sitojun static int port6 = -1;			/* active: outbound  passive: inbound */
60e5db40b6Sitojun static struct sockaddr_storage data4;	/* server data address */
61e5db40b6Sitojun static struct sockaddr_storage data6;	/* client data address */
62e5db40b6Sitojun static int epsvall = 0;
63e5db40b6Sitojun 
64e5db40b6Sitojun enum state { NONE, LPRT, EPRT, LPSV, EPSV };
65e5db40b6Sitojun 
662579358aSchristos static int ftp_activeconn(void);
672579358aSchristos static int ftp_passiveconn(void);
682579358aSchristos static ssize_t ftp_copy(int, int);
692579358aSchristos static ssize_t ftp_copyresult(int, int, enum state);
702579358aSchristos static ssize_t ftp_copycommand(int, int, enum state *);
71e5db40b6Sitojun 
72e5db40b6Sitojun void
ftp_relay(int ctl6,int ctl4)73e5db40b6Sitojun ftp_relay(int ctl6, int ctl4)
74e5db40b6Sitojun {
75ecf55737Sitojun 	struct pollfd pfd[6];
762579358aSchristos 	ssize_t error;
77e5db40b6Sitojun 	enum state state = NONE;
78e5db40b6Sitojun 
79e5db40b6Sitojun 	syslog(LOG_INFO, "starting ftp control connection");
80e5db40b6Sitojun 
81e5db40b6Sitojun 	for (;;) {
82ecf55737Sitojun 		pfd[0].fd = ctl4;
83ecf55737Sitojun 		pfd[0].events = POLLIN;
84ecf55737Sitojun 		pfd[1].fd = ctl6;
85ecf55737Sitojun 		pfd[1].events = POLLIN;
86bc0d6cddSitojun 		if (0 <= port4) {
87ecf55737Sitojun 			pfd[2].fd = port4;
88ecf55737Sitojun 			pfd[2].events = POLLIN;
89ecf55737Sitojun 		} else
90ecf55737Sitojun 			pfd[2].fd = -1;
91bc0d6cddSitojun 		if (0 <= port6) {
92ecf55737Sitojun 			pfd[3].fd = port6;
93ecf55737Sitojun 			pfd[3].events = POLLIN;
94ecf55737Sitojun 		} else
95ecf55737Sitojun 			pfd[3].fd = -1;
96e5db40b6Sitojun #if 0
97bc0d6cddSitojun 		if (0 <= wport4) {
98ecf55737Sitojun 			pfd[4].fd = wport4;
99ecf55737Sitojun 			pfd[4].events = POLLIN;
100ecf55737Sitojun 		} else
101ecf55737Sitojun 			pfd[4].fd = -1;
102bc0d6cddSitojun 		if (0 <= wport6) {
103ecf55737Sitojun 			pfd[5].fd = wport4;
104ecf55737Sitojun 			pfd[5].events = POLLIN;
105ecf55737Sitojun 		} else
106ecf55737Sitojun 			pfd[5].fd = -1;
107ecf55737Sitojun #else
108ecf55737Sitojun 		pfd[4].fd = pfd[5].fd = -1;
109ecf55737Sitojun 		pfd[4].events = pfd[5].events = 0;
110e5db40b6Sitojun #endif
1112579358aSchristos 		error = poll(pfd, (unsigned int)(sizeof(pfd) / sizeof(pfd[0])),
1122579358aSchristos 		    FAITH_TIMEOUT * 1000);
113ecf55737Sitojun 		if (error == -1) {
114ecf55737Sitojun 			exit_failure("poll: %s", strerror(errno));
115ecf55737Sitojun 		}
116e5db40b6Sitojun 		else if (error == 0)
117e5db40b6Sitojun 			exit_failure("connection timeout");
118e5db40b6Sitojun 
119e5db40b6Sitojun 		/*
120e5db40b6Sitojun 		 * The order of the following checks does (slightly) matter.
121e5db40b6Sitojun 		 * It is important to visit all checks (do not use "continue"),
122e5db40b6Sitojun 		 * otherwise some of the pipe may become full and we cannot
123e5db40b6Sitojun 		 * relay correctly.
124e5db40b6Sitojun 		 */
125ecf55737Sitojun 		if (pfd[1].revents & POLLIN)
126ecf55737Sitojun 		{
127e5db40b6Sitojun 			/*
128e5db40b6Sitojun 			 * copy control connection from the client.
129e5db40b6Sitojun 			 * command translation is necessary.
130e5db40b6Sitojun 			 */
131e5db40b6Sitojun 			error = ftp_copycommand(ctl6, ctl4, &state);
132e5db40b6Sitojun 
13318446509Sitojun 			if (error < 0)
134e5db40b6Sitojun 				goto bad;
13518446509Sitojun 			else if (error == 0) {
1364c1a6c87Schristos 				(void)close(ctl4);
1374c1a6c87Schristos 				(void)close(ctl6);
138e5db40b6Sitojun 				exit_success("terminating ftp control connection");
139e5db40b6Sitojun 				/*NOTREACHED*/
140e5db40b6Sitojun 			}
141e5db40b6Sitojun 		}
142ecf55737Sitojun 		if (pfd[0].revents & POLLIN)
143ecf55737Sitojun 		{
144e5db40b6Sitojun 			/*
145e5db40b6Sitojun 			 * copy control connection from the server
146e5db40b6Sitojun 			 * translation of result code is necessary.
147e5db40b6Sitojun 			 */
148e5db40b6Sitojun 			error = ftp_copyresult(ctl4, ctl6, state);
149e5db40b6Sitojun 
15018446509Sitojun 			if (error < 0)
151e5db40b6Sitojun 				goto bad;
15218446509Sitojun 			else if (error == 0) {
1534c1a6c87Schristos 				(void)close(ctl4);
1544c1a6c87Schristos 				(void)close(ctl6);
155e5db40b6Sitojun 				exit_success("terminating ftp control connection");
156e5db40b6Sitojun 				/*NOTREACHED*/
157e5db40b6Sitojun 			}
158e5db40b6Sitojun 		}
159ecf55737Sitojun 		if (0 <= port4 && 0 <= port6 && (pfd[2].revents & POLLIN))
160ecf55737Sitojun 		{
161e5db40b6Sitojun 			/*
162e5db40b6Sitojun 			 * copy data connection.
163e5db40b6Sitojun 			 * no special treatment necessary.
164e5db40b6Sitojun 			 */
165ecf55737Sitojun 			if (pfd[2].revents & POLLIN)
166e5db40b6Sitojun 				error = ftp_copy(port4, port6);
167e5db40b6Sitojun 			switch (error) {
168e5db40b6Sitojun 			case -1:
169e5db40b6Sitojun 				goto bad;
170e5db40b6Sitojun 			case 0:
17110dfada8Schristos 				if (port4 >= 0) {
1722579358aSchristos 					(void)close(port4);
17310dfada8Schristos 					port4 = -1;
17410dfada8Schristos 				}
17510dfada8Schristos 				if (port6 >= 0) {
1762579358aSchristos 					(void)close(port6);
17710dfada8Schristos 					port6 = -1;
17810dfada8Schristos 				}
179e5db40b6Sitojun 				syslog(LOG_INFO, "terminating data connection");
180e5db40b6Sitojun 				break;
181e5db40b6Sitojun 			default:
182e5db40b6Sitojun 				break;
183e5db40b6Sitojun 			}
184e5db40b6Sitojun 		}
185ecf55737Sitojun 		if (0 <= port4 && 0 <= port6 && (pfd[3].revents & POLLIN))
186ecf55737Sitojun 		{
187e5db40b6Sitojun 			/*
188e5db40b6Sitojun 			 * copy data connection.
189e5db40b6Sitojun 			 * no special treatment necessary.
190e5db40b6Sitojun 			 */
191ecf55737Sitojun 			if (pfd[3].revents & POLLIN)
192e5db40b6Sitojun 				error = ftp_copy(port6, port4);
193e5db40b6Sitojun 			switch (error) {
194e5db40b6Sitojun 			case -1:
195e5db40b6Sitojun 				goto bad;
196e5db40b6Sitojun 			case 0:
19710dfada8Schristos 				if (port4 >= 0) {
1982579358aSchristos 					(void)close(port4);
19910dfada8Schristos 					port4 = -1;
20010dfada8Schristos 				}
20110dfada8Schristos 				if (port6 >= 0) {
2022579358aSchristos 					(void)close(port6);
20310dfada8Schristos 					port6 = -1;
20410dfada8Schristos 				}
205e5db40b6Sitojun 				syslog(LOG_INFO, "terminating data connection");
206e5db40b6Sitojun 				break;
207e5db40b6Sitojun 			default:
208e5db40b6Sitojun 				break;
209e5db40b6Sitojun 			}
210e5db40b6Sitojun 		}
211e5db40b6Sitojun #if 0
212ecf55737Sitojun 		if (wport4 && (pfd[4].revents & POLLIN))
213ecf55737Sitojun 		{
214e5db40b6Sitojun 			/*
215e5db40b6Sitojun 			 * establish active data connection from the server.
216e5db40b6Sitojun 			 */
217e5db40b6Sitojun 			ftp_activeconn();
218e5db40b6Sitojun 		}
219ecf55737Sitojun 		if (wport4 && (pfd[5].revents & POLLIN))
220ecf55737Sitojun 		{
221e5db40b6Sitojun 			/*
222e5db40b6Sitojun 			 * establish passive data connection from the client.
223e5db40b6Sitojun 			 */
224e5db40b6Sitojun 			ftp_passiveconn();
225e5db40b6Sitojun 		}
226e5db40b6Sitojun #endif
227e5db40b6Sitojun 	}
228e5db40b6Sitojun 
229e5db40b6Sitojun  bad:
230bc0d6cddSitojun 	exit_failure("%s", strerror(errno));
231e5db40b6Sitojun }
232e5db40b6Sitojun 
233e5db40b6Sitojun static int
ftp_activeconn()234e5db40b6Sitojun ftp_activeconn()
235e5db40b6Sitojun {
23652c469ffSitojun 	socklen_t n;
237e5db40b6Sitojun 	int error;
238ecf55737Sitojun 	struct pollfd pfd[1];
2399e8f1055Sitojun 	struct sockaddr *sa;
240e5db40b6Sitojun 
241e5db40b6Sitojun 	/* get active connection from server */
242ecf55737Sitojun 	pfd[0].fd = wport4;
243ecf55737Sitojun 	pfd[0].events = POLLIN;
244e5db40b6Sitojun 	n = sizeof(data4);
2452579358aSchristos 	if (poll(pfd, (unsigned int)(sizeof(pfd) / sizeof(pfd[0])),
2462579358aSchristos 	    120000) == 0 ||
2472579358aSchristos 	    (port4 = accept(wport4, (void *)&data4, &n)) < 0)
248ecf55737Sitojun 	{
2494c1a6c87Schristos 		(void)close(wport4);
250e5db40b6Sitojun 		wport4 = -1;
251e5db40b6Sitojun 		syslog(LOG_INFO, "active mode data connection failed");
252e5db40b6Sitojun 		return -1;
253e5db40b6Sitojun 	}
254e5db40b6Sitojun 
255e5db40b6Sitojun 	/* ask active connection to client */
2562579358aSchristos 	sa = (void *)&data6;
2579e8f1055Sitojun 	port6 = socket(sa->sa_family, SOCK_STREAM, 0);
258e5db40b6Sitojun 	if (port6 == -1) {
2594c1a6c87Schristos 		(void)close(port4);
2604c1a6c87Schristos 		(void)close(wport4);
261e5db40b6Sitojun 		port4 = wport4 = -1;
262e5db40b6Sitojun 		syslog(LOG_INFO, "active mode data connection failed");
263e5db40b6Sitojun 		return -1;
264e5db40b6Sitojun 	}
2652579358aSchristos 	error = connect(port6, sa, (socklen_t)sa->sa_len);
2663f183427Sitojun 	if (error < 0) {
2674c1a6c87Schristos 		(void)close(port6);
2684c1a6c87Schristos 		(void)close(port4);
2694c1a6c87Schristos 		(void)close(wport4);
270e5db40b6Sitojun 		port6 = port4 = wport4 = -1;
271e5db40b6Sitojun 		syslog(LOG_INFO, "active mode data connection failed");
272e5db40b6Sitojun 		return -1;
273e5db40b6Sitojun 	}
274e5db40b6Sitojun 
275e5db40b6Sitojun 	syslog(LOG_INFO, "active mode data connection established");
276e5db40b6Sitojun 	return 0;
277e5db40b6Sitojun }
278e5db40b6Sitojun 
279e5db40b6Sitojun static int
ftp_passiveconn()280e5db40b6Sitojun ftp_passiveconn()
281e5db40b6Sitojun {
28252c469ffSitojun 	socklen_t len;
283e5db40b6Sitojun 	int error;
284ecf55737Sitojun 	struct pollfd pfd[1];
2859e8f1055Sitojun 	struct sockaddr *sa;
286e5db40b6Sitojun 
287e5db40b6Sitojun 	/* get passive connection from client */
288ecf55737Sitojun 	pfd[0].fd = wport6;
289ecf55737Sitojun 	pfd[0].events = POLLIN;
29052c469ffSitojun 	len = sizeof(data6);
2912579358aSchristos 	if (poll(pfd, (unsigned int)(sizeof(pfd) / sizeof(pfd[0])),
2922579358aSchristos 	    120000) == 0 ||
2932579358aSchristos 	    (port6 = accept(wport6, (void *)&data6, &len)) < 0)
294ecf55737Sitojun 	{
2954c1a6c87Schristos 		(void)close(wport6);
296e5db40b6Sitojun 		wport6 = -1;
297e5db40b6Sitojun 		syslog(LOG_INFO, "passive mode data connection failed");
298e5db40b6Sitojun 		return -1;
299e5db40b6Sitojun 	}
300e5db40b6Sitojun 
301e5db40b6Sitojun 	/* ask passive connection to server */
3022579358aSchristos 	sa = (void *)&data4;
3039e8f1055Sitojun 	port4 = socket(sa->sa_family, SOCK_STREAM, 0);
304e5db40b6Sitojun 	if (port4 == -1) {
3054c1a6c87Schristos 		(void)close(wport6);
3064c1a6c87Schristos 		(void)close(port6);
307e5db40b6Sitojun 		wport6 = port6 = -1;
308e5db40b6Sitojun 		syslog(LOG_INFO, "passive mode data connection failed");
309e5db40b6Sitojun 		return -1;
310e5db40b6Sitojun 	}
3112579358aSchristos 	error = connect(port4, sa, (socklen_t)sa->sa_len);
3123f183427Sitojun 	if (error < 0) {
3134c1a6c87Schristos 		(void)close(wport6);
3144c1a6c87Schristos 		(void)close(port4);
3154c1a6c87Schristos 		(void)close(port6);
316e5db40b6Sitojun 		wport6 = port4 = port6 = -1;
317e5db40b6Sitojun 		syslog(LOG_INFO, "passive mode data connection failed");
318e5db40b6Sitojun 		return -1;
319e5db40b6Sitojun 	}
320e5db40b6Sitojun 
321e5db40b6Sitojun 	syslog(LOG_INFO, "passive mode data connection established");
322e5db40b6Sitojun 	return 0;
323e5db40b6Sitojun }
324e5db40b6Sitojun 
3252579358aSchristos static ssize_t
ftp_copy(int src,int dst)326e5db40b6Sitojun ftp_copy(int src, int dst)
327e5db40b6Sitojun {
3282579358aSchristos 	int error, atmark;
3292579358aSchristos 	ssize_t n;
330e5db40b6Sitojun 
331e5db40b6Sitojun 	/* OOB data handling */
332e5db40b6Sitojun 	error = ioctl(src, SIOCATMARK, &atmark);
333e5db40b6Sitojun 	if (error != -1 && atmark == 1) {
334e5db40b6Sitojun 		n = read(src, rbuf, 1);
335e5db40b6Sitojun 		if (n == -1)
336e5db40b6Sitojun 			goto bad;
3372579358aSchristos 		(void)send(dst, rbuf, (size_t)n, MSG_OOB);
338e5db40b6Sitojun #if 0
339e5db40b6Sitojun 		n = read(src, rbuf, sizeof(rbuf));
340e5db40b6Sitojun 		if (n == -1)
341e5db40b6Sitojun 			goto bad;
3422579358aSchristos 		(void)write(dst, rbuf, (size_t)n);
343e5db40b6Sitojun 		return n;
344e5db40b6Sitojun #endif
345e5db40b6Sitojun 	}
346e5db40b6Sitojun 
347e5db40b6Sitojun 	n = read(src, rbuf, sizeof(rbuf));
348e5db40b6Sitojun 	switch (n) {
349e5db40b6Sitojun 	case -1:
350e5db40b6Sitojun 	case 0:
351e5db40b6Sitojun 		return n;
352e5db40b6Sitojun 	default:
3532579358aSchristos 		(void)write(dst, rbuf, (size_t)n);
354e5db40b6Sitojun 		return n;
355e5db40b6Sitojun 	}
356e5db40b6Sitojun 
357e5db40b6Sitojun  bad:
358bc0d6cddSitojun 	exit_failure("%s", strerror(errno));
359e5db40b6Sitojun 	/*NOTREACHED*/
360e5db40b6Sitojun 	return 0;	/* to make gcc happy */
361e5db40b6Sitojun }
362e5db40b6Sitojun 
3632579358aSchristos static ssize_t
ftp_copyresult(int src,int dst,enum state state)364e5db40b6Sitojun ftp_copyresult(int src, int dst, enum state state)
365e5db40b6Sitojun {
3662579358aSchristos 	int error, atmark;
3672579358aSchristos 	ssize_t n;
36852c469ffSitojun 	socklen_t len;
369e5db40b6Sitojun 	char *param;
370e5db40b6Sitojun 	int code;
371bc0d6cddSitojun 	char *a, *p;
372bc0d6cddSitojun 	int i;
373e5db40b6Sitojun 
374e5db40b6Sitojun 	/* OOB data handling */
375e5db40b6Sitojun 	error = ioctl(src, SIOCATMARK, &atmark);
376e5db40b6Sitojun 	if (error != -1 && atmark == 1) {
377e5db40b6Sitojun 		n = read(src, rbuf, 1);
378e5db40b6Sitojun 		if (n == -1)
379e5db40b6Sitojun 			goto bad;
3802579358aSchristos 		(void)send(dst, rbuf, (size_t)n, MSG_OOB);
381e5db40b6Sitojun #if 0
382e5db40b6Sitojun 		n = read(src, rbuf, sizeof(rbuf));
383e5db40b6Sitojun 		if (n == -1)
384e5db40b6Sitojun 			goto bad;
3852579358aSchristos 		(void)write(dst, rbuf, (size_t)n);
386e5db40b6Sitojun 		return n;
387e5db40b6Sitojun #endif
388e5db40b6Sitojun 	}
389e5db40b6Sitojun 
390e5db40b6Sitojun 	n = read(src, rbuf, sizeof(rbuf));
391e5db40b6Sitojun 	if (n <= 0)
392e5db40b6Sitojun 		return n;
393e5db40b6Sitojun 	rbuf[n] = '\0';
394e5db40b6Sitojun 
395e5db40b6Sitojun 	/*
396e5db40b6Sitojun 	 * parse argument
397e5db40b6Sitojun 	 */
398e5db40b6Sitojun 	p = rbuf;
399e5db40b6Sitojun 	for (i = 0; i < 3; i++) {
400cfe7f80fSdsl 		if (!isdigit((unsigned char)*p)) {
401e5db40b6Sitojun 			/* invalid reply */
4022579358aSchristos 			(void)write(dst, rbuf, (size_t)n);
403e5db40b6Sitojun 			return n;
404e5db40b6Sitojun 		}
405e5db40b6Sitojun 		p++;
406e5db40b6Sitojun 	}
407cfe7f80fSdsl 	if (!isspace((unsigned char)*p)) {
408e5db40b6Sitojun 		/* invalid reply */
4092579358aSchristos 		(void)write(dst, rbuf, (size_t)n);
410e5db40b6Sitojun 		return n;
411e5db40b6Sitojun 	}
412e5db40b6Sitojun 	code = atoi(rbuf);
413e5db40b6Sitojun 	param = p;
414e5db40b6Sitojun 	/* param points to first non-command token, if any */
415cfe7f80fSdsl 	while (*param && isspace((unsigned char)*param))
416e5db40b6Sitojun 		param++;
417e5db40b6Sitojun 	if (!*param)
418e5db40b6Sitojun 		param = NULL;
419e5db40b6Sitojun 
420e5db40b6Sitojun 	switch (state) {
421e5db40b6Sitojun 	case NONE:
422e5db40b6Sitojun 		if (!passivemode && rbuf[0] == '1') {
423e5db40b6Sitojun 			if (ftp_activeconn() < 0) {
4242d9ec4daSitojun 				n = snprintf(rbuf, sizeof(rbuf),
425*749ce5c9Sandvar 				    "425 Cannot open data connection\r\n");
4266bc6e73bSlukem 				if (n < 0 || n >= (int)sizeof(rbuf))
427bc0d6cddSitojun 					n = 0;
428e5db40b6Sitojun 			}
429e5db40b6Sitojun 		}
430bc0d6cddSitojun 		if (n)
4312579358aSchristos 			(void)write(dst, rbuf, (size_t)n);
432e5db40b6Sitojun 		return n;
433e5db40b6Sitojun 	case LPRT:
434e5db40b6Sitojun 	case EPRT:
435e5db40b6Sitojun 		/* expecting "200 PORT command successful." */
436e5db40b6Sitojun 		if (code == 200) {
437e5db40b6Sitojun 			p = strstr(rbuf, "PORT");
438e5db40b6Sitojun 			if (p) {
439e5db40b6Sitojun 				p[0] = (state == LPRT) ? 'L' : 'E';
440e5db40b6Sitojun 				p[1] = 'P';
441e5db40b6Sitojun 			}
442e5db40b6Sitojun 		} else {
4434c1a6c87Schristos 			(void)close(wport4);
444e5db40b6Sitojun 			wport4 = -1;
445e5db40b6Sitojun 		}
4462579358aSchristos 		(void)write(dst, rbuf, (size_t)n);
447e5db40b6Sitojun 		return n;
448e5db40b6Sitojun 	case LPSV:
449e5db40b6Sitojun 	case EPSV:
450a5d0cbc5Sitojun 		/*
451a5d0cbc5Sitojun 		 * expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)"
452a5d0cbc5Sitojun 		 * (in some cases result comes without paren)
453a5d0cbc5Sitojun 		 */
454e5db40b6Sitojun 		if (code != 227) {
455e5db40b6Sitojun passivefail0:
4564c1a6c87Schristos 			(void)close(wport6);
457e5db40b6Sitojun 			wport6 = -1;
4582579358aSchristos 			(void)write(dst, rbuf, (size_t)n);
459e5db40b6Sitojun 			return n;
460e5db40b6Sitojun 		}
461e5db40b6Sitojun 
462e5db40b6Sitojun 	    {
463e5db40b6Sitojun 		unsigned int ho[4], po[2];
464e5db40b6Sitojun 		struct sockaddr_in *sin;
465e5db40b6Sitojun 		struct sockaddr_in6 *sin6;
466e5db40b6Sitojun 		u_short port;
467e5db40b6Sitojun 
468e5db40b6Sitojun 		/*
469e5db40b6Sitojun 		 * PASV result -> LPSV/EPSV result
470e5db40b6Sitojun 		 */
471e5db40b6Sitojun 		p = param;
472cfe7f80fSdsl 		while (*p && *p != '(' && !isdigit((unsigned char)*p))	/*)*/
473e5db40b6Sitojun 			p++;
474e5db40b6Sitojun 		if (!*p)
475e5db40b6Sitojun 			goto passivefail0;	/*XXX*/
476a5d0cbc5Sitojun 		if (*p == '(')	/*)*/
477e5db40b6Sitojun 			p++;
478e5db40b6Sitojun 		n = sscanf(p, "%u,%u,%u,%u,%u,%u",
479e5db40b6Sitojun 			&ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
480e5db40b6Sitojun 		if (n != 6)
481e5db40b6Sitojun 			goto passivefail0;	/*XXX*/
482e5db40b6Sitojun 
483e5db40b6Sitojun 		/* keep PORT parameter */
484e5db40b6Sitojun 		memset(&data4, 0, sizeof(data4));
4852579358aSchristos 		sin = (void *)&data4;
486e5db40b6Sitojun 		sin->sin_len = sizeof(*sin);
487e5db40b6Sitojun 		sin->sin_family = AF_INET;
488e5db40b6Sitojun 		sin->sin_addr.s_addr = 0;
489e5db40b6Sitojun 		for (n = 0; n < 4; n++) {
4902579358aSchristos 			sin->sin_addr.s_addr |= htonl(((uint32_t)(ho[n] & 0xff)
4912579358aSchristos 			    << (int)((3 - n) * 8)));
492e5db40b6Sitojun 		}
493e5db40b6Sitojun 		sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
494e5db40b6Sitojun 
495e5db40b6Sitojun 		/* get ready for passive data connection */
496e5db40b6Sitojun 		memset(&data6, 0, sizeof(data6));
4972579358aSchristos 		sin6 = (void *)&data6;
498e5db40b6Sitojun 		sin6->sin6_len = sizeof(*sin6);
499e5db40b6Sitojun 		sin6->sin6_family = AF_INET6;
500e5db40b6Sitojun 		wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0);
501e5db40b6Sitojun 		if (wport6 == -1) {
502e5db40b6Sitojun passivefail:
5032579358aSchristos 			return dprintf(src,
504e5db40b6Sitojun 			    "500 could not translate from PASV\r\n");
505e5db40b6Sitojun 		}
506e5db40b6Sitojun #ifdef IPV6_FAITH
507e5db40b6Sitojun 	    {
508e5db40b6Sitojun 		int on = 1;
509e5db40b6Sitojun 		error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH,
5102579358aSchristos 			&on, (socklen_t)sizeof(on));
511e5db40b6Sitojun 		if (error == -1)
512bc0d6cddSitojun 			exit_failure("setsockopt(IPV6_FAITH): %s", strerror(errno));
513e5db40b6Sitojun 	    }
514e5db40b6Sitojun #endif
5152579358aSchristos 		error = bind(wport6, (void *)sin6, (socklen_t)sin6->sin6_len);
516e5db40b6Sitojun 		if (error == -1) {
5174c1a6c87Schristos 			(void)close(wport6);
518e5db40b6Sitojun 			wport6 = -1;
519e5db40b6Sitojun 			goto passivefail;
520e5db40b6Sitojun 		}
521e5db40b6Sitojun 		error = listen(wport6, 1);
522e5db40b6Sitojun 		if (error == -1) {
5234c1a6c87Schristos 			(void)close(wport6);
524e5db40b6Sitojun 			wport6 = -1;
525e5db40b6Sitojun 			goto passivefail;
526e5db40b6Sitojun 		}
527e5db40b6Sitojun 
528e5db40b6Sitojun 		/* transmit LPSV or EPSV */
529e5db40b6Sitojun 		/*
530e5db40b6Sitojun 		 * addr from dst, port from wport6
531e5db40b6Sitojun 		 */
53252c469ffSitojun 		len = sizeof(data6);
5332579358aSchristos 		error = getsockname(wport6, (void *)&data6, &len);
534e5db40b6Sitojun 		if (error == -1) {
5354c1a6c87Schristos 			(void)close(wport6);
536e5db40b6Sitojun 			wport6 = -1;
537e5db40b6Sitojun 			goto passivefail;
538e5db40b6Sitojun 		}
5392579358aSchristos 		sin6 = (void *)&data6;
540e5db40b6Sitojun 		port = sin6->sin6_port;
541e5db40b6Sitojun 
54252c469ffSitojun 		len = sizeof(data6);
5432579358aSchristos 		error = getsockname(dst, (void *)&data6, &len);
544e5db40b6Sitojun 		if (error == -1) {
5454c1a6c87Schristos 			(void)close(wport6);
546e5db40b6Sitojun 			wport6 = -1;
547e5db40b6Sitojun 			goto passivefail;
548e5db40b6Sitojun 		}
5492579358aSchristos 		sin6 = (void *)&data6;
550e5db40b6Sitojun 		sin6->sin6_port = port;
551e5db40b6Sitojun 
552e5db40b6Sitojun 		if (state == LPSV) {
5532579358aSchristos 			a = (void *)&sin6->sin6_addr;
5542579358aSchristos 			p = (void *)&sin6->sin6_port;
5552579358aSchristos 			passivemode = 1;
5562579358aSchristos 			return dprintf(dst,
5572579358aSchristos 			    "228 Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,"
5582579358aSchristos 			    "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)\r\n",
559e5db40b6Sitojun 			    6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
560e5db40b6Sitojun 			    UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
561e5db40b6Sitojun 			    UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
562e5db40b6Sitojun 			    UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
563e5db40b6Sitojun 			    2, UC(p[0]), UC(p[1]));
564e5db40b6Sitojun 		} else {
5652579358aSchristos 			passivemode = 1;
5662579358aSchristos 			return dprintf(dst,
567e5db40b6Sitojun 			    "229 Entering Extended Passive Mode (|||%d|)\r\n",
568e5db40b6Sitojun 			    ntohs(sin6->sin6_port));
569e5db40b6Sitojun 		}
570e5db40b6Sitojun 	    }
571e5db40b6Sitojun 	}
572e5db40b6Sitojun 
573e5db40b6Sitojun  bad:
574bc0d6cddSitojun 	exit_failure("%s", strerror(errno));
575e5db40b6Sitojun 	/*NOTREACHED*/
576e5db40b6Sitojun 	return 0;	/* to make gcc happy */
577e5db40b6Sitojun }
578e5db40b6Sitojun 
5792579358aSchristos static ssize_t
ftp_copycommand(int src,int dst,enum state * state)580e5db40b6Sitojun ftp_copycommand(int src, int dst, enum state *state)
581e5db40b6Sitojun {
5822579358aSchristos 	int error, atmark;
5832579358aSchristos 	ssize_t n;
58452c469ffSitojun 	socklen_t len;
585e5db40b6Sitojun 	unsigned int af, hal, ho[16], pal, po[2];
586bc0d6cddSitojun 	char *a, *p, *q;
587e5db40b6Sitojun 	char cmd[5], *param;
588e5db40b6Sitojun 	struct sockaddr_in *sin;
589e5db40b6Sitojun 	struct sockaddr_in6 *sin6;
590e5db40b6Sitojun 	enum state nstate;
591e5db40b6Sitojun 	char ch;
592bc0d6cddSitojun 	int i;
593e5db40b6Sitojun 
594e5db40b6Sitojun 	/* OOB data handling */
595e5db40b6Sitojun 	error = ioctl(src, SIOCATMARK, &atmark);
596e5db40b6Sitojun 	if (error != -1 && atmark == 1) {
597e5db40b6Sitojun 		n = read(src, rbuf, 1);
598e5db40b6Sitojun 		if (n == -1)
599e5db40b6Sitojun 			goto bad;
6002579358aSchristos 		(void)send(dst, rbuf, (size_t)n, MSG_OOB);
601e5db40b6Sitojun #if 0
602e5db40b6Sitojun 		n = read(src, rbuf, sizeof(rbuf));
603e5db40b6Sitojun 		if (n == -1)
604e5db40b6Sitojun 			goto bad;
6052579358aSchristos 		(void)write(dst, rbuf, (size_t)n);
606e5db40b6Sitojun 		return n;
607e5db40b6Sitojun #endif
608e5db40b6Sitojun 	}
609e5db40b6Sitojun 
610e5db40b6Sitojun 	n = read(src, rbuf, sizeof(rbuf));
611e5db40b6Sitojun 	if (n <= 0)
612e5db40b6Sitojun 		return n;
613e5db40b6Sitojun 	rbuf[n] = '\0';
614e5db40b6Sitojun 
615e5db40b6Sitojun 	if (n < 4) {
6162579358aSchristos 		(void)write(dst, rbuf, (size_t)n);
617e5db40b6Sitojun 		return n;
618e5db40b6Sitojun 	}
619e5db40b6Sitojun 
620e5db40b6Sitojun 	/*
621e5db40b6Sitojun 	 * parse argument
622e5db40b6Sitojun 	 */
623e5db40b6Sitojun 	p = rbuf;
624e5db40b6Sitojun 	q = cmd;
625e5db40b6Sitojun 	for (i = 0; i < 4; i++) {
626cfe7f80fSdsl 		if (!isalpha((unsigned char)*p)) {
627e5db40b6Sitojun 			/* invalid command */
6282579358aSchristos 			(void)write(dst, rbuf, (size_t)n);
629e5db40b6Sitojun 			return n;
630e5db40b6Sitojun 		}
631cfe7f80fSdsl 		*q++ = islower((unsigned char)*p) ? toupper((unsigned char)*p) : *p;
632e5db40b6Sitojun 		p++;
633e5db40b6Sitojun 	}
634cfe7f80fSdsl 	if (!isspace((unsigned char)*p)) {
635e5db40b6Sitojun 		/* invalid command */
6362579358aSchristos 		(void)write(dst, rbuf, (size_t)n);
637e5db40b6Sitojun 		return n;
638e5db40b6Sitojun 	}
639e5db40b6Sitojun 	*q = '\0';
640e5db40b6Sitojun 	param = p;
641e5db40b6Sitojun 	/* param points to first non-command token, if any */
642cfe7f80fSdsl 	while (*param && isspace((unsigned char)*param))
643e5db40b6Sitojun 		param++;
644e5db40b6Sitojun 	if (!*param)
645e5db40b6Sitojun 		param = NULL;
646e5db40b6Sitojun 
647e5db40b6Sitojun 	*state = NONE;
648e5db40b6Sitojun 
649e5db40b6Sitojun 	if (strcmp(cmd, "LPRT") == 0 && param) {
650e5db40b6Sitojun 		/*
651e5db40b6Sitojun 		 * LPRT -> PORT
652e5db40b6Sitojun 		 */
653e5db40b6Sitojun 		nstate = LPRT;
654e5db40b6Sitojun 
6554c1a6c87Schristos 		(void)close(wport4);
6564c1a6c87Schristos 		(void)close(wport6);
6574c1a6c87Schristos 		(void)close(port4);
6584c1a6c87Schristos 		(void)close(port6);
659e5db40b6Sitojun 		wport4 = wport6 = port4 = port6 = -1;
660e5db40b6Sitojun 
661e5db40b6Sitojun 		if (epsvall) {
6622579358aSchristos 			return dprintf(src, "501 %s disallowed in EPSV ALL\r\n",
663e5db40b6Sitojun 			    cmd);
664e5db40b6Sitojun 		}
665e5db40b6Sitojun 
666e5db40b6Sitojun 		n = sscanf(param,
6672579358aSchristos 		    "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,"
6682579358aSchristos 		    "%u,%u,%u", &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3],
669e5db40b6Sitojun 		    &ho[4], &ho[5], &ho[6], &ho[7],
670e5db40b6Sitojun 		    &ho[8], &ho[9], &ho[10], &ho[11],
671e5db40b6Sitojun 		    &ho[12], &ho[13], &ho[14], &ho[15],
672e5db40b6Sitojun 		    &pal, &po[0], &po[1]);
673e5db40b6Sitojun 		if (n != 21 || af != 6 || hal != 16|| pal != 2) {
6742579358aSchristos 			return dprintf(src,
675e5db40b6Sitojun 			    "501 illegal parameter to LPRT\r\n");
676e5db40b6Sitojun 		}
677e5db40b6Sitojun 
678e5db40b6Sitojun 		/* keep LPRT parameter */
679e5db40b6Sitojun 		memset(&data6, 0, sizeof(data6));
6802579358aSchristos 		sin6 = (void *)&data6;
681e5db40b6Sitojun 		sin6->sin6_len = sizeof(*sin6);
682e5db40b6Sitojun 		sin6->sin6_family = AF_INET6;
683e5db40b6Sitojun 		for (n = 0; n < 16; n++)
6849e8f1055Sitojun 			sin6->sin6_addr.s6_addr[n] = ho[n];
685e5db40b6Sitojun 		sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
686e5db40b6Sitojun 
687e5db40b6Sitojun sendport:
688e5db40b6Sitojun 		/* get ready for active data connection */
68952c469ffSitojun 		len = sizeof(data4);
6902579358aSchristos 		error = getsockname(dst, (void *)&data4, &len);
691e5db40b6Sitojun 		if (error == -1) {
692e5db40b6Sitojun lprtfail:
6932579358aSchristos 			return dprintf(src,
694e5db40b6Sitojun 			    "500 could not translate to PORT\r\n");
695e5db40b6Sitojun 		}
6962579358aSchristos 		if (((struct sockaddr *)(void *)&data4)->sa_family != AF_INET)
697e5db40b6Sitojun 			goto lprtfail;
6982579358aSchristos 		sin = (void *)&data4;
699e5db40b6Sitojun 		sin->sin_port = 0;
700e5db40b6Sitojun 		wport4 = socket(sin->sin_family, SOCK_STREAM, 0);
701e5db40b6Sitojun 		if (wport4 == -1)
702e5db40b6Sitojun 			goto lprtfail;
7032579358aSchristos 		error = bind(wport4, (void *)sin, (socklen_t)sin->sin_len);
704e5db40b6Sitojun 		if (error == -1) {
7054c1a6c87Schristos 			(void)close(wport4);
706e5db40b6Sitojun 			wport4 = -1;
707e5db40b6Sitojun 			goto lprtfail;
708e5db40b6Sitojun 		}
709e5db40b6Sitojun 		error = listen(wport4, 1);
710e5db40b6Sitojun 		if (error == -1) {
7114c1a6c87Schristos 			(void)close(wport4);
712e5db40b6Sitojun 			wport4 = -1;
713e5db40b6Sitojun 			goto lprtfail;
714e5db40b6Sitojun 		}
715e5db40b6Sitojun 
716e5db40b6Sitojun 		/* transmit PORT */
71752c469ffSitojun 		len = sizeof(data4);
7182579358aSchristos 		error = getsockname(wport4, (void *)&data4, &len);
719e5db40b6Sitojun 		if (error == -1) {
7204c1a6c87Schristos 			(void)close(wport4);
721e5db40b6Sitojun 			wport4 = -1;
722e5db40b6Sitojun 			goto lprtfail;
723e5db40b6Sitojun 		}
7242579358aSchristos 		if (((struct sockaddr *)(void *)&data4)->sa_family != AF_INET) {
7254c1a6c87Schristos 			(void)close(wport4);
726e5db40b6Sitojun 			wport4 = -1;
727e5db40b6Sitojun 			goto lprtfail;
728e5db40b6Sitojun 		}
7292579358aSchristos 		sin = (void *)&data4;
7302579358aSchristos 		a = (void *)&sin->sin_addr;
7312579358aSchristos 		p = (void *)&sin->sin_port;
732e5db40b6Sitojun 		*state = nstate;
733e5db40b6Sitojun 		passivemode = 0;
7342579358aSchristos 		return dprintf(dst, "PORT %d,%d,%d,%d,%d,%d\r\n",
7352579358aSchristos 		    UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
736e5db40b6Sitojun 	} else if (strcmp(cmd, "EPRT") == 0 && param) {
737e5db40b6Sitojun 		/*
738e5db40b6Sitojun 		 * EPRT -> PORT
739e5db40b6Sitojun 		 */
740e5db40b6Sitojun 		char *afp, *hostp, *portp;
741e5db40b6Sitojun 		struct addrinfo hints, *res;
742e5db40b6Sitojun 
743e5db40b6Sitojun 		nstate = EPRT;
744e5db40b6Sitojun 
7454c1a6c87Schristos 		(void)close(wport4);
7464c1a6c87Schristos 		(void)close(wport6);
7474c1a6c87Schristos 		(void)close(port4);
7484c1a6c87Schristos 		(void)close(port6);
749e5db40b6Sitojun 		wport4 = wport6 = port4 = port6 = -1;
750e5db40b6Sitojun 
751e5db40b6Sitojun 		if (epsvall) {
7522579358aSchristos 			return dprintf(src, "501 %s disallowed in EPSV ALL\r\n",
753e5db40b6Sitojun 			    cmd);
754e5db40b6Sitojun 		}
755e5db40b6Sitojun 
756e5db40b6Sitojun 		p = param;
757e5db40b6Sitojun 		ch = *p++;	/* boundary character */
758e5db40b6Sitojun 		afp = p;
759e5db40b6Sitojun 		while (*p && *p != ch)
760e5db40b6Sitojun 			p++;
761e5db40b6Sitojun 		if (!*p) {
762e5db40b6Sitojun eprtparamfail:
7632579358aSchristos 			return dprintf(src,
764e5db40b6Sitojun 			    "501 illegal parameter to EPRT\r\n");
765e5db40b6Sitojun 		}
766e5db40b6Sitojun 		*p++ = '\0';
767e5db40b6Sitojun 		hostp = p;
768e5db40b6Sitojun 		while (*p && *p != ch)
769e5db40b6Sitojun 			p++;
770e5db40b6Sitojun 		if (!*p)
771e5db40b6Sitojun 			goto eprtparamfail;
772e5db40b6Sitojun 		*p++ = '\0';
773e5db40b6Sitojun 		portp = p;
774e5db40b6Sitojun 		while (*p && *p != ch)
775e5db40b6Sitojun 			p++;
776e5db40b6Sitojun 		if (!*p)
777e5db40b6Sitojun 			goto eprtparamfail;
778e5db40b6Sitojun 		*p++ = '\0';
779e5db40b6Sitojun 
780e5db40b6Sitojun 		n = sscanf(afp, "%d", &af);
781e5db40b6Sitojun 		if (n != 1 || af != 2) {
7822579358aSchristos 			return dprintf(src,
783e5db40b6Sitojun 			    "501 unsupported address family to EPRT\r\n");
784e5db40b6Sitojun 		}
785e5db40b6Sitojun 		memset(&hints, 0, sizeof(hints));
786e5db40b6Sitojun 		hints.ai_family = AF_UNSPEC;
787a5d0cbc5Sitojun 		hints.ai_socktype = SOCK_STREAM;
788ecf55737Sitojun 		hints.ai_protocol = IPPROTO_TCP;
789e5db40b6Sitojun 		error = getaddrinfo(hostp, portp, &hints, &res);
790e5db40b6Sitojun 		if (error) {
7912579358aSchristos 			return dprintf(src,
792e5db40b6Sitojun 			    "501 EPRT: %s\r\n", gai_strerror(error));
793e5db40b6Sitojun 		}
794e5db40b6Sitojun 		if (res->ai_next) {
795522016beSitojun 			freeaddrinfo(res);
7962579358aSchristos 			return dprintf(src,
7972579358aSchristos 			    "501 EPRT: %s resolved to multiple addresses\r\n",
7982579358aSchristos 			    hostp);
799e5db40b6Sitojun 		}
800e5db40b6Sitojun 
801e5db40b6Sitojun 		memcpy(&data6, res->ai_addr, res->ai_addrlen);
802e5db40b6Sitojun 
803522016beSitojun 		freeaddrinfo(res);
804e5db40b6Sitojun 		goto sendport;
805e5db40b6Sitojun 	} else if (strcmp(cmd, "LPSV") == 0 && !param) {
806e5db40b6Sitojun 		/*
807e5db40b6Sitojun 		 * LPSV -> PASV
808e5db40b6Sitojun 		 */
809e5db40b6Sitojun 		nstate = LPSV;
810e5db40b6Sitojun 
8114c1a6c87Schristos 		(void)close(wport4);
8124c1a6c87Schristos 		(void)close(wport6);
8134c1a6c87Schristos 		(void)close(port4);
8144c1a6c87Schristos 		(void)close(port6);
815e5db40b6Sitojun 		wport4 = wport6 = port4 = port6 = -1;
816e5db40b6Sitojun 
817e5db40b6Sitojun 		if (epsvall) {
8182579358aSchristos 			return dprintf(src, "501 %s disallowed in EPSV ALL\r\n",
819e5db40b6Sitojun 			    cmd);
820e5db40b6Sitojun 		}
821e5db40b6Sitojun 
822e5db40b6Sitojun 		*state = LPSV;
823e5db40b6Sitojun 		passivemode = 0;	/* to be set to 1 later */
8242579358aSchristos 		/* transmit PASV */
8252579358aSchristos 		return dprintf(dst, "PASV\r\n");
826e5db40b6Sitojun 	} else if (strcmp(cmd, "EPSV") == 0 && !param) {
827e5db40b6Sitojun 		/*
828e5db40b6Sitojun 		 * EPSV -> PASV
829e5db40b6Sitojun 		 */
8304c1a6c87Schristos 		(void)close(wport4);
8314c1a6c87Schristos 		(void)close(wport6);
8324c1a6c87Schristos 		(void)close(port4);
8334c1a6c87Schristos 		(void)close(port6);
834e5db40b6Sitojun 		wport4 = wport6 = port4 = port6 = -1;
835e5db40b6Sitojun 
836e5db40b6Sitojun 		*state = EPSV;
837e5db40b6Sitojun 		passivemode = 0;	/* to be set to 1 later */
8382579358aSchristos 		return dprintf(dst, "PASV\r\n");
8392579358aSchristos 	} else if (strcmp(cmd, "EPSV") == 0 && param &&
8402579358aSchristos 	    strncasecmp(param, "ALL", 3) == 0 &&
8412579358aSchristos 	    isspace((unsigned char)param[3])) {
842e5db40b6Sitojun 		/*
843e5db40b6Sitojun 		 * EPSV ALL
844e5db40b6Sitojun 		 */
845e5db40b6Sitojun 		epsvall = 1;
8462579358aSchristos 		return dprintf(src, "200 EPSV ALL command successful.\r\n");
847e5db40b6Sitojun 	} else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) {
848e5db40b6Sitojun 		/*
849e5db40b6Sitojun 		 * reject PORT/PASV
850e5db40b6Sitojun 		 */
8512579358aSchristos 		return dprintf(src, "502 %s not implemented.\r\n", cmd);
852e5db40b6Sitojun 	} else if (passivemode
853e5db40b6Sitojun 		&& (strcmp(cmd, "STOR") == 0
854e5db40b6Sitojun 		 || strcmp(cmd, "STOU") == 0
855e5db40b6Sitojun 		 || strcmp(cmd, "RETR") == 0
856e5db40b6Sitojun 		 || strcmp(cmd, "LIST") == 0
857e5db40b6Sitojun 		 || strcmp(cmd, "NLST") == 0
858e5db40b6Sitojun 		 || strcmp(cmd, "APPE") == 0)) {
859e5db40b6Sitojun 		/*
860e5db40b6Sitojun 		 * commands with data transfer.  need to care about passive
861e5db40b6Sitojun 		 * mode data connection.
862e5db40b6Sitojun 		 */
863e5db40b6Sitojun 
8642579358aSchristos 		*state = NONE;
865e5db40b6Sitojun 		if (ftp_passiveconn() < 0) {
8662579358aSchristos 			return dprintf(src,
867*749ce5c9Sandvar 			    "425 Cannot open data connection\r\n");
868e5db40b6Sitojun 		} else {
869e5db40b6Sitojun 			/* simply relay the command */
8702579358aSchristos 			return write(dst, rbuf, (size_t)n);
871e5db40b6Sitojun 		}
872e5db40b6Sitojun 	} else {
873e5db40b6Sitojun 		/* simply relay it */
874e5db40b6Sitojun 		*state = NONE;
8752579358aSchristos 		return write(dst, rbuf, (size_t)n);
876e5db40b6Sitojun 	}
877e5db40b6Sitojun 
878e5db40b6Sitojun  bad:
879bc0d6cddSitojun 	exit_failure("%s", strerror(errno));
880e5db40b6Sitojun 	/*NOTREACHED*/
881e5db40b6Sitojun 	return 0;	/* to make gcc happy */
882e5db40b6Sitojun }
883