Remove __P macros from src/usr.bin and src/usr.sbin.
[dragonfly.git] / usr.sbin / faithd / ftp.c
CommitLineData
984263bc
MD
1/* $KAME: ftp.c,v 1.11 2001/07/02 14:36:49 itojun Exp $ */
2
3/*
4 * Copyright (C) 1997 and 1998 WIDE Project.
5 * All rights reserved.
6 *
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.
18 *
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.
30 *
31 * $FreeBSD: src/usr.sbin/faithd/ftp.c,v 1.2.2.5 2002/04/28 05:40:29 suz Exp $
2d8a3be7 32 * $DragonFly: src/usr.sbin/faithd/ftp.c,v 1.3 2003/11/03 19:31:37 eirikn Exp $
984263bc
MD
33 */
34
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>
40
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>
48
49#include <netinet/in.h>
50#include <arpa/inet.h>
51#include <netdb.h>
52
53#include "faithd.h"
54
55static char rbuf[MSS];
56static char sbuf[MSS];
57static int passivemode = 0;
58static int wport4 = -1; /* listen() to active */
59static int wport6 = -1; /* listen() to passive */
60static int port4 = -1; /* active: inbound passive: outbound */
61static int port6 = -1; /* active: outbound passive: inbound */
62static struct sockaddr_storage data4; /* server data address */
63static struct sockaddr_storage data6; /* client data address */
64static int epsvall = 0;
65
66#ifdef FAITH4
67enum state { NONE, LPRT, EPRT, PORT, LPSV, EPSV, PASV };
68#else
69enum state { NONE, LPRT, EPRT, LPSV, EPSV };
70#endif
71
2d8a3be7
EN
72static int ftp_activeconn(void);
73static int ftp_passiveconn(void);
74static int ftp_copy(int, int);
75static int ftp_copyresult(int, int, enum state);
76static int ftp_copycommand(int, int, enum state *);
984263bc
MD
77
78void
79ftp_relay(int ctl6, int ctl4)
80{
81 fd_set readfds;
82 int error;
83 enum state state = NONE;
84 struct timeval tv;
85
86 syslog(LOG_INFO, "starting ftp control connection");
87
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;
104
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");
110
111 /*
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.
116 */
117 if (FD_ISSET(ctl6, &readfds)) {
118 /*
119 * copy control connection from the client.
120 * command translation is necessary.
121 */
122 error = ftp_copycommand(ctl6, ctl4, &state);
123
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;
134 }
135 }
136 if (FD_ISSET(ctl4, &readfds)) {
137 /*
138 * copy control connection from the server
139 * translation of result code is necessary.
140 */
141 error = ftp_copyresult(ctl4, ctl6, state);
142
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;
153 }
154 }
155 if (0 <= port4 && 0 <= port6 && FD_ISSET(port4, &readfds)) {
156 /*
157 * copy data connection.
158 * no special treatment necessary.
159 */
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;
173 }
174 }
175 if (0 <= port4 && 0 <= port6 && FD_ISSET(port6, &readfds)) {
176 /*
177 * copy data connection.
178 * no special treatment necessary.
179 */
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;
193 }
194 }
195#if 0
196 if (wport4 && FD_ISSET(wport4, &readfds)) {
197 /*
198 * establish active data connection from the server.
199 */
200 ftp_activeconn();
201 }
202 if (wport6 && FD_ISSET(wport6, &readfds)) {
203 /*
204 * establish passive data connection from the client.
205 */
206 ftp_passiveconn();
207 }
208#endif
209 }
210
211 bad:
212 exit_failure("%s", strerror(errno));
213}
214
215static int
216ftp_activeconn()
217{
218 int n;
219 int error;
220 fd_set set;
221 struct timeval timeout;
222 struct sockaddr *sa;
223
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;
236 }
237
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;
247 }
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;
256 }
257
258 syslog(LOG_INFO, "active mode data connection established");
259 return 0;
260}
261
262static int
263ftp_passiveconn()
264{
265 int n;
266 int error;
267 fd_set set;
268 struct timeval timeout;
269 struct sockaddr *sa;
270
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;
283 }
284
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;
294 }
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;
303 }
304
305 syslog(LOG_INFO, "passive mode data connection established");
306 return 0;
307}
308
309static int
310ftp_copy(int src, int dst)
311{
312 int error, atmark;
313 int n;
314
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
329 }
330
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;
339 }
340
341 bad:
342 exit_failure("%s", strerror(errno));
343 /*NOTREACHED*/
344 return 0; /* to make gcc happy */
345}
346
347static int
348ftp_copyresult(int src, int dst, enum state state)
349{
350 int error, atmark;
351 int n;
352 char *param;
353 int code;
354
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
369 }
370
371 n = read(src, rbuf, sizeof(rbuf));
372 if (n <= 0)
373 return n;
374 rbuf[n] = '\0';
375
376 /*
377 * parse argument
378 */
379 {
380 char *p;
381 int i;
382
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;
389 }
390 p++;
391 }
392 if (!isspace(*p)) {
393 /* invalid reply */
394 write(dst, rbuf, n);
395 return n;
396 }
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;
404 }
405
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");
412 }
413 }
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;
421
422 p = strstr(rbuf, "PORT");
423 if (p) {
424 p[0] = (state == LPRT) ? 'L' : 'E';
425 p[1] = 'P';
426 }
427 } else {
428 close(wport4);
429 wport4 = -1;
430 }
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;
438
439 p = strstr(rbuf, "EPRT");
440 if (p) {
441 p[0] = 'P';
442 p[1] = 'O';
443 }
444 } else {
445 close(wport4);
446 wport4 = -1;
447 }
448 write(dst, rbuf, n);
449 return n;
450#endif
451 case LPSV:
452 case EPSV:
453 /*
454 * expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)"
455 * (in some cases result comes without paren)
456 */
457 if (code != 227) {
458passivefail0:
459 close(wport6);
460 wport6 = -1;
461 write(dst, rbuf, n);
462 return n;
463 }
464
465 {
466 unsigned int ho[4], po[2];
467 struct sockaddr_in *sin;
468 struct sockaddr_in6 *sin6;
469 u_short port;
470 char *p;
471
472 /*
473 * PASV result -> LPSV/EPSV result
474 */
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*/
486
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));
496 }
497 sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
498
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) {
506passivefail:
507 n = snprintf(sbuf, sizeof(sbuf),
508 "500 could not translate from PASV\r\n");
509 write(src, sbuf, n);
510 return n;
511 }
512#ifdef IPV6_FAITH
513 {
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));
519 }
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;
526 }
527 error = listen(wport6, 1);
528 if (error == -1) {
529 close(wport6);
530 wport6 = -1;
531 goto passivefail;
532 }
533
534 /* transmit LPSV or EPSV */
535 /*
536 * addr from dst, port from wport6
537 */
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;
544 }
545 sin6 = (struct sockaddr_in6 *)&data6;
546 port = sin6->sin6_port;
547
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;
554 }
555 sin6 = (struct sockaddr_in6 *)&data6;
556 sin6->sin6_port = port;
557
558 if (state == LPSV) {
559 char *a, *p;
560
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;
580 }
581 }
582#ifdef FAITH4
583 case PASV:
584 /* expecting "229 Entering Extended Passive Mode (|||x|)" */
585 if (code != 229) {
586passivefail1:
587 close(wport6);
588 wport6 = -1;
589 write(dst, rbuf, n);
590 return n;
591 }
592
593 {
594 u_short port;
595 char *p;
596 struct sockaddr_in *sin;
597 struct sockaddr_in6 *sin6;
598
599 /*
600 * EPSV result -> PORT result
601 */
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*/
611
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);
619
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) {
627passivefail2:
628 n = snprintf(sbuf, sizeof(sbuf),
629 "500 could not translate from EPSV\r\n");
630 write(src, sbuf, n);
631 return n;
632 }
633#ifdef IP_FAITH
634 {
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));
640 }
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;
647 }
648 error = listen(wport6, 1);
649 if (error == -1) {
650 close(wport6);
651 wport6 = -1;
652 goto passivefail2;
653 }
654
655 /* transmit PORT */
656 /*
657 * addr from dst, port from wport6
658 */
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;
665 }
666 sin = (struct sockaddr_in *)&data6;
667 port = sin->sin_port;
668
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;
675 }
676 sin = (struct sockaddr_in *)&data6;
677 sin->sin_port = port;
678
679 {
680 char *a, *p;
681
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;
691 }
692 }
693#endif /* FAITH4 */
694 }
695
696 bad:
697 exit_failure("%s", strerror(errno));
698 /*NOTREACHED*/
699 return 0; /* to make gcc happy */
700}
701
702static int
703ftp_copycommand(int src, int dst, enum state *state)
704{
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;
714
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
729 }
730
731 n = read(src, rbuf, sizeof(rbuf));
732 if (n <= 0)
733 return n;
734 rbuf[n] = '\0';
735
736 if (n < 4) {
737 write(dst, rbuf, n);
738 return n;
739 }
740
741 /*
742 * parse argument
743 */
744 {
745 char *p, *q;
746 int i;
747
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;
755 }
756 *q++ = islower(*p) ? toupper(*p) : *p;
757 p++;
758 }
759 if (!isspace(*p)) {
760 /* invalid command */
761 write(dst, rbuf, n);
762 return n;
763 }
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;
771 }
772
773 *state = NONE;
774
775 if (strcmp(cmd, "LPRT") == 0 && param) {
776 /*
777 * LPRT -> PORT
778 */
779 nstate = LPRT;
780
781 close(wport4);
782 close(wport6);
783 close(port4);
784 close(port6);
785 wport4 = wport6 = port4 = port6 = -1;
786
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;
792 }
793
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;
806 }
807
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));
816
817sendport:
818 /* get ready for active data connection */
819 n = sizeof(data4);
820 error = getsockname(dst, (struct sockaddr *)&data4, &n);
821 if (error == -1) {
822lprtfail:
823 n = snprintf(sbuf, sizeof(sbuf),
824 "500 could not translate to PORT\r\n");
825 write(src, sbuf, n);
826 return n;
827 }
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;
840 }
841 error = listen(wport4, 1);
842 if (error == -1) {
843 close(wport4);
844 wport4 = -1;
845 goto lprtfail;
846 }
847
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;
855 }
856 if (((struct sockaddr *)&data4)->sa_family != AF_INET) {
857 close(wport4);
858 wport4 = -1;
859 goto lprtfail;
860 }
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) {
872 /*
873 * EPRT -> PORT
874 */
875 char *afp, *hostp, *portp;
876 struct addrinfo hints, *res;
877
878 nstate = EPRT;
879
880 close(wport4);
881 close(wport6);
882 close(port4);
883 close(port6);
884 wport4 = wport6 = port4 = port6 = -1;
885
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;
891 }
892
893 p = param;
894 ch = *p++; /* boundary character */
895 afp = p;
896 while (*p && *p != ch)
897 p++;
898 if (!*p) {
899eprtparamfail:
900 n = snprintf(sbuf, sizeof(sbuf),
901 "501 illegal parameter to EPRT\r\n");
902 write(src, sbuf, n);
903 return n;
904 }
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';
918
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;
925 }
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;
935 }
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;
941 }
942
943 memcpy(&data6, res->ai_addr, res->ai_addrlen);
944
945 goto sendport;
946 } else if (strcmp(cmd, "LPSV") == 0 && !param) {
947 /*
948 * LPSV -> PASV
949 */
950 nstate = LPSV;
951
952 close(wport4);
953 close(wport6);
954 close(port4);
955 close(port6);
956 wport4 = wport6 = port4 = port6 = -1;
957
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;
963 }
964
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) {
972 /*
973 * EPSV -> PASV
974 */
975 close(wport4);
976 close(wport6);
977 close(port4);
978 close(port6);
979 wport4 = wport6 = port4 = port6 = -1;
980
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])) {
988 /*
989 * EPSV ALL
990 */
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) {
997 /*
998 * PORT -> EPRT
999 */
1000 char host[NI_MAXHOST], serv[NI_MAXSERV];
1001
1002 nstate = PORT;
1003
1004 close(wport4);
1005 close(wport6);
1006 close(port4);
1007 close(port6);
1008 wport4 = wport6 = port4 = port6 = -1;
1009
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;
1018 }
1019
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));
1028
1029 /* get ready for active data connection */
1030 n = sizeof(data4);
1031 error = getsockname(dst, (struct sockaddr *)&data4, &n);
1032 if (error == -1) {
1033portfail:
1034 n = snprintf(sbuf, sizeof(sbuf),
1035 "500 could not translate to EPRT\r\n");
1036 write(src, sbuf, n);
1037 return n;
1038 }
1039 if (((struct sockaddr *)&data4)->sa_family != AF_INET6)
1040 goto portfail;
1041
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;
1052 }
1053 error = listen(wport4, 1);
1054 if (error == -1) {
1055 close(wport4);
1056 wport4 = -1;
1057 goto portfail;
1058 }
1059
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;
1067 }
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;
1075 }
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) {
1082 /*
1083 * PASV -> EPSV
1084 */
1085
1086 nstate = PASV;
1087
1088 close(wport4);
1089 close(wport6);
1090 close(port4);
1091 close(port6);
1092 wport4 = wport6 = port4 = port6 = -1;
1093
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) {
1102 /*
1103 * reject PORT/PASV
1104 */
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)) {
1116 /*
1117 * commands with data transfer. need to care about passive
1118 * mode data connection.
1119 */
1120
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);
1127 }
1128
1129 *state = NONE;
1130 return n;
1131 } else {
1132 /* simply relay it */
1133 *state = NONE;
1134 write(dst, rbuf, n);
1135 return n;
1136 }
1137
1138 bad:
1139 exit_failure("%s", strerror(errno));
1140 /*NOTREACHED*/
1141 return 0; /* to make gcc happy */
1142}