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