wget: add support for https using "openssl s_client" as a helper
No binary (build failed)
—
(log yok)
| @@ -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", | |