From df6da52cd502398fe6c35f43d83f92d89b3956d3 Mon Sep 17 00:00:00 2001 From: Eric Molitor Date: Thu, 28 Oct 2021 09:53:58 +0100 Subject: [PATCH] wget: Add TLS Support v2 --- scripts/make.sh | 2 +- toys/pending/wget.c | 147 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 118 insertions(+), 31 deletions(-) diff --git a/scripts/make.sh b/scripts/make.sh index 3caa855d..76ec03b1 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -108,7 +108,7 @@ then # and skip nonexistent libraries for it. > generated/optlibs.dat - for i in util crypt m resolv rt selinux smack attr crypto z log iconv + for i in util crypt m resolv rt selinux smack attr crypto z log iconv tls do echo "int main(int argc, char *argv[]) {return 0;}" | \ ${CROSS_COMPILE}${CC} $CFLAGS $LDFLAGS -xc - -o generated/libprobe $LDASNEEDED -l$i > /dev/null 2>/dev/null && diff --git a/toys/pending/wget.c b/toys/pending/wget.c index afaeaf2f..5fbdbeaa 100644 --- a/toys/pending/wget.c +++ b/toys/pending/wget.c @@ -14,44 +14,68 @@ * Chunked Encoding: https://jigsaw.w3.org/HTTP/ChunkedScript * Redirect 301: https://jigsaw.w3.org/HTTP/300/301.html * Redirect 302: https://jigsaw.w3.org/HTTP/300/302.html - * - * Test URLs for future features - * ----------------------------- * TLS 1.0: https://tls-v1-0.badssl.com:1010/ * TLS 1.1: https://tls-v1-0.badssl.com:1011/ * TLS 1.2: https://tls-v1-0.badssl.com:1012/ + * TLS 1.3: https://tls13.1d.pw/ + * + * Test URLs for future features + * ----------------------------- * Transfer Encoding [gzip|deflate]: https://jigsaw.w3.org/HTTP/TE/bar.txt * -USE_WGET(NEWTOY(wget, "<1>1d(debug)O(output-document):", TOYFLAG_USR|TOYFLAG_BIN)) +USE_WGET(NEWTOY(wget, "<1>1(max-redirect)#<0=20d(debug)O(output-document):", TOYFLAG_USR|TOYFLAG_BIN)) config WGET bool "wget" default n help - Usage: wget [OPTION]... [URL] + usage: wget [OPTIONS]... [URL] + --max-redirect maximum redirections allowed -d, --debug print lots of debugging information -O, --output-document=FILE specify output filename examples: wget http://www.example.com + +config WGET_TLS + bool "Enable HTTPS support for wget" + default n + depends on WGET + help + Enable HTTPS support for wget by linking to libtls. + Supports using libtls, libretls or libtls-bearssl. */ -// todo: Add support for TLS +// todo: Add support for configurable TLS versions // todo: Add support for ftp +// todo: Add support for Transfer Encoding (gzip|deflate) // todo: Add support for RFC5987 #define FOR_wget #include "toys.h" -#define WGET_FILENAME "Content-Disposition: attachment; filename=" -#define WGET_CHUNKED "transfer-encoding: chunked" -#define WGET_LOCATION "Location: " +#if CFG_WGET_TLS +#include +#endif -#define MAX_URL 2048 +#define WGET_FILENAME "Content-Disposition: attachment; filename=" +#define WGET_CHUNKED "transfer-encoding: chunked" +#define WGET_LOCATION "Location: " +#define WGET_TLS_PROTOCOLS "tlsv1.2" + +#define WGET_IS_HTTP (strncmp(TT.url, "http://", 7) == 0) +#define WGET_IS_HTTPS (CFG_WGET_TLS && (strncmp(TT.url, "https://", 8) == 0)) GLOBALS( char *filename; + long redirects; + + int sock; + char *url; + #if CFG_WGET_TLS + struct tls *tls; + #endif ) static char *wget_strncaseafter(char *haystack, char *needle) @@ -85,13 +109,74 @@ static void wget_info(char *url, char **host, char **port, char **path) } } - if (!*port) *port="80"; + if (!*port && WGET_IS_HTTP) *port = "80"; + else if (!*port && WGET_IS_HTTPS) *port = "443"; + else if (!*port) error_exit("unsupported protocol"); +} + +static void wget_connect(char *host, char *port) +{ + if (WGET_IS_HTTP) { + struct addrinfo *ai = xgetaddrinfo(host, port, AF_UNSPEC, SOCK_STREAM, 0,0); + TT.sock = xconnectany(ai); + } else if (WGET_IS_HTTPS) { + #if CFG_WGET_TLS + struct tls_config *cfg = NULL; + uint32_t protocols; + if ((TT.tls = tls_client()) == NULL) + error_exit("tls_client: %s", tls_error(TT.tls)); + if ((cfg = tls_config_new()) == NULL) + error_exit("tls_config_new: %s", tls_config_error(cfg)); + if (tls_config_parse_protocols(&protocols, WGET_TLS_PROTOCOLS) != 0) + error_exit("tls_config_parse_protocols"); + if (tls_config_set_protocols(cfg, protocols) != 0) + error_exit("tls_config_set_protocols: %s", tls_config_error(cfg)); + if (tls_configure(TT.tls, cfg) != 0) + error_exit("tls_configure: %s", tls_error(TT.tls)); + tls_config_free(cfg); + + if (tls_connect(TT.tls, host, port) != 0) + error_exit("tls_connect: %s", tls_error(TT.tls)); + #endif + } else error_exit("unsupported protocol"); +} + +static size_t wget_read(void *buf, size_t len) +{ + if (WGET_IS_HTTP) return xread(TT.sock, buf, len); + else if (WGET_IS_HTTPS) { + #if CFG_WGET_TLS + ssize_t ret = tls_read(TT.tls, buf, len); + if (ret < 0) error_exit("tls_read: %s", tls_error(TT.tls)); + return ret; + #endif + } else error_exit("unsupported protocol"); +} + +static void wget_write(void *buf, size_t len) +{ + if (WGET_IS_HTTP) { + xwrite(TT.sock, buf, len); + } else if (WGET_IS_HTTPS) { + #if CFG_WGET_TLS + if (len != tls_write(TT.tls, buf, len)) error_exit("tls_write: %s", tls_error(TT.tls)); + #endif + } else error_exit("unsupported protocol"); } -static int wget_connect(char *host, char *port) +static void wget_close() { - struct addrinfo *ai = xgetaddrinfo(host, port, AF_UNSPEC, SOCK_STREAM, 0,0); - return xconnectany(ai); + if (TT.sock) { + xclose(TT.sock); + TT.sock = 0; + } + + #if CFG_WGET_TLS + if (TT.tls) { + tls_close(TT.tls); + tls_free(TT.tls); + } + #endif } static char* wget_find_header(char *header, char *val) { @@ -104,11 +189,11 @@ static int wget_has_header(char *header, char *val) return wget_find_header(header, val) != NULL; } -static void wget_redirect(char *header, char url[]) +static char *wget_redirect(char *header) { char *redir = wget_find_header(header, WGET_LOCATION); if (!redir) error_exit("could not parse redirect URL"); - snprintf(url, MAX_URL, "%.*s", stridx(redir, '\r'), redir); + return xstrndup(redir, stridx(redir, '\r')); } static char *wget_filename(char *header, char *path) @@ -125,29 +210,29 @@ static char *wget_filename(char *header, char *path) void wget_main(void) { long status = 0; - ssize_t len, c_len = 0; - int sock, fd, chunked, redirects = 10; + size_t len, c_len = 0; + int fd, chunked; char *body, *index, *host, *port, *path; char agent[] = "toybox wget/" TOYBOX_VERSION; - char url[MAX_URL]; - xstrncpy(url, toys.optargs[0], MAX_URL); + TT.url = xstrdup(toys.optargs[0]); + + for (;status != 200; TT.redirects--) { + if (TT.redirects < 0) error_exit("Too many redirects"); - for (;status != 200; redirects--) { - if (redirects < 0) error_exit("Too many redirects"); - wget_info(url, &host, &port, &path); + wget_info(TT.url, &host, &port, &path); sprintf(toybuf, "GET /%s HTTP/1.1\r\nHost: %s\r\n" "User-Agent: %s\r\nConnection: close\r\n\r\n", path, host, agent); if (FLAG(d)) printf("--- Request\n%s", toybuf); - sock = wget_connect(host, port); - xwrite(sock, toybuf, strlen(toybuf)); + wget_connect(host, port); + wget_write(toybuf, strlen(toybuf)); // Greedily read the HTTP response until either complete or toybuf is full index = toybuf; - while ((len = read(sock, index, sizeof(toybuf) - (index - toybuf))) > 0) + while ((len = wget_read(index, sizeof(toybuf) - (index - toybuf))) > 0) index += len; //Process the response such that @@ -167,8 +252,9 @@ void wget_main(void) status = strtol(strafter(toybuf, " "), NULL, 10); if ((status == 301) || (status == 302)) { - wget_redirect(toybuf, url); - close(sock); + free(TT.url); + TT.url = wget_redirect(toybuf); + wget_close(); } else if (status != 200) error_exit("response: %ld", status); } @@ -231,8 +317,9 @@ void wget_main(void) xwrite(fd, toybuf, len); len = 0; } - } while ((len += xread(sock, toybuf + len, sizeof(toybuf) - len)) > 0); + } while ((len += wget_read(toybuf + len, sizeof(toybuf) - len)) > 0); exit: - close(sock); + wget_close(); + free(TT.url); } -- 2.39.2