wget: add support for https using "openssl s_client" as a helper

www.kernel.org started redirecting http:// to https://
making https support mandatory for any auto build scripts.

function                                             old     new   delta
wget_main                                           2631    2971    +340
parse_url                                            409     471     +62
.rodata                                           115607  115626     +19
P_HTTPS                                                -       6      +6
P_HTTP                                                 -       5      +5
P_FTP                                                  -       4      +4
------------------------------------------------------------------------------
(add/remove: 3/0 grow/shrink: 3/0 up/down: 436/0)             Total: 436 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2014-02-22 14:12:29 +01:00
parent 192c14bd87
commit 8b7e8ae224

View File

@ -47,10 +47,13 @@ struct host_info {
char *allocated;
const char *path;
char *user;
const char *protocol;
char *host;
int port;
smallint is_ftp;
};
static const char P_FTP[] = "ftp";
static const char P_HTTP[] = "http";
static const char P_HTTPS[] = "https";
/* Globals */
@ -219,7 +222,7 @@ static FILE *open_socket(len_and_sockaddr *lsa)
/* glibc 2.4 seems to try seeking on it - ??! */
/* hopefully it understands what ESPIPE means... */
fp = fdopen(fd, "r+");
if (fp == NULL)
if (!fp)
bb_perror_msg_and_die(bb_msg_memory_exhausted);
return fp;
@ -274,23 +277,31 @@ static void parse_url(const char *src_url, struct host_info *h)
free(h->allocated);
h->allocated = url = xstrdup(src_url);
if (strncmp(url, "ftp://", 6) == 0) {
h->port = bb_lookup_port("ftp", "tcp", 21);
h->host = url + 6;
h->is_ftp = 1;
h->protocol = P_FTP;
p = strstr(url, "://");
if (p) {
*p = '\0';
h->host = p + 3;
if (strcmp(url, P_FTP) == 0) {
h->port = bb_lookup_port(P_FTP, "tcp", 21);
} else
if (strncmp(url, "http://", 7) == 0) {
h->host = url + 7;
if (strcmp(url, P_HTTPS) == 0) {
h->port = bb_lookup_port(P_HTTPS, "tcp", 443);
h->protocol = P_HTTPS;
} else
if (strcmp(url, P_HTTP) == 0) {
http:
h->port = bb_lookup_port("http", "tcp", 80);
h->is_ftp = 0;
} else
if (!strstr(url, "//")) {
h->port = bb_lookup_port(P_HTTP, "tcp", 80);
h->protocol = P_HTTP;
} else {
*p = ':';
bb_error_msg_and_die("not an http or ftp url: %s", sanitize_string(url));
}
} else {
// GNU wget is user-friendly and falls back to http://
h->host = url;
goto http;
} else
bb_error_msg_and_die("not an http or ftp url: %s", sanitize_string(url));
}
// FYI:
// "Real" wget 'http://busybox.net?var=a/b' sends this request:
@ -472,6 +483,56 @@ static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_
return sfp;
}
static int spawn_https_helper(const char *host, unsigned port)
{
char *allocated = NULL;
int sp[2];
int pid;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) != 0)
/* Kernel can have AF_UNIX support disabled */
bb_perror_msg_and_die("socketpair");
if (!strchr(host, ':'))
host = allocated = xasprintf("%s:%u", host, port);
pid = BB_MMU ? xfork() : xvfork();
if (pid == 0) {
/* Child */
char *argv[6];
close(sp[0]);
xmove_fd(sp[1], 0);
xdup2(0, 1);
/*
* TODO: develop a tiny ssl/tls helper (using matrixssl?),
* try to exec it here before falling back to big fat openssl.
*/
/*
* openssl s_client -quiet -connect www.kernel.org:443 2>/dev/null
* It prints some debug stuff on stderr, don't know how to suppress it.
* Work around by dev-nulling stderr. We lose all error messages :(
*/
xmove_fd(2, 3);
xopen("/dev/null", O_RDWR);
argv[0] = (char*)"openssl";
argv[1] = (char*)"s_client";
argv[2] = (char*)"-quiet";
argv[3] = (char*)"-connect";
argv[4] = (char*)host;
argv[5] = NULL;
BB_EXECVP(argv[0], argv);
xmove_fd(3, 2);
bb_perror_msg_and_die("can't execute '%s'", argv[0]);
/* notreached */
}
/* parent process */
free(allocated);
close(sp[1]);
return sp[0];
}
static void NOINLINE retrieve_file_data(FILE *dfp)
{
#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
@ -644,7 +705,8 @@ static void download_one_url(const char *url)
/* Use the proxy if necessary */
use_proxy = (strcmp(G.proxy_flag, "off") != 0);
if (use_proxy) {
proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
proxy = getenv(target.protocol == P_FTP ? "ftp_proxy" : "http_proxy");
//FIXME: what if protocol is https? Ok to use http_proxy?
use_proxy = (proxy && proxy[0]);
if (use_proxy)
parse_url(proxy, &server);
@ -704,27 +766,31 @@ static void download_one_url(const char *url)
/*G.content_len = 0; - redundant, got_clen = 0 is enough */
G.got_clen = 0;
G.chunked = 0;
if (use_proxy || !target.is_ftp) {
if (use_proxy || target.protocol != P_FTP) {
/*
* HTTP session
*/
char *str;
int status;
/* Open socket to http server */
/* Open socket to http(s) server */
if (target.protocol == P_HTTPS) {
int fd = spawn_https_helper(server.host, server.port);
sfp = fdopen(fd, "r+");
if (!sfp)
bb_perror_msg_and_die(bb_msg_memory_exhausted);
} else
sfp = open_socket(lsa);
/* Send HTTP request */
if (use_proxy) {
fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
target.is_ftp ? "f" : "ht", target.host,
fprintf(sfp, "GET %s://%s/%s HTTP/1.1\r\n",
target.protocol, target.host,
target.path);
} else {
if (option_mask32 & WGET_OPT_POST_DATA)
fprintf(sfp, "POST /%s HTTP/1.1\r\n", target.path);
else
fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
fprintf(sfp, "%s /%s HTTP/1.1\r\n",
(option_mask32 & WGET_OPT_POST_DATA) ? "POST" : "GET",
target.path);
}
fprintf(sfp, "Host: %s\r\nUser-Agent: %s\r\n",