X-Git-Url: http://sjero.net/git/?p=wget;a=blobdiff_plain;f=src%2Fftp-basic.c;h=0e11a8d3353a66d9385373dd47ea81be0e6690ad;hp=753566aeb862d5c3b4d6ecb3d18ebbfdb8060746;hb=d5e283b1a75c5f8249300b465b4e7b55130bec49;hpb=b3900f1fbd5b01d959736fa30fa165afc73ca135 diff --git a/src/ftp-basic.c b/src/ftp-basic.c index 753566ae..0e11a8d3 100644 --- a/src/ftp-basic.c +++ b/src/ftp-basic.c @@ -1,11 +1,12 @@ /* Basic FTP routines. - Copyright (C) 1996-2005 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, + 2004, 2005, 2006, 2007, 2008 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 +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, @@ -14,20 +15,20 @@ 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., -51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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 @@ -38,8 +39,6 @@ so, delete this exception statement from your version. */ #ifdef HAVE_UNISTD_H # include #endif - -#include "wget.h" #include "utils.h" #include "connect.h" #include "host.h" @@ -66,30 +65,31 @@ ftp_response (int fd, char **ret_line) char *p; char *line = fd_read_line (fd); if (!line) - return FTPRERR; + return FTPRERR; /* Strip trailing CRLF before printing the line, so that - escnonprint doesn't include bogus \012 and \015. */ + quotting doesn't include bogus \012 and \015. */ p = strchr (line, '\0'); if (p > line && p[-1] == '\n') - *--p = '\0'; + *--p = '\0'; if (p > line && p[-1] == '\r') - *--p = '\0'; + *--p = '\0'; if (opt.server_response) - logprintf (LOG_NOTQUIET, "%s\n", escnonprint (line)); + logprintf (LOG_NOTQUIET, "%s\n", + quotearg_style (escape_quoting_style, line)); else - DEBUGP (("%s\n", escnonprint (line))); + DEBUGP (("%s\n", quotearg_style (escape_quoting_style, line))); /* The last line of output is the one that begins with "ddd ". */ - if (ISDIGIT (line[0]) && 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'; - *ret_line = line; - return FTPOK; - } + 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); } } @@ -104,23 +104,24 @@ ftp_request (const char *command, const char *value) 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. */ + 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, escnonprint (value), command, escnonprint (defanged))); - /* Make VALUE point to the defanged copy of the string. */ - value = defanged; - } + { + /* 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 @@ -188,34 +189,34 @@ ftp_login (int csock, const char *acc, const char *pass) "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); } } @@ -250,13 +251,11 @@ ip_address_to_port_repr (const ip_address *addr, int port, char *buf, { 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'; @@ -280,7 +279,7 @@ ftp_port (int csock, int *local_sock) 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; @@ -326,32 +325,29 @@ static void 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 (); } } @@ -373,7 +369,7 @@ ftp_lprt (int csock, int *local_sock) 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; @@ -425,7 +421,7 @@ ip_address_to_eprt_repr (const ip_address *addr, int port, char *buf, 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); + afnum = (addr->family == AF_INET ? 1 : 2); snprintf (buf, buflen, "|%d|%s|%d|", afnum, print_address (addr), port); buf[buflen - 1] = '\0'; } @@ -526,14 +522,14 @@ ftp_pasv (int csock, 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; 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++; @@ -546,8 +542,8 @@ ftp_pasv (int csock, 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; @@ -595,14 +591,14 @@ ftp_lpsv (int csock, ip_address *addr, int *port) /* Parse the response. */ s = respline; - for (s += 4; *s && !ISDIGIT (*s); s++) + for (s += 4; *s && !c_isdigit (*s); s++) ; if (!*s) 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) @@ -619,7 +615,7 @@ ftp_lpsv (int csock, 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++ != ',') @@ -645,7 +641,7 @@ ftp_lpsv (int csock, 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++; @@ -658,7 +654,7 @@ ftp_lpsv (int csock, 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++ != ',') @@ -675,7 +671,7 @@ ftp_lpsv (int csock, 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++ != ',') @@ -685,15 +681,15 @@ ftp_lpsv (int csock, 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", print_address(addr))); DEBUGP (("tmpprt[0] is: %d\n", tmpprt[0])); @@ -703,8 +699,8 @@ ftp_lpsv (int csock, 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", print_address(addr))); DEBUGP (("tmpprt[0] is: %d\n", tmpprt[0])); @@ -735,7 +731,7 @@ ftp_epsv (int csock, 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 (csock, request, strlen (request), -1); @@ -791,7 +787,7 @@ ftp_epsv (int csock, ip_address *ip, int *port) /* Finally, get the port number */ tport = 0; - for (i = 1; ISDIGIT (*s); s++) + for (i = 1; c_isdigit (*s); s++) { if (i > 5) { @@ -893,16 +889,17 @@ ftp_cwd (int csock, const char *dir) return FTPOK; } -/* Sends REST command to the FTP server. */ +/* Sends DELE command to the FTP server. */ uerr_t -ftp_rest (int csock, wgint offset) +ftp_dele (int csock, const char *file) { char *request, *respline; int nwritten; uerr_t err; - request = ftp_request ("REST", number_to_static_string (offset)); - nwritten = fd_write (csock, request, strlen (request), -1); + /* Send DELE request. */ + request = ftp_request ("DELE", file); + nwritten = fd_write (csock, request, strlen (request), -1.0); if (nwritten < 0) { xfree (request); @@ -912,27 +909,31 @@ ftp_rest (int csock, wgint offset) /* Get appropriate response. */ err = ftp_response (csock, &respline); if (err != FTPOK) - return err; - if (*respline != '3') + return err; /* Return with early bad status. */ + + /* All OK, so far. */ + if (*respline == '5') { - xfree (respline); - return FTPRESTFAIL; + err = FTPNSFOD; /* Permanent Negative Completion. */ } - xfree (respline); - /* All OK. */ - return FTPOK; + else if (*respline != '2') /* Success might be 226 or 250 (or ???). */ + { + err = FTPRERR; /* Not Positive Completion. */ + } + + xfree (respline); /* Free "respline" storage. */ + return err; /* Return response-based status code. */ } -/* Sends RETR command to the FTP server. */ +/* Sends REST command to the FTP server. */ uerr_t -ftp_retr (int csock, const char *file) +ftp_rest (int csock, wgint offset) { char *request, *respline; int nwritten; uerr_t err; - /* Send RETR request. */ - request = ftp_request ("RETR", file); + request = ftp_request ("REST", number_to_static_string (offset)); nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { @@ -944,32 +945,26 @@ ftp_retr (int csock, const char *file) err = ftp_response (csock, &respline); if (err != FTPOK) return err; - if (*respline == '5') - { - xfree (respline); - return FTPNSFOD; - } - if (*respline != '1') + if (*respline != '3') { xfree (respline); - return FTPRERR; + return FTPRESTFAIL; } xfree (respline); /* All OK. */ return FTPOK; } -/* Sends the LIST command to the server. If FILE is NULL, send just - `LIST' (no space). */ +/* Sends RETR command to the FTP server. */ uerr_t -ftp_list (int csock, const char *file) +ftp_retr (int csock, const char *file) { char *request, *respline; int nwritten; uerr_t err; - /* Send LIST request. */ - request = ftp_request ("LIST", file); + /* Send RETR request. */ + request = ftp_request ("RETR", file); nwritten = fd_write (csock, request, strlen (request), -1); if (nwritten < 0) { @@ -977,7 +972,7 @@ ftp_list (int csock, const char *file) return WRITEFAILED; } xfree (request); - /* Get appropriate respone. */ + /* Get appropriate response. */ err = ftp_response (csock, &respline); if (err != FTPOK) return err; @@ -996,6 +991,62 @@ ftp_list (int csock, const char *file) return FTPOK; } +/* Sends the LIST command to the server. If FILE is NULL, send just + `LIST' (no space). */ +uerr_t +ftp_list (int csock, const char *file, enum stype rs) +{ + char *request, *respline; + int nwritten; + uerr_t err; + bool ok = false; + size_t i = 0; + /* Try `LIST -a' first and revert to `LIST' in case of failure. */ + const char *list_commands[] = { "LIST -a", + "LIST" }; + + /* 2008-01-29 SMS. For a VMS FTP server, where "LIST -a" may not + fail, but will never do what is desired here, skip directly to the + simple "LIST" command (assumed to be the last one in the list). + */ + if (rs == ST_VMS) + i = countof (list_commands)- 1; + + 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; + } + else + { + err = FTPRERR; + } + xfree (respline); + } + ++i; + } while (i < countof (list_commands) && !ok); + + return err; +} + /* Sends the SYST command to the server. */ uerr_t ftp_syst (int csock, enum stype *server_type) @@ -1031,12 +1082,14 @@ ftp_syst (int csock, enum stype *server_type) first word of the server response)? */ request = strtok (NULL, " "); - if (!strcasecmp (request, "VMS")) + 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; 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; @@ -1162,7 +1215,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'; }