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