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