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