X-Git-Url: http://sjero.net/git/?p=wget;a=blobdiff_plain;f=src%2Fftp-basic.c;h=7a512c6ed607478ea39373c88196ba773aa07a29;hp=644e215fb095a8eb94aee8cc785f73975cc3787b;hb=320cfdcb658e8d6556ae9dfd902c2db1db866a6b;hpb=0716c335a0d38c09ade6f05be17bffa0d586b3da diff --git a/src/ftp-basic.c b/src/ftp-basic.c index 644e215f..7a512c6e 100644 --- a/src/ftp-basic.c +++ b/src/ftp-basic.c @@ -1,12 +1,14 @@ /* Basic FTP routines. - Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, + 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, + Inc. This file is part of GNU Wget. GNU Wget is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. +the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. GNU Wget is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -14,42 +16,33 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with Wget; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +along with Wget. If not, see . -In addition, as a special exception, the Free Software Foundation -gives permission to link the code of its release of Wget with the -OpenSSL project's "OpenSSL" library (or with modified versions of it -that use the same license as the "OpenSSL" library), and distribute -the linked executables. You must obey the GNU General Public License -in all respects for all of the code used other than "OpenSSL". If you -modify this file, you may extend this exception to your version of the -file, but you are not obligated to do so. If you do not wish to do -so, delete this exception statement from your version. */ +Additional permission under GNU GPL version 3 section 7 -#include +If you modify this program, or any covered work, by linking or +combining it with the OpenSSL project's OpenSSL library (or a +modified version of that library), containing parts covered by the +terms of the OpenSSL or SSLeay licenses, the Free Software Foundation +grants you additional permission to convey the resulting work. +Corresponding Source for a non-source form of such a combination +shall include the source code for the parts of OpenSSL used as well +as that of the covered work. */ + +#include "wget.h" #include #include #include #include -#ifdef HAVE_STRING_H -# include -#else -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif -#include - -#include "wget.h" +#include +#include #include "utils.h" -#include "rbuf.h" #include "connect.h" #include "host.h" #include "ftp.h" +#include "retr.h" char ftp_last_respline[128]; @@ -57,47 +50,47 @@ char ftp_last_respline[128]; /* Get the response of FTP server and allocate enough room to handle it. and characters are stripped from the line, and the line is 0-terminated. All the response lines but the last one are - skipped. The last line is determined as described in RFC959. */ + skipped. The last line is determined as described in RFC959. + + If the line is successfully read, FTPOK is returned, and *ret_line + is assigned a freshly allocated line. Otherwise, FTPRERR is + returned, and the value of *ret_line should be ignored. */ + uerr_t -ftp_response (struct rbuf *rbuf, char **line) +ftp_response (int fd, char **ret_line) { - int i; - int bufsize = 40; - - *line = (char *)xmalloc (bufsize); - do + while (1) { - for (i = 0; 1; i++) - { - int res; - if (i > bufsize - 1) - *line = (char *)xrealloc (*line, (bufsize <<= 1)); - res = RBUF_READCHAR (rbuf, *line + i); - /* RES is number of bytes read. */ - if (res == 1) - { - if ((*line)[i] == '\n') - { - (*line)[i] = '\0'; - /* Get rid of \r. */ - if (i > 0 && (*line)[i - 1] == '\r') - (*line)[i - 1] = '\0'; - break; - } - } - else - return FTPRERR; - } + char *p; + char *line = fd_read_line (fd); + if (!line) + return FTPRERR; + + /* Strip trailing CRLF before printing the line, so that + quotting doesn't include bogus \012 and \015. */ + p = strchr (line, '\0'); + if (p > line && p[-1] == '\n') + *--p = '\0'; + if (p > line && p[-1] == '\r') + *--p = '\0'; + if (opt.server_response) - logprintf (LOG_ALWAYS, "%s\n", *line); + logprintf (LOG_NOTQUIET, "%s\n", + quotearg_style (escape_quoting_style, line)); else - DEBUGP (("%s\n", *line)); + DEBUGP (("%s\n", quotearg_style (escape_quoting_style, line))); + + /* The last line of output is the one that begins with "ddd ". */ + if (c_isdigit (line[0]) && c_isdigit (line[1]) && c_isdigit (line[2]) + && line[3] == ' ') + { + strncpy (ftp_last_respline, line, sizeof (ftp_last_respline)); + ftp_last_respline[sizeof (ftp_last_respline) - 1] = '\0'; + *ret_line = line; + return FTPOK; + } + xfree (line); } - while (!(i >= 3 && ISDIGIT (**line) && ISDIGIT ((*line)[1]) && - ISDIGIT ((*line)[2]) && (*line)[3] == ' ')); - strncpy (ftp_last_respline, *line, sizeof (ftp_last_respline)); - ftp_last_respline[sizeof (ftp_last_respline) - 1] = '\0'; - return FTPOK; } /* Returns the malloc-ed FTP request, ending with , printing @@ -106,10 +99,32 @@ ftp_response (struct rbuf *rbuf, char **line) static char * ftp_request (const char *command, const char *value) { - char *res = (char *)xmalloc (strlen (command) - + (value ? (1 + strlen (value)) : 0) - + 2 + 1); - sprintf (res, "%s%s%s\r\n", command, value ? " " : "", value ? value : ""); + char *res; + if (value) + { + /* Check for newlines in VALUE (possibly injected by the %0A URL + escape) making the callers inadvertently send multiple FTP + commands at once. Without this check an attacker could + intentionally redirect to ftp://server/fakedir%0Acommand.../ + and execute arbitrary FTP command on a remote FTP server. */ + if (strpbrk (value, "\r\n")) + { + /* Copy VALUE to the stack and modify CR/LF to space. */ + char *defanged, *p; + STRDUP_ALLOCA (defanged, value); + for (p = defanged; *p; p++) + if (*p == '\r' || *p == '\n') + *p = ' '; + DEBUGP (("\nDetected newlines in %s \"%s\"; changing to %s \"%s\"\n", + command, quotearg_style (escape_quoting_style, value), + command, quotearg_style (escape_quoting_style, defanged))); + /* Make VALUE point to the defanged copy of the string. */ + value = defanged; + } + res = concat_strings (command, " ", value, "\r\n", (char *) 0); + } + else + res = concat_strings (command, "\r\n", (char *) 0); if (opt.server_response) { /* Hack: don't print out password. */ @@ -126,19 +141,16 @@ ftp_request (const char *command, const char *value) /* Sends the USER and PASS commands to the server, to control connection socket csock. */ uerr_t -ftp_login (struct rbuf *rbuf, const char *acc, const char *pass) +ftp_login (int csock, const char *acc, const char *pass) { uerr_t err; char *request, *respline; int nwritten; /* Get greeting. */ - err = ftp_response (rbuf, &respline); + err = ftp_response (csock, &respline); if (err != FTPOK) - { - xfree (respline); - return err; - } + return err; if (*respline != '2') { xfree (respline); @@ -147,7 +159,7 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass) xfree (respline); /* Send USER username. */ request = ftp_request ("USER", acc); - nwritten = fd_write (RBUF_FD (rbuf), request, strlen (request), -1); + nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { xfree (request); @@ -155,12 +167,9 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass) } xfree (request); /* Get appropriate response. */ - err = ftp_response (rbuf, &respline); + err = ftp_response (csock, &respline); if (err != FTPOK) - { - xfree (respline); - return err; - } + return err; /* An unprobable possibility of logging without a password. */ if (*respline == '2') { @@ -173,48 +182,48 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass) xfree (respline); return FTPLOGREFUSED; } -#ifdef USE_OPIE +#ifdef ENABLE_OPIE { static const char *skey_head[] = { "331 s/key ", "331 opiekey " }; - int i; + size_t i; const char *seed = NULL; for (i = 0; i < countof (skey_head); i++) { - int l = strlen (skey_head[i]); + int l = strlen (skey_head[i]); if (0 == strncasecmp (skey_head[i], respline, l)) - { - seed = respline + l; - break; - } + { + seed = respline + l; + break; + } } if (seed) { int skey_sequence = 0; - /* Extract the sequence from SEED. */ - for (; ISDIGIT (*seed); seed++) - skey_sequence = 10 * skey_sequence + *seed - '0'; - if (*seed == ' ') - ++seed; + /* Extract the sequence from SEED. */ + for (; c_isdigit (*seed); seed++) + skey_sequence = 10 * skey_sequence + *seed - '0'; + if (*seed == ' ') + ++seed; else { xfree (respline); return FTPLOGREFUSED; } - /* Replace the password with the SKEY response to the - challenge. */ + /* Replace the password with the SKEY response to the + challenge. */ pass = skey_response (skey_sequence, seed, pass); } } -#endif /* USE_OPIE */ +#endif /* ENABLE_OPIE */ xfree (respline); /* Send PASS password. */ request = ftp_request ("PASS", pass); - nwritten = fd_write (RBUF_FD (rbuf), request, strlen (request), -1); + nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { xfree (request); @@ -222,12 +231,9 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass) } xfree (request); /* Get appropriate response. */ - err = ftp_response (rbuf, &respline); + err = ftp_response (csock, &respline); if (err != FTPOK) - { - xfree (respline); - return err; - } + return err; if (*respline != '2') { xfree (respline); @@ -239,18 +245,16 @@ ftp_login (struct rbuf *rbuf, const char *acc, const char *pass) } static void -ip_address_to_port_repr (const ip_address *addr, int port, char *buf, +ip_address_to_port_repr (const ip_address *addr, int port, char *buf, size_t buflen) { unsigned char *ptr; - assert (addr != NULL); - assert (addr->type == IPV4_ADDRESS); - assert (buf != NULL); + assert (addr->family == AF_INET); /* buf must contain the argument of PORT (of the form a,b,c,d,e,f). */ assert (buflen >= 6 * 4); - ptr = ADDRESS_IPV4_DATA (addr); + ptr = IP_INADDR_DATA (addr); snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d", ptr[0], ptr[1], ptr[2], ptr[3], (port & 0xff00) >> 8, port & 0xff); buf[buflen - 1] = '\0'; @@ -260,7 +264,7 @@ ip_address_to_port_repr (const ip_address *addr, int port, char *buf, server. Use acceptport after RETR, to get the socket of data connection. */ uerr_t -ftp_port (struct rbuf *rbuf, int *local_sock) +ftp_port (int csock, int *local_sock) { uerr_t err; char *request, *respline; @@ -270,14 +274,11 @@ ftp_port (struct rbuf *rbuf, int *local_sock) /* Must contain the argument of PORT (of the form a,b,c,d,e,f). */ char bytes[6 * 4 + 1]; - assert (rbuf != NULL); - assert (rbuf_initialized_p (rbuf)); - /* Get the address of this side of the connection. */ - if (!socket_ip_address (RBUF_FD (rbuf), &addr, ENDPOINT_LOCAL)) + if (!socket_ip_address (csock, &addr, ENDPOINT_LOCAL)) return FTPSYSERR; - assert (addr.type == IPV4_ADDRESS); + assert (addr.family == AF_INET); /* Setting port to 0 lets the system choose a free port. */ port = 0; @@ -292,7 +293,7 @@ ftp_port (struct rbuf *rbuf, int *local_sock) /* Send PORT request. */ request = ftp_request ("PORT", bytes); - nwritten = fd_write (RBUF_FD (rbuf), request, strlen (request), -1); + nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { xfree (request); @@ -302,10 +303,9 @@ ftp_port (struct rbuf *rbuf, int *local_sock) xfree (request); /* Get appropriate response. */ - err = ftp_response (rbuf, &respline); + err = ftp_response (csock, &respline); if (err != FTPOK) { - xfree (respline); fd_close (*local_sock); return err; } @@ -321,35 +321,32 @@ ftp_port (struct rbuf *rbuf, int *local_sock) #ifdef ENABLE_IPV6 static void -ip_address_to_lprt_repr (const ip_address *addr, int port, char *buf, +ip_address_to_lprt_repr (const ip_address *addr, int port, char *buf, size_t buflen) { - unsigned char *ptr; + unsigned char *ptr = IP_INADDR_DATA (addr); - assert (addr != NULL); - assert (addr->type == IPV4_ADDRESS || addr->type == IPV6_ADDRESS); - assert (buf != NULL); /* buf must contain the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */ assert (buflen >= 21 * 4); /* Construct the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */ - switch (addr->type) - { - case IPV4_ADDRESS: - ptr = ADDRESS_IPV4_DATA (addr); - snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d,%d,%d,%d", 4, 4, - ptr[0], ptr[1], ptr[2], ptr[3], 2, - (port & 0xff00) >> 8, port & 0xff); - buf[buflen - 1] = '\0'; - break; - case IPV6_ADDRESS: - ptr = ADDRESS_IPV6_DATA (addr); - snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", - 6, 16, ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7], - ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15], 2, - (port & 0xff00) >> 8, port & 0xff); - buf[buflen - 1] = '\0'; - break; + switch (addr->family) + { + case AF_INET: + snprintf (buf, buflen, "%d,%d,%d,%d,%d,%d,%d,%d,%d", 4, 4, + ptr[0], ptr[1], ptr[2], ptr[3], 2, + (port & 0xff00) >> 8, port & 0xff); + break; + case AF_INET6: + snprintf (buf, buflen, + "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + 6, 16, + ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7], + ptr[8], ptr[9], ptr[10], ptr[11], ptr[12], ptr[13], ptr[14], ptr[15], + 2, (port & 0xff00) >> 8, port & 0xff); + break; + default: + abort (); } } @@ -357,7 +354,7 @@ ip_address_to_lprt_repr (const ip_address *addr, int port, char *buf, server. Use acceptport after RETR, to get the socket of data connection. */ uerr_t -ftp_lprt (struct rbuf *rbuf, int *local_sock) +ftp_lprt (int csock, int *local_sock) { uerr_t err; char *request, *respline; @@ -367,14 +364,11 @@ ftp_lprt (struct rbuf *rbuf, int *local_sock) /* Must contain the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */ char bytes[21 * 4 + 1]; - assert (rbuf != NULL); - assert (rbuf_initialized_p (rbuf)); - /* Get the address of this side of the connection. */ - if (!socket_ip_address (RBUF_FD (rbuf), &addr, ENDPOINT_LOCAL)) + if (!socket_ip_address (csock, &addr, ENDPOINT_LOCAL)) return FTPSYSERR; - assert (addr.type == IPV4_ADDRESS || addr.type == IPV6_ADDRESS); + assert (addr.family == AF_INET || addr.family == AF_INET6); /* Setting port to 0 lets the system choose a free port. */ port = 0; @@ -389,7 +383,7 @@ ftp_lprt (struct rbuf *rbuf, int *local_sock) /* Send PORT request. */ request = ftp_request ("LPRT", bytes); - nwritten = fd_write (RBUF_FD (rbuf), request, strlen (request), -1); + nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { xfree (request); @@ -398,10 +392,9 @@ ftp_lprt (struct rbuf *rbuf, int *local_sock) } xfree (request); /* Get appropriate response. */ - err = ftp_response (rbuf, &respline); + err = ftp_response (csock, &respline); if (err != FTPOK) { - xfree (respline); fd_close (*local_sock); return err; } @@ -416,22 +409,19 @@ ftp_lprt (struct rbuf *rbuf, int *local_sock) } static void -ip_address_to_eprt_repr (const ip_address *addr, int port, char *buf, +ip_address_to_eprt_repr (const ip_address *addr, int port, char *buf, size_t buflen) { int afnum; - assert (addr != NULL); - assert (addr->type == IPV4_ADDRESS || addr->type == IPV6_ADDRESS); - assert (buf != NULL); - /* buf must contain the argument of EPRT (of the form |af|addr|port|). - * 4 chars for the | separators, INET6_ADDRSTRLEN chars for addr + /* buf must contain the argument of EPRT (of the form |af|addr|port|). + * 4 chars for the | separators, INET6_ADDRSTRLEN chars for addr * 1 char for af (1-2) and 5 chars for port (0-65535) */ - assert (buflen >= 4 + INET6_ADDRSTRLEN + 1 + 5); + assert (buflen >= 4 + INET6_ADDRSTRLEN + 1 + 5); /* Construct the argument of EPRT (of the form |af|addr|port|). */ - afnum = (addr->type == IPV4_ADDRESS ? 1 : 2); - snprintf (buf, buflen, "|%d|%s|%d|", afnum, pretty_print_address (addr), port); + afnum = (addr->family == AF_INET ? 1 : 2); + snprintf (buf, buflen, "|%d|%s|%d|", afnum, print_address (addr), port); buf[buflen - 1] = '\0'; } @@ -439,27 +429,22 @@ ip_address_to_eprt_repr (const ip_address *addr, int port, char *buf, server. Use acceptport after RETR, to get the socket of data connection. */ uerr_t -ftp_eprt (struct rbuf *rbuf, int *local_sock) +ftp_eprt (int csock, int *local_sock) { uerr_t err; char *request, *respline; ip_address addr; int nwritten; int port; - /* Must contain the argument of EPRT (of the form |af|addr|port|). - * 4 chars for the | separators, ENABLE_IPV6_ADDRSTRLEN chars for addr + /* Must contain the argument of EPRT (of the form |af|addr|port|). + * 4 chars for the | separators, INET6_ADDRSTRLEN chars for addr * 1 char for af (1-2) and 5 chars for port (0-65535) */ char bytes[4 + INET6_ADDRSTRLEN + 1 + 5 + 1]; - assert (rbuf != NULL); - assert (rbuf_initialized_p(rbuf)); - /* Get the address of this side of the connection. */ - if (!socket_ip_address (RBUF_FD (rbuf), &addr, ENDPOINT_LOCAL)) + if (!socket_ip_address (csock, &addr, ENDPOINT_LOCAL)) return FTPSYSERR; - assert (addr.type == IPV4_ADDRESS || addr.type == IPV6_ADDRESS); - /* Setting port to 0 lets the system choose a free port. */ port = 0; @@ -468,12 +453,12 @@ ftp_eprt (struct rbuf *rbuf, int *local_sock) if (*local_sock < 0) return FTPSYSERR; - /* Construct the argument of LPRT (of the form af,n,h1,h2,...,hn,p1,p2). */ + /* Construct the argument of EPRT (of the form |af|addr|port|). */ ip_address_to_eprt_repr (&addr, port, bytes, sizeof (bytes)); /* Send PORT request. */ request = ftp_request ("EPRT", bytes); - nwritten = fd_write (RBUF_FD (rbuf), request, strlen (request), -1); + nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { xfree (request); @@ -482,10 +467,9 @@ ftp_eprt (struct rbuf *rbuf, int *local_sock) } xfree (request); /* Get appropriate response. */ - err = ftp_response (rbuf, &respline); + err = ftp_response (csock, &respline); if (err != FTPOK) { - xfree (respline); fd_close (*local_sock); return err; } @@ -504,24 +488,22 @@ ftp_eprt (struct rbuf *rbuf, int *local_sock) transfer. Reads the response from server and parses it. Reads the host and port addresses and returns them. */ uerr_t -ftp_pasv (struct rbuf *rbuf, ip_address *addr, int *port) +ftp_pasv (int csock, ip_address *addr, int *port) { char *request, *respline, *s; int nwritten, i; uerr_t err; unsigned char tmp[6]; - assert (rbuf != NULL); - assert (rbuf_initialized_p (rbuf)); assert (addr != NULL); assert (port != NULL); - memset (addr, 0, sizeof (ip_address)); + xzero (*addr); /* Form the request. */ request = ftp_request ("PASV", NULL); /* And send it. */ - nwritten = fd_write (RBUF_FD (rbuf), request, strlen (request), -1); + nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { xfree (request); @@ -529,12 +511,9 @@ ftp_pasv (struct rbuf *rbuf, ip_address *addr, int *port) } xfree (request); /* Get the server response. */ - err = ftp_response (rbuf, &respline); + err = ftp_response (csock, &respline); if (err != FTPOK) - { - xfree (respline); - return err; - } + return err; if (*respline != '2') { xfree (respline); @@ -542,13 +521,17 @@ ftp_pasv (struct rbuf *rbuf, ip_address *addr, int *port) } /* Parse the request. */ s = respline; - for (s += 4; *s && !ISDIGIT (*s); s++); + for (s += 4; *s && !c_isdigit (*s); s++) + ; if (!*s) - return FTPINVPASV; + { + xfree (respline); + return FTPINVPASV; + } for (i = 0; i < 6; i++) { tmp[i] = 0; - for (; ISDIGIT (*s); s++) + for (; c_isdigit (*s); s++) tmp[i] = (*s - '0') + 10 * tmp[i]; if (*s == ',') s++; @@ -561,8 +544,8 @@ ftp_pasv (struct rbuf *rbuf, ip_address *addr, int *port) } xfree (respline); - addr->type = IPV4_ADDRESS; - memcpy (ADDRESS_IPV4_DATA (addr), tmp, 4); + addr->family = AF_INET; + memcpy (IP_INADDR_DATA (addr), tmp, 4); *port = ((tmp[4] << 8) & 0xff00) + tmp[5]; return FTPOK; @@ -573,7 +556,7 @@ ftp_pasv (struct rbuf *rbuf, ip_address *addr, int *port) transfer. Reads the response from server and parses it. Reads the host and port addresses and returns them. */ uerr_t -ftp_lpsv (struct rbuf *rbuf, ip_address *addr, int *port) +ftp_lpsv (int csock, ip_address *addr, int *port) { char *request, *respline, *s; int nwritten, i, af, addrlen, portlen; @@ -581,18 +564,16 @@ ftp_lpsv (struct rbuf *rbuf, ip_address *addr, int *port) unsigned char tmp[16]; unsigned char tmpprt[2]; - assert (rbuf != NULL); - assert (rbuf_initialized_p(rbuf)); assert (addr != NULL); assert (port != NULL); - memset (addr, 0, sizeof (ip_address)); + xzero (*addr); /* Form the request. */ request = ftp_request ("LPSV", NULL); /* And send it. */ - nwritten = fd_write (RBUF_FD (rbuf), request, strlen (request), -1); + nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { xfree (request); @@ -601,27 +582,28 @@ ftp_lpsv (struct rbuf *rbuf, ip_address *addr, int *port) xfree (request); /* Get the server response. */ - err = ftp_response (rbuf, &respline); + err = ftp_response (csock, &respline); if (err != FTPOK) - { - xfree (respline); - return err; - } + return err; if (*respline != '2') { xfree (respline); return FTPNOPASV; - } + } /* Parse the response. */ s = respline; - for (s += 4; *s && !ISDIGIT (*s); s++); + for (s += 4; *s && !c_isdigit (*s); s++) + ; if (!*s) - return FTPINVPASV; + { + xfree (respline); + return FTPINVPASV; + } /* First, get the address family */ af = 0; - for (; ISDIGIT (*s); s++) + for (; c_isdigit (*s); s++) af = (*s - '0') + 10 * af; if (af != 4 && af != 6) @@ -638,7 +620,7 @@ ftp_lpsv (struct rbuf *rbuf, ip_address *addr, int *port) /* Then, get the address length */ addrlen = 0; - for (; ISDIGIT (*s); s++) + for (; c_isdigit (*s); s++) addrlen = (*s - '0') + 10 * addrlen; if (!*s || *s++ != ',') @@ -664,7 +646,7 @@ ftp_lpsv (struct rbuf *rbuf, ip_address *addr, int *port) for (i = 0; i < addrlen; i++) { tmp[i] = 0; - for (; ISDIGIT (*s); s++) + for (; c_isdigit (*s); s++) tmp[i] = (*s - '0') + 10 * tmp[i]; if (*s == ',') s++; @@ -677,7 +659,7 @@ ftp_lpsv (struct rbuf *rbuf, ip_address *addr, int *port) /* Now, get the port length */ portlen = 0; - for (; ISDIGIT (*s); s++) + for (; c_isdigit (*s); s++) portlen = (*s - '0') + 10 * portlen; if (!*s || *s++ != ',') @@ -694,7 +676,7 @@ ftp_lpsv (struct rbuf *rbuf, ip_address *addr, int *port) /* Finally, we get the port number */ tmpprt[0] = 0; - for (; ISDIGIT (*s); s++) + for (; c_isdigit (*s); s++) tmpprt[0] = (*s - '0') + 10 * tmpprt[0]; if (!*s || *s++ != ',') @@ -704,17 +686,17 @@ ftp_lpsv (struct rbuf *rbuf, ip_address *addr, int *port) } tmpprt[1] = 0; - for (; ISDIGIT (*s); s++) + for (; c_isdigit (*s); s++) tmpprt[1] = (*s - '0') + 10 * tmpprt[1]; assert (s != NULL); if (af == 4) { - addr->type = IPV4_ADDRESS; - memcpy (ADDRESS_IPV4_DATA (addr), tmp, 4); + addr->family = AF_INET; + memcpy (IP_INADDR_DATA (addr), tmp, 4); *port = ((tmpprt[0] << 8) & 0xff00) + tmpprt[1]; - DEBUGP (("lpsv addr is: %s\n", pretty_print_address(addr))); + DEBUGP (("lpsv addr is: %s\n", print_address(addr))); DEBUGP (("tmpprt[0] is: %d\n", tmpprt[0])); DEBUGP (("tmpprt[1] is: %d\n", tmpprt[1])); DEBUGP (("*port is: %d\n", *port)); @@ -722,10 +704,10 @@ ftp_lpsv (struct rbuf *rbuf, ip_address *addr, int *port) else { assert (af == 6); - addr->type = IPV6_ADDRESS; - memcpy (ADDRESS_IPV6_DATA (addr), tmp, 16); + addr->family = AF_INET6; + memcpy (IP_INADDR_DATA (addr), tmp, 16); *port = ((tmpprt[0] << 8) & 0xff00) + tmpprt[1]; - DEBUGP (("lpsv addr is: %s\n", pretty_print_address(addr))); + DEBUGP (("lpsv addr is: %s\n", print_address(addr))); DEBUGP (("tmpprt[0] is: %d\n", tmpprt[0])); DEBUGP (("tmpprt[1] is: %d\n", tmpprt[1])); DEBUGP (("*port is: %d\n", *port)); @@ -739,15 +721,13 @@ ftp_lpsv (struct rbuf *rbuf, ip_address *addr, int *port) transfer. Reads the response from server and parses it. Reads the host and port addresses and returns them. */ uerr_t -ftp_epsv (struct rbuf *rbuf, ip_address *ip, int *port) +ftp_epsv (int csock, ip_address *ip, int *port) { char *request, *respline, *start, delim, *s; int nwritten, i; uerr_t err; int tport; - assert (rbuf != NULL); - assert (rbuf_initialized_p(rbuf)); assert (ip != NULL); assert (port != NULL); @@ -756,10 +736,10 @@ ftp_epsv (struct rbuf *rbuf, ip_address *ip, int *port) /* Form the request. */ /* EPSV 1 means that we ask for IPv4 and EPSV 2 means that we ask for IPv6. */ - request = ftp_request ("EPSV", (ip->type == IPV4_ADDRESS ? "1" : "2")); + request = ftp_request ("EPSV", (ip->family == AF_INET ? "1" : "2")); /* And send it. */ - nwritten = fd_write (RBUF_FD (rbuf), request, strlen (request), -1); + nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { xfree (request); @@ -768,32 +748,26 @@ ftp_epsv (struct rbuf *rbuf, ip_address *ip, int *port) xfree (request); /* Get the server response. */ - err = ftp_response (rbuf, &respline); + err = ftp_response (csock, &respline); if (err != FTPOK) - { - xfree (respline); - return err; - } + return err; if (*respline != '2') { xfree (respline); return FTPNOPASV; - } + } assert (respline != NULL); DEBUGP(("respline is %s\n", respline)); - /* Parse the response. */ - s = respline; - /* Skip the useless stuff and get what's inside the parentheses */ start = strchr (respline, '('); if (start == NULL) { xfree (respline); return FTPINVPASV; - } + } /* Skip the first two void fields */ s = start + 1; @@ -802,26 +776,26 @@ ftp_epsv (struct rbuf *rbuf, ip_address *ip, int *port) { xfree (respline); return FTPINVPASV; - } + } for (i = 0; i < 2; i++) { - if (*s++ != delim) + if (*s++ != delim) { xfree (respline); return FTPINVPASV; - } + } } /* Finally, get the port number */ - tport = 0; - for (i = 1; ISDIGIT (*s); s++) + tport = 0; + for (i = 1; c_isdigit (*s); s++) { if (i > 5) { xfree (respline); return FTPINVPASV; - } + } tport = (*s - '0') + 10 * tport; } @@ -830,13 +804,13 @@ ftp_epsv (struct rbuf *rbuf, ip_address *ip, int *port) { xfree (respline); return FTPINVPASV; - } + } - if (*s++ != ')') + if (*s != ')') { xfree (respline); return FTPINVPASV; - } + } *port = tport; @@ -847,7 +821,7 @@ ftp_epsv (struct rbuf *rbuf, ip_address *ip, int *port) /* Sends the TYPE request to the server. */ uerr_t -ftp_type (struct rbuf *rbuf, int type) +ftp_type (int csock, int type) { char *request, *respline; int nwritten; @@ -859,7 +833,7 @@ ftp_type (struct rbuf *rbuf, int type) stype[1] = 0; /* Send TYPE request. */ request = ftp_request ("TYPE", stype); - nwritten = fd_write (RBUF_FD (rbuf), request, strlen (request), -1); + nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { xfree (request); @@ -867,12 +841,9 @@ ftp_type (struct rbuf *rbuf, int type) } xfree (request); /* Get appropriate response. */ - err = ftp_response (rbuf, &respline); + err = ftp_response (csock, &respline); if (err != FTPOK) - { - xfree (respline); - return err; - } + return err; if (*respline != '2') { xfree (respline); @@ -886,7 +857,7 @@ ftp_type (struct rbuf *rbuf, int type) /* Changes the working directory by issuing a CWD command to the server. */ uerr_t -ftp_cwd (struct rbuf *rbuf, const char *dir) +ftp_cwd (int csock, const char *dir) { char *request, *respline; int nwritten; @@ -894,7 +865,7 @@ ftp_cwd (struct rbuf *rbuf, const char *dir) /* Send CWD request. */ request = ftp_request ("CWD", dir); - nwritten = fd_write (RBUF_FD (rbuf), request, strlen (request), -1); + nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { xfree (request); @@ -902,12 +873,9 @@ ftp_cwd (struct rbuf *rbuf, const char *dir) } xfree (request); /* Get appropriate response. */ - err = ftp_response (rbuf, &respline); + err = ftp_response (csock, &respline); if (err != FTPOK) - { - xfree (respline); - return err; - } + return err; if (*respline == '5') { xfree (respline); @@ -925,16 +893,14 @@ ftp_cwd (struct rbuf *rbuf, const char *dir) /* Sends REST command to the FTP server. */ uerr_t -ftp_rest (struct rbuf *rbuf, long offset) +ftp_rest (int csock, wgint offset) { char *request, *respline; int nwritten; uerr_t err; - static char numbuf[24]; /* Buffer for the number */ - number_to_string (numbuf, offset); - request = ftp_request ("REST", numbuf); - nwritten = fd_write (RBUF_FD (rbuf), request, strlen (request), -1); + request = ftp_request ("REST", number_to_static_string (offset)); + nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { xfree (request); @@ -942,12 +908,9 @@ ftp_rest (struct rbuf *rbuf, long offset) } xfree (request); /* Get appropriate response. */ - err = ftp_response (rbuf, &respline); + err = ftp_response (csock, &respline); if (err != FTPOK) - { - xfree (respline); - return err; - } + return err; if (*respline != '3') { xfree (respline); @@ -960,7 +923,7 @@ ftp_rest (struct rbuf *rbuf, long offset) /* Sends RETR command to the FTP server. */ uerr_t -ftp_retr (struct rbuf *rbuf, const char *file) +ftp_retr (int csock, const char *file) { char *request, *respline; int nwritten; @@ -968,7 +931,7 @@ ftp_retr (struct rbuf *rbuf, const char *file) /* Send RETR request. */ request = ftp_request ("RETR", file); - nwritten = fd_write (RBUF_FD (rbuf), request, strlen (request), -1); + nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { xfree (request); @@ -976,12 +939,9 @@ ftp_retr (struct rbuf *rbuf, const char *file) } xfree (request); /* Get appropriate response. */ - err = ftp_response (rbuf, &respline); + err = ftp_response (csock, &respline); if (err != FTPOK) - { - xfree (respline); - return err; - } + return err; if (*respline == '5') { xfree (respline); @@ -1000,46 +960,79 @@ ftp_retr (struct rbuf *rbuf, const char *file) /* Sends the LIST command to the server. If FILE is NULL, send just `LIST' (no space). */ uerr_t -ftp_list (struct rbuf *rbuf, const char *file) +ftp_list (int csock, const char *file, bool avoid_list_a, bool avoid_list, + bool *list_a_used) { char *request, *respline; int nwritten; uerr_t err; + bool ok = false; + size_t i = 0; - /* Send LIST request. */ - request = ftp_request ("LIST", file); - nwritten = fd_write (RBUF_FD (rbuf), request, strlen (request), -1); - if (nwritten < 0) - { - xfree (request); - return WRITEFAILED; - } - xfree (request); - /* Get appropriate respone. */ - err = ftp_response (rbuf, &respline); - if (err != FTPOK) - { - xfree (respline); - return err; - } - if (*respline == '5') - { - xfree (respline); - return FTPNSFOD; - } - if (*respline != '1') + *list_a_used = false; + + /* 2013-10-12 Andrea Urbani (matfanjol) + For more information about LIST and "LIST -a" please look at ftp.c, + function getftp, text "__LIST_A_EXPLANATION__". + + If somebody changes the following commands, please, checks also the + later "i" variable. */ + const char *list_commands[] = { "LIST -a", + "LIST" }; + + if (avoid_list_a) { - xfree (respline); - return FTPRERR; + i = countof (list_commands)- 1; + DEBUGP (("(skipping \"LIST -a\")")); } - xfree (respline); - /* All OK. */ - return FTPOK; + + + do { + /* Send request. */ + request = ftp_request (list_commands[i], file); + nwritten = fd_write (csock, request, strlen (request), -1); + if (nwritten < 0) + { + xfree (request); + return WRITEFAILED; + } + xfree (request); + /* Get appropriate response. */ + err = ftp_response (csock, &respline); + if (err == FTPOK) + { + if (*respline == '5') + { + err = FTPNSFOD; + } + else if (*respline == '1') + { + err = FTPOK; + ok = true; + /* Which list command was used? */ + *list_a_used = (i == 0); + } + else + { + err = FTPRERR; + } + xfree (respline); + } + ++i; + if ((avoid_list) && (i == 1)) + { + /* I skip LIST */ + ++i; + DEBUGP (("(skipping \"LIST\")")); + } + } while (i < countof (list_commands) && !ok); + + return err; } /* Sends the SYST command to the server. */ uerr_t -ftp_syst (struct rbuf *rbuf, enum stype *server_type) +ftp_syst (int csock, enum stype *server_type, enum ustype *unix_type) { char *request, *respline; int nwritten; @@ -1047,7 +1040,7 @@ ftp_syst (struct rbuf *rbuf, enum stype *server_type) /* Send SYST request. */ request = ftp_request ("SYST", NULL); - nwritten = fd_write (RBUF_FD (rbuf), request, strlen (request), -1); + nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { xfree (request); @@ -1056,12 +1049,9 @@ ftp_syst (struct rbuf *rbuf, enum stype *server_type) xfree (request); /* Get appropriate response. */ - err = ftp_response (rbuf, &respline); + err = ftp_response (csock, &respline); if (err != FTPOK) - { - xfree (respline); - return err; - } + return err; if (*respline == '5') { xfree (respline); @@ -1075,12 +1065,25 @@ ftp_syst (struct rbuf *rbuf, enum stype *server_type) first word of the server response)? */ request = strtok (NULL, " "); - if (!strcasecmp (request, "VMS")) + *unix_type = UST_OTHER; + + if (request == NULL) + *server_type = ST_OTHER; + else if (!strcasecmp (request, "VMS")) *server_type = ST_VMS; else if (!strcasecmp (request, "UNIX")) - *server_type = ST_UNIX; + { + *server_type = ST_UNIX; + /* 2013-10-17 Andrea Urbani (matfanjol) + I check more in depth the system type */ + if (!strncasecmp (ftp_last_respline, "215 UNIX Type: L8", 17)) + *unix_type = UST_TYPE_L8; + else if (!strncasecmp (ftp_last_respline, + "215 UNIX MultiNet Unix Emulation V5.3(93)", 41)) + *unix_type = UST_MULTINET; + } else if (!strcasecmp (request, "WINDOWS_NT") - || !strcasecmp (request, "WINDOWS2000")) + || !strcasecmp (request, "WINDOWS2000")) *server_type = ST_WINNT; else if (!strcasecmp (request, "MACOS")) *server_type = ST_MACOS; @@ -1096,7 +1099,7 @@ ftp_syst (struct rbuf *rbuf, enum stype *server_type) /* Sends the PWD command to the server. */ uerr_t -ftp_pwd (struct rbuf *rbuf, char **pwd) +ftp_pwd (int csock, char **pwd) { char *request, *respline; int nwritten; @@ -1104,7 +1107,7 @@ ftp_pwd (struct rbuf *rbuf, char **pwd) /* Send PWD request. */ request = ftp_request ("PWD", NULL); - nwritten = fd_write (RBUF_FD (rbuf), request, strlen (request), -1); + nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { xfree (request); @@ -1112,14 +1115,12 @@ ftp_pwd (struct rbuf *rbuf, char **pwd) } xfree (request); /* Get appropriate response. */ - err = ftp_response (rbuf, &respline); + err = ftp_response (csock, &respline); if (err != FTPOK) - { - xfree (respline); - return err; - } + return err; if (*respline == '5') { + err: xfree (respline); return FTPSRVERR; } @@ -1128,6 +1129,10 @@ ftp_pwd (struct rbuf *rbuf, char **pwd) and everything following it. */ strtok (respline, "\""); request = strtok (NULL, "\""); + if (!request) + /* Treat the malformed response as an error, which the caller has + to handle gracefully anyway. */ + goto err; /* Has the `pwd' been already allocated? Free! */ xfree_null (*pwd); @@ -1142,7 +1147,7 @@ ftp_pwd (struct rbuf *rbuf, char **pwd) /* Sends the SIZE command to the server, and returns the value in 'size'. * If an error occurs, size is set to zero. */ uerr_t -ftp_size (struct rbuf *rbuf, const char *file, long int *size) +ftp_size (int csock, const char *file, wgint *size) { char *request, *respline; int nwritten; @@ -1150,7 +1155,7 @@ ftp_size (struct rbuf *rbuf, const char *file, long int *size) /* Send PWD request. */ request = ftp_request ("SIZE", file); - nwritten = fd_write (RBUF_FD (rbuf), request, strlen (request), -1); + nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { xfree (request); @@ -1159,18 +1164,17 @@ ftp_size (struct rbuf *rbuf, const char *file, long int *size) } xfree (request); /* Get appropriate response. */ - err = ftp_response (rbuf, &respline); + err = ftp_response (csock, &respline); if (err != FTPOK) { - xfree (respline); *size = 0; return err; } if (*respline == '5') { - /* + /* * Probably means SIZE isn't supported on this server. - * Error is nonfatal since SIZE isn't in RFC 959 + * Error is nonfatal since SIZE isn't in RFC 959 */ xfree (respline); *size = 0; @@ -1178,10 +1182,10 @@ ftp_size (struct rbuf *rbuf, const char *file, long int *size) } errno = 0; - *size = strtol (respline + 4, NULL, 0); - if (errno) + *size = str_to_wgint (respline + 4, NULL, 10); + if (errno) { - /* + /* * Couldn't parse the response for some reason. On the (few) * tests I've done, the response is 213 with nothing else - * maybe something a bit more resilient is necessary. It's not a @@ -1205,7 +1209,7 @@ ftp_process_type (const char *params) if (params && 0 == strncasecmp (params, "type=", 5) && params[5] != '\0') - return TOUPPER (params[5]); + return c_toupper (params[5]); else return 'I'; }