make it more AF-neutral and v6-capable

git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@6631 ec53bebd-3082-4978-b11e-865c3cabbd6b
This commit is contained in:
Assar Westerlund
1999-07-28 13:36:35 +00:00
parent 4bc7ce877b
commit fea0b36f3c
2 changed files with 544 additions and 266 deletions

View File

@@ -34,14 +34,17 @@
#include "ftp_locl.h" #include "ftp_locl.h"
RCSID ("$Id$"); RCSID ("$Id$");
struct sockaddr_in hisctladdr; struct sockaddr_storage hisctladdr_ss;
struct sockaddr_in data_addr; struct sockaddr *hisctladdr = (struct sockaddr *)&hisctladdr_ss;
struct sockaddr_storage data_addr_ss;
struct sockaddr *data_addr = (struct sockaddr *)&data_addr_ss;
struct sockaddr_storage myctladdr_ss;
struct sockaddr *myctladdr = (struct sockaddr *)&myctladdr_ss;
int data = -1; int data = -1;
int abrtflag = 0; int abrtflag = 0;
jmp_buf ptabort; jmp_buf ptabort;
int ptabflg; int ptabflg;
int ptflag = 0; int ptflag = 0;
struct sockaddr_in myctladdr;
off_t restart_point = 0; off_t restart_point = 0;
@@ -50,77 +53,76 @@ FILE *cin, *cout;
typedef void (*sighand) (int); typedef void (*sighand) (int);
char * char *
hookup (char *host, int port) hookup (const char *host, int port)
{ {
struct hostent *hp = 0; struct hostent *hp = NULL;
int s, len; int s, len;
static char hostnamebuf[MaxHostNameLen]; static char hostnamebuf[MaxHostNameLen];
int error;
int af;
char **h;
int ret;
memset (&hisctladdr, 0, sizeof (hisctladdr)); #ifdef HAVE_IPV6
if (inet_aton (host, &hisctladdr.sin_addr)) { if (hp == NULL)
hisctladdr.sin_family = AF_INET; hp = getipnodebyname (host, AF_INET6, 0, &error);
strcpy_truncate (hostnamebuf, host, sizeof (hostnamebuf)); #endif
} else { if (hp == NULL)
hp = gethostbyname (host); hp = getipnodebyname (host, AF_INET, 0, &error);
if (hp == NULL) {
warnx("%s: %s", host, hstrerror(h_errno)); if (hp == NULL) {
code = -1; warnx ("%s: %s", host, hstrerror(error));
return NULL;
}
hisctladdr.sin_family = hp->h_addrtype;
memmove(&hisctladdr.sin_addr,
hp->h_addr_list[0],
sizeof(hisctladdr.sin_addr));
strcpy_truncate (hostnamebuf, hp->h_name, sizeof (hostnamebuf));
}
hostname = hostnamebuf;
s = socket (hisctladdr.sin_family, SOCK_STREAM, 0);
if (s < 0) {
warn ("socket");
code = -1; code = -1;
return (0); return NULL;
} }
hisctladdr.sin_port = port; strcpy_truncate (hostnamebuf, hp->h_name, sizeof(hostnamebuf));
while (connect (s, (struct sockaddr *) & hisctladdr, sizeof (hisctladdr)) < 0) { hostname = hostnamebuf;
if (hp && hp->h_addr_list[1]) { af = hisctladdr->sa_family = hp->h_addrtype;
int oerrno = errno;
char *ia;
ia = inet_ntoa (hisctladdr.sin_addr); for (h = hp->h_addr_list;
errno = oerrno; *h != NULL;
warn ("connect to address %s", ia); ++h) {
hp->h_addr_list++;
memmove (&hisctladdr.sin_addr, s = socket (af, SOCK_STREAM, 0);
hp->h_addr_list[0], if (s < 0) {
sizeof (hisctladdr.sin_addr)); warn ("socket");
fprintf (stdout, "Trying %s...\n", code = -1;
inet_ntoa (hisctladdr.sin_addr)); freehostent (hp);
return (0);
}
socket_set_address_and_port (hisctladdr, *h, port);
ret = connect (s, hisctladdr, socket_sockaddr_size(hisctladdr));
if (ret < 0) {
char addr[256];
if (inet_ntop (af, socket_get_address(hisctladdr),
addr, sizeof(addr)) == NULL)
strcpy_truncate (addr, "unknown address",
sizeof(addr));
warn ("connect %s", addr);
close (s); close (s);
s = socket (hisctladdr.sin_family, SOCK_STREAM, 0);
if (s < 0) {
warn ("socket");
code = -1;
return (0);
}
continue; continue;
} }
warn ("connect"); break;
code = -1;
goto bad;
} }
len = sizeof (myctladdr); freehostent (hp);
if (getsockname (s, (struct sockaddr *) & myctladdr, &len) < 0) { if (ret < 0) {
code = -1;
close (s);
return NULL;
}
len = sizeof(myctladdr_ss);
if (getsockname (s, myctladdr, &len) < 0) {
warn ("getsockname"); warn ("getsockname");
code = -1; code = -1;
goto bad; close (s);
} return NULL;
#if defined(IP_TOS) && defined(HAVE_SETSOCKOPT)
{
int tos = IPTOS_LOWDELAY;
if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
warn("setsockopt TOS (ignored)");
} }
#ifdef IPTOS_LOWDELAY
socket_set_tos (s, IPTOS_LOWDELAY);
#endif #endif
cin = fdopen (s, "r"); cin = fdopen (s, "r");
cout = fdopen (s, "w"); cout = fdopen (s, "w");
@@ -375,7 +377,7 @@ getreply (int expecteof)
osa.sa_handler != SIG_IGN) osa.sa_handler != SIG_IGN)
osa.sa_handler (SIGINT); osa.sa_handler (SIGINT);
#endif #endif
if (code == 227) { if (code == 227 || code == 229) {
char *p, *q; char *p, *q;
pasv[0] = 0; pasv[0] = 0;
@@ -1120,6 +1122,218 @@ abort:
signal (SIGINT, oldintr); signal (SIGINT, oldintr);
} }
static int
parse_epsv (const char *str)
{
char sep;
char *end;
int port;
if (*str == '\0')
return -1;
sep = *str++;
if (sep != *str++)
return -1;
if (sep != *str++)
return -1;
port = strtol (str, &end, 0);
if (str == end)
return -1;
if (end[0] != sep || end[1] != '\0')
return -1;
return htons(port);
}
static int
parse_pasv (struct sockaddr_in *sin, const char *str)
{
int a0, a1, a2, a3, p0, p1;
/*
* What we've got at this point is a string of comma separated
* one-byte unsigned integer values. The first four are the an IP
* address. The fifth is the MSB of the port number, the sixth is the
* LSB. From that we'll prepare a sockaddr_in.
*/
if (sscanf (str, "%d,%d,%d,%d,%d,%d",
&a0, &a1, &a2, &a3, &p0, &p1) != 6) {
printf ("Passive mode address scan failure. "
"Shouldn't happen!\n");
return -1;
}
if (a0 < 0 || a0 > 255 ||
a1 < 0 || a1 > 255 ||
a2 < 0 || a2 > 255 ||
a3 < 0 || a3 > 255 ||
p0 < 0 || p0 > 255 ||
p1 < 0 || p1 > 255) {
printf ("Can't parse passive mode string.\n");
return -1;
}
memset (sin, 0, sizeof(*sin));
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = htonl ((a0 << 24) | (a1 << 16) |
(a2 << 8) | a3);
sin->sin_port = htons ((p0 << 8) | p1);
return 0;
}
static int
passive_mode (void)
{
int port;
data = socket (myctladdr->sa_family, SOCK_STREAM, 0);
if (data < 0) {
warn ("socket");
return (1);
}
if (options & SO_DEBUG)
socket_set_debug (data);
if (command ("EPSV") != COMPLETE) {
if (command ("PASV") != COMPLETE) {
printf ("Passive mode refused.\n");
goto bad;
}
}
/*
* Parse the reply to EPSV or PASV
*/
port = parse_epsv (pasv);
if (port > 0) {
data_addr->sa_family = myctladdr->sa_family;
socket_set_address_and_port (data_addr,
socket_get_address (hisctladdr),
port);
} else {
if (parse_pasv ((struct sockaddr_in *)data_addr, pasv) < 0)
goto bad;
}
if (connect (data, data_addr, socket_sockaddr_size (data_addr)) < 0) {
warn ("connect");
goto bad;
}
#ifdef IPTOS_THROUGHPUT
socket_set_tos (data, IPTOS_THROUGHPUT);
#endif
return (0);
bad:
close (data);
data = -1;
sendport = 1;
return (1);
}
static int
active_mode (void)
{
int tmpno = 0;
int len;
int result;
noport:
data_addr->sa_family = myctladdr->sa_family;
socket_set_address_and_port (data_addr, socket_get_address (myctladdr),
sendport ? 0 : socket_get_port (myctladdr));
if (data != -1)
close (data);
data = socket (data_addr->sa_family, SOCK_STREAM, 0);
if (data < 0) {
warn ("socket");
if (tmpno)
sendport = 1;
return (1);
}
if (!sendport)
socket_set_reuseaddr (data, 1);
if (bind (data, data_addr, socket_sockaddr_size (data_addr)) < 0) {
warn ("bind");
goto bad;
}
if (options & SO_DEBUG)
socket_set_debug (data);
len = sizeof (data_addr_ss);
if (getsockname (data, data_addr, &len) < 0) {
warn ("getsockname");
goto bad;
}
if (listen (data, 1) < 0)
warn ("listen");
if (sendport) {
char *cmd;
char addr_str[256];
int inet_af;
if (inet_ntop (data_addr->sa_family, socket_get_address (data_addr),
addr_str, sizeof(addr_str)) == NULL)
errx (1, "inet_ntop failed");
switch (data_addr->sa_family) {
case AF_INET :
inet_af = 1;
break;
#ifdef HAVE_IPV6
case AF_INET6 :
inet_af = 2;
break;
#endif
default :
errx (1, "bad address family %d", data_addr->sa_family);
}
asprintf (&cmd, "EPRT |%d|%s|%d|",
inet_af, addr_str, ntohs(socket_get_port (data_addr)));
result = command (cmd);
if (result == ERROR) {
struct sockaddr_in *sin = (struct sockaddr_in *)data_addr;
unsigned int a = ntohl(sin->sin_addr.s_addr);
unsigned int p = ntohs(sin->sin_port);
if (data_addr->sa_family != AF_INET) {
warnx ("remote server doesn't support EPRT");
goto bad;
}
result = command("PORT %d,%d,%d,%d,%d,%d",
(a >> 24) & 0xff,
(a >> 16) & 0xff,
(a >> 8) & 0xff,
a & 0xff,
(p >> 8) & 0xff,
p & 0xff);
if (result == ERROR && sendport == -1) {
sendport = 0;
tmpno = 1;
goto noport;
}
return (result != COMPLETE);
}
return result != COMPLETE;
}
if (tmpno)
sendport = 1;
#ifdef IPTOS_THROUGHPUT
socket_set_tos (data, IPTOS_THROUGHPUT);
#endif
return (0);
bad:
close (data);
data = -1;
if (tmpno)
sendport = 1;
return (1);
}
/* /*
* Need to start a listen on the data channel before we send the command, * Need to start a listen on the data channel before we send the command,
* otherwise the server's connect may fail. * otherwise the server's connect may fail.
@@ -1127,147 +1341,23 @@ abort:
int int
initconn (void) initconn (void)
{ {
int result, len, tmpno = 0; if (passivemode)
int on = 1; return passive_mode ();
int a0, a1, a2, a3, p0, p1; else
return active_mode ();
if (passivemode) {
data = socket (AF_INET, SOCK_STREAM, 0);
if (data < 0) {
perror ("ftp: socket");
return (1);
}
#if defined(SO_DEBUG) && defined(HAVE_SETSOCKOPT)
if ((options & SO_DEBUG) &&
setsockopt (data, SOL_SOCKET, SO_DEBUG, (char *) &on,
sizeof (on)) < 0)
perror ("ftp: setsockopt (ignored)");
#endif
if (command ("PASV") != COMPLETE) {
printf ("Passive mode refused.\n");
goto bad;
}
/*
* What we've got at this point is a string of comma separated
* one-byte unsigned integer values. The first four are the an IP
* address. The fifth is the MSB of the port number, the sixth is the
* LSB. From that we'll prepare a sockaddr_in.
*/
if (sscanf (pasv, "%d,%d,%d,%d,%d,%d",
&a0, &a1, &a2, &a3, &p0, &p1) != 6) {
printf ("Passive mode address scan failure. "
"Shouldn't happen!\n");
goto bad;
}
if (a0 < 0 || a0 > 255 ||
a1 < 0 || a1 > 255 ||
a2 < 0 || a2 > 255 ||
a3 < 0 || a3 > 255 ||
p0 < 0 || p0 > 255 ||
p1 < 0 || p1 > 255) {
printf ("Can't parse passive mode string.\n");
goto bad;
}
memset(&data_addr, 0, sizeof(data_addr));
data_addr.sin_family = AF_INET;
data_addr.sin_addr.s_addr = htonl ((a0 << 24) | (a1 << 16) |
(a2 << 8) | a3);
data_addr.sin_port = htons ((p0 << 8) | p1);
if (connect (data, (struct sockaddr *) & data_addr,
sizeof (data_addr)) < 0) {
perror ("ftp: connect");
goto bad;
}
#if defined(IP_TOS) && defined(HAVE_SETSOCKOPT)
on = IPTOS_THROUGHPUT;
if (setsockopt (data, IPPROTO_IP, IP_TOS, (char *) &on,
sizeof (int)) < 0)
perror ("ftp: setsockopt TOS (ignored)");
#endif
return (0);
}
noport:
data_addr = myctladdr;
if (sendport)
data_addr.sin_port = 0; /* let system pick one */
if (data != -1)
close (data);
data = socket (AF_INET, SOCK_STREAM, 0);
if (data < 0) {
warn ("socket");
if (tmpno)
sendport = 1;
return (1);
}
#if defined(SO_REUSEADDR) && defined(HAVE_SETSOCKOPT)
if (!sendport)
if (setsockopt (data, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) {
warn ("setsockopt (reuse address)");
goto bad;
}
#endif
if (bind (data, (struct sockaddr *) & data_addr, sizeof (data_addr)) < 0) {
warn ("bind");
goto bad;
}
#if defined(SO_DEBUG) && defined(HAVE_SETSOCKOPT)
if (options & SO_DEBUG &&
setsockopt (data, SOL_SOCKET, SO_DEBUG, (char *) &on, sizeof (on)) < 0)
warn ("setsockopt (ignored)");
#endif
len = sizeof (data_addr);
if (getsockname (data, (struct sockaddr *) & data_addr, &len) < 0) {
warn ("getsockname");
goto bad;
}
if (listen (data, 1) < 0)
warn ("listen");
if (sendport) {
unsigned int a = ntohl(data_addr.sin_addr.s_addr);
unsigned int p = ntohs(data_addr.sin_port);
result = command("PORT %d,%d,%d,%d,%d,%d",
(a >> 24) & 0xff,
(a >> 16) & 0xff,
(a >> 8) & 0xff,
a & 0xff,
(p >> 8) & 0xff,
p & 0xff);
if (result == ERROR && sendport == -1) {
sendport = 0;
tmpno = 1;
goto noport;
}
return (result != COMPLETE);
}
if (tmpno)
sendport = 1;
#if defined(IP_TOS) && defined(HAVE_SETSOCKOPT)
on = IPTOS_THROUGHPUT;
if (setsockopt (data, IPPROTO_IP, IP_TOS, (char *) &on, sizeof (int)) < 0)
warn ("setsockopt TOS (ignored)");
#endif
return (0);
bad:
close (data), data = -1;
if (tmpno)
sendport = 1;
return (1);
} }
FILE * FILE *
dataconn (char *lmode) dataconn (char *lmode)
{ {
struct sockaddr_in from; struct sockaddr_storage from_ss;
int s, fromlen = sizeof (from), tos; struct sockaddr *from = (struct sockaddr *)&from_ss;
int s, fromlen = sizeof (from_ss);
if (passivemode) if (passivemode)
return (fdopen (data, lmode)); return (fdopen (data, lmode));
s = accept (data, (struct sockaddr *) & from, &fromlen); s = accept (data, from, &fromlen);
if (s < 0) { if (s < 0) {
warn ("accept"); warn ("accept");
close (data), data = -1; close (data), data = -1;
@@ -1275,10 +1365,8 @@ dataconn (char *lmode)
} }
close (data); close (data);
data = s; data = s;
#if defined(IP_TOS) && defined(HAVE_SETSOCKOPT) #ifdef IPTOS_THROUGHPUT
tos = IPTOS_THROUGHPUT; socket_set_tos (s, IPTOS_THROUGHPUT);
if (setsockopt (s, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof (int)) < 0)
warn ("setsockopt TOS (ignored)");
#endif #endif
return (fdopen (data, lmode)); return (fdopen (data, lmode));
} }
@@ -1334,8 +1422,8 @@ pswitch (int flag)
static struct comvars { static struct comvars {
int connect; int connect;
char name[MaxHostNameLen]; char name[MaxHostNameLen];
struct sockaddr_in mctl; struct sockaddr_storage mctl;
struct sockaddr_in hctl; struct sockaddr_storage hctl;
FILE *in; FILE *in;
FILE *out; FILE *out;
int tpe; int tpe;
@@ -1375,10 +1463,10 @@ pswitch (int flag)
} else } else
ip->name[0] = 0; ip->name[0] = 0;
hostname = op->name; hostname = op->name;
ip->hctl = hisctladdr; ip->hctl = hisctladdr_ss;
hisctladdr = op->hctl; hisctladdr_ss = op->hctl;
ip->mctl = myctladdr; ip->mctl = myctladdr_ss;
myctladdr = op->mctl; myctladdr_ss = op->mctl;
ip->in = cin; ip->in = cin;
cin = op->in; cin = op->in;
ip->out = cout; ip->out = cout;

