[git commit] wget: add support for https using "openssl s_client" as a helper

Denys Vlasenko vda.linux at googlemail.com
Sat Feb 22 13:12:29 UTC 2014


commit: http://git.busybox.net/busybox/commit/?id=8b7e8ae2249ffd9aa2c67536554eb9f6b6636ba5
branch: http://git.busybox.net/busybox/commit/?id=refs/heads/master

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 at googlemail.com>
---
 networking/wget.c |  118 +++++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 92 insertions(+), 26 deletions(-)

diff --git a/networking/wget.c b/networking/wget.c
index 7ca947a..dfea3d4 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -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;
-	} else
-	if (strncmp(url, "http://", 7) == 0) {
-		h->host = url + 7;
+	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 (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 */
-		sfp = open_socket(lsa);
+		/* 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",


More information about the busybox-cvs mailing list