1/*	$NetBSD: ftp.c,v 1.20 2024/02/09 20:55:15 andvar Exp $	*/
2/*	$KAME: ftp.c,v 1.23 2003/08/19 21:20:33 itojun Exp $	*/
3
4/*
5 * Copyright (C) 1997 and 1998 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include <sys/param.h>
34#include <sys/types.h>
35#include <sys/socket.h>
36#include <sys/ioctl.h>
37#include <sys/time.h>
38
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <syslog.h>
43#include <unistd.h>
44#include <poll.h>
45#include <errno.h>
46#include <ctype.h>
47
48#include <netinet/in.h>
49#include <arpa/inet.h>
50#include <netdb.h>
51
52#include "faithd.h"
53
54static char rbuf[MSS];
55static int passivemode = 0;
56static int wport4 = -1;			/* listen() to active */
57static int wport6 = -1;			/* listen() to passive */
58static int port4 = -1;			/* active: inbound  passive: outbound */
59static int port6 = -1;			/* active: outbound  passive: inbound */
60static struct sockaddr_storage data4;	/* server data address */
61static struct sockaddr_storage data6;	/* client data address */
62static int epsvall = 0;
63
64enum state { NONE, LPRT, EPRT, LPSV, EPSV };
65
66static int ftp_activeconn(void);
67static int ftp_passiveconn(void);
68static ssize_t ftp_copy(int, int);
69static ssize_t ftp_copyresult(int, int, enum state);
70static ssize_t ftp_copycommand(int, int, enum state *);
71
72void
73ftp_relay(int ctl6, int ctl4)
74{
75	struct pollfd pfd[6];
76	ssize_t error;
77	enum state state = NONE;
78
79	syslog(LOG_INFO, "starting ftp control connection");
80
81	for (;;) {
82		pfd[0].fd = ctl4;
83		pfd[0].events = POLLIN;
84		pfd[1].fd = ctl6;
85		pfd[1].events = POLLIN;
86		if (0 <= port4) {
87			pfd[2].fd = port4;
88			pfd[2].events = POLLIN;
89		} else
90			pfd[2].fd = -1;
91		if (0 <= port6) {
92			pfd[3].fd = port6;
93			pfd[3].events = POLLIN;
94		} else
95			pfd[3].fd = -1;
96#if 0
97		if (0 <= wport4) {
98			pfd[4].fd = wport4;
99			pfd[4].events = POLLIN;
100		} else
101			pfd[4].fd = -1;
102		if (0 <= wport6) {
103			pfd[5].fd = wport4;
104			pfd[5].events = POLLIN;
105		} else
106			pfd[5].fd = -1;
107#else
108		pfd[4].fd = pfd[5].fd = -1;
109		pfd[4].events = pfd[5].events = 0;
110#endif
111		error = poll(pfd, (unsigned int)(sizeof(pfd) / sizeof(pfd[0])),
112		    FAITH_TIMEOUT * 1000);
113		if (error == -1) {
114			exit_failure("poll: %s", strerror(errno));
115		}
116		else if (error == 0)
117			exit_failure("connection timeout");
118
119		/*
120		 * The order of the following checks does (slightly) matter.
121		 * It is important to visit all checks (do not use "continue"),
122		 * otherwise some of the pipe may become full and we cannot
123		 * relay correctly.
124		 */
125		if (pfd[1].revents & POLLIN)
126		{
127			/*
128			 * copy control connection from the client.
129			 * command translation is necessary.
130			 */
131			error = ftp_copycommand(ctl6, ctl4, &state);
132
133			if (error < 0)
134				goto bad;
135			else if (error == 0) {
136				(void)close(ctl4);
137				(void)close(ctl6);
138				exit_success("terminating ftp control connection");
139				/*NOTREACHED*/
140			}
141		}
142		if (pfd[0].revents & POLLIN)
143		{
144			/*
145			 * copy control connection from the server
146			 * translation of result code is necessary.
147			 */
148			error = ftp_copyresult(ctl4, ctl6, state);
149
150			if (error < 0)
151				goto bad;
152			else if (error == 0) {
153				(void)close(ctl4);
154				(void)close(ctl6);
155				exit_success("terminating ftp control connection");
156				/*NOTREACHED*/
157			}
158		}
159		if (0 <= port4 && 0 <= port6 && (pfd[2].revents & POLLIN))
160		{
161			/*
162			 * copy data connection.
163			 * no special treatment necessary.
164			 */
165			if (pfd[2].revents & POLLIN)
166				error = ftp_copy(port4, port6);
167			switch (error) {
168			case -1:
169				goto bad;
170			case 0:
171				if (port4 >= 0) {
172					(void)close(port4);
173					port4 = -1;
174				}
175				if (port6 >= 0) {
176					(void)close(port6);
177					port6 = -1;
178				}
179				syslog(LOG_INFO, "terminating data connection");
180				break;
181			default:
182				break;
183			}
184		}
185		if (0 <= port4 && 0 <= port6 && (pfd[3].revents & POLLIN))
186		{
187			/*
188			 * copy data connection.
189			 * no special treatment necessary.
190			 */
191			if (pfd[3].revents & POLLIN)
192				error = ftp_copy(port6, port4);
193			switch (error) {
194			case -1:
195				goto bad;
196			case 0:
197				if (port4 >= 0) {
198					(void)close(port4);
199					port4 = -1;
200				}
201				if (port6 >= 0) {
202					(void)close(port6);
203					port6 = -1;
204				}
205				syslog(LOG_INFO, "terminating data connection");
206				break;
207			default:
208				break;
209			}
210		}
211#if 0
212		if (wport4 && (pfd[4].revents & POLLIN))
213		{
214			/*
215			 * establish active data connection from the server.
216			 */
217			ftp_activeconn();
218		}
219		if (wport4 && (pfd[5].revents & POLLIN))
220		{
221			/*
222			 * establish passive data connection from the client.
223			 */
224			ftp_passiveconn();
225		}
226#endif
227	}
228
229 bad:
230	exit_failure("%s", strerror(errno));
231}
232
233static int
234ftp_activeconn()
235{
236	socklen_t n;
237	int error;
238	struct pollfd pfd[1];
239	struct sockaddr *sa;
240
241	/* get active connection from server */
242	pfd[0].fd = wport4;
243	pfd[0].events = POLLIN;
244	n = sizeof(data4);
245	if (poll(pfd, (unsigned int)(sizeof(pfd) / sizeof(pfd[0])),
246	    120000) == 0 ||
247	    (port4 = accept(wport4, (void *)&data4, &n)) < 0)
248	{
249		(void)close(wport4);
250		wport4 = -1;
251		syslog(LOG_INFO, "active mode data connection failed");
252		return -1;
253	}
254
255	/* ask active connection to client */
256	sa = (void *)&data6;
257	port6 = socket(sa->sa_family, SOCK_STREAM, 0);
258	if (port6 == -1) {
259		(void)close(port4);
260		(void)close(wport4);
261		port4 = wport4 = -1;
262		syslog(LOG_INFO, "active mode data connection failed");
263		return -1;
264	}
265	error = connect(port6, sa, (socklen_t)sa->sa_len);
266	if (error < 0) {
267		(void)close(port6);
268		(void)close(port4);
269		(void)close(wport4);
270		port6 = port4 = wport4 = -1;
271		syslog(LOG_INFO, "active mode data connection failed");
272		return -1;
273	}
274
275	syslog(LOG_INFO, "active mode data connection established");
276	return 0;
277}
278
279static int
280ftp_passiveconn()
281{
282	socklen_t len;
283	int error;
284	struct pollfd pfd[1];
285	struct sockaddr *sa;
286
287	/* get passive connection from client */
288	pfd[0].fd = wport6;
289	pfd[0].events = POLLIN;
290	len = sizeof(data6);
291	if (poll(pfd, (unsigned int)(sizeof(pfd) / sizeof(pfd[0])),
292	    120000) == 0 ||
293	    (port6 = accept(wport6, (void *)&data6, &len)) < 0)
294	{
295		(void)close(wport6);
296		wport6 = -1;
297		syslog(LOG_INFO, "passive mode data connection failed");
298		return -1;
299	}
300
301	/* ask passive connection to server */
302	sa = (void *)&data4;
303	port4 = socket(sa->sa_family, SOCK_STREAM, 0);
304	if (port4 == -1) {
305		(void)close(wport6);
306		(void)close(port6);
307		wport6 = port6 = -1;
308		syslog(LOG_INFO, "passive mode data connection failed");
309		return -1;
310	}
311	error = connect(port4, sa, (socklen_t)sa->sa_len);
312	if (error < 0) {
313		(void)close(wport6);
314		(void)close(port4);
315		(void)close(port6);
316		wport6 = port4 = port6 = -1;
317		syslog(LOG_INFO, "passive mode data connection failed");
318		return -1;
319	}
320
321	syslog(LOG_INFO, "passive mode data connection established");
322	return 0;
323}
324
325static ssize_t
326ftp_copy(int src, int dst)
327{
328	int error, atmark;
329	ssize_t n;
330
331	/* OOB data handling */
332	error = ioctl(src, SIOCATMARK, &atmark);
333	if (error != -1 && atmark == 1) {
334		n = read(src, rbuf, 1);
335		if (n == -1)
336			goto bad;
337		(void)send(dst, rbuf, (size_t)n, MSG_OOB);
338#if 0
339		n = read(src, rbuf, sizeof(rbuf));
340		if (n == -1)
341			goto bad;
342		(void)write(dst, rbuf, (size_t)n);
343		return n;
344#endif
345	}
346
347	n = read(src, rbuf, sizeof(rbuf));
348	switch (n) {
349	case -1:
350	case 0:
351		return n;
352	default:
353		(void)write(dst, rbuf, (size_t)n);
354		return n;
355	}
356
357 bad:
358	exit_failure("%s", strerror(errno));
359	/*NOTREACHED*/
360	return 0;	/* to make gcc happy */
361}
362
363static ssize_t
364ftp_copyresult(int src, int dst, enum state state)
365{
366	int error, atmark;
367	ssize_t n;
368	socklen_t len;
369	char *param;
370	int code;
371	char *a, *p;
372	int i;
373
374	/* OOB data handling */
375	error = ioctl(src, SIOCATMARK, &atmark);
376	if (error != -1 && atmark == 1) {
377		n = read(src, rbuf, 1);
378		if (n == -1)
379			goto bad;
380		(void)send(dst, rbuf, (size_t)n, MSG_OOB);
381#if 0
382		n = read(src, rbuf, sizeof(rbuf));
383		if (n == -1)
384			goto bad;
385		(void)write(dst, rbuf, (size_t)n);
386		return n;
387#endif
388	}
389
390	n = read(src, rbuf, sizeof(rbuf));
391	if (n <= 0)
392		return n;
393	rbuf[n] = '\0';
394
395	/*
396	 * parse argument
397	 */
398	p = rbuf;
399	for (i = 0; i < 3; i++) {
400		if (!isdigit((unsigned char)*p)) {
401			/* invalid reply */
402			(void)write(dst, rbuf, (size_t)n);
403			return n;
404		}
405		p++;
406	}
407	if (!isspace((unsigned char)*p)) {
408		/* invalid reply */
409		(void)write(dst, rbuf, (size_t)n);
410		return n;
411	}
412	code = atoi(rbuf);
413	param = p;
414	/* param points to first non-command token, if any */
415	while (*param && isspace((unsigned char)*param))
416		param++;
417	if (!*param)
418		param = NULL;
419
420	switch (state) {
421	case NONE:
422		if (!passivemode && rbuf[0] == '1') {
423			if (ftp_activeconn() < 0) {
424				n = snprintf(rbuf, sizeof(rbuf),
425				    "425 Cannot open data connection\r\n");
426				if (n < 0 || n >= (int)sizeof(rbuf))
427					n = 0;
428			}
429		}
430		if (n)
431			(void)write(dst, rbuf, (size_t)n);
432		return n;
433	case LPRT:
434	case EPRT:
435		/* expecting "200 PORT command successful." */
436		if (code == 200) {
437			p = strstr(rbuf, "PORT");
438			if (p) {
439				p[0] = (state == LPRT) ? 'L' : 'E';
440				p[1] = 'P';
441			}
442		} else {
443			(void)close(wport4);
444			wport4 = -1;
445		}
446		(void)write(dst, rbuf, (size_t)n);
447		return n;
448	case LPSV:
449	case EPSV:
450		/*
451		 * expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)"
452		 * (in some cases result comes without paren)
453		 */
454		if (code != 227) {
455passivefail0:
456			(void)close(wport6);
457			wport6 = -1;
458			(void)write(dst, rbuf, (size_t)n);
459			return n;
460		}
461
462	    {
463		unsigned int ho[4], po[2];
464		struct sockaddr_in *sin;
465		struct sockaddr_in6 *sin6;
466		u_short port;
467
468		/*
469		 * PASV result -> LPSV/EPSV result
470		 */
471		p = param;
472		while (*p && *p != '(' && !isdigit((unsigned char)*p))	/*)*/
473			p++;
474		if (!*p)
475			goto passivefail0;	/*XXX*/
476		if (*p == '(')	/*)*/
477			p++;
478		n = sscanf(p, "%u,%u,%u,%u,%u,%u",
479			&ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
480		if (n != 6)
481			goto passivefail0;	/*XXX*/
482
483		/* keep PORT parameter */
484		memset(&data4, 0, sizeof(data4));
485		sin = (void *)&data4;
486		sin->sin_len = sizeof(*sin);
487		sin->sin_family = AF_INET;
488		sin->sin_addr.s_addr = 0;
489		for (n = 0; n < 4; n++) {
490			sin->sin_addr.s_addr |= htonl(((uint32_t)(ho[n] & 0xff)
491			    << (int)((3 - n) * 8)));
492		}
493		sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
494
495		/* get ready for passive data connection */
496		memset(&data6, 0, sizeof(data6));
497		sin6 = (void *)&data6;
498		sin6->sin6_len = sizeof(*sin6);
499		sin6->sin6_family = AF_INET6;
500		wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0);
501		if (wport6 == -1) {
502passivefail:
503			return dprintf(src,
504			    "500 could not translate from PASV\r\n");
505		}
506#ifdef IPV6_FAITH
507	    {
508		int on = 1;
509		error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH,
510			&on, (socklen_t)sizeof(on));
511		if (error == -1)
512			exit_failure("setsockopt(IPV6_FAITH): %s", strerror(errno));
513	    }
514#endif
515		error = bind(wport6, (void *)sin6, (socklen_t)sin6->sin6_len);
516		if (error == -1) {
517			(void)close(wport6);
518			wport6 = -1;
519			goto passivefail;
520		}
521		error = listen(wport6, 1);
522		if (error == -1) {
523			(void)close(wport6);
524			wport6 = -1;
525			goto passivefail;
526		}
527
528		/* transmit LPSV or EPSV */
529		/*
530		 * addr from dst, port from wport6
531		 */
532		len = sizeof(data6);
533		error = getsockname(wport6, (void *)&data6, &len);
534		if (error == -1) {
535			(void)close(wport6);
536			wport6 = -1;
537			goto passivefail;
538		}
539		sin6 = (void *)&data6;
540		port = sin6->sin6_port;
541
542		len = sizeof(data6);
543		error = getsockname(dst, (void *)&data6, &len);
544		if (error == -1) {
545			(void)close(wport6);
546			wport6 = -1;
547			goto passivefail;
548		}
549		sin6 = (void *)&data6;
550		sin6->sin6_port = port;
551
552		if (state == LPSV) {
553			a = (void *)&sin6->sin6_addr;
554			p = (void *)&sin6->sin6_port;
555			passivemode = 1;
556			return dprintf(dst,
557			    "228 Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,"
558			    "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)\r\n",
559			    6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
560			    UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
561			    UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
562			    UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
563			    2, UC(p[0]), UC(p[1]));
564		} else {
565			passivemode = 1;
566			return dprintf(dst,
567			    "229 Entering Extended Passive Mode (|||%d|)\r\n",
568			    ntohs(sin6->sin6_port));
569		}
570	    }
571	}
572
573 bad:
574	exit_failure("%s", strerror(errno));
575	/*NOTREACHED*/
576	return 0;	/* to make gcc happy */
577}
578
579static ssize_t
580ftp_copycommand(int src, int dst, enum state *state)
581{
582	int error, atmark;
583	ssize_t n;
584	socklen_t len;
585	unsigned int af, hal, ho[16], pal, po[2];
586	char *a, *p, *q;
587	char cmd[5], *param;
588	struct sockaddr_in *sin;
589	struct sockaddr_in6 *sin6;
590	enum state nstate;
591	char ch;
592	int i;
593
594	/* OOB data handling */
595	error = ioctl(src, SIOCATMARK, &atmark);
596	if (error != -1 && atmark == 1) {
597		n = read(src, rbuf, 1);
598		if (n == -1)
599			goto bad;
600		(void)send(dst, rbuf, (size_t)n, MSG_OOB);
601#if 0
602		n = read(src, rbuf, sizeof(rbuf));
603		if (n == -1)
604			goto bad;
605		(void)write(dst, rbuf, (size_t)n);
606		return n;
607#endif
608	}
609
610	n = read(src, rbuf, sizeof(rbuf));
611	if (n <= 0)
612		return n;
613	rbuf[n] = '\0';
614
615	if (n < 4) {
616		(void)write(dst, rbuf, (size_t)n);
617		return n;
618	}
619
620	/*
621	 * parse argument
622	 */
623	p = rbuf;
624	q = cmd;
625	for (i = 0; i < 4; i++) {
626		if (!isalpha((unsigned char)*p)) {
627			/* invalid command */
628			(void)write(dst, rbuf, (size_t)n);
629			return n;
630		}
631		*q++ = islower((unsigned char)*p) ? toupper((unsigned char)*p) : *p;
632		p++;
633	}
634	if (!isspace((unsigned char)*p)) {
635		/* invalid command */
636		(void)write(dst, rbuf, (size_t)n);
637		return n;
638	}
639	*q = '\0';
640	param = p;
641	/* param points to first non-command token, if any */
642	while (*param && isspace((unsigned char)*param))
643		param++;
644	if (!*param)
645		param = NULL;
646
647	*state = NONE;
648
649	if (strcmp(cmd, "LPRT") == 0 && param) {
650		/*
651		 * LPRT -> PORT
652		 */
653		nstate = LPRT;
654
655		(void)close(wport4);
656		(void)close(wport6);
657		(void)close(port4);
658		(void)close(port6);
659		wport4 = wport6 = port4 = port6 = -1;
660
661		if (epsvall) {
662			return dprintf(src, "501 %s disallowed in EPSV ALL\r\n",
663			    cmd);
664		}
665
666		n = sscanf(param,
667		    "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,"
668		    "%u,%u,%u", &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3],
669		    &ho[4], &ho[5], &ho[6], &ho[7],
670		    &ho[8], &ho[9], &ho[10], &ho[11],
671		    &ho[12], &ho[13], &ho[14], &ho[15],
672		    &pal, &po[0], &po[1]);
673		if (n != 21 || af != 6 || hal != 16|| pal != 2) {
674			return dprintf(src,
675			    "501 illegal parameter to LPRT\r\n");
676		}
677
678		/* keep LPRT parameter */
679		memset(&data6, 0, sizeof(data6));
680		sin6 = (void *)&data6;
681		sin6->sin6_len = sizeof(*sin6);
682		sin6->sin6_family = AF_INET6;
683		for (n = 0; n < 16; n++)
684			sin6->sin6_addr.s6_addr[n] = ho[n];
685		sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
686
687sendport:
688		/* get ready for active data connection */
689		len = sizeof(data4);
690		error = getsockname(dst, (void *)&data4, &len);
691		if (error == -1) {
692lprtfail:
693			return dprintf(src,
694			    "500 could not translate to PORT\r\n");
695		}
696		if (((struct sockaddr *)(void *)&data4)->sa_family != AF_INET)
697			goto lprtfail;
698		sin = (void *)&data4;
699		sin->sin_port = 0;
700		wport4 = socket(sin->sin_family, SOCK_STREAM, 0);
701		if (wport4 == -1)
702			goto lprtfail;
703		error = bind(wport4, (void *)sin, (socklen_t)sin->sin_len);
704		if (error == -1) {
705			(void)close(wport4);
706			wport4 = -1;
707			goto lprtfail;
708		}
709		error = listen(wport4, 1);
710		if (error == -1) {
711			(void)close(wport4);
712			wport4 = -1;
713			goto lprtfail;
714		}
715
716		/* transmit PORT */
717		len = sizeof(data4);
718		error = getsockname(wport4, (void *)&data4, &len);
719		if (error == -1) {
720			(void)close(wport4);
721			wport4 = -1;
722			goto lprtfail;
723		}
724		if (((struct sockaddr *)(void *)&data4)->sa_family != AF_INET) {
725			(void)close(wport4);
726			wport4 = -1;
727			goto lprtfail;
728		}
729		sin = (void *)&data4;
730		a = (void *)&sin->sin_addr;
731		p = (void *)&sin->sin_port;
732		*state = nstate;
733		passivemode = 0;
734		return dprintf(dst, "PORT %d,%d,%d,%d,%d,%d\r\n",
735		    UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
736	} else if (strcmp(cmd, "EPRT") == 0 && param) {
737		/*
738		 * EPRT -> PORT
739		 */
740		char *afp, *hostp, *portp;
741		struct addrinfo hints, *res;
742
743		nstate = EPRT;
744
745		(void)close(wport4);
746		(void)close(wport6);
747		(void)close(port4);
748		(void)close(port6);
749		wport4 = wport6 = port4 = port6 = -1;
750
751		if (epsvall) {
752			return dprintf(src, "501 %s disallowed in EPSV ALL\r\n",
753			    cmd);
754		}
755
756		p = param;
757		ch = *p++;	/* boundary character */
758		afp = p;
759		while (*p && *p != ch)
760			p++;
761		if (!*p) {
762eprtparamfail:
763			return dprintf(src,
764			    "501 illegal parameter to EPRT\r\n");
765		}
766		*p++ = '\0';
767		hostp = p;
768		while (*p && *p != ch)
769			p++;
770		if (!*p)
771			goto eprtparamfail;
772		*p++ = '\0';
773		portp = p;
774		while (*p && *p != ch)
775			p++;
776		if (!*p)
777			goto eprtparamfail;
778		*p++ = '\0';
779
780		n = sscanf(afp, "%d", &af);
781		if (n != 1 || af != 2) {
782			return dprintf(src,
783			    "501 unsupported address family to EPRT\r\n");
784		}
785		memset(&hints, 0, sizeof(hints));
786		hints.ai_family = AF_UNSPEC;
787		hints.ai_socktype = SOCK_STREAM;
788		hints.ai_protocol = IPPROTO_TCP;
789		error = getaddrinfo(hostp, portp, &hints, &res);
790		if (error) {
791			return dprintf(src,
792			    "501 EPRT: %s\r\n", gai_strerror(error));
793		}
794		if (res->ai_next) {
795			freeaddrinfo(res);
796			return dprintf(src,
797			    "501 EPRT: %s resolved to multiple addresses\r\n",
798			    hostp);
799		}
800
801		memcpy(&data6, res->ai_addr, res->ai_addrlen);
802
803		freeaddrinfo(res);
804		goto sendport;
805	} else if (strcmp(cmd, "LPSV") == 0 && !param) {
806		/*
807		 * LPSV -> PASV
808		 */
809		nstate = LPSV;
810
811		(void)close(wport4);
812		(void)close(wport6);
813		(void)close(port4);
814		(void)close(port6);
815		wport4 = wport6 = port4 = port6 = -1;
816
817		if (epsvall) {
818			return dprintf(src, "501 %s disallowed in EPSV ALL\r\n",
819			    cmd);
820		}
821
822		*state = LPSV;
823		passivemode = 0;	/* to be set to 1 later */
824		/* transmit PASV */
825		return dprintf(dst, "PASV\r\n");
826	} else if (strcmp(cmd, "EPSV") == 0 && !param) {
827		/*
828		 * EPSV -> PASV
829		 */
830		(void)close(wport4);
831		(void)close(wport6);
832		(void)close(port4);
833		(void)close(port6);
834		wport4 = wport6 = port4 = port6 = -1;
835
836		*state = EPSV;
837		passivemode = 0;	/* to be set to 1 later */
838		return dprintf(dst, "PASV\r\n");
839	} else if (strcmp(cmd, "EPSV") == 0 && param &&
840	    strncasecmp(param, "ALL", 3) == 0 &&
841	    isspace((unsigned char)param[3])) {
842		/*
843		 * EPSV ALL
844		 */
845		epsvall = 1;
846		return dprintf(src, "200 EPSV ALL command successful.\r\n");
847	} else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) {
848		/*
849		 * reject PORT/PASV
850		 */
851		return dprintf(src, "502 %s not implemented.\r\n", cmd);
852	} else if (passivemode
853		&& (strcmp(cmd, "STOR") == 0
854		 || strcmp(cmd, "STOU") == 0
855		 || strcmp(cmd, "RETR") == 0
856		 || strcmp(cmd, "LIST") == 0
857		 || strcmp(cmd, "NLST") == 0
858		 || strcmp(cmd, "APPE") == 0)) {
859		/*
860		 * commands with data transfer.  need to care about passive
861		 * mode data connection.
862		 */
863
864		*state = NONE;
865		if (ftp_passiveconn() < 0) {
866			return dprintf(src,
867			    "425 Cannot open data connection\r\n");
868		} else {
869			/* simply relay the command */
870			return write(dst, rbuf, (size_t)n);
871		}
872	} else {
873		/* simply relay it */
874		*state = NONE;
875		return write(dst, rbuf, (size_t)n);
876	}
877
878 bad:
879	exit_failure("%s", strerror(errno));
880	/*NOTREACHED*/
881	return 0;	/* to make gcc happy */
882}
883