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