2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2010,2011 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/misc.h>
20 #include <grub/net/tcp.h>
21 #include <grub/net/ip.h>
22 #include <grub/net/ethernet.h>
23 #include <grub/net/netbuff.h>
27 #include <grub/file.h>
28 #include <grub/i18n.h>
30 GRUB_MOD_LICENSE ("GPLv3+");
38 typedef struct http_data
41 grub_size_t current_line_len
;
45 grub_net_tcp_socket_t sock
;
50 grub_size_t chunk_rem
;
55 have_ahead (struct grub_file
*file
)
57 grub_net_t net
= file
->device
->net
;
58 grub_off_t ret
= net
->offset
;
59 struct grub_net_packet
*pack
;
60 for (pack
= net
->packs
.first
; pack
; pack
= pack
->next
)
61 ret
+= pack
->nb
->tail
- pack
->nb
->data
;
66 parse_line (grub_file_t file
, http_data_t data
, char *ptr
, grub_size_t len
)
68 char *end
= ptr
+ len
;
69 while (end
> ptr
&& *(end
- 1) == '\r')
73 if (data
->in_chunk_len
== 1)
75 data
->in_chunk_len
= 2;
78 if (data
->in_chunk_len
== 2)
80 data
->chunk_rem
= grub_strtoul (ptr
, 0, 16);
81 grub_errno
= GRUB_ERR_NONE
;
82 if (data
->chunk_rem
== 0)
84 file
->device
->net
->eof
= 1;
85 file
->device
->net
->stall
= 1;
86 if (file
->size
== GRUB_FILE_SIZE_UNKNOWN
)
87 file
->size
= have_ahead (file
);
89 data
->in_chunk_len
= 0;
94 data
->headers_recv
= 1;
96 data
->in_chunk_len
= 2;
100 if (!data
->first_line_recv
)
103 if (grub_memcmp (ptr
, "HTTP/1.1 ", sizeof ("HTTP/1.1 ") - 1) != 0)
105 data
->errmsg
= grub_strdup (_("unsupported HTTP response"));
106 data
->first_line_recv
= 1;
107 return GRUB_ERR_NONE
;
109 ptr
+= sizeof ("HTTP/1.1 ") - 1;
110 code
= grub_strtoul (ptr
, &ptr
, 10);
119 data
->err
= GRUB_ERR_FILE_NOT_FOUND
;
120 data
->errmsg
= grub_xasprintf (_("file `%s' not found"), data
->filename
);
121 return GRUB_ERR_NONE
;
123 data
->err
= GRUB_ERR_NET_UNKNOWN_ERROR
;
124 /* TRANSLATORS: GRUB HTTP code is pretty young. So even perfectly
125 valid answers like 403 will trigger this very generic message. */
126 data
->errmsg
= grub_xasprintf (_("unsupported HTTP error %d: %s"),
128 return GRUB_ERR_NONE
;
130 data
->first_line_recv
= 1;
131 return GRUB_ERR_NONE
;
133 if (grub_memcmp (ptr
, "Content-Length: ", sizeof ("Content-Length: ") - 1)
134 == 0 && !data
->size_recv
)
136 ptr
+= sizeof ("Content-Length: ") - 1;
137 file
->size
= grub_strtoull (ptr
, &ptr
, 10);
139 return GRUB_ERR_NONE
;
141 if (grub_memcmp (ptr
, "Transfer-Encoding: chunked",
142 sizeof ("Transfer-Encoding: chunked") - 1) == 0)
145 return GRUB_ERR_NONE
;
148 return GRUB_ERR_NONE
;
152 http_err (grub_net_tcp_socket_t sock
__attribute__ ((unused
)),
155 grub_file_t file
= f
;
156 http_data_t data
= file
->data
;
159 grub_net_tcp_close (data
->sock
, GRUB_NET_TCP_ABORT
);
161 if (data
->current_line
)
162 grub_free (data
->current_line
);
163 data
->current_line
= 0;
164 file
->device
->net
->eof
= 1;
165 file
->device
->net
->stall
= 1;
166 if (file
->size
== GRUB_FILE_SIZE_UNKNOWN
)
167 file
->size
= have_ahead (file
);
171 http_receive (grub_net_tcp_socket_t sock
__attribute__ ((unused
)),
172 struct grub_net_buff
*nb
,
175 grub_file_t file
= f
;
176 http_data_t data
= file
->data
;
181 grub_netbuff_free (nb
);
182 return GRUB_ERR_NONE
;
187 char *ptr
= (char *) nb
->data
;
188 if ((!data
->headers_recv
|| data
->in_chunk_len
) && data
->current_line
)
192 ptr
= grub_memchr (nb
->data
, '\n', nb
->tail
- nb
->data
);
198 ptr
= (char *) nb
->tail
;
200 t
= grub_realloc (data
->current_line
,
201 data
->current_line_len
+ (ptr
- (char *) nb
->data
));
204 grub_netbuff_free (nb
);
205 grub_net_tcp_close (data
->sock
, GRUB_NET_TCP_ABORT
);
209 data
->current_line
= t
;
210 grub_memcpy (data
->current_line
+ data
->current_line_len
,
211 nb
->data
, ptr
- (char *) nb
->data
);
212 data
->current_line_len
+= ptr
- (char *) nb
->data
;
215 grub_netbuff_free (nb
);
216 return GRUB_ERR_NONE
;
218 err
= parse_line (file
, data
, data
->current_line
,
219 data
->current_line_len
);
220 grub_free (data
->current_line
);
221 data
->current_line
= 0;
222 data
->current_line_len
= 0;
225 grub_net_tcp_close (data
->sock
, GRUB_NET_TCP_ABORT
);
226 grub_netbuff_free (nb
);
231 while (ptr
< (char *) nb
->tail
&& (!data
->headers_recv
232 || data
->in_chunk_len
))
235 ptr2
= grub_memchr (ptr
, '\n', (char *) nb
->tail
- ptr
);
238 data
->current_line
= grub_malloc ((char *) nb
->tail
- ptr
);
239 if (!data
->current_line
)
241 grub_netbuff_free (nb
);
242 grub_net_tcp_close (data
->sock
, GRUB_NET_TCP_ABORT
);
245 data
->current_line_len
= (char *) nb
->tail
- ptr
;
246 grub_memcpy (data
->current_line
, ptr
, data
->current_line_len
);
247 grub_netbuff_free (nb
);
248 return GRUB_ERR_NONE
;
250 err
= parse_line (file
, data
, ptr
, ptr2
- ptr
);
253 grub_net_tcp_close (data
->sock
, GRUB_NET_TCP_ABORT
);
254 grub_netbuff_free (nb
);
260 if (((char *) nb
->tail
- ptr
) <= 0)
262 grub_netbuff_free (nb
);
263 return GRUB_ERR_NONE
;
265 err
= grub_netbuff_pull (nb
, ptr
- (char *) nb
->data
);
268 grub_net_tcp_close (data
->sock
, GRUB_NET_TCP_ABORT
);
269 grub_netbuff_free (nb
);
272 if (!(data
->chunked
&& (grub_ssize_t
) data
->chunk_rem
273 < nb
->tail
- nb
->data
))
275 grub_net_put_packet (&file
->device
->net
->packs
, nb
);
276 if (file
->device
->net
->packs
.count
>= 20)
277 file
->device
->net
->stall
= 1;
279 if (file
->device
->net
->packs
.count
>= 100)
280 grub_net_tcp_stall (data
->sock
);
283 data
->chunk_rem
-= nb
->tail
- nb
->data
;
284 return GRUB_ERR_NONE
;
288 struct grub_net_buff
*nb2
;
289 nb2
= grub_netbuff_alloc (data
->chunk_rem
);
292 grub_netbuff_put (nb2
, data
->chunk_rem
);
293 grub_memcpy (nb2
->data
, nb
->data
, data
->chunk_rem
);
294 if (file
->device
->net
->packs
.count
>= 20)
296 file
->device
->net
->stall
= 1;
297 grub_net_tcp_stall (data
->sock
);
300 grub_net_put_packet (&file
->device
->net
->packs
, nb2
);
301 grub_netbuff_pull (nb
, data
->chunk_rem
);
303 data
->in_chunk_len
= 1;
308 http_establish (struct grub_file
*file
, grub_off_t offset
, int initial
)
310 http_data_t data
= file
->data
;
313 struct grub_net_buff
*nb
;
316 nb
= grub_netbuff_alloc (GRUB_NET_TCP_RESERVE_SIZE
317 + sizeof ("GET ") - 1
318 + grub_strlen (data
->filename
)
319 + sizeof (" HTTP/1.1\r\nHost: ") - 1
320 + grub_strlen (file
->device
->net
->server
)
321 + sizeof ("\r\nUser-Agent: " PACKAGE_STRING
323 + sizeof ("Range: bytes=XXXXXXXXXXXXXXXXXXXX"
328 grub_netbuff_reserve (nb
, GRUB_NET_TCP_RESERVE_SIZE
);
330 err
= grub_netbuff_put (nb
, sizeof ("GET ") - 1);
333 grub_netbuff_free (nb
);
336 grub_memcpy (ptr
, "GET ", sizeof ("GET ") - 1);
340 err
= grub_netbuff_put (nb
, grub_strlen (data
->filename
));
343 grub_netbuff_free (nb
);
346 grub_memcpy (ptr
, data
->filename
, grub_strlen (data
->filename
));
349 err
= grub_netbuff_put (nb
, sizeof (" HTTP/1.1\r\nHost: ") - 1);
352 grub_netbuff_free (nb
);
355 grub_memcpy (ptr
, " HTTP/1.1\r\nHost: ",
356 sizeof (" HTTP/1.1\r\nHost: ") - 1);
359 err
= grub_netbuff_put (nb
, grub_strlen (file
->device
->net
->server
));
362 grub_netbuff_free (nb
);
365 grub_memcpy (ptr
, file
->device
->net
->server
,
366 grub_strlen (file
->device
->net
->server
));
369 err
= grub_netbuff_put (nb
,
370 sizeof ("\r\nUser-Agent: " PACKAGE_STRING
"\r\n")
374 grub_netbuff_free (nb
);
377 grub_memcpy (ptr
, "\r\nUser-Agent: " PACKAGE_STRING
"\r\n",
378 sizeof ("\r\nUser-Agent: " PACKAGE_STRING
"\r\n") - 1);
382 grub_snprintf ((char *) ptr
,
383 sizeof ("Range: bytes=XXXXXXXXXXXXXXXXXXXX-"
386 "Range: bytes=%" PRIuGRUB_UINT64_T
"-\r\n\r\n",
388 grub_netbuff_put (nb
, grub_strlen ((char *) ptr
));
391 grub_netbuff_put (nb
, 2);
392 grub_memcpy (ptr
, "\r\n", 2);
394 data
->sock
= grub_net_tcp_open (file
->device
->net
->server
,
395 HTTP_PORT
, http_receive
,
400 grub_netbuff_free (nb
);
404 // grub_net_poll_cards (5000);
406 err
= grub_net_send_tcp_packet (data
->sock
, nb
, 1);
409 grub_net_tcp_close (data
->sock
, GRUB_NET_TCP_ABORT
);
413 for (i
= 0; !data
->headers_recv
&& i
< 100; i
++)
415 grub_net_tcp_retransmit ();
416 grub_net_poll_cards (300, &data
->headers_recv
);
419 if (!data
->headers_recv
)
421 grub_net_tcp_close (data
->sock
, GRUB_NET_TCP_ABORT
);
424 char *str
= data
->errmsg
;
425 err
= grub_error (data
->err
, "%s", str
);
430 return grub_error (GRUB_ERR_TIMEOUT
, N_("time out opening `%s'"), data
->filename
);
432 return GRUB_ERR_NONE
;
436 http_seek (struct grub_file
*file
, grub_off_t off
)
438 struct http_data
*old_data
, *data
;
440 old_data
= file
->data
;
441 /* FIXME: Reuse socket? */
443 grub_net_tcp_close (old_data
->sock
, GRUB_NET_TCP_ABORT
);
446 while (file
->device
->net
->packs
.first
)
448 grub_netbuff_free (file
->device
->net
->packs
.first
->nb
);
449 grub_net_remove_packet (file
->device
->net
->packs
.first
);
452 file
->device
->net
->stall
= 0;
453 file
->device
->net
->offset
= off
;
455 data
= grub_zalloc (sizeof (*data
));
460 data
->filename
= old_data
->filename
;
467 grub_free (old_data
);
470 err
= http_establish (file
, off
, 0);
473 grub_free (data
->filename
);
478 return GRUB_ERR_NONE
;
482 http_open (struct grub_file
*file
, const char *filename
)
485 struct http_data
*data
;
487 data
= grub_zalloc (sizeof (*data
));
490 file
->size
= GRUB_FILE_SIZE_UNKNOWN
;
492 data
->filename
= grub_strdup (filename
);
499 file
->not_easily_seekable
= 0;
502 err
= http_establish (file
, 0, 1);
505 grub_free (data
->filename
);
510 return GRUB_ERR_NONE
;
514 http_close (struct grub_file
*file
)
516 http_data_t data
= file
->data
;
519 return GRUB_ERR_NONE
;
522 grub_net_tcp_close (data
->sock
, GRUB_NET_TCP_ABORT
);
523 if (data
->current_line
)
524 grub_free (data
->current_line
);
525 grub_free (data
->filename
);
527 return GRUB_ERR_NONE
;
531 http_packets_pulled (struct grub_file
*file
)
533 http_data_t data
= file
->data
;
535 if (file
->device
->net
->packs
.count
>= 20)
538 if (!file
->device
->net
->eof
)
539 file
->device
->net
->stall
= 0;
540 if (data
&& data
->sock
)
541 grub_net_tcp_unstall (data
->sock
);
545 static struct grub_net_app_protocol grub_http_protocol
=
551 .packets_pulled
= http_packets_pulled
556 grub_net_app_level_register (&grub_http_protocol
);
561 grub_net_app_level_unregister (&grub_http_protocol
);