summaryrefslogtreecommitdiffstats
path: root/libexec/ftpd/ftpd.c
diff options
context:
space:
mode:
authorshin <shin@FreeBSD.org>2000-01-27 09:28:38 +0000
committershin <shin@FreeBSD.org>2000-01-27 09:28:38 +0000
commitce15efb7c04858f00b57c16093d4a3043809048e (patch)
tree8b3d00f78a4a5a34cc3b17e29c28b4472d93a35c /libexec/ftpd/ftpd.c
parentdcbae417f8f4365a5eea807290162acd308b720d (diff)
downloadFreeBSD-src-ce15efb7c04858f00b57c16093d4a3043809048e.zip
FreeBSD-src-ce15efb7c04858f00b57c16093d4a3043809048e.tar.gz
another tcp apps IPv6 updates.(should be make world safe)
ftp, telnet, ftpd, faithd also telnet related sync with crypto, secure, kerberosIV Obtained from: KAME project
Diffstat (limited to 'libexec/ftpd/ftpd.c')
-rw-r--r--libexec/ftpd/ftpd.c503
1 files changed, 415 insertions, 88 deletions
diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c
index 0f9d20c..cd72ed2 100644
--- a/libexec/ftpd/ftpd.c
+++ b/libexec/ftpd/ftpd.c
@@ -110,15 +110,20 @@ static const char rcsid[] =
static char version[] = "Version 6.00LS";
#undef main
+/* wrapper for KAME-special getnameinfo() */
+#ifndef NI_WITHSCOPEID
+#define NI_WITHSCOPEID 0
+#endif
+
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;
int data;
@@ -155,9 +160,11 @@ char *hostname;
#ifdef VIRTUAL_HOSTING
char *ftpuser;
+int epsvall = 0;
+
static struct ftphost {
struct ftphost *next;
- struct in_addr hostaddr;
+ union sockunion hostaddr;
char *hostname;
char *anonuser;
char *statfile;
@@ -176,7 +183,6 @@ char *tty = ttyline; /* for klogin */
static int auth_pam __P((struct passwd**, const char*));
#endif
-struct in_addr bind_address;
char *pid_file = NULL;
/*
@@ -200,7 +206,7 @@ char proctitle[LINE_MAX]; /* initial part of title */
#ifdef SKEY
int pwok = 0;
-char addr_string[20]; /* XXX */
+char addr_string[INET6_ADDRSTRLEN]; /* XXX */
#endif
#define LOGCMD(cmd, file) \
@@ -224,13 +230,13 @@ char addr_string[20]; /* XXX */
#ifdef VIRTUAL_HOSTING
static void inithosts __P((void));
-static void selecthost __P((struct in_addr *));
+static void selecthost __P((union sockunion *));
#endif
static void ack __P((char *));
static void myoob __P((int));
static int checkuser __P((char *, char *, int));
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 *));
@@ -266,6 +272,10 @@ main(argc, argv, envp)
int addrlen, ch, on = 1, tos;
char *cp, line[LINE_MAX];
FILE *fd;
+ int error;
+ char *bindname = NULL;
+ int family = AF_UNSPEC;
+ int enable_v4 = 0;
tzset(); /* in case no timezone database in ~ftp */
@@ -280,8 +290,7 @@ main(argc, argv, envp)
#endif /* OLD_SETPROCTITLE */
- bind_address.s_addr = htonl(INADDR_ANY);
- while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:")) != -1) {
+ while ((ch = getopt(argc, argv, "AdlDSURt:T:u:va:p:46")) != -1) {
switch (ch) {
case 'D':
daemon_mode++;
@@ -320,8 +329,7 @@ main(argc, argv, envp)
break;
case 'a':
- if (!inet_aton(optarg, &bind_address))
- errx(1, "invalid address for -a");
+ bindname = optarg;
break;
case 'p':
@@ -347,6 +355,16 @@ main(argc, argv, envp)
debug = 1;
break;
+ case '4':
+ enable_v4 = 1;
+ if (family == AF_UNSPEC)
+ family = AF_INET;
+ break;
+
+ case '6':
+ family = AF_INET6;
+ break;
+
default:
warnx("unknown flag -%c ignored", optopt);
break;
@@ -366,7 +384,7 @@ main(argc, argv, envp)
if (daemon_mode) {
int ctl_sock, fd;
- struct servent *sv;
+ struct addrinfo hints, *res;
/*
* Detach from parent.
@@ -376,30 +394,56 @@ main(argc, argv, envp)
exit(1);
}
(void) signal(SIGCHLD, reapchild);
- /*
- * Get port number for ftp/tcp.
- */
- sv = getservbyname("ftp", "tcp");
- if (sv == NULL) {
- syslog(LOG_ERR, "getservbyname for ftp failed");
+ /* init bind_sa */
+ memset(&hints, 0, sizeof(hints));
+
+ hints.ai_family = family == AF_UNSPEC ? AF_INET : family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ hints.ai_flags = AI_PASSIVE;
+ error = getaddrinfo(bindname, "ftp", &hints, &res);
+ if (error) {
+ if (family == AF_UNSPEC) {
+ hints.ai_family = AF_UNSPEC;
+ error = getaddrinfo(bindname, "ftp", &hints,
+ &res);
+ }
+ if (error == 0 && res->ai_addr != NULL)
+ family = res->ai_addr->sa_family;
+ }
+ if (error) {
+ syslog(LOG_ERR, gai_strerror(error));
+ if (error == EAI_SYSTEM)
+ syslog(LOG_ERR, strerror(errno));
+ exit(1);
+ }
+ if (res->ai_addr == NULL) {
+ syslog(LOG_ERR, "-a %s: getaddrinfo failed", hostname);
exit(1);
}
/*
* 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);
}
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 = bind_address;
- server_addr.sin_port = sv->s_port;
- if (bind(ctl_sock, (struct sockaddr *)&server_addr, sizeof(server_addr))) {
+ syslog(LOG_ERR, "control setsockopt: %m");
+#ifdef IPV6_BINDV6ONLY
+ if (family == AF_INET6 && enable_v4 == 0) {
+ if (setsockopt(ctl_sock, IPPROTO_IPV6, IPV6_BINDV6ONLY,
+ (char *)&on, sizeof (on)) < 0)
+ syslog(LOG_ERR,
+ "control setsockopt(IPV6_BINDV6ONLY): %m");
+ }
+#endif /* IPV6_BINDV6ONLY */
+ memcpy(&server_addr, res->ai_addr, res->ai_addr->sa_len);
+ if (bind(ctl_sock, (struct sockaddr *)&server_addr,
+ server_addr.su_len) < 0) {
syslog(LOG_ERR, "control bind: %m");
exit(1);
}
@@ -434,7 +478,7 @@ main(argc, argv, envp)
* children to handle them.
*/
while (1) {
- addrlen = sizeof(his_addr);
+ addrlen = server_addr.su_len;
fd = accept(ctl_sock, (struct sockaddr *)&his_addr, &addrlen);
if (fork() == 0) {
/* child */
@@ -459,7 +503,9 @@ main(argc, argv, envp)
syslog(LOG_ERR, "signal: %m");
#ifdef SKEY
- strncpy(addr_string, inet_ntoa(his_addr.sin_addr), sizeof(addr_string));
+ getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
+ addr_string, sizeof(addr_string) - 1, NULL, 0,
+ NI_NUMERICHOST|NI_WITHSCOPEID);
#endif
addrlen = sizeof(ctrl_addr);
if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
@@ -468,12 +514,15 @@ main(argc, argv, envp)
}
#ifdef VIRTUAL_HOSTING
/* select our identity from virtual host table */
- selecthost(&ctrl_addr.sin_addr);
+ selecthost(&ctrl_addr);
#endif
#ifdef IP_TOS
+ if (ctrl_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
/*
* Disable Nagle on the control channel so that we don't have to wait
@@ -482,7 +531,7 @@ main(argc, argv, envp)
if (setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
syslog(LOG_WARNING, "control setsockopt TCP_NODELAY: %m");
- data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1);
+ data_source.su_port = htons(ntohs(ctrl_addr.su_port) - 1);
/* set this here so klogin can use it... */
(void)snprintf(ttyline, sizeof(ttyline), "ftp%d", getpid());
@@ -497,7 +546,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
*/
@@ -567,9 +616,9 @@ inithosts()
{
FILE *fp;
char *cp;
- struct hostent *hp;
struct ftphost *hrp, *lhrp;
char line[1024];
+ struct addrinfo hints, *res, *ai;
/*
* Fill in the default host information
@@ -579,20 +628,28 @@ inithosts()
if ((hrp = malloc(sizeof(struct ftphost))) == NULL ||
(hrp->hostname = strdup(line)) == NULL)
fatal("Ran out of memory.");
- memset(&hrp->hostaddr, 0, sizeof hrp->hostaddr);
- if ((hp = gethostbyname(hrp->hostname)) != NULL)
- (void) memcpy(&hrp->hostaddr,
- hp->h_addr_list[0],
- sizeof(hrp->hostaddr));
+ memset(&hrp->hostaddr, 0, sizeof(hrp->hostaddr));
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = AF_UNSPEC;
+ getaddrinfo(hrp->hostname, NULL, &hints, &res);
+ if (res)
+ memcpy(&hrp->hostaddr, res->ai_addr, res->ai_addrlen);
hrp->statfile = _PATH_FTPDSTATFILE;
hrp->welcome = _PATH_FTPWELCOME;
hrp->loginmsg = _PATH_FTPLOGINMESG;
hrp->anonuser = "ftp";
hrp->next = NULL;
thishost = firsthost = lhrp = hrp;
+ freeaddrinfo(res);
if ((fp = fopen(_PATH_FTPHOSTS, "r")) != NULL) {
+ int addrsize, error;
+ void *addr;
+ struct hostent *hp;
+
while (fgets(line, sizeof(line), fp) != NULL) {
- int i;
+ int i, hp_error;
if ((cp = strchr(line, '\n')) == NULL) {
/* ignore long lines */
@@ -606,13 +663,21 @@ inithosts()
/* skip comments and empty lines */
if (cp == NULL || line[0] == '#')
continue;
- /* first, try a standard gethostbyname() */
- if ((hp = gethostbyname(cp)) == NULL)
+
+ hints.ai_flags = 0;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_PASSIVE;
+ error = getaddrinfo(cp, NULL, &hints, &res);
+ if (error != NULL)
continue;
+ for (ai = res; ai != NULL && ai->ai_addr != NULL;
+ ai = ai->ai_next)
+ {
+
for (hrp = firsthost; hrp != NULL; hrp = hrp->next) {
if (memcmp(&hrp->hostaddr,
- hp->h_addr_list[0],
- sizeof(hrp->hostaddr)) == 0)
+ ai->ai_addr,
+ ai->ai_addr->sa_len) == 0)
break;
}
if (hrp == NULL) {
@@ -628,16 +693,32 @@ inithosts()
lhrp = hrp;
}
(void) memcpy(&hrp->hostaddr,
- hp->h_addr_list[0],
- sizeof(hrp->hostaddr));
+ ai->ai_addr,
+ ai->ai_addr->sa_len);
/*
* determine hostname to use.
- * force defined name if it is a valid alias
+ * force defined name if there is a valid alias
* otherwise fallback to primary hostname
*/
- if ((hp = gethostbyaddr((char*)&hrp->hostaddr,
- sizeof(hrp->hostaddr),
- AF_INET)) != NULL) {
+ /* XXX: getaddrinfo() can't do alias check */
+ switch(hrp->hostaddr.su_family) {
+ case AF_INET:
+ addr = &((struct sockaddr_in *)&hrp->hostaddr)->sin_addr;
+ addrsize = sizeof(struct sockaddr_in);
+ break;
+ case AF_INET6:
+ addr = &((struct sockaddr_in6 *)&hrp->hostaddr)->sin6_addr;
+ addrsize = sizeof(struct sockaddr_in6);
+ break;
+ default:
+ /* should not reach here */
+ free(hrp);
+ continue;
+ /* NOTREACHED */
+ }
+ if ((hp = getipnodebyaddr((char*)addr, addrsize,
+ hrp->hostaddr.su_family,
+ &hp_error)) != NULL) {
if (strcmp(cp, hp->h_name) != 0) {
if (hp->h_aliases == NULL)
cp = hp->h_name;
@@ -652,6 +733,7 @@ inithosts()
}
}
hrp->hostname = strdup(cp);
+ freehostent(hp);
/* ok, now we now peel off the rest */
i = 0;
while (i < 4 && (cp = strtok(NULL, " \t")) != NULL) {
@@ -673,25 +755,55 @@ inithosts()
}
++i;
}
+ /* XXX: re-initialization for getaddrinfo() loop */
+ cp = strtok(line, " \t");
+ }
}
(void) fclose(fp);
}
}
static void
-selecthost(a)
- struct in_addr *a;
+selecthost(su)
+ union sockunion *su;
{
struct ftphost *hrp;
+ u_int16_t port;
+#ifdef INET6
+ struct in6_addr *mapped_in6 = NULL;
+#endif
+
+#ifdef INET6
+ /*
+ * XXX IPv4 mapped IPv6 addr consideraton,
+ * specified in rfc2373.
+ */
+ if (su->su_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&su->su_sin6.sin6_addr))
+ mapped_in6 = &su->su_sin6.sin6_addr;
+#endif
hrp = thishost = firsthost; /* default */
+ port = su->su_port;
+ su->su_port = 0;
while (hrp != NULL) {
- if (memcmp(a, &hrp->hostaddr, sizeof(hrp->hostaddr)) == 0) {
+ if (memcmp(su, &hrp->hostaddr, sizeof(hrp->hostaddr)) == 0) {
+ thishost = hrp;
+ break;
+ }
+#ifdef INET6
+ /* XXX IPv4 mapped IPv6 addr consideraton */
+ if (hrp->hostaddr.su_family == AF_INET && mapped_in6 != NULL &&
+ (memcmp(&mapped_in6->s6_addr[12],
+ &hrp->hostaddr.su_sin.sin_addr,
+ sizeof(struct in_addr)) == 0)) {
thishost = hrp;
break;
}
+#endif
hrp = hrp->next;
}
+ su->su_port = port;
/* setup static variables as appropriate */
hostname = thishost->hostname;
ftpuser = thishost->anonuser;
@@ -1107,8 +1219,9 @@ skip:
if ((lc = login_getpwclass(pw)) != NULL) {
char remote_ip[MAXHOSTNAMELEN];
- strncpy(remote_ip, inet_ntoa(his_addr.sin_addr),
- sizeof(remote_ip) - 1);
+ getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
+ remote_ip, sizeof(remote_ip) - 1, NULL, 0,
+ NI_NUMERICHOST|NI_WITHSCOPEID);
remote_ip[sizeof(remote_ip) - 1] = 0;
if (!auth_hostok(lc, remotehost, remote_ip)) {
syslog(LOG_INFO|LOG_AUTH,
@@ -1402,19 +1515,19 @@ getdatasock(mode)
if (data >= 0)
return (fdopen(data, mode));
(void) seteuid((uid_t)0);
- s = socket(AF_INET, SOCK_STREAM, 0);
+
+ s = socket(data_dest.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;
@@ -1422,9 +1535,12 @@ getdatasock(mode)
}
(void) seteuid((uid_t)pw->pw_uid);
#ifdef IP_TOS
+ if (data_source.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
/*
@@ -1470,8 +1586,8 @@ dataconn(name, size, mode)
else
*sizebuf = '\0';
if (pdata >= 0) {
- struct sockaddr_in from;
- int s, fromlen = sizeof(from);
+ union sockunion from;
+ int s, fromlen = ctrl_addr.su_len;
struct timeval timeout;
fd_set set;
@@ -1491,9 +1607,12 @@ dataconn(name, size, mode)
(void) close(pdata);
pdata = s;
#ifdef IP_TOS
+ if (from.su_family == AF_INET)
+ {
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);
@@ -1510,14 +1629,18 @@ 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 hostbuf[BUFSIZ], portbuf[BUFSIZ];
+ getnameinfo((struct sockaddr *)&data_source,
+ data_source.su_len, hostbuf, sizeof(hostbuf) - 1,
+ portbuf, sizeof(portbuf),
+ NI_NUMERICHOST|NI_NUMERICSERV|NI_WITHSCOPEID);
+ reply(425, "Can't create data socket (%s,%s): %s.",
+ hostbuf, portbuf, strerror(errno));
return (NULL);
}
data = fileno(file);
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;
@@ -1768,14 +1891,20 @@ statfilecmd(filename)
void
statcmd()
{
- struct sockaddr_in *sin;
+ union sockunion *su;
u_char *a, *p;
+ char hname[INET6_ADDRSTRLEN];
+ int ispassive;
lreply(211, "%s FTP server status:", hostname, version);
printf(" %s\r\n", version);
printf(" Connected to %s", remotehost);
- if (!isdigit(remotehost[0]))
- printf(" (%s)", inet_ntoa(his_addr.sin_addr));
+ if (!getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
+ hname, sizeof(hname) - 1, NULL, 0,
+ NI_NUMERICHOST|NI_WITHSCOPEID)) {
+ if (strcmp(hname, remotehost) != 0)
+ printf(" (%s)", hname);
+ }
printf("\r\n");
if (logged_in) {
if (guest)
@@ -1800,18 +1929,85 @@ statcmd()
if (data != -1)
printf(" Data connection open\r\n");
else if (pdata != -1) {
- printf(" in Passive mode");
- sin = &pasv_addr;
+ ispassive = 1;
+ su = &pasv_addr;
goto printaddr;
} else if (usedefault == 0) {
- printf(" PORT");
- sin = &data_dest;
+ ispassive = 0;
+ su = &data_dest;
printaddr:
- a = (u_char *) &sin->sin_addr;
- p = (u_char *) &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]));
+ if (epsvall) {
+ printf(" EPSV only mode (EPSV ALL)\r\n");
+ goto epsvonly;
+ }
+
+ /* PORT/PASV */
+ if (su->su_family == AF_INET) {
+ a = (u_char *) &su->su_sin.sin_addr;
+ p = (u_char *) &su->su_sin.sin_port;
+ printf(" %s (%d,%d,%d,%d,%d,%d)\r\n",
+ ispassive ? "PASV" : "PORT",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(p[0]), UC(p[1]));
+ }
+
+ /* LPRT/LPSV */
+ {
+ int alen, af, i;
+
+ 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) {
+ printf(" %s (%d,%d,", ispassive ? "LPSV" : "LPRT",
+ af, alen);
+ for (i = 0; i < alen; i++)
+ printf("%d,", UC(a[i]));
+ printf("%d,%d,%d)\r\n", 2, UC(p[0]), UC(p[1]));
+ }
+ }
+
+epsvonly:;
+ /* EPRT/EPSV */
+ {
+ int af;
+
+ switch (su->su_family) {
+ case AF_INET:
+ af = 1;
+ break;
+ case AF_INET6:
+ af = 2;
+ break;
+ default:
+ af = 0;
+ break;
+ }
+ if (af) {
+ if (!getnameinfo((struct sockaddr *)su, su->su_len,
+ hname, sizeof(hname) - 1, NULL, 0,
+ NI_NUMERICHOST)) {
+ printf(" %s |%d|%s|%d|\r\n",
+ ispassive ? "EPSV" : "EPRT",
+ af, hname, htons(su->su_port));
+ }
+ }
+ }
#undef UC
} else
printf(" No data connection\r\n");
@@ -2015,10 +2211,12 @@ renamecmd(from, to)
}
static void
-dolog(sin)
- struct sockaddr_in *sin;
+dolog(who)
+ struct sockaddr *who;
{
- realhostname(remotehost, sizeof(remotehost) - 1, &sin->sin_addr);
+ int error;
+
+ realhostname_sa(remotehost, sizeof(remotehost) - 1, who, who->sa_len);
#ifdef SETPROCTITLE
#ifdef VIRTUAL_HOSTING
@@ -2039,8 +2237,16 @@ dolog(sin)
remotehost, hostname);
else
#endif
+ {
+ char who_name[MAXHOSTNAMELEN];
+
+ error = getnameinfo(who, who->sa_len,
+ who_name, sizeof(who_name) - 1,
+ NULL, 0,
+ NI_NUMERICHOST|NI_WITHSCOPEID);
syslog(LOG_INFO, "connection from %s (%s)", remotehost,
- inet_ntoa(sin->sin_addr));
+ error == 0 ? who_name : "");
+ }
}
}
@@ -2112,7 +2318,7 @@ passive()
if (pdata >= 0) /* close old port if one set */
close(pdata);
- pdata = socket(AF_INET, SOCK_STREAM, 0);
+ pdata = socket(ctrl_addr.su_family, SOCK_STREAM, 0);
if (pdata < 0) {
perror_reply(425, "Can't open passive connection");
return;
@@ -2121,7 +2327,7 @@ passive()
(void) seteuid((uid_t)0);
#ifdef IP_PORTRANGE
- {
+ if (ctrl_addr.su_family == AF_INET) {
int on = restricted_data_ports ? IP_PORTRANGE_HIGH
: IP_PORTRANGE_DEFAULT;
@@ -2132,9 +2338,8 @@ passive()
#endif
pasv_addr = ctrl_addr;
- pasv_addr.sin_port = 0;
- if (bind(pdata, (struct sockaddr *)&pasv_addr,
- sizeof(pasv_addr)) < 0)
+ pasv_addr.su_port = 0;
+ if (bind(pdata, (struct sockaddr *)&pasv_addr, pasv_addr.su_len) < 0)
goto pasv_error;
(void) seteuid((uid_t)pw->pw_uid);
@@ -2144,8 +2349,15 @@ passive()
goto pasv_error;
if (listen(pdata, 1) < 0)
goto pasv_error;
- a = (char *) &pasv_addr.sin_addr;
- p = (char *) &pasv_addr.sin_port;
+ if (pasv_addr.su_family == AF_INET)
+ a = (char *) &pasv_addr.su_sin.sin_addr;
+ else if (pasv_addr.su_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr))
+ a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12];
+ else
+ goto pasv_error;
+
+ p = (char *) &pasv_addr.su_port;
#define UC(b) (((int) b) & 0xff)
@@ -2162,6 +2374,121 @@ pasv_error:
}
/*
+ * Long Passive defined in RFC 1639.
+ * 228 Entering Long Passive Mode
+ * (af, hal, h1, h2, h3,..., pal, p1, p2...)
+ */
+
+void
+long_passive(cmd, pf)
+ char *cmd;
+ int pf;
+{
+ int len;
+ char *p, *a;
+
+ if (pdata >= 0) /* close old port if one set */
+ close(pdata);
+
+ 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;
+ }
+
+ (void) seteuid((uid_t)0);
+
+ pasv_addr = ctrl_addr;
+ pasv_addr.su_port = 0;
+ len = pasv_addr.su_len;
+
+ if (bind(pdata, (struct sockaddr *)&pasv_addr, len) < 0)
+ goto pasv_error;
+
+ (void) seteuid((uid_t)pw->pw_uid);
+
+ if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
+ goto pasv_error;
+ if (listen(pdata, 1) < 0)
+ goto pasv_error;
+
+#define UC(b) (((int) b) & 0xff)
+
+ if (strcmp(cmd, "LPSV") == 0) {
+ p = (char *)&pasv_addr.su_port;
+ switch (pasv_addr.su_family) {
+ case AF_INET:
+ a = (char *) &pasv_addr.su_sin.sin_addr;
+ v4_reply:
+ 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:
+ if (IN6_IS_ADDR_V4MAPPED(&pasv_addr.su_sin6.sin6_addr)) {
+ a = (char *) &pasv_addr.su_sin6.sin6_addr.s6_addr[12];
+ goto v4_reply;
+ }
+ 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;
+ }
+ } 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) seteuid((uid_t)pw->pw_uid);
+ (void) close(pdata);
+ pdata = -1;
+ perror_reply(425, "Can't open passive connection");
+ return;
+}
+
+/*
* Generate unique name for file with basename "local".
* The file named "local" is already known to exist.
* Generates failure reply on error.
OpenPOWER on IntegriCloud