]> Git Repo - J-u-boot.git/blob - net/wget.c
cmd: efidebug: add uri device path
[J-u-boot.git] / net / wget.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * WGET/HTTP support driver based on U-BOOT's nfs.c
4  * Copyright Duncan Hare <[email protected]> 2017
5  */
6
7 #include <asm/global_data.h>
8 #include <command.h>
9 #include <common.h>
10 #include <display_options.h>
11 #include <env.h>
12 #include <image.h>
13 #include <lmb.h>
14 #include <mapmem.h>
15 #include <net.h>
16 #include <net/tcp.h>
17 #include <net/wget.h>
18 #include <stdlib.h>
19
20 DECLARE_GLOBAL_DATA_PTR;
21
22 static const char bootfile1[] = "GET ";
23 static const char bootfile3[] = " HTTP/1.0\r\n\r\n";
24 static const char http_eom[] = "\r\n\r\n";
25 static const char http_ok[] = "200";
26 static const char content_len[] = "Content-Length";
27 static const char linefeed[] = "\r\n";
28 static struct in_addr web_server_ip;
29 static int our_port;
30 static int wget_timeout_count;
31
32 struct pkt_qd {
33         uchar *pkt;
34         unsigned int tcp_seq_num;
35         unsigned int len;
36 };
37
38 /*
39  * This is a control structure for out of order packets received.
40  * The actual packet bufers are in the kernel space, and are
41  * expected to be overwritten by the downloaded image.
42  */
43 #define PKTQ_SZ (PKTBUFSRX / 4)
44 static struct pkt_qd pkt_q[PKTQ_SZ];
45 static int pkt_q_idx;
46 static unsigned long content_length;
47 static unsigned int packets;
48
49 static unsigned int initial_data_seq_num;
50
51 static enum  wget_state current_wget_state;
52
53 static char *image_url;
54 static unsigned int wget_timeout = WGET_TIMEOUT;
55
56 static enum net_loop_state wget_loop_state;
57
58 /* Timeout retry parameters */
59 static u8 retry_action;                 /* actions for TCP retry */
60 static unsigned int retry_tcp_ack_num;  /* TCP retry acknowledge number*/
61 static unsigned int retry_tcp_seq_num;  /* TCP retry sequence number */
62 static int retry_len;                   /* TCP retry length */
63
64 static ulong wget_load_size;
65
66 /**
67  * wget_init_max_size() - initialize maximum load size
68  *
69  * Return:      0 if success, -1 if fails
70  */
71 static int wget_init_load_size(void)
72 {
73         struct lmb lmb;
74         phys_size_t max_size;
75
76         lmb_init_and_reserve(&lmb, gd->bd, (void *)gd->fdt_blob);
77
78         max_size = lmb_get_free_size(&lmb, image_load_addr);
79         if (!max_size)
80                 return -1;
81
82         wget_load_size = max_size;
83
84         return 0;
85 }
86
87 /**
88  * store_block() - store block in memory
89  * @src: source of data
90  * @offset: offset
91  * @len: length
92  */
93 static inline int store_block(uchar *src, unsigned int offset, unsigned int len)
94 {
95         ulong store_addr = image_load_addr + offset;
96         ulong newsize = offset + len;
97         uchar *ptr;
98
99         if (IS_ENABLED(CONFIG_LMB)) {
100                 ulong end_addr = image_load_addr + wget_load_size;
101
102                 if (!end_addr)
103                         end_addr = ULONG_MAX;
104
105                 if (store_addr < image_load_addr ||
106                     store_addr + len > end_addr) {
107                         printf("\nwget error: ");
108                         printf("trying to overwrite reserved memory...\n");
109                         return -1;
110                 }
111         }
112
113         ptr = map_sysmem(store_addr, len);
114         memcpy(ptr, src, len);
115         unmap_sysmem(ptr);
116
117         if (net_boot_file_size < (offset + len))
118                 net_boot_file_size = newsize;
119
120         return 0;
121 }
122
123 /**
124  * wget_send_stored() - wget response dispatcher
125  *
126  * WARNING, This, and only this, is the place in wget.c where
127  * SEQUENCE NUMBERS are swapped between incoming (RX)
128  * and outgoing (TX).
129  * Procedure wget_handler() is correct for RX traffic.
130  */
131 static void wget_send_stored(void)
132 {
133         u8 action = retry_action;
134         int len = retry_len;
135         unsigned int tcp_ack_num = retry_tcp_seq_num + (len == 0 ? 1 : len);
136         unsigned int tcp_seq_num = retry_tcp_ack_num;
137         uchar *ptr, *offset;
138
139         switch (current_wget_state) {
140         case WGET_CLOSED:
141                 debug_cond(DEBUG_WGET, "wget: send SYN\n");
142                 current_wget_state = WGET_CONNECTING;
143                 net_send_tcp_packet(0, SERVER_PORT, our_port, action,
144                                     tcp_seq_num, tcp_ack_num);
145                 packets = 0;
146                 break;
147         case WGET_CONNECTING:
148                 pkt_q_idx = 0;
149                 net_send_tcp_packet(0, SERVER_PORT, our_port, action,
150                                     tcp_seq_num, tcp_ack_num);
151
152                 ptr = net_tx_packet + net_eth_hdr_size() +
153                         IP_TCP_HDR_SIZE + TCP_TSOPT_SIZE + 2;
154                 offset = ptr;
155
156                 memcpy(offset, &bootfile1, strlen(bootfile1));
157                 offset += strlen(bootfile1);
158
159                 memcpy(offset, image_url, strlen(image_url));
160                 offset += strlen(image_url);
161
162                 memcpy(offset, &bootfile3, strlen(bootfile3));
163                 offset += strlen(bootfile3);
164                 net_send_tcp_packet((offset - ptr), SERVER_PORT, our_port,
165                                     TCP_PUSH, tcp_seq_num, tcp_ack_num);
166                 current_wget_state = WGET_CONNECTED;
167                 break;
168         case WGET_CONNECTED:
169         case WGET_TRANSFERRING:
170         case WGET_TRANSFERRED:
171                 net_send_tcp_packet(0, SERVER_PORT, our_port, action,
172                                     tcp_seq_num, tcp_ack_num);
173                 break;
174         }
175 }
176
177 static void wget_send(u8 action, unsigned int tcp_seq_num,
178                       unsigned int tcp_ack_num, int len)
179 {
180         retry_action = action;
181         retry_tcp_ack_num = tcp_ack_num;
182         retry_tcp_seq_num = tcp_seq_num;
183         retry_len = len;
184
185         wget_send_stored();
186 }
187
188 void wget_fail(char *error_message, unsigned int tcp_seq_num,
189                unsigned int tcp_ack_num, u8 action)
190 {
191         printf("wget: Transfer Fail - %s\n", error_message);
192         net_set_timeout_handler(0, NULL);
193         wget_send(action, tcp_seq_num, tcp_ack_num, 0);
194 }
195
196 void wget_success(u8 action, unsigned int tcp_seq_num,
197                   unsigned int tcp_ack_num, int len, int packets)
198 {
199         printf("Packets received %d, Transfer Successful\n", packets);
200         wget_send(action, tcp_seq_num, tcp_ack_num, len);
201 }
202
203 /*
204  * Interfaces of U-BOOT
205  */
206 static void wget_timeout_handler(void)
207 {
208         if (++wget_timeout_count > WGET_RETRY_COUNT) {
209                 puts("\nRetry count exceeded; starting again\n");
210                 wget_send(TCP_RST, 0, 0, 0);
211                 net_start_again();
212         } else {
213                 puts("T ");
214                 net_set_timeout_handler(wget_timeout +
215                                         WGET_TIMEOUT * wget_timeout_count,
216                                         wget_timeout_handler);
217                 wget_send_stored();
218         }
219 }
220
221 #define PKT_QUEUE_OFFSET 0x20000
222 #define PKT_QUEUE_PACKET_SIZE 0x800
223
224 static void wget_connected(uchar *pkt, unsigned int tcp_seq_num,
225                            u8 action, unsigned int tcp_ack_num, unsigned int len)
226 {
227         uchar *pkt_in_q;
228         char *pos;
229         int hlen, i;
230         uchar *ptr1;
231
232         pkt[len] = '\0';
233         pos = strstr((char *)pkt, http_eom);
234
235         if (!pos) {
236                 debug_cond(DEBUG_WGET,
237                            "wget: Connected, data before Header %p\n", pkt);
238                 pkt_in_q = (void *)image_load_addr + PKT_QUEUE_OFFSET +
239                         (pkt_q_idx * PKT_QUEUE_PACKET_SIZE);
240
241                 ptr1 = map_sysmem((phys_addr_t)pkt_in_q, len);
242                 memcpy(ptr1, pkt, len);
243                 unmap_sysmem(ptr1);
244
245                 pkt_q[pkt_q_idx].pkt = pkt_in_q;
246                 pkt_q[pkt_q_idx].tcp_seq_num = tcp_seq_num;
247                 pkt_q[pkt_q_idx].len = len;
248                 pkt_q_idx++;
249
250                 if (pkt_q_idx >= PKTQ_SZ) {
251                         printf("wget: Fatal error, queue overrun!\n");
252                         net_set_state(NETLOOP_FAIL);
253
254                         return;
255                 }
256         } else {
257                 debug_cond(DEBUG_WGET, "wget: Connected HTTP Header %p\n", pkt);
258                 /* sizeof(http_eom) - 1 is the string length of (http_eom) */
259                 hlen = pos - (char *)pkt + sizeof(http_eom) - 1;
260                 pos = strstr((char *)pkt, linefeed);
261                 if (pos > 0)
262                         i = pos - (char *)pkt;
263                 else
264                         i = hlen;
265                 printf("%.*s", i,  pkt);
266
267                 current_wget_state = WGET_TRANSFERRING;
268
269                 if (strstr((char *)pkt, http_ok) == 0) {
270                         debug_cond(DEBUG_WGET,
271                                    "wget: Connected Bad Xfer\n");
272                         initial_data_seq_num = tcp_seq_num + hlen;
273                         wget_loop_state = NETLOOP_FAIL;
274                         wget_send(action, tcp_seq_num, tcp_ack_num, len);
275                 } else {
276                         debug_cond(DEBUG_WGET,
277                                    "wget: Connctd pkt %p  hlen %x\n",
278                                    pkt, hlen);
279                         initial_data_seq_num = tcp_seq_num + hlen;
280
281                         pos = strstr((char *)pkt, content_len);
282                         if (!pos) {
283                                 content_length = -1;
284                         } else {
285                                 pos += sizeof(content_len) + 2;
286                                 strict_strtoul(pos, 10, &content_length);
287                                 debug_cond(DEBUG_WGET,
288                                            "wget: Connected Len %lu\n",
289                                            content_length);
290                         }
291
292                         net_boot_file_size = 0;
293
294                         if (len > hlen) {
295                                 if (store_block(pkt + hlen, 0, len - hlen) != 0) {
296                                         wget_loop_state = NETLOOP_FAIL;
297                                         wget_fail("wget: store error\n", tcp_seq_num, tcp_ack_num, action);
298                                         net_set_state(NETLOOP_FAIL);
299                                         return;
300                                 }
301                         }
302
303                         debug_cond(DEBUG_WGET,
304                                    "wget: Connected Pkt %p hlen %x\n",
305                                    pkt, hlen);
306
307                         for (i = 0; i < pkt_q_idx; i++) {
308                                 int err;
309
310                                 ptr1 = map_sysmem(
311                                         (phys_addr_t)(pkt_q[i].pkt),
312                                         pkt_q[i].len);
313                                 err = store_block(ptr1,
314                                           pkt_q[i].tcp_seq_num -
315                                           initial_data_seq_num,
316                                           pkt_q[i].len);
317                                 unmap_sysmem(ptr1);
318                                 debug_cond(DEBUG_WGET,
319                                            "wget: Connctd pkt Q %p len %x\n",
320                                            pkt_q[i].pkt, pkt_q[i].len);
321                                 if (err) {
322                                         wget_loop_state = NETLOOP_FAIL;
323                                         wget_fail("wget: store error\n", tcp_seq_num, tcp_ack_num, action);
324                                         net_set_state(NETLOOP_FAIL);
325                                         return;
326                                 }
327                         }
328                 }
329         }
330         wget_send(action, tcp_seq_num, tcp_ack_num, len);
331 }
332
333 /**
334  * wget_handler() - TCP handler of wget
335  * @pkt: pointer to the application packet
336  * @dport: destination TCP port
337  * @sip: source IP address
338  * @sport: source TCP port
339  * @tcp_seq_num: TCP sequential number
340  * @tcp_ack_num: TCP acknowledgment number
341  * @action: TCP action (SYN, ACK, FIN, etc)
342  * @len: packet length
343  *
344  * In the "application push" invocation, the TCP header with all
345  * its information is pointed to by the packet pointer.
346  */
347 static void wget_handler(uchar *pkt, u16 dport,
348                          struct in_addr sip, u16 sport,
349                          u32 tcp_seq_num, u32 tcp_ack_num,
350                          u8 action, unsigned int len)
351 {
352         enum tcp_state wget_tcp_state = tcp_get_tcp_state();
353
354         net_set_timeout_handler(wget_timeout, wget_timeout_handler);
355         packets++;
356
357         switch (current_wget_state) {
358         case WGET_CLOSED:
359                 debug_cond(DEBUG_WGET, "wget: Handler: Error!, State wrong\n");
360                 break;
361         case WGET_CONNECTING:
362                 debug_cond(DEBUG_WGET,
363                            "wget: Connecting In len=%x, Seq=%u, Ack=%u\n",
364                            len, tcp_seq_num, tcp_ack_num);
365                 if (!len) {
366                         if (wget_tcp_state == TCP_ESTABLISHED) {
367                                 debug_cond(DEBUG_WGET,
368                                            "wget: Cting, send, len=%x\n", len);
369                                 wget_send(action, tcp_seq_num, tcp_ack_num,
370                                           len);
371                         } else {
372                                 printf("%.*s", len,  pkt);
373                                 wget_fail("wget: Handler Connected Fail\n",
374                                           tcp_seq_num, tcp_ack_num, action);
375                         }
376                 }
377                 break;
378         case WGET_CONNECTED:
379                 debug_cond(DEBUG_WGET, "wget: Connected seq=%u, len=%x\n",
380                            tcp_seq_num, len);
381                 if (!len) {
382                         wget_fail("Image not found, no data returned\n",
383                                   tcp_seq_num, tcp_ack_num, action);
384                 } else {
385                         wget_connected(pkt, tcp_seq_num, action, tcp_ack_num, len);
386                 }
387                 break;
388         case WGET_TRANSFERRING:
389                 debug_cond(DEBUG_WGET,
390                            "wget: Transferring, seq=%x, ack=%x,len=%x\n",
391                            tcp_seq_num, tcp_ack_num, len);
392
393                 if (tcp_seq_num >= initial_data_seq_num &&
394                     store_block(pkt, tcp_seq_num - initial_data_seq_num,
395                                 len) != 0) {
396                         wget_fail("wget: store error\n",
397                                   tcp_seq_num, tcp_ack_num, action);
398                         net_set_state(NETLOOP_FAIL);
399                         return;
400                 }
401
402                 switch (wget_tcp_state) {
403                 case TCP_FIN_WAIT_2:
404                         wget_send(TCP_ACK, tcp_seq_num, tcp_ack_num, len);
405                         fallthrough;
406                 case TCP_SYN_SENT:
407                 case TCP_SYN_RECEIVED:
408                 case TCP_CLOSING:
409                 case TCP_FIN_WAIT_1:
410                 case TCP_CLOSED:
411                         net_set_state(NETLOOP_FAIL);
412                         break;
413                 case TCP_ESTABLISHED:
414                         wget_send(TCP_ACK, tcp_seq_num, tcp_ack_num,
415                                   len);
416                         wget_loop_state = NETLOOP_SUCCESS;
417                         break;
418                 case TCP_CLOSE_WAIT:     /* End of transfer */
419                         current_wget_state = WGET_TRANSFERRED;
420                         wget_send(action | TCP_ACK | TCP_FIN,
421                                   tcp_seq_num, tcp_ack_num, len);
422                         break;
423                 }
424                 break;
425         case WGET_TRANSFERRED:
426                 printf("Packets received %d, Transfer Successful\n", packets);
427                 net_set_state(wget_loop_state);
428                 break;
429         }
430 }
431
432 #define RANDOM_PORT_START 1024
433 #define RANDOM_PORT_RANGE 0x4000
434
435 /**
436  * random_port() - make port a little random (1024-17407)
437  *
438  * Return: random port number from 1024 to 17407
439  *
440  * This keeps the math somewhat trivial to compute, and seems to work with
441  * all supported protocols/clients/servers
442  */
443 static unsigned int random_port(void)
444 {
445         return RANDOM_PORT_START + (get_timer(0) % RANDOM_PORT_RANGE);
446 }
447
448 #define BLOCKSIZE 512
449
450 void wget_start(void)
451 {
452         image_url = strchr(net_boot_file_name, ':');
453         if (image_url > 0) {
454                 web_server_ip = string_to_ip(net_boot_file_name);
455                 ++image_url;
456                 net_server_ip = web_server_ip;
457         } else {
458                 web_server_ip = net_server_ip;
459                 image_url = net_boot_file_name;
460         }
461
462         debug_cond(DEBUG_WGET,
463                    "wget: Transfer HTTP Server %pI4; our IP %pI4\n",
464                    &web_server_ip, &net_ip);
465
466         /* Check if we need to send across this subnet */
467         if (net_gateway.s_addr && net_netmask.s_addr) {
468                 struct in_addr our_net;
469                 struct in_addr server_net;
470
471                 our_net.s_addr = net_ip.s_addr & net_netmask.s_addr;
472                 server_net.s_addr = net_server_ip.s_addr & net_netmask.s_addr;
473                 if (our_net.s_addr != server_net.s_addr)
474                         debug_cond(DEBUG_WGET,
475                                    "wget: sending through gateway %pI4",
476                                    &net_gateway);
477         }
478         debug_cond(DEBUG_WGET, "URL '%s'\n", image_url);
479
480         if (net_boot_file_expected_size_in_blocks) {
481                 debug_cond(DEBUG_WGET, "wget: Size is 0x%x Bytes = ",
482                            net_boot_file_expected_size_in_blocks * BLOCKSIZE);
483                 print_size(net_boot_file_expected_size_in_blocks * BLOCKSIZE,
484                            "");
485         }
486         debug_cond(DEBUG_WGET,
487                    "\nwget:Load address: 0x%lx\nLoading: *\b", image_load_addr);
488
489         if (IS_ENABLED(CONFIG_LMB)) {
490                 if (wget_init_load_size()) {
491                         printf("\nwget error: ");
492                         printf("trying to overwrite reserved memory...\n");
493                         net_set_state(NETLOOP_FAIL);
494                         return;
495                 }
496         }
497
498         net_set_timeout_handler(wget_timeout, wget_timeout_handler);
499         tcp_set_tcp_handler(wget_handler);
500
501         wget_timeout_count = 0;
502         current_wget_state = WGET_CLOSED;
503
504         our_port = random_port();
505
506         /*
507          * Zero out server ether to force arp resolution in case
508          * the server ip for the previous u-boot command, for example dns
509          * is not the same as the web server ip.
510          */
511
512         memset(net_server_ethaddr, 0, 6);
513
514         wget_send(TCP_SYN, 0, 0, 0);
515 }
516
517 #if (IS_ENABLED(CONFIG_CMD_DNS))
518 int wget_with_dns(ulong dst_addr, char *uri)
519 {
520         int ret;
521         char *s, *host_name, *file_name, *str_copy;
522
523         /*
524          * Download file using wget.
525          *
526          * U-Boot wget takes the target uri in this format.
527          *  "<http server ip>:<file path>"  e.g.) 192.168.1.1:/sample/test.iso
528          * Need to resolve the http server ip address before starting wget.
529          */
530         str_copy = strdup(uri);
531         if (!str_copy)
532                 return -ENOMEM;
533
534         s = str_copy + strlen("http://");
535         host_name = strsep(&s, "/");
536         if (!s) {
537                 log_err("Error: invalied uri, no file path\n");
538                 ret = -EINVAL;
539                 goto out;
540         }
541         file_name = s;
542
543         /* TODO: If the given uri has ip address for the http server, skip dns */
544         net_dns_resolve = host_name;
545         net_dns_env_var = "httpserverip";
546         if (net_loop(DNS) < 0) {
547                 log_err("Error: dns lookup of %s failed, check setup\n", net_dns_resolve);
548                 ret = -EINVAL;
549                 goto out;
550         }
551         s = env_get("httpserverip");
552         if (!s) {
553                 ret = -EINVAL;
554                 goto out;
555         }
556
557         strlcpy(net_boot_file_name, s, sizeof(net_boot_file_name));
558         strlcat(net_boot_file_name, ":/", sizeof(net_boot_file_name)); /* append '/' which is removed by strsep() */
559         strlcat(net_boot_file_name, file_name, sizeof(net_boot_file_name));
560         image_load_addr = dst_addr;
561         ret = net_loop(WGET);
562
563 out:
564         free(str_copy);
565
566         return ret;
567 }
568 #endif
569
570 /**
571  * wget_validate_uri() - validate the uri for wget
572  *
573  * @uri:        uri string
574  *
575  * This function follows the current U-Boot wget implementation.
576  * scheme: only "http:" is supported
577  * authority:
578  *   - user information: not supported
579  *   - host: supported
580  *   - port: not supported(always use the default port)
581  *
582  * Uri is expected to be correctly percent encoded.
583  * This is the minimum check, control codes(0x1-0x19, 0x7F, except '\0')
584  * and space character(0x20) are not allowed.
585  *
586  * TODO: stricter uri conformance check
587  *
588  * Return:      true on success, false on failure
589  */
590 bool wget_validate_uri(char *uri)
591 {
592         char c;
593         bool ret = true;
594         char *str_copy, *s, *authority;
595
596         for (c = 0x1; c < 0x21; c++) {
597                 if (strchr(uri, c)) {
598                         log_err("invalid character is used\n");
599                         return false;
600                 }
601         }
602         if (strchr(uri, 0x7f)) {
603                 log_err("invalid character is used\n");
604                 return false;
605         }
606
607         if (strncmp(uri, "http://", 7)) {
608                 log_err("only http:// is supported\n");
609                 return false;
610         }
611         str_copy = strdup(uri);
612         if (!str_copy)
613                 return false;
614
615         s = str_copy + strlen("http://");
616         authority = strsep(&s, "/");
617         if (!s) {
618                 log_err("invalid uri, no file path\n");
619                 ret = false;
620                 goto out;
621         }
622         s = strchr(authority, '@');
623         if (s) {
624                 log_err("user information is not supported\n");
625                 ret = false;
626                 goto out;
627         }
628         s = strchr(authority, ':');
629         if (s) {
630                 log_err("user defined port is not supported\n");
631                 ret = false;
632                 goto out;
633         }
634
635 out:
636         free(str_copy);
637
638         return ret;
639 }
This page took 0.062336 seconds and 4 git commands to generate.