Home | History | Annotate | Line # | Download | only in faithd
ftp.c revision 1.18
      1 /*	$NetBSD: ftp.c,v 1.18 2009/04/19 06:09:42 lukem 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 
     54 static char rbuf[MSS];
     55 static char sbuf[MSS];
     56 static int passivemode = 0;
     57 static int wport4 = -1;			/* listen() to active */
     58 static int wport6 = -1;			/* listen() to passive */
     59 static int port4 = -1;			/* active: inbound  passive: outbound */
     60 static int port6 = -1;			/* active: outbound  passive: inbound */
     61 static struct sockaddr_storage data4;	/* server data address */
     62 static struct sockaddr_storage data6;	/* client data address */
     63 static int epsvall = 0;
     64 
     65 enum state { NONE, LPRT, EPRT, LPSV, EPSV };
     66 
     67 static int ftp_activeconn __P((void));
     68 static int ftp_passiveconn __P((void));
     69 static int ftp_copy __P((int, int));
     70 static int ftp_copyresult __P((int, int, enum state));
     71 static int ftp_copycommand __P((int, int, enum state *));
     72 
     73 void
     74 ftp_relay(int ctl6, int ctl4)
     75 {
     76 	struct pollfd pfd[6];
     77 	int error;
     78 	enum state state = NONE;
     79 	struct timeval tv;
     80 
     81 	syslog(LOG_INFO, "starting ftp control connection");
     82 
     83 	for (;;) {
     84 		pfd[0].fd = ctl4;
     85 		pfd[0].events = POLLIN;
     86 		pfd[1].fd = ctl6;
     87 		pfd[1].events = POLLIN;
     88 		if (0 <= port4) {
     89 			pfd[2].fd = port4;
     90 			pfd[2].events = POLLIN;
     91 		} else
     92 			pfd[2].fd = -1;
     93 		if (0 <= port6) {
     94 			pfd[3].fd = port6;
     95 			pfd[3].events = POLLIN;
     96 		} else
     97 			pfd[3].fd = -1;
     98 #if 0
     99 		if (0 <= wport4) {
    100 			pfd[4].fd = wport4;
    101 			pfd[4].events = POLLIN;
    102 		} else
    103 			pfd[4].fd = -1;
    104 		if (0 <= wport6) {
    105 			pfd[5].fd = wport4;
    106 			pfd[5].events = POLLIN;
    107 		} else
    108 			pfd[5].fd = -1;
    109 #else
    110 		pfd[4].fd = pfd[5].fd = -1;
    111 		pfd[4].events = pfd[5].events = 0;
    112 #endif
    113 		tv.tv_sec = FAITH_TIMEOUT;
    114 		tv.tv_usec = 0;
    115 
    116 		error = poll(pfd, sizeof(pfd)/sizeof(pfd[0]), tv.tv_sec * 1000);
    117 		if (error == -1) {
    118 			exit_failure("poll: %s", strerror(errno));
    119 		}
    120 		else if (error == 0)
    121 			exit_failure("connection timeout");
    122 
    123 		/*
    124 		 * The order of the following checks does (slightly) matter.
    125 		 * It is important to visit all checks (do not use "continue"),
    126 		 * otherwise some of the pipe may become full and we cannot
    127 		 * relay correctly.
    128 		 */
    129 		if (pfd[1].revents & POLLIN)
    130 		{
    131 			/*
    132 			 * copy control connection from the client.
    133 			 * command translation is necessary.
    134 			 */
    135 			error = ftp_copycommand(ctl6, ctl4, &state);
    136 
    137 			if (error < 0)
    138 				goto bad;
    139 			else if (error == 0) {
    140 				(void)close(ctl4);
    141 				(void)close(ctl6);
    142 				exit_success("terminating ftp control connection");
    143 				/*NOTREACHED*/
    144 			}
    145 		}
    146 		if (pfd[0].revents & POLLIN)
    147 		{
    148 			/*
    149 			 * copy control connection from the server
    150 			 * translation of result code is necessary.
    151 			 */
    152 			error = ftp_copyresult(ctl4, ctl6, state);
    153 
    154 			if (error < 0)
    155 				goto bad;
    156 			else if (error == 0) {
    157 				(void)close(ctl4);
    158 				(void)close(ctl6);
    159 				exit_success("terminating ftp control connection");
    160 				/*NOTREACHED*/
    161 			}
    162 		}
    163 		if (0 <= port4 && 0 <= port6 && (pfd[2].revents & POLLIN))
    164 		{
    165 			/*
    166 			 * copy data connection.
    167 			 * no special treatment necessary.
    168 			 */
    169 			if (pfd[2].revents & POLLIN)
    170 				error = ftp_copy(port4, port6);
    171 			switch (error) {
    172 			case -1:
    173 				goto bad;
    174 			case 0:
    175 				if (port4 >= 0) {
    176 					close(port4);
    177 					port4 = -1;
    178 				}
    179 				if (port6 >= 0) {
    180 					close(port6);
    181 					port6 = -1;
    182 				}
    183 				syslog(LOG_INFO, "terminating data connection");
    184 				break;
    185 			default:
    186 				break;
    187 			}
    188 		}
    189 		if (0 <= port4 && 0 <= port6 && (pfd[3].revents & POLLIN))
    190 		{
    191 			/*
    192 			 * copy data connection.
    193 			 * no special treatment necessary.
    194 			 */
    195 			if (pfd[3].revents & POLLIN)
    196 				error = ftp_copy(port6, port4);
    197 			switch (error) {
    198 			case -1:
    199 				goto bad;
    200 			case 0:
    201 				if (port4 >= 0) {
    202 					close(port4);
    203 					port4 = -1;
    204 				}
    205 				if (port6 >= 0) {
    206 					close(port6);
    207 					port6 = -1;
    208 				}
    209 				syslog(LOG_INFO, "terminating data connection");
    210 				break;
    211 			default:
    212 				break;
    213 			}
    214 		}
    215 #if 0
    216 		if (wport4 && (pfd[4].revents & POLLIN))
    217 		{
    218 			/*
    219 			 * establish active data connection from the server.
    220 			 */
    221 			ftp_activeconn();
    222 		}
    223 		if (wport4 && (pfd[5].revents & POLLIN))
    224 		{
    225 			/*
    226 			 * establish passive data connection from the client.
    227 			 */
    228 			ftp_passiveconn();
    229 		}
    230 #endif
    231 	}
    232 
    233  bad:
    234 	exit_failure("%s", strerror(errno));
    235 }
    236 
    237 static int
    238 ftp_activeconn()
    239 {
    240 	socklen_t n;
    241 	int error;
    242 	struct pollfd pfd[1];
    243 	struct timeval timeout;
    244 	struct sockaddr *sa;
    245 
    246 	/* get active connection from server */
    247 	pfd[0].fd = wport4;
    248 	pfd[0].events = POLLIN;
    249 	timeout.tv_sec = 120;
    250 	timeout.tv_usec = 0;
    251 	n = sizeof(data4);
    252 	if (poll(pfd, sizeof(pfd)/sizeof(pfd[0]), timeout.tv_sec * 1000) == 0 ||
    253 	    (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0)
    254 	{
    255 		(void)close(wport4);
    256 		wport4 = -1;
    257 		syslog(LOG_INFO, "active mode data connection failed");
    258 		return -1;
    259 	}
    260 
    261 	/* ask active connection to client */
    262 	sa = (struct sockaddr *)&data6;
    263 	port6 = socket(sa->sa_family, SOCK_STREAM, 0);
    264 	if (port6 == -1) {
    265 		(void)close(port4);
    266 		(void)close(wport4);
    267 		port4 = wport4 = -1;
    268 		syslog(LOG_INFO, "active mode data connection failed");
    269 		return -1;
    270 	}
    271 	error = connect(port6, sa, sa->sa_len);
    272 	if (error < 0) {
    273 		(void)close(port6);
    274 		(void)close(port4);
    275 		(void)close(wport4);
    276 		port6 = port4 = wport4 = -1;
    277 		syslog(LOG_INFO, "active mode data connection failed");
    278 		return -1;
    279 	}
    280 
    281 	syslog(LOG_INFO, "active mode data connection established");
    282 	return 0;
    283 }
    284 
    285 static int
    286 ftp_passiveconn()
    287 {
    288 	socklen_t len;
    289 	int error;
    290 	struct pollfd pfd[1];
    291 	struct timeval timeout;
    292 	struct sockaddr *sa;
    293 
    294 	/* get passive connection from client */
    295 	pfd[0].fd = wport6;
    296 	pfd[0].events = POLLIN;
    297 	timeout.tv_sec = 120;
    298 	timeout.tv_usec = 0;
    299 	len = sizeof(data6);
    300 	if (poll(pfd, sizeof(pfd)/sizeof(pfd[0]), timeout.tv_sec * 1000) == 0 ||
    301 	    (port6 = accept(wport6, (struct sockaddr *)&data6, &len)) < 0)
    302 	{
    303 		(void)close(wport6);
    304 		wport6 = -1;
    305 		syslog(LOG_INFO, "passive mode data connection failed");
    306 		return -1;
    307 	}
    308 
    309 	/* ask passive connection to server */
    310 	sa = (struct sockaddr *)&data4;
    311 	port4 = socket(sa->sa_family, SOCK_STREAM, 0);
    312 	if (port4 == -1) {
    313 		(void)close(wport6);
    314 		(void)close(port6);
    315 		wport6 = port6 = -1;
    316 		syslog(LOG_INFO, "passive mode data connection failed");
    317 		return -1;
    318 	}
    319 	error = connect(port4, sa, sa->sa_len);
    320 	if (error < 0) {
    321 		(void)close(wport6);
    322 		(void)close(port4);
    323 		(void)close(port6);
    324 		wport6 = port4 = port6 = -1;
    325 		syslog(LOG_INFO, "passive mode data connection failed");
    326 		return -1;
    327 	}
    328 
    329 	syslog(LOG_INFO, "passive mode data connection established");
    330 	return 0;
    331 }
    332 
    333 static int
    334 ftp_copy(int src, int dst)
    335 {
    336 	int error, atmark, n;
    337 
    338 	/* OOB data handling */
    339 	error = ioctl(src, SIOCATMARK, &atmark);
    340 	if (error != -1 && atmark == 1) {
    341 		n = read(src, rbuf, 1);
    342 		if (n == -1)
    343 			goto bad;
    344 		send(dst, rbuf, n, MSG_OOB);
    345 #if 0
    346 		n = read(src, rbuf, sizeof(rbuf));
    347 		if (n == -1)
    348 			goto bad;
    349 		write(dst, rbuf, n);
    350 		return n;
    351 #endif
    352 	}
    353 
    354 	n = read(src, rbuf, sizeof(rbuf));
    355 	switch (n) {
    356 	case -1:
    357 	case 0:
    358 		return n;
    359 	default:
    360 		write(dst, rbuf, n);
    361 		return n;
    362 	}
    363 
    364  bad:
    365 	exit_failure("%s", strerror(errno));
    366 	/*NOTREACHED*/
    367 	return 0;	/* to make gcc happy */
    368 }
    369 
    370 static int
    371 ftp_copyresult(int src, int dst, enum state state)
    372 {
    373 	int error, atmark, n;
    374 	socklen_t len;
    375 	char *param;
    376 	int code;
    377 	char *a, *p;
    378 	int i;
    379 
    380 	/* OOB data handling */
    381 	error = ioctl(src, SIOCATMARK, &atmark);
    382 	if (error != -1 && atmark == 1) {
    383 		n = read(src, rbuf, 1);
    384 		if (n == -1)
    385 			goto bad;
    386 		send(dst, rbuf, n, MSG_OOB);
    387 #if 0
    388 		n = read(src, rbuf, sizeof(rbuf));
    389 		if (n == -1)
    390 			goto bad;
    391 		write(dst, rbuf, n);
    392 		return n;
    393 #endif
    394 	}
    395 
    396 	n = read(src, rbuf, sizeof(rbuf));
    397 	if (n <= 0)
    398 		return n;
    399 	rbuf[n] = '\0';
    400 
    401 	/*
    402 	 * parse argument
    403 	 */
    404 	p = rbuf;
    405 	for (i = 0; i < 3; i++) {
    406 		if (!isdigit((unsigned char)*p)) {
    407 			/* invalid reply */
    408 			write(dst, rbuf, n);
    409 			return n;
    410 		}
    411 		p++;
    412 	}
    413 	if (!isspace((unsigned char)*p)) {
    414 		/* invalid reply */
    415 		write(dst, rbuf, n);
    416 		return n;
    417 	}
    418 	code = atoi(rbuf);
    419 	param = p;
    420 	/* param points to first non-command token, if any */
    421 	while (*param && isspace((unsigned char)*param))
    422 		param++;
    423 	if (!*param)
    424 		param = NULL;
    425 
    426 	switch (state) {
    427 	case NONE:
    428 		if (!passivemode && rbuf[0] == '1') {
    429 			if (ftp_activeconn() < 0) {
    430 				n = snprintf(rbuf, sizeof(rbuf),
    431 					"425 Cannot open data connetion\r\n");
    432 				if (n < 0 || n >= (int)sizeof(rbuf))
    433 					n = 0;
    434 			}
    435 		}
    436 		if (n)
    437 			write(dst, rbuf, n);
    438 		return n;
    439 	case LPRT:
    440 	case EPRT:
    441 		/* expecting "200 PORT command successful." */
    442 		if (code == 200) {
    443 			p = strstr(rbuf, "PORT");
    444 			if (p) {
    445 				p[0] = (state == LPRT) ? 'L' : 'E';
    446 				p[1] = 'P';
    447 			}
    448 		} else {
    449 			(void)close(wport4);
    450 			wport4 = -1;
    451 		}
    452 		write(dst, rbuf, n);
    453 		return n;
    454 	case LPSV:
    455 	case EPSV:
    456 		/*
    457 		 * expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)"
    458 		 * (in some cases result comes without paren)
    459 		 */
    460 		if (code != 227) {
    461 passivefail0:
    462 			(void)close(wport6);
    463 			wport6 = -1;
    464 			write(dst, rbuf, n);
    465 			return n;
    466 		}
    467 
    468 	    {
    469 		unsigned int ho[4], po[2];
    470 		struct sockaddr_in *sin;
    471 		struct sockaddr_in6 *sin6;
    472 		u_short port;
    473 
    474 		/*
    475 		 * PASV result -> LPSV/EPSV result
    476 		 */
    477 		p = param;
    478 		while (*p && *p != '(' && !isdigit((unsigned char)*p))	/*)*/
    479 			p++;
    480 		if (!*p)
    481 			goto passivefail0;	/*XXX*/
    482 		if (*p == '(')	/*)*/
    483 			p++;
    484 		n = sscanf(p, "%u,%u,%u,%u,%u,%u",
    485 			&ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
    486 		if (n != 6)
    487 			goto passivefail0;	/*XXX*/
    488 
    489 		/* keep PORT parameter */
    490 		memset(&data4, 0, sizeof(data4));
    491 		sin = (struct sockaddr_in *)&data4;
    492 		sin->sin_len = sizeof(*sin);
    493 		sin->sin_family = AF_INET;
    494 		sin->sin_addr.s_addr = 0;
    495 		for (n = 0; n < 4; n++) {
    496 			sin->sin_addr.s_addr |=
    497 				htonl((ho[n] & 0xff) << ((3 - n) * 8));
    498 		}
    499 		sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
    500 
    501 		/* get ready for passive data connection */
    502 		memset(&data6, 0, sizeof(data6));
    503 		sin6 = (struct sockaddr_in6 *)&data6;
    504 		sin6->sin6_len = sizeof(*sin6);
    505 		sin6->sin6_family = AF_INET6;
    506 		wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0);
    507 		if (wport6 == -1) {
    508 passivefail:
    509 			n = snprintf(sbuf, sizeof(sbuf),
    510 				"500 could not translate from PASV\r\n");
    511 			if (n < 0 || n >= (int)sizeof(sbuf))
    512 				n = 0;
    513 			if (n)
    514 				write(src, sbuf, n);
    515 			return n;
    516 		}
    517 #ifdef IPV6_FAITH
    518 	    {
    519 		int on = 1;
    520 		error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH,
    521 			&on, sizeof(on));
    522 		if (error == -1)
    523 			exit_failure("setsockopt(IPV6_FAITH): %s", strerror(errno));
    524 	    }
    525 #endif
    526 		error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len);
    527 		if (error == -1) {
    528 			(void)close(wport6);
    529 			wport6 = -1;
    530 			goto passivefail;
    531 		}
    532 		error = listen(wport6, 1);
    533 		if (error == -1) {
    534 			(void)close(wport6);
    535 			wport6 = -1;
    536 			goto passivefail;
    537 		}
    538 
    539 		/* transmit LPSV or EPSV */
    540 		/*
    541 		 * addr from dst, port from wport6
    542 		 */
    543 		len = sizeof(data6);
    544 		error = getsockname(wport6, (struct sockaddr *)&data6, &len);
    545 		if (error == -1) {
    546 			(void)close(wport6);
    547 			wport6 = -1;
    548 			goto passivefail;
    549 		}
    550 		sin6 = (struct sockaddr_in6 *)&data6;
    551 		port = sin6->sin6_port;
    552 
    553 		len = sizeof(data6);
    554 		error = getsockname(dst, (struct sockaddr *)&data6, &len);
    555 		if (error == -1) {
    556 			(void)close(wport6);
    557 			wport6 = -1;
    558 			goto passivefail;
    559 		}
    560 		sin6 = (struct sockaddr_in6 *)&data6;
    561 		sin6->sin6_port = port;
    562 
    563 		if (state == LPSV) {
    564 			a = (char *)&sin6->sin6_addr;
    565 			p = (char *)&sin6->sin6_port;
    566 			n = snprintf(sbuf, sizeof(sbuf),
    567 "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",
    568 				6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
    569 				UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
    570 				UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
    571 				UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
    572 				2, UC(p[0]), UC(p[1]));
    573 			if (n < 0 || n >= (int)sizeof(sbuf))
    574 				n = 0;
    575 			if (n)
    576 				write(dst, sbuf, n);
    577 			passivemode = 1;
    578 			return n;
    579 		} else {
    580 			n = snprintf(sbuf, sizeof(sbuf),
    581 "229 Entering Extended Passive Mode (|||%d|)\r\n",
    582 				ntohs(sin6->sin6_port));
    583 			if (n < 0 || n >= (int)sizeof(sbuf))
    584 				n = 0;
    585 			if (n)
    586 				write(dst, sbuf, n);
    587 			passivemode = 1;
    588 			return n;
    589 		}
    590 	    }
    591 	}
    592 
    593  bad:
    594 	exit_failure("%s", strerror(errno));
    595 	/*NOTREACHED*/
    596 	return 0;	/* to make gcc happy */
    597 }
    598 
    599 static int
    600 ftp_copycommand(int src, int dst, enum state *state)
    601 {
    602 	int error, atmark, n;
    603 	socklen_t len;
    604 	unsigned int af, hal, ho[16], pal, po[2];
    605 	char *a, *p, *q;
    606 	char cmd[5], *param;
    607 	struct sockaddr_in *sin;
    608 	struct sockaddr_in6 *sin6;
    609 	enum state nstate;
    610 	char ch;
    611 	int i;
    612 
    613 	/* OOB data handling */
    614 	error = ioctl(src, SIOCATMARK, &atmark);
    615 	if (error != -1 && atmark == 1) {
    616 		n = read(src, rbuf, 1);
    617 		if (n == -1)
    618 			goto bad;
    619 		send(dst, rbuf, n, MSG_OOB);
    620 #if 0
    621 		n = read(src, rbuf, sizeof(rbuf));
    622 		if (n == -1)
    623 			goto bad;
    624 		write(dst, rbuf, n);
    625 		return n;
    626 #endif
    627 	}
    628 
    629 	n = read(src, rbuf, sizeof(rbuf));
    630 	if (n <= 0)
    631 		return n;
    632 	rbuf[n] = '\0';
    633 
    634 	if (n < 4) {
    635 		write(dst, rbuf, n);
    636 		return n;
    637 	}
    638 
    639 	/*
    640 	 * parse argument
    641 	 */
    642 	p = rbuf;
    643 	q = cmd;
    644 	for (i = 0; i < 4; i++) {
    645 		if (!isalpha((unsigned char)*p)) {
    646 			/* invalid command */
    647 			write(dst, rbuf, n);
    648 			return n;
    649 		}
    650 		*q++ = islower((unsigned char)*p) ? toupper((unsigned char)*p) : *p;
    651 		p++;
    652 	}
    653 	if (!isspace((unsigned char)*p)) {
    654 		/* invalid command */
    655 		write(dst, rbuf, n);
    656 		return n;
    657 	}
    658 	*q = '\0';
    659 	param = p;
    660 	/* param points to first non-command token, if any */
    661 	while (*param && isspace((unsigned char)*param))
    662 		param++;
    663 	if (!*param)
    664 		param = NULL;
    665 
    666 	*state = NONE;
    667 
    668 	if (strcmp(cmd, "LPRT") == 0 && param) {
    669 		/*
    670 		 * LPRT -> PORT
    671 		 */
    672 		nstate = LPRT;
    673 
    674 		(void)close(wport4);
    675 		(void)close(wport6);
    676 		(void)close(port4);
    677 		(void)close(port6);
    678 		wport4 = wport6 = port4 = port6 = -1;
    679 
    680 		if (epsvall) {
    681 			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
    682 				cmd);
    683 			if (n < 0 || n >= (int)sizeof(sbuf))
    684 				n = 0;
    685 			if (n)
    686 				write(src, sbuf, n);
    687 			return n;
    688 		}
    689 
    690 		n = sscanf(param,
    691 "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u",
    692 			      &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3],
    693 			      &ho[4], &ho[5], &ho[6], &ho[7],
    694 			      &ho[8], &ho[9], &ho[10], &ho[11],
    695 			      &ho[12], &ho[13], &ho[14], &ho[15],
    696 			      &pal, &po[0], &po[1]);
    697 		if (n != 21 || af != 6 || hal != 16|| pal != 2) {
    698 			n = snprintf(sbuf, sizeof(sbuf),
    699 				"501 illegal parameter to LPRT\r\n");
    700 			if (n < 0 || n >= (int)sizeof(sbuf))
    701 				n = 0;
    702 			if (n)
    703 				write(src, sbuf, n);
    704 			return n;
    705 		}
    706 
    707 		/* keep LPRT parameter */
    708 		memset(&data6, 0, sizeof(data6));
    709 		sin6 = (struct sockaddr_in6 *)&data6;
    710 		sin6->sin6_len = sizeof(*sin6);
    711 		sin6->sin6_family = AF_INET6;
    712 		for (n = 0; n < 16; n++)
    713 			sin6->sin6_addr.s6_addr[n] = ho[n];
    714 		sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
    715 
    716 sendport:
    717 		/* get ready for active data connection */
    718 		len = sizeof(data4);
    719 		error = getsockname(dst, (struct sockaddr *)&data4, &len);
    720 		if (error == -1) {
    721 lprtfail:
    722 			n = snprintf(sbuf, sizeof(sbuf),
    723 				"500 could not translate to PORT\r\n");
    724 			if (n < 0 || n >= (int)sizeof(sbuf))
    725 				n = 0;
    726 			if (n)
    727 				write(src, sbuf, n);
    728 			return n;
    729 		}
    730 		if (((struct sockaddr *)&data4)->sa_family != AF_INET)
    731 			goto lprtfail;
    732 		sin = (struct sockaddr_in *)&data4;
    733 		sin->sin_port = 0;
    734 		wport4 = socket(sin->sin_family, SOCK_STREAM, 0);
    735 		if (wport4 == -1)
    736 			goto lprtfail;
    737 		error = bind(wport4, (struct sockaddr *)sin, sin->sin_len);
    738 		if (error == -1) {
    739 			(void)close(wport4);
    740 			wport4 = -1;
    741 			goto lprtfail;
    742 		}
    743 		error = listen(wport4, 1);
    744 		if (error == -1) {
    745 			(void)close(wport4);
    746 			wport4 = -1;
    747 			goto lprtfail;
    748 		}
    749 
    750 		/* transmit PORT */
    751 		len = sizeof(data4);
    752 		error = getsockname(wport4, (struct sockaddr *)&data4, &len);
    753 		if (error == -1) {
    754 			(void)close(wport4);
    755 			wport4 = -1;
    756 			goto lprtfail;
    757 		}
    758 		if (((struct sockaddr *)&data4)->sa_family != AF_INET) {
    759 			(void)close(wport4);
    760 			wport4 = -1;
    761 			goto lprtfail;
    762 		}
    763 		sin = (struct sockaddr_in *)&data4;
    764 		a = (char *)&sin->sin_addr;
    765 		p = (char *)&sin->sin_port;
    766 		n = snprintf(sbuf, sizeof(sbuf), "PORT %d,%d,%d,%d,%d,%d\r\n",
    767 				  UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
    768 				  UC(p[0]), UC(p[1]));
    769 		if (n < 0 || n >= (int)sizeof(sbuf))
    770 			n = 0;
    771 		if (n)
    772 			write(dst, sbuf, n);
    773 		*state = nstate;
    774 		passivemode = 0;
    775 		return n;
    776 	} else if (strcmp(cmd, "EPRT") == 0 && param) {
    777 		/*
    778 		 * EPRT -> PORT
    779 		 */
    780 		char *afp, *hostp, *portp;
    781 		struct addrinfo hints, *res;
    782 
    783 		nstate = EPRT;
    784 
    785 		(void)close(wport4);
    786 		(void)close(wport6);
    787 		(void)close(port4);
    788 		(void)close(port6);
    789 		wport4 = wport6 = port4 = port6 = -1;
    790 
    791 		if (epsvall) {
    792 			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
    793 				cmd);
    794 			if (n < 0 || n >= (int)sizeof(sbuf))
    795 				n = 0;
    796 			if (n)
    797 				write(src, sbuf, n);
    798 			return n;
    799 		}
    800 
    801 		p = param;
    802 		ch = *p++;	/* boundary character */
    803 		afp = p;
    804 		while (*p && *p != ch)
    805 			p++;
    806 		if (!*p) {
    807 eprtparamfail:
    808 			n = snprintf(sbuf, sizeof(sbuf),
    809 				"501 illegal parameter to EPRT\r\n");
    810 			if (n < 0 || n >= (int)sizeof(sbuf))
    811 				n = 0;
    812 			if (n)
    813 				write(src, sbuf, n);
    814 			return n;
    815 		}
    816 		*p++ = '\0';
    817 		hostp = p;
    818 		while (*p && *p != ch)
    819 			p++;
    820 		if (!*p)
    821 			goto eprtparamfail;
    822 		*p++ = '\0';
    823 		portp = p;
    824 		while (*p && *p != ch)
    825 			p++;
    826 		if (!*p)
    827 			goto eprtparamfail;
    828 		*p++ = '\0';
    829 
    830 		n = sscanf(afp, "%d", &af);
    831 		if (n != 1 || af != 2) {
    832 			n = snprintf(sbuf, sizeof(sbuf),
    833 				"501 unsupported address family to EPRT\r\n");
    834 			if (n < 0 || n >= (int)sizeof(sbuf))
    835 				n = 0;
    836 			if (n)
    837 				write(src, sbuf, n);
    838 			return n;
    839 		}
    840 		memset(&hints, 0, sizeof(hints));
    841 		hints.ai_family = AF_UNSPEC;
    842 		hints.ai_socktype = SOCK_STREAM;
    843 		hints.ai_protocol = IPPROTO_TCP;
    844 		error = getaddrinfo(hostp, portp, &hints, &res);
    845 		if (error) {
    846 			n = snprintf(sbuf, sizeof(sbuf),
    847 				"501 EPRT: %s\r\n", gai_strerror(error));
    848 			if (n < 0 || n >= (int)sizeof(sbuf))
    849 				n = 0;
    850 			if (n)
    851 				write(src, sbuf, n);
    852 			return n;
    853 		}
    854 		if (res->ai_next) {
    855 			n = snprintf(sbuf, sizeof(sbuf),
    856 				"501 EPRT: %s resolved to multiple addresses\r\n", hostp);
    857 			if (n < 0 || n >= (int)sizeof(sbuf))
    858 				n = 0;
    859 			if (n)
    860 				write(src, sbuf, n);
    861 			freeaddrinfo(res);
    862 			return n;
    863 		}
    864 
    865 		memcpy(&data6, res->ai_addr, res->ai_addrlen);
    866 
    867 		freeaddrinfo(res);
    868 		goto sendport;
    869 	} else if (strcmp(cmd, "LPSV") == 0 && !param) {
    870 		/*
    871 		 * LPSV -> PASV
    872 		 */
    873 		nstate = LPSV;
    874 
    875 		(void)close(wport4);
    876 		(void)close(wport6);
    877 		(void)close(port4);
    878 		(void)close(port6);
    879 		wport4 = wport6 = port4 = port6 = -1;
    880 
    881 		if (epsvall) {
    882 			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
    883 				cmd);
    884 			if (n < 0 || n >= (int)sizeof(sbuf))
    885 				n = 0;
    886 			if (n)
    887 				write(src, sbuf, n);
    888 			return n;
    889 		}
    890 
    891 		/* transmit PASV */
    892 		n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
    893 		if (n < 0 || n >= (int)sizeof(sbuf))
    894 			n = 0;
    895 		if (n)
    896 			write(dst, sbuf, n);
    897 		*state = LPSV;
    898 		passivemode = 0;	/* to be set to 1 later */
    899 		return n;
    900 	} else if (strcmp(cmd, "EPSV") == 0 && !param) {
    901 		/*
    902 		 * EPSV -> PASV
    903 		 */
    904 		(void)close(wport4);
    905 		(void)close(wport6);
    906 		(void)close(port4);
    907 		(void)close(port6);
    908 		wport4 = wport6 = port4 = port6 = -1;
    909 
    910 		n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
    911 		if (n < 0 || n >= (int)sizeof(sbuf))
    912 			n = 0;
    913 		if (n)
    914 			write(dst, sbuf, n);
    915 		*state = EPSV;
    916 		passivemode = 0;	/* to be set to 1 later */
    917 		return n;
    918 	} else if (strcmp(cmd, "EPSV") == 0 && param
    919 	 && strncasecmp(param, "ALL", 3) == 0 && isspace((unsigned char)param[3])) {
    920 		/*
    921 		 * EPSV ALL
    922 		 */
    923 		epsvall = 1;
    924 		n = snprintf(sbuf, sizeof(sbuf), "200 EPSV ALL command successful.\r\n");
    925 		if (n < 0 || n >= (int)sizeof(sbuf))
    926 			n = 0;
    927 		if (n)
    928 			write(src, sbuf, n);
    929 		return n;
    930 	} else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) {
    931 		/*
    932 		 * reject PORT/PASV
    933 		 */
    934 		n = snprintf(sbuf, sizeof(sbuf), "502 %s not implemented.\r\n", cmd);
    935 		if (n < 0 || n >= (int)sizeof(sbuf))
    936 			n = 0;
    937 		if (n)
    938 			write(src, sbuf, n);
    939 		return n;
    940 	} else if (passivemode
    941 		&& (strcmp(cmd, "STOR") == 0
    942 		 || strcmp(cmd, "STOU") == 0
    943 		 || strcmp(cmd, "RETR") == 0
    944 		 || strcmp(cmd, "LIST") == 0
    945 		 || strcmp(cmd, "NLST") == 0
    946 		 || strcmp(cmd, "APPE") == 0)) {
    947 		/*
    948 		 * commands with data transfer.  need to care about passive
    949 		 * mode data connection.
    950 		 */
    951 
    952 		if (ftp_passiveconn() < 0) {
    953 			n = snprintf(sbuf, sizeof(sbuf), "425 Cannot open data connetion\r\n");
    954 			if (n < 0 || n >= (int)sizeof(sbuf))
    955 				n = 0;
    956 			if (n)
    957 				write(src, sbuf, n);
    958 		} else {
    959 			/* simply relay the command */
    960 			write(dst, rbuf, n);
    961 		}
    962 
    963 		*state = NONE;
    964 		return n;
    965 	} else {
    966 		/* simply relay it */
    967 		*state = NONE;
    968 		write(dst, rbuf, n);
    969 		return n;
    970 	}
    971 
    972  bad:
    973 	exit_failure("%s", strerror(errno));
    974 	/*NOTREACHED*/
    975 	return 0;	/* to make gcc happy */
    976 }
    977