wget: fix redirection from HTTP to FTP server

while at it, sanitize redirection in general; add printout
of every redirection hop; make sure we won't print any non-ASCII
garbage from remote server in error meesages.

function                                             old     new   delta
sanitize_string                                        -      14     +14
parse_url                                            294     301      +7
gethdr                                               190     197      +7
wget_main                                           2326    2331      +5
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 3/0 up/down: 33/0)               Total: 33 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2009-06-28 03:33:57 +02:00
parent ab0c8d7b35
commit f1fab09242

View File

@ -6,7 +6,6 @@
* *
* Licensed under GPLv2, see file LICENSE in this tarball for details. * Licensed under GPLv2, see file LICENSE in this tarball for details.
*/ */
#include "libbb.h" #include "libbb.h"
struct host_info { struct host_info {
@ -239,6 +238,15 @@ static char *base64enc_512(char buf[512], const char *str)
} }
#endif #endif
static char* sanitize_string(char *s)
{
unsigned char *p = (void *) s;
while (*p >= ' ')
p++;
*p = '\0';
return s;
}
static FILE *open_socket(len_and_sockaddr *lsa) static FILE *open_socket(len_and_sockaddr *lsa)
{ {
FILE *fp; FILE *fp;
@ -294,7 +302,7 @@ static void parse_url(char *src_url, struct host_info *h)
h->host = url + 6; h->host = url + 6;
h->is_ftp = 1; h->is_ftp = 1;
} else } else
bb_error_msg_and_die("not an http or ftp url: %s", url); bb_error_msg_and_die("not an http or ftp url: %s", sanitize_string(url));
// FYI: // FYI:
// "Real" wget 'http://busybox.net?var=a/b' sends this request: // "Real" wget 'http://busybox.net?var=a/b' sends this request:
@ -360,7 +368,7 @@ static char *gethdr(char *buf, size_t bufsiz, FILE *fp /*, int *istrunc*/)
/* verify we are at the end of the header name */ /* verify we are at the end of the header name */
if (*s != ':') if (*s != ':')
bb_error_msg_and_die("bad header line: %s", buf); bb_error_msg_and_die("bad header line: %s", sanitize_string(buf));
/* locate the start of the header value */ /* locate the start of the header value */
*s++ = '\0'; *s++ = '\0';
@ -433,7 +441,7 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
sfp = open_socket(lsa); sfp = open_socket(lsa);
if (ftpcmd(NULL, NULL, sfp, buf) != 220) if (ftpcmd(NULL, NULL, sfp, buf) != 220)
bb_error_msg_and_die("%s", buf+4); bb_error_msg_and_die("%s", sanitize_string(buf+4));
/* /*
* Splitting username:password pair, * Splitting username:password pair,
@ -450,7 +458,7 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
break; break;
/* fall through (failed login) */ /* fall through (failed login) */
default: default:
bb_error_msg_and_die("ftp login: %s", buf+4); bb_error_msg_and_die("ftp login: %s", sanitize_string(buf+4));
} }
ftpcmd("TYPE I", NULL, sfp, buf); ftpcmd("TYPE I", NULL, sfp, buf);
@ -471,7 +479,7 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
*/ */
if (ftpcmd("PASV", NULL, sfp, buf) != 227) { if (ftpcmd("PASV", NULL, sfp, buf) != 227) {
pasv_error: pasv_error:
bb_error_msg_and_die("bad response to %s: %s", "PASV", buf); bb_error_msg_and_die("bad response to %s: %s", "PASV", sanitize_string(buf));
} }
// Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage] // Response is "227 garbageN1,N2,N3,N4,P1,P2[)garbage]
// Server's IP is N1.N2.N3.N4 (we ignore it) // Server's IP is N1.N2.N3.N4 (we ignore it)
@ -496,7 +504,7 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
} }
if (ftpcmd("RETR ", target->path, sfp, buf) > 150) if (ftpcmd("RETR ", target->path, sfp, buf) > 150)
bb_error_msg_and_die("bad response to %s: %s", "RETR", buf); bb_error_msg_and_die("bad response to %s: %s", "RETR", sanitize_string(buf));
return sfp; return sfp;
} }
@ -574,6 +582,7 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
struct host_info server, target; struct host_info server, target;
len_and_sockaddr *lsa; len_and_sockaddr *lsa;
unsigned opt; unsigned opt;
int redir_limit;
char *proxy = NULL; char *proxy = NULL;
char *dir_prefix = NULL; char *dir_prefix = NULL;
#if ENABLE_FEATURE_WGET_LONG_OPTIONS #if ENABLE_FEATURE_WGET_LONG_OPTIONS
@ -696,36 +705,23 @@ int wget_main(int argc UNUSED_PARAM, char **argv)
* We are not sure it exists on remove side */ * We are not sure it exists on remove side */
} }
/* We want to do exactly _one_ DNS lookup, since some redir_limit = 5;
* sites (i.e. ftp.us.debian.org) use round-robin DNS resolve_lsa:
* and we want to connect to only one IP... */
lsa = xhost2sockaddr(server.host, server.port); lsa = xhost2sockaddr(server.host, server.port);
if (!(opt & WGET_OPT_QUIET)) { if (!(opt & WGET_OPT_QUIET)) {
fprintf(stderr, "Connecting to %s (%s)\n", server.host, char *s = xmalloc_sockaddr2dotted(&lsa->u.sa);
xmalloc_sockaddr2dotted(&lsa->u.sa)); fprintf(stderr, "Connecting to %s (%s)\n", server.host, s);
/* We leak result of xmalloc_sockaddr2dotted */ free(s);
} }
establish_session:
/* G.got_clen = 0; - already is */
sfp = NULL;
if (use_proxy || !target.is_ftp) { if (use_proxy || !target.is_ftp) {
/* /*
* HTTP session * HTTP session
*/ */
int status;
int try = 5;
do {
char *str; char *str;
int status;
G.got_clen = 0;
G.chunked = 0;
if (!--try)
bb_error_msg_and_die("too many redirections");
/* Open socket to http server */ /* Open socket to http server */
if (sfp) fclose(sfp);
sfp = open_socket(lsa); sfp = open_socket(lsa);
/* Send HTTP request */ /* Send HTTP request */
@ -830,9 +826,7 @@ However, in real world it was observed that some web servers
break; break;
/* fall through */ /* fall through */
default: default:
/* Show first line only and kill any ESC tricks */ bb_error_msg_and_die("server returned error: %s", sanitize_string(buf));
buf[strcspn(buf, "\n\r\x1b")] = '\0';
bb_error_msg_and_die("server returned error: %s", buf);
} }
/* /*
@ -844,34 +838,42 @@ However, in real world it was observed that some web servers
if (key == KEY_content_length) { if (key == KEY_content_length) {
content_len = BB_STRTOOFF(str, NULL, 10); content_len = BB_STRTOOFF(str, NULL, 10);
if (errno || content_len < 0) { if (errno || content_len < 0) {
bb_error_msg_and_die("content-length %s is garbage", str); bb_error_msg_and_die("content-length %s is garbage", sanitize_string(str));
} }
G.got_clen = 1; G.got_clen = 1;
continue; continue;
} }
if (key == KEY_transfer_encoding) { if (key == KEY_transfer_encoding) {
if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked) if (index_in_strings(keywords, str_tolower(str)) + 1 != KEY_chunked)
bb_error_msg_and_die("transfer encoding '%s' is not supported", str); bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str));
G.chunked = G.got_clen = 1; G.chunked = G.got_clen = 1;
} }
if (key == KEY_location) { if (key == KEY_location && status >= 300) {
if (--redir_limit == 0)
bb_error_msg_and_die("too many redirections");
fclose(sfp);
G.got_clen = 0;
G.chunked = 0;
if (str[0] == '/') if (str[0] == '/')
/* free(target.allocated); */ /* free(target.allocated); */
target.path = /* target.allocated = */ xstrdup(str+1); target.path = /* target.allocated = */ xstrdup(str+1);
/* lsa stays the same: it's on the same server */
else { else {
parse_url(str, &target); parse_url(str, &target);
if (use_proxy == 0) { if (!use_proxy) {
server.host = target.host; server.host = target.host;
server.port = target.port; server.port = target.port;
}
free(lsa); free(lsa);
lsa = xhost2sockaddr(server.host, server.port); goto resolve_lsa;
break; } /* else: lsa stays the same: we use proxy */
}
goto establish_session;
} }
} }
} // if (status >= 300)
} while (status >= 300); // bb_error_msg_and_die("bad redirection (no Location: header from server)");
/* For HTTP, data is pumped over the same connection */
dfp = sfp; dfp = sfp;
} else { } else {
@ -897,10 +899,11 @@ However, in real world it was observed that some web servers
retrieve_file_data(dfp, output_fd); retrieve_file_data(dfp, output_fd);
if ((use_proxy == 0) && target.is_ftp) { if (dfp != sfp) {
/* It's ftp. Close it properly */
fclose(dfp); fclose(dfp);
if (ftpcmd(NULL, NULL, sfp, buf) != 226) if (ftpcmd(NULL, NULL, sfp, buf) != 226)
bb_error_msg_and_die("ftp error: %s", buf+4); bb_error_msg_and_die("ftp error: %s", sanitize_string(buf+4));
ftpcmd("QUIT", NULL, sfp, buf); ftpcmd("QUIT", NULL, sfp, buf);
} }