xref: /netbsd/usr.sbin/faithd/ftp.c (revision bf9ec67e)
1 /*	$NetBSD: ftp.c,v 1.8 2002/04/24 12:14:42 itojun Exp $	*/
2 /*	$KAME: ftp.c,v 1.14 2002/04/24 08:17:23 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 #ifdef FAITH4
65 enum state { NONE, LPRT, EPRT, PORT, LPSV, EPSV, PASV };
66 #else
67 enum state { NONE, LPRT, EPRT, LPSV, EPSV };
68 #endif
69 
70 static int ftp_activeconn __P((void));
71 static int ftp_passiveconn __P((void));
72 static int ftp_copy __P((int, int));
73 static int ftp_copyresult __P((int, int, enum state));
74 static int ftp_copycommand __P((int, int, enum state *));
75 
76 void
77 ftp_relay(int ctl6, int ctl4)
78 {
79 	fd_set readfds;
80 	int error;
81 	enum state state = NONE;
82 	struct timeval tv;
83 
84 	syslog(LOG_INFO, "starting ftp control connection");
85 
86 	for (;;) {
87 		int maxfd = 0;
88 
89 		FD_ZERO(&readfds);
90 		FD_SET(ctl4, &readfds);
91 		FD_SET(ctl6, &readfds);
92 		if (0 <= port4) {
93 			FD_SET(port4, &readfds);
94 			if (port4 > maxfd)
95 				maxfd = port4;
96 		}
97 		if (0 <= port6) {
98 			FD_SET(port6, &readfds);
99 			if (port6 > maxfd)
100 				maxfd = port6;
101 		}
102 #if 0
103 		if (0 <= wport4) {
104 			FD_SET(wport4, &readfds);
105 			if (wport4 > maxfd)
106 				maxfd = wport4;
107 		}
108 		if (0 <= wport6) {
109 			FD_SET(wport6, &readfds);
110 			if (wport6 > maxfd)
111 				maxfd = wport6;
112 		}
113 #endif
114 		tv.tv_sec = FAITH_TIMEOUT;
115 		tv.tv_usec = 0;
116 
117 		error = select(maxfd + 1, &readfds, NULL, NULL, &tv);
118 		if (error == -1)
119 			exit_failure("select: %s", strerror(errno));
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 (FD_ISSET(ctl6, &readfds)) {
130 			/*
131 			 * copy control connection from the client.
132 			 * command translation is necessary.
133 			 */
134 			error = ftp_copycommand(ctl6, ctl4, &state);
135 
136 			switch (error) {
137 			case -1:
138 				goto bad;
139 			case 0:
140 				close(ctl4);
141 				close(ctl6);
142 				exit_success("terminating ftp control connection");
143 				/*NOTREACHED*/
144 			default:
145 				break;
146 			}
147 		}
148 		if (FD_ISSET(ctl4, &readfds)) {
149 			/*
150 			 * copy control connection from the server
151 			 * translation of result code is necessary.
152 			 */
153 			error = ftp_copyresult(ctl4, ctl6, state);
154 
155 			switch (error) {
156 			case -1:
157 				goto bad;
158 			case 0:
159 				close(ctl4);
160 				close(ctl6);
161 				exit_success("terminating ftp control connection");
162 				/*NOTREACHED*/
163 			default:
164 				break;
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 	int 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 	FD_SET(wport4, &set);
239 	timeout.tv_sec = 120;
240 	timeout.tv_usec = -1;
241 	n = sizeof(data4);
242 	if (select(wport4 + 1, &set, NULL, NULL, &timeout) == 0
243 	 || (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0) {
244 		close(wport4);
245 		wport4 = -1;
246 		syslog(LOG_INFO, "active mode data connection failed");
247 		return -1;
248 	}
249 
250 	/* ask active connection to client */
251 	sa = (struct sockaddr *)&data6;
252 	port6 = socket(sa->sa_family, SOCK_STREAM, 0);
253 	if (port6 == -1) {
254 		close(port4);
255 		close(wport4);
256 		port4 = wport4 = -1;
257 		syslog(LOG_INFO, "active mode data connection failed");
258 		return -1;
259 	}
260 	error = connect(port6, sa, sa->sa_len);
261 	if (error < 0) {
262 		close(port6);
263 		close(port4);
264 		close(wport4);
265 		port6 = port4 = wport4 = -1;
266 		syslog(LOG_INFO, "active mode data connection failed");
267 		return -1;
268 	}
269 
270 	syslog(LOG_INFO, "active mode data connection established");
271 	return 0;
272 }
273 
274 static int
275 ftp_passiveconn()
276 {
277 	int n;
278 	int error;
279 	fd_set set;
280 	struct timeval timeout;
281 	struct sockaddr *sa;
282 
283 	/* get passive connection from client */
284 	FD_ZERO(&set);
285 	FD_SET(wport6, &set);
286 	timeout.tv_sec = 120;
287 	timeout.tv_usec = 0;
288 	n = sizeof(data6);
289 	if (select(wport6 + 1, &set, NULL, NULL, &timeout) == 0
290 	 || (port6 = accept(wport6, (struct sockaddr *)&data6, &n)) < 0) {
291 		close(wport6);
292 		wport6 = -1;
293 		syslog(LOG_INFO, "passive mode data connection failed");
294 		return -1;
295 	}
296 
297 	/* ask passive connection to server */
298 	sa = (struct sockaddr *)&data4;
299 	port4 = socket(sa->sa_family, SOCK_STREAM, 0);
300 	if (port4 == -1) {
301 		close(wport6);
302 		close(port6);
303 		wport6 = port6 = -1;
304 		syslog(LOG_INFO, "passive mode data connection failed");
305 		return -1;
306 	}
307 	error = connect(port4, sa, sa->sa_len);
308 	if (error < 0) {
309 		close(wport6);
310 		close(port4);
311 		close(port6);
312 		wport6 = port4 = port6 = -1;
313 		syslog(LOG_INFO, "passive mode data connection failed");
314 		return -1;
315 	}
316 
317 	syslog(LOG_INFO, "passive mode data connection established");
318 	return 0;
319 }
320 
321 static int
322 ftp_copy(int src, int dst)
323 {
324 	int error, atmark;
325 	int n;
326 
327 	/* OOB data handling */
328 	error = ioctl(src, SIOCATMARK, &atmark);
329 	if (error != -1 && atmark == 1) {
330 		n = read(src, rbuf, 1);
331 		if (n == -1)
332 			goto bad;
333 		send(dst, rbuf, n, MSG_OOB);
334 #if 0
335 		n = read(src, rbuf, sizeof(rbuf));
336 		if (n == -1)
337 			goto bad;
338 		write(dst, rbuf, n);
339 		return n;
340 #endif
341 	}
342 
343 	n = read(src, rbuf, sizeof(rbuf));
344 	switch (n) {
345 	case -1:
346 	case 0:
347 		return n;
348 	default:
349 		write(dst, rbuf, n);
350 		return n;
351 	}
352 
353  bad:
354 	exit_failure("%s", strerror(errno));
355 	/*NOTREACHED*/
356 	return 0;	/* to make gcc happy */
357 }
358 
359 static int
360 ftp_copyresult(int src, int dst, enum state state)
361 {
362 	int error, atmark;
363 	int n;
364 	char *param;
365 	int code;
366 	char *a, *p;
367 	int i;
368 
369 	/* OOB data handling */
370 	error = ioctl(src, SIOCATMARK, &atmark);
371 	if (error != -1 && atmark == 1) {
372 		n = read(src, rbuf, 1);
373 		if (n == -1)
374 			goto bad;
375 		send(dst, rbuf, n, MSG_OOB);
376 #if 0
377 		n = read(src, rbuf, sizeof(rbuf));
378 		if (n == -1)
379 			goto bad;
380 		write(dst, rbuf, n);
381 		return n;
382 #endif
383 	}
384 
385 	n = read(src, rbuf, sizeof(rbuf));
386 	if (n <= 0)
387 		return n;
388 	rbuf[n] = '\0';
389 
390 	/*
391 	 * parse argument
392 	 */
393 	p = rbuf;
394 	for (i = 0; i < 3; i++) {
395 		if (!isdigit(*p)) {
396 			/* invalid reply */
397 			write(dst, rbuf, n);
398 			return n;
399 		}
400 		p++;
401 	}
402 	if (!isspace(*p)) {
403 		/* invalid reply */
404 		write(dst, rbuf, n);
405 		return n;
406 	}
407 	code = atoi(rbuf);
408 	param = p;
409 	/* param points to first non-command token, if any */
410 	while (*param && isspace(*param))
411 		param++;
412 	if (!*param)
413 		param = NULL;
414 
415 	switch (state) {
416 	case NONE:
417 		if (!passivemode && rbuf[0] == '1') {
418 			if (ftp_activeconn() < 0) {
419 				n = snprintf(rbuf, sizeof(rbuf),
420 					"425 Cannot open data connetion\r\n");
421 				if (n < 0 || n >= sizeof(rbuf))
422 					n = 0;
423 			}
424 		}
425 		if (n)
426 			write(dst, rbuf, n);
427 		return n;
428 	case LPRT:
429 	case EPRT:
430 		/* expecting "200 PORT command successful." */
431 		if (code == 200) {
432 			p = strstr(rbuf, "PORT");
433 			if (p) {
434 				p[0] = (state == LPRT) ? 'L' : 'E';
435 				p[1] = 'P';
436 			}
437 		} else {
438 			close(wport4);
439 			wport4 = -1;
440 		}
441 		write(dst, rbuf, n);
442 		return n;
443 #ifdef FAITH4
444 	case PORT:
445 		/* expecting "200 EPRT command successful." */
446 		if (code == 200) {
447 			p = strstr(rbuf, "EPRT");
448 			if (p) {
449 				p[0] = 'P';
450 				p[1] = 'O';
451 			}
452 		} else {
453 			close(wport4);
454 			wport4 = -1;
455 		}
456 		write(dst, rbuf, n);
457 		return n;
458 #endif
459 	case LPSV:
460 	case EPSV:
461 		/*
462 		 * expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)"
463 		 * (in some cases result comes without paren)
464 		 */
465 		if (code != 227) {
466 passivefail0:
467 			close(wport6);
468 			wport6 = -1;
469 			write(dst, rbuf, n);
470 			return n;
471 		}
472 
473 	    {
474 		unsigned int ho[4], po[2];
475 		struct sockaddr_in *sin;
476 		struct sockaddr_in6 *sin6;
477 		u_short port;
478 
479 		/*
480 		 * PASV result -> LPSV/EPSV result
481 		 */
482 		p = param;
483 		while (*p && *p != '(' && !isdigit(*p))	/*)*/
484 			p++;
485 		if (!*p)
486 			goto passivefail0;	/*XXX*/
487 		if (*p == '(')	/*)*/
488 			p++;
489 		n = sscanf(p, "%u,%u,%u,%u,%u,%u",
490 			&ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
491 		if (n != 6)
492 			goto passivefail0;	/*XXX*/
493 
494 		/* keep PORT parameter */
495 		memset(&data4, 0, sizeof(data4));
496 		sin = (struct sockaddr_in *)&data4;
497 		sin->sin_len = sizeof(*sin);
498 		sin->sin_family = AF_INET;
499 		sin->sin_addr.s_addr = 0;
500 		for (n = 0; n < 4; n++) {
501 			sin->sin_addr.s_addr |=
502 				htonl((ho[n] & 0xff) << ((3 - n) * 8));
503 		}
504 		sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
505 
506 		/* get ready for passive data connection */
507 		memset(&data6, 0, sizeof(data6));
508 		sin6 = (struct sockaddr_in6 *)&data6;
509 		sin6->sin6_len = sizeof(*sin6);
510 		sin6->sin6_family = AF_INET6;
511 		wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0);
512 		if (wport6 == -1) {
513 passivefail:
514 			n = snprintf(sbuf, sizeof(sbuf),
515 				"500 could not translate from PASV\r\n");
516 			if (n < 0 || n >= sizeof(sbuf))
517 			if (n)
518 				write(src, sbuf, n);
519 			return n;
520 		}
521 #ifdef IPV6_FAITH
522 	    {
523 		int on = 1;
524 		error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH,
525 			&on, sizeof(on));
526 		if (error == -1)
527 			exit_failure("setsockopt(IPV6_FAITH): %s", strerror(errno));
528 	    }
529 #endif
530 		error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len);
531 		if (error == -1) {
532 			close(wport6);
533 			wport6 = -1;
534 			goto passivefail;
535 		}
536 		error = listen(wport6, 1);
537 		if (error == -1) {
538 			close(wport6);
539 			wport6 = -1;
540 			goto passivefail;
541 		}
542 
543 		/* transmit LPSV or EPSV */
544 		/*
545 		 * addr from dst, port from wport6
546 		 */
547 		n = sizeof(data6);
548 		error = getsockname(wport6, (struct sockaddr *)&data6, &n);
549 		if (error == -1) {
550 			close(wport6);
551 			wport6 = -1;
552 			goto passivefail;
553 		}
554 		sin6 = (struct sockaddr_in6 *)&data6;
555 		port = sin6->sin6_port;
556 
557 		n = sizeof(data6);
558 		error = getsockname(dst, (struct sockaddr *)&data6, &n);
559 		if (error == -1) {
560 			close(wport6);
561 			wport6 = -1;
562 			goto passivefail;
563 		}
564 		sin6 = (struct sockaddr_in6 *)&data6;
565 		sin6->sin6_port = port;
566 
567 		if (state == LPSV) {
568 			a = (char *)&sin6->sin6_addr;
569 			p = (char *)&sin6->sin6_port;
570 			n = snprintf(sbuf, sizeof(sbuf),
571 "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",
572 				6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
573 				UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
574 				UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
575 				UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
576 				2, UC(p[0]), UC(p[1]));
577 			if (n < 0 || n >= sizeof(sbuf))
578 				n = 0;
579 			if (n)
580 				write(dst, sbuf, n);
581 			passivemode = 1;
582 			return n;
583 		} else {
584 			n = snprintf(sbuf, sizeof(sbuf),
585 "229 Entering Extended Passive Mode (|||%d|)\r\n",
586 				ntohs(sin6->sin6_port));
587 			if (n < 0 || n >= sizeof(sbuf))
588 				n = 0;
589 			if (n)
590 				write(dst, sbuf, n);
591 			passivemode = 1;
592 			return n;
593 		}
594 	    }
595 #ifdef FAITH4
596 	case PASV:
597 		/* expecting "229 Entering Extended Passive Mode (|||x|)" */
598 		if (code != 229) {
599 passivefail1:
600 			close(wport6);
601 			wport6 = -1;
602 			write(dst, rbuf, n);
603 			return n;
604 		}
605 
606 	    {
607 		u_short port;
608 		struct sockaddr_in *sin;
609 		struct sockaddr_in6 *sin6;
610 
611 		/*
612 		 * EPSV result -> PORT result
613 		 */
614 		p = param;
615 		while (*p && *p != '(')	/*)*/
616 			p++;
617 		if (!*p)
618 			goto passivefail1;	/*XXX*/
619 		p++;
620 		n = sscanf(p, "|||%hu|", &port);
621 		if (n != 1)
622 			goto passivefail1;	/*XXX*/
623 
624 		/* keep EPRT parameter */
625 		n = sizeof(data4);
626 		error = getpeername(src, (struct sockaddr *)&data4, &n);
627 		if (error == -1)
628 			goto passivefail1;	/*XXX*/
629 		sin6 = (struct sockaddr_in6 *)&data4;
630 		sin6->sin6_port = htons(port);
631 
632 		/* get ready for passive data connection */
633 		memset(&data6, 0, sizeof(data6));
634 		sin = (struct sockaddr_in *)&data6;
635 		sin->sin_len = sizeof(*sin);
636 		sin->sin_family = AF_INET;
637 		wport6 = socket(sin->sin_family, SOCK_STREAM, 0);
638 		if (wport6 == -1) {
639 passivefail2:
640 			n = snprintf(sbuf, sizeof(sbuf),
641 				"500 could not translate from EPSV\r\n");
642 			if (n < 0 || n >= sizeof(sbuf))
643 				n = 0;
644 			if (n)
645 				write(src, sbuf, n);
646 			return n;
647 		}
648 #ifdef IP_FAITH
649 	    {
650 		int on = 1;
651 		error = setsockopt(wport6, IPPROTO_IP, IP_FAITH,
652 			&on, sizeof(on));
653 		if (error == -1)
654 			exit_error("setsockopt(IP_FAITH): %s", strerror(errno));
655 	    }
656 #endif
657 		error = bind(wport6, (struct sockaddr *)sin, sin->sin_len);
658 		if (error == -1) {
659 			close(wport6);
660 			wport6 = -1;
661 			goto passivefail2;
662 		}
663 		error = listen(wport6, 1);
664 		if (error == -1) {
665 			close(wport6);
666 			wport6 = -1;
667 			goto passivefail2;
668 		}
669 
670 		/* transmit PORT */
671 		/*
672 		 * addr from dst, port from wport6
673 		 */
674 		n = sizeof(data6);
675 		error = getsockname(wport6, (struct sockaddr *)&data6, &n);
676 		if (error == -1) {
677 			close(wport6);
678 			wport6 = -1;
679 			goto passivefail2;
680 		}
681 		sin = (struct sockaddr_in *)&data6;
682 		port = sin->sin_port;
683 
684 		n = sizeof(data6);
685 		error = getsockname(dst, (struct sockaddr *)&data6, &n);
686 		if (error == -1) {
687 			close(wport6);
688 			wport6 = -1;
689 			goto passivefail2;
690 		}
691 		sin = (struct sockaddr_in *)&data6;
692 		sin->sin_port = port;
693 
694 		{
695 			char *a, *p;
696 
697 			a = (char *)&sin->sin_addr;
698 			p = (char *)&sin->sin_port;
699 			n = snprintf(sbuf, sizeof(sbuf),
700 "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
701 				UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
702 				UC(p[0]), UC(p[1]));
703 			if (n < 0 || n >= sizeof(sbuf))
704 				n = 0;
705 			if (n)
706 				write(dst, sbuf, n);
707 			passivemode = 1;
708 			return n;
709 		}
710 	    }
711 #endif /* FAITH4 */
712 	}
713 
714  bad:
715 	exit_failure("%s", strerror(errno));
716 	/*NOTREACHED*/
717 	return 0;	/* to make gcc happy */
718 }
719 
720 static int
721 ftp_copycommand(int src, int dst, enum state *state)
722 {
723 	int error, atmark;
724 	int n;
725 	unsigned int af, hal, ho[16], pal, po[2];
726 	char *a, *p, *q;
727 	char cmd[5], *param;
728 	struct sockaddr_in *sin;
729 	struct sockaddr_in6 *sin6;
730 	enum state nstate;
731 	char ch;
732 	int i;
733 
734 	/* OOB data handling */
735 	error = ioctl(src, SIOCATMARK, &atmark);
736 	if (error != -1 && atmark == 1) {
737 		n = read(src, rbuf, 1);
738 		if (n == -1)
739 			goto bad;
740 		send(dst, rbuf, n, MSG_OOB);
741 #if 0
742 		n = read(src, rbuf, sizeof(rbuf));
743 		if (n == -1)
744 			goto bad;
745 		write(dst, rbuf, n);
746 		return n;
747 #endif
748 	}
749 
750 	n = read(src, rbuf, sizeof(rbuf));
751 	if (n <= 0)
752 		return n;
753 	rbuf[n] = '\0';
754 
755 	if (n < 4) {
756 		write(dst, rbuf, n);
757 		return n;
758 	}
759 
760 	/*
761 	 * parse argument
762 	 */
763 	p = rbuf;
764 	q = cmd;
765 	for (i = 0; i < 4; i++) {
766 		if (!isalpha(*p)) {
767 			/* invalid command */
768 			write(dst, rbuf, n);
769 			return n;
770 		}
771 		*q++ = islower(*p) ? toupper(*p) : *p;
772 		p++;
773 	}
774 	if (!isspace(*p)) {
775 		/* invalid command */
776 		write(dst, rbuf, n);
777 		return n;
778 	}
779 	*q = '\0';
780 	param = p;
781 	/* param points to first non-command token, if any */
782 	while (*param && isspace(*param))
783 		param++;
784 	if (!*param)
785 		param = NULL;
786 
787 	*state = NONE;
788 
789 	if (strcmp(cmd, "LPRT") == 0 && param) {
790 		/*
791 		 * LPRT -> PORT
792 		 */
793 		nstate = LPRT;
794 
795 		close(wport4);
796 		close(wport6);
797 		close(port4);
798 		close(port6);
799 		wport4 = wport6 = port4 = port6 = -1;
800 
801 		if (epsvall) {
802 			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
803 				cmd);
804 			if (n < 0 || n >= sizeof(sbuf))
805 				n = 0;
806 			if (n)
807 				write(src, sbuf, n);
808 			return n;
809 		}
810 
811 		n = sscanf(param,
812 "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u",
813 			      &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3],
814 			      &ho[4], &ho[5], &ho[6], &ho[7],
815 			      &ho[8], &ho[9], &ho[10], &ho[11],
816 			      &ho[12], &ho[13], &ho[14], &ho[15],
817 			      &pal, &po[0], &po[1]);
818 		if (n != 21 || af != 6 || hal != 16|| pal != 2) {
819 			n = snprintf(sbuf, sizeof(sbuf),
820 				"501 illegal parameter to LPRT\r\n");
821 			if (n < 0 || n >= sizeof(sbuf))
822 				n = 0;
823 			if (n)
824 				write(src, sbuf, n);
825 			return n;
826 		}
827 
828 		/* keep LPRT parameter */
829 		memset(&data6, 0, sizeof(data6));
830 		sin6 = (struct sockaddr_in6 *)&data6;
831 		sin6->sin6_len = sizeof(*sin6);
832 		sin6->sin6_family = AF_INET6;
833 		for (n = 0; n < 16; n++)
834 			sin6->sin6_addr.s6_addr[n] = ho[n];
835 		sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
836 
837 sendport:
838 		/* get ready for active data connection */
839 		n = sizeof(data4);
840 		error = getsockname(dst, (struct sockaddr *)&data4, &n);
841 		if (error == -1) {
842 lprtfail:
843 			n = snprintf(sbuf, sizeof(sbuf),
844 				"500 could not translate to PORT\r\n");
845 			if (n < 0 || n >= sizeof(sbuf))
846 				n = 0;
847 			if (n)
848 				write(src, sbuf, n);
849 			return n;
850 		}
851 		if (((struct sockaddr *)&data4)->sa_family != AF_INET)
852 			goto lprtfail;
853 		sin = (struct sockaddr_in *)&data4;
854 		sin->sin_port = 0;
855 		wport4 = socket(sin->sin_family, SOCK_STREAM, 0);
856 		if (wport4 == -1)
857 			goto lprtfail;
858 		error = bind(wport4, (struct sockaddr *)sin, sin->sin_len);
859 		if (error == -1) {
860 			close(wport4);
861 			wport4 = -1;
862 			goto lprtfail;
863 		}
864 		error = listen(wport4, 1);
865 		if (error == -1) {
866 			close(wport4);
867 			wport4 = -1;
868 			goto lprtfail;
869 		}
870 
871 		/* transmit PORT */
872 		n = sizeof(data4);
873 		error = getsockname(wport4, (struct sockaddr *)&data4, &n);
874 		if (error == -1) {
875 			close(wport4);
876 			wport4 = -1;
877 			goto lprtfail;
878 		}
879 		if (((struct sockaddr *)&data4)->sa_family != AF_INET) {
880 			close(wport4);
881 			wport4 = -1;
882 			goto lprtfail;
883 		}
884 		sin = (struct sockaddr_in *)&data4;
885 		a = (char *)&sin->sin_addr;
886 		p = (char *)&sin->sin_port;
887 		n = snprintf(sbuf, sizeof(sbuf), "PORT %d,%d,%d,%d,%d,%d\r\n",
888 				  UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
889 				  UC(p[0]), UC(p[1]));
890 		if (n < 0 || n >= sizeof(sbuf))
891 			n = 0;
892 		if (n)
893 			write(dst, sbuf, n);
894 		*state = nstate;
895 		passivemode = 0;
896 		return n;
897 	} else if (strcmp(cmd, "EPRT") == 0 && param) {
898 		/*
899 		 * EPRT -> PORT
900 		 */
901 		char *afp, *hostp, *portp;
902 		struct addrinfo hints, *res;
903 
904 		nstate = EPRT;
905 
906 		close(wport4);
907 		close(wport6);
908 		close(port4);
909 		close(port6);
910 		wport4 = wport6 = port4 = port6 = -1;
911 
912 		if (epsvall) {
913 			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
914 				cmd);
915 			if (n < 0 || n >= sizeof(sbuf))
916 				n = 0;
917 			if (n)
918 				write(src, sbuf, n);
919 			return n;
920 		}
921 
922 		p = param;
923 		ch = *p++;	/* boundary character */
924 		afp = p;
925 		while (*p && *p != ch)
926 			p++;
927 		if (!*p) {
928 eprtparamfail:
929 			n = snprintf(sbuf, sizeof(sbuf),
930 				"501 illegal parameter to EPRT\r\n");
931 			if (n < 0 || n >= sizeof(sbuf))
932 				n = 0;
933 			if (n)
934 				write(src, sbuf, n);
935 			return n;
936 		}
937 		*p++ = '\0';
938 		hostp = p;
939 		while (*p && *p != ch)
940 			p++;
941 		if (!*p)
942 			goto eprtparamfail;
943 		*p++ = '\0';
944 		portp = p;
945 		while (*p && *p != ch)
946 			p++;
947 		if (!*p)
948 			goto eprtparamfail;
949 		*p++ = '\0';
950 
951 		n = sscanf(afp, "%d", &af);
952 		if (n != 1 || af != 2) {
953 			n = snprintf(sbuf, sizeof(sbuf),
954 				"501 unsupported address family to EPRT\r\n");
955 			if (n < 0 || n >= sizeof(sbuf))
956 				n = 0;
957 			if (n)
958 				write(src, sbuf, n);
959 			return n;
960 		}
961 		memset(&hints, 0, sizeof(hints));
962 		hints.ai_family = AF_UNSPEC;
963 		hints.ai_socktype = SOCK_STREAM;
964 		error = getaddrinfo(hostp, portp, &hints, &res);
965 		if (error) {
966 			n = snprintf(sbuf, sizeof(sbuf),
967 				"501 EPRT: %s\r\n", gai_strerror(error));
968 			if (n < 0 || n >= sizeof(sbuf))
969 				n = 0;
970 			if (n)
971 				write(src, sbuf, n);
972 			return n;
973 		}
974 		if (res->ai_next) {
975 			n = snprintf(sbuf, sizeof(sbuf),
976 				"501 EPRT: %s resolved to multiple addresses\r\n", hostp);
977 			if (n < 0 || n >= sizeof(sbuf))
978 				n = 0;
979 			if (n)
980 				write(src, sbuf, n);
981 			return n;
982 		}
983 
984 		memcpy(&data6, res->ai_addr, res->ai_addrlen);
985 
986 		goto sendport;
987 	} else if (strcmp(cmd, "LPSV") == 0 && !param) {
988 		/*
989 		 * LPSV -> PASV
990 		 */
991 		nstate = LPSV;
992 
993 		close(wport4);
994 		close(wport6);
995 		close(port4);
996 		close(port6);
997 		wport4 = wport6 = port4 = port6 = -1;
998 
999 		if (epsvall) {
1000 			n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
1001 				cmd);
1002 			if (n < 0 || n >= sizeof(sbuf))
1003 				n = 0;
1004 			if (n)
1005 				write(src, sbuf, n);
1006 			return n;
1007 		}
1008 
1009 		/* transmit PASV */
1010 		n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
1011 		if (n < 0 || n >= sizeof(sbuf))
1012 			n = 0;
1013 		if (n)
1014 			write(dst, sbuf, n);
1015 		*state = LPSV;
1016 		passivemode = 0;	/* to be set to 1 later */
1017 		return n;
1018 	} else if (strcmp(cmd, "EPSV") == 0 && !param) {
1019 		/*
1020 		 * EPSV -> PASV
1021 		 */
1022 		close(wport4);
1023 		close(wport6);
1024 		close(port4);
1025 		close(port6);
1026 		wport4 = wport6 = port4 = port6 = -1;
1027 
1028 		n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
1029 		if (n < 0 || n >= sizeof(sbuf))
1030 			n = 0;
1031 		if (n)
1032 			write(dst, sbuf, n);
1033 		*state = EPSV;
1034 		passivemode = 0;	/* to be set to 1 later */
1035 		return n;
1036 	} else if (strcmp(cmd, "EPSV") == 0 && param
1037 	 && strncasecmp(param, "ALL", 3) == 0 && isspace(param[3])) {
1038 		/*
1039 		 * EPSV ALL
1040 		 */
1041 		epsvall = 1;
1042 		n = snprintf(sbuf, sizeof(sbuf), "200 EPSV ALL command successful.\r\n");
1043 		if (n < 0 || n >= sizeof(sbuf))
1044 			n = 0;
1045 		if (n)
1046 			write(src, sbuf, n);
1047 		return n;
1048 #ifdef FAITH4
1049 	} else if (strcmp(cmd, "PORT") == 0 && param) {
1050 		/*
1051 		 * PORT -> EPRT
1052 		 */
1053 		char host[NI_MAXHOST], serv[NI_MAXSERV];
1054 
1055 		nstate = PORT;
1056 
1057 		close(wport4);
1058 		close(wport6);
1059 		close(port4);
1060 		close(port6);
1061 		wport4 = wport6 = port4 = port6 = -1;
1062 
1063 		p = param;
1064 		n = sscanf(p, "%u,%u,%u,%u,%u,%u",
1065 			&ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
1066 		if (n != 6) {
1067 			n = snprintf(sbuf, sizeof(sbuf),
1068 				"501 illegal parameter to PORT\r\n");
1069 			if (n < 0 || n >= sizeof(sbuf))
1070 				n = 0;
1071 			if (n)
1072 				write(src, sbuf, n);
1073 			return n;
1074 		}
1075 
1076 		memset(&data6, 0, sizeof(data6));
1077 		sin = (struct sockaddr_in *)&data6;
1078 		sin->sin_len = sizeof(*sin);
1079 		sin->sin_family = AF_INET;
1080 		sin->sin_addr.s_addr = htonl(
1081 			((ho[0] & 0xff) << 24) | ((ho[1] & 0xff) << 16) |
1082 			((ho[2] & 0xff) << 8) | (ho[3] & 0xff));
1083 		sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
1084 
1085 		/* get ready for active data connection */
1086 		n = sizeof(data4);
1087 		error = getsockname(dst, (struct sockaddr *)&data4, &n);
1088 		if (error == -1) {
1089 portfail:
1090 			n = snprintf(sbuf, sizeof(sbuf),
1091 				"500 could not translate to EPRT\r\n");
1092 			if (n < 0 || n >= sizeof(sbuf))
1093 				n = 0;
1094 			if (n)
1095 				write(src, sbuf, n);
1096 			return n;
1097 		}
1098 		if (((struct sockaddr *)&data4)->sa_family != AF_INET6)
1099 			goto portfail;
1100 
1101 		((struct sockaddr_in6 *)&data4)->sin6_port = 0;
1102 		sa = (struct sockaddr *)&data4;
1103 		wport4 = socket(sa->sa_family, SOCK_STREAM, 0);
1104 		if (wport4 == -1)
1105 			goto portfail;
1106 		error = bind(wport4, sa, sa->sa_len);
1107 		if (error == -1) {
1108 			close(wport4);
1109 			wport4 = -1;
1110 			goto portfail;
1111 		}
1112 		error = listen(wport4, 1);
1113 		if (error == -1) {
1114 			close(wport4);
1115 			wport4 = -1;
1116 			goto portfail;
1117 		}
1118 
1119 		/* transmit EPRT */
1120 		n = sizeof(data4);
1121 		error = getsockname(wport4, (struct sockaddr *)&data4, &n);
1122 		if (error == -1) {
1123 			close(wport4);
1124 			wport4 = -1;
1125 			goto portfail;
1126 		}
1127 		af = 2;
1128 		sa = (struct sockaddr *)&data4;
1129 		if (getnameinfo(sa, sa->sa_len, host, sizeof(host),
1130 			serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV)) {
1131 			close(wport4);
1132 			wport4 = -1;
1133 			goto portfail;
1134 		}
1135 		n = snprintf(sbuf, sizeof(sbuf), "EPRT |%d|%s|%s|\r\n", af, host, serv);
1136 		if (n < 0 || n >= sizeof(sbuf))
1137 			n = 0;
1138 		if (n)
1139 			write(dst, sbuf, n);
1140 		*state = nstate;
1141 		passivemode = 0;
1142 		return n;
1143 	} else if (strcmp(cmd, "PASV") == 0 && !param) {
1144 		/*
1145 		 * PASV -> EPSV
1146 		 */
1147 
1148 		nstate = PASV;
1149 
1150 		close(wport4);
1151 		close(wport6);
1152 		close(port4);
1153 		close(port6);
1154 		wport4 = wport6 = port4 = port6 = -1;
1155 
1156 		/* transmit EPSV */
1157 		n = snprintf(sbuf, sizeof(sbuf), "EPSV\r\n");
1158 		if (n < 0 || n >= sizeof(sbuf))
1159 			n = 0;
1160 		if (n)
1161 			write(dst, sbuf, n);
1162 		*state = PASV;
1163 		passivemode = 0;	/* to be set to 1 later */
1164 		return n;
1165 #else /* FAITH4 */
1166 	} else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) {
1167 		/*
1168 		 * reject PORT/PASV
1169 		 */
1170 		n = snprintf(sbuf, sizeof(sbuf), "502 %s not implemented.\r\n", cmd);
1171 		if (n < 0 || n >= sizeof(sbuf))
1172 			n = 0;
1173 		if (n)
1174 			write(src, sbuf, n);
1175 		return n;
1176 #endif /* FAITH4 */
1177 	} else if (passivemode
1178 		&& (strcmp(cmd, "STOR") == 0
1179 		 || strcmp(cmd, "STOU") == 0
1180 		 || strcmp(cmd, "RETR") == 0
1181 		 || strcmp(cmd, "LIST") == 0
1182 		 || strcmp(cmd, "NLST") == 0
1183 		 || strcmp(cmd, "APPE") == 0)) {
1184 		/*
1185 		 * commands with data transfer.  need to care about passive
1186 		 * mode data connection.
1187 		 */
1188 
1189 		if (ftp_passiveconn() < 0) {
1190 			n = snprintf(sbuf, sizeof(sbuf), "425 Cannot open data connetion\r\n");
1191 			if (n < 0 || n >= sizeof(sbuf))
1192 				n = 0;
1193 			if (n)
1194 				write(src, sbuf, n);
1195 		} else {
1196 			/* simply relay the command */
1197 			write(dst, rbuf, n);
1198 		}
1199 
1200 		*state = NONE;
1201 		return n;
1202 	} else {
1203 		/* simply relay it */
1204 		*state = NONE;
1205 		write(dst, rbuf, n);
1206 		return n;
1207 	}
1208 
1209  bad:
1210 	exit_failure("%s", strerror(errno));
1211 	/*NOTREACHED*/
1212 	return 0;	/* to make gcc happy */
1213 }
1214