ftp.c revision 145357
1238384Sjkim/*	$KAME: ftp.c,v 1.24 2005/03/16 05:05:48 itojun Exp $	*/
2238384Sjkim
3238384Sjkim/*
4238384Sjkim * Copyright (C) 1997 and 1998 WIDE Project.
5238384Sjkim * All rights reserved.
6238384Sjkim *
7238384Sjkim * Redistribution and use in source and binary forms, with or without
8238384Sjkim * modification, are permitted provided that the following conditions
9238384Sjkim * are met:
10238384Sjkim * 1. Redistributions of source code must retain the above copyright
11238384Sjkim *    notice, this list of conditions and the following disclaimer.
12238384Sjkim * 2. Redistributions in binary form must reproduce the above copyright
13238384Sjkim *    notice, this list of conditions and the following disclaimer in the
14238384Sjkim *    documentation and/or other materials provided with the distribution.
15238384Sjkim * 3. Neither the name of the project nor the names of its contributors
16238384Sjkim *    may be used to endorse or promote products derived from this software
17238384Sjkim *    without specific prior written permission.
18238384Sjkim *
19238384Sjkim * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20238384Sjkim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21238384Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22238384Sjkim * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23238384Sjkim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24238384Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25238384Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26238384Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27238384Sjkim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28238384Sjkim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29238384Sjkim * SUCH DAMAGE.
30238384Sjkim *
31246772Sjkim * $FreeBSD: head/usr.sbin/faithd/ftp.c 145357 2005-04-21 13:21:48Z suz $
32246772Sjkim */
33238384Sjkim
34238384Sjkim#include <sys/param.h>
35238384Sjkim#include <sys/types.h>
36238384Sjkim#include <sys/socket.h>
37238384Sjkim#include <sys/ioctl.h>
38238384Sjkim#include <sys/time.h>
39238384Sjkim
40238384Sjkim#include <stdio.h>
41238384Sjkim#include <stdlib.h>
42238384Sjkim#include <string.h>
43238384Sjkim#include <syslog.h>
44238384Sjkim#include <unistd.h>
45238384Sjkim#ifdef HAVE_POLL_H
46238384Sjkim#include <poll.h>
47238384Sjkim#endif
48238384Sjkim#include <errno.h>
49238384Sjkim#include <ctype.h>
50238384Sjkim
51238384Sjkim#include <netinet/in.h>
52238384Sjkim#include <arpa/inet.h>
53238384Sjkim#include <netdb.h>
54238384Sjkim
55238384Sjkim#include "faithd.h"
56238384Sjkim
57238384Sjkimstatic char rbuf[MSS];
58238384Sjkimstatic char sbuf[MSS];
59238384Sjkimstatic int passivemode = 0;
60238384Sjkimstatic int wport4 = -1;			/* listen() to active */
61238384Sjkimstatic int wport6 = -1;			/* listen() to passive */
62238384Sjkimstatic int port4 = -1;			/* active: inbound  passive: outbound */
63238384Sjkimstatic int port6 = -1;			/* active: outbound  passive: inbound */
64238384Sjkimstatic struct sockaddr_storage data4;	/* server data address */
65238384Sjkimstatic struct sockaddr_storage data6;	/* client data address */
66238384Sjkimstatic int epsvall = 0;
67238384Sjkim
68238384Sjkimenum state { NONE, LPRT, EPRT, LPSV, EPSV };
69238384Sjkim
70238384Sjkimstatic int ftp_activeconn __P((void));
71238384Sjkimstatic int ftp_passiveconn __P((void));
72238384Sjkimstatic int ftp_copy __P((int, int));
73238384Sjkimstatic int ftp_copyresult __P((int, int, enum state));
74238384Sjkimstatic int ftp_copycommand __P((int, int, enum state *));
75238384Sjkim
76238384Sjkimvoid
77238384Sjkimftp_relay(int ctl6, int ctl4)
78238384Sjkim{
79238384Sjkim#ifdef HAVE_POLL_H
80238384Sjkim	struct pollfd pfd[6];
81238384Sjkim#else
82238384Sjkim	fd_set readfds;
83238384Sjkim#endif
84238384Sjkim	int error;
85238384Sjkim	enum state state = NONE;
86238384Sjkim	struct timeval tv;
87238384Sjkim
88238384Sjkim	syslog(LOG_INFO, "starting ftp control connection");
89238384Sjkim
90238384Sjkim	for (;;) {
91238384Sjkim#ifdef HAVE_POLL_H
92238384Sjkim		pfd[0].fd = ctl4;
93238384Sjkim		pfd[0].events = POLLIN;
94238384Sjkim		pfd[1].fd = ctl6;
95238384Sjkim		pfd[1].events = POLLIN;
96238384Sjkim		if (0 <= port4) {
97238384Sjkim			pfd[2].fd = port4;
98238384Sjkim			pfd[2].events = POLLIN;
99238384Sjkim		} else
100238384Sjkim			pfd[2].fd = -1;
101238384Sjkim		if (0 <= port6) {
102238384Sjkim			pfd[3].fd = port6;
103238384Sjkim			pfd[3].events = POLLIN;
104238384Sjkim		} else
105238384Sjkim			pfd[3].fd = -1;
106238384Sjkim#if 0
107238384Sjkim		if (0 <= wport4) {
108238384Sjkim			pfd[4].fd = wport4;
109238384Sjkim			pfd[4].events = POLLIN;
110238384Sjkim		} else
111238384Sjkim			pfd[4].fd = -1;
112238384Sjkim		if (0 <= wport6) {
113238384Sjkim			pfd[5].fd = wport4;
114238384Sjkim			pfd[5].events = POLLIN;
115238384Sjkim		} else
116238384Sjkim			pfd[5].fd = -1;
117238384Sjkim#else
118238384Sjkim		pfd[4].fd = pfd[5].fd = -1;
119238384Sjkim		pfd[4].events = pfd[5].events = 0;
120238384Sjkim#endif
121238384Sjkim#else
122238384Sjkim		int maxfd = 0;
123238384Sjkim
124238384Sjkim		FD_ZERO(&readfds);
125238384Sjkim		if (ctl4 >= FD_SETSIZE)
126238384Sjkim			exit_failure("descriptor too big");
127238384Sjkim		FD_SET(ctl4, &readfds);
128238384Sjkim		maxfd = ctl4;
129238384Sjkim		if (ctl6 >= FD_SETSIZE)
130238384Sjkim			exit_failure("descriptor too big");
131238384Sjkim		FD_SET(ctl6, &readfds);
132238384Sjkim		maxfd = (ctl6 > maxfd) ? ctl6 : maxfd;
133238384Sjkim		if (0 <= port4) {
134238384Sjkim			if (port4 >= FD_SETSIZE)
135238384Sjkim				exit_failure("descriptor too big");
136238384Sjkim			FD_SET(port4, &readfds);
137238384Sjkim			maxfd = (port4 > maxfd) ? port4 : maxfd;
138238384Sjkim		}
139238384Sjkim		if (0 <= port6) {
140238384Sjkim			if (port6 >= FD_SETSIZE)
141238384Sjkim				exit_failure("descriptor too big");
142238384Sjkim			FD_SET(port6, &readfds);
143238384Sjkim			maxfd = (port6 > maxfd) ? port6 : maxfd;
144238384Sjkim		}
145238384Sjkim#if 0
146238384Sjkim		if (0 <= wport4) {
147238384Sjkim			if (wport4 >= FD_SETSIZE)
148238384Sjkim				exit_failure("descriptor too big");
149238384Sjkim			FD_SET(wport4, &readfds);
150238384Sjkim			maxfd = (wport4 > maxfd) ? wport4 : maxfd;
151238384Sjkim		}
152238384Sjkim		if (0 <= wport6) {
153238384Sjkim			if (wport6 >= FD_SETSIZE)
154238384Sjkim				exit_failure("descriptor too big");
155238384Sjkim			FD_SET(wport6, &readfds);
156238384Sjkim			maxfd = (wport6 > maxfd) ? wport6 : maxfd;
157238384Sjkim		}
158238384Sjkim#endif
159238384Sjkim#endif
160238384Sjkim		tv.tv_sec = FAITH_TIMEOUT;
161238384Sjkim		tv.tv_usec = 0;
162238384Sjkim
163238384Sjkim#ifdef HAVE_POLL_H
164238384Sjkim		error = poll(pfd, sizeof(pfd)/sizeof(pfd[0]), tv.tv_sec * 1000);
165238384Sjkim#else
166238384Sjkim		error = select(maxfd + 1, &readfds, NULL, NULL, &tv);
167238384Sjkim#endif
168238384Sjkim		if (error == -1) {
169238384Sjkim#ifdef HAVE_POLL_H
170238384Sjkim			exit_failure("poll: %s", strerror(errno));
171238384Sjkim#else
172238384Sjkim			exit_failure("select: %s", strerror(errno));
173238384Sjkim#endif
174238384Sjkim		}
175238384Sjkim		else if (error == 0)
176238384Sjkim			exit_failure("connection timeout");
177238384Sjkim
178238384Sjkim		/*
179238384Sjkim		 * The order of the following checks does (slightly) matter.
180238384Sjkim		 * It is important to visit all checks (do not use "continue"),
181238384Sjkim		 * otherwise some of the pipe may become full and we cannot
182238384Sjkim		 * relay correctly.
183238384Sjkim		 */
184238384Sjkim#ifdef HAVE_POLL_H
185238384Sjkim		if (pfd[1].revents & POLLIN)
186238384Sjkim#else
187238384Sjkim		if (FD_ISSET(ctl6, &readfds))
188238384Sjkim#endif
189238384Sjkim		{
190238384Sjkim			/*
191238384Sjkim			 * copy control connection from the client.
192238384Sjkim			 * command translation is necessary.
193238384Sjkim			 */
194238384Sjkim			error = ftp_copycommand(ctl6, ctl4, &state);
195238384Sjkim
196238384Sjkim			if (error < 0)
197238384Sjkim				goto bad;
198238384Sjkim			else if (error == 0) {
199238384Sjkim				close(ctl4);
200238384Sjkim				close(ctl6);
201238384Sjkim				exit_success("terminating ftp control connection");
202238384Sjkim				/*NOTREACHED*/
203238384Sjkim			}
204238384Sjkim		}
205238384Sjkim#ifdef HAVE_POLL_H
206238384Sjkim		if (pfd[0].revents & POLLIN)
207238384Sjkim#else
208238384Sjkim		if (FD_ISSET(ctl4, &readfds))
209238384Sjkim#endif
210238384Sjkim		{
211238384Sjkim			/*
212238384Sjkim			 * copy control connection from the server
213238384Sjkim			 * translation of result code is necessary.
214238384Sjkim			 */
215238384Sjkim			error = ftp_copyresult(ctl4, ctl6, state);
216238384Sjkim
217238384Sjkim			if (error < 0)
218238384Sjkim				goto bad;
219238384Sjkim			else if (error == 0) {
220238384Sjkim				close(ctl4);
221238384Sjkim				close(ctl6);
222238384Sjkim				exit_success("terminating ftp control connection");
223238384Sjkim				/*NOTREACHED*/
224238384Sjkim			}
225238384Sjkim		}
226238384Sjkim#ifdef HAVE_POLL_H
227238384Sjkim		if (0 <= port4 && 0 <= port6 && (pfd[2].revents & POLLIN))
228238384Sjkim#else
229238384Sjkim		if (0 <= port4 && 0 <= port6 && FD_ISSET(port4, &readfds))
230238384Sjkim#endif
231238384Sjkim		{
232238384Sjkim			/*
233238384Sjkim			 * copy data connection.
234238384Sjkim			 * no special treatment necessary.
235238384Sjkim			 */
236238384Sjkim#ifdef HAVE_POLL_H
237238384Sjkim			if (pfd[2].revents & POLLIN)
238238384Sjkim#else
239238384Sjkim			if (FD_ISSET(port4, &readfds))
240238384Sjkim#endif
241238384Sjkim				error = ftp_copy(port4, port6);
242238384Sjkim			switch (error) {
243238384Sjkim			case -1:
244238384Sjkim				goto bad;
245238384Sjkim			case 0:
246238384Sjkim				close(port4);
247238384Sjkim				close(port6);
248238384Sjkim				port4 = port6 = -1;
249238384Sjkim				syslog(LOG_INFO, "terminating data connection");
250238384Sjkim				break;
251238384Sjkim			default:
252238384Sjkim				break;
253238384Sjkim			}
254238384Sjkim		}
255238384Sjkim#ifdef HAVE_POLL_H
256238384Sjkim		if (0 <= port4 && 0 <= port6 && (pfd[3].revents & POLLIN))
257238384Sjkim#else
258238384Sjkim		if (0 <= port4 && 0 <= port6 && FD_ISSET(port6, &readfds))
259238384Sjkim#endif
260238384Sjkim		{
261238384Sjkim			/*
262238384Sjkim			 * copy data connection.
263238384Sjkim			 * no special treatment necessary.
264238384Sjkim			 */
265238384Sjkim#ifdef HAVE_POLL_H
266238384Sjkim			if (pfd[3].revents & POLLIN)
267238384Sjkim#else
268238384Sjkim			if (FD_ISSET(port6, &readfds))
269238384Sjkim#endif
270238384Sjkim				error = ftp_copy(port6, port4);
271238384Sjkim			switch (error) {
272238384Sjkim			case -1:
273238384Sjkim				goto bad;
274238384Sjkim			case 0:
275238384Sjkim				close(port4);
276238384Sjkim				close(port6);
277238384Sjkim				port4 = port6 = -1;
278238384Sjkim				syslog(LOG_INFO, "terminating data connection");
279238384Sjkim				break;
280238384Sjkim			default:
281238384Sjkim				break;
282238384Sjkim			}
283238384Sjkim		}
284238384Sjkim#if 0
285238384Sjkim#ifdef HAVE_POLL_H
286238384Sjkim		if (wport4 && (pfd[4].revents & POLLIN))
287238384Sjkim#else
288238384Sjkim		if (wport4 && FD_ISSET(wport4, &readfds))
289238384Sjkim#endif
290238384Sjkim		{
291238384Sjkim			/*
292238384Sjkim			 * establish active data connection from the server.
293238384Sjkim			 */
294238384Sjkim			ftp_activeconn();
295238384Sjkim		}
296238384Sjkim#ifdef HAVE_POLL_H
297238384Sjkim		if (wport4 && (pfd[5].revents & POLLIN))
298238384Sjkim#else
299238384Sjkim		if (wport6 && FD_ISSET(wport6, &readfds))
300238384Sjkim#endif
301238384Sjkim		{
302238384Sjkim			/*
303238384Sjkim			 * establish passive data connection from the client.
304238384Sjkim			 */
305238384Sjkim			ftp_passiveconn();
306238384Sjkim		}
307238384Sjkim#endif
308238384Sjkim	}
309238384Sjkim
310238384Sjkim bad:
311238384Sjkim	exit_failure("%s", strerror(errno));
312238384Sjkim}
313238384Sjkim
314238384Sjkimstatic int
315238384Sjkimftp_activeconn()
316238384Sjkim{
317238384Sjkim	socklen_t n;
318238384Sjkim	int error;
319238384Sjkim#ifdef HAVE_POLL_H
320238384Sjkim	struct pollfd pfd[1];
321238384Sjkim#else
322238384Sjkim	fd_set set;
323238384Sjkim#endif
324238384Sjkim	struct timeval timeout;
325238384Sjkim	struct sockaddr *sa;
326238384Sjkim
327238384Sjkim	/* get active connection from server */
328238384Sjkim#ifdef HAVE_POLL_H
329238384Sjkim	pfd[0].fd = wport4;
330238384Sjkim	pfd[0].events = POLLIN;
331238384Sjkim#else
332238384Sjkim	FD_ZERO(&set);
333238384Sjkim	if (wport4 >= FD_SETSIZE)
334238384Sjkim		exit_failure("descriptor too big");
335238384Sjkim	FD_SET(wport4, &set);
336238384Sjkim#endif
337238384Sjkim	timeout.tv_sec = 120;
338238384Sjkim	timeout.tv_usec = 0;
339238384Sjkim	n = sizeof(data4);
340238384Sjkim#ifdef HAVE_POLL_H
341238384Sjkim	if (poll(pfd, sizeof(pfd)/sizeof(pfd[0]), timeout.tv_sec * 1000) == 0 ||
342238384Sjkim	    (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0)
343238384Sjkim#else
344238384Sjkim	if (select(wport4 + 1, &set, NULL, NULL, &timeout) == 0 ||
345238384Sjkim	    (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0)
346238384Sjkim#endif
347238384Sjkim	{
348238384Sjkim		close(wport4);
349238384Sjkim		wport4 = -1;
350238384Sjkim		syslog(LOG_INFO, "active mode data connection failed");
351238384Sjkim		return -1;
352238384Sjkim	}
353238384Sjkim
354238384Sjkim	/* ask active connection to client */
355238384Sjkim	sa = (struct sockaddr *)&data6;
356238384Sjkim	port6 = socket(sa->sa_family, SOCK_STREAM, 0);
357238384Sjkim	if (port6 == -1) {
358238384Sjkim		close(port4);
359238384Sjkim		close(wport4);
360238384Sjkim		port4 = wport4 = -1;
361238384Sjkim		syslog(LOG_INFO, "active mode data connection failed");
362238384Sjkim		return -1;
363238384Sjkim	}
364238384Sjkim	error = connect(port6, sa, sa->sa_len);
365238384Sjkim	if (error < 0) {
366238384Sjkim		close(port6);
367238384Sjkim		close(port4);
368238384Sjkim		close(wport4);
369238384Sjkim		port6 = port4 = wport4 = -1;
370238384Sjkim		syslog(LOG_INFO, "active mode data connection failed");
371238384Sjkim		return -1;
372238384Sjkim	}
373238384Sjkim
374238384Sjkim	syslog(LOG_INFO, "active mode data connection established");
375238384Sjkim	return 0;
376238384Sjkim}
377238384Sjkim
378238384Sjkimstatic int
379238384Sjkimftp_passiveconn()
380238384Sjkim{
381238384Sjkim	socklen_t len;
382238384Sjkim	int error;
383238384Sjkim#ifdef HAVE_POLL_H
384238384Sjkim	struct pollfd pfd[1];
385238384Sjkim#else
386238384Sjkim	fd_set set;
387238384Sjkim#endif
388238384Sjkim	struct timeval timeout;
389238384Sjkim	struct sockaddr *sa;
390238384Sjkim
391238384Sjkim	/* get passive connection from client */
392238384Sjkim#ifdef HAVE_POLL_H
393238384Sjkim	pfd[0].fd = wport6;
394238384Sjkim	pfd[0].events = POLLIN;
395238384Sjkim#else
396238384Sjkim	FD_ZERO(&set);
397238384Sjkim	if (wport6 >= FD_SETSIZE)
398238384Sjkim		exit_failure("descriptor too big");
399238384Sjkim	FD_SET(wport6, &set);
400238384Sjkim#endif
401238384Sjkim	timeout.tv_sec = 120;
402238384Sjkim	timeout.tv_usec = 0;
403238384Sjkim	len = sizeof(data6);
404238384Sjkim#ifdef HAVE_POLL_H
405238384Sjkim	if (poll(pfd, sizeof(pfd)/sizeof(pfd[0]), timeout.tv_sec * 1000) == 0 ||
406238384Sjkim	    (port6 = accept(wport6, (struct sockaddr *)&data6, &len)) < 0)
407238384Sjkim#else
408238384Sjkim	if (select(wport6 + 1, &set, NULL, NULL, &timeout) == 0 ||
409238384Sjkim	    (port6 = accept(wport6, (struct sockaddr *)&data6, &len)) < 0)
410238384Sjkim#endif
411238384Sjkim	{
412238384Sjkim		close(wport6);
413238384Sjkim		wport6 = -1;
414238384Sjkim		syslog(LOG_INFO, "passive mode data connection failed");
415238384Sjkim		return -1;
416238384Sjkim	}
417238384Sjkim
418238384Sjkim	/* ask passive connection to server */
419238384Sjkim	sa = (struct sockaddr *)&data4;
420238384Sjkim	port4 = socket(sa->sa_family, SOCK_STREAM, 0);
421238384Sjkim	if (port4 == -1) {
422238384Sjkim		close(wport6);
423238384Sjkim		close(port6);
424238384Sjkim		wport6 = port6 = -1;
425238384Sjkim		syslog(LOG_INFO, "passive mode data connection failed");
426238384Sjkim		return -1;
427238384Sjkim	}
428238384Sjkim	error = connect(port4, sa, sa->sa_len);
429238384Sjkim	if (error < 0) {
430238384Sjkim		close(wport6);
431238384Sjkim		close(port4);
432238384Sjkim		close(port6);
433238384Sjkim		wport6 = port4 = port6 = -1;
434238384Sjkim		syslog(LOG_INFO, "passive mode data connection failed");
435238384Sjkim		return -1;
436238384Sjkim	}
437238384Sjkim
438238384Sjkim	syslog(LOG_INFO, "passive mode data connection established");
439238384Sjkim	return 0;
440238384Sjkim}
441238384Sjkim
442238384Sjkimstatic int
443238384Sjkimftp_copy(int src, int dst)
444238384Sjkim{
445238384Sjkim	int error, atmark, n;
446238384Sjkim
447238384Sjkim	/* OOB data handling */
448238384Sjkim	error = ioctl(src, SIOCATMARK, &atmark);
449238384Sjkim	if (error != -1 && atmark == 1) {
450238384Sjkim		n = read(src, rbuf, 1);
451238384Sjkim		if (n == -1)
452238384Sjkim			goto bad;
453238384Sjkim		send(dst, rbuf, n, MSG_OOB);
454238384Sjkim#if 0
455238384Sjkim		n = read(src, rbuf, sizeof(rbuf));
456238384Sjkim		if (n == -1)
457238384Sjkim			goto bad;
458238384Sjkim		write(dst, rbuf, n);
459238384Sjkim		return n;
460238384Sjkim#endif
461238384Sjkim	}
462238384Sjkim
463238384Sjkim	n = read(src, rbuf, sizeof(rbuf));
464238384Sjkim	switch (n) {
465238384Sjkim	case -1:
466238384Sjkim	case 0:
467238384Sjkim		return n;
468238384Sjkim	default:
469238384Sjkim		write(dst, rbuf, n);
470238384Sjkim		return n;
471238384Sjkim	}
472238384Sjkim
473238384Sjkim bad:
474238384Sjkim	exit_failure("%s", strerror(errno));
475238384Sjkim	/*NOTREACHED*/
476238384Sjkim	return 0;	/* to make gcc happy */
477238384Sjkim}
478238384Sjkim
479238384Sjkimstatic int
480238384Sjkimftp_copyresult(int src, int dst, enum state state)
481238384Sjkim{
482238384Sjkim	int error, atmark, n;
483238384Sjkim	socklen_t len;
484238384Sjkim	char *param;
485238384Sjkim	int code;
486238384Sjkim	char *a, *p;
487238384Sjkim	int i;
488238384Sjkim
489238384Sjkim	/* OOB data handling */
490238384Sjkim	error = ioctl(src, SIOCATMARK, &atmark);
491238384Sjkim	if (error != -1 && atmark == 1) {
492238384Sjkim		n = read(src, rbuf, 1);
493238384Sjkim		if (n == -1)
494238384Sjkim			goto bad;
495238384Sjkim		send(dst, rbuf, n, MSG_OOB);
496238384Sjkim#if 0
497238384Sjkim		n = read(src, rbuf, sizeof(rbuf));
498238384Sjkim		if (n == -1)
499238384Sjkim			goto bad;
500238384Sjkim		write(dst, rbuf, n);
501238384Sjkim		return n;
502238384Sjkim#endif
503238384Sjkim	}
504238384Sjkim
505238384Sjkim	n = read(src, rbuf, sizeof(rbuf));
506238384Sjkim	if (n <= 0)
507238384Sjkim		return n;
508238384Sjkim	rbuf[n] = '\0';
509238384Sjkim
510238384Sjkim	/*
511238384Sjkim	 * parse argument
512238384Sjkim	 */
513238384Sjkim	p = rbuf;
514238384Sjkim	for (i = 0; i < 3; i++) {
515238384Sjkim		if (!isdigit(*p)) {
516238384Sjkim			/* invalid reply */
517238384Sjkim			write(dst, rbuf, n);
518238384Sjkim			return n;
519238384Sjkim		}
520238384Sjkim		p++;
521238384Sjkim	}
522238384Sjkim	if (!isspace(*p)) {
523238384Sjkim		/* invalid reply */
524238384Sjkim		write(dst, rbuf, n);
525238384Sjkim		return n;
526238384Sjkim	}
527238384Sjkim	code = atoi(rbuf);
528238384Sjkim	param = p;
529238384Sjkim	/* param points to first non-command token, if any */
530238384Sjkim	while (*param && isspace(*param))
531238384Sjkim		param++;
532238384Sjkim	if (!*param)
533238384Sjkim		param = NULL;
534238384Sjkim
535238384Sjkim	switch (state) {
536238384Sjkim	case NONE:
537238384Sjkim		if (!passivemode && rbuf[0] == '1') {
538238384Sjkim			if (ftp_activeconn() < 0) {
539238384Sjkim				n = snprintf(rbuf, sizeof(rbuf),
540238384Sjkim					"425 Cannot open data connetion\r\n");
541238384Sjkim				if (n < 0 || n >= sizeof(rbuf))
542238384Sjkim					n = 0;
543238384Sjkim			}
544238384Sjkim		}
545238384Sjkim		if (n)
546238384Sjkim			write(dst, rbuf, n);
547238384Sjkim		return n;
548238384Sjkim	case LPRT:
549238384Sjkim	case EPRT:
550238384Sjkim		/* expecting "200 PORT command successful." */
551238384Sjkim		if (code == 200) {
552238384Sjkim			p = strstr(rbuf, "PORT");
553238384Sjkim			if (p) {
554238384Sjkim				p[0] = (state == LPRT) ? 'L' : 'E';
555238384Sjkim				p[1] = 'P';
556238384Sjkim			}
557238384Sjkim		} else {
558238384Sjkim			close(wport4);
559238384Sjkim			wport4 = -1;
560238384Sjkim		}
561238384Sjkim		write(dst, rbuf, n);
562238384Sjkim		return n;
563238384Sjkim	case LPSV:
564238384Sjkim	case EPSV:
565238384Sjkim		/*
566238384Sjkim		 * expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)"
567238384Sjkim		 * (in some cases result comes without paren)
568238384Sjkim		 */
569238384Sjkim		if (code != 227) {
570238384Sjkimpassivefail0:
571238384Sjkim			close(wport6);
572238384Sjkim			wport6 = -1;
573238384Sjkim			write(dst, rbuf, n);
574238384Sjkim			return n;
575238384Sjkim		}
576238384Sjkim
577238384Sjkim	    {
578238384Sjkim		unsigned int ho[4], po[2];
579238384Sjkim		struct sockaddr_in *sin;
580238384Sjkim		struct sockaddr_in6 *sin6;
581238384Sjkim		u_short port;
582238384Sjkim
583238384Sjkim		/*
584238384Sjkim		 * PASV result -> LPSV/EPSV result
585238384Sjkim		 */
586238384Sjkim		p = param;
587238384Sjkim		while (*p && *p != '(' && !isdigit(*p))	/*)*/
588238384Sjkim			p++;
589238384Sjkim		if (!*p)
590238384Sjkim			goto passivefail0;	/*XXX*/
591238384Sjkim		if (*p == '(')	/*)*/
592238384Sjkim			p++;
593238384Sjkim		n = sscanf(p, "%u,%u,%u,%u,%u,%u",
594238384Sjkim			&ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
595238384Sjkim		if (n != 6)
596238384Sjkim			goto passivefail0;	/*XXX*/
597238384Sjkim
598238384Sjkim		/* keep PORT parameter */
599238384Sjkim		memset(&data4, 0, sizeof(data4));
600238384Sjkim		sin = (struct sockaddr_in *)&data4;
601238384Sjkim		sin->sin_len = sizeof(*sin);
602238384Sjkim		sin->sin_family = AF_INET;
603238384Sjkim		sin->sin_addr.s_addr = 0;
604238384Sjkim		for (n = 0; n < 4; n++) {
605238384Sjkim			sin->sin_addr.s_addr |=
606238384Sjkim				htonl((ho[n] & 0xff) << ((3 - n) * 8));
607238384Sjkim		}
608238384Sjkim		sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
609238384Sjkim
610238384Sjkim		/* get ready for passive data connection */
611238384Sjkim		memset(&data6, 0, sizeof(data6));
612238384Sjkim		sin6 = (struct sockaddr_in6 *)&data6;
613238384Sjkim		sin6->sin6_len = sizeof(*sin6);
614238384Sjkim		sin6->sin6_family = AF_INET6;
615238384Sjkim		wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0);
616238384Sjkim		if (wport6 == -1) {
617238384Sjkimpassivefail:
618238384Sjkim			n = snprintf(sbuf, sizeof(sbuf),
619238384Sjkim				"500 could not translate from PASV\r\n");
620238384Sjkim			if (n < 0 || n >= sizeof(sbuf))
621238384Sjkim				n = 0;
622238384Sjkim			if (n)
623238384Sjkim				write(src, sbuf, n);
624238384Sjkim			return n;
625238384Sjkim		}
626238384Sjkim#ifdef IPV6_FAITH
627238384Sjkim	    {
628238384Sjkim		int on = 1;
629238384Sjkim		error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH,
630238384Sjkim			&on, sizeof(on));
631238384Sjkim		if (error == -1)
632238384Sjkim			exit_failure("setsockopt(IPV6_FAITH): %s", strerror(errno));
633238384Sjkim	    }
634238384Sjkim#endif
635238384Sjkim		error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len);
636238384Sjkim		if (error == -1) {
637238384Sjkim			close(wport6);
638238384Sjkim			wport6 = -1;
639238384Sjkim			goto passivefail;
640238384Sjkim		}
641238384Sjkim		error = listen(wport6, 1);
642238384Sjkim		if (error == -1) {
643238384Sjkim			close(wport6);
644238384Sjkim			wport6 = -1;
645238384Sjkim			goto passivefail;
646238384Sjkim		}
647238384Sjkim
648238384Sjkim		/* transmit LPSV or EPSV */
649238384Sjkim		/*
650238384Sjkim		 * addr from dst, port from wport6
651238384Sjkim		 */
652238384Sjkim		len = sizeof(data6);
653238384Sjkim		error = getsockname(wport6, (struct sockaddr *)&data6, &len);
654238384Sjkim		if (error == -1) {
655238384Sjkim			close(wport6);
656238384Sjkim			wport6 = -1;
657238384Sjkim			goto passivefail;
658238384Sjkim		}
659238384Sjkim		sin6 = (struct sockaddr_in6 *)&data6;
660238384Sjkim		port = sin6->sin6_port;
661238384Sjkim
662238384Sjkim		len = sizeof(data6);
663238384Sjkim		error = getsockname(dst, (struct sockaddr *)&data6, &len);
664238384Sjkim		if (error == -1) {
665238384Sjkim			close(wport6);
666238384Sjkim			wport6 = -1;
667238384Sjkim			goto passivefail;
668238384Sjkim		}
669238384Sjkim		sin6 = (struct sockaddr_in6 *)&data6;
670238384Sjkim		sin6->sin6_port = port;
671238384Sjkim
672238384Sjkim		if (state == LPSV) {
673238384Sjkim			a = (char *)&sin6->sin6_addr;
674238384Sjkim			p = (char *)&sin6->sin6_port;
675238384Sjkim			n = snprintf(sbuf, sizeof(sbuf),
676238384Sjkim"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",
677238384Sjkim				6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
678238384Sjkim				UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
679238384Sjkim				UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
680238384Sjkim				UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
681238384Sjkim				2, UC(p[0]), UC(p[1]));
682238384Sjkim			if (n < 0 || n >= sizeof(sbuf))
683238384Sjkim				n = 0;
684238384Sjkim			if (n)
685238384Sjkim				write(dst, sbuf, n);
686238384Sjkim			passivemode = 1;
687238384Sjkim			return n;
688238384Sjkim		} else {
689238384Sjkim			n = snprintf(sbuf, sizeof(sbuf),
690238384Sjkim"229 Entering Extended Passive Mode (|||%d|)\r\n",
691238384Sjkim				ntohs(sin6->sin6_port));
692238384Sjkim			if (n < 0 || n >= sizeof(sbuf))
693238384Sjkim				n = 0;
694238384Sjkim			if (n)
695238384Sjkim				write(dst, sbuf, n);
696238384Sjkim			passivemode = 1;
697238384Sjkim			return n;
698238384Sjkim		}
699238384Sjkim	    }
700238384Sjkim	}
701238384Sjkim
702238384Sjkim bad:
703238384Sjkim	exit_failure("%s", strerror(errno));
704238384Sjkim	/*NOTREACHED*/
705238384Sjkim	return 0;	/* to make gcc happy */
706238384Sjkim}
707238384Sjkim
708238384Sjkimstatic int
709238384Sjkimftp_copycommand(int src, int dst, enum state *state)
710238384Sjkim{
711238384Sjkim	int error, atmark, n;
712238384Sjkim	socklen_t len;
713238384Sjkim	unsigned int af, hal, ho[16], pal, po[2];
714238384Sjkim	char *a, *p, *q;
715238384Sjkim	char cmd[5], *param;
716238384Sjkim	struct sockaddr_in *sin;
717238384Sjkim	struct sockaddr_in6 *sin6;
718238384Sjkim	enum state nstate;
719238384Sjkim	char ch;
720238384Sjkim	int i;
721238384Sjkim
722238384Sjkim	/* OOB data handling */
723238384Sjkim	error = ioctl(src, SIOCATMARK, &atmark);
724238384Sjkim	if (error != -1 && atmark == 1) {
725238384Sjkim		n = read(src, rbuf, 1);
726238384Sjkim		if (n == -1)
727238384Sjkim			goto bad;
728238384Sjkim		send(dst, rbuf, n, MSG_OOB);
729238384Sjkim#if 0
730238384Sjkim		n = read(src, rbuf, sizeof(rbuf));
731238384Sjkim		if (n == -1)
732238384Sjkim			goto bad;
733238384Sjkim		write(dst, rbuf, n);
734238384Sjkim		return n;
735238384Sjkim#endif
736238384Sjkim	}
737238384Sjkim
738238384Sjkim	n = read(src, rbuf, sizeof(rbuf));
739238384Sjkim	if (n <= 0)
740238384Sjkim		return n;
741238384Sjkim	rbuf[n] = '\0';
742238384Sjkim
743238384Sjkim	if (n < 4) {
744238384Sjkim		write(dst, rbuf, n);
745238384Sjkim		return n;
746238384Sjkim	}
747238384Sjkim
748238384Sjkim	/*
749238384Sjkim	 * parse argument
750238384Sjkim	 */
751238384Sjkim	p = rbuf;
752238384Sjkim	q = cmd;
753238384Sjkim	for (i = 0; i < 4; i++) {
754238384Sjkim		if (!isalpha(*p)) {
755238384Sjkim			/* invalid command */
756238384Sjkim			write(dst, rbuf, n);
757238384Sjkim			return n;
758238384Sjkim		}
759238384Sjkim		*q++ = islower(*p) ? toupper(*p) : *p;
760238384Sjkim		p++;
761238384Sjkim	}
762238384Sjkim	if (!isspace(*p)) {
763238384Sjkim		/* invalid command */
764238384Sjkim		write(dst, rbuf, n);
765238384Sjkim		return n;
766238384Sjkim	}
767238384Sjkim	*q = '\0';
768238384Sjkim	param = p;
769238384Sjkim	/* param points to first non-command token, if any */
770238384Sjkim	while (*param && isspace(*param))
771238384Sjkim		param++;
772238384Sjkim	if (!*param)
773238384Sjkim		param = NULL;
774238384Sjkim
775238384Sjkim	*state = NONE;
776238384Sjkim
777238384Sjkim	if (strcmp(cmd, "LPRT") == 0 && param) {
778238384Sjkim		/*
779238384Sjkim		 * LPRT -> PORT
780238384Sjkim		 */
781238384Sjkim		nstate = LPRT;
782238384Sjkim
783238384Sjkim		close(wport4);
784238384Sjkim		close(wport6);
785238384Sjkim		close(port4);
786238384Sjkim		close(port6);
787238384Sjkim		wport4 = wport6 = port4 = port6 = -1;
788238384Sjkim
789238384Sjkim		if (epsvall) {
790238384Sjkim			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
791238384Sjkim				cmd);
792238384Sjkim			if (n < 0 || n >= sizeof(sbuf))
793238384Sjkim				n = 0;
794238384Sjkim			if (n)
795238384Sjkim				write(src, sbuf, n);
796238384Sjkim			return n;
797238384Sjkim		}
798238384Sjkim
799238384Sjkim		n = sscanf(param,
800238384Sjkim"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u",
801238384Sjkim			      &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3],
802238384Sjkim			      &ho[4], &ho[5], &ho[6], &ho[7],
803238384Sjkim			      &ho[8], &ho[9], &ho[10], &ho[11],
804238384Sjkim			      &ho[12], &ho[13], &ho[14], &ho[15],
805238384Sjkim			      &pal, &po[0], &po[1]);
806238384Sjkim		if (n != 21 || af != 6 || hal != 16|| pal != 2) {
807238384Sjkim			n = snprintf(sbuf, sizeof(sbuf),
808238384Sjkim				"501 illegal parameter to LPRT\r\n");
809238384Sjkim			if (n < 0 || n >= sizeof(sbuf))
810238384Sjkim				n = 0;
811238384Sjkim			if (n)
812238384Sjkim				write(src, sbuf, n);
813238384Sjkim			return n;
814238384Sjkim		}
815238384Sjkim
816238384Sjkim		/* keep LPRT parameter */
817238384Sjkim		memset(&data6, 0, sizeof(data6));
818238384Sjkim		sin6 = (struct sockaddr_in6 *)&data6;
819238384Sjkim		sin6->sin6_len = sizeof(*sin6);
820238384Sjkim		sin6->sin6_family = AF_INET6;
821238384Sjkim		for (n = 0; n < 16; n++)
822238384Sjkim			sin6->sin6_addr.s6_addr[n] = ho[n];
823238384Sjkim		sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
824238384Sjkim
825238384Sjkimsendport:
826238384Sjkim		/* get ready for active data connection */
827238384Sjkim		len = sizeof(data4);
828238384Sjkim		error = getsockname(dst, (struct sockaddr *)&data4, &len);
829238384Sjkim		if (error == -1) {
830238384Sjkimlprtfail:
831238384Sjkim			n = snprintf(sbuf, sizeof(sbuf),
832238384Sjkim				"500 could not translate to PORT\r\n");
833238384Sjkim			if (n < 0 || n >= sizeof(sbuf))
834238384Sjkim				n = 0;
835238384Sjkim			if (n)
836238384Sjkim				write(src, sbuf, n);
837238384Sjkim			return n;
838238384Sjkim		}
839238384Sjkim		if (((struct sockaddr *)&data4)->sa_family != AF_INET)
840238384Sjkim			goto lprtfail;
841238384Sjkim		sin = (struct sockaddr_in *)&data4;
842238384Sjkim		sin->sin_port = 0;
843238384Sjkim		wport4 = socket(sin->sin_family, SOCK_STREAM, 0);
844238384Sjkim		if (wport4 == -1)
845238384Sjkim			goto lprtfail;
846238384Sjkim		error = bind(wport4, (struct sockaddr *)sin, sin->sin_len);
847238384Sjkim		if (error == -1) {
848238384Sjkim			close(wport4);
849238384Sjkim			wport4 = -1;
850238384Sjkim			goto lprtfail;
851238384Sjkim		}
852238384Sjkim		error = listen(wport4, 1);
853238384Sjkim		if (error == -1) {
854238384Sjkim			close(wport4);
855238384Sjkim			wport4 = -1;
856238384Sjkim			goto lprtfail;
857238384Sjkim		}
858238384Sjkim
859238384Sjkim		/* transmit PORT */
860238384Sjkim		len = sizeof(data4);
861238384Sjkim		error = getsockname(wport4, (struct sockaddr *)&data4, &len);
862238384Sjkim		if (error == -1) {
863238384Sjkim			close(wport4);
864238384Sjkim			wport4 = -1;
865238384Sjkim			goto lprtfail;
866238384Sjkim		}
867238384Sjkim		if (((struct sockaddr *)&data4)->sa_family != AF_INET) {
868238384Sjkim			close(wport4);
869238384Sjkim			wport4 = -1;
870238384Sjkim			goto lprtfail;
871238384Sjkim		}
872238384Sjkim		sin = (struct sockaddr_in *)&data4;
873238384Sjkim		a = (char *)&sin->sin_addr;
874238384Sjkim		p = (char *)&sin->sin_port;
875238384Sjkim		n = snprintf(sbuf, sizeof(sbuf), "PORT %d,%d,%d,%d,%d,%d\r\n",
876238384Sjkim				  UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
877238384Sjkim				  UC(p[0]), UC(p[1]));
878238384Sjkim		if (n < 0 || n >= sizeof(sbuf))
879238384Sjkim			n = 0;
880238384Sjkim		if (n)
881238384Sjkim			write(dst, sbuf, n);
882238384Sjkim		*state = nstate;
883238384Sjkim		passivemode = 0;
884238384Sjkim		return n;
885238384Sjkim	} else if (strcmp(cmd, "EPRT") == 0 && param) {
886238384Sjkim		/*
887238384Sjkim		 * EPRT -> PORT
888238384Sjkim		 */
889238384Sjkim		char *afp, *hostp, *portp;
890238384Sjkim		struct addrinfo hints, *res;
891238384Sjkim
892238384Sjkim		nstate = EPRT;
893238384Sjkim
894238384Sjkim		close(wport4);
895238384Sjkim		close(wport6);
896238384Sjkim		close(port4);
897238384Sjkim		close(port6);
898238384Sjkim		wport4 = wport6 = port4 = port6 = -1;
899238384Sjkim
900238384Sjkim		if (epsvall) {
901238384Sjkim			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
902238384Sjkim				cmd);
903238384Sjkim			if (n < 0 || n >= sizeof(sbuf))
904279264Sdelphij				n = 0;
905279264Sdelphij			if (n)
906238384Sjkim				write(src, sbuf, n);
907238384Sjkim			return n;
908238384Sjkim		}
909238384Sjkim
910238384Sjkim		p = param;
911238384Sjkim		ch = *p++;	/* boundary character */
912238384Sjkim		afp = p;
913238384Sjkim		while (*p && *p != ch)
914238384Sjkim			p++;
915238384Sjkim		if (!*p) {
916238384Sjkimeprtparamfail:
917238384Sjkim			n = snprintf(sbuf, sizeof(sbuf),
918238384Sjkim				"501 illegal parameter to EPRT\r\n");
919238384Sjkim			if (n < 0 || n >= sizeof(sbuf))
920238384Sjkim				n = 0;
921238384Sjkim			if (n)
922238384Sjkim				write(src, sbuf, n);
923238384Sjkim			return n;
924238384Sjkim		}
925238384Sjkim		*p++ = '\0';
926238384Sjkim		hostp = p;
927238384Sjkim		while (*p && *p != ch)
928238384Sjkim			p++;
929238384Sjkim		if (!*p)
930238384Sjkim			goto eprtparamfail;
931238384Sjkim		*p++ = '\0';
932238384Sjkim		portp = p;
933238384Sjkim		while (*p && *p != ch)
934238384Sjkim			p++;
935238384Sjkim		if (!*p)
936238384Sjkim			goto eprtparamfail;
937238384Sjkim		*p++ = '\0';
938238384Sjkim
939238384Sjkim		n = sscanf(afp, "%d", &af);
940238384Sjkim		if (n != 1 || af != 2) {
941238384Sjkim			n = snprintf(sbuf, sizeof(sbuf),
942238384Sjkim				"501 unsupported address family to EPRT\r\n");
943238384Sjkim			if (n < 0 || n >= sizeof(sbuf))
944238384Sjkim				n = 0;
945238384Sjkim			if (n)
946238384Sjkim				write(src, sbuf, n);
947238384Sjkim			return n;
948238384Sjkim		}
949238384Sjkim		memset(&hints, 0, sizeof(hints));
950238384Sjkim		hints.ai_family = AF_UNSPEC;
951238384Sjkim		hints.ai_socktype = SOCK_STREAM;
952238384Sjkim		hints.ai_protocol = IPPROTO_TCP;
953238384Sjkim		error = getaddrinfo(hostp, portp, &hints, &res);
954238384Sjkim		if (error) {
955238384Sjkim			n = snprintf(sbuf, sizeof(sbuf),
956238384Sjkim				"501 EPRT: %s\r\n", gai_strerror(error));
957238384Sjkim			if (n < 0 || n >= sizeof(sbuf))
958238384Sjkim				n = 0;
959238384Sjkim			if (n)
960238384Sjkim				write(src, sbuf, n);
961238384Sjkim			return n;
962238384Sjkim		}
963238384Sjkim		if (res->ai_next) {
964238384Sjkim			n = snprintf(sbuf, sizeof(sbuf),
965238384Sjkim				"501 EPRT: %s resolved to multiple addresses\r\n", hostp);
966238384Sjkim			if (n < 0 || n >= sizeof(sbuf))
967238384Sjkim				n = 0;
968238384Sjkim			if (n)
969238384Sjkim				write(src, sbuf, n);
970238384Sjkim			freeaddrinfo(res);
971238384Sjkim			return n;
972238384Sjkim		}
973238384Sjkim
974238384Sjkim		freeaddrinfo(res);
975238384Sjkim		memcpy(&data6, res->ai_addr, res->ai_addrlen);
976238384Sjkim
977238384Sjkim		goto sendport;
978238384Sjkim	} else if (strcmp(cmd, "LPSV") == 0 && !param) {
979238384Sjkim		/*
980238384Sjkim		 * LPSV -> PASV
981238384Sjkim		 */
982238384Sjkim		nstate = LPSV;
983238384Sjkim
984238384Sjkim		close(wport4);
985238384Sjkim		close(wport6);
986238384Sjkim		close(port4);
987238384Sjkim		close(port6);
988238384Sjkim		wport4 = wport6 = port4 = port6 = -1;
989238384Sjkim
990238384Sjkim		if (epsvall) {
991238384Sjkim			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
992238384Sjkim				cmd);
993238384Sjkim			if (n < 0 || n >= sizeof(sbuf))
994238384Sjkim				n = 0;
995238384Sjkim			if (n)
996238384Sjkim				write(src, sbuf, n);
997238384Sjkim			return n;
998238384Sjkim		}
999238384Sjkim
1000238384Sjkim		/* transmit PASV */
1001238384Sjkim		n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
1002238384Sjkim		if (n < 0 || n >= sizeof(sbuf))
1003238384Sjkim			n = 0;
1004238384Sjkim		if (n)
1005238384Sjkim			write(dst, sbuf, n);
1006238384Sjkim		*state = LPSV;
1007238384Sjkim		passivemode = 0;	/* to be set to 1 later */
1008238384Sjkim		return n;
1009238384Sjkim	} else if (strcmp(cmd, "EPSV") == 0 && !param) {
1010238384Sjkim		/*
1011238384Sjkim		 * EPSV -> PASV
1012238384Sjkim		 */
1013238384Sjkim		close(wport4);
1014238384Sjkim		close(wport6);
1015238384Sjkim		close(port4);
1016238384Sjkim		close(port6);
1017238384Sjkim		wport4 = wport6 = port4 = port6 = -1;
1018238384Sjkim
1019238384Sjkim		n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
1020238384Sjkim		if (n < 0 || n >= sizeof(sbuf))
1021238384Sjkim			n = 0;
1022238384Sjkim		if (n)
1023238384Sjkim			write(dst, sbuf, n);
1024238384Sjkim		*state = EPSV;
1025238384Sjkim		passivemode = 0;	/* to be set to 1 later */
1026238384Sjkim		return n;
1027238384Sjkim	} else if (strcmp(cmd, "EPSV") == 0 && param
1028238384Sjkim	 && strncasecmp(param, "ALL", 3) == 0 && isspace(param[3])) {
1029238384Sjkim		/*
1030238384Sjkim		 * EPSV ALL
1031238384Sjkim		 */
1032238384Sjkim		epsvall = 1;
1033238384Sjkim		n = snprintf(sbuf, sizeof(sbuf), "200 EPSV ALL command successful.\r\n");
1034238384Sjkim		if (n < 0 || n >= sizeof(sbuf))
1035238384Sjkim			n = 0;
1036238384Sjkim		if (n)
1037238384Sjkim			write(src, sbuf, n);
1038238384Sjkim		return n;
1039238384Sjkim	} else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) {
1040238384Sjkim		/*
1041238384Sjkim		 * reject PORT/PASV
1042238384Sjkim		 */
1043238384Sjkim		n = snprintf(sbuf, sizeof(sbuf), "502 %s not implemented.\r\n", cmd);
1044238384Sjkim		if (n < 0 || n >= sizeof(sbuf))
1045238384Sjkim			n = 0;
1046238384Sjkim		if (n)
1047238384Sjkim			write(src, sbuf, n);
1048238384Sjkim		return n;
1049238384Sjkim	} else if (passivemode
1050238384Sjkim		&& (strcmp(cmd, "STOR") == 0
1051238384Sjkim		 || strcmp(cmd, "STOU") == 0
1052238384Sjkim		 || strcmp(cmd, "RETR") == 0
1053238384Sjkim		 || strcmp(cmd, "LIST") == 0
1054238384Sjkim		 || strcmp(cmd, "NLST") == 0
1055238384Sjkim		 || strcmp(cmd, "APPE") == 0)) {
1056238384Sjkim		/*
1057238384Sjkim		 * commands with data transfer.  need to care about passive
1058238384Sjkim		 * mode data connection.
1059238384Sjkim		 */
1060238384Sjkim
1061238384Sjkim		if (ftp_passiveconn() < 0) {
1062238384Sjkim			n = snprintf(sbuf, sizeof(sbuf), "425 Cannot open data connetion\r\n");
1063238384Sjkim			if (n < 0 || n >= sizeof(sbuf))
1064238384Sjkim				n = 0;
1065238384Sjkim			if (n)
1066238384Sjkim				write(src, sbuf, n);
1067238384Sjkim		} else {
1068238384Sjkim			/* simply relay the command */
1069238384Sjkim			write(dst, rbuf, n);
1070238384Sjkim		}
1071238384Sjkim
1072		*state = NONE;
1073		return n;
1074	} else {
1075		/* simply relay it */
1076		*state = NONE;
1077		write(dst, rbuf, n);
1078		return n;
1079	}
1080
1081 bad:
1082	exit_failure("%s", strerror(errno));
1083	/*NOTREACHED*/
1084	return 0;	/* to make gcc happy */
1085}
1086