* K&R function cleanup
[dragonfly.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: src/usr.sbin/faithd/ftp.c,v 1.2.2.5 2002/04/28 05:40:29 suz Exp $
32  * $DragonFly: src/usr.sbin/faithd/ftp.c,v 1.4 2003/11/16 14:10:45 eirikn Exp $
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
55 static char rbuf[MSS];
56 static char sbuf[MSS];
57 static int passivemode = 0;
58 static int wport4 = -1;                 /* listen() to active */
59 static int wport6 = -1;                 /* listen() to passive */
60 static int port4 = -1;                  /* active: inbound  passive: outbound */
61 static int port6 = -1;                  /* active: outbound  passive: inbound */
62 static struct sockaddr_storage data4;   /* server data address */
63 static struct sockaddr_storage data6;   /* client data address */
64 static int epsvall = 0;
65
66 #ifdef FAITH4
67 enum state { NONE, LPRT, EPRT, PORT, LPSV, EPSV, PASV };
68 #else
69 enum state { NONE, LPRT, EPRT, LPSV, EPSV };
70 #endif
71
72 static int ftp_activeconn(void);
73 static int ftp_passiveconn(void);
74 static int ftp_copy(int, int);
75 static int ftp_copyresult(int, int, enum state);
76 static int ftp_copycommand(int, int, enum state *);
77
78 void
79 ftp_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
215 static int
216 ftp_activeconn(void)
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
262 static int
263 ftp_passiveconn(void)
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
309 static int
310 ftp_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
347 static int
348 ftp_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) {
458 passivefail0:
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) {
506 passivefail:
507                         n = snprintf(sbuf, sizeof(sbuf),
508                                 "500 could not translate from PASV\r\n");
509                         write(src, sbuf, n);
510                         return n;
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) {
586 passivefail1:
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) {
627 passivefail2:
628                         n = snprintf(sbuf, sizeof(sbuf),
629                                 "500 could not translate from EPSV\r\n");
630                         write(src, sbuf, n);
631                         return n;
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
702 static int
703 ftp_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
817 sendport:
818                 /* get ready for active data connection */
819                 n = sizeof(data4);
820                 error = getsockname(dst, (struct sockaddr *)&data4, &n);
821                 if (error == -1) {
822 lprtfail:
823                         n = snprintf(sbuf, sizeof(sbuf),
824                                 "500 could not translate to PORT\r\n");
825                         write(src, sbuf, n);
826                         return n;
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) {
899 eprtparamfail:
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) {
1033 portfail:
1034                         n = snprintf(sbuf, sizeof(sbuf),
1035                                 "500 could not translate to EPRT\r\n");
1036                         write(src, sbuf, n);
1037                         return n;
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 }