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