blob: 0b082c61947379f9818602cfb6bc33a20c32b3ec [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 Cordova47f35e32024-11-11 18:08:58 -030056 if (CONFIG_IS_ENABLED(LMB) && wget_info->set_bootdev) {
Masahisa Kojima77b0ae32023-11-10 13:25:34 +090057 if (store_addr < image_load_addr ||
Sughosh Ganu77728092024-09-16 20:50:25 +053058 lmb_read_check(store_addr, len)) {
Masahisa Kojima77b0ae32023-11-10 13:25:34 +090059 printf("\nwget error: ");
60 printf("trying to overwrite reserved memory...\n");
61 return -1;
62 }
63 }
64
65 ptr = map_sysmem(store_addr, len);
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080066 memcpy(ptr, src, len);
67 unmap_sysmem(ptr);
68
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080069 return 0;
70}
71
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030072static void show_block_marker(u32 packets)
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080073{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030074 int cnt;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080075
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030076 if (content_length != -1) {
77 if (net_boot_file_size > content_length)
78 content_length = net_boot_file_size;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080079
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030080 cnt = net_boot_file_size * 50 / content_length;
81 while (wget_tsize_num_hash < cnt) {
82 putc('#');
83 wget_tsize_num_hash++;
Adriano Cordova47f35e32024-11-11 18:08:58 -030084 }
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030085 } else {
86 if ((packets % 10) == 0)
87 putc('#');
88 else if (((packets + 1) % (10 * HASHES_PER_LINE)) == 0)
89 puts("\n");
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080090 }
91}
92
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030093static void tcp_stream_on_closed(struct tcp_stream *tcp)
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080094{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030095 if (tcp->status != TCP_ERR_OK)
96 wget_loop_state = NETLOOP_FAIL;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +080097
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +030098 net_set_state(wget_loop_state);
99 if (wget_loop_state != NETLOOP_SUCCESS) {
100 net_boot_file_size = 0;
101 if (wget_info->status_code == HTTP_STATUS_OK) {
102 wget_info->status_code = HTTP_STATUS_BAD;
103 wget_info->hdr_cont_len = 0;
104 if (wget_info->headers)
105 wget_info->headers[0] = 0;
106 }
107 printf("\nwget: Transfer Fail, TCP status - %d\n", tcp->status);
108 return;
109 }
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800110
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300111 printf("\nPackets received %d, Transfer Successful\n", tcp->rx_packets);
112 wget_info->file_size = net_boot_file_size;
113 if (wget_info->method == WGET_HTTP_METHOD_GET && wget_info->set_bootdev) {
114 efi_set_bootdev("Http", NULL, image_url,
115 map_sysmem(image_load_addr, 0),
116 net_boot_file_size);
117 env_set_hex("filesize", net_boot_file_size);
118 }
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800119}
120
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300121static void tcp_stream_on_rcv_nxt_update(struct tcp_stream *tcp, u32 rx_bytes)
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800122{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300123 char *pos, *tail;
124 uchar saved, *ptr;
125 int reply_len;
126
127 if (http_hdr_size) {
128 net_boot_file_size = rx_bytes - http_hdr_size;
129 show_block_marker(tcp->rx_packets);
130 return;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800131 }
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800132
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300133 ptr = map_sysmem(image_load_addr, rx_bytes + 1);
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800134
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300135 saved = ptr[rx_bytes];
136 ptr[rx_bytes] = '\0';
137 pos = strstr((char *)ptr, http_eom);
138 ptr[rx_bytes] = saved;
139
140 if (!pos) {
141 if (rx_bytes < HTTP_MAX_HDR_LEN &&
142 tcp->state == TCP_ESTABLISHED)
143 goto end;
Adriano Cordova47f35e32024-11-11 18:08:58 -0300144
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300145 printf("ERROR: misssed HTTP header\n");
146 tcp_stream_close(tcp);
147 goto end;
Heinrich Schuchardt89e40902024-11-26 13:19:20 -0300148 }
Adriano Cordova47f35e32024-11-11 18:08:58 -0300149
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300150 http_hdr_size = pos - (char *)ptr + strlen(http_eom);
151 *pos = '\0';
152
153 if (wget_info->headers && http_hdr_size < MAX_HTTP_HEADERS_SIZE)
154 strcpy(wget_info->headers, ptr);
155
156 /* check for HTTP proto */
157 if (strncasecmp((char *)ptr, "HTTP/", 5)) {
158 debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer "
159 "(no HTTP Status Line found)\n");
160 tcp_stream_close(tcp);
161 goto end;
Adriano Cordova47f35e32024-11-11 18:08:58 -0300162 }
163
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300164 /* get HTTP reply len */
165 pos = strstr((char *)ptr, linefeed);
166 if (pos)
167 reply_len = pos - (char *)ptr;
168 else
169 reply_len = http_hdr_size - strlen(http_eom);
170
171 pos = strchr((char *)ptr, ' ');
172 if (!pos || pos - (char *)ptr > reply_len) {
173 debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer "
174 "(no HTTP Status Code found)\n");
175 tcp_stream_close(tcp);
176 goto end;
Adriano Cordova47f35e32024-11-11 18:08:58 -0300177 }
178
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300179 wget_info->status_code = (u32)simple_strtoul(pos + 1, &tail, 10);
180 if (tail == pos + 1 || *tail != ' ') {
181 debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer "
182 "(bad HTTP Status Code)\n");
183 tcp_stream_close(tcp);
184 goto end;
185 }
Adriano Cordova47f35e32024-11-11 18:08:58 -0300186
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300187 debug_cond(DEBUG_WGET,
188 "wget: HTTP Status Code %d\n", wget_info->status_code);
Adriano Cordova47f35e32024-11-11 18:08:58 -0300189
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300190 if (wget_info->status_code != HTTP_STATUS_OK) {
191 debug_cond(DEBUG_WGET, "wget: Connected Bad Xfer\n");
192 tcp_stream_close(tcp);
193 goto end;
194 }
Adriano Cordova47f35e32024-11-11 18:08:58 -0300195
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300196 debug_cond(DEBUG_WGET, "wget: Connctd pkt %p hlen %x\n",
197 ptr, http_hdr_size);
198
199 content_length = -1;
200 pos = strstr((char *)ptr, content_len);
Adriano Cordova47f35e32024-11-11 18:08:58 -0300201 if (pos) {
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300202 pos += strlen(content_len) + 1;
Adriano Cordova47f35e32024-11-11 18:08:58 -0300203 while (*pos == ' ')
204 pos++;
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300205 content_length = simple_strtoul(pos, &tail, 10);
206 if (*tail != '\r' && *tail != '\n' && *tail != '\0')
207 content_length = -1;
208 }
209
210 if (content_length >= 0) {
Adriano Cordova47f35e32024-11-11 18:08:58 -0300211 debug_cond(DEBUG_WGET,
212 "wget: Connected Len %lu\n",
213 content_length);
214 wget_info->hdr_cont_len = content_length;
215 }
Adriano Cordova47f35e32024-11-11 18:08:58 -0300216
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300217 net_boot_file_size = rx_bytes - http_hdr_size;
218 memmove(ptr, ptr + http_hdr_size, max_rx_pos + 1 - http_hdr_size);
219 wget_loop_state = NETLOOP_SUCCESS;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800220
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300221end:
222 unmap_sysmem(ptr);
223}
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800224
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300225static int tcp_stream_rx(struct tcp_stream *tcp, u32 rx_offs, void *buf, int len)
226{
227 if ((max_rx_pos == (u32)(-1)) || (max_rx_pos < rx_offs + len - 1))
228 max_rx_pos = rx_offs + len - 1;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800229
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300230 store_block(buf, rx_offs - http_hdr_size, len);
Masahisa Kojima77b0ae32023-11-10 13:25:34 +0900231
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300232 return len;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800233}
234
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300235static 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 +0800236{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300237 int ret;
238 const char *method;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800239
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300240 if (tx_offs)
241 return 0;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800242
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300243 switch (wget_info->method) {
244 case WGET_HTTP_METHOD_HEAD:
245 method = "HEAD";
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800246 break;
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300247 case WGET_HTTP_METHOD_GET:
248 default:
249 method = "GET";
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800250 break;
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300251 }
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800252
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300253 ret = snprintf(buf, maxlen, "%s %s %s\r\n\r\n",
254 method, image_url, http_proto);
Yasuharu Shibata07a00ef2024-04-14 19:46:07 +0900255
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300256 return ret;
257}
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800258
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300259static int tcp_stream_on_create(struct tcp_stream *tcp)
260{
261 if (tcp->rhost.s_addr != web_server_ip.s_addr ||
262 tcp->rport != server_port)
263 return 0;
264
265 tcp->max_retry_count = WGET_RETRY_COUNT;
266 tcp->initial_timeout = WGET_TIMEOUT;
267 tcp->on_closed = tcp_stream_on_closed;
268 tcp->on_rcv_nxt_update = tcp_stream_on_rcv_nxt_update;
269 tcp->rx = tcp_stream_rx;
270 tcp->tx = tcp_stream_tx;
271
272 return 1;
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800273}
274
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800275#define BLOCKSIZE 512
276
277void wget_start(void)
278{
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300279 struct tcp_stream *tcp;
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300280
Adriano Cordova47f35e32024-11-11 18:08:58 -0300281 if (!wget_info)
282 wget_info = &default_wget_info;
283
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800284 image_url = strchr(net_boot_file_name, ':');
285 if (image_url > 0) {
286 web_server_ip = string_to_ip(net_boot_file_name);
287 ++image_url;
288 net_server_ip = web_server_ip;
289 } else {
290 web_server_ip = net_server_ip;
291 image_url = net_boot_file_name;
292 }
293
294 debug_cond(DEBUG_WGET,
295 "wget: Transfer HTTP Server %pI4; our IP %pI4\n",
296 &web_server_ip, &net_ip);
297
298 /* Check if we need to send across this subnet */
299 if (net_gateway.s_addr && net_netmask.s_addr) {
300 struct in_addr our_net;
301 struct in_addr server_net;
302
303 our_net.s_addr = net_ip.s_addr & net_netmask.s_addr;
304 server_net.s_addr = net_server_ip.s_addr & net_netmask.s_addr;
305 if (our_net.s_addr != server_net.s_addr)
306 debug_cond(DEBUG_WGET,
307 "wget: sending through gateway %pI4",
308 &net_gateway);
309 }
310 debug_cond(DEBUG_WGET, "URL '%s'\n", image_url);
311
312 if (net_boot_file_expected_size_in_blocks) {
313 debug_cond(DEBUG_WGET, "wget: Size is 0x%x Bytes = ",
314 net_boot_file_expected_size_in_blocks * BLOCKSIZE);
315 print_size(net_boot_file_expected_size_in_blocks * BLOCKSIZE,
316 "");
317 }
318 debug_cond(DEBUG_WGET,
319 "\nwget:Load address: 0x%lx\nLoading: *\b", image_load_addr);
320
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800321 /*
322 * Zero out server ether to force arp resolution in case
323 * the server ip for the previous u-boot command, for example dns
324 * is not the same as the web server ip.
325 */
326
327 memset(net_server_ethaddr, 0, 6);
328
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300329 max_rx_pos = (u32)(-1);
330 net_boot_file_size = 0;
331 http_hdr_size = 0;
332 wget_tsize_num_hash = 0;
333 wget_loop_state = NETLOOP_FAIL;
334
335 wget_info->status_code = HTTP_STATUS_BAD;
336 wget_info->file_size = 0;
337 wget_info->hdr_cont_len = 0;
338 if (wget_info->headers)
339 wget_info->headers[0] = 0;
340
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300341 server_port = env_get_ulong("httpdstp", 10, SERVER_PORT) & 0xffff;
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300342 tcp_stream_set_on_create_handler(tcp_stream_on_create);
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300343 tcp = tcp_stream_connect(web_server_ip, server_port);
344 if (!tcp) {
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300345 printf("No free tcp streams\n");
Mikhail Kshevetskiy215f73f2024-12-28 13:46:30 +0300346 net_set_state(NETLOOP_FAIL);
347 return;
348 }
Mikhail Kshevetskiye2c4e9d2024-12-28 13:46:32 +0300349 tcp_stream_put(tcp);
Ying-Chun Liu (PaulLiu)cc96a1d2022-11-08 14:17:29 +0800350}
Masahisa Kojima6721d182023-11-10 13:25:35 +0900351
Adriano Cordovab479fc42024-12-04 00:05:16 -0300352int wget_do_request(ulong dst_addr, char *uri)
Masahisa Kojima6721d182023-11-10 13:25:35 +0900353{
354 int ret;
355 char *s, *host_name, *file_name, *str_copy;
356
357 /*
358 * Download file using wget.
359 *
360 * U-Boot wget takes the target uri in this format.
361 * "<http server ip>:<file path>" e.g.) 192.168.1.1:/sample/test.iso
362 * Need to resolve the http server ip address before starting wget.
363 */
364 str_copy = strdup(uri);
365 if (!str_copy)
366 return -ENOMEM;
367
368 s = str_copy + strlen("http://");
369 host_name = strsep(&s, "/");
370 if (!s) {
Masahisa Kojima6721d182023-11-10 13:25:35 +0900371 ret = -EINVAL;
372 goto out;
373 }
374 file_name = s;
375
Adriano Cordovab479fc42024-12-04 00:05:16 -0300376 host_name = strsep(&host_name, ":");
377
378 if (string_to_ip(host_name).s_addr) {
379 s = host_name;
380 } else {
381#if IS_ENABLED(CONFIG_CMD_DNS)
382 net_dns_resolve = host_name;
383 net_dns_env_var = "httpserverip";
384 if (net_loop(DNS) < 0) {
385 ret = -EINVAL;
386 goto out;
387 }
388 s = env_get("httpserverip");
389 if (!s) {
390 ret = -EINVAL;
391 goto out;
392 }
393#else
Masahisa Kojima6721d182023-11-10 13:25:35 +0900394 ret = -EINVAL;
395 goto out;
Adriano Cordovab479fc42024-12-04 00:05:16 -0300396#endif
Masahisa Kojima6721d182023-11-10 13:25:35 +0900397 }
398
399 strlcpy(net_boot_file_name, s, sizeof(net_boot_file_name));
400 strlcat(net_boot_file_name, ":/", sizeof(net_boot_file_name)); /* append '/' which is removed by strsep() */
401 strlcat(net_boot_file_name, file_name, sizeof(net_boot_file_name));
402 image_load_addr = dst_addr;
403 ret = net_loop(WGET);
404
405out:
406 free(str_copy);
407
Adriano Cordova450b2342024-11-11 18:08:59 -0300408 return ret < 0 ? ret : 0;
Masahisa Kojima6721d182023-11-10 13:25:35 +0900409}
Masahisa Kojimae501ce12023-11-10 13:25:41 +0900410
411/**
412 * wget_validate_uri() - validate the uri for wget
413 *
414 * @uri: uri string
415 *
416 * This function follows the current U-Boot wget implementation.
417 * scheme: only "http:" is supported
418 * authority:
419 * - user information: not supported
420 * - host: supported
421 * - port: not supported(always use the default port)
422 *
423 * Uri is expected to be correctly percent encoded.
424 * This is the minimum check, control codes(0x1-0x19, 0x7F, except '\0')
425 * and space character(0x20) are not allowed.
426 *
427 * TODO: stricter uri conformance check
428 *
429 * Return: true on success, false on failure
430 */
431bool wget_validate_uri(char *uri)
432{
433 char c;
434 bool ret = true;
435 char *str_copy, *s, *authority;
436
437 for (c = 0x1; c < 0x21; c++) {
438 if (strchr(uri, c)) {
439 log_err("invalid character is used\n");
440 return false;
441 }
442 }
443 if (strchr(uri, 0x7f)) {
444 log_err("invalid character is used\n");
445 return false;
446 }
447
448 if (strncmp(uri, "http://", 7)) {
449 log_err("only http:// is supported\n");
450 return false;
451 }
452 str_copy = strdup(uri);
453 if (!str_copy)
454 return false;
455
456 s = str_copy + strlen("http://");
457 authority = strsep(&s, "/");
458 if (!s) {
459 log_err("invalid uri, no file path\n");
460 ret = false;
461 goto out;
462 }
463 s = strchr(authority, '@');
464 if (s) {
465 log_err("user information is not supported\n");
466 ret = false;
467 goto out;
468 }
469 s = strchr(authority, ':');
470 if (s) {
471 log_err("user defined port is not supported\n");
472 ret = false;
473 goto out;
474 }
475
476out:
477 free(str_copy);
478
479 return ret;
480}