summaryrefslogtreecommitdiff
path: root/libexec/ftpd/ftpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'libexec/ftpd/ftpd.c')
-rw-r--r--libexec/ftpd/ftpd.c478
1 files changed, 397 insertions, 81 deletions
diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c
index 23a1120a08a..e9c4b2446d1 100644
--- a/libexec/ftpd/ftpd.c
+++ b/libexec/ftpd/ftpd.c
@@ -1,7 +1,36 @@
-/* $OpenBSD: ftpd.c,v 1.62 1999/12/03 01:22:46 millert Exp $ */
+/* $OpenBSD: ftpd.c,v 1.63 1999/12/08 13:15:21 itojun Exp $ */
/* $NetBSD: ftpd.c,v 1.15 1995/06/03 22:46:47 mycroft Exp $ */
/*
+ * 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.
+ */
+
+/*
* Copyright (c) 1985, 1988, 1990, 1992, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
@@ -111,12 +140,12 @@ static char version[] = "Version 6.4/OpenBSD";
extern off_t restart_point;
extern char cbuf[];
-struct sockaddr_in server_addr;
-struct sockaddr_in ctrl_addr;
-struct sockaddr_in data_source;
-struct sockaddr_in data_dest;
-struct sockaddr_in his_addr;
-struct sockaddr_in pasv_addr;
+union sockunion server_addr;
+union sockunion ctrl_addr;
+union sockunion data_source;
+union sockunion data_dest;
+union sockunion his_addr;
+union sockunion pasv_addr;
int daemon_mode = 0;
int data;
@@ -142,6 +171,7 @@ int mode;
int doutmp = 0; /* update utmp file */
int usedefault = 1; /* for data transfers */
int pdata = -1; /* for passive mode */
+int family = AF_INET;
sig_atomic_t transflag;
off_t file_size;
off_t byte_count;
@@ -172,6 +202,8 @@ char *krbtkfile_env = NULL;
char *ident = NULL;
+int epsvall = 0;
+
/*
* Timeout intervals for retrying connections
* to hosts that don't accept PORT cmds. This
@@ -210,7 +242,7 @@ static void ack __P((char *));
static void myoob __P((int));
static int checkuser __P((char *, char *));
static FILE *dataconn __P((char *, off_t, char *));
-static void dolog __P((struct sockaddr_in *));
+static void dolog __P((struct sockaddr *));
static char *curdir __P((void));
static void end_login __P((void));
static FILE *getdatasock __P((char *));
@@ -224,7 +256,7 @@ static struct passwd *
sgetpwnam __P((char *));
static char *sgetsave __P((char *));
static void reapchild __P((int));
-static int check_host __P((struct sockaddr_in *));
+static int check_host __P((struct sockaddr *));
static void usage __P((void));
void logxfer __P((char *, off_t, time_t));
@@ -242,7 +274,7 @@ curdir()
return (guest ? path+1 : path);
}
-char *argstr = "AdDhlMSt:T:u:UvP";
+char *argstr = "AdDhlMSt:T:u:UvP46";
static void
usage()
@@ -335,6 +367,14 @@ main(argc, argv, envp)
debug = 1;
break;
+ case '4':
+ family = AF_INET;
+ break;
+
+ case '6':
+ family = AF_INET6;
+ break;
+
default:
usage();
break;
@@ -373,7 +413,7 @@ main(argc, argv, envp)
* Open a socket, bind it to the FTP port, and start
* listening.
*/
- ctl_sock = socket(AF_INET, SOCK_STREAM, 0);
+ ctl_sock = socket(family, SOCK_STREAM, 0);
if (ctl_sock < 0) {
syslog(LOG_ERR, "control socket: %m");
exit(1);
@@ -381,11 +421,20 @@ main(argc, argv, envp)
if (setsockopt(ctl_sock, SOL_SOCKET, SO_REUSEADDR,
(char *)&on, sizeof(on)) < 0)
syslog(LOG_ERR, "control setsockopt: %m");;
- server_addr.sin_family = AF_INET;
- server_addr.sin_addr.s_addr = INADDR_ANY;
- server_addr.sin_port = sv->s_port;
+ memset(&server_addr, 0, sizeof(server_addr));
+ server_addr.su_sin.sin_family = family;
+ switch (family) {
+ case AF_INET:
+ server_addr.su_len = sizeof(struct sockaddr_in);
+ server_addr.su_sin.sin_port = sv->s_port;
+ break;
+ case AF_INET6:
+ server_addr.su_len = sizeof(struct sockaddr_in6);
+ server_addr.su_sin6.sin6_port = sv->s_port;
+ break;
+ }
if (bind(ctl_sock, (struct sockaddr *)&server_addr,
- sizeof(server_addr))) {
+ server_addr.su_len)) {
syslog(LOG_ERR, "control bind: %m");
exit(1);
}
@@ -421,7 +470,7 @@ main(argc, argv, envp)
#if defined(TCPWRAPPERS)
/* ..in the child. */
- if (!check_host(&his_addr))
+ if (!check_host((struct sockaddr *)&his_addr))
exit(1);
#endif /* TCPWRAPPERS */
} else {
@@ -450,12 +499,55 @@ main(argc, argv, envp)
syslog(LOG_ERR, "getsockname (%s): %m", argv[0]);
exit(1);
}
+ if (his_addr.su_family == AF_INET6
+ && IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) {
+#if 1
+ /*
+ * IPv4 control connection arrived to AF_INET6 socket.
+ * I hate to do this, but this is the easiest solution.
+ */
+ union sockunion tmp_addr;
+ const int off = sizeof(struct in6_addr) - sizeof(struct in_addr);
+
+ tmp_addr = his_addr;
+ memset(&his_addr, 0, sizeof(his_addr));
+ his_addr.su_sin.sin_family = AF_INET;
+ his_addr.su_sin.sin_len = sizeof(his_addr.su_sin);
+ memcpy(&his_addr.su_sin.sin_addr,
+ &tmp_addr.su_sin6.sin6_addr.s6_addr[off],
+ sizeof(his_addr.su_sin.sin_addr));
+ his_addr.su_sin.sin_port = tmp_addr.su_sin6.sin6_port;
+
+ tmp_addr = ctrl_addr;
+ memset(&ctrl_addr, 0, sizeof(ctrl_addr));
+ ctrl_addr.su_sin.sin_family = AF_INET;
+ ctrl_addr.su_sin.sin_len = sizeof(ctrl_addr.su_sin);
+ memcpy(&ctrl_addr.su_sin.sin_addr,
+ &tmp_addr.su_sin6.sin6_addr.s6_addr[off],
+ sizeof(ctrl_addr.su_sin.sin_addr));
+ ctrl_addr.su_sin.sin_port = tmp_addr.su_sin6.sin6_port;
+#else
+ while (fgets(line, sizeof(line), fd) != NULL) {
+ if ((cp = strchr(line, '\n')) != NULL)
+ *cp = '\0';
+ lreply(530, "%s", line);
+ }
+ (void) fflush(stdout);
+ (void) fclose(fd);
+ reply(530,
+ "Connection from IPv4 mapped address is not supported.");
+ exit(0);
+#endif
+ }
#ifdef IP_TOS
- tos = IPTOS_LOWDELAY;
- if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
- syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+ if (his_addr.su_family == AF_INET) {
+ tos = IPTOS_LOWDELAY;
+ if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos,
+ sizeof(int)) < 0)
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+ }
#endif
- data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
+ data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1);
/* Try to handle urgent data inline */
#ifdef SO_OOBINLINE
@@ -467,7 +559,7 @@ main(argc, argv, envp)
if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
syslog(LOG_ERR, "fcntl F_SETOWN: %m");
#endif
- dolog(&his_addr);
+ dolog((struct sockaddr *)&his_addr);
/*
* Set up default state
*/
@@ -508,14 +600,8 @@ main(argc, argv, envp)
strcpy(hostname, hp->h_name);
if (multihome) {
- hp = gethostbyaddr((char *) &ctrl_addr.sin_addr,
- sizeof (struct in_addr), AF_INET);
- if (hp != NULL) {
- strcpy(dhostname, hp->h_name);
- } else {
- /* Default. */
- strcpy(dhostname, inet_ntoa(ctrl_addr.sin_addr));
- }
+ getnameinfo((struct sockaddr *)&ctrl_addr, ctrl_addr.su_len,
+ dhostname, sizeof(dhostname), NULL, 0, 0);
}
reply(220, "%s FTP server (%s) ready.",
@@ -1112,19 +1198,18 @@ getdatasock(mode)
sigfillset(&allsigs);
sigprocmask (SIG_BLOCK, &allsigs, NULL);
(void) seteuid((uid_t)0);
- s = socket(AF_INET, SOCK_STREAM, 0);
+ s = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
if (s < 0)
goto bad;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(char *) &on, sizeof(on)) < 0)
goto bad;
/* anchor socket to avoid multi-homing problems */
- data_source.sin_len = sizeof(struct sockaddr_in);
- data_source.sin_family = AF_INET;
- data_source.sin_addr = ctrl_addr.sin_addr;
+ data_source = ctrl_addr;
+ data_source.su_port = htons(20); /* ftp-data port */
for (tries = 1; ; tries++) {
if (bind(s, (struct sockaddr *)&data_source,
- sizeof(data_source)) >= 0)
+ data_source.su_len) >= 0)
break;
if (errno != EADDRINUSE || tries > 10)
goto bad;
@@ -1135,9 +1220,12 @@ getdatasock(mode)
sigprocmask (SIG_UNBLOCK, &allsigs, NULL);
#ifdef IP_TOS
- on = IPTOS_THROUGHPUT;
- if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
- syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+ if (ctrl_addr.su_family == AF_INET) {
+ on = IPTOS_THROUGHPUT;
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on,
+ sizeof(int)) < 0)
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+ }
#endif
#ifdef TCP_NOPUSH
/*
@@ -1176,7 +1264,10 @@ dataconn(name, size, mode)
{
char sizebuf[32];
FILE *file;
- int retry = 0, tos;
+ int retry = 0;
+ in_port_t *p;
+ char *fa, *ha;
+ int alen;
file_size = size;
byte_count = 0;
@@ -1186,7 +1277,7 @@ dataconn(name, size, mode)
} else
sizebuf[0] = '\0';
if (pdata >= 0) {
- struct sockaddr_in from;
+ union sockunion from;
int s, fromlen = sizeof(from);
signal (SIGALRM, toolong);
@@ -1199,14 +1290,35 @@ dataconn(name, size, mode)
pdata = -1;
return (NULL);
}
- if (ntohs(from.sin_port) < IPPORT_RESERVED) {
+ switch (from.su_family) {
+ case AF_INET:
+ p = (in_port_t *)&from.su_sin.sin_port;
+ fa = (u_char *)&from.su_sin.sin_addr;
+ ha = (u_char *)&his_addr.su_sin.sin_addr;
+ alen = sizeof(struct in_addr);;
+ break;
+ case AF_INET6:
+ p = (in_port_t *)&from.su_sin6.sin6_port;
+ fa = (u_char *)&from.su_sin6.sin6_addr;
+ ha = (u_char *)&his_addr.su_sin6.sin6_addr;
+ alen = sizeof(struct in6_addr);;
+ break;
+ default:
+ perror_reply(425, "Can't build data connection");
+ (void) close(pdata);
+ (void) close(s);
+ pdata = -1;
+ return (NULL);
+ }
+ if (from.su_family != his_addr.su_family
+ || ntohs(*p) < IPPORT_RESERVED) {
perror_reply(425, "Can't build data connection");
(void) close(pdata);
(void) close(s);
pdata = -1;
return (NULL);
}
- if (from.sin_addr.s_addr != his_addr.sin_addr.s_addr) {
+ if (memcmp(fa, ha, alen) != 0) {
perror_reply(435, "Can't build data connection");
(void) close(pdata);
(void) close(s);
@@ -1215,11 +1327,6 @@ dataconn(name, size, mode)
}
(void) close(pdata);
pdata = s;
-#ifdef IP_TOS
- tos = IPTOS_THROUGHPUT;
- (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
- sizeof(int));
-#endif
reply(150, "Opening %s mode data connection for '%s'%s.",
type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
return (fdopen(pdata, mode));
@@ -1235,9 +1342,12 @@ dataconn(name, size, mode)
usedefault = 1;
file = getdatasock(mode);
if (file == NULL) {
- reply(425, "Can't create data socket (%s,%d): %s.",
- inet_ntoa(data_source.sin_addr),
- ntohs(data_source.sin_port), strerror(errno));
+ char hbuf[MAXHOSTNAMELEN], pbuf[10];
+ getnameinfo((struct sockaddr *)&data_source, data_source.su_len,
+ hbuf, sizeof(hbuf), pbuf, sizeof(pbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ reply(425, "Can't create data socket (%s,%s): %s.",
+ hbuf, pbuf, strerror(errno));
return (NULL);
}
data = fileno(file);
@@ -1246,21 +1356,40 @@ dataconn(name, size, mode)
* attempt to connect to reserved port on client machine;
* this looks like an attack
*/
- if (ntohs(data_dest.sin_port) < IPPORT_RESERVED ||
- ntohs(data_dest.sin_port) == 2049) { /* XXX */
+ switch (data_dest.su_family) {
+ case AF_INET:
+ p = (in_port_t *)&data_dest.su_sin.sin_port;
+ fa = (u_char *)&data_dest.su_sin.sin_addr;
+ ha = (u_char *)&his_addr.su_sin.sin_addr;
+ alen = sizeof(struct in_addr);;
+ break;
+ case AF_INET6:
+ p = (in_port_t *)&data_dest.su_sin6.sin6_port;
+ fa = (u_char *)&data_dest.su_sin6.sin6_addr;
+ ha = (u_char *)&his_addr.su_sin6.sin6_addr;
+ alen = sizeof(struct in6_addr);;
+ break;
+ default:
+ perror_reply(425, "Can't build data connection");
+ (void) fclose(file);
+ pdata = -1;
+ return (NULL);
+ }
+ if (data_dest.su_family != his_addr.su_family
+ || ntohs(*p) < IPPORT_RESERVED || ntohs(*p) == 2049) { /* XXX */
perror_reply(425, "Can't build data connection");
(void) fclose(file);
data = -1;
return NULL;
}
- if (data_dest.sin_addr.s_addr != his_addr.sin_addr.s_addr) {
+ if (memcmp(fa, ha, alen) != 0) {
perror_reply(435, "Can't build data connection");
(void) fclose(file);
data = -1;
return NULL;
}
while (connect(data, (struct sockaddr *)&data_dest,
- sizeof(data_dest)) < 0) {
+ data_dest.su_len) < 0) {
if (errno == EADDRINUSE && retry < swaitmax) {
sleep((unsigned) swaitint);
retry += swaitint;
@@ -1516,14 +1645,18 @@ statfilecmd(filename)
void
statcmd()
{
- struct sockaddr_in *sin;
+ union sockunion *su;
u_char *a, *p;
+ char hbuf[MAXHOSTNAMELEN];
+ int ispassive;
lreply(211, "%s FTP server status:", hostname, version);
printf(" %s\r\n", version);
+ getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
+ hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST);
printf(" Connected to %s", remotehost);
- if (!isdigit(remotehost[0]))
- printf(" (%s)", inet_ntoa(his_addr.sin_addr));
+ if (strcmp(remotehost, hbuf) != 0)
+ printf(" (%s)", hbuf);
printf("\r\n");
if (logged_in) {
if (guest)
@@ -1545,22 +1678,96 @@ statcmd()
#endif
printf("; STRUcture: %s; transfer MODE: %s\r\n",
strunames[stru], modenames[mode]);
+ ispassive = 0;
if (data != -1)
printf(" Data connection open\r\n");
else if (pdata != -1) {
printf(" in Passive mode");
- sin = &pasv_addr;
+ su = (union sockunion *)&pasv_addr;
+ ispassive++;
goto printaddr;
} else if (usedefault == 0) {
- printf(" PORT");
- sin = &data_dest;
+ su = (union sockunion *)&data_dest;
printaddr:
- a = (u_char *) &sin->sin_addr;
- p = (u_char *) &sin->sin_port;
+ /* PASV/PORT */
+ if (su->su_family == AF_INET) {
+ if (ispassive)
+ printf("211- PASV ");
+ else
+ printf("211- PORT ");
+ a = (u_char *) &su->su_sin.sin_addr;
+ p = (u_char *) &su->su_sin.sin_port;
#define UC(b) (((int) b) & 0xff)
- printf(" (%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]));
+ printf("(%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]));
+ }
+
+ /* LPSV/LPRT */
+ {
+ int alen, af, i;
+
+ alen = 0;
+ switch (su->su_family) {
+ case AF_INET:
+ a = (u_char *) &su->su_sin.sin_addr;
+ p = (u_char *) &su->su_sin.sin_port;
+ alen = sizeof(su->su_sin.sin_addr);
+ af = 4;
+ break;
+ case AF_INET6:
+ a = (u_char *) &su->su_sin6.sin6_addr;
+ p = (u_char *) &su->su_sin6.sin6_port;
+ alen = sizeof(su->su_sin6.sin6_addr);
+ af = 6;
+ break;
+ default:
+ af = 0;
+ break;
+ }
+ if (af) {
+ if (ispassive)
+ printf("211- LPSV ");
+ else
+ printf("211- LPRT ");
+ printf("(%d,%d", af, alen);
+ for (i = 0; i < alen; i++)
+ printf("%d,", UC(a[alen]));
+ printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1]));
#undef UC
+ }
+ }
+
+ /* EPRT/EPSV */
+epsvonly:;
+ {
+ int af;
+
+ switch (su->su_family) {
+ case AF_INET:
+ af = 1;
+ break;
+ case AF_INET6:
+ af = 2;
+ break;
+ default:
+ af = 0;
+ break;
+ }
+ if (af) {
+ char hbuf[MAXHOSTNAMELEN], pbuf[10];
+ if (getnameinfo((struct sockaddr *)su, su->su_len,
+ hbuf, sizeof(hbuf), pbuf, sizeof(pbuf),
+ NI_NUMERICHOST) == 0) {
+ if (ispassive)
+ printf("211 - EPSV ");
+ else
+ printf("211 - EPRT ");
+ printf("(|%d|%s|%s|)\r\n",
+ af, hbuf, pbuf);
+ }
+ }
+ }
} else
printf(" No data connection\r\n");
reply(211, "End of status");
@@ -1785,17 +1992,14 @@ renamecmd(from, to)
}
static void
-dolog(sin)
- struct sockaddr_in *sin;
+dolog(sa)
+ struct sockaddr *sa;
{
- struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
- sizeof(struct in_addr), AF_INET);
+ char hbuf[sizeof(remotehost)];
+
+ getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), NULL, 0, 0);
+ (void) strncpy(remotehost, hbuf, sizeof(remotehost)-1);
- if (hp)
- (void) strncpy(remotehost, hp->h_name, sizeof(remotehost)-1);
- else
- (void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
- sizeof(remotehost)-1);
remotehost[sizeof(remotehost)-1] = '\0';
#ifdef HASSETPROCTITLE
snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
@@ -1899,9 +2103,9 @@ passive()
#endif
pasv_addr = ctrl_addr;
- pasv_addr.sin_port = 0;
+ pasv_addr.su_sin.sin_port = 0;
if (bind(pdata, (struct sockaddr *)&pasv_addr,
- sizeof(pasv_addr)) < 0)
+ pasv_addr.su_len) < 0)
goto pasv_error;
len = sizeof(pasv_addr);
@@ -1909,8 +2113,8 @@ passive()
goto pasv_error;
if (listen(pdata, 1) < 0)
goto pasv_error;
- a = (char *) &pasv_addr.sin_addr;
- p = (char *) &pasv_addr.sin_port;
+ a = (char *) &pasv_addr.su_sin.sin_addr;
+ p = (char *) &pasv_addr.su_sin.sin_port;
#define UC(b) (((int) b) & 0xff)
@@ -1919,6 +2123,111 @@ passive()
return;
pasv_error:
+ (void) seteuid((uid_t)pw->pw_uid);
+ (void) close(pdata);
+ pdata = -1;
+ perror_reply(425, "Can't open passive connection");
+ return;
+}
+
+/*
+ * 228 Entering Long Passive Mode (af, hal, h1, h2, h3,..., pal, p1, p2...)
+ * 229 Entering Extended Passive Mode (|||port|)
+ */
+void
+long_passive(char *cmd, int pf)
+{
+ int len;
+ register char *p, *a;
+
+ if (!logged_in) {
+ syslog(LOG_NOTICE, "long passive but not logged in");
+ reply(503, "Login with USER first.");
+ return;
+ }
+
+ if (pf != PF_UNSPEC) {
+ if (ctrl_addr.su_family != pf) {
+ switch (ctrl_addr.su_family) {
+ case AF_INET:
+ pf = 1;
+ break;
+ case AF_INET6:
+ pf = 2;
+ break;
+ default:
+ pf = 0;
+ break;
+ }
+ /*
+ * XXX
+ * only EPRT/EPSV ready clients will understand this
+ */
+ if (strcmp(cmd, "EPSV") == 0 && pf) {
+ reply(522, "Network protocol mismatch, "
+ "use (%d)", pf);
+ } else
+ reply(501, "Network protocol mismatch"); /*XXX*/
+
+ return;
+ }
+ }
+
+ pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
+ if (pdata < 0) {
+ perror_reply(425, "Can't open passive connection");
+ return;
+ }
+ pasv_addr = ctrl_addr;
+ pasv_addr.su_port = 0;
+ (void) seteuid((uid_t) 0);
+ if (bind(pdata, (struct sockaddr *) &pasv_addr, pasv_addr.su_len) < 0) {
+ (void) seteuid((uid_t) pw->pw_uid);
+ goto pasv_error;
+ }
+ (void) seteuid((uid_t) pw->pw_uid);
+ len = pasv_addr.su_len;
+ if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
+ goto pasv_error;
+ if (listen(pdata, 1) < 0)
+ goto pasv_error;
+ p = (char *) &pasv_addr.su_port;
+
+#define UC(b) (((int) b) & 0xff)
+
+ if (strcmp(cmd, "LPSV") == 0) {
+ switch (pasv_addr.su_family) {
+ case AF_INET:
+ a = (char *) &pasv_addr.su_sin.sin_addr;
+ reply(228, "Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d)",
+ 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ 2, UC(p[0]), UC(p[1]));
+ return;
+ case AF_INET6:
+ a = (char *) &pasv_addr.su_sin6.sin6_addr;
+ reply(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)",
+ 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]));
+ return;
+ }
+#undef UC
+ } else if (strcmp(cmd, "EPSV") == 0) {
+ switch (pasv_addr.su_family) {
+ case AF_INET:
+ case AF_INET6:
+ reply(229, "Entering Extended Passive Mode (|||%d|)",
+ ntohs(pasv_addr.su_port));
+ return;
+ }
+ } else {
+ /* more proper error code? */
+ }
+
+ pasv_error:
(void) close(pdata);
pdata = -1;
perror_reply(425, "Can't open passive connection");
@@ -2172,13 +2481,20 @@ logxfer(name, size, start)
#if defined(TCPWRAPPERS)
static int
-check_host(sin)
- struct sockaddr_in *sin;
+check_host(sa)
+ struct sockaddr *sa;
{
- struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
- sizeof(struct in_addr), AF_INET);
- char *addr = inet_ntoa(sin->sin_addr);
+ struct sockaddr_in *sin;
+ struct hostent *hp;
+ char *addr;
+ if (sa->sa_family != AF_INET)
+ return 1; /*XXX*/
+
+ sin = (struct sockaddr_in *)sa;
+ hp = gethostbyaddr((char *)&sin->sin_addr,
+ sizeof(struct in_addr), AF_INET);
+ addr = inet_ntoa(sin->sin_addr);
if (hp) {
if (!hosts_ctl("ftpd", hp->h_name, addr, STRING_UNKNOWN)) {
syslog(LOG_NOTICE, "tcpwrappers rejected: %s [%s]",