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