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