diff --git a/grub-core/net/http.c b/grub-core/net/http.c index b5de4c5c6..e4664757c 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -42,7 +42,11 @@ typedef struct http_data grub_size_t current_line_len; int headers_recv; int first_line_recv; + int size_recv; grub_net_tcp_socket_t sock; + char *filename; + grub_err_t err; + char *errmsg; } *http_data_t; static grub_err_t @@ -72,19 +76,25 @@ parse_line (http_data_t data, char *ptr, grub_size_t len) { case 200: break; + case 404: + data->err = GRUB_ERR_FILE_NOT_FOUND; + data->errmsg = grub_xasprintf ("file `%s' not found", data->filename); + return GRUB_ERR_NONE; default: - return grub_error (GRUB_ERR_NET_UNKNOWN_ERROR, - "unsupported HTTP error %d: %s", - code, ptr); + data->err = GRUB_ERR_NET_UNKNOWN_ERROR; + data->errmsg = grub_xasprintf ("unsupported HTTP error %d: %s", + code, ptr); + return GRUB_ERR_NONE; } data->first_line_recv = 1; return GRUB_ERR_NONE; } if (grub_memcmp (ptr, "Content-Length: ", sizeof ("Content-Length: ") - 1) - == 0) + == 0 && !data->size_recv) { ptr += sizeof ("Content-Length: ") - 1; data->file_size = grub_strtoull (ptr, &ptr, 10); + data->size_recv = 1; return GRUB_ERR_NONE; } return GRUB_ERR_NONE; @@ -98,10 +108,10 @@ http_err (grub_net_tcp_socket_t sock __attribute__ ((unused)), http_data_t data = file->data; if (data->sock) - grub_net_tcp_close (data->sock); - grub_free (data); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); if (data->current_line) grub_free (data->current_line); + grub_free (data); file->device->net->eof = 1; } @@ -132,7 +142,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), if (!t) { grub_netbuff_free (nb); - grub_net_tcp_close (data->sock); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); return grub_errno; } @@ -151,7 +161,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), data->current_line_len = 0; if (err) { - grub_net_tcp_close (data->sock); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); grub_netbuff_free (nb); return err; } @@ -167,7 +177,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), if (!data->current_line) { grub_netbuff_free (nb); - grub_net_tcp_close (data->sock); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); return grub_errno; } data->current_line_len = (char *) nb->tail - ptr; @@ -178,7 +188,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), err = parse_line (data, ptr, ptr2 - ptr); if (err) { - grub_net_tcp_close (data->sock); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); grub_netbuff_free (nb); return err; } @@ -191,7 +201,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), err = grub_netbuff_pull (nb, ptr - (char *) nb->data); if (err) { - grub_net_tcp_close (data->sock); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); grub_netbuff_free (nb); return err; } @@ -203,57 +213,51 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), } static grub_err_t -http_open (struct grub_file *file, const char *filename) +http_establish (struct grub_file *file, grub_off_t offset, int initial) { - struct grub_net_buff *nb; - http_data_t data; - grub_err_t err; + http_data_t data = file->data; grub_uint8_t *ptr; int i; - - data = grub_zalloc (sizeof (*data)); - if (!data) - return grub_errno; + struct grub_net_buff *nb; + grub_err_t err; nb = grub_netbuff_alloc (GRUB_NET_TCP_RESERVE_SIZE + sizeof ("GET ") - 1 - + grub_strlen (filename) + + grub_strlen (data->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); + "\r\n") - 1 + + sizeof ("Content-Range: bytes XXXXXXXXXXXXXXXXXXXX" + "-XXXXXXXXXXXXXXXXXXXX/" + "XXXXXXXXXXXXXXXXXXXX\r\n\r\n")); if (!nb) - { - grub_free (data); - return grub_errno; - } + 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)); + + err = grub_netbuff_put (nb, grub_strlen (data->filename)); if (err) { - grub_free (data); grub_netbuff_free (nb); return err; } - grub_memcpy (ptr, filename, grub_strlen (filename)); + grub_memcpy (ptr, data->filename, grub_strlen (data->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; } @@ -264,7 +268,6 @@ http_open (struct grub_file *file, const char *filename) err = grub_netbuff_put (nb, grub_strlen (file->device->net->server)); if (err) { - grub_free (data); grub_netbuff_free (nb); return err; } @@ -273,19 +276,30 @@ http_open (struct grub_file *file, const char *filename) ptr = nb->tail; err = grub_netbuff_put (nb, - sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n\r\n") + sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\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; + grub_memcpy (ptr, "\r\nUser-Agent: " PACKAGE_STRING "\r\n", + sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n") - 1); + if (!initial) + { + ptr = nb->tail; + grub_snprintf ((char *) ptr, + sizeof ("Content-Range: bytes XXXXXXXXXXXXXXXXXXXX-" + "XXXXXXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXXXXXX\r\n" + "\r\n"), + "Content-Range: bytes %" PRIuGRUB_UINT64_T "-%" + PRIuGRUB_UINT64_T "/%" PRIuGRUB_UINT64_T "\r\n\r\n", + offset, data->file_size - 1, data->file_size); + grub_netbuff_put (nb, grub_strlen ((char *) ptr)); + } + ptr = nb->tail; + grub_netbuff_put (nb, 2); + grub_memcpy (ptr, "\r\n", 2); data->sock = grub_net_tcp_open (file->device->net->server, HTTP_PORT, http_receive, @@ -293,7 +307,6 @@ http_open (struct grub_file *file, const char *filename) file); if (!data->sock) { - grub_free (data); grub_netbuff_free (nb); return grub_errno; } @@ -303,8 +316,7 @@ http_open (struct grub_file *file, const char *filename) err = grub_net_send_tcp_packet (data->sock, nb, 1); if (err) { - grub_free (data); - grub_net_tcp_close (data->sock); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); return err; } @@ -316,9 +328,81 @@ http_open (struct grub_file *file, const char *filename) if (!data->headers_recv) { - grub_net_tcp_close (data->sock); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); + if (data->err) + { + char *str = data->errmsg; + err = grub_error (data->err, "%s", str); + grub_free (str); + return data->err; + } + return grub_error (GRUB_ERR_TIMEOUT, "timeout opening http"); + } + return GRUB_ERR_NONE; +} + +static grub_err_t +http_seek (struct grub_file *file, grub_off_t off) +{ + struct http_data *old_data, *data; + grub_err_t err; + old_data = file->data; + /* FIXME: Reuse socket? */ + grub_net_tcp_close (old_data->sock, GRUB_NET_TCP_ABORT); + + while (file->device->net->packs.first) + grub_net_remove_packet (file->device->net->packs.first); + + data = grub_zalloc (sizeof (*data)); + if (!data) + return grub_errno; + + data->file_size = old_data->file_size; + data->size_recv = 1; + data->filename = old_data->filename; + if (!data->filename) + { grub_free (data); - return grub_error (GRUB_ERR_TIMEOUT, "Time out opening http."); + return grub_errno; + } + grub_free (old_data); + + err = http_establish (file, off, 0); + if (err) + { + grub_free (data->filename); + grub_free (data); + return err; + } + return GRUB_ERR_NONE; +} + +static grub_err_t +http_open (struct grub_file *file, const char *filename) +{ + grub_err_t err; + struct http_data *data; + + data = grub_zalloc (sizeof (*data)); + if (!data) + return grub_errno; + + data->filename = grub_strdup (filename); + if (!data->filename) + { + grub_free (data); + return grub_errno; + } + + file->not_easily_seekable = 0; + file->data = data; + + err = http_establish (file, 0, 1); + if (err) + { + grub_free (data->filename); + grub_free (data); + return err; } file->size = data->file_size; @@ -331,10 +415,10 @@ http_close (struct grub_file *file) http_data_t data = file->data; if (data->sock) - grub_net_tcp_close (data->sock); - grub_free (data); + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); if (data->current_line) grub_free (data->current_line); + grub_free (data); return GRUB_ERR_NONE; } @@ -342,7 +426,8 @@ static struct grub_net_app_protocol grub_http_protocol = { .name = "http", .open = http_open, - .close = http_close + .close = http_close, + .seek = http_seek }; GRUB_MOD_INIT (http) diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 0ff8d01ff..0bde49322 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -815,17 +815,17 @@ grub_net_poll_cards_idle_real (void) static grub_ssize_t grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len) { - grub_net_t sock = file->device->net; + grub_net_t net = file->device->net; struct grub_net_buff *nb; char *ptr = buf; grub_size_t amount, total = 0; int try = 0; while (try <= 3) { - while (sock->packs.first) + while (net->packs.first) { try = 0; - nb = sock->packs.first->nb; + nb = net->packs.first->nb; amount = nb->tail - nb->data; if (amount > len) amount = len; @@ -840,7 +840,7 @@ grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len) if (amount == (grub_size_t) (nb->tail - nb->data)) { grub_netbuff_free (nb); - grub_net_remove_packet (sock->packs.first); + grub_net_remove_packet (net->packs.first); } else nb->data += amount; @@ -848,7 +848,7 @@ grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len) if (!len) return total; } - if (!sock->eof) + if (!net->eof) { try++; grub_net_poll_cards (200); @@ -856,39 +856,59 @@ grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len) else return total; } - return total; + grub_error (GRUB_ERR_TIMEOUT, "timeout reading '%s'", net->name); + return -1; +} + +static grub_off_t +have_ahead (struct grub_file *file) +{ + grub_net_t net = file->device->net; + grub_off_t ret = net->offset; + struct grub_net_packet *pack; + for (pack = net->packs.first; pack; pack = pack->next) + ret += pack->nb->tail - pack->nb->data; + return ret; } static grub_err_t grub_net_seek_real (struct grub_file *file, grub_off_t offset) { - grub_size_t len = offset - file->device->net->offset; - - if (!len) + if (offset == file->device->net->offset) return GRUB_ERR_NONE; - if (file->device->net->offset > offset) + if (offset > file->device->net->offset) { - grub_err_t err; - while (file->device->net->packs.first) + if (!file->device->net->protocol->seek || have_ahead (file) >= offset) { - grub_netbuff_free (file->device->net->packs.first->nb); - grub_net_remove_packet (file->device->net->packs.first); + grub_net_fs_read_real (file, NULL, + offset - file->device->net->offset); + return grub_errno; } - file->device->net->protocol->close (file); - - file->device->net->packs.first = NULL; - file->device->net->packs.last = NULL; - file->device->net->offset = 0; - file->device->net->eof = 0; - err = file->device->net->protocol->open (file, file->device->net->name); - if (err) - return err; - len = offset; + return file->device->net->protocol->seek (file, offset); } - grub_net_fs_read_real (file, NULL, len); - return GRUB_ERR_NONE; + { + grub_err_t err; + if (file->device->net->protocol->seek) + return file->device->net->protocol->seek (file, offset); + while (file->device->net->packs.first) + { + grub_netbuff_free (file->device->net->packs.first->nb); + grub_net_remove_packet (file->device->net->packs.first); + } + file->device->net->protocol->close (file); + + file->device->net->packs.first = NULL; + file->device->net->packs.last = NULL; + file->device->net->offset = 0; + file->device->net->eof = 0; + err = file->device->net->protocol->open (file, file->device->net->name); + if (err) + return err; + grub_net_fs_read_real (file, NULL, offset); + return grub_errno; + } } static grub_ssize_t diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index 25e8ab4ed..7f120ac16 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -56,7 +56,8 @@ struct grub_net_tcp_socket int in_port; int out_port; int errors; - int reseted; + int they_reseted; + int i_reseted; grub_uint32_t my_start_seq; grub_uint32_t my_cur_seq; grub_uint32_t their_start_seq; @@ -153,7 +154,7 @@ error (grub_net_tcp_socket_t sock) { struct unacked *unack, *next; - if (sock->established && sock->error_hook) + if (sock->error_hook) sock->error_hook (sock, sock->hook_data); for (unack = sock->unack_first; unack; unack = next) @@ -217,7 +218,8 @@ tcp_send (struct grub_net_buff *nb, grub_net_tcp_socket_t socket) } void -grub_net_tcp_close (grub_net_tcp_socket_t sock) +grub_net_tcp_close (grub_net_tcp_socket_t sock, + int discard_received) { struct grub_net_buff *nb_fin; struct tcphdr *tcph_fin; @@ -225,6 +227,9 @@ grub_net_tcp_close (grub_net_tcp_socket_t sock) sock->i_closed = 1; + if (discard_received != GRUB_NET_TCP_CONTINUE_RECEIVING) + sock->recv_hook = NULL; + nb_fin = grub_netbuff_alloc (sizeof (*tcph_fin) + GRUB_NET_OUR_IPV4_HEADER_SIZE + GRUB_NET_MAX_LINK_HEADER_SIZE); @@ -254,6 +259,8 @@ grub_net_tcp_close (grub_net_tcp_socket_t sock) tcph_fin->window = grub_cpu_to_be16 (0); tcph_fin->urgent = 0; err = tcp_send (nb_fin, sock); + if (discard_received == GRUB_NET_TCP_ABORT) + sock->i_reseted = 1; if (err) { grub_netbuff_free (nb_fin); @@ -264,7 +271,7 @@ grub_net_tcp_close (grub_net_tcp_socket_t sock) } static void -ack (grub_net_tcp_socket_t sock) +ack_real (grub_net_tcp_socket_t sock, int res) { struct grub_net_buff *nb_ack; struct tcphdr *tcph_ack; @@ -291,9 +298,18 @@ ack (grub_net_tcp_socket_t sock) return; } tcph_ack = (void *) nb_ack->data; - tcph_ack->ack = grub_cpu_to_be32 (sock->their_cur_seq); - tcph_ack->flags = grub_cpu_to_be16 ((5 << 12) | TCP_ACK); - tcph_ack->window = grub_cpu_to_be16 (sock->my_window); + if (res) + { + tcph_ack->ack = grub_cpu_to_be32 (0); + tcph_ack->flags = grub_cpu_to_be16 ((5 << 12) | TCP_RST); + tcph_ack->window = grub_cpu_to_be16 (0); + } + else + { + tcph_ack->ack = grub_cpu_to_be32 (sock->their_cur_seq); + tcph_ack->flags = grub_cpu_to_be16 ((5 << 12) | TCP_ACK); + tcph_ack->window = grub_cpu_to_be16 (sock->my_window); + } tcph_ack->urgent = 0; tcph_ack->src = grub_cpu_to_be16 (sock->in_port); tcph_ack->dst = grub_cpu_to_be16 (sock->out_port); @@ -305,6 +321,18 @@ ack (grub_net_tcp_socket_t sock) } } +static void +ack (grub_net_tcp_socket_t sock) +{ + ack_real (sock, 0); +} + +static void +reset (grub_net_tcp_socket_t sock) +{ + ack_real (sock, 1); +} + void grub_net_tcp_retransmit (void) { @@ -434,6 +462,7 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock, tcph->flags = grub_cpu_to_be16 ((5 << 12) | TCP_SYN | TCP_ACK); tcph->window = grub_cpu_to_be16 (sock->my_window); tcph->urgent = 0; + sock->established = 1; tcp_socket_register (sock); err = tcp_send (nb_ack, sock); if (err) @@ -555,7 +584,7 @@ grub_net_tcp_open (char *server, { grub_list_remove (GRUB_AS_LIST_P (&tcp_sockets), GRUB_AS_LIST (socket)); - if (socket->reseted) + if (socket->they_reseted) grub_error (GRUB_ERR_NET_PORT_CLOSED, "port closed"); else grub_error (GRUB_ERR_NET_NO_ANSWER, "no answer"); @@ -693,7 +722,7 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, if (grub_be_to_cpu16 (tcph->flags) & TCP_RST) { - sock->reseted = 1; + sock->they_reseted = 1; error (sock); @@ -725,7 +754,7 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, grub_free (unack); } sock->unack_first = unack; - if (!sock->unack_last) + if (!sock->unack_first) sock->unack_last = NULL; } @@ -735,6 +764,12 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, grub_netbuff_free (nb); return GRUB_ERR_NONE; } + if (sock->i_reseted) + { + reset (sock); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } err = grub_priority_queue_push (sock->pq, &nb); if (err) diff --git a/include/grub/net.h b/include/grub/net.h index 255784315..ac011520a 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -204,6 +204,7 @@ struct grub_net_app_protocol int (*hook) (const char *filename, const struct grub_dirhook_info *info)); grub_err_t (*open) (struct grub_file *file, const char *filename); + grub_err_t (*seek) (struct grub_file *file, grub_off_t off); grub_err_t (*close) (struct grub_file *file); }; diff --git a/include/grub/net/tcp.h b/include/grub/net/tcp.h index 0ac7819bc..b25ceff1b 100644 --- a/include/grub/net/tcp.h +++ b/include/grub/net/tcp.h @@ -53,8 +53,15 @@ grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, struct grub_net_buff *nb, int push); +enum + { + GRUB_NET_TCP_CONTINUE_RECEIVING, + GRUB_NET_TCP_DISCARD, + GRUB_NET_TCP_ABORT + }; + void -grub_net_tcp_close (grub_net_tcp_socket_t sock); +grub_net_tcp_close (grub_net_tcp_socket_t sock, int discard_received); grub_err_t grub_net_tcp_accept (grub_net_tcp_socket_t sock,