From 80e722366d307f93c94ab54d4717d973887c87b9 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Sat, 23 Jul 2011 03:49:02 +0200 Subject: [PATCH] First attempt at http --- grub-core/Makefile.core.def | 5 + grub-core/net/http.c | 356 ++++++++++++++++++++++++++++++++++++ grub-core/net/tcp.c | 5 +- include/grub/err.h | 4 +- include/grub/misc.h | 14 ++ include/grub/net.h | 13 +- 6 files changed, 391 insertions(+), 6 deletions(-) create mode 100644 grub-core/net/http.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 673092b4f..f42b272ca 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1609,6 +1609,11 @@ module = { common = net/tftp.c; }; +module = { + name = http; + common = net/http.c; +}; + module = { name = ofnet; common = net/drivers/ieee1275/ofnet.c; diff --git a/grub-core/net/http.c b/grub-core/net/http.c new file mode 100644 index 000000000..b5de4c5c6 --- /dev/null +++ b/grub-core/net/http.c @@ -0,0 +1,356 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010,2011 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +enum + { + HTTP_PORT = 80 + }; + + +typedef struct http_data +{ + grub_uint64_t file_size; + grub_uint64_t position; + char *current_line; + grub_size_t current_line_len; + int headers_recv; + int first_line_recv; + grub_net_tcp_socket_t sock; +} *http_data_t; + +static grub_err_t +parse_line (http_data_t data, char *ptr, grub_size_t len) +{ + char *end = ptr + len; + while (end > ptr && *(end - 1) == '\r') + end--; + *end = 0; + if (ptr == end) + { + data->headers_recv = 1; + return GRUB_ERR_NONE; + } + + if (!data->first_line_recv) + { + int code; + if (grub_memcmp (ptr, "HTTP/1.1 ", sizeof ("HTTP/1.1 ") - 1) != 0) + return grub_error (GRUB_ERR_NET_INVALID_RESPONSE, + "unsupported HTTP response"); + ptr += sizeof ("HTTP/1.1 ") - 1; + code = grub_strtoul (ptr, &ptr, 10); + if (grub_errno) + return grub_errno; + switch (code) + { + case 200: + break; + default: + return grub_error (GRUB_ERR_NET_UNKNOWN_ERROR, + "unsupported HTTP error %d: %s", + code, ptr); + } + data->first_line_recv = 1; + return GRUB_ERR_NONE; + } + if (grub_memcmp (ptr, "Content-Length: ", sizeof ("Content-Length: ") - 1) + == 0) + { + ptr += sizeof ("Content-Length: ") - 1; + data->file_size = grub_strtoull (ptr, &ptr, 10); + return GRUB_ERR_NONE; + } + return GRUB_ERR_NONE; +} + +static void +http_err (grub_net_tcp_socket_t sock __attribute__ ((unused)), + void *f) +{ + grub_file_t file = f; + http_data_t data = file->data; + + if (data->sock) + grub_net_tcp_close (data->sock); + grub_free (data); + if (data->current_line) + grub_free (data->current_line); + file->device->net->eof = 1; +} + +static grub_err_t +http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), + struct grub_net_buff *nb, + void *f) +{ + grub_file_t file = f; + http_data_t data = file->data; + char *ptr = (char *) nb->data; + grub_err_t err; + + if (!data->headers_recv && data->current_line) + { + int have_line = 1; + char *t; + ptr = grub_memchr (nb->data, '\n', nb->tail - nb->data); + if (ptr) + ptr++; + else + { + have_line = 0; + ptr = (char *) nb->tail; + } + t = grub_realloc (data->current_line, + data->current_line_len + (ptr - (char *) nb->data)); + if (!t) + { + grub_netbuff_free (nb); + grub_net_tcp_close (data->sock); + return grub_errno; + } + + data->current_line = t; + grub_memcpy (data->current_line + data->current_line_len, + nb->data, ptr - (char *) nb->data); + data->current_line_len += ptr - (char *) nb->data; + if (!have_line) + { + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + err = parse_line (data, data->current_line, data->current_line_len); + grub_free (data->current_line); + data->current_line = 0; + data->current_line_len = 0; + if (err) + { + grub_net_tcp_close (data->sock); + grub_netbuff_free (nb); + return err; + } + } + + while (ptr < (char *) nb->tail && !data->headers_recv) + { + char *ptr2; + ptr2 = grub_memchr (ptr, '\n', (char *) nb->tail - ptr); + if (!ptr2) + { + data->current_line = grub_malloc ((char *) nb->tail - ptr); + if (!data->current_line) + { + grub_netbuff_free (nb); + grub_net_tcp_close (data->sock); + return grub_errno; + } + data->current_line_len = (char *) nb->tail - ptr; + grub_memcpy (data->current_line, ptr, data->current_line_len); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + err = parse_line (data, ptr, ptr2 - ptr); + if (err) + { + grub_net_tcp_close (data->sock); + grub_netbuff_free (nb); + return err; + } + ptr = ptr2 + 1; + } + + if (((char *) nb->tail - ptr) > 0) + { + data->position += ((char *) nb->tail - ptr); + err = grub_netbuff_pull (nb, ptr - (char *) nb->data); + if (err) + { + grub_net_tcp_close (data->sock); + grub_netbuff_free (nb); + return err; + } + grub_net_put_packet (&file->device->net->packs, nb); + } + else + grub_netbuff_free (nb); + return GRUB_ERR_NONE; +} + +static grub_err_t +http_open (struct grub_file *file, const char *filename) +{ + struct grub_net_buff *nb; + http_data_t data; + grub_err_t err; + grub_uint8_t *ptr; + int i; + + data = grub_zalloc (sizeof (*data)); + if (!data) + return grub_errno; + + nb = grub_netbuff_alloc (GRUB_NET_TCP_RESERVE_SIZE + + sizeof ("GET ") - 1 + + grub_strlen (filename) + + sizeof (" HTTP/1.1\r\nHost: ") - 1 + + grub_strlen (file->device->net->server) + + sizeof ("\r\nUser-Agent: " PACKAGE_STRING + "\r\n\r\n") - 1); + if (!nb) + { + grub_free (data); + return grub_errno; + } + + grub_netbuff_reserve (nb, GRUB_NET_TCP_RESERVE_SIZE); + ptr = nb->tail; + err = grub_netbuff_put (nb, sizeof ("GET ") - 1); + if (err) + { + grub_free (data); + grub_netbuff_free (nb); + return err; + } + grub_memcpy (ptr, "GET ", sizeof ("GET ") - 1); + + ptr = nb->tail; + err = grub_netbuff_put (nb, grub_strlen (filename)); + if (err) + { + grub_free (data); + grub_netbuff_free (nb); + return err; + } + grub_memcpy (ptr, filename, grub_strlen (filename)); + + ptr = nb->tail; + err = grub_netbuff_put (nb, sizeof (" HTTP/1.1\r\nHost: ") - 1); + if (err) + { + grub_free (data); + grub_netbuff_free (nb); + return err; + } + grub_memcpy (ptr, " HTTP/1.1\r\nHost: ", + sizeof (" HTTP/1.1\r\nHost: ") - 1); + + ptr = nb->tail; + err = grub_netbuff_put (nb, grub_strlen (file->device->net->server)); + if (err) + { + grub_free (data); + grub_netbuff_free (nb); + return err; + } + grub_memcpy (ptr, file->device->net->server, + grub_strlen (file->device->net->server)); + + ptr = nb->tail; + err = grub_netbuff_put (nb, + sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n\r\n") + - 1); + if (err) + { + grub_free (data); + grub_netbuff_free (nb); + return err; + } + grub_memcpy (ptr, "\r\nUser-Agent: " PACKAGE_STRING "\r\n\r\n", + sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n\r\n") - 1); + + file->not_easily_seekable = 1; + file->data = data; + + data->sock = grub_net_tcp_open (file->device->net->server, + HTTP_PORT, http_receive, + http_err, + file); + if (!data->sock) + { + grub_free (data); + grub_netbuff_free (nb); + return grub_errno; + } + + // grub_net_poll_cards (5000); + + err = grub_net_send_tcp_packet (data->sock, nb, 1); + if (err) + { + grub_free (data); + grub_net_tcp_close (data->sock); + return err; + } + + for (i = 0; !data->headers_recv && i < 100; i++) + { + grub_net_tcp_retransmit (); + grub_net_poll_cards (300); + } + + if (!data->headers_recv) + { + grub_net_tcp_close (data->sock); + grub_free (data); + return grub_error (GRUB_ERR_TIMEOUT, "Time out opening http."); + } + file->size = data->file_size; + + return GRUB_ERR_NONE; +} + +static grub_err_t +http_close (struct grub_file *file) +{ + http_data_t data = file->data; + + if (data->sock) + grub_net_tcp_close (data->sock); + grub_free (data); + if (data->current_line) + grub_free (data->current_line); + return GRUB_ERR_NONE; +} + +static struct grub_net_app_protocol grub_http_protocol = + { + .name = "http", + .open = http_open, + .close = http_close + }; + +GRUB_MOD_INIT (http) +{ + grub_net_app_level_register (&grub_http_protocol); +} + +GRUB_MOD_FINI (http) +{ + grub_net_app_level_unregister (&grub_http_protocol); +} diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index 578a254f4..25e8ab4ed 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -25,8 +25,8 @@ #define TCP_SYN_RETRANSMISSION_TIMEOUT 1000 #define TCP_SYN_RETRANSMISSION_COUNT 3 -#define TCP_RETRANSMISSION_TIMEOUT 10000 -#define TCP_RETRANSMISSION_COUNT 5 +#define TCP_RETRANSMISSION_TIMEOUT 1000 +#define TCP_RETRANSMISSION_COUNT 10 struct unacked { @@ -577,6 +577,7 @@ grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, struct tcphdr *tcph; grub_err_t err; grub_ssize_t fraglen; + COMPILE_TIME_ASSERT (sizeof (struct tcphdr) == GRUB_NET_TCP_HEADER_SIZE); fraglen = (socket->inf->card->mtu - GRUB_NET_OUR_IPV4_HEADER_SIZE - sizeof (*tcph)); diff --git a/include/grub/err.h b/include/grub/err.h index c91911644..f6610193c 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -62,7 +62,9 @@ typedef enum GRUB_ERR_NET_NO_ANSWER, GRUB_ERR_WAIT, GRUB_ERR_BUG, - GRUB_ERR_NET_PORT_CLOSED + GRUB_ERR_NET_PORT_CLOSED, + GRUB_ERR_NET_INVALID_RESPONSE, + GRUB_ERR_NET_UNKNOWN_ERROR } grub_err_t; diff --git a/include/grub/misc.h b/include/grub/misc.h index da4bd4a7e..e608ddfb8 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -302,6 +302,20 @@ void EXPORT_FUNC (__deregister_frame_info) (void); /* Inline functions. */ +static inline char * +grub_memchr (const void *p, int c, grub_size_t len) +{ + const char *s = p; + const char *e = s + len; + + for (; s < e; s++) + if (*s == c) + return (char *) s; + + return 0; +} + + static inline unsigned int grub_abs (int x) { diff --git a/include/grub/net.h b/include/grub/net.h index d71edfd1e..255784315 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -27,9 +27,16 @@ #include #include -#define GRUB_NET_MAX_LINK_HEADER_SIZE 64 -#define GRUB_NET_UDP_HEADER_SIZE 8 -#define GRUB_NET_OUR_IPV4_HEADER_SIZE 20 +enum + { + GRUB_NET_MAX_LINK_HEADER_SIZE = 64, + GRUB_NET_UDP_HEADER_SIZE = 8, + GRUB_NET_TCP_HEADER_SIZE = 20, + GRUB_NET_OUR_IPV4_HEADER_SIZE = 20, + GRUB_NET_TCP_RESERVE_SIZE = GRUB_NET_TCP_HEADER_SIZE + + GRUB_NET_OUR_IPV4_HEADER_SIZE + + GRUB_NET_MAX_LINK_HEADER_SIZE + }; typedef enum grub_link_level_protocol_id {