Expand PMF_FN_* macros.
[netbsd-mini2440.git] / usr.sbin / faithd / ftp.c
blob070da3bbb27a58ea5c05bcb08e30fe23115ab279
1 /* $NetBSD: ftp.c,v 1.17 2006/05/24 21:47:25 christos Exp $ */
2 /* $KAME: ftp.c,v 1.23 2003/08/19 21:20:33 itojun Exp $ */
4 /*
5 * Copyright (C) 1997 and 1998 WIDE Project.
6 * All rights reserved.
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.
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.
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>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <syslog.h>
43 #include <unistd.h>
44 #include <poll.h>
45 #include <errno.h>
46 #include <ctype.h>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
50 #include <netdb.h>
52 #include "faithd.h"
54 static char rbuf[MSS];
55 static char sbuf[MSS];
56 static int passivemode = 0;
57 static int wport4 = -1; /* listen() to active */
58 static int wport6 = -1; /* listen() to passive */
59 static int port4 = -1; /* active: inbound passive: outbound */
60 static int port6 = -1; /* active: outbound passive: inbound */
61 static struct sockaddr_storage data4; /* server data address */
62 static struct sockaddr_storage data6; /* client data address */
63 static int epsvall = 0;
65 enum state { NONE, LPRT, EPRT, LPSV, EPSV };
67 static int ftp_activeconn __P((void));
68 static int ftp_passiveconn __P((void));
69 static int ftp_copy __P((int, int));
70 static int ftp_copyresult __P((int, int, enum state));
71 static int ftp_copycommand __P((int, int, enum state *));
73 void
74 ftp_relay(int ctl6, int ctl4)
76 struct pollfd pfd[6];
77 int error;
78 enum state state = NONE;
79 struct timeval tv;
81 syslog(LOG_INFO, "starting ftp control connection");
83 for (;;) {
84 pfd[0].fd = ctl4;
85 pfd[0].events = POLLIN;
86 pfd[1].fd = ctl6;
87 pfd[1].events = POLLIN;
88 if (0 <= port4) {
89 pfd[2].fd = port4;
90 pfd[2].events = POLLIN;
91 } else
92 pfd[2].fd = -1;
93 if (0 <= port6) {
94 pfd[3].fd = port6;
95 pfd[3].events = POLLIN;
96 } else
97 pfd[3].fd = -1;
98 #if 0
99 if (0 <= wport4) {
100 pfd[4].fd = wport4;
101 pfd[4].events = POLLIN;
102 } else
103 pfd[4].fd = -1;
104 if (0 <= wport6) {
105 pfd[5].fd = wport4;
106 pfd[5].events = POLLIN;
107 } else
108 pfd[5].fd = -1;
109 #else
110 pfd[4].fd = pfd[5].fd = -1;
111 pfd[4].events = pfd[5].events = 0;
112 #endif
113 tv.tv_sec = FAITH_TIMEOUT;
114 tv.tv_usec = 0;
116 error = poll(pfd, sizeof(pfd)/sizeof(pfd[0]), tv.tv_sec * 1000);
117 if (error == -1) {
118 exit_failure("poll: %s", strerror(errno));
120 else if (error == 0)
121 exit_failure("connection timeout");
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.
129 if (pfd[1].revents & POLLIN)
132 * copy control connection from the client.
133 * command translation is necessary.
135 error = ftp_copycommand(ctl6, ctl4, &state);
137 if (error < 0)
138 goto bad;
139 else if (error == 0) {
140 (void)close(ctl4);
141 (void)close(ctl6);
142 exit_success("terminating ftp control connection");
143 /*NOTREACHED*/
146 if (pfd[0].revents & POLLIN)
149 * copy control connection from the server
150 * translation of result code is necessary.
152 error = ftp_copyresult(ctl4, ctl6, state);
154 if (error < 0)
155 goto bad;
156 else if (error == 0) {
157 (void)close(ctl4);
158 (void)close(ctl6);
159 exit_success("terminating ftp control connection");
160 /*NOTREACHED*/
163 if (0 <= port4 && 0 <= port6 && (pfd[2].revents & POLLIN))
166 * copy data connection.
167 * no special treatment necessary.
169 if (pfd[2].revents & POLLIN)
170 error = ftp_copy(port4, port6);
171 switch (error) {
172 case -1:
173 goto bad;
174 case 0:
175 if (port4 >= 0) {
176 close(port4);
177 port4 = -1;
179 if (port6 >= 0) {
180 close(port6);
181 port6 = -1;
183 syslog(LOG_INFO, "terminating data connection");
184 break;
185 default:
186 break;
189 if (0 <= port4 && 0 <= port6 && (pfd[3].revents & POLLIN))
192 * copy data connection.
193 * no special treatment necessary.
195 if (pfd[3].revents & POLLIN)
196 error = ftp_copy(port6, port4);
197 switch (error) {
198 case -1:
199 goto bad;
200 case 0:
201 if (port4 >= 0) {
202 close(port4);
203 port4 = -1;
205 if (port6 >= 0) {
206 close(port6);
207 port6 = -1;
209 syslog(LOG_INFO, "terminating data connection");
210 break;
211 default:
212 break;
215 #if 0
216 if (wport4 && (pfd[4].revents & POLLIN))
219 * establish active data connection from the server.
221 ftp_activeconn();
223 if (wport4 && (pfd[5].revents & POLLIN))
226 * establish passive data connection from the client.
228 ftp_passiveconn();
230 #endif
233 bad:
234 exit_failure("%s", strerror(errno));
237 static int
238 ftp_activeconn()
240 socklen_t n;
241 int error;
242 struct pollfd pfd[1];
243 struct timeval timeout;
244 struct sockaddr *sa;
246 /* get active connection from server */
247 pfd[0].fd = wport4;
248 pfd[0].events = POLLIN;
249 timeout.tv_sec = 120;
250 timeout.tv_usec = 0;
251 n = sizeof(data4);
252 if (poll(pfd, sizeof(pfd)/sizeof(pfd[0]), timeout.tv_sec * 1000) == 0 ||
253 (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0)
255 (void)close(wport4);
256 wport4 = -1;
257 syslog(LOG_INFO, "active mode data connection failed");
258 return -1;
261 /* ask active connection to client */
262 sa = (struct sockaddr *)&data6;
263 port6 = socket(sa->sa_family, SOCK_STREAM, 0);
264 if (port6 == -1) {
265 (void)close(port4);
266 (void)close(wport4);
267 port4 = wport4 = -1;
268 syslog(LOG_INFO, "active mode data connection failed");
269 return -1;
271 error = connect(port6, sa, sa->sa_len);
272 if (error < 0) {
273 (void)close(port6);
274 (void)close(port4);
275 (void)close(wport4);
276 port6 = port4 = wport4 = -1;
277 syslog(LOG_INFO, "active mode data connection failed");
278 return -1;
281 syslog(LOG_INFO, "active mode data connection established");
282 return 0;
285 static int
286 ftp_passiveconn()
288 socklen_t len;
289 int error;
290 struct pollfd pfd[1];
291 struct timeval timeout;
292 struct sockaddr *sa;
294 /* get passive connection from client */
295 pfd[0].fd = wport6;
296 pfd[0].events = POLLIN;
297 timeout.tv_sec = 120;
298 timeout.tv_usec = 0;
299 len = sizeof(data6);
300 if (poll(pfd, sizeof(pfd)/sizeof(pfd[0]), timeout.tv_sec * 1000) == 0 ||
301 (port6 = accept(wport6, (struct sockaddr *)&data6, &len)) < 0)
303 (void)close(wport6);
304 wport6 = -1;
305 syslog(LOG_INFO, "passive mode data connection failed");
306 return -1;
309 /* ask passive connection to server */
310 sa = (struct sockaddr *)&data4;
311 port4 = socket(sa->sa_family, SOCK_STREAM, 0);
312 if (port4 == -1) {
313 (void)close(wport6);
314 (void)close(port6);
315 wport6 = port6 = -1;
316 syslog(LOG_INFO, "passive mode data connection failed");
317 return -1;
319 error = connect(port4, sa, sa->sa_len);
320 if (error < 0) {
321 (void)close(wport6);
322 (void)close(port4);
323 (void)close(port6);
324 wport6 = port4 = port6 = -1;
325 syslog(LOG_INFO, "passive mode data connection failed");
326 return -1;
329 syslog(LOG_INFO, "passive mode data connection established");
330 return 0;
333 static int
334 ftp_copy(int src, int dst)
336 int error, atmark, n;
338 /* OOB data handling */
339 error = ioctl(src, SIOCATMARK, &atmark);
340 if (error != -1 && atmark == 1) {
341 n = read(src, rbuf, 1);
342 if (n == -1)
343 goto bad;
344 send(dst, rbuf, n, MSG_OOB);
345 #if 0
346 n = read(src, rbuf, sizeof(rbuf));
347 if (n == -1)
348 goto bad;
349 write(dst, rbuf, n);
350 return n;
351 #endif
354 n = read(src, rbuf, sizeof(rbuf));
355 switch (n) {
356 case -1:
357 case 0:
358 return n;
359 default:
360 write(dst, rbuf, n);
361 return n;
364 bad:
365 exit_failure("%s", strerror(errno));
366 /*NOTREACHED*/
367 return 0; /* to make gcc happy */
370 static int
371 ftp_copyresult(int src, int dst, enum state state)
373 int error, atmark, n;
374 socklen_t len;
375 char *param;
376 int code;
377 char *a, *p;
378 int i;
380 /* OOB data handling */
381 error = ioctl(src, SIOCATMARK, &atmark);
382 if (error != -1 && atmark == 1) {
383 n = read(src, rbuf, 1);
384 if (n == -1)
385 goto bad;
386 send(dst, rbuf, n, MSG_OOB);
387 #if 0
388 n = read(src, rbuf, sizeof(rbuf));
389 if (n == -1)
390 goto bad;
391 write(dst, rbuf, n);
392 return n;
393 #endif
396 n = read(src, rbuf, sizeof(rbuf));
397 if (n <= 0)
398 return n;
399 rbuf[n] = '\0';
402 * parse argument
404 p = rbuf;
405 for (i = 0; i < 3; i++) {
406 if (!isdigit((unsigned char)*p)) {
407 /* invalid reply */
408 write(dst, rbuf, n);
409 return n;
411 p++;
413 if (!isspace((unsigned char)*p)) {
414 /* invalid reply */
415 write(dst, rbuf, n);
416 return n;
418 code = atoi(rbuf);
419 param = p;
420 /* param points to first non-command token, if any */
421 while (*param && isspace((unsigned char)*param))
422 param++;
423 if (!*param)
424 param = NULL;
426 switch (state) {
427 case NONE:
428 if (!passivemode && rbuf[0] == '1') {
429 if (ftp_activeconn() < 0) {
430 n = snprintf(rbuf, sizeof(rbuf),
431 "425 Cannot open data connetion\r\n");
432 if (n < 0 || n >= (int)sizeof(rbuf))
433 n = 0;
436 if (n)
437 write(dst, rbuf, n);
438 return n;
439 case LPRT:
440 case EPRT:
441 /* expecting "200 PORT command successful." */
442 if (code == 200) {
443 p = strstr(rbuf, "PORT");
444 if (p) {
445 p[0] = (state == LPRT) ? 'L' : 'E';
446 p[1] = 'P';
448 } else {
449 (void)close(wport4);
450 wport4 = -1;
452 write(dst, rbuf, n);
453 return n;
454 case LPSV:
455 case EPSV:
457 * expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)"
458 * (in some cases result comes without paren)
460 if (code != 227) {
461 passivefail0:
462 (void)close(wport6);
463 wport6 = -1;
464 write(dst, rbuf, n);
465 return n;
469 unsigned int ho[4], po[2];
470 struct sockaddr_in *sin;
471 struct sockaddr_in6 *sin6;
472 u_short port;
475 * PASV result -> LPSV/EPSV result
477 p = param;
478 while (*p && *p != '(' && !isdigit((unsigned char)*p)) /*)*/
479 p++;
480 if (!*p)
481 goto passivefail0; /*XXX*/
482 if (*p == '(') /*)*/
483 p++;
484 n = sscanf(p, "%u,%u,%u,%u,%u,%u",
485 &ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
486 if (n != 6)
487 goto passivefail0; /*XXX*/
489 /* keep PORT parameter */
490 memset(&data4, 0, sizeof(data4));
491 sin = (struct sockaddr_in *)&data4;
492 sin->sin_len = sizeof(*sin);
493 sin->sin_family = AF_INET;
494 sin->sin_addr.s_addr = 0;
495 for (n = 0; n < 4; n++) {
496 sin->sin_addr.s_addr |=
497 htonl((ho[n] & 0xff) << ((3 - n) * 8));
499 sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
501 /* get ready for passive data connection */
502 memset(&data6, 0, sizeof(data6));
503 sin6 = (struct sockaddr_in6 *)&data6;
504 sin6->sin6_len = sizeof(*sin6);
505 sin6->sin6_family = AF_INET6;
506 wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0);
507 if (wport6 == -1) {
508 passivefail:
509 n = snprintf(sbuf, sizeof(sbuf),
510 "500 could not translate from PASV\r\n");
511 if (n < 0 || n >= (int)sizeof(sbuf))
512 n = 0;
513 if (n)
514 write(src, sbuf, n);
515 return n;
517 #ifdef IPV6_FAITH
519 int on = 1;
520 error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH,
521 &on, sizeof(on));
522 if (error == -1)
523 exit_failure("setsockopt(IPV6_FAITH): %s", strerror(errno));
525 #endif
526 error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len);
527 if (error == -1) {
528 (void)close(wport6);
529 wport6 = -1;
530 goto passivefail;
532 error = listen(wport6, 1);
533 if (error == -1) {
534 (void)close(wport6);
535 wport6 = -1;
536 goto passivefail;
539 /* transmit LPSV or EPSV */
541 * addr from dst, port from wport6
543 len = sizeof(data6);
544 error = getsockname(wport6, (struct sockaddr *)&data6, &len);
545 if (error == -1) {
546 (void)close(wport6);
547 wport6 = -1;
548 goto passivefail;
550 sin6 = (struct sockaddr_in6 *)&data6;
551 port = sin6->sin6_port;
553 len = sizeof(data6);
554 error = getsockname(dst, (struct sockaddr *)&data6, &len);
555 if (error == -1) {
556 (void)close(wport6);
557 wport6 = -1;
558 goto passivefail;
560 sin6 = (struct sockaddr_in6 *)&data6;
561 sin6->sin6_port = port;
563 if (state == LPSV) {
564 a = (char *)&sin6->sin6_addr;
565 p = (char *)&sin6->sin6_port;
566 n = snprintf(sbuf, sizeof(sbuf),
567 "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",
568 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
569 UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
570 UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
571 UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
572 2, UC(p[0]), UC(p[1]));
573 if (n < 0 || n >= (int)sizeof(sbuf))
574 n = 0;
575 if (n)
576 write(dst, sbuf, n);
577 passivemode = 1;
578 return n;
579 } else {
580 n = snprintf(sbuf, sizeof(sbuf),
581 "229 Entering Extended Passive Mode (|||%d|)\r\n",
582 ntohs(sin6->sin6_port));
583 if (n < 0 || n >= (int)sizeof(sbuf))
584 n = 0;
585 if (n)
586 write(dst, sbuf, n);
587 passivemode = 1;
588 return n;
593 bad:
594 exit_failure("%s", strerror(errno));
595 /*NOTREACHED*/
596 return 0; /* to make gcc happy */
599 static int
600 ftp_copycommand(int src, int dst, enum state *state)
602 int error, atmark, n;
603 socklen_t len;
604 unsigned int af, hal, ho[16], pal, po[2];
605 char *a, *p, *q;
606 char cmd[5], *param;
607 struct sockaddr_in *sin;
608 struct sockaddr_in6 *sin6;
609 enum state nstate;
610 char ch;
611 int i;
613 /* OOB data handling */
614 error = ioctl(src, SIOCATMARK, &atmark);
615 if (error != -1 && atmark == 1) {
616 n = read(src, rbuf, 1);
617 if (n == -1)
618 goto bad;
619 send(dst, rbuf, n, MSG_OOB);
620 #if 0
621 n = read(src, rbuf, sizeof(rbuf));
622 if (n == -1)
623 goto bad;
624 write(dst, rbuf, n);
625 return n;
626 #endif
629 n = read(src, rbuf, sizeof(rbuf));
630 if (n <= 0)
631 return n;
632 rbuf[n] = '\0';
634 if (n < 4) {
635 write(dst, rbuf, n);
636 return n;
640 * parse argument
642 p = rbuf;
643 q = cmd;
644 for (i = 0; i < 4; i++) {
645 if (!isalpha((unsigned char)*p)) {
646 /* invalid command */
647 write(dst, rbuf, n);
648 return n;
650 *q++ = islower((unsigned char)*p) ? toupper((unsigned char)*p) : *p;
651 p++;
653 if (!isspace((unsigned char)*p)) {
654 /* invalid command */
655 write(dst, rbuf, n);
656 return n;
658 *q = '\0';
659 param = p;
660 /* param points to first non-command token, if any */
661 while (*param && isspace((unsigned char)*param))
662 param++;
663 if (!*param)
664 param = NULL;
666 *state = NONE;
668 if (strcmp(cmd, "LPRT") == 0 && param) {
670 * LPRT -> PORT
672 nstate = LPRT;
674 (void)close(wport4);
675 (void)close(wport6);
676 (void)close(port4);
677 (void)close(port6);
678 wport4 = wport6 = port4 = port6 = -1;
680 if (epsvall) {
681 n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
682 cmd);
683 if (n < 0 || n >= (int)sizeof(sbuf))
684 n = 0;
685 if (n)
686 write(src, sbuf, n);
687 return n;
690 n = sscanf(param,
691 "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u",
692 &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3],
693 &ho[4], &ho[5], &ho[6], &ho[7],
694 &ho[8], &ho[9], &ho[10], &ho[11],
695 &ho[12], &ho[13], &ho[14], &ho[15],
696 &pal, &po[0], &po[1]);
697 if (n != 21 || af != 6 || hal != 16|| pal != 2) {
698 n = snprintf(sbuf, sizeof(sbuf),
699 "501 illegal parameter to LPRT\r\n");
700 if (n < 0 || n >= (int)sizeof(sbuf))
701 n = 0;
702 if (n)
703 write(src, sbuf, n);
704 return n;
707 /* keep LPRT parameter */
708 memset(&data6, 0, sizeof(data6));
709 sin6 = (struct sockaddr_in6 *)&data6;
710 sin6->sin6_len = sizeof(*sin6);
711 sin6->sin6_family = AF_INET6;
712 for (n = 0; n < 16; n++)
713 sin6->sin6_addr.s6_addr[n] = ho[n];
714 sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
716 sendport:
717 /* get ready for active data connection */
718 len = sizeof(data4);
719 error = getsockname(dst, (struct sockaddr *)&data4, &len);
720 if (error == -1) {
721 lprtfail:
722 n = snprintf(sbuf, sizeof(sbuf),
723 "500 could not translate to PORT\r\n");
724 if (n < 0 || n >= (int)sizeof(sbuf))
725 n = 0;
726 if (n)
727 write(src, sbuf, n);
728 return n;
730 if (((struct sockaddr *)&data4)->sa_family != AF_INET)
731 goto lprtfail;
732 sin = (struct sockaddr_in *)&data4;
733 sin->sin_port = 0;
734 wport4 = socket(sin->sin_family, SOCK_STREAM, 0);
735 if (wport4 == -1)
736 goto lprtfail;
737 error = bind(wport4, (struct sockaddr *)sin, sin->sin_len);
738 if (error == -1) {
739 (void)close(wport4);
740 wport4 = -1;
741 goto lprtfail;
743 error = listen(wport4, 1);
744 if (error == -1) {
745 (void)close(wport4);
746 wport4 = -1;
747 goto lprtfail;
750 /* transmit PORT */
751 len = sizeof(data4);
752 error = getsockname(wport4, (struct sockaddr *)&data4, &len);
753 if (error == -1) {
754 (void)close(wport4);
755 wport4 = -1;
756 goto lprtfail;
758 if (((struct sockaddr *)&data4)->sa_family != AF_INET) {
759 (void)close(wport4);
760 wport4 = -1;
761 goto lprtfail;
763 sin = (struct sockaddr_in *)&data4;
764 a = (char *)&sin->sin_addr;
765 p = (char *)&sin->sin_port;
766 n = snprintf(sbuf, sizeof(sbuf), "PORT %d,%d,%d,%d,%d,%d\r\n",
767 UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
768 UC(p[0]), UC(p[1]));
769 if (n < 0 || n >= (int)sizeof(sbuf))
770 n = 0;
771 if (n)
772 write(dst, sbuf, n);
773 *state = nstate;
774 passivemode = 0;
775 return n;
776 } else if (strcmp(cmd, "EPRT") == 0 && param) {
778 * EPRT -> PORT
780 char *afp, *hostp, *portp;
781 struct addrinfo hints, *res;
783 nstate = EPRT;
785 (void)close(wport4);
786 (void)close(wport6);
787 (void)close(port4);
788 (void)close(port6);
789 wport4 = wport6 = port4 = port6 = -1;
791 if (epsvall) {
792 n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
793 cmd);
794 if (n < 0 || n >= (int)sizeof(sbuf))
795 n = 0;
796 if (n)
797 write(src, sbuf, n);
798 return n;
801 p = param;
802 ch = *p++; /* boundary character */
803 afp = p;
804 while (*p && *p != ch)
805 p++;
806 if (!*p) {
807 eprtparamfail:
808 n = snprintf(sbuf, sizeof(sbuf),
809 "501 illegal parameter to EPRT\r\n");
810 if (n < 0 || n >= (int)sizeof(sbuf))
811 n = 0;
812 if (n)
813 write(src, sbuf, n);
814 return n;
816 *p++ = '\0';
817 hostp = p;
818 while (*p && *p != ch)
819 p++;
820 if (!*p)
821 goto eprtparamfail;
822 *p++ = '\0';
823 portp = p;
824 while (*p && *p != ch)
825 p++;
826 if (!*p)
827 goto eprtparamfail;
828 *p++ = '\0';
830 n = sscanf(afp, "%d", &af);
831 if (n != 1 || af != 2) {
832 n = snprintf(sbuf, sizeof(sbuf),
833 "501 unsupported address family to EPRT\r\n");
834 if (n < 0 || n >= (int)sizeof(sbuf))
835 n = 0;
836 if (n)
837 write(src, sbuf, n);
838 return n;
840 memset(&hints, 0, sizeof(hints));
841 hints.ai_family = AF_UNSPEC;
842 hints.ai_socktype = SOCK_STREAM;
843 hints.ai_protocol = IPPROTO_TCP;
844 error = getaddrinfo(hostp, portp, &hints, &res);
845 if (error) {
846 n = snprintf(sbuf, sizeof(sbuf),
847 "501 EPRT: %s\r\n", gai_strerror(error));
848 if (n < 0 || n >= (int)sizeof(sbuf))
849 n = 0;
850 if (n)
851 write(src, sbuf, n);
852 return n;
854 if (res->ai_next) {
855 n = snprintf(sbuf, sizeof(sbuf),
856 "501 EPRT: %s resolved to multiple addresses\r\n", hostp);
857 if (n < 0 || n >= (int)sizeof(sbuf))
858 n = 0;
859 if (n)
860 write(src, sbuf, n);
861 freeaddrinfo(res);
862 return n;
865 memcpy(&data6, res->ai_addr, res->ai_addrlen);
867 freeaddrinfo(res);
868 goto sendport;
869 } else if (strcmp(cmd, "LPSV") == 0 && !param) {
871 * LPSV -> PASV
873 nstate = LPSV;
875 (void)close(wport4);
876 (void)close(wport6);
877 (void)close(port4);
878 (void)close(port6);
879 wport4 = wport6 = port4 = port6 = -1;
881 if (epsvall) {
882 n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
883 cmd);
884 if (n < 0 || n >= (int)sizeof(sbuf))
885 n = 0;
886 if (n)
887 write(src, sbuf, n);
888 return n;
891 /* transmit PASV */
892 n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
893 if (n < 0 || n >= (int)sizeof(sbuf))
894 n = 0;
895 if (n)
896 write(dst, sbuf, n);
897 *state = LPSV;
898 passivemode = 0; /* to be set to 1 later */
899 return n;
900 } else if (strcmp(cmd, "EPSV") == 0 && !param) {
902 * EPSV -> PASV
904 (void)close(wport4);
905 (void)close(wport6);
906 (void)close(port4);
907 (void)close(port6);
908 wport4 = wport6 = port4 = port6 = -1;
910 n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
911 if (n < 0 || n >= (int)sizeof(sbuf))
912 n = 0;
913 if (n)
914 write(dst, sbuf, n);
915 *state = EPSV;
916 passivemode = 0; /* to be set to 1 later */
917 return n;
918 } else if (strcmp(cmd, "EPSV") == 0 && param
919 && strncasecmp(param, "ALL", 3) == 0 && isspace((unsigned char)param[3])) {
921 * EPSV ALL
923 epsvall = 1;
924 n = snprintf(sbuf, sizeof(sbuf), "200 EPSV ALL command successful.\r\n");
925 if (n < 0 || n >= (int)sizeof(sbuf))
926 n = 0;
927 if (n)
928 write(src, sbuf, n);
929 return n;
930 } else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) {
932 * reject PORT/PASV
934 n = snprintf(sbuf, sizeof(sbuf), "502 %s not implemented.\r\n", cmd);
935 if (n < 0 || n >= (int)sizeof(sbuf))
936 n = 0;
937 if (n)
938 write(src, sbuf, n);
939 return n;
940 } else if (passivemode
941 && (strcmp(cmd, "STOR") == 0
942 || strcmp(cmd, "STOU") == 0
943 || strcmp(cmd, "RETR") == 0
944 || strcmp(cmd, "LIST") == 0
945 || strcmp(cmd, "NLST") == 0
946 || strcmp(cmd, "APPE") == 0)) {
948 * commands with data transfer. need to care about passive
949 * mode data connection.
952 if (ftp_passiveconn() < 0) {
953 n = snprintf(sbuf, sizeof(sbuf), "425 Cannot open data connetion\r\n");
954 if (n < 0 || n >= (int)sizeof(sbuf))
955 n = 0;
956 if (n)
957 write(src, sbuf, n);
958 } else {
959 /* simply relay the command */
960 write(dst, rbuf, n);
963 *state = NONE;
964 return n;
965 } else {
966 /* simply relay it */
967 *state = NONE;
968 write(dst, rbuf, n);
969 return n;
972 bad:
973 exit_failure("%s", strerror(errno));
974 /*NOTREACHED*/
975 return 0; /* to make gcc happy */