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