diff options
Diffstat (limited to 'net/lwip/wget.c')
-rw-r--r-- | net/lwip/wget.c | 156 |
1 files changed, 132 insertions, 24 deletions
diff --git a/net/lwip/wget.c b/net/lwip/wget.c index b495ebd1aa9..c23f0640ec6 100644 --- a/net/lwip/wget.c +++ b/net/lwip/wget.c @@ -7,22 +7,28 @@ #include <efi_loader.h> #include <image.h> #include <lwip/apps/http_client.h> +#include "lwip/altcp_tls.h" #include <lwip/timeouts.h> +#include <rng.h> #include <mapmem.h> #include <net.h> #include <time.h> +#include <dm/uclass.h> -#define SERVER_NAME_SIZE 200 +#define SERVER_NAME_SIZE 254 #define HTTP_PORT_DEFAULT 80 +#define HTTPS_PORT_DEFAULT 443 #define PROGRESS_PRINT_STEP_BYTES (100 * 1024) enum done_state { - NOT_DONE = 0, - SUCCESS = 1, - FAILURE = 2 + NOT_DONE = 0, + SUCCESS = 1, + FAILURE = 2 }; struct wget_ctx { + char server_name[SERVER_NAME_SIZE]; + u16 port; char *path; ulong daddr; ulong saved_daddr; @@ -32,18 +38,71 @@ struct wget_ctx { enum done_state done; }; -static int parse_url(char *url, char *host, u16 *port, char **path) +static void wget_lwip_fill_info(struct pbuf *hdr, u16_t hdr_len, u32_t hdr_cont_len) +{ + if (wget_info->headers) { + if (hdr_len < MAX_HTTP_HEADERS_SIZE) + pbuf_copy_partial(hdr, (void *)wget_info->headers, hdr_len, 0); + else + hdr_len = 0; + wget_info->headers[hdr_len] = 0; + } + wget_info->hdr_cont_len = (u32)hdr_cont_len; +} + +static void wget_lwip_set_file_size(u32_t rx_content_len) +{ + wget_info->file_size = (ulong)rx_content_len; +} + +bool wget_validate_uri(char *uri); + +int mbedtls_hardware_poll(void *data, unsigned char *output, size_t len, + size_t *olen) +{ + struct udevice *dev; + int ret; + + *olen = 0; + + ret = uclass_get_device(UCLASS_RNG, 0, &dev); + if (ret) { + log_err("Failed to get an rng: %d\n", ret); + return ret; + } + ret = dm_rng_read(dev, output, len); + if (ret) + return ret; + + *olen = len; + + return 0; +} + +static int parse_url(char *url, char *host, u16 *port, char **path, + bool *is_https) { char *p, *pp; long lport; + size_t prefix_len = 0; + if (!wget_validate_uri(url)) { + log_err("Invalid URL. Use http(s)://\n"); + return -EINVAL; + } + + *is_https = false; + *port = HTTP_PORT_DEFAULT; + prefix_len = strlen("http://"); p = strstr(url, "http://"); if (!p) { - log_err("only http:// is supported\n"); - return -EINVAL; + p = strstr(url, "https://"); + prefix_len = strlen("https://"); + *port = HTTPS_PORT_DEFAULT; + *is_https = true; } - p += strlen("http://"); + p += prefix_len; /* Parse hostname */ pp = strchr(p, ':'); @@ -67,9 +126,8 @@ static int parse_url(char *url, char *host, u16 *port, char **path) if (lport > 65535) return -EINVAL; *port = (u16)lport; - } else { - *port = HTTP_PORT_DEFAULT; } + if (*pp != '/') return -EINVAL; *path = pp; @@ -122,7 +180,7 @@ static int parse_legacy_arg(char *arg, char *nurl, size_t rem) if (rem < n) return -1; - strncpy(p, server, n); + strlcpy(p, server, n); p += n; rem -= n; if (rem < 1) @@ -133,7 +191,7 @@ static int parse_legacy_arg(char *arg, char *nurl, size_t rem) n = strlen(path); if (rem < n) return -1; - strncpy(p, path, n); + strlcpy(p, path, n); p += n; rem -= n; if (rem < 1) @@ -176,6 +234,13 @@ static void httpc_result_cb(void *arg, httpc_result_t httpc_result, struct wget_ctx *ctx = arg; ulong elapsed; + wget_info->status_code = (u32)srv_res; + + if (err == ERR_BUF) { + ctx->done = FAILURE; + return; + } + if (httpc_result != HTTPC_RESULT_OK) { log_err("\nHTTP client error %d\n", httpc_result); ctx->done = FAILURE; @@ -195,8 +260,10 @@ static void httpc_result_cb(void *arg, httpc_result_t httpc_result, printf("%u bytes transferred in %lu ms (", rx_content_len, elapsed); print_size(rx_content_len / elapsed * 1000, "/s)\n"); printf("Bytes transferred = %lu (%lx hex)\n", ctx->size, ctx->size); - efi_set_bootdev("Net", "", ctx->path, map_sysmem(ctx->saved_daddr, 0), - rx_content_len); + if (wget_info->set_bootdev) + efi_set_bootdev("Http", ctx->server_name, ctx->path, map_sysmem(ctx->saved_daddr, 0), + rx_content_len); + wget_lwip_set_file_size(rx_content_len); if (env_set_hex("filesize", rx_content_len) || env_set_hex("fileaddr", ctx->saved_daddr)) { log_err("Could not set filesize or fileaddr\n"); @@ -207,15 +274,28 @@ static void httpc_result_cb(void *arg, httpc_result_t httpc_result, ctx->done = SUCCESS; } +static err_t httpc_headers_done_cb(httpc_state_t *connection, void *arg, struct pbuf *hdr, + u16_t hdr_len, u32_t content_len) +{ + wget_lwip_fill_info(hdr, hdr_len, content_len); + + if (wget_info->check_buffer_size && (ulong)content_len > wget_info->buffer_size) + return ERR_BUF; + + return ERR_OK; +} + static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri) { - char server_name[SERVER_NAME_SIZE]; +#if defined CONFIG_WGET_HTTPS + altcp_allocator_t tls_allocator; +#endif httpc_connection_t conn; httpc_state_t *state; struct netif *netif; struct wget_ctx ctx; char *path; - u16 port; + bool is_https; ctx.daddr = dst_addr; ctx.saved_daddr = dst_addr; @@ -224,7 +304,7 @@ static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri) ctx.prevsize = 0; ctx.start_time = 0; - if (parse_url(uri, server_name, &port, &path)) + if (parse_url(uri, ctx.server_name, &ctx.port, &path, &is_https)) return CMD_RET_USAGE; netif = net_lwip_new_netif(udev); @@ -232,9 +312,26 @@ static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri) return -1; memset(&conn, 0, sizeof(conn)); +#if defined CONFIG_WGET_HTTPS + if (is_https) { + tls_allocator.alloc = &altcp_tls_alloc; + tls_allocator.arg = + altcp_tls_create_config_client(NULL, 0, ctx.server_name); + + if (!tls_allocator.arg) { + log_err("error: Cannot create a TLS connection\n"); + net_lwip_remove_netif(netif); + return -1; + } + + conn.altcp_allocator = &tls_allocator; + } +#endif + conn.result_fn = httpc_result_cb; + conn.headers_done_fn = httpc_headers_done_cb; ctx.path = path; - if (httpc_get_file_dns(server_name, port, path, &conn, httpc_recv_cb, + if (httpc_get_file_dns(ctx.server_name, ctx.port, path, &conn, httpc_recv_cb, &ctx, &state)) { net_lwip_remove_netif(netif); return CMD_RET_FAILURE; @@ -255,10 +352,13 @@ static int wget_loop(struct udevice *udev, ulong dst_addr, char *uri) return -1; } -int wget_with_dns(ulong dst_addr, char *uri) +int wget_do_request(ulong dst_addr, char *uri) { eth_set_current(); + if (!wget_info) + wget_info = &default_wget_info; + return wget_loop(eth_get_dev(), dst_addr, uri); } @@ -273,7 +373,7 @@ int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) return CMD_RET_USAGE; dst_addr = hextoul(argv[1], &end); - if (end == (argv[1] + strlen(argv[1]))) { + if (end == (argv[1] + strlen(argv[1]))) { if (argc < 3) return CMD_RET_USAGE; url = argv[2]; @@ -283,9 +383,10 @@ int do_wget(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) } if (parse_legacy_arg(url, nurl, sizeof(nurl))) - return CMD_RET_FAILURE; + return CMD_RET_FAILURE; - if (wget_with_dns(dst_addr, nurl)) + wget_info = &default_wget_info; + if (wget_do_request(dst_addr, nurl)) return CMD_RET_FAILURE; return CMD_RET_SUCCESS; @@ -316,6 +417,7 @@ bool wget_validate_uri(char *uri) char c; bool ret = true; char *str_copy, *s, *authority; + size_t prefix_len = 0; for (c = 0x1; c < 0x21; c++) { if (strchr(uri, c)) { @@ -323,15 +425,21 @@ bool wget_validate_uri(char *uri) return false; } } + if (strchr(uri, 0x7f)) { log_err("invalid character is used\n"); return false; } - if (strncmp(uri, "http://", 7)) { - log_err("only http:// is supported\n"); + if (!strncmp(uri, "http://", strlen("http://"))) { + prefix_len = strlen("http://"); + } else if (!strncmp(uri, "https://", strlen("https://"))) { + prefix_len = strlen("https://"); + } else { + log_err("only http(s):// is supported\n"); return false; } + str_copy = strdup(uri); if (!str_copy) return false; |