Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * WGET/HTTP support driver based on U-BOOT's nfs.c |
| 4 | * Copyright Duncan Hare <dh@synoia.com> 2017 |
| 5 | */ |
| 6 | |
Masahisa Kojima | 77b0ae3 | 2023-11-10 13:25:34 +0900 | [diff] [blame] | 7 | #include <asm/global_data.h> |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 8 | #include <command.h> |
Michael Walle | caad55b | 2022-12-28 16:27:14 +0100 | [diff] [blame] | 9 | #include <display_options.h> |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 10 | #include <env.h> |
Jerome Forissier | 612b2b1 | 2024-09-11 11:58:22 +0200 | [diff] [blame] | 11 | #include <efi_loader.h> |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 12 | #include <image.h> |
Masahisa Kojima | 77b0ae3 | 2023-11-10 13:25:34 +0900 | [diff] [blame] | 13 | #include <lmb.h> |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 14 | #include <mapmem.h> |
| 15 | #include <net.h> |
| 16 | #include <net/tcp.h> |
| 17 | #include <net/wget.h> |
Masahisa Kojima | 6721d18 | 2023-11-10 13:25:35 +0900 | [diff] [blame] | 18 | #include <stdlib.h> |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 19 | |
Masahisa Kojima | 77b0ae3 | 2023-11-10 13:25:34 +0900 | [diff] [blame] | 20 | DECLARE_GLOBAL_DATA_PTR; |
| 21 | |
Marek Vasut | 22a9508 | 2023-12-13 22:11:13 +0100 | [diff] [blame] | 22 | /* The default, change with environment variable 'httpdstp' */ |
| 23 | #define SERVER_PORT 80 |
| 24 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 25 | #define HASHES_PER_LINE 65 |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 26 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 27 | #define HTTP_MAX_HDR_LEN 2048 |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 28 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 29 | #define HTTP_STATUS_BAD 0 |
| 30 | #define HTTP_STATUS_OK 200 |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 31 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 32 | static const char http_proto[] = "HTTP/1.0"; |
| 33 | static const char http_eom[] = "\r\n\r\n"; |
| 34 | static const char content_len[] = "Content-Length:"; |
| 35 | static const char linefeed[] = "\r\n"; |
| 36 | static struct in_addr web_server_ip; |
| 37 | static unsigned int server_port; |
| 38 | static unsigned long content_length; |
| 39 | static u32 http_hdr_size, max_rx_pos; |
| 40 | static int wget_tsize_num_hash; |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 41 | |
| 42 | static char *image_url; |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 43 | static enum net_loop_state wget_loop_state; |
| 44 | |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 45 | /** |
| 46 | * store_block() - store block in memory |
| 47 | * @src: source of data |
| 48 | * @offset: offset |
| 49 | * @len: length |
| 50 | */ |
| 51 | static inline int store_block(uchar *src, unsigned int offset, unsigned int len) |
| 52 | { |
Masahisa Kojima | 77b0ae3 | 2023-11-10 13:25:34 +0900 | [diff] [blame] | 53 | ulong store_addr = image_load_addr + offset; |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 54 | uchar *ptr; |
| 55 | |
Adriano Cordova | 3f3409c | 2025-02-06 14:40:11 -0300 | [diff] [blame] | 56 | // Avoid overflow |
| 57 | if (wget_info->buffer_size && wget_info->buffer_size < offset + len) |
| 58 | return -1; |
Adriano Cordova | 47f35e3 | 2024-11-11 18:08:58 -0300 | [diff] [blame] | 59 | if (CONFIG_IS_ENABLED(LMB) && wget_info->set_bootdev) { |
Masahisa Kojima | 77b0ae3 | 2023-11-10 13:25:34 +0900 | [diff] [blame] | 60 | if (store_addr < image_load_addr || |
Sughosh Ganu | 7772809 | 2024-09-16 20:50:25 +0530 | [diff] [blame] | 61 | lmb_read_check(store_addr, len)) { |
Jerome Forissier | 95b1035 | 2025-04-17 15:26:58 +0200 | [diff] [blame] | 62 | if (!wget_info->silent) { |
| 63 | printf("\nwget error: "); |
| 64 | printf("trying to overwrite reserved memory\n"); |
| 65 | } |
Masahisa Kojima | 77b0ae3 | 2023-11-10 13:25:34 +0900 | [diff] [blame] | 66 | return -1; |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | ptr = map_sysmem(store_addr, len); |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 71 | memcpy(ptr, src, len); |
| 72 | unmap_sysmem(ptr); |
| 73 | |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 74 | return 0; |
| 75 | } |
| 76 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 77 | static void show_block_marker(u32 packets) |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 78 | { |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 79 | int cnt; |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 80 | |
Jerome Forissier | 95b1035 | 2025-04-17 15:26:58 +0200 | [diff] [blame] | 81 | if (wget_info->silent) |
| 82 | return; |
| 83 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 84 | if (content_length != -1) { |
| 85 | if (net_boot_file_size > content_length) |
| 86 | content_length = net_boot_file_size; |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 87 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 88 | cnt = net_boot_file_size * 50 / content_length; |
| 89 | while (wget_tsize_num_hash < cnt) { |
| 90 | putc('#'); |
| 91 | wget_tsize_num_hash++; |
Adriano Cordova | 47f35e3 | 2024-11-11 18:08:58 -0300 | [diff] [blame] | 92 | } |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 93 | } else { |
| 94 | if ((packets % 10) == 0) |
| 95 | putc('#'); |
| 96 | else if (((packets + 1) % (10 * HASHES_PER_LINE)) == 0) |
| 97 | puts("\n"); |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 98 | } |
| 99 | } |
| 100 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 101 | static void tcp_stream_on_closed(struct tcp_stream *tcp) |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 102 | { |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 103 | if (tcp->status != TCP_ERR_OK) |
| 104 | wget_loop_state = NETLOOP_FAIL; |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 105 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 106 | net_set_state(wget_loop_state); |
| 107 | if (wget_loop_state != NETLOOP_SUCCESS) { |
| 108 | net_boot_file_size = 0; |
Jerome Forissier | 95b1035 | 2025-04-17 15:26:58 +0200 | [diff] [blame] | 109 | if (!wget_info->silent) |
| 110 | printf("\nwget: Transfer Fail, TCP status - %d\n", |
| 111 | tcp->status); |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 112 | return; |
| 113 | } |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 114 | |
Jerome Forissier | 95b1035 | 2025-04-17 15:26:58 +0200 | [diff] [blame] | 115 | if (!wget_info->silent) |
| 116 | printf("\nPackets received %d, Transfer Successful\n", |
| 117 | tcp->rx_packets); |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 118 | wget_info->file_size = net_boot_file_size; |
| 119 | if (wget_info->method == WGET_HTTP_METHOD_GET && wget_info->set_bootdev) { |
| 120 | efi_set_bootdev("Http", NULL, image_url, |
| 121 | map_sysmem(image_load_addr, 0), |
| 122 | net_boot_file_size); |
| 123 | env_set_hex("filesize", net_boot_file_size); |
| 124 | } |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 125 | } |
| 126 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 127 | static void tcp_stream_on_rcv_nxt_update(struct tcp_stream *tcp, u32 rx_bytes) |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 128 | { |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 129 | char *pos, *tail; |
| 130 | uchar saved, *ptr; |
| 131 | int reply_len; |
| 132 | |
| 133 | if (http_hdr_size) { |
| 134 | net_boot_file_size = rx_bytes - http_hdr_size; |
| 135 | show_block_marker(tcp->rx_packets); |
| 136 | return; |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 137 | } |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 138 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 139 | ptr = map_sysmem(image_load_addr, rx_bytes + 1); |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 140 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 141 | saved = ptr[rx_bytes]; |
| 142 | ptr[rx_bytes] = '\0'; |
| 143 | pos = strstr((char *)ptr, http_eom); |
| 144 | ptr[rx_bytes] = saved; |
| 145 | |
| 146 | if (!pos) { |
| 147 | if (rx_bytes < HTTP_MAX_HDR_LEN && |
| 148 | tcp->state == TCP_ESTABLISHED) |
| 149 | goto end; |
Adriano Cordova | 47f35e3 | 2024-11-11 18:08:58 -0300 | [diff] [blame] | 150 | |
Jerome Forissier | 95b1035 | 2025-04-17 15:26:58 +0200 | [diff] [blame] | 151 | if (!wget_info->silent) |
| 152 | printf("ERROR: misssed HTTP header\n"); |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 153 | tcp_stream_close(tcp); |
| 154 | goto end; |
Heinrich Schuchardt | 89e4090 | 2024-11-26 13:19:20 -0300 | [diff] [blame] | 155 | } |
Adriano Cordova | 47f35e3 | 2024-11-11 18:08:58 -0300 | [diff] [blame] | 156 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 157 | http_hdr_size = pos - (char *)ptr + strlen(http_eom); |
| 158 | *pos = '\0'; |
| 159 | |
| 160 | if (wget_info->headers && http_hdr_size < MAX_HTTP_HEADERS_SIZE) |
| 161 | strcpy(wget_info->headers, ptr); |
| 162 | |
| 163 | /* check for HTTP proto */ |
| 164 | if (strncasecmp((char *)ptr, "HTTP/", 5)) { |
| 165 | debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer " |
| 166 | "(no HTTP Status Line found)\n"); |
| 167 | tcp_stream_close(tcp); |
| 168 | goto end; |
Adriano Cordova | 47f35e3 | 2024-11-11 18:08:58 -0300 | [diff] [blame] | 169 | } |
| 170 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 171 | /* get HTTP reply len */ |
| 172 | pos = strstr((char *)ptr, linefeed); |
| 173 | if (pos) |
| 174 | reply_len = pos - (char *)ptr; |
| 175 | else |
| 176 | reply_len = http_hdr_size - strlen(http_eom); |
| 177 | |
| 178 | pos = strchr((char *)ptr, ' '); |
| 179 | if (!pos || pos - (char *)ptr > reply_len) { |
| 180 | debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer " |
| 181 | "(no HTTP Status Code found)\n"); |
| 182 | tcp_stream_close(tcp); |
| 183 | goto end; |
Adriano Cordova | 47f35e3 | 2024-11-11 18:08:58 -0300 | [diff] [blame] | 184 | } |
| 185 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 186 | wget_info->status_code = (u32)simple_strtoul(pos + 1, &tail, 10); |
| 187 | if (tail == pos + 1 || *tail != ' ') { |
| 188 | debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer " |
| 189 | "(bad HTTP Status Code)\n"); |
| 190 | tcp_stream_close(tcp); |
| 191 | goto end; |
| 192 | } |
Adriano Cordova | 47f35e3 | 2024-11-11 18:08:58 -0300 | [diff] [blame] | 193 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 194 | debug_cond(DEBUG_WGET, |
| 195 | "wget: HTTP Status Code %d\n", wget_info->status_code); |
Adriano Cordova | 47f35e3 | 2024-11-11 18:08:58 -0300 | [diff] [blame] | 196 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 197 | if (wget_info->status_code != HTTP_STATUS_OK) { |
| 198 | debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer\n"); |
| 199 | tcp_stream_close(tcp); |
| 200 | goto end; |
| 201 | } |
Adriano Cordova | 47f35e3 | 2024-11-11 18:08:58 -0300 | [diff] [blame] | 202 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 203 | debug_cond(DEBUG_WGET, "wget: Connctd pkt %p hlen %x\n", |
| 204 | ptr, http_hdr_size); |
| 205 | |
| 206 | content_length = -1; |
| 207 | pos = strstr((char *)ptr, content_len); |
Adriano Cordova | 47f35e3 | 2024-11-11 18:08:58 -0300 | [diff] [blame] | 208 | if (pos) { |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 209 | pos += strlen(content_len) + 1; |
Adriano Cordova | 47f35e3 | 2024-11-11 18:08:58 -0300 | [diff] [blame] | 210 | while (*pos == ' ') |
| 211 | pos++; |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 212 | content_length = simple_strtoul(pos, &tail, 10); |
| 213 | if (*tail != '\r' && *tail != '\n' && *tail != '\0') |
| 214 | content_length = -1; |
| 215 | } |
| 216 | |
| 217 | if (content_length >= 0) { |
Adriano Cordova | 47f35e3 | 2024-11-11 18:08:58 -0300 | [diff] [blame] | 218 | debug_cond(DEBUG_WGET, |
| 219 | "wget: Connected Len %lu\n", |
| 220 | content_length); |
| 221 | wget_info->hdr_cont_len = content_length; |
Adriano Cordova | 3f3409c | 2025-02-06 14:40:11 -0300 | [diff] [blame] | 222 | if (wget_info->buffer_size && wget_info->buffer_size < wget_info->hdr_cont_len){ |
| 223 | tcp_stream_reset(tcp); |
| 224 | goto end; |
| 225 | } |
| 226 | |
Adriano Cordova | 47f35e3 | 2024-11-11 18:08:58 -0300 | [diff] [blame] | 227 | } |
Adriano Cordova | 47f35e3 | 2024-11-11 18:08:58 -0300 | [diff] [blame] | 228 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 229 | net_boot_file_size = rx_bytes - http_hdr_size; |
| 230 | memmove(ptr, ptr + http_hdr_size, max_rx_pos + 1 - http_hdr_size); |
| 231 | wget_loop_state = NETLOOP_SUCCESS; |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 232 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 233 | end: |
| 234 | unmap_sysmem(ptr); |
| 235 | } |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 236 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 237 | static int tcp_stream_rx(struct tcp_stream *tcp, u32 rx_offs, void *buf, int len) |
| 238 | { |
| 239 | if ((max_rx_pos == (u32)(-1)) || (max_rx_pos < rx_offs + len - 1)) |
| 240 | max_rx_pos = rx_offs + len - 1; |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 241 | |
Adriano Cordova | 3f3409c | 2025-02-06 14:40:11 -0300 | [diff] [blame] | 242 | // Avoid overflow |
| 243 | if (store_block(buf, rx_offs - http_hdr_size, len) < 0) |
| 244 | return -1; |
Masahisa Kojima | 77b0ae3 | 2023-11-10 13:25:34 +0900 | [diff] [blame] | 245 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 246 | return len; |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 247 | } |
| 248 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 249 | static int tcp_stream_tx(struct tcp_stream *tcp, u32 tx_offs, void *buf, int maxlen) |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 250 | { |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 251 | int ret; |
| 252 | const char *method; |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 253 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 254 | if (tx_offs) |
| 255 | return 0; |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 256 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 257 | switch (wget_info->method) { |
| 258 | case WGET_HTTP_METHOD_HEAD: |
| 259 | method = "HEAD"; |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 260 | break; |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 261 | case WGET_HTTP_METHOD_GET: |
| 262 | default: |
| 263 | method = "GET"; |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 264 | break; |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 265 | } |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 266 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 267 | ret = snprintf(buf, maxlen, "%s %s %s\r\n\r\n", |
| 268 | method, image_url, http_proto); |
Yasuharu Shibata | 07a00ef | 2024-04-14 19:46:07 +0900 | [diff] [blame] | 269 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 270 | return ret; |
| 271 | } |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 272 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 273 | static int tcp_stream_on_create(struct tcp_stream *tcp) |
| 274 | { |
| 275 | if (tcp->rhost.s_addr != web_server_ip.s_addr || |
| 276 | tcp->rport != server_port) |
| 277 | return 0; |
| 278 | |
| 279 | tcp->max_retry_count = WGET_RETRY_COUNT; |
| 280 | tcp->initial_timeout = WGET_TIMEOUT; |
| 281 | tcp->on_closed = tcp_stream_on_closed; |
| 282 | tcp->on_rcv_nxt_update = tcp_stream_on_rcv_nxt_update; |
| 283 | tcp->rx = tcp_stream_rx; |
| 284 | tcp->tx = tcp_stream_tx; |
| 285 | |
| 286 | return 1; |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 287 | } |
| 288 | |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 289 | #define BLOCKSIZE 512 |
| 290 | |
| 291 | void wget_start(void) |
| 292 | { |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 293 | struct tcp_stream *tcp; |
Mikhail Kshevetskiy | 215f73f | 2024-12-28 13:46:30 +0300 | [diff] [blame] | 294 | |
Adriano Cordova | 47f35e3 | 2024-11-11 18:08:58 -0300 | [diff] [blame] | 295 | if (!wget_info) |
| 296 | wget_info = &default_wget_info; |
| 297 | |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 298 | image_url = strchr(net_boot_file_name, ':'); |
| 299 | if (image_url > 0) { |
| 300 | web_server_ip = string_to_ip(net_boot_file_name); |
| 301 | ++image_url; |
| 302 | net_server_ip = web_server_ip; |
| 303 | } else { |
| 304 | web_server_ip = net_server_ip; |
| 305 | image_url = net_boot_file_name; |
| 306 | } |
| 307 | |
| 308 | debug_cond(DEBUG_WGET, |
| 309 | "wget: Transfer HTTP Server %pI4; our IP %pI4\n", |
| 310 | &web_server_ip, &net_ip); |
| 311 | |
| 312 | /* Check if we need to send across this subnet */ |
| 313 | if (net_gateway.s_addr && net_netmask.s_addr) { |
| 314 | struct in_addr our_net; |
| 315 | struct in_addr server_net; |
| 316 | |
| 317 | our_net.s_addr = net_ip.s_addr & net_netmask.s_addr; |
| 318 | server_net.s_addr = net_server_ip.s_addr & net_netmask.s_addr; |
| 319 | if (our_net.s_addr != server_net.s_addr) |
| 320 | debug_cond(DEBUG_WGET, |
| 321 | "wget: sending through gateway %pI4", |
| 322 | &net_gateway); |
| 323 | } |
| 324 | debug_cond(DEBUG_WGET, "URL '%s'\n", image_url); |
| 325 | |
| 326 | if (net_boot_file_expected_size_in_blocks) { |
| 327 | debug_cond(DEBUG_WGET, "wget: Size is 0x%x Bytes = ", |
| 328 | net_boot_file_expected_size_in_blocks * BLOCKSIZE); |
| 329 | print_size(net_boot_file_expected_size_in_blocks * BLOCKSIZE, |
| 330 | ""); |
| 331 | } |
| 332 | debug_cond(DEBUG_WGET, |
| 333 | "\nwget:Load address: 0x%lx\nLoading: *\b", image_load_addr); |
| 334 | |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 335 | /* |
| 336 | * Zero out server ether to force arp resolution in case |
| 337 | * the server ip for the previous u-boot command, for example dns |
| 338 | * is not the same as the web server ip. |
| 339 | */ |
| 340 | |
| 341 | memset(net_server_ethaddr, 0, 6); |
| 342 | |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 343 | max_rx_pos = (u32)(-1); |
| 344 | net_boot_file_size = 0; |
| 345 | http_hdr_size = 0; |
| 346 | wget_tsize_num_hash = 0; |
| 347 | wget_loop_state = NETLOOP_FAIL; |
| 348 | |
| 349 | wget_info->status_code = HTTP_STATUS_BAD; |
| 350 | wget_info->file_size = 0; |
| 351 | wget_info->hdr_cont_len = 0; |
| 352 | if (wget_info->headers) |
| 353 | wget_info->headers[0] = 0; |
| 354 | |
Mikhail Kshevetskiy | 215f73f | 2024-12-28 13:46:30 +0300 | [diff] [blame] | 355 | server_port = env_get_ulong("httpdstp", 10, SERVER_PORT) & 0xffff; |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 356 | tcp_stream_set_on_create_handler(tcp_stream_on_create); |
Mikhail Kshevetskiy | 215f73f | 2024-12-28 13:46:30 +0300 | [diff] [blame] | 357 | tcp = tcp_stream_connect(web_server_ip, server_port); |
| 358 | if (!tcp) { |
Jerome Forissier | 95b1035 | 2025-04-17 15:26:58 +0200 | [diff] [blame] | 359 | if (!wget_info->silent) |
| 360 | printf("No free tcp streams\n"); |
Mikhail Kshevetskiy | 215f73f | 2024-12-28 13:46:30 +0300 | [diff] [blame] | 361 | net_set_state(NETLOOP_FAIL); |
| 362 | return; |
| 363 | } |
Mikhail Kshevetskiy | e2c4e9d | 2024-12-28 13:46:32 +0300 | [diff] [blame] | 364 | tcp_stream_put(tcp); |
Ying-Chun Liu (PaulLiu) | cc96a1d | 2022-11-08 14:17:29 +0800 | [diff] [blame] | 365 | } |
Masahisa Kojima | 6721d18 | 2023-11-10 13:25:35 +0900 | [diff] [blame] | 366 | |
Adriano Cordova | b479fc4 | 2024-12-04 00:05:16 -0300 | [diff] [blame] | 367 | int wget_do_request(ulong dst_addr, char *uri) |
Masahisa Kojima | 6721d18 | 2023-11-10 13:25:35 +0900 | [diff] [blame] | 368 | { |
| 369 | int ret; |
| 370 | char *s, *host_name, *file_name, *str_copy; |
| 371 | |
| 372 | /* |
| 373 | * Download file using wget. |
| 374 | * |
| 375 | * U-Boot wget takes the target uri in this format. |
| 376 | * "<http server ip>:<file path>" e.g.) 192.168.1.1:/sample/test.iso |
| 377 | * Need to resolve the http server ip address before starting wget. |
| 378 | */ |
| 379 | str_copy = strdup(uri); |
| 380 | if (!str_copy) |
| 381 | return -ENOMEM; |
| 382 | |
| 383 | s = str_copy + strlen("http://"); |
| 384 | host_name = strsep(&s, "/"); |
| 385 | if (!s) { |
Masahisa Kojima | 6721d18 | 2023-11-10 13:25:35 +0900 | [diff] [blame] | 386 | ret = -EINVAL; |
| 387 | goto out; |
| 388 | } |
| 389 | file_name = s; |
| 390 | |
Adriano Cordova | b479fc4 | 2024-12-04 00:05:16 -0300 | [diff] [blame] | 391 | host_name = strsep(&host_name, ":"); |
| 392 | |
| 393 | if (string_to_ip(host_name).s_addr) { |
| 394 | s = host_name; |
| 395 | } else { |
| 396 | #if IS_ENABLED(CONFIG_CMD_DNS) |
| 397 | net_dns_resolve = host_name; |
| 398 | net_dns_env_var = "httpserverip"; |
| 399 | if (net_loop(DNS) < 0) { |
| 400 | ret = -EINVAL; |
| 401 | goto out; |
| 402 | } |
| 403 | s = env_get("httpserverip"); |
| 404 | if (!s) { |
| 405 | ret = -EINVAL; |
| 406 | goto out; |
| 407 | } |
| 408 | #else |
Masahisa Kojima | 6721d18 | 2023-11-10 13:25:35 +0900 | [diff] [blame] | 409 | ret = -EINVAL; |
| 410 | goto out; |
Adriano Cordova | b479fc4 | 2024-12-04 00:05:16 -0300 | [diff] [blame] | 411 | #endif |
Masahisa Kojima | 6721d18 | 2023-11-10 13:25:35 +0900 | [diff] [blame] | 412 | } |
| 413 | |
| 414 | strlcpy(net_boot_file_name, s, sizeof(net_boot_file_name)); |
| 415 | strlcat(net_boot_file_name, ":/", sizeof(net_boot_file_name)); /* append '/' which is removed by strsep() */ |
| 416 | strlcat(net_boot_file_name, file_name, sizeof(net_boot_file_name)); |
| 417 | image_load_addr = dst_addr; |
| 418 | ret = net_loop(WGET); |
| 419 | |
| 420 | out: |
| 421 | free(str_copy); |
| 422 | |
Adriano Cordova | 450b234 | 2024-11-11 18:08:59 -0300 | [diff] [blame] | 423 | return ret < 0 ? ret : 0; |
Masahisa Kojima | 6721d18 | 2023-11-10 13:25:35 +0900 | [diff] [blame] | 424 | } |
Masahisa Kojima | e501ce1 | 2023-11-10 13:25:41 +0900 | [diff] [blame] | 425 | |
| 426 | /** |
| 427 | * wget_validate_uri() - validate the uri for wget |
| 428 | * |
| 429 | * @uri: uri string |
| 430 | * |
| 431 | * This function follows the current U-Boot wget implementation. |
| 432 | * scheme: only "http:" is supported |
| 433 | * authority: |
| 434 | * - user information: not supported |
| 435 | * - host: supported |
| 436 | * - port: not supported(always use the default port) |
| 437 | * |
| 438 | * Uri is expected to be correctly percent encoded. |
| 439 | * This is the minimum check, control codes(0x1-0x19, 0x7F, except '\0') |
| 440 | * and space character(0x20) are not allowed. |
| 441 | * |
| 442 | * TODO: stricter uri conformance check |
| 443 | * |
| 444 | * Return: true on success, false on failure |
| 445 | */ |
| 446 | bool wget_validate_uri(char *uri) |
| 447 | { |
| 448 | char c; |
| 449 | bool ret = true; |
| 450 | char *str_copy, *s, *authority; |
| 451 | |
| 452 | for (c = 0x1; c < 0x21; c++) { |
| 453 | if (strchr(uri, c)) { |
| 454 | log_err("invalid character is used\n"); |
| 455 | return false; |
| 456 | } |
| 457 | } |
| 458 | if (strchr(uri, 0x7f)) { |
| 459 | log_err("invalid character is used\n"); |
| 460 | return false; |
| 461 | } |
| 462 | |
| 463 | if (strncmp(uri, "http://", 7)) { |
| 464 | log_err("only http:// is supported\n"); |
| 465 | return false; |
| 466 | } |
| 467 | str_copy = strdup(uri); |
| 468 | if (!str_copy) |
| 469 | return false; |
| 470 | |
| 471 | s = str_copy + strlen("http://"); |
| 472 | authority = strsep(&s, "/"); |
| 473 | if (!s) { |
| 474 | log_err("invalid uri, no file path\n"); |
| 475 | ret = false; |
| 476 | goto out; |
| 477 | } |
| 478 | s = strchr(authority, '@'); |
| 479 | if (s) { |
| 480 | log_err("user information is not supported\n"); |
| 481 | ret = false; |
| 482 | goto out; |
| 483 | } |
| 484 | s = strchr(authority, ':'); |
| 485 | if (s) { |
| 486 | log_err("user defined port is not supported\n"); |
| 487 | ret = false; |
| 488 | goto out; |
| 489 | } |
| 490 | |
| 491 | out: |
| 492 | free(str_copy); |
| 493 | |
| 494 | return ret; |
| 495 | } |