ftp.c revision 95355
155714Skris/*	$KAME: ftp.c,v 1.11 2001/07/02 14:36:49 itojun Exp $	*/
255714Skris
355714Skris/*
455714Skris * Copyright (C) 1997 and 1998 WIDE Project.
555714Skris * All rights reserved.
655714Skris *
755714Skris * Redistribution and use in source and binary forms, with or without
855714Skris * modification, are permitted provided that the following conditions
955714Skris * are met:
1055714Skris * 1. Redistributions of source code must retain the above copyright
1155714Skris *    notice, this list of conditions and the following disclaimer.
1255714Skris * 2. Redistributions in binary form must reproduce the above copyright
1355714Skris *    notice, this list of conditions and the following disclaimer in the
1455714Skris *    documentation and/or other materials provided with the distribution.
1555714Skris * 3. Neither the name of the project nor the names of its contributors
1655714Skris *    may be used to endorse or promote products derived from this software
1755714Skris *    without specific prior written permission.
1855714Skris *
1955714Skris * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2055714Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2155714Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2255714Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2355714Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2455714Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2555714Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2655714Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2755714Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2855714Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2955714Skris * SUCH DAMAGE.
3055714Skris *
3155714Skris * $FreeBSD: head/usr.sbin/faithd/ftp.c 95355 2002-04-24 10:43:40Z ume $
3255714Skris */
3355714Skris
3455714Skris#include <sys/param.h>
3555714Skris#include <sys/types.h>
3655714Skris#include <sys/socket.h>
3755714Skris#include <sys/ioctl.h>
3855714Skris#include <sys/time.h>
3955714Skris
4055714Skris#include <stdio.h>
4155714Skris#include <stdlib.h>
4255714Skris#include <string.h>
4355714Skris#include <syslog.h>
4455714Skris#include <unistd.h>
4555714Skris#include <errno.h>
4655714Skris#include <ctype.h>
4755714Skris
4855714Skris#include <netinet/in.h>
4955714Skris#include <arpa/inet.h>
5055714Skris#include <netdb.h>
5155714Skris
5255714Skris#include "faithd.h"
5355714Skris
5455714Skrisstatic char rbuf[MSS];
5555714Skrisstatic char sbuf[MSS];
5655714Skrisstatic int passivemode = 0;
5755714Skrisstatic int wport4 = -1;			/* listen() to active */
5855714Skrisstatic int wport6 = -1;			/* listen() to passive */
5955714Skrisstatic int port4 = -1;			/* active: inbound  passive: outbound */
6055714Skrisstatic int port6 = -1;			/* active: outbound  passive: inbound */
6155714Skrisstatic struct sockaddr_storage data4;	/* server data address */
6255714Skrisstatic struct sockaddr_storage data6;	/* client data address */
6355714Skrisstatic int epsvall = 0;
6455714Skris
6555714Skris#ifdef FAITH4
6655714Skrisenum state { NONE, LPRT, EPRT, PORT, LPSV, EPSV, PASV };
6755714Skris#else
6855714Skrisenum state { NONE, LPRT, EPRT, LPSV, EPSV };
6955714Skris#endif
7055714Skris
7155714Skrisstatic int ftp_activeconn __P((void));
7255714Skrisstatic int ftp_passiveconn __P((void));
7355714Skrisstatic int ftp_copy __P((int, int));
7455714Skrisstatic int ftp_copyresult __P((int, int, enum state));
7555714Skrisstatic int ftp_copycommand __P((int, int, enum state *));
7655714Skris
77void
78ftp_relay(int ctl6, int ctl4)
79{
80	fd_set readfds;
81	int error;
82	enum state state = NONE;
83	struct timeval tv;
84
85	syslog(LOG_INFO, "starting ftp control connection");
86
87	for (;;) {
88		FD_ZERO(&readfds);
89		FD_SET(ctl4, &readfds);
90		FD_SET(ctl6, &readfds);
91		if (0 <= port4)
92			FD_SET(port4, &readfds);
93		if (0 <= port6)
94			FD_SET(port6, &readfds);
95#if 0
96		if (0 <= wport4)
97			FD_SET(wport4, &readfds);
98		if (0 <= wport6)
99			FD_SET(wport6, &readfds);
100#endif
101		tv.tv_sec = FAITH_TIMEOUT;
102		tv.tv_usec = 0;
103
104		error = select(256, &readfds, NULL, NULL, &tv);
105		if (error == -1)
106			exit_failure("select: %s", strerror(errno));
107		else if (error == 0)
108			exit_failure("connection timeout");
109
110		/*
111		 * The order of the following checks does (slightly) matter.
112		 * It is important to visit all checks (do not use "continue"),
113		 * otherwise some of the pipe may become full and we cannot
114		 * relay correctly.
115		 */
116		if (FD_ISSET(ctl6, &readfds)) {
117			/*
118			 * copy control connection from the client.
119			 * command translation is necessary.
120			 */
121			error = ftp_copycommand(ctl6, ctl4, &state);
122
123			if (error < 0)
124				goto bad;
125			else if (error == 0) {
126				close(ctl4);
127				close(ctl6);
128				exit_success("terminating ftp control connection");
129			}
130		}
131		if (FD_ISSET(ctl4, &readfds)) {
132			/*
133			 * copy control connection from the server
134			 * translation of result code is necessary.
135			 */
136			error = ftp_copyresult(ctl4, ctl6, state);
137
138			if (error < 0)
139				goto bad;
140			else if (error == 0) {
141				close(ctl4);
142				close(ctl6);
143				exit_success("terminating ftp control connection");
144			}
145		}
146		if (0 <= port4 && 0 <= port6 && FD_ISSET(port4, &readfds)) {
147			/*
148			 * copy data connection.
149			 * no special treatment necessary.
150			 */
151			if (FD_ISSET(port4, &readfds))
152				error = ftp_copy(port4, port6);
153			switch (error) {
154			case -1:
155				goto bad;
156			case 0:
157				close(port4);
158				close(port6);
159				port4 = port6 = -1;
160				syslog(LOG_INFO, "terminating data connection");
161				break;
162			default:
163				break;
164			}
165		}
166		if (0 <= port4 && 0 <= port6 && FD_ISSET(port6, &readfds)) {
167			/*
168			 * copy data connection.
169			 * no special treatment necessary.
170			 */
171			if (FD_ISSET(port6, &readfds))
172				error = ftp_copy(port6, port4);
173			switch (error) {
174			case -1:
175				goto bad;
176			case 0:
177				close(port4);
178				close(port6);
179				port4 = port6 = -1;
180				syslog(LOG_INFO, "terminating data connection");
181				break;
182			default:
183				break;
184			}
185		}
186#if 0
187		if (wport4 && FD_ISSET(wport4, &readfds)) {
188			/*
189			 * establish active data connection from the server.
190			 */
191			ftp_activeconn();
192		}
193		if (wport6 && FD_ISSET(wport6, &readfds)) {
194			/*
195			 * establish passive data connection from the client.
196			 */
197			ftp_passiveconn();
198		}
199#endif
200	}
201
202 bad:
203	exit_failure("%s", strerror(errno));
204}
205
206static int
207ftp_activeconn()
208{
209	int n;
210	int error;
211	fd_set set;
212	struct timeval timeout;
213	struct sockaddr *sa;
214
215	/* get active connection from server */
216	FD_ZERO(&set);
217	FD_SET(wport4, &set);
218	timeout.tv_sec = 120;
219	timeout.tv_usec = -1;
220	n = sizeof(data4);
221	if (select(wport4 + 1, &set, NULL, NULL, &timeout) == 0
222	 || (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0) {
223		close(wport4);
224		wport4 = -1;
225		syslog(LOG_INFO, "active mode data connection failed");
226		return -1;
227	}
228
229	/* ask active connection to client */
230	sa = (struct sockaddr *)&data6;
231	port6 = socket(sa->sa_family, SOCK_STREAM, 0);
232	if (port6 == -1) {
233		close(port4);
234		close(wport4);
235		port4 = wport4 = -1;
236		syslog(LOG_INFO, "active mode data connection failed");
237		return -1;
238	}
239	error = connect(port6, sa, sa->sa_len);
240	if (error < 0) {
241		close(port6);
242		close(port4);
243		close(wport4);
244		port6 = port4 = wport4 = -1;
245		syslog(LOG_INFO, "active mode data connection failed");
246		return -1;
247	}
248
249	syslog(LOG_INFO, "active mode data connection established");
250	return 0;
251}
252
253static int
254ftp_passiveconn()
255{
256	int n;
257	int error;
258	fd_set set;
259	struct timeval timeout;
260	struct sockaddr *sa;
261
262	/* get passive connection from client */
263	FD_ZERO(&set);
264	FD_SET(wport6, &set);
265	timeout.tv_sec = 120;
266	timeout.tv_usec = 0;
267	n = sizeof(data6);
268	if (select(wport6 + 1, &set, NULL, NULL, &timeout) == 0
269	 || (port6 = accept(wport6, (struct sockaddr *)&data6, &n)) < 0) {
270		close(wport6);
271		wport6 = -1;
272		syslog(LOG_INFO, "passive mode data connection failed");
273		return -1;
274	}
275
276	/* ask passive connection to server */
277	sa = (struct sockaddr *)&data4;
278	port4 = socket(sa->sa_family, SOCK_STREAM, 0);
279	if (port4 == -1) {
280		close(wport6);
281		close(port6);
282		wport6 = port6 = -1;
283		syslog(LOG_INFO, "passive mode data connection failed");
284		return -1;
285	}
286	error = connect(port4, sa, sa->sa_len);
287	if (error < 0) {
288		close(wport6);
289		close(port4);
290		close(port6);
291		wport6 = port4 = port6 = -1;
292		syslog(LOG_INFO, "passive mode data connection failed");
293		return -1;
294	}
295
296	syslog(LOG_INFO, "passive mode data connection established");
297	return 0;
298}
299
300static int
301ftp_copy(int src, int dst)
302{
303	int error, atmark;
304	int n;
305
306	/* OOB data handling */
307	error = ioctl(src, SIOCATMARK, &atmark);
308	if (error != -1 && atmark == 1) {
309		n = read(src, rbuf, 1);
310		if (n == -1)
311			goto bad;
312		send(dst, rbuf, n, MSG_OOB);
313#if 0
314		n = read(src, rbuf, sizeof(rbuf));
315		if (n == -1)
316			goto bad;
317		write(dst, rbuf, n);
318		return n;
319#endif
320	}
321
322	n = read(src, rbuf, sizeof(rbuf));
323	switch (n) {
324	case -1:
325	case 0:
326		return n;
327	default:
328		write(dst, rbuf, n);
329		return n;
330	}
331
332 bad:
333	exit_failure("%s", strerror(errno));
334	/*NOTREACHED*/
335	return 0;	/* to make gcc happy */
336}
337
338static int
339ftp_copyresult(int src, int dst, enum state state)
340{
341	int error, atmark;
342	int n;
343	char *param;
344	int code;
345
346	/* OOB data handling */
347	error = ioctl(src, SIOCATMARK, &atmark);
348	if (error != -1 && atmark == 1) {
349		n = read(src, rbuf, 1);
350		if (n == -1)
351			goto bad;
352		send(dst, rbuf, n, MSG_OOB);
353#if 0
354		n = read(src, rbuf, sizeof(rbuf));
355		if (n == -1)
356			goto bad;
357		write(dst, rbuf, n);
358		return n;
359#endif
360	}
361
362	n = read(src, rbuf, sizeof(rbuf));
363	if (n <= 0)
364		return n;
365	rbuf[n] = '\0';
366
367	/*
368	 * parse argument
369	 */
370    {
371	char *p;
372	int i;
373
374	p = rbuf;
375	for (i = 0; i < 3; i++) {
376		if (!isdigit(*p)) {
377			/* invalid reply */
378			write(dst, rbuf, n);
379			return n;
380		}
381		p++;
382	}
383	if (!isspace(*p)) {
384		/* invalid reply */
385		write(dst, rbuf, n);
386		return n;
387	}
388	code = atoi(rbuf);
389	param = p;
390	/* param points to first non-command token, if any */
391	while (*param && isspace(*param))
392		param++;
393	if (!*param)
394		param = NULL;
395    }
396
397	switch (state) {
398	case NONE:
399		if (!passivemode && rbuf[0] == '1') {
400			if (ftp_activeconn() < 0) {
401				n = snprintf(rbuf, sizeof(rbuf),
402					"425 Cannot open data connetion\r\n");
403			}
404		}
405		return n > 0 ? write(dst, rbuf, n) : n;
406	case LPRT:
407	case EPRT:
408		/* expecting "200 PORT command successful." */
409		if (code == 200) {
410			char *p;
411
412			p = strstr(rbuf, "PORT");
413			if (p) {
414				p[0] = (state == LPRT) ? 'L' : 'E';
415				p[1] = 'P';
416			}
417		} else {
418			close(wport4);
419			wport4 = -1;
420		}
421		write(dst, rbuf, n);
422		return n;
423#ifdef FAITH4
424	case PORT:
425		/* expecting "200 EPRT command successful." */
426		if (code == 200) {
427			char *p;
428
429			p = strstr(rbuf, "EPRT");
430			if (p) {
431				p[0] = 'P';
432				p[1] = 'O';
433			}
434		} else {
435			close(wport4);
436			wport4 = -1;
437		}
438		write(dst, rbuf, n);
439		return n;
440#endif
441	case LPSV:
442	case EPSV:
443		/*
444		 * expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)"
445		 * (in some cases result comes without paren)
446		 */
447		if (code != 227) {
448passivefail0:
449			close(wport6);
450			wport6 = -1;
451			write(dst, rbuf, n);
452			return n;
453		}
454
455	    {
456		unsigned int ho[4], po[2];
457		struct sockaddr_in *sin;
458		struct sockaddr_in6 *sin6;
459		u_short port;
460		char *p;
461
462		/*
463		 * PASV result -> LPSV/EPSV result
464		 */
465		p = param;
466		while (*p && *p != '(' && !isdigit(*p))	/*)*/
467			p++;
468		if (!*p)
469			goto passivefail0;	/*XXX*/
470		if (*p == '(')	/*)*/
471			p++;
472		n = sscanf(p, "%u,%u,%u,%u,%u,%u",
473			&ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
474		if (n != 6)
475			goto passivefail0;	/*XXX*/
476
477		/* keep PORT parameter */
478		memset(&data4, 0, sizeof(data4));
479		sin = (struct sockaddr_in *)&data4;
480		sin->sin_len = sizeof(*sin);
481		sin->sin_family = AF_INET;
482		sin->sin_addr.s_addr = 0;
483		for (n = 0; n < 4; n++) {
484			sin->sin_addr.s_addr |=
485				htonl((ho[n] & 0xff) << ((3 - n) * 8));
486		}
487		sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
488
489		/* get ready for passive data connection */
490		memset(&data6, 0, sizeof(data6));
491		sin6 = (struct sockaddr_in6 *)&data6;
492		sin6->sin6_len = sizeof(*sin6);
493		sin6->sin6_family = AF_INET6;
494		wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0);
495		if (wport6 == -1) {
496passivefail:
497			n = snprintf(sbuf, sizeof(sbuf),
498				"500 could not translate from PASV\r\n");
499			return n > 0 ? write(src, sbuf, n) : n;
500		}
501#ifdef IPV6_FAITH
502	    {
503		int on = 1;
504		error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH,
505			&on, sizeof(on));
506		if (error == -1)
507			exit_failure("setsockopt(IPV6_FAITH): %s", strerror(errno));
508	    }
509#endif
510		error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len);
511		if (error == -1) {
512			close(wport6);
513			wport6 = -1;
514			goto passivefail;
515		}
516		error = listen(wport6, 1);
517		if (error == -1) {
518			close(wport6);
519			wport6 = -1;
520			goto passivefail;
521		}
522
523		/* transmit LPSV or EPSV */
524		/*
525		 * addr from dst, port from wport6
526		 */
527		n = sizeof(data6);
528		error = getsockname(wport6, (struct sockaddr *)&data6, &n);
529		if (error == -1) {
530			close(wport6);
531			wport6 = -1;
532			goto passivefail;
533		}
534		sin6 = (struct sockaddr_in6 *)&data6;
535		port = sin6->sin6_port;
536
537		n = sizeof(data6);
538		error = getsockname(dst, (struct sockaddr *)&data6, &n);
539		if (error == -1) {
540			close(wport6);
541			wport6 = -1;
542			goto passivefail;
543		}
544		sin6 = (struct sockaddr_in6 *)&data6;
545		sin6->sin6_port = port;
546
547		if (state == LPSV) {
548			char *a, *p;
549
550			a = (char *)&sin6->sin6_addr;
551			p = (char *)&sin6->sin6_port;
552			n = snprintf(sbuf, sizeof(sbuf),
553"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",
554				6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
555				UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
556				UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
557				UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
558				2, UC(p[0]), UC(p[1]));
559			if (n > 0)
560				n = write(dst, sbuf, n);
561			passivemode = 1;
562			return n;
563		} else {
564			n = snprintf(sbuf, sizeof(sbuf),
565"229 Entering Extended Passive Mode (|||%d|)\r\n",
566				ntohs(sin6->sin6_port));
567			if (n > 0)
568				n = write(dst, sbuf, n);
569			passivemode = 1;
570			return n;
571		}
572	    }
573#ifdef FAITH4
574	case PASV:
575		/* expecting "229 Entering Extended Passive Mode (|||x|)" */
576		if (code != 229) {
577passivefail1:
578			close(wport6);
579			wport6 = -1;
580			write(dst, rbuf, n);
581			return n;
582		}
583
584	    {
585		u_short port;
586		char *p;
587		struct sockaddr_in *sin;
588		struct sockaddr_in6 *sin6;
589
590		/*
591		 * EPSV result -> PORT result
592		 */
593		p = param;
594		while (*p && *p != '(')	/*)*/
595			p++;
596		if (!*p)
597			goto passivefail1;	/*XXX*/
598		p++;
599		n = sscanf(p, "|||%hu|", &port);
600		if (n != 1)
601			goto passivefail1;	/*XXX*/
602
603		/* keep EPRT parameter */
604		n = sizeof(data4);
605		error = getpeername(src, (struct sockaddr *)&data4, &n);
606		if (error == -1)
607			goto passivefail1;	/*XXX*/
608		sin6 = (struct sockaddr_in6 *)&data4;
609		sin6->sin6_port = htons(port);
610
611		/* get ready for passive data connection */
612		memset(&data6, 0, sizeof(data6));
613		sin = (struct sockaddr_in *)&data6;
614		sin->sin_len = sizeof(*sin);
615		sin->sin_family = AF_INET;
616		wport6 = socket(sin->sin_family, SOCK_STREAM, 0);
617		if (wport6 == -1) {
618passivefail2:
619			n = snprintf(sbuf, sizeof(sbuf),
620				"500 could not translate from EPSV\r\n");
621			return n > 0 ? write(src, sbuf, n) : n;
622		}
623#ifdef IP_FAITH
624	    {
625		int on = 1;
626		error = setsockopt(wport6, IPPROTO_IP, IP_FAITH,
627			&on, sizeof(on));
628		if (error == -1)
629			exit_error("setsockopt(IP_FAITH): %s", strerror(errno));
630	    }
631#endif
632		error = bind(wport6, (struct sockaddr *)sin, sin->sin_len);
633		if (error == -1) {
634			close(wport6);
635			wport6 = -1;
636			goto passivefail2;
637		}
638		error = listen(wport6, 1);
639		if (error == -1) {
640			close(wport6);
641			wport6 = -1;
642			goto passivefail2;
643		}
644
645		/* transmit PORT */
646		/*
647		 * addr from dst, port from wport6
648		 */
649		n = sizeof(data6);
650		error = getsockname(wport6, (struct sockaddr *)&data6, &n);
651		if (error == -1) {
652			close(wport6);
653			wport6 = -1;
654			goto passivefail2;
655		}
656		sin = (struct sockaddr_in *)&data6;
657		port = sin->sin_port;
658
659		n = sizeof(data6);
660		error = getsockname(dst, (struct sockaddr *)&data6, &n);
661		if (error == -1) {
662			close(wport6);
663			wport6 = -1;
664			goto passivefail2;
665		}
666		sin = (struct sockaddr_in *)&data6;
667		sin->sin_port = port;
668
669		{
670			char *a, *p;
671
672			a = (char *)&sin->sin_addr;
673			p = (char *)&sin->sin_port;
674			n = snprintf(sbuf, sizeof(sbuf),
675"227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
676				UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
677				UC(p[0]), UC(p[1]));
678			if (n > 0)
679				n = write(dst, sbuf, n);
680			passivemode = 1;
681			return n;
682		}
683	    }
684#endif /* FAITH4 */
685	}
686
687 bad:
688	exit_failure("%s", strerror(errno));
689	/*NOTREACHED*/
690	return 0;	/* to make gcc happy */
691}
692
693static int
694ftp_copycommand(int src, int dst, enum state *state)
695{
696	int error, atmark;
697	int n;
698	unsigned int af, hal, ho[16], pal, po[2];
699	char *a, *p;
700	char cmd[5], *param;
701	struct sockaddr_in *sin;
702	struct sockaddr_in6 *sin6;
703	enum state nstate;
704	char ch;
705
706	/* OOB data handling */
707	error = ioctl(src, SIOCATMARK, &atmark);
708	if (error != -1 && atmark == 1) {
709		n = read(src, rbuf, 1);
710		if (n == -1)
711			goto bad;
712		send(dst, rbuf, n, MSG_OOB);
713#if 0
714		n = read(src, rbuf, sizeof(rbuf));
715		if (n == -1)
716			goto bad;
717		write(dst, rbuf, n);
718		return n;
719#endif
720	}
721
722	n = read(src, rbuf, sizeof(rbuf));
723	if (n <= 0)
724		return n;
725	rbuf[n] = '\0';
726
727	if (n < 4) {
728		write(dst, rbuf, n);
729		return n;
730	}
731
732	/*
733	 * parse argument
734	 */
735    {
736	char *p, *q;
737	int i;
738
739	p = rbuf;
740	q = cmd;
741	for (i = 0; i < 4; i++) {
742		if (!isalpha(*p)) {
743			/* invalid command */
744			write(dst, rbuf, n);
745			return n;
746		}
747		*q++ = islower(*p) ? toupper(*p) : *p;
748		p++;
749	}
750	if (!isspace(*p)) {
751		/* invalid command */
752		write(dst, rbuf, n);
753		return n;
754	}
755	*q = '\0';
756	param = p;
757	/* param points to first non-command token, if any */
758	while (*param && isspace(*param))
759		param++;
760	if (!*param)
761		param = NULL;
762    }
763
764	*state = NONE;
765
766	if (strcmp(cmd, "LPRT") == 0 && param) {
767		/*
768		 * LPRT -> PORT
769		 */
770		nstate = LPRT;
771
772		close(wport4);
773		close(wport6);
774		close(port4);
775		close(port6);
776		wport4 = wport6 = port4 = port6 = -1;
777
778		if (epsvall) {
779			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
780				cmd);
781			return n > 0 ? write(src, sbuf, n) : n;
782		}
783
784		n = sscanf(param,
785"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u",
786			      &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3],
787			      &ho[4], &ho[5], &ho[6], &ho[7],
788			      &ho[8], &ho[9], &ho[10], &ho[11],
789			      &ho[12], &ho[13], &ho[14], &ho[15],
790			      &pal, &po[0], &po[1]);
791		if (n != 21 || af != 6 || hal != 16|| pal != 2) {
792			n = snprintf(sbuf, sizeof(sbuf),
793				"501 illegal parameter to LPRT\r\n");
794			return n > 0 ? write(src, sbuf, n) : n;
795		}
796
797		/* keep LPRT parameter */
798		memset(&data6, 0, sizeof(data6));
799		sin6 = (struct sockaddr_in6 *)&data6;
800		sin6->sin6_len = sizeof(*sin6);
801		sin6->sin6_family = AF_INET6;
802		for (n = 0; n < 16; n++)
803			sin6->sin6_addr.s6_addr[n] = ho[n];
804		sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
805
806sendport:
807		/* get ready for active data connection */
808		n = sizeof(data4);
809		error = getsockname(dst, (struct sockaddr *)&data4, &n);
810		if (error == -1) {
811lprtfail:
812			n = snprintf(sbuf, sizeof(sbuf),
813				"500 could not translate to PORT\r\n");
814			return n > 0 ? write(src, sbuf, n) : n;
815		}
816		if (((struct sockaddr *)&data4)->sa_family != AF_INET)
817			goto lprtfail;
818		sin = (struct sockaddr_in *)&data4;
819		sin->sin_port = 0;
820		wport4 = socket(sin->sin_family, SOCK_STREAM, 0);
821		if (wport4 == -1)
822			goto lprtfail;
823		error = bind(wport4, (struct sockaddr *)sin, sin->sin_len);
824		if (error == -1) {
825			close(wport4);
826			wport4 = -1;
827			goto lprtfail;
828		}
829		error = listen(wport4, 1);
830		if (error == -1) {
831			close(wport4);
832			wport4 = -1;
833			goto lprtfail;
834		}
835
836		/* transmit PORT */
837		n = sizeof(data4);
838		error = getsockname(wport4, (struct sockaddr *)&data4, &n);
839		if (error == -1) {
840			close(wport4);
841			wport4 = -1;
842			goto lprtfail;
843		}
844		if (((struct sockaddr *)&data4)->sa_family != AF_INET) {
845			close(wport4);
846			wport4 = -1;
847			goto lprtfail;
848		}
849		sin = (struct sockaddr_in *)&data4;
850		a = (char *)&sin->sin_addr;
851		p = (char *)&sin->sin_port;
852		n = snprintf(sbuf, sizeof(sbuf), "PORT %d,%d,%d,%d,%d,%d\r\n",
853				  UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
854				  UC(p[0]), UC(p[1]));
855		if (n > 0)
856			n = write(dst, sbuf, n);
857		*state = nstate;
858		passivemode = 0;
859		return n;
860	} else if (strcmp(cmd, "EPRT") == 0 && param) {
861		/*
862		 * EPRT -> PORT
863		 */
864		char *afp, *hostp, *portp;
865		struct addrinfo hints, *res;
866
867		nstate = EPRT;
868
869		close(wport4);
870		close(wport6);
871		close(port4);
872		close(port6);
873		wport4 = wport6 = port4 = port6 = -1;
874
875		if (epsvall) {
876			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
877				cmd);
878			return n > 0 ? write(src, sbuf, n) : n;
879		}
880
881		p = param;
882		ch = *p++;	/* boundary character */
883		afp = p;
884		while (*p && *p != ch)
885			p++;
886		if (!*p) {
887eprtparamfail:
888			n = snprintf(sbuf, sizeof(sbuf),
889				"501 illegal parameter to EPRT\r\n");
890			return n > 0 ? write(src, sbuf, n) : n;
891		}
892		*p++ = '\0';
893		hostp = p;
894		while (*p && *p != ch)
895			p++;
896		if (!*p)
897			goto eprtparamfail;
898		*p++ = '\0';
899		portp = p;
900		while (*p && *p != ch)
901			p++;
902		if (!*p)
903			goto eprtparamfail;
904		*p++ = '\0';
905
906		n = sscanf(afp, "%d", &af);
907		if (n != 1 || af != 2) {
908			n = snprintf(sbuf, sizeof(sbuf),
909				"501 unsupported address family to EPRT\r\n");
910			return n > 0 ? write(src, sbuf, n) : n;
911		}
912		memset(&hints, 0, sizeof(hints));
913		hints.ai_family = AF_UNSPEC;
914		hints.ai_socktype = SOCK_STREAM;
915		error = getaddrinfo(hostp, portp, &hints, &res);
916		if (error) {
917			n = snprintf(sbuf, sizeof(sbuf),
918				"501 EPRT: %s\r\n", gai_strerror(error));
919			return n > 0 ? write(src, sbuf, n) : n;
920		}
921		if (res->ai_next) {
922			n = snprintf(sbuf, sizeof(sbuf),
923				"501 EPRT: %s resolved to multiple addresses\r\n", hostp);
924			return n > 0 ? write(src, sbuf, n) : n;
925		}
926
927		memcpy(&data6, res->ai_addr, res->ai_addrlen);
928
929		goto sendport;
930	} else if (strcmp(cmd, "LPSV") == 0 && !param) {
931		/*
932		 * LPSV -> PASV
933		 */
934		nstate = LPSV;
935
936		close(wport4);
937		close(wport6);
938		close(port4);
939		close(port6);
940		wport4 = wport6 = port4 = port6 = -1;
941
942		if (epsvall) {
943			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
944				cmd);
945			return n > 0 ? write(src, sbuf, n) : n;
946		}
947
948		/* transmit PASV */
949		n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
950		if (n > 0)
951			n = write(dst, sbuf, n);
952		*state = LPSV;
953		passivemode = 0;	/* to be set to 1 later */
954		return n;
955	} else if (strcmp(cmd, "EPSV") == 0 && !param) {
956		/*
957		 * EPSV -> PASV
958		 */
959		close(wport4);
960		close(wport6);
961		close(port4);
962		close(port6);
963		wport4 = wport6 = port4 = port6 = -1;
964
965		n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
966		if (n > 0)
967			n = write(dst, sbuf, n);
968		*state = EPSV;
969		passivemode = 0;	/* to be set to 1 later */
970		return n;
971	} else if (strcmp(cmd, "EPSV") == 0 && param
972	 && strncasecmp(param, "ALL", 3) == 0 && isspace(param[3])) {
973		/*
974		 * EPSV ALL
975		 */
976		epsvall = 1;
977		n = snprintf(sbuf, sizeof(sbuf), "200 EPSV ALL command successful.\r\n");
978		return n > 0 ? write(src, sbuf, n) : n;
979#ifdef FAITH4
980	} else if (strcmp(cmd, "PORT") == 0 && param) {
981		/*
982		 * PORT -> EPRT
983		 */
984		char host[NI_MAXHOST], serv[NI_MAXSERV];
985
986		nstate = PORT;
987
988		close(wport4);
989		close(wport6);
990		close(port4);
991		close(port6);
992		wport4 = wport6 = port4 = port6 = -1;
993
994		p = param;
995		n = sscanf(p, "%u,%u,%u,%u,%u,%u",
996			&ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
997		if (n != 6) {
998			n = snprintf(sbuf, sizeof(sbuf),
999				"501 illegal parameter to PORT\r\n");
1000			return n > 0 ? write(src, sbuf, n) : n;
1001		}
1002
1003		memset(&data6, 0, sizeof(data6));
1004		sin = (struct sockaddr_in *)&data6;
1005		sin->sin_len = sizeof(*sin);
1006		sin->sin_family = AF_INET;
1007		sin->sin_addr.s_addr = htonl(
1008			((ho[0] & 0xff) << 24) | ((ho[1] & 0xff) << 16) |
1009			((ho[2] & 0xff) << 8) | (ho[3] & 0xff));
1010		sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
1011
1012		/* get ready for active data connection */
1013		n = sizeof(data4);
1014		error = getsockname(dst, (struct sockaddr *)&data4, &n);
1015		if (error == -1) {
1016portfail:
1017			n = snprintf(sbuf, sizeof(sbuf),
1018				"500 could not translate to EPRT\r\n");
1019			return n > 0 ? write(src, sbuf, n) : n;
1020		}
1021		if (((struct sockaddr *)&data4)->sa_family != AF_INET6)
1022			goto portfail;
1023
1024		((struct sockaddr_in6 *)&data4)->sin6_port = 0;
1025		sa = (struct sockaddr *)&data4;
1026		wport4 = socket(sa->sa_family, SOCK_STREAM, 0);
1027		if (wport4 == -1)
1028			goto portfail;
1029		error = bind(wport4, sa, sa->sa_len);
1030		if (error == -1) {
1031			close(wport4);
1032			wport4 = -1;
1033			goto portfail;
1034		}
1035		error = listen(wport4, 1);
1036		if (error == -1) {
1037			close(wport4);
1038			wport4 = -1;
1039			goto portfail;
1040		}
1041
1042		/* transmit EPRT */
1043		n = sizeof(data4);
1044		error = getsockname(wport4, (struct sockaddr *)&data4, &n);
1045		if (error == -1) {
1046			close(wport4);
1047			wport4 = -1;
1048			goto portfail;
1049		}
1050		af = 2;
1051		sa = (struct sockaddr *)&data4;
1052		if (getnameinfo(sa, sa->sa_len, host, sizeof(host),
1053			serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV)) {
1054			close(wport4);
1055			wport4 = -1;
1056			goto portfail;
1057		}
1058		n = snprintf(sbuf, sizeof(sbuf), "EPRT |%d|%s|%s|\r\n", af, host, serv);
1059		if (n > 0)
1060			n = write(dst, sbuf, n);
1061		*state = nstate;
1062		passivemode = 0;
1063		return n;
1064	} else if (strcmp(cmd, "PASV") == 0 && !param) {
1065		/*
1066		 * PASV -> EPSV
1067		 */
1068
1069		nstate = PASV;
1070
1071		close(wport4);
1072		close(wport6);
1073		close(port4);
1074		close(port6);
1075		wport4 = wport6 = port4 = port6 = -1;
1076
1077		/* transmit EPSV */
1078		n = snprintf(sbuf, sizeof(sbuf), "EPSV\r\n");
1079		if (n > 0)
1080			n = write(dst, sbuf, n);
1081		*state = PASV;
1082		passivemode = 0;	/* to be set to 1 later */
1083		return n;
1084#else /* FAITH4 */
1085	} else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) {
1086		/*
1087		 * reject PORT/PASV
1088		 */
1089		n = snprintf(sbuf, sizeof(sbuf), "502 %s not implemented.\r\n", cmd);
1090		return n > 0 ? write(src, sbuf, n) : n;
1091#endif /* FAITH4 */
1092	} else if (passivemode
1093		&& (strcmp(cmd, "STOR") == 0
1094		 || strcmp(cmd, "STOU") == 0
1095		 || strcmp(cmd, "RETR") == 0
1096		 || strcmp(cmd, "LIST") == 0
1097		 || strcmp(cmd, "NLST") == 0
1098		 || strcmp(cmd, "APPE") == 0)) {
1099		/*
1100		 * commands with data transfer.  need to care about passive
1101		 * mode data connection.
1102		 */
1103
1104		if (ftp_passiveconn() < 0) {
1105			n = snprintf(sbuf, sizeof(sbuf), "425 Cannot open data connetion\r\n");
1106			if (n > 0)
1107				n = write(src, sbuf, n);
1108		} else {
1109			/* simply relay the command */
1110			write(dst, rbuf, n);
1111		}
1112
1113		*state = NONE;
1114		return n;
1115	} else {
1116		/* simply relay it */
1117		*state = NONE;
1118		return write(dst, rbuf, n);
1119	}
1120
1121 bad:
1122	exit_failure("%s", strerror(errno));
1123	/*NOTREACHED*/
1124	return 0;	/* to make gcc happy */
1125}
1126