summaryrefslogtreecommitdiff
path: root/usr.sbin/faithd/ftp.c
diff options
context:
space:
mode:
authorJun-ichiro itojun Hagino <itojun@cvs.openbsd.org>1999-12-20 16:07:48 +0000
committerJun-ichiro itojun Hagino <itojun@cvs.openbsd.org>1999-12-20 16:07:48 +0000
commit05d299e7b204e7e1df159eed593901871dbe66da (patch)
tree848a34ae3eb74820e4d2e9b6e6aded548ffdb4d4 /usr.sbin/faithd/ftp.c
parent3606e5dd2fbac1a717afb5022715d2e056590252 (diff)
faith translator deamon, from KAME
Diffstat (limited to 'usr.sbin/faithd/ftp.c')
-rw-r--r--usr.sbin/faithd/ftp.c1132
1 files changed, 1132 insertions, 0 deletions
diff --git a/usr.sbin/faithd/ftp.c b/usr.sbin/faithd/ftp.c
new file mode 100644
index 00000000000..96258f32e19
--- /dev/null
+++ b/usr.sbin/faithd/ftp.c
@@ -0,0 +1,1132 @@
+/*
+ * Copyright (C) 1997 and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "faithd.h"
+
+static char rbuf[MSS];
+static char sbuf[MSS];
+static int passivemode = 0;
+static int wport4 = -1; /* listen() to active */
+static int wport6 = -1; /* listen() to passive */
+static int port4 = -1; /* active: inbound passive: outbound */
+static int port6 = -1; /* active: outbound passive: inbound */
+static struct sockaddr_storage data4; /* server data address */
+static struct sockaddr_storage data6; /* client data address */
+static int epsvall = 0;
+
+#ifdef FAITH4
+enum state { NONE, LPRT, EPRT, PORT, LPSV, EPSV, PASV };
+#else
+enum state { NONE, LPRT, EPRT, LPSV, EPSV };
+#endif
+
+static int ftp_activeconn __P((void));
+static int ftp_passiveconn __P((void));
+static int ftp_copy __P((int, int));
+static int ftp_copyresult __P((int, int, enum state));
+static int ftp_copycommand __P((int, int, enum state *));
+
+void
+ftp_relay(int ctl6, int ctl4)
+{
+ fd_set readfds;
+ int error;
+ enum state state = NONE;
+ struct timeval tv;
+
+ syslog(LOG_INFO, "starting ftp control connection");
+
+ for (;;) {
+ FD_ZERO(&readfds);
+ FD_SET(ctl4, &readfds);
+ FD_SET(ctl6, &readfds);
+ if (0 <= port4)
+ FD_SET(port4, &readfds);
+ if (0 <= port6)
+ FD_SET(port6, &readfds);
+#if 0
+ if (0 <= wport4)
+ FD_SET(wport4, &readfds);
+ if (0 <= wport6)
+ FD_SET(wport6, &readfds);
+#endif
+ tv.tv_sec = FAITH_TIMEOUT;
+ tv.tv_usec = 0;
+
+ error = select(256, &readfds, NULL, NULL, &tv);
+ if (error == -1)
+ exit_failure("select: %s", ERRSTR);
+ else if (error == 0)
+ exit_failure("connection timeout");
+
+ /*
+ * The order of the following checks does (slightly) matter.
+ * It is important to visit all checks (do not use "continue"),
+ * otherwise some of the pipe may become full and we cannot
+ * relay correctly.
+ */
+ if (FD_ISSET(ctl6, &readfds)) {
+ /*
+ * copy control connection from the client.
+ * command translation is necessary.
+ */
+ error = ftp_copycommand(ctl6, ctl4, &state);
+
+ switch (error) {
+ case -1:
+ goto bad;
+ case 0:
+ close(ctl4);
+ close(ctl6);
+ exit_success("terminating ftp control connection");
+ /*NOTREACHED*/
+ default:
+ break;
+ }
+ }
+ if (FD_ISSET(ctl4, &readfds)) {
+ /*
+ * copy control connection from the server
+ * translation of result code is necessary.
+ */
+ error = ftp_copyresult(ctl4, ctl6, state);
+
+ switch (error) {
+ case -1:
+ goto bad;
+ case 0:
+ close(ctl4);
+ close(ctl6);
+ exit_success("terminating ftp control connection");
+ /*NOTREACHED*/
+ default:
+ break;
+ }
+ }
+ if (0 <= port4 && 0 <= port6 && FD_ISSET(port4, &readfds)) {
+ /*
+ * copy data connection.
+ * no special treatment necessary.
+ */
+ if (FD_ISSET(port4, &readfds))
+ error = ftp_copy(port4, port6);
+ switch (error) {
+ case -1:
+ goto bad;
+ case 0:
+ close(port4);
+ close(port6);
+ port4 = port6 = -1;
+ syslog(LOG_INFO, "terminating data connection");
+ break;
+ default:
+ break;
+ }
+ }
+ if (0 <= port4 && 0 <= port6 && FD_ISSET(port6, &readfds)) {
+ /*
+ * copy data connection.
+ * no special treatment necessary.
+ */
+ if (FD_ISSET(port6, &readfds))
+ error = ftp_copy(port6, port4);
+ switch (error) {
+ case -1:
+ goto bad;
+ case 0:
+ close(port4);
+ close(port6);
+ port4 = port6 = -1;
+ syslog(LOG_INFO, "terminating data connection");
+ break;
+ default:
+ break;
+ }
+ }
+#if 0
+ if (wport4 && FD_ISSET(wport4, &readfds)) {
+ /*
+ * establish active data connection from the server.
+ */
+ ftp_activeconn();
+ }
+ if (wport6 && FD_ISSET(wport6, &readfds)) {
+ /*
+ * establish passive data connection from the client.
+ */
+ ftp_passiveconn();
+ }
+#endif
+ }
+
+ bad:
+ exit_failure(ERRSTR);
+}
+
+static int
+ftp_activeconn()
+{
+ int n;
+ int error;
+ fd_set set;
+ struct timeval timeout;
+ struct sockaddr *sa;
+
+ /* get active connection from server */
+ FD_ZERO(&set);
+ FD_SET(wport4, &set);
+ timeout.tv_sec = 120;
+ timeout.tv_usec = -1;
+ n = sizeof(data4);
+ if (select(wport4 + 1, &set, NULL, NULL, &timeout) == 0
+ || (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0) {
+ close(wport4);
+ wport4 = -1;
+ syslog(LOG_INFO, "active mode data connection failed");
+ return -1;
+ }
+
+ /* ask active connection to client */
+ sa = (struct sockaddr *)&data6;
+ port6 = socket(sa->sa_family, SOCK_STREAM, 0);
+ if (port6 == -1) {
+ close(port4);
+ close(wport4);
+ port4 = wport4 = -1;
+ syslog(LOG_INFO, "active mode data connection failed");
+ return -1;
+ }
+ error = connect(port6, sa, sa->sa_len);
+ if (port6 == -1) {
+ close(port6);
+ close(port4);
+ close(wport4);
+ port6 = port4 = wport4 = -1;
+ syslog(LOG_INFO, "active mode data connection failed");
+ return -1;
+ }
+
+ syslog(LOG_INFO, "active mode data connection established");
+ return 0;
+}
+
+static int
+ftp_passiveconn()
+{
+ int n;
+ int error;
+ fd_set set;
+ struct timeval timeout;
+ struct sockaddr *sa;
+
+ /* get passive connection from client */
+ FD_ZERO(&set);
+ FD_SET(wport6, &set);
+ timeout.tv_sec = 120;
+ timeout.tv_usec = 0;
+ n = sizeof(data6);
+ if (select(wport6 + 1, &set, NULL, NULL, &timeout) == 0
+ || (port6 = accept(wport6, (struct sockaddr *)&data6, &n)) < 0) {
+ close(wport6);
+ wport6 = -1;
+ syslog(LOG_INFO, "passive mode data connection failed");
+ return -1;
+ }
+
+ /* ask passive connection to server */
+ sa = (struct sockaddr *)&data4;
+ port4 = socket(sa->sa_family, SOCK_STREAM, 0);
+ if (port4 == -1) {
+ close(wport6);
+ close(port6);
+ wport6 = port6 = -1;
+ syslog(LOG_INFO, "passive mode data connection failed");
+ return -1;
+ }
+ error = connect(port4, sa, sa->sa_len);
+ if (port4 == -1) {
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport6 = port4 = port6 = -1;
+ syslog(LOG_INFO, "passive mode data connection failed");
+ return -1;
+ }
+
+ syslog(LOG_INFO, "passive mode data connection established");
+ return 0;
+}
+
+static int
+ftp_copy(int src, int dst)
+{
+ int error, atmark;
+ int n;
+
+ /* OOB data handling */
+ error = ioctl(src, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ n = read(src, rbuf, 1);
+ if (n == -1)
+ goto bad;
+ send(dst, rbuf, n, MSG_OOB);
+#if 0
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n == -1)
+ goto bad;
+ write(dst, rbuf, n);
+ return n;
+#endif
+ }
+
+ n = read(src, rbuf, sizeof(rbuf));
+ switch (n) {
+ case -1:
+ case 0:
+ return n;
+ default:
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ bad:
+ exit_failure(ERRSTR);
+ /*NOTREACHED*/
+ return 0; /* to make gcc happy */
+}
+
+static int
+ftp_copyresult(int src, int dst, enum state state)
+{
+ int error, atmark;
+ int n;
+ char *param;
+ int code;
+
+ /* OOB data handling */
+ error = ioctl(src, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ n = read(src, rbuf, 1);
+ if (n == -1)
+ goto bad;
+ send(dst, rbuf, n, MSG_OOB);
+#if 0
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n == -1)
+ goto bad;
+ write(dst, rbuf, n);
+ return n;
+#endif
+ }
+
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n <= 0)
+ return n;
+ rbuf[n] = '\0';
+
+ /*
+ * parse argument
+ */
+ {
+ char *p;
+ int i;
+
+ p = rbuf;
+ for (i = 0; i < 3; i++) {
+ if (!isdigit(*p)) {
+ /* invalid reply */
+ write(dst, rbuf, n);
+ return n;
+ }
+ p++;
+ }
+ if (!isspace(*p)) {
+ /* invalid reply */
+ write(dst, rbuf, n);
+ return n;
+ }
+ code = atoi(rbuf);
+ param = p;
+ /* param points to first non-command token, if any */
+ while (*param && isspace(*param))
+ param++;
+ if (!*param)
+ param = NULL;
+ }
+
+ switch (state) {
+ case NONE:
+ if (!passivemode && rbuf[0] == '1') {
+ if (ftp_activeconn() < 0) {
+ n = sprintf(rbuf,
+ "425 Cannot open data connetion\r\n");
+ }
+ }
+ write(dst, rbuf, n);
+ return n;
+ case LPRT:
+ case EPRT:
+ /* expecting "200 PORT command successful." */
+ if (code == 200) {
+ char *p;
+
+ p = strstr(rbuf, "PORT");
+ if (p) {
+ p[0] = (state == LPRT) ? 'L' : 'E';
+ p[1] = 'P';
+ }
+ } else {
+ close(wport4);
+ wport4 = -1;
+ }
+ write(dst, rbuf, n);
+ return n;
+#ifdef FAITH4
+ case PORT:
+ /* expecting "200 EPRT command successful." */
+ if (code == 200) {
+ char *p;
+
+ p = strstr(rbuf, "EPRT");
+ if (p) {
+ p[0] = 'P';
+ p[1] = 'O';
+ }
+ } else {
+ close(wport4);
+ wport4 = -1;
+ }
+ write(dst, rbuf, n);
+ return n;
+#endif
+ case LPSV:
+ case EPSV:
+ /* expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)" */
+ if (code != 227) {
+passivefail0:
+ close(wport6);
+ wport6 = -1;
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ {
+ unsigned int ho[4], po[2];
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ u_short port;
+ char *p;
+
+ /*
+ * PASV result -> LPSV/EPSV result
+ */
+ p = param;
+ while (*p && *p != '(')
+ p++;
+ if (!*p)
+ goto passivefail0; /*XXX*/
+ p++;
+ n = sscanf(p, "%u,%u,%u,%u,%u,%u",
+ &ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
+ if (n != 6)
+ goto passivefail0; /*XXX*/
+
+ /* keep PORT parameter */
+ memset(&data4, 0, sizeof(data4));
+ sin = (struct sockaddr_in *)&data4;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = 0;
+ for (n = 0; n < 4; n++) {
+ sin->sin_addr.s_addr |=
+ htonl((ho[n] & 0xff) << ((3 - n) * 8));
+ }
+ sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
+
+ /* get ready for passive data connection */
+ memset(&data6, 0, sizeof(data6));
+ sin6 = (struct sockaddr_in6 *)&data6;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0);
+ if (wport6 == -1) {
+passivefail:
+ n = sprintf(sbuf,
+ "500 could not translate from PASV\r\n");
+ write(src, sbuf, n);
+ return n;
+ }
+#ifdef IPV6_FAITH
+ {
+ int on = 1;
+ error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH,
+ &on, sizeof(on));
+ if (error == -1)
+ exit_error("setsockopt(IPV6_FAITH): %s", ERRSTR);
+ }
+#endif
+ error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail;
+ }
+ error = listen(wport6, 1);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail;
+ }
+
+ /* transmit LPSV or EPSV */
+ /*
+ * addr from dst, port from wport6
+ */
+ n = sizeof(data6);
+ error = getsockname(wport6, (struct sockaddr *)&data6, &n);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail;
+ }
+ sin6 = (struct sockaddr_in6 *)&data6;
+ port = sin6->sin6_port;
+
+ n = sizeof(data6);
+ error = getsockname(dst, (struct sockaddr *)&data6, &n);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail;
+ }
+ sin6 = (struct sockaddr_in6 *)&data6;
+ sin6->sin6_port = port;
+
+ if (state == LPSV) {
+ char *a, *p;
+
+ a = (char *)&sin6->sin6_addr;
+ p = (char *)&sin6->sin6_port;
+ n = sprintf(sbuf,
+"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",
+ 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
+ UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
+ UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
+ 2, UC(p[0]), UC(p[1]));
+ write(dst, sbuf, n);
+ passivemode = 1;
+ return n;
+ } else {
+ n = sprintf(sbuf,
+"229 Entering Extended Passive Mode (|||%d|)\r\n",
+ ntohs(sin6->sin6_port));
+ write(dst, sbuf, n);
+ passivemode = 1;
+ return n;
+ }
+ }
+#ifdef FAITH4
+ case PASV:
+ /* expecting "229 Entering Extended Passive Mode (|||x|)" */
+ if (code != 229) {
+passivefail1:
+ close(wport6);
+ wport6 = -1;
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ {
+ u_short port;
+ char *p;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+
+ /*
+ * EPSV result -> PORT result
+ */
+ p = param;
+ while (*p && *p != '(')
+ p++;
+ if (!*p)
+ goto passivefail1; /*XXX*/
+ p++;
+ n = sscanf(p, "|||%hu|", &port);
+ if (n != 1)
+ goto passivefail1; /*XXX*/
+
+ /* keep EPRT parameter */
+ n = sizeof(data4);
+ error = getpeername(src, (struct sockaddr *)&data4, &n);
+ if (error == -1)
+ goto passivefail1; /*XXX*/
+ sin6 = (struct sockaddr_in6 *)&data4;
+ sin6->sin6_port = htons(port);
+
+ /* get ready for passive data connection */
+ memset(&data6, 0, sizeof(data6));
+ sin = (struct sockaddr_in *)&data6;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ wport6 = socket(sin->sin_family, SOCK_STREAM, 0);
+ if (wport6 == -1) {
+passivefail2:
+ n = sprintf(sbuf,
+ "500 could not translate from EPSV\r\n");
+ write(src, sbuf, n);
+ return n;
+ }
+#ifdef IP_FAITH
+ {
+ int on = 1;
+ error = setsockopt(wport6, IPPROTO_IP, IP_FAITH,
+ &on, sizeof(on));
+ if (error == -1)
+ exit_error("setsockopt(IP_FAITH): %s", ERRSTR);
+ }
+#endif
+ error = bind(wport6, (struct sockaddr *)sin, sin->sin_len);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail2;
+ }
+ error = listen(wport6, 1);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail2;
+ }
+
+ /* transmit PORT */
+ /*
+ * addr from dst, port from wport6
+ */
+ n = sizeof(data6);
+ error = getsockname(wport6, (struct sockaddr *)&data6, &n);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail2;
+ }
+ sin = (struct sockaddr_in *)&data6;
+ port = sin->sin_port;
+
+ n = sizeof(data6);
+ error = getsockname(dst, (struct sockaddr *)&data6, &n);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail2;
+ }
+ sin = (struct sockaddr_in *)&data6;
+ sin->sin_port = port;
+
+ {
+ char *a, *p;
+
+ a = (char *)&sin->sin_addr;
+ p = (char *)&sin->sin_port;
+ n = sprintf(sbuf,
+"227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(p[0]), UC(p[1]));
+ write(dst, sbuf, n);
+ passivemode = 1;
+ return n;
+ }
+ }
+#endif /* FAITH4 */
+ }
+
+ bad:
+ exit_failure(ERRSTR);
+ /*NOTREACHED*/
+ return 0; /* to make gcc happy */
+}
+
+static int
+ftp_copycommand(int src, int dst, enum state *state)
+{
+ int error, atmark;
+ int n;
+ unsigned int af, hal, ho[16], pal, po[2];
+ char *a, *p;
+ char cmd[5], *param;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ enum state nstate;
+ char ch;
+
+ /* OOB data handling */
+ error = ioctl(src, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ n = read(src, rbuf, 1);
+ if (n == -1)
+ goto bad;
+ send(dst, rbuf, n, MSG_OOB);
+#if 0
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n == -1)
+ goto bad;
+ write(dst, rbuf, n);
+ return n;
+#endif
+ }
+
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n <= 0)
+ return n;
+ rbuf[n] = '\0';
+
+ if (n < 4) {
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ /*
+ * parse argument
+ */
+ {
+ char *p, *q;
+ int i;
+
+ p = rbuf;
+ q = cmd;
+ for (i = 0; i < 4; i++) {
+ if (!isalpha(*p)) {
+ /* invalid command */
+ write(dst, rbuf, n);
+ return n;
+ }
+ *q++ = islower(*p) ? toupper(*p) : *p;
+ p++;
+ }
+ if (!isspace(*p)) {
+ /* invalid command */
+ write(dst, rbuf, n);
+ return n;
+ }
+ *q = '\0';
+ param = p;
+ /* param points to first non-command token, if any */
+ while (*param && isspace(*param))
+ param++;
+ if (!*param)
+ param = NULL;
+ }
+
+ *state = NONE;
+
+ if (strcmp(cmd, "LPRT") == 0 && param) {
+ /*
+ * LPRT -> PORT
+ */
+ nstate = LPRT;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ if (epsvall) {
+ n = sprintf(sbuf, "501 %s disallowed in EPSV ALL\r\n",
+ cmd);
+ write(src, sbuf, n);
+ return n;
+ }
+
+ n = sscanf(param,
+"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u",
+ &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3],
+ &ho[4], &ho[5], &ho[6], &ho[7],
+ &ho[8], &ho[9], &ho[10], &ho[11],
+ &ho[12], &ho[13], &ho[14], &ho[15],
+ &pal, &po[0], &po[1]);
+ if (n != 21 || af != 6 || hal != 16|| pal != 2) {
+ n = sprintf(sbuf,
+ "501 illegal parameter to LPRT\r\n");
+ write(src, sbuf, n);
+ return n;
+ }
+
+ /* keep LPRT parameter */
+ memset(&data6, 0, sizeof(data6));
+ sin6 = (struct sockaddr_in6 *)&data6;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ for (n = 0; n < 16; n++)
+ sin6->sin6_addr.s6_addr[n] = ho[n];
+ sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
+
+sendport:
+ /* get ready for active data connection */
+ n = sizeof(data4);
+ error = getsockname(dst, (struct sockaddr *)&data4, &n);
+ if (error == -1) {
+lprtfail:
+ n = sprintf(sbuf,
+ "500 could not translate to PORT\r\n");
+ write(src, sbuf, n);
+ return n;
+ }
+ if (((struct sockaddr *)&data4)->sa_family != AF_INET)
+ goto lprtfail;
+ sin = (struct sockaddr_in *)&data4;
+ sin->sin_port = 0;
+ wport4 = socket(sin->sin_family, SOCK_STREAM, 0);
+ if (wport4 == -1)
+ goto lprtfail;
+ error = bind(wport4, (struct sockaddr *)sin, sin->sin_len);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto lprtfail;
+ }
+ error = listen(wport4, 1);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto lprtfail;
+ }
+
+ /* transmit PORT */
+ n = sizeof(data4);
+ error = getsockname(wport4, (struct sockaddr *)&data4, &n);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto lprtfail;
+ }
+ if (((struct sockaddr *)&data4)->sa_family != AF_INET) {
+ close(wport4);
+ wport4 = -1;
+ goto lprtfail;
+ }
+ sin = (struct sockaddr_in *)&data4;
+ a = (char *)&sin->sin_addr;
+ p = (char *)&sin->sin_port;
+ n = sprintf(sbuf, "PORT %d,%d,%d,%d,%d,%d\r\n",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(p[0]), UC(p[1]));
+ write(dst, sbuf, n);
+ *state = nstate;
+ passivemode = 0;
+ return n;
+ } else if (strcmp(cmd, "EPRT") == 0 && param) {
+ /*
+ * EPRT -> PORT
+ */
+ char *afp, *hostp, *portp;
+ struct addrinfo hints, *res;
+
+ nstate = EPRT;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ if (epsvall) {
+ n = sprintf(sbuf, "501 %s disallowed in EPSV ALL\r\n",
+ cmd);
+ write(src, sbuf, n);
+ return n;
+ }
+
+ p = param;
+ ch = *p++; /* boundary character */
+ afp = p;
+ while (*p && *p != ch)
+ p++;
+ if (!*p) {
+eprtparamfail:
+ n = sprintf(sbuf,
+ "501 illegal parameter to EPRT\r\n");
+ write(src, sbuf, n);
+ return n;
+ }
+ *p++ = '\0';
+ hostp = p;
+ while (*p && *p != ch)
+ p++;
+ if (!*p)
+ goto eprtparamfail;
+ *p++ = '\0';
+ portp = p;
+ while (*p && *p != ch)
+ p++;
+ if (!*p)
+ goto eprtparamfail;
+ *p++ = '\0';
+
+ n = sscanf(afp, "%d", &af);
+ if (n != 1 || af != 2) {
+ n = sprintf(sbuf,
+ "501 unsupported address family to EPRT\r\n");
+ write(src, sbuf, n);
+ return n;
+ }
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ error = getaddrinfo(hostp, portp, &hints, &res);
+ if (error) {
+ n = sprintf(sbuf,
+ "501 EPRT: %s\r\n", gai_strerror(error));
+ write(src, sbuf, n);
+ return n;
+ }
+ if (res->ai_next) {
+ n = sprintf(sbuf,
+ "501 EPRT: %s resolved to multiple addresses\r\n", hostp);
+ write(src, sbuf, n);
+ return n;
+ }
+
+ memcpy(&data6, res->ai_addr, res->ai_addrlen);
+
+ goto sendport;
+ } else if (strcmp(cmd, "LPSV") == 0 && !param) {
+ /*
+ * LPSV -> PASV
+ */
+ nstate = LPSV;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ if (epsvall) {
+ n = sprintf(sbuf, "501 %s disallowed in EPSV ALL\r\n",
+ cmd);
+ write(src, sbuf, n);
+ return n;
+ }
+
+ /* transmit PASV */
+ n = sprintf(sbuf, "PASV\r\n");
+ write(dst, sbuf, n);
+ *state = LPSV;
+ passivemode = 0; /* to be set to 1 later */
+ return n;
+ } else if (strcmp(cmd, "EPSV") == 0 && !param) {
+ /*
+ * EPSV -> PASV
+ */
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ n = sprintf(sbuf, "PASV\r\n");
+ write(dst, sbuf, n);
+ *state = EPSV;
+ passivemode = 0; /* to be set to 1 later */
+ return n;
+ } else if (strcmp(cmd, "EPSV") == 0 && param
+ && strncasecmp(param, "ALL", 3) == 0 && isspace(param[3])) {
+ /*
+ * EPSV ALL
+ */
+ epsvall = 1;
+ n = sprintf(sbuf, "200 EPSV ALL command successful.\r\n");
+ write(src, sbuf, n);
+ return n;
+#ifdef FAITH4
+ } else if (strcmp(cmd, "PORT") == 0 && param) {
+ /*
+ * PORT -> EPRT
+ */
+ char host[NI_MAXHOST], serv[NI_MAXSERV];
+
+ nstate = PORT;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ p = param;
+ n = sscanf(p, "%u,%u,%u,%u,%u,%u",
+ &ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
+ if (n != 6) {
+ n = sprintf(sbuf,
+ "501 illegal parameter to PORT\r\n");
+ write(src, sbuf, n);
+ return n;
+ }
+
+ memset(&data6, 0, sizeof(data6));
+ sin = (struct sockaddr_in *)&data6;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = htonl(
+ ((ho[0] & 0xff) << 24) | ((ho[1] & 0xff) << 16) |
+ ((ho[2] & 0xff) << 8) | (ho[3] & 0xff));
+ sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
+
+ /* get ready for active data connection */
+ n = sizeof(data4);
+ error = getsockname(dst, (struct sockaddr *)&data4, &n);
+ if (error == -1) {
+portfail:
+ n = sprintf(sbuf,
+ "500 could not translate to EPRT\r\n");
+ write(src, sbuf, n);
+ return n;
+ }
+ if (((struct sockaddr *)&data4)->sa_family != AF_INET6)
+ goto portfail;
+
+ ((struct sockaddr_in6 *)&data4)->sin6_port = 0;
+ sa = (struct sockaddr *)&data4;
+ wport4 = socket(sa->sa_family, SOCK_STREAM, 0);
+ if (wport4 == -1)
+ goto portfail;
+ error = bind(wport4, sa, sa->sa_len);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto portfail;
+ }
+ error = listen(wport4, 1);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto portfail;
+ }
+
+ /* transmit EPRT */
+ n = sizeof(data4);
+ error = getsockname(wport4, (struct sockaddr *)&data4, &n);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto portfail;
+ }
+ af = 2;
+ sa = (struct sockaddr *)&data4;
+ if (getnameinfo(sa, sa->sa_len, host, sizeof(host),
+ serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV)) {
+ close(wport4);
+ wport4 = -1;
+ goto portfail;
+ }
+ n = sprintf(sbuf, "EPRT |%d|%s|%s|\r\n", af, host, serv);
+ write(dst, sbuf, n);
+ *state = nstate;
+ passivemode = 0;
+ return n;
+ } else if (strcmp(cmd, "PASV") == 0 && !param) {
+ /*
+ * PASV -> EPSV
+ */
+
+ nstate = PASV;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ /* transmit EPSV */
+ n = sprintf(sbuf, "EPSV\r\n");
+ write(dst, sbuf, n);
+ *state = PASV;
+ passivemode = 0; /* to be set to 1 later */
+ return n;
+#else /* FAITH4 */
+ } else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) {
+ /*
+ * reject PORT/PASV
+ */
+ n = sprintf(sbuf, "502 %s not implemented.\r\n", cmd);
+ write(src, sbuf, n);
+ return n;
+#endif /* FAITH4 */
+ } else if (passivemode
+ && (strcmp(cmd, "STOR") == 0
+ || strcmp(cmd, "STOU") == 0
+ || strcmp(cmd, "RETR") == 0
+ || strcmp(cmd, "LIST") == 0
+ || strcmp(cmd, "NLST") == 0
+ || strcmp(cmd, "APPE") == 0)) {
+ /*
+ * commands with data transfer. need to care about passive
+ * mode data connection.
+ */
+
+ if (ftp_passiveconn() < 0) {
+ n = sprintf(sbuf, "425 Cannot open data connetion\r\n");
+ write(src, sbuf, n);
+ } else {
+ /* simply relay the command */
+ write(dst, rbuf, n);
+ }
+
+ *state = NONE;
+ return n;
+ } else {
+ /* simply relay it */
+ *state = NONE;
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ bad:
+ exit_failure(ERRSTR);
+ /*NOTREACHED*/
+ return 0; /* to make gcc happy */
+}