]> git.ipfire.org Git - thirdparty/u-boot.git/blame - net/wget.c
Merge tag 'efi-2025-07-rc6' of https://source.denx.de/u-boot/custodians/u-boot-efi
[thirdparty/u-boot.git] / net / wget.c
CommitLineData
cfbae482
YCLP
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
04592adb 7#include <asm/global_data.h>
cfbae482 8#include <command.h>
fe1489bc 9#include <display_options.h>
cfbae482 10#include <env.h>
0ebbed66 11#include <efi_loader.h>
cfbae482 12#include <image.h>
04592adb 13#include <lmb.h>
cfbae482
YCLP
14#include <mapmem.h>
15#include <net.h>
16#include <net/tcp.h>
17#include <net/wget.h>
8cf18da1 18#include <stdlib.h>
cfbae482 19
04592adb
MK
20DECLARE_GLOBAL_DATA_PTR;
21
4caacb2f
MV
22/* The default, change with environment variable 'httpdstp' */
23#define SERVER_PORT 80
24
bf962de9 25#define HASHES_PER_LINE 65
cfbae482 26
bf962de9 27#define HTTP_MAX_HDR_LEN 2048
cfbae482 28
bf962de9
MK
29#define HTTP_STATUS_BAD 0
30#define HTTP_STATUS_OK 200
cfbae482 31
bf962de9
MK
32static const char http_proto[] = "HTTP/1.0";
33static const char http_eom[] = "\r\n\r\n";
34static const char content_len[] = "Content-Length:";
35static const char linefeed[] = "\r\n";
36static struct in_addr web_server_ip;
37static unsigned int server_port;
38static unsigned long content_length;
39static u32 http_hdr_size, max_rx_pos;
40static int wget_tsize_num_hash;
cfbae482
YCLP
41
42static char *image_url;
cfbae482
YCLP
43static enum net_loop_state wget_loop_state;
44
cfbae482
YCLP
45/**
46 * store_block() - store block in memory
47 * @src: source of data
48 * @offset: offset
49 * @len: length
50 */
51static inline int store_block(uchar *src, unsigned int offset, unsigned int len)
52{
04592adb 53 ulong store_addr = image_load_addr + offset;
cfbae482
YCLP
54 uchar *ptr;
55
32a6c5ea
AC
56 // Avoid overflow
57 if (wget_info->buffer_size && wget_info->buffer_size < offset + len)
58 return -1;
2dd076a9 59 if (CONFIG_IS_ENABLED(LMB) && wget_info->set_bootdev) {
04592adb 60 if (store_addr < image_load_addr ||
51ebd514 61 lmb_read_check(store_addr, len)) {
9349fc2e
JF
62 if (!wget_info->silent) {
63 printf("\nwget error: ");
64 printf("trying to overwrite reserved memory\n");
65 }
04592adb
MK
66 return -1;
67 }
68 }
69
70 ptr = map_sysmem(store_addr, len);
cfbae482
YCLP
71 memcpy(ptr, src, len);
72 unmap_sysmem(ptr);
73
cfbae482
YCLP
74 return 0;
75}
76
bf962de9 77static void show_block_marker(u32 packets)
cfbae482 78{
bf962de9 79 int cnt;
cfbae482 80
9349fc2e
JF
81 if (wget_info->silent)
82 return;
83
bf962de9
MK
84 if (content_length != -1) {
85 if (net_boot_file_size > content_length)
86 content_length = net_boot_file_size;
cfbae482 87
bf962de9
MK
88 cnt = net_boot_file_size * 50 / content_length;
89 while (wget_tsize_num_hash < cnt) {
90 putc('#');
91 wget_tsize_num_hash++;
92 }
93 } else {
94 if ((packets % 10) == 0)
95 putc('#');
96 else if (((packets + 1) % (10 * HASHES_PER_LINE)) == 0)
97 puts("\n");
cfbae482
YCLP
98 }
99}
100
bf962de9 101static void tcp_stream_on_closed(struct tcp_stream *tcp)
cfbae482 102{
bf962de9
MK
103 if (tcp->status != TCP_ERR_OK)
104 wget_loop_state = NETLOOP_FAIL;
105
106 net_set_state(wget_loop_state);
107 if (wget_loop_state != NETLOOP_SUCCESS) {
108 net_boot_file_size = 0;
9349fc2e
JF
109 if (!wget_info->silent)
110 printf("\nwget: Transfer Fail, TCP status - %d\n",
111 tcp->status);
bf962de9
MK
112 return;
113 }
cfbae482 114
9349fc2e
JF
115 if (!wget_info->silent)
116 printf("\nPackets received %d, Transfer Successful\n",
117 tcp->rx_packets);
bf962de9
MK
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 }
cfbae482
YCLP
125}
126
bf962de9 127static void tcp_stream_on_rcv_nxt_update(struct tcp_stream *tcp, u32 rx_bytes)
cfbae482 128{
bf962de9
MK
129 char *pos, *tail;
130 uchar saved, *ptr;
131 int reply_len;
cfbae482 132
bf962de9
MK
133 if (http_hdr_size) {
134 net_boot_file_size = rx_bytes - http_hdr_size;
135 show_block_marker(tcp->rx_packets);
136 return;
cfbae482 137 }
cfbae482 138
bf962de9 139 ptr = map_sysmem(image_load_addr, rx_bytes + 1);
cfbae482 140
bf962de9
MK
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;
150
9349fc2e
JF
151 if (!wget_info->silent)
152 printf("ERROR: misssed HTTP header\n");
bf962de9
MK
153 tcp_stream_close(tcp);
154 goto end;
737c2dca 155 }
2dd076a9 156
bf962de9
MK
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;
2dd076a9
AC
169 }
170
bf962de9
MK
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;
2dd076a9
AC
184 }
185
bf962de9
MK
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 }
2dd076a9 193
bf962de9
MK
194 debug_cond(DEBUG_WGET,
195 "wget: HTTP Status Code %d\n", wget_info->status_code);
196
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 }
2dd076a9 202
bf962de9
MK
203 debug_cond(DEBUG_WGET, "wget: Connctd pkt %p hlen %x\n",
204 ptr, http_hdr_size);
2dd076a9 205
bf962de9
MK
206 content_length = -1;
207 pos = strstr((char *)ptr, content_len);
2dd076a9 208 if (pos) {
bf962de9 209 pos += strlen(content_len) + 1;
2dd076a9
AC
210 while (*pos == ' ')
211 pos++;
bf962de9
MK
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) {
2dd076a9
AC
218 debug_cond(DEBUG_WGET,
219 "wget: Connected Len %lu\n",
220 content_length);
221 wget_info->hdr_cont_len = content_length;
32a6c5ea
AC
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
2dd076a9 227 }
2dd076a9 228
bf962de9
MK
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;
cfbae482 232
bf962de9
MK
233end:
234 unmap_sysmem(ptr);
235}
a8bd5ec0 236
bf962de9
MK
237static 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;
a8bd5ec0 241
32a6c5ea
AC
242 // Avoid overflow
243 if (store_block(buf, rx_offs - http_hdr_size, len) < 0)
244 return -1;
2dd076a9 245
bf962de9 246 return len;
cfbae482
YCLP
247}
248
bf962de9 249static int tcp_stream_tx(struct tcp_stream *tcp, u32 tx_offs, void *buf, int maxlen)
cfbae482 250{
bf962de9
MK
251 int ret;
252 const char *method;
cfbae482 253
bf962de9
MK
254 if (tx_offs)
255 return 0;
cfbae482 256
bf962de9
MK
257 switch (wget_info->method) {
258 case WGET_HTTP_METHOD_HEAD:
259 method = "HEAD";
cfbae482 260 break;
bf962de9
MK
261 case WGET_HTTP_METHOD_GET:
262 default:
263 method = "GET";
cfbae482 264 break;
bf962de9 265 }
cfbae482 266
bf962de9
MK
267 ret = snprintf(buf, maxlen, "%s %s %s\r\n\r\n",
268 method, image_url, http_proto);
cab7867c 269
bf962de9
MK
270 return ret;
271}
cfbae482 272
bf962de9
MK
273static 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;
cfbae482
YCLP
287}
288
cfbae482
YCLP
289#define BLOCKSIZE 512
290
291void wget_start(void)
292{
bf962de9 293 struct tcp_stream *tcp;
2b177498 294
2dd076a9
AC
295 if (!wget_info)
296 wget_info = &default_wget_info;
297
cfbae482
YCLP
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
cfbae482
YCLP
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
bf962de9
MK
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
2b177498 355 server_port = env_get_ulong("httpdstp", 10, SERVER_PORT) & 0xffff;
bf962de9 356 tcp_stream_set_on_create_handler(tcp_stream_on_create);
2b177498
MK
357 tcp = tcp_stream_connect(web_server_ip, server_port);
358 if (!tcp) {
9349fc2e
JF
359 if (!wget_info->silent)
360 printf("No free tcp streams\n");
2b177498
MK
361 net_set_state(NETLOOP_FAIL);
362 return;
363 }
bf962de9 364 tcp_stream_put(tcp);
cfbae482 365}
8cf18da1 366
9bab7d2a 367int wget_do_request(ulong dst_addr, char *uri)
8cf18da1
MK
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) {
8cf18da1
MK
386 ret = -EINVAL;
387 goto out;
388 }
389 file_name = s;
390
9bab7d2a
AC
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
8cf18da1
MK
409 ret = -EINVAL;
410 goto out;
9bab7d2a 411#endif
8cf18da1
MK
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
420out:
421 free(str_copy);
422
de28a2a5 423 return ret < 0 ? ret : 0;
8cf18da1 424}
f01c961e
MK
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 */
446bool 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
491out:
492 free(str_copy);
493
494 return ret;
495}