From fea0b36f3c822a52116f347c08f3fcd607cbea02 Mon Sep 17 00:00:00 2001 From: Assar Westerlund Date: Wed, 28 Jul 1999 13:36:35 +0000 Subject: [PATCH] make it more AF-neutral and v6-capable git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@6631 ec53bebd-3082-4978-b11e-865c3cabbd6b --- appl/ftp/ftp/ftp.c | 494 +++++++++++++++++++++++++------------------ appl/ftp/ftpd/ftpd.c | 316 +++++++++++++++++++++------ 2 files changed, 544 insertions(+), 266 deletions(-) diff --git a/appl/ftp/ftp/ftp.c b/appl/ftp/ftp/ftp.c index 4fff40f51..379a7d690 100644 --- a/appl/ftp/ftp/ftp.c +++ b/appl/ftp/ftp/ftp.c @@ -34,14 +34,17 @@ #include "ftp_locl.h" RCSID ("$Id$"); -struct sockaddr_in hisctladdr; -struct sockaddr_in data_addr; +struct sockaddr_storage hisctladdr_ss; +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 abrtflag = 0; jmp_buf ptabort; int ptabflg; int ptflag = 0; -struct sockaddr_in myctladdr; off_t restart_point = 0; @@ -50,77 +53,76 @@ FILE *cin, *cout; typedef void (*sighand) (int); char * -hookup (char *host, int port) +hookup (const char *host, int port) { - struct hostent *hp = 0; + struct hostent *hp = NULL; int s, len; static char hostnamebuf[MaxHostNameLen]; + int error; + int af; + char **h; + int ret; - memset (&hisctladdr, 0, sizeof (hisctladdr)); - if (inet_aton (host, &hisctladdr.sin_addr)) { - hisctladdr.sin_family = AF_INET; - strcpy_truncate (hostnamebuf, host, sizeof (hostnamebuf)); - } else { - hp = gethostbyname (host); - if (hp == NULL) { - warnx("%s: %s", host, hstrerror(h_errno)); - code = -1; - 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"); +#ifdef HAVE_IPV6 + if (hp == NULL) + hp = getipnodebyname (host, AF_INET6, 0, &error); +#endif + if (hp == NULL) + hp = getipnodebyname (host, AF_INET, 0, &error); + + if (hp == NULL) { + warnx ("%s: %s", host, hstrerror(error)); code = -1; - return (0); + return NULL; } - hisctladdr.sin_port = port; - while (connect (s, (struct sockaddr *) & hisctladdr, sizeof (hisctladdr)) < 0) { - if (hp && hp->h_addr_list[1]) { - int oerrno = errno; - char *ia; + strcpy_truncate (hostnamebuf, hp->h_name, sizeof(hostnamebuf)); + hostname = hostnamebuf; + af = hisctladdr->sa_family = hp->h_addrtype; - ia = inet_ntoa (hisctladdr.sin_addr); - errno = oerrno; - warn ("connect to address %s", ia); - hp->h_addr_list++; - memmove (&hisctladdr.sin_addr, - hp->h_addr_list[0], - sizeof (hisctladdr.sin_addr)); - fprintf (stdout, "Trying %s...\n", - inet_ntoa (hisctladdr.sin_addr)); + for (h = hp->h_addr_list; + *h != NULL; + ++h) { + + s = socket (af, SOCK_STREAM, 0); + if (s < 0) { + warn ("socket"); + code = -1; + 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); - s = socket (hisctladdr.sin_family, SOCK_STREAM, 0); - if (s < 0) { - warn ("socket"); - code = -1; - return (0); - } continue; } - warn ("connect"); - code = -1; - goto bad; + break; } - len = sizeof (myctladdr); - if (getsockname (s, (struct sockaddr *) & myctladdr, &len) < 0) { + freehostent (hp); + if (ret < 0) { + code = -1; + close (s); + return NULL; + } + + len = sizeof(myctladdr_ss); + if (getsockname (s, myctladdr, &len) < 0) { warn ("getsockname"); code = -1; - goto bad; - } -#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)"); + close (s); + return NULL; } +#ifdef IPTOS_LOWDELAY + socket_set_tos (s, IPTOS_LOWDELAY); #endif cin = fdopen (s, "r"); cout = fdopen (s, "w"); @@ -375,7 +377,7 @@ getreply (int expecteof) osa.sa_handler != SIG_IGN) osa.sa_handler (SIGINT); #endif - if (code == 227) { + if (code == 227 || code == 229) { char *p, *q; pasv[0] = 0; @@ -1120,6 +1122,218 @@ abort: 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, * otherwise the server's connect may fail. @@ -1127,147 +1341,23 @@ abort: int initconn (void) { - int result, len, tmpno = 0; - int on = 1; - int a0, a1, a2, a3, p0, p1; - - 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); + if (passivemode) + return passive_mode (); + else + return active_mode (); } FILE * dataconn (char *lmode) { - struct sockaddr_in from; - int s, fromlen = sizeof (from), tos; + struct sockaddr_storage from_ss; + struct sockaddr *from = (struct sockaddr *)&from_ss; + int s, fromlen = sizeof (from_ss); if (passivemode) return (fdopen (data, lmode)); - s = accept (data, (struct sockaddr *) & from, &fromlen); + s = accept (data, from, &fromlen); if (s < 0) { warn ("accept"); close (data), data = -1; @@ -1275,10 +1365,8 @@ dataconn (char *lmode) } close (data); data = s; -#if defined(IP_TOS) && defined(HAVE_SETSOCKOPT) - tos = IPTOS_THROUGHPUT; - if (setsockopt (s, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof (int)) < 0) - warn ("setsockopt TOS (ignored)"); +#ifdef IPTOS_THROUGHPUT + socket_set_tos (s, IPTOS_THROUGHPUT); #endif return (fdopen (data, lmode)); } @@ -1334,8 +1422,8 @@ pswitch (int flag) static struct comvars { int connect; char name[MaxHostNameLen]; - struct sockaddr_in mctl; - struct sockaddr_in hctl; + struct sockaddr_storage mctl; + struct sockaddr_storage hctl; FILE *in; FILE *out; int tpe; @@ -1375,10 +1463,10 @@ pswitch (int flag) } else ip->name[0] = 0; hostname = op->name; - ip->hctl = hisctladdr; - hisctladdr = op->hctl; - ip->mctl = myctladdr; - myctladdr = op->mctl; + ip->hctl = hisctladdr_ss; + hisctladdr_ss = op->hctl; + ip->mctl = myctladdr_ss; + myctladdr_ss = op->mctl; ip->in = cin; cin = op->in; ip->out = cout; diff --git a/appl/ftp/ftpd/ftpd.c b/appl/ftp/ftpd/ftpd.c index aefe73c20..ee5863487 100644 --- a/appl/ftp/ftpd/ftpd.c +++ b/appl/ftp/ftpd/ftpd.c @@ -44,11 +44,20 @@ static char version[] = "Version 6.00"; extern off_t restart_point; extern char cbuf[]; -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; +struct sockaddr_storage ctrl_addr_ss; +struct sockaddr *ctrl_addr = (struct sockaddr *)&ctrl_addr_ss; + +struct sockaddr_storage data_source_ss; +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; jmp_buf errcatch, urgcatch; @@ -126,7 +135,7 @@ static void myoob (int); static int checkuser (char *, char *); static int checkaccess (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 FILE *getdatasock (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. */ openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); - addrlen = sizeof(his_addr); - if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) { + addrlen = sizeof(his_addr_ss); + if (getpeername(STDIN_FILENO, his_addr, &addrlen) < 0) { syslog(LOG_ERR, "getpeername (%s): %m",argv[0]); exit(1); } - addrlen = sizeof(ctrl_addr); - if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) { + addrlen = sizeof(ctrl_addr_ss); + if (getsockname(STDIN_FILENO, ctrl_addr, &addrlen) < 0) { syslog(LOG_ERR, "getsockname (%s): %m",argv[0]); exit(1); } #if defined(IP_TOS) && defined(HAVE_SETSOCKOPT) 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"); #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 */ 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) syslog(LOG_ERR, "fcntl F_SETOWN: %m"); #endif - dolog(&his_addr); + dolog(his_addr); /* * Set up default state */ @@ -506,10 +518,19 @@ user(char *name) reply(331, "Guest login ok, type your name as password."); } else 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, "ANONYMOUS FTP LOGIN REFUSED FROM %s(%s)", - remotehost, inet_ntoa(his_addr.sin_addr)); + remotehost, data_addr); + } return; } if((auth_level & AUTH_PLAIN) == 0 && !sec_complete){ @@ -526,12 +547,23 @@ user(char *name) if (cp == NULL || checkaccess(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, "FTP LOGIN REFUSED FROM %s(%s), %s", remotehost, - inet_ntoa(his_addr.sin_addr), + data_addr, name); + } pw = (struct passwd *) NULL; return; } @@ -725,22 +757,40 @@ int do_login(int code, char *passwd) remotehost, passwd); #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", remotehost, - inet_ntoa(his_addr.sin_addr), + data_addr, passwd); + } } else { reply(code, "User %s logged in.", pw->pw_name); #ifdef HAVE_SETPROCTITLE snprintf(proctitle, sizeof(proctitle), "%s: %s", remotehost, pw->pw_name); setproctitle(proctitle); #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", remotehost, - inet_ntoa(his_addr.sin_addr), + data_addr, pw->pw_name); + } } umask(defumask); return 0; @@ -819,19 +869,27 @@ pass(char *passwd) * local authentication succeeded. */ 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."); if (logging) syslog(LOG_NOTICE, "FTP LOGIN FAILED FROM %s(%s), %s", remotehost, - inet_ntoa(his_addr.sin_addr), + data_addr, curname); pw = NULL; if (login_attempts++ >= 5) { syslog(LOG_NOTICE, "repeated login failures from %s(%s)", remotehost, - inet_ntoa(his_addr.sin_addr)); + data_addr); exit(0); } return; @@ -1068,35 +1126,31 @@ done: static FILE * getdatasock(char *mode) { - int on = 1, s, t, tries; + int s, t, tries; if (data >= 0) return (fdopen(data, mode)); - seteuid((uid_t)0); - s = socket(AF_INET, SOCK_STREAM, 0); + seteuid(0); + s = socket(ctrl_addr->sa_family, SOCK_STREAM, 0); if (s < 0) goto bad; -#if defined(SO_REUSEADDR) && defined(HAVE_SETSOCKOPT) - if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, - (void *) &on, sizeof(on)) < 0) - goto bad; -#endif + socket_set_reuseaddr (s, 1); /* anchor socket to avoid multi-homing problems */ - data_source.sin_family = AF_INET; - data_source.sin_addr = ctrl_addr.sin_addr; + socket_set_address_and_port (data_source, + socket_get_address (ctrl_addr), + 0); + for (tries = 1; ; tries++) { - if (bind(s, (struct sockaddr *)&data_source, - sizeof(data_source)) >= 0) + if (bind(s, data_source, + socket_sockaddr_size (data_source)) >= 0) break; if (errno != EADDRINUSE || tries > 10) goto bad; sleep(tries); } - seteuid((uid_t)pw->pw_uid); -#if defined(IP_TOS) && defined(HAVE_SETSOCKOPT) - on = IPTOS_THROUGHPUT; - if (setsockopt(s, IPPROTO_IP, IP_TOS, (void *)&on, sizeof(int)) < 0) - syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); + seteuid(pw->pw_uid); +#ifdef IPTOS_THROUGHPUT + socket_set_tos (s, IPTOS_THROUGHPUT); #endif return (fdopen(s, mode)); bad: @@ -1122,10 +1176,12 @@ dataconn(char *name, off_t size, char *mode) else *sizebuf = '\0'; if (pdata >= 0) { - struct sockaddr_in from; - int s, fromlen = sizeof(from); + struct sockaddr_storage from_ss; + 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) { reply(425, "Can't open data connection."); close(pdata); @@ -1157,16 +1213,25 @@ dataconn(char *name, off_t size, char *mode) usedefault = 1; file = getdatasock(mode); 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.", - inet_ntoa(data_source.sin_addr), - ntohs(data_source.sin_port), strerror(errno)); + data_addr, + socket_get_port (data_source), + strerror(errno)); return (NULL); } data = fileno(file); - while (connect(data, (struct sockaddr *)&data_dest, - sizeof(data_dest)) < 0) { + while (connect(data, data_dest, + socket_sockaddr_size(data_dest)) < 0) { if (errno == EADDRINUSE && retry < swaitmax) { - sleep((unsigned) swaitint); + sleep(swaitint); retry += swaitint; continue; } @@ -1673,18 +1738,30 @@ renamecmd(char *from, char *to) } 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)); #ifdef HAVE_SETPROCTITLE snprintf(proctitle, sizeof(proctitle), "%s: connected", remotehost); setproctitle(proctitle); #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)", remotehost, - inet_ntoa(his_addr.sin_addr)); + data_addr); + } } /* @@ -1766,31 +1843,41 @@ myoob(int signo) * with Rick Adams on 25 Jan 89. */ void -passive(void) +pasv(void) { int len; 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) { perror_reply(425, "Can't open passive connection"); return; } - pasv_addr = ctrl_addr; - pasv_addr.sin_port = 0; - seteuid((uid_t)0); - if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) { - seteuid((uid_t)pw->pw_uid); + 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((uid_t)pw->pw_uid); - len = sizeof(pasv_addr); - if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0) + 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; - a = (char *) &pasv_addr.sin_addr; - p = (char *) &pasv_addr.sin_port; + sin = (struct sockaddr_in *)pasv_addr; + a = (char *) &sin->sin_addr; + p = (char *) &sin->sin_port; #define UC(b) (((int) b) & 0xff) @@ -1805,6 +1892,109 @@ pasv_error: 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". * The file named "local" is already known to exist.