blob: c73836cbc9d64e643509e0faf1375c983122d277 [file] [log] [blame]
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +08001// 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 Kojima77b0ae32023-11-10 13:25:34 +09007#include <asm/global_data.h>
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +08008#include <command.h>
Michael Wallecaad55b2022-12-28 16:27:14 +01009#include <display_options.h>
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080010#include <env.h>
Jerome Forissier612b2b12024-09-11 11:58:22 +020011#include <efi_loader.h>
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080012#include <image.h>
Masahisa Kojima77b0ae32023-11-10 13:25:34 +090013#include <lmb.h>
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080014#include <mapmem.h>
15#include <net.h>
16#include <net/tcp.h>
17#include <net/wget.h>
Masahisa Kojima6721d182023-11-10 13:25:35 +090018#include <stdlib.h>
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080019
Masahisa Kojima77b0ae32023-11-10 13:25:34 +090020DECLARE_GLOBAL_DATA_PTR;
21
Marek Vasut22a95082023-12-13 22:11:13 +010022/* The default, change with environment variable 'httpdstp' */
23#define SERVER_PORT 80
24
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030025#define HASHES_PER_LINE 65
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080026
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030027#define HTTP_MAX_HDR_LEN 2048
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080028
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030029#define HTTP_STATUS_BAD 0
30#define HTTP_STATUS_OK 200
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080031
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030032static 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;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080041
42static char *image_url;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080043static enum net_loop_state wget_loop_state;
44
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080045/**
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{
Masahisa Kojima77b0ae32023-11-10 13:25:34 +090053 ulong store_addr = image_load_addr + offset;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080054 uchar *ptr;
55
Adriano Cordova3f3409c2025-02-06 14:40:11 -030056 // Avoid overflow
57 if (wget_info->buffer_size && wget_info->buffer_size < offset + len)
58 return -1;
Adriano Cordova47f35e32024-11-11 18:08:58 -030059 if (CONFIG_IS_ENABLED(LMB) && wget_info->set_bootdev) {
Masahisa Kojima77b0ae32023-11-10 13:25:34 +090060 if (store_addr < image_load_addr ||
Sughosh Ganu77728092024-09-16 20:50:25 +053061 lmb_read_check(store_addr, len)) {
Masahisa Kojima77b0ae32023-11-10 13:25:34 +090062 printf("\nwget error: ");
63 printf("trying to overwrite reserved memory...\n");
64 return -1;
65 }
66 }
67
68 ptr = map_sysmem(store_addr, len);
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080069 memcpy(ptr, src, len);
70 unmap_sysmem(ptr);
71
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080072 return 0;
73}
74
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030075static void show_block_marker(u32 packets)
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080076{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030077 int cnt;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080078
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030079 if (content_length != -1) {
80 if (net_boot_file_size > content_length)
81 content_length = net_boot_file_size;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080082
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030083 cnt = net_boot_file_size * 50 / content_length;
84 while (wget_tsize_num_hash < cnt) {
85 putc('#');
86 wget_tsize_num_hash++;
Adriano Cordova47f35e32024-11-11 18:08:58 -030087 }
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030088 } else {
89 if ((packets % 10) == 0)
90 putc('#');
91 else if (((packets + 1) % (10 * HASHES_PER_LINE)) == 0)
92 puts("\n");
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080093 }
94}
95
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030096static void tcp_stream_on_closed(struct tcp_stream *tcp)
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080097{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030098 if (tcp->status != TCP_ERR_OK)
99 wget_loop_state = NETLOOP_FAIL;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800100
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300101 net_set_state(wget_loop_state);
102 if (wget_loop_state != NETLOOP_SUCCESS) {
103 net_boot_file_size = 0;
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300104 printf("\nwget: Transfer Fail, TCP status - %d\n", tcp->status);
105 return;
106 }
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800107
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300108 printf("\nPackets received %d, Transfer Successful\n", tcp->rx_packets);
109 wget_info->file_size = net_boot_file_size;
110 if (wget_info->method == WGET_HTTP_METHOD_GET && wget_info->set_bootdev) {
111 efi_set_bootdev("Http", NULL, image_url,
112 map_sysmem(image_load_addr, 0),
113 net_boot_file_size);
114 env_set_hex("filesize", net_boot_file_size);
115 }
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800116}
117
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300118static void tcp_stream_on_rcv_nxt_update(struct tcp_stream *tcp, u32 rx_bytes)
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800119{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300120 char *pos, *tail;
121 uchar saved, *ptr;
122 int reply_len;
123
124 if (http_hdr_size) {
125 net_boot_file_size = rx_bytes - http_hdr_size;
126 show_block_marker(tcp->rx_packets);
127 return;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800128 }
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800129
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300130 ptr = map_sysmem(image_load_addr, rx_bytes + 1);
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800131
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300132 saved = ptr[rx_bytes];
133 ptr[rx_bytes] = '\0';
134 pos = strstr((char *)ptr, http_eom);
135 ptr[rx_bytes] = saved;
136
137 if (!pos) {
138 if (rx_bytes < HTTP_MAX_HDR_LEN &&
139 tcp->state == TCP_ESTABLISHED)
140 goto end;
Adriano Cordova47f35e32024-11-11 18:08:58 -0300141
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300142 printf("ERROR: misssed HTTP header\n");
143 tcp_stream_close(tcp);
144 goto end;
Heinrich Schuchardt89e40902024-11-26 13:19:20 -0300145 }
Adriano Cordova47f35e32024-11-11 18:08:58 -0300146
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300147 http_hdr_size = pos - (char *)ptr + strlen(http_eom);
148 *pos = '\0';
149
150 if (wget_info->headers && http_hdr_size < MAX_HTTP_HEADERS_SIZE)
151 strcpy(wget_info->headers, ptr);
152
153 /* check for HTTP proto */
154 if (strncasecmp((char *)ptr, "HTTP/", 5)) {
155 debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer "
156 "(no HTTP Status Line found)\n");
157 tcp_stream_close(tcp);
158 goto end;
Adriano Cordova47f35e32024-11-11 18:08:58 -0300159 }
160
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300161 /* get HTTP reply len */
162 pos = strstr((char *)ptr, linefeed);
163 if (pos)
164 reply_len = pos - (char *)ptr;
165 else
166 reply_len = http_hdr_size - strlen(http_eom);
167
168 pos = strchr((char *)ptr, ' ');
169 if (!pos || pos - (char *)ptr > reply_len) {
170 debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer "
171 "(no HTTP Status Code found)\n");
172 tcp_stream_close(tcp);
173 goto end;
Adriano Cordova47f35e32024-11-11 18:08:58 -0300174 }
175
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300176 wget_info->status_code = (u32)simple_strtoul(pos + 1, &tail, 10);
177 if (tail == pos + 1 || *tail != ' ') {
178 debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer "
179 "(bad HTTP Status Code)\n");
180 tcp_stream_close(tcp);
181 goto end;
182 }
Adriano Cordova47f35e32024-11-11 18:08:58 -0300183
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300184 debug_cond(DEBUG_WGET,
185 "wget: HTTP Status Code %d\n", wget_info->status_code);
Adriano Cordova47f35e32024-11-11 18:08:58 -0300186
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300187 if (wget_info->status_code != HTTP_STATUS_OK) {
188 debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer\n");
189 tcp_stream_close(tcp);
190 goto end;
191 }
Adriano Cordova47f35e32024-11-11 18:08:58 -0300192
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300193 debug_cond(DEBUG_WGET, "wget: Connctd pkt %p hlen %x\n",
194 ptr, http_hdr_size);
195
196 content_length = -1;
197 pos = strstr((char *)ptr, content_len);
Adriano Cordova47f35e32024-11-11 18:08:58 -0300198 if (pos) {
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300199 pos += strlen(content_len) + 1;
Adriano Cordova47f35e32024-11-11 18:08:58 -0300200 while (*pos == ' ')
201 pos++;
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300202 content_length = simple_strtoul(pos, &tail, 10);
203 if (*tail != '\r' && *tail != '\n' && *tail != '\0')
204 content_length = -1;
205 }
206
207 if (content_length >= 0) {
Adriano Cordova47f35e32024-11-11 18:08:58 -0300208 debug_cond(DEBUG_WGET,
209 "wget: Connected Len %lu\n",
210 content_length);
211 wget_info->hdr_cont_len = content_length;
Adriano Cordova3f3409c2025-02-06 14:40:11 -0300212 if (wget_info->buffer_size && wget_info->buffer_size < wget_info->hdr_cont_len){
213 tcp_stream_reset(tcp);
214 goto end;
215 }
216
Adriano Cordova47f35e32024-11-11 18:08:58 -0300217 }
Adriano Cordova47f35e32024-11-11 18:08:58 -0300218
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300219 net_boot_file_size = rx_bytes - http_hdr_size;
220 memmove(ptr, ptr + http_hdr_size, max_rx_pos + 1 - http_hdr_size);
221 wget_loop_state = NETLOOP_SUCCESS;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800222
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300223end:
224 unmap_sysmem(ptr);
225}
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800226
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300227static int tcp_stream_rx(struct tcp_stream *tcp, u32 rx_offs, void *buf, int len)
228{
229 if ((max_rx_pos == (u32)(-1)) || (max_rx_pos < rx_offs + len - 1))
230 max_rx_pos = rx_offs + len - 1;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800231
Adriano Cordova3f3409c2025-02-06 14:40:11 -0300232 // Avoid overflow
233 if (store_block(buf, rx_offs - http_hdr_size, len) < 0)
234 return -1;
Masahisa Kojima77b0ae32023-11-10 13:25:34 +0900235
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300236 return len;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800237}
238
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300239static int tcp_stream_tx(struct tcp_stream *tcp, u32 tx_offs, void *buf, int maxlen)
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800240{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300241 int ret;
242 const char *method;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800243
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300244 if (tx_offs)
245 return 0;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800246
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300247 switch (wget_info->method) {
248 case WGET_HTTP_METHOD_HEAD:
249 method = "HEAD";
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800250 break;
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300251 case WGET_HTTP_METHOD_GET:
252 default:
253 method = "GET";
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800254 break;
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300255 }
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800256
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300257 ret = snprintf(buf, maxlen, "%s %s %s\r\n\r\n",
258 method, image_url, http_proto);
Yasuharu Shibata07a00ef2024-04-14 19:46:07 +0900259
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300260 return ret;
261}
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800262
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300263static int tcp_stream_on_create(struct tcp_stream *tcp)
264{
265 if (tcp->rhost.s_addr != web_server_ip.s_addr ||
266 tcp->rport != server_port)
267 return 0;
268
269 tcp->max_retry_count = WGET_RETRY_COUNT;
270 tcp->initial_timeout = WGET_TIMEOUT;
271 tcp->on_closed = tcp_stream_on_closed;
272 tcp->on_rcv_nxt_update = tcp_stream_on_rcv_nxt_update;
273 tcp->rx = tcp_stream_rx;
274 tcp->tx = tcp_stream_tx;
275
276 return 1;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800277}
278
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800279#define BLOCKSIZE 512
280
281void wget_start(void)
282{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300283 struct tcp_stream *tcp;
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300284
Adriano Cordova47f35e32024-11-11 18:08:58 -0300285 if (!wget_info)
286 wget_info = &default_wget_info;
287
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800288 image_url = strchr(net_boot_file_name, ':');
289 if (image_url > 0) {
290 web_server_ip = string_to_ip(net_boot_file_name);
291 ++image_url;
292 net_server_ip = web_server_ip;
293 } else {
294 web_server_ip = net_server_ip;
295 image_url = net_boot_file_name;
296 }
297
298 debug_cond(DEBUG_WGET,
299 "wget: Transfer HTTP Server %pI4; our IP %pI4\n",
300 &web_server_ip, &net_ip);
301
302 /* Check if we need to send across this subnet */
303 if (net_gateway.s_addr && net_netmask.s_addr) {
304 struct in_addr our_net;
305 struct in_addr server_net;
306
307 our_net.s_addr = net_ip.s_addr & net_netmask.s_addr;
308 server_net.s_addr = net_server_ip.s_addr & net_netmask.s_addr;
309 if (our_net.s_addr != server_net.s_addr)
310 debug_cond(DEBUG_WGET,
311 "wget: sending through gateway %pI4",
312 &net_gateway);
313 }
314 debug_cond(DEBUG_WGET, "URL '%s'\n", image_url);
315
316 if (net_boot_file_expected_size_in_blocks) {
317 debug_cond(DEBUG_WGET, "wget: Size is 0x%x Bytes = ",
318 net_boot_file_expected_size_in_blocks * BLOCKSIZE);
319 print_size(net_boot_file_expected_size_in_blocks * BLOCKSIZE,
320 "");
321 }
322 debug_cond(DEBUG_WGET,
323 "\nwget:Load address: 0x%lx\nLoading: *\b", image_load_addr);
324
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800325 /*
326 * Zero out server ether to force arp resolution in case
327 * the server ip for the previous u-boot command, for example dns
328 * is not the same as the web server ip.
329 */
330
331 memset(net_server_ethaddr, 0, 6);
332
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300333 max_rx_pos = (u32)(-1);
334 net_boot_file_size = 0;
335 http_hdr_size = 0;
336 wget_tsize_num_hash = 0;
337 wget_loop_state = NETLOOP_FAIL;
338
339 wget_info->status_code = HTTP_STATUS_BAD;
340 wget_info->file_size = 0;
341 wget_info->hdr_cont_len = 0;
342 if (wget_info->headers)
343 wget_info->headers[0] = 0;
344
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300345 server_port = env_get_ulong("httpdstp", 10, SERVER_PORT) & 0xffff;
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300346 tcp_stream_set_on_create_handler(tcp_stream_on_create);
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300347 tcp = tcp_stream_connect(web_server_ip, server_port);
348 if (!tcp) {
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300349 printf("No free tcp streams\n");
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300350 net_set_state(NETLOOP_FAIL);
351 return;
352 }
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300353 tcp_stream_put(tcp);
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800354}
Masahisa Kojima6721d182023-11-10 13:25:35 +0900355
Adriano Cordovab479fc42024-12-04 00:05:16 -0300356int wget_do_request(ulong dst_addr, char *uri)
Masahisa Kojima6721d182023-11-10 13:25:35 +0900357{
358 int ret;
359 char *s, *host_name, *file_name, *str_copy;
360
361 /*
362 * Download file using wget.
363 *
364 * U-Boot wget takes the target uri in this format.
365 * "<http server ip>:<file path>" e.g.) 192.168.1.1:/sample/test.iso
366 * Need to resolve the http server ip address before starting wget.
367 */
368 str_copy = strdup(uri);
369 if (!str_copy)
370 return -ENOMEM;
371
372 s = str_copy + strlen("http://");
373 host_name = strsep(&s, "/");
374 if (!s) {
Masahisa Kojima6721d182023-11-10 13:25:35 +0900375 ret = -EINVAL;
376 goto out;
377 }
378 file_name = s;
379
Adriano Cordovab479fc42024-12-04 00:05:16 -0300380 host_name = strsep(&host_name, ":");
381
382 if (string_to_ip(host_name).s_addr) {
383 s = host_name;
384 } else {
385#if IS_ENABLED(CONFIG_CMD_DNS)
386 net_dns_resolve = host_name;
387 net_dns_env_var = "httpserverip";
388 if (net_loop(DNS) < 0) {
389 ret = -EINVAL;
390 goto out;
391 }
392 s = env_get("httpserverip");
393 if (!s) {
394 ret = -EINVAL;
395 goto out;
396 }
397#else
Masahisa Kojima6721d182023-11-10 13:25:35 +0900398 ret = -EINVAL;
399 goto out;
Adriano Cordovab479fc42024-12-04 00:05:16 -0300400#endif
Masahisa Kojima6721d182023-11-10 13:25:35 +0900401 }
402
403 strlcpy(net_boot_file_name, s, sizeof(net_boot_file_name));
404 strlcat(net_boot_file_name, ":/", sizeof(net_boot_file_name)); /* append '/' which is removed by strsep() */
405 strlcat(net_boot_file_name, file_name, sizeof(net_boot_file_name));
406 image_load_addr = dst_addr;
407 ret = net_loop(WGET);
408
409out:
410 free(str_copy);
411
Adriano Cordova450b2342024-11-11 18:08:59 -0300412 return ret < 0 ? ret : 0;
Masahisa Kojima6721d182023-11-10 13:25:35 +0900413}
Masahisa Kojimae501ce12023-11-10 13:25:41 +0900414
415/**
416 * wget_validate_uri() - validate the uri for wget
417 *
418 * @uri: uri string
419 *
420 * This function follows the current U-Boot wget implementation.
421 * scheme: only "http:" is supported
422 * authority:
423 * - user information: not supported
424 * - host: supported
425 * - port: not supported(always use the default port)
426 *
427 * Uri is expected to be correctly percent encoded.
428 * This is the minimum check, control codes(0x1-0x19, 0x7F, except '\0')
429 * and space character(0x20) are not allowed.
430 *
431 * TODO: stricter uri conformance check
432 *
433 * Return: true on success, false on failure
434 */
435bool wget_validate_uri(char *uri)
436{
437 char c;
438 bool ret = true;
439 char *str_copy, *s, *authority;
440
441 for (c = 0x1; c < 0x21; c++) {
442 if (strchr(uri, c)) {
443 log_err("invalid character is used\n");
444 return false;
445 }
446 }
447 if (strchr(uri, 0x7f)) {
448 log_err("invalid character is used\n");
449 return false;
450 }
451
452 if (strncmp(uri, "http://", 7)) {
453 log_err("only http:// is supported\n");
454 return false;
455 }
456 str_copy = strdup(uri);
457 if (!str_copy)
458 return false;
459
460 s = str_copy + strlen("http://");
461 authority = strsep(&s, "/");
462 if (!s) {
463 log_err("invalid uri, no file path\n");
464 ret = false;
465 goto out;
466 }
467 s = strchr(authority, '@');
468 if (s) {
469 log_err("user information is not supported\n");
470 ret = false;
471 goto out;
472 }
473 s = strchr(authority, ':');
474 if (s) {
475 log_err("user defined port is not supported\n");
476 ret = false;
477 goto out;
478 }
479
480out:
481 free(str_copy);
482
483 return ret;
484}