View File

@@ -44,11 +44,20 @@ static char version[] = "Version 6.00";
extern off_t restart_point; extern off_t restart_point;
extern char cbuf[]; extern char cbuf[];
struct sockaddr_in ctrl_addr; struct sockaddr_storage ctrl_addr_ss;
struct sockaddr_in data_source; struct sockaddr *ctrl_addr = (struct sockaddr *)&ctrl_addr_ss;
struct sockaddr_in data_dest;
struct sockaddr_in his_addr; struct sockaddr_storage data_source_ss;
struct sockaddr_in pasv_addr; struct sockaddr *data_source = (struct sockaddr *)&data_source_ss;
struct sockaddr_storage data_dest_ss;
struct sockaddr *data_dest = (struct sockaddr *)&data_dest_ss;
struct sockaddr_storage his_addr_ss;
struct sockaddr *his_addr = (struct sockaddr *)&his_addr_ss;
struct sockaddr_storage pasv_addr_ss;
struct sockaddr *pasv_addr = (struct sockaddr *)&pasv_addr_ss;
int data; int data;
jmp_buf errcatch, urgcatch; jmp_buf errcatch, urgcatch;
@@ -126,7 +135,7 @@ static void myoob (int);
static int checkuser (char *, char *); static int checkuser (char *, char *);
static int checkaccess (char *); static int checkaccess (char *);
static FILE *dataconn (char *, off_t, char *); static FILE *dataconn (char *, off_t, char *);
static void dolog (struct sockaddr_in *); static void dolog (struct sockaddr *);
static void end_login (void); static void end_login (void);
static FILE *getdatasock (char *); static FILE *getdatasock (char *);
static char *gunique (char *); static char *gunique (char *);
@@ -305,22 +314,25 @@ main(int argc, char **argv)
* necessary for anonymous ftp's that chroot and can't do it later. * necessary for anonymous ftp's that chroot and can't do it later.
*/ */
openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP);
addrlen = sizeof(his_addr); addrlen = sizeof(his_addr_ss);
if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { if (getpeername(STDIN_FILENO, his_addr, &addrlen) < 0) {
syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
exit(1); exit(1);
} }
addrlen = sizeof(ctrl_addr); addrlen = sizeof(ctrl_addr_ss);
if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { if (getsockname(STDIN_FILENO, ctrl_addr, &addrlen) < 0) {
syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
exit(1); exit(1);
} }
#if defined(IP_TOS) && defined(HAVE_SETSOCKOPT) #if defined(IP_TOS) && defined(HAVE_SETSOCKOPT)
tos = IPTOS_LOWDELAY; tos = IPTOS_LOWDELAY;
if (setsockopt(0, IPPROTO_IP, IP_TOS, (void *)&tos, sizeof(int)) < 0) if (setsockopt(STDIN_FILENO, IPPROTO_IP, IP_TOS,
(void *)&tos, sizeof(int)) < 0)
syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
#endif #endif
data_source.sin_port = htons(ntohs(ctrl_addr.sin_port) - 1); data_source->sa_family = ctrl_addr->sa_family;
socket_set_port (data_source,
htons(ntohs(socket_get_port(ctrl_addr)) - 1));
/* set this here so it can be put in wtmp */ /* set this here so it can be put in wtmp */
snprintf(ttyline, sizeof(ttyline), "ftp%u", (unsigned)getpid()); snprintf(ttyline, sizeof(ttyline), "ftp%u", (unsigned)getpid());
@@ -345,7 +357,7 @@ main(int argc, char **argv)
if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1) if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
syslog(LOG_ERR, "fcntl F_SETOWN: %m"); syslog(LOG_ERR, "fcntl F_SETOWN: %m");
#endif #endif
dolog(&his_addr); dolog(his_addr);
/* /*
* Set up default state * Set up default state
*/ */
@@ -506,10 +518,19 @@ user(char *name)
reply(331, "Guest login ok, type your name as password."); reply(331, "Guest login ok, type your name as password.");
} else } else
reply(530, "User %s unknown.", name); reply(530, "User %s unknown.", name);
if (!askpasswd && logging) if (!askpasswd && logging) {
char data_addr[256];
if (inet_ntop (his_addr->sa_family,
socket_get_address(his_addr),
data_addr, sizeof(data_addr)) == NULL)
strcpy_truncate (data_addr, "unknown address",
sizeof(data_addr));
syslog(LOG_NOTICE, syslog(LOG_NOTICE,
"ANONYMOUS FTP LOGIN REFUSED FROM %s(%s)", "ANONYMOUS FTP LOGIN REFUSED FROM %s(%s)",
remotehost, inet_ntoa(his_addr.sin_addr)); remotehost, data_addr);
}
return; return;
} }
if((auth_level & AUTH_PLAIN) == 0 && !sec_complete){ if((auth_level & AUTH_PLAIN) == 0 && !sec_complete){
@@ -526,12 +547,23 @@ user(char *name)
if (cp == NULL || checkaccess(name)) { if (cp == NULL || checkaccess(name)) {
reply(530, "User %s access denied.", name); reply(530, "User %s access denied.", name);
if (logging) if (logging) {
char data_addr[256];
if (inet_ntop (his_addr->sa_family,
socket_get_address(his_addr),
data_addr,
sizeof(data_addr)) == NULL)
strcpy_truncate (data_addr,
"unknown address",
sizeof(data_addr));
syslog(LOG_NOTICE, syslog(LOG_NOTICE,
"FTP LOGIN REFUSED FROM %s(%s), %s", "FTP LOGIN REFUSED FROM %s(%s), %s",
remotehost, remotehost,
inet_ntoa(his_addr.sin_addr), data_addr,
name); name);
}
pw = (struct passwd *) NULL; pw = (struct passwd *) NULL;
return; return;
} }
@@ -725,22 +757,40 @@ int do_login(int code, char *passwd)
remotehost, remotehost,
passwd); passwd);
#endif /* HAVE_SETPROCTITLE */ #endif /* HAVE_SETPROCTITLE */
if (logging) if (logging) {
char data_addr[256];
if (inet_ntop (his_addr->sa_family,
socket_get_address(his_addr),
data_addr, sizeof(data_addr)) == NULL)
strcpy_truncate (data_addr, "unknown address",
sizeof(data_addr));
syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s(%s), %s", syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s(%s), %s",
remotehost, remotehost,
inet_ntoa(his_addr.sin_addr), data_addr,
passwd); passwd);
}
} else { } else {
reply(code, "User %s logged in.", pw->pw_name); reply(code, "User %s logged in.", pw->pw_name);
#ifdef HAVE_SETPROCTITLE #ifdef HAVE_SETPROCTITLE
snprintf(proctitle, sizeof(proctitle), "%s: %s", remotehost, pw->pw_name); snprintf(proctitle, sizeof(proctitle), "%s: %s", remotehost, pw->pw_name);
setproctitle(proctitle); setproctitle(proctitle);
#endif /* HAVE_SETPROCTITLE */ #endif /* HAVE_SETPROCTITLE */
if (logging) if (logging) {
char data_addr[256];
if (inet_ntop (his_addr->sa_family,
socket_get_address(his_addr),
data_addr, sizeof(data_addr)) == NULL)
strcpy_truncate (data_addr, "unknown address",
sizeof(data_addr));
syslog(LOG_INFO, "FTP LOGIN FROM %s(%s) as %s", syslog(LOG_INFO, "FTP LOGIN FROM %s(%s) as %s",
remotehost, remotehost,
inet_ntoa(his_addr.sin_addr), data_addr,
pw->pw_name); pw->pw_name);
}
} }
umask(defumask); umask(defumask);
return 0; return 0;
@@ -819,19 +869,27 @@ pass(char *passwd)
* local authentication succeeded. * local authentication succeeded.
*/ */
if (rval) { if (rval) {
char data_addr[256];
if (inet_ntop (his_addr->sa_family,
socket_get_address(his_addr),
data_addr, sizeof(data_addr)) == NULL)
strcpy_truncate (data_addr, "unknown address",
sizeof(data_addr));
reply(530, "Login incorrect."); reply(530, "Login incorrect.");
if (logging) if (logging)
syslog(LOG_NOTICE, syslog(LOG_NOTICE,
"FTP LOGIN FAILED FROM %s(%s), %s", "FTP LOGIN FAILED FROM %s(%s), %s",
remotehost, remotehost,
inet_ntoa(his_addr.sin_addr), data_addr,
curname); curname);
pw = NULL; pw = NULL;
if (login_attempts++ >= 5) { if (login_attempts++ >= 5) {
syslog(LOG_NOTICE, syslog(LOG_NOTICE,
"repeated login failures from %s(%s)", "repeated login failures from %s(%s)",
remotehost, remotehost,
inet_ntoa(his_addr.sin_addr)); data_addr);
exit(0); exit(0);
} }
return; return;
@@ -1068,35 +1126,31 @@ done:
static FILE * static FILE *
getdatasock(char *mode) getdatasock(char *mode)
{ {
int on = 1, s, t, tries; int s, t, tries;
if (data >= 0) if (data >= 0)
return (fdopen(data, mode)); return (fdopen(data, mode));
seteuid((uid_t)0); seteuid(0);
s = socket(AF_INET, SOCK_STREAM, 0); s = socket(ctrl_addr->sa_family, SOCK_STREAM, 0);
if (s < 0) if (s < 0)
goto bad; goto bad;
#if defined(SO_REUSEADDR) && defined(HAVE_SETSOCKOPT) socket_set_reuseaddr (s, 1);
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(void *) &on, sizeof(on)) < 0)
goto bad;
#endif
/* anchor socket to avoid multi-homing problems */ /* anchor socket to avoid multi-homing problems */
data_source.sin_family = AF_INET; socket_set_address_and_port (data_source,
data_source.sin_addr = ctrl_addr.sin_addr; socket_get_address (ctrl_addr),
0);
for (tries = 1; ; tries++) { for (tries = 1; ; tries++) {
if (bind(s, (struct sockaddr *)&data_source, if (bind(s, data_source,
sizeof(data_source)) >= 0) socket_sockaddr_size (data_source)) >= 0)
break; break;
if (errno != EADDRINUSE || tries > 10) if (errno != EADDRINUSE || tries > 10)
goto bad; goto bad;
sleep(tries); sleep(tries);
} }
seteuid((uid_t)pw->pw_uid); seteuid(pw->pw_uid);
#if defined(IP_TOS) && defined(HAVE_SETSOCKOPT) #ifdef IPTOS_THROUGHPUT
on = IPTOS_THROUGHPUT; socket_set_tos (s, IPTOS_THROUGHPUT);
if (setsockopt(s, IPPROTO_IP, IP_TOS, (void *)&on, sizeof(int)) < 0)
syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
#endif #endif
return (fdopen(s, mode)); return (fdopen(s, mode));
bad: bad:
@@ -1122,10 +1176,12 @@ dataconn(char *name, off_t size, char *mode)
else else
*sizebuf = '\0'; *sizebuf = '\0';
if (pdata >= 0) { if (pdata >= 0) {
struct sockaddr_in from; struct sockaddr_storage from_ss;
int s, fromlen = sizeof(from); struct sockaddr *from = (struct sockaddr *)&from;
int s;
int fromlen = sizeof(from_ss);
s = accept(pdata, (struct sockaddr *)&from, &fromlen); s = accept(pdata, from, &fromlen);
if (s < 0) { if (s < 0) {
reply(425, "Can't open data connection."); reply(425, "Can't open data connection.");
close(pdata); close(pdata);
@@ -1157,16 +1213,25 @@ dataconn(char *name, off_t size, char *mode)
usedefault = 1; usedefault = 1;
file = getdatasock(mode); file = getdatasock(mode);
if (file == NULL) { if (file == NULL) {
char data_addr[256];
if (inet_ntop (data_source->sa_family,
socket_get_address(data_source),
data_addr, sizeof(data_addr)) == NULL)
strcpy_truncate (data_addr, "unknown address",
sizeof(data_addr));
reply(425, "Can't create data socket (%s,%d): %s.", reply(425, "Can't create data socket (%s,%d): %s.",
inet_ntoa(data_source.sin_addr), data_addr,
ntohs(data_source.sin_port), strerror(errno)); socket_get_port (data_source),
strerror(errno));
return (NULL); return (NULL);
} }
data = fileno(file); data = fileno(file);
while (connect(data, (struct sockaddr *)&data_dest, while (connect(data, data_dest,
sizeof(data_dest)) < 0) { socket_sockaddr_size(data_dest)) < 0) {
if (errno == EADDRINUSE && retry < swaitmax) { if (errno == EADDRINUSE && retry < swaitmax) {
sleep((unsigned) swaitint); sleep(swaitint);
retry += swaitint; retry += swaitint;
continue; continue;
} }
@@ -1673,18 +1738,30 @@ renamecmd(char *from, char *to)
} }
static void static void
dolog(struct sockaddr_in *sin) dolog(struct sockaddr *sa)
{ {
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
inaddr2str (sin->sin_addr, remotehost, sizeof(remotehost)); inaddr2str (sin->sin_addr, remotehost, sizeof(remotehost));
#ifdef HAVE_SETPROCTITLE #ifdef HAVE_SETPROCTITLE
snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost);
setproctitle(proctitle); setproctitle(proctitle);
#endif /* HAVE_SETPROCTITLE */ #endif /* HAVE_SETPROCTITLE */
if (logging) if (logging) {
char data_addr[256];
if (inet_ntop (his_addr->sa_family,
socket_get_address(his_addr),
data_addr, sizeof(data_addr)) == NULL)
strcpy_truncate (data_addr, "unknown address",
sizeof(data_addr));
syslog(LOG_INFO, "connection from %s(%s)", syslog(LOG_INFO, "connection from %s(%s)",
remotehost, remotehost,
inet_ntoa(his_addr.sin_addr)); data_addr);
}
} }
/* /*
@@ -1766,31 +1843,41 @@ myoob(int signo)
* with Rick Adams on 25 Jan 89. * with Rick Adams on 25 Jan 89.
*/ */
void void
passive(void) pasv(void)
{ {
int len; int len;
char *p, *a; char *p, *a;
struct sockaddr_in *sin;
pdata = socket(AF_INET, SOCK_STREAM, 0); if (ctrl_addr->sa_family != AF_INET) {
reply(425,
"You cannot do PASV with something that's not IPv4");
return;
}
pdata = socket(ctrl_addr->sa_family, SOCK_STREAM, 0);
if (pdata < 0) { if (pdata < 0) {
perror_reply(425, "Can't open passive connection"); perror_reply(425, "Can't open passive connection");
return; return;
} }
pasv_addr = ctrl_addr; pasv_addr->sa_family = ctrl_addr->sa_family;
pasv_addr.sin_port = 0; socket_set_address_and_port (pasv_addr,
seteuid((uid_t)0); socket_get_address (ctrl_addr),
if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { 0);
seteuid((uid_t)pw->pw_uid); seteuid(0);
if (bind(pdata, pasv_addr, socket_sockaddr_size (pasv_addr)) < 0) {
seteuid(pw->pw_uid);
goto pasv_error; goto pasv_error;
} }
seteuid((uid_t)pw->pw_uid); seteuid(pw->pw_uid);
len = sizeof(pasv_addr); len = sizeof(pasv_addr_ss);
if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) if (getsockname(pdata, pasv_addr, &len) < 0)
goto pasv_error; goto pasv_error;
if (listen(pdata, 1) < 0) if (listen(pdata, 1) < 0)
goto pasv_error; goto pasv_error;
a = (char *) &pasv_addr.sin_addr; sin = (struct sockaddr_in *)pasv_addr;
p = (char *) &pasv_addr.sin_port; a = (char *) &sin->sin_addr;
p = (char *) &sin->sin_port;
#define UC(b) (((int) b) & 0xff) #define UC(b) (((int) b) & 0xff)
@@ -1805,6 +1892,109 @@ pasv_error:
return; return;
} }
void
epsv(char *proto)
{
int len;
pdata = socket(ctrl_addr->sa_family, SOCK_STREAM, 0);
if (pdata < 0) {
perror_reply(425, "Can't open passive connection");
return;
}
pasv_addr->sa_family = ctrl_addr->sa_family;
socket_set_address_and_port (pasv_addr,
socket_get_address (ctrl_addr),
0);
seteuid(0);
if (bind(pdata, pasv_addr, socket_sockaddr_size (pasv_addr)) < 0) {
seteuid(pw->pw_uid);
goto pasv_error;
}
seteuid(pw->pw_uid);
len = sizeof(pasv_addr_ss);
if (getsockname(pdata, pasv_addr, &len) < 0)
goto pasv_error;
if (listen(pdata, 1) < 0)
goto pasv_error;
reply(229, "Entering Extended Passive Mode (|||%d|)",
ntohs(socket_get_port (pasv_addr)));
return;
pasv_error:
close(pdata);
pdata = -1;
perror_reply(425, "Can't open passive connection");
return;
}
void
eprt(char *str)
{
char *end;
char sep;
int af;
int ret;
int port;
usedefault = 0;
if (pdata >= 0) {
close(pdata);
pdata = -1;
}
sep = *str++;
if (sep == '\0') {
reply(500, "Bad syntax in EPRT");
return;
}
af = strtol (str, &end, 0);
if (af == 0 || *end != sep) {
reply(500, "Bad syntax in EPRT");
return;
}
str = end + 1;
switch (af) {
#ifdef HAVE_IPV6
case 2 :
data_dest->sa_family = AF_INET6;
break;
#endif
case 1 :
data_dest->sa_family = AF_INET;
break;
default :
reply(522, "Network protocol %d not supported, use (1"
#ifdef HAVE_IPV6
",2"
#endif
")", af);
return;
}
end = strchr (str, sep);
if (end == NULL) {
reply(500, "Bad syntax in EPRT");
return;
}
*end = '\0';
ret = inet_pton (data_dest->sa_family, str,
socket_get_address (data_dest));
if (ret != 1) {
reply(500, "Bad address syntax in EPRT");
return;
}
str = end + 1;
port = strtol (str, &end, 0);
if (port == 0 || *end != sep) {
reply(500, "Bad port syntax in EPRT");
return;
}
socket_set_port (data_dest, htons(port));
reply(200, "EPRT command successful.");
}
/* /*
* Generate unique name for file with basename "local". * Generate unique name for file with basename "local".
* The file named "local" is already known to exist. * The file named "local" is already known to exist.