From bd40efbf0b38c0398bf732504e3219df55862012 Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Thu, 13 Oct 2011 18:31:53 +0200 Subject: [PATCH] several net bugfixes and improvements and fix some memory leaks --- grub-core/io/bufio.c | 124 +++++++++++----------- grub-core/kern/disk.c | 3 + grub-core/net/bootp.c | 16 +++ grub-core/net/http.c | 227 +++++++++++++++++++++++++++-------------- grub-core/net/net.c | 60 +++++++++-- grub-core/net/tcp.c | 109 ++++++++++++++------ include/grub/net/tcp.h | 4 + include/grub/types.h | 2 + 8 files changed, 365 insertions(+), 180 deletions(-) diff --git a/grub-core/io/bufio.c b/grub-core/io/bufio.c index adb38af56..0d01e62f9 100644 --- a/grub-core/io/bufio.c +++ b/grub-core/io/bufio.c @@ -35,6 +35,7 @@ struct grub_bufio grub_file_t file; grub_size_t block_size; grub_size_t buffer_len; + grub_off_t buffer_at; char buffer[0]; }; typedef struct grub_bufio *grub_bufio_t; @@ -70,6 +71,7 @@ grub_bufio_open (grub_file_t io, int size) bufio->file = io; bufio->block_size = size; bufio->buffer_len = 0; + bufio->buffer_at = 0; file->device = io->device; file->offset = 0; @@ -104,83 +106,87 @@ grub_buffile_open (const char *name, int size) static grub_ssize_t grub_bufio_read (grub_file_t file, char *buf, grub_size_t len) { - grub_size_t res = len; + grub_size_t res = 0; grub_bufio_t bufio = file->data; - grub_uint64_t pos; - if ((file->offset >= bufio->file->offset) && - (file->offset < bufio->file->offset + bufio->buffer_len)) + if (file->size == GRUB_FILE_SIZE_UNKNOWN) + file->size = bufio->file->size; + + /* First part: use whatever we already have in the buffer. */ + if ((file->offset >= bufio->buffer_at) && + (file->offset < bufio->buffer_at + bufio->buffer_len)) { grub_size_t n; + grub_uint64_t pos; - pos = file->offset - bufio->file->offset; + pos = file->offset - bufio->buffer_at; n = bufio->buffer_len - pos; if (n > len) n = len; grub_memcpy (buf, &bufio->buffer[pos], n); len -= n; - if (! len) - return res; + res += n; buf += n; - bufio->file->offset += bufio->buffer_len; - pos = 0; + } + if (! len) + return res; + + /* Need to read some more. */ + bufio->buffer_at = grub_divmod64 (file->offset + res + len, bufio->block_size, + 0) * bufio->block_size; + + /* Now read between file->offset + res and bufio->buffer_at. */ + if (file->offset + res < bufio->buffer_at) + { + grub_size_t read_now; + grub_ssize_t really_read; + read_now = bufio->buffer_at - (file->offset + res); + grub_file_seek (bufio->file, file->offset + res); + really_read = grub_file_read (bufio->file, buf, read_now); + if (grub_errno) + return -1; + if (file->size == GRUB_FILE_SIZE_UNKNOWN) + file->size = bufio->file->size; + len -= really_read; + buf += really_read; + res += really_read; + + /* Partial read. File ended unexpectedly. Save the last chunk in buffer. + */ + if (really_read != (grub_ssize_t) read_now) + { + bufio->buffer_len = really_read; + if (bufio->buffer_len > bufio->block_size) + bufio->buffer_len = bufio->block_size; + bufio->buffer_at = file->offset + res - bufio->buffer_len; + grub_memcpy (&bufio->buffer[0], buf - bufio->buffer_len, + bufio->buffer_len); + return res; + } + } + + /* Read into buffer. */ + grub_file_seek (bufio->file, bufio->buffer_at); + bufio->buffer_len = grub_file_read (bufio->file, bufio->buffer, + bufio->block_size); + if (grub_errno) + return -1; + if (file->size == GRUB_FILE_SIZE_UNKNOWN) + file->size = bufio->file->size; + + if (len < bufio->buffer_len) + { + grub_memcpy (buf, &bufio->buffer[0], len); + res += len; } else { - bufio->file->offset = grub_divmod64 (file->offset, bufio->block_size, - &pos); - bufio->file->offset *= bufio->block_size; + grub_memcpy (buf, &bufio->buffer[0], bufio->buffer_len); + res += bufio->buffer_len; } - if (pos + len >= bufio->block_size) - { - if (pos) - { - grub_size_t n; - - bufio->file->fs->read (bufio->file, bufio->buffer, - bufio->block_size); - if (grub_errno) - return -1; - - n = bufio->block_size - pos; - grub_memcpy (buf, &bufio->buffer[pos], n); - len -= n; - buf += n; - bufio->file->offset += bufio->block_size; - pos = 0; - } - - while (len >= bufio->block_size) - { - bufio->file->fs->read (bufio->file, buf, bufio->block_size); - if (grub_errno) - return -1; - - len -= bufio->block_size; - buf += bufio->block_size; - bufio->file->offset += bufio->block_size; - } - - if (! len) - { - bufio->buffer_len = 0; - return res; - } - } - - bufio->buffer_len = bufio->file->size - bufio->file->offset; - if (bufio->buffer_len > bufio->block_size) - bufio->buffer_len = bufio->block_size; - - bufio->file->fs->read (bufio->file, bufio->buffer, bufio->buffer_len); - if (grub_errno) - return -1; - - grub_memcpy (buf, &bufio->buffer[pos], len); - return res; } diff --git a/grub-core/kern/disk.c b/grub-core/kern/disk.c index f296b9d0f..460d8778f 100644 --- a/grub-core/kern/disk.c +++ b/grub-core/kern/disk.c @@ -442,6 +442,7 @@ grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector, } } + grub_free (tmp_buf); grub_errno = GRUB_ERR_NONE; { @@ -468,9 +469,11 @@ grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector, grub_error_push (); grub_dprintf ("disk", "%s read failed\n", disk->name); grub_error_pop (); + grub_free (tmp_buf); return grub_errno; } grub_memcpy (buf, tmp_buf + offset, size); + grub_free (tmp_buf); return GRUB_ERR_NONE; } } diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index be79463ce..c41e9968a 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -79,6 +79,22 @@ parse_dhcp_vendor (const char *name, void *vend, int limit) switch (tagtype) { + case 3: + if (taglength == 4) + { + grub_net_network_level_netaddress_t target; + grub_net_network_level_address_t gw; + char rname[grub_strlen (name) + sizeof (":default")]; + + target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + target.ipv4.base = 0; + target.ipv4.masksize = 0; + gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + grub_memcpy (&gw.ipv4, ptr, sizeof (gw.ipv4)); + grub_snprintf (rname, sizeof (rname), "%s:default", name); + grub_net_add_route_gw (rname, target, gw); + } + break; case 12: set_env_limn_ro (name, "hostname", (char *) ptr, taglength); break; diff --git a/grub-core/net/http.c b/grub-core/net/http.c index e4664757c..9f6628cb9 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -36,8 +36,6 @@ enum 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; @@ -47,18 +45,53 @@ typedef struct http_data char *filename; grub_err_t err; char *errmsg; + int chunked; + grub_size_t chunk_rem; + int in_chunk_len; } *http_data_t; +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 -parse_line (http_data_t data, char *ptr, grub_size_t len) +parse_line (grub_file_t file, http_data_t data, char *ptr, grub_size_t len) { char *end = ptr + len; while (end > ptr && *(end - 1) == '\r') end--; *end = 0; + /* Trailing CRLF. */ + if (data->in_chunk_len == 1) + { + data->in_chunk_len = 2; + return GRUB_ERR_NONE; + } + if (data->in_chunk_len == 2) + { + data->chunk_rem = grub_strtoul (ptr, 0, 16); + grub_errno = GRUB_ERR_NONE; + if (data->chunk_rem == 0) + { + file->device->net->eof = 1; + if (file->size == GRUB_FILE_SIZE_UNKNOWN) + file->size = have_ahead (file); + } + data->in_chunk_len = 0; + return GRUB_ERR_NONE; + } if (ptr == end) { data->headers_recv = 1; + if (data->chunked) + data->in_chunk_len = 2; return GRUB_ERR_NONE; } @@ -93,10 +126,17 @@ parse_line (http_data_t data, char *ptr, grub_size_t len) == 0 && !data->size_recv) { ptr += sizeof ("Content-Length: ") - 1; - data->file_size = grub_strtoull (ptr, &ptr, 10); + file->size = grub_strtoull (ptr, &ptr, 10); data->size_recv = 1; return GRUB_ERR_NONE; } + if (grub_memcmp (ptr, "Transfer-Encoding: chunked", + sizeof ("Transfer-Encoding: chunked") - 1) == 0) + { + data->chunked = 1; + return GRUB_ERR_NONE; + } + return GRUB_ERR_NONE; } @@ -113,6 +153,8 @@ http_err (grub_net_tcp_socket_t sock __attribute__ ((unused)), grub_free (data->current_line); grub_free (data); file->device->net->eof = 1; + if (file->size == GRUB_FILE_SIZE_UNKNOWN) + file->size = have_ahead (file); } static grub_err_t @@ -122,94 +164,116 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)), { 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) + while (1) { - int have_line = 1; - char *t; - ptr = grub_memchr (nb->data, '\n', nb->tail - nb->data); - if (ptr) - ptr++; - else + char *ptr = (char *) nb->data; + if ((!data->headers_recv || data->in_chunk_len) && data->current_line) { - 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, GRUB_NET_TCP_ABORT); - 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_NET_TCP_ABORT); - 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) + 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, GRUB_NET_TCP_ABORT); return grub_errno; } - data->current_line_len = (char *) nb->tail - ptr; - grub_memcpy (data->current_line, ptr, data->current_line_len); + + 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 (file, 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_NET_TCP_ABORT); + grub_netbuff_free (nb); + return err; + } + } + + while (ptr < (char *) nb->tail && (!data->headers_recv + || data->in_chunk_len)) + { + 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, GRUB_NET_TCP_ABORT); + 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 (file, data, ptr, ptr2 - ptr); + if (err) + { + grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); + grub_netbuff_free (nb); + return err; + } + ptr = ptr2 + 1; + } + + if (((char *) nb->tail - ptr) <= 0) + { grub_netbuff_free (nb); return GRUB_ERR_NONE; - } - err = parse_line (data, ptr, ptr2 - ptr); - if (err) - { - grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); - 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_NET_TCP_ABORT); grub_netbuff_free (nb); return err; - } - grub_net_put_packet (&file->device->net->packs, nb); + } + if (!(data->chunked && (grub_ssize_t) data->chunk_rem + < nb->tail - nb->data)) + { + grub_net_put_packet (&file->device->net->packs, nb); + if (data->chunked) + data->chunk_rem -= nb->tail - nb->data; + return GRUB_ERR_NONE; + } + if (data->chunk_rem) + { + struct grub_net_buff *nb2; + nb2 = grub_netbuff_alloc (data->chunk_rem); + if (!nb2) + return grub_errno; + grub_netbuff_put (nb2, data->chunk_rem); + grub_memcpy (nb2->data, nb->data, data->chunk_rem); + grub_net_put_packet (&file->device->net->packs, nb2); + grub_netbuff_pull (nb, data->chunk_rem); + } + data->in_chunk_len = 1; } - else - grub_netbuff_free (nb); - return GRUB_ERR_NONE; } static grub_err_t @@ -294,7 +358,7 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) "\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); + offset, file->size - 1, file->size); grub_netbuff_put (nb, grub_strlen ((char *) ptr)); } ptr = nb->tail; @@ -303,7 +367,7 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial) data->sock = grub_net_tcp_open (file->device->net->server, HTTP_PORT, http_receive, - http_err, + http_err, http_err, file); if (!data->sock) { @@ -351,13 +415,17 @@ http_seek (struct grub_file *file, grub_off_t off) 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); + { + grub_netbuff_free (file->device->net->packs.first->nb); + grub_net_remove_packet (file->device->net->packs.first); + } + + file->device->net->offset = off; 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) @@ -367,6 +435,7 @@ http_seek (struct grub_file *file, grub_off_t off) } grub_free (old_data); + file->data = data; err = http_establish (file, off, 0); if (err) { @@ -386,6 +455,7 @@ http_open (struct grub_file *file, const char *filename) data = grub_zalloc (sizeof (*data)); if (!data) return grub_errno; + file->size = GRUB_FILE_SIZE_UNKNOWN; data->filename = grub_strdup (filename); if (!data->filename) @@ -404,7 +474,6 @@ http_open (struct grub_file *file, const char *filename) grub_free (data); return err; } - file->size = data->file_size; return GRUB_ERR_NONE; } diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 2e8d9678e..fe713f05c 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -469,6 +469,8 @@ match_net (const grub_net_network_level_netaddress_t *net, case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: { grub_uint32_t mask = (0xffffffffU << (32 - net->ipv4.masksize)); + if (net->ipv4.masksize == 0) + mask = 0; return ((grub_be_to_cpu32 (net->ipv4.base) & mask) == (grub_be_to_cpu32 (addr->ipv4) & mask)); } @@ -554,6 +556,39 @@ grub_net_resolve_net_address (const char *name, name); } +static int +route_cmp (const struct grub_net_route *a, const struct grub_net_route *b) +{ + if (a == NULL && b == NULL) + return 0; + if (b == NULL) + return +1; + if (a == NULL) + return -1; + if (a->target.type < b->target.type) + return -1; + if (a->target.type > b->target.type) + return +1; + switch (a->target.type) + { + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV: + break; + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6: + if (a->target.ipv6.masksize > b->target.ipv6.masksize) + return +1; + if (a->target.ipv6.masksize < b->target.ipv6.masksize) + return -1; + break; + case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: + if (a->target.ipv4.masksize > b->target.ipv4.masksize) + return +1; + if (a->target.ipv4.masksize < b->target.ipv4.masksize) + return -1; + break; + } + return 0; +} + grub_err_t grub_net_route_address (grub_net_network_level_address_t addr, grub_net_network_level_address_t *gateway, @@ -572,25 +607,27 @@ grub_net_route_address (grub_net_network_level_address_t addr, for (depth = 0; depth < routecnt + 2; depth++) { + struct grub_net_route *bestroute = NULL; FOR_NET_ROUTES(route) { if (depth && prot != route->prot) continue; if (!match_net (&route->target, &curtarget)) continue; - - if (route->is_gateway) - { - if (depth == 0) - *gateway = route->gw; - curtarget = route->gw; - break; - } - *interf = route->interface; - return GRUB_ERR_NONE; + if (route_cmp (route, bestroute) > 0) + bestroute = route; } - if (route == NULL) + if (bestroute == NULL) return grub_error (GRUB_ERR_NET_NO_ROUTE, "destination unreachable"); + + if (!bestroute->is_gateway) + { + *interf = bestroute->interface; + return GRUB_ERR_NONE; + } + if (depth == 0) + *gateway = bestroute->gw; + curtarget = bestroute->gw; } return grub_error (GRUB_ERR_NET_ROUTE_LOOP, "route loop detected"); @@ -1285,6 +1322,7 @@ grub_net_fs_read_real (grub_file_t file, char *buf, grub_size_t len) char *ptr = buf; grub_size_t amount, total = 0; int try = 0; + while (try <= GRUB_NET_TRIES) { while (net->packs.first) diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index ae1dc57ed..579f94e99 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -68,6 +68,7 @@ struct grub_net_tcp_socket grub_err_t (*recv_hook) (grub_net_tcp_socket_t sock, struct grub_net_buff *nb, void *recv); void (*error_hook) (grub_net_tcp_socket_t sock, void *recv); + void (*fin_hook) (grub_net_tcp_socket_t sock, void *recv); void *hook_data; grub_net_network_level_address_t out_nla, gw; struct grub_net_network_level_interface *inf; @@ -234,10 +235,20 @@ grub_net_tcp_close (grub_net_tcp_socket_t sock, struct tcphdr *tcph_fin; grub_err_t err; - sock->i_closed = 1; - if (discard_received != GRUB_NET_TCP_CONTINUE_RECEIVING) - sock->recv_hook = NULL; + { + sock->recv_hook = NULL; + sock->error_hook = NULL; + sock->fin_hook = NULL; + } + + if (discard_received == GRUB_NET_TCP_ABORT) + sock->i_reseted = 1; + + if (sock->i_closed) + return; + + sock->i_closed = 1; nb_fin = grub_netbuff_alloc (sizeof (*tcph_fin) + GRUB_NET_OUR_MAX_IP_HEADER_SIZE @@ -263,13 +274,12 @@ grub_net_tcp_close (grub_net_tcp_socket_t sock, return; } tcph_fin = (void *) nb_fin->data; - tcph_fin->ack = grub_cpu_to_be32 (0); - tcph_fin->flags = grub_cpu_to_be16 ((5 << 12) | TCP_FIN); - tcph_fin->window = grub_cpu_to_be16 (0); + tcph_fin->ack = grub_cpu_to_be32 (sock->their_cur_seq); + tcph_fin->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_FIN + | TCP_ACK); + tcph_fin->window = grub_cpu_to_be16_compile_time (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); @@ -309,14 +319,14 @@ ack_real (grub_net_tcp_socket_t sock, int res) tcph_ack = (void *) nb_ack->data; 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); + tcph_ack->ack = grub_cpu_to_be32_compile_time (0); + tcph_ack->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_RST); + tcph_ack->window = grub_cpu_to_be16_compile_time (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->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK); tcph_ack->window = grub_cpu_to_be16 (sock->my_window); } tcph_ack->urgent = 0; @@ -354,6 +364,7 @@ grub_net_tcp_retransmit (void) struct unacked *unack; for (unack = sock->unack_first; unack; unack = unack->next) { + struct tcphdr *tcph; grub_uint8_t *nbd; grub_err_t err; @@ -368,6 +379,18 @@ grub_net_tcp_retransmit (void) unack->try_count++; unack->last_try = ctime; nbd = unack->nb->data; + tcph = (struct tcphdr *) nbd; + + if ((tcph->flags & grub_cpu_to_be16_compile_time (TCP_ACK)) + && tcph->ack != grub_cpu_to_be32 (sock->their_cur_seq)) + { + tcph->checksum = 0; + tcph->checksum = grub_net_ip_transport_checksum (unack->nb, + GRUB_NET_IP_TCP, + &sock->inf->address, + &sock->out_nla); + } + err = grub_net_send_ip_packet (sock->inf, &(sock->out_nla), &(sock->gw), unack->nb, GRUB_NET_IP_TCP); @@ -447,7 +470,10 @@ destroy_pq (grub_net_tcp_socket_t sock) { struct grub_net_buff **nb_p; while ((nb_p = grub_priority_queue_top (sock->pq))) - grub_netbuff_free (*nb_p); + { + grub_netbuff_free (*nb_p); + grub_priority_queue_pop (sock->pq); + } grub_priority_queue_destroy (sock->pq); } @@ -459,6 +485,8 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock, void *data), void (*error_hook) (grub_net_tcp_socket_t sock, void *data), + void (*fin_hook) (grub_net_tcp_socket_t sock, + void *data), void *hook_data) { struct grub_net_buff *nb_ack; @@ -467,6 +495,7 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock, sock->recv_hook = recv_hook; sock->error_hook = error_hook; + sock->fin_hook = fin_hook; sock->hook_data = hook_data; nb_ack = grub_netbuff_alloc (sizeof (*tcph) + GRUB_NET_OUR_MAX_IP_HEADER_SIZE @@ -489,7 +518,7 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock, } tcph = (void *) nb_ack->data; tcph->ack = grub_cpu_to_be32 (sock->their_cur_seq); - tcph->flags = grub_cpu_to_be16 ((5 << 12) | TCP_SYN | TCP_ACK); + tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_SYN | TCP_ACK); tcph->window = grub_cpu_to_be16 (sock->my_window); tcph->urgent = 0; sock->established = 1; @@ -509,6 +538,8 @@ grub_net_tcp_open (char *server, void *data), void (*error_hook) (grub_net_tcp_socket_t sock, void *data), + void (*fin_hook) (grub_net_tcp_socket_t sock, + void *data), void *hook_data) { grub_err_t err; @@ -548,6 +579,7 @@ grub_net_tcp_open (char *server, socket->in_port = in_port++; socket->recv_hook = recv_hook; socket->error_hook = error_hook; + socket->fin_hook = fin_hook; socket->hook_data = hook_data; nb = grub_netbuff_alloc (sizeof (*tcph) + 128); @@ -578,8 +610,8 @@ grub_net_tcp_open (char *server, socket->my_cur_seq = socket->my_start_seq + 1; socket->my_window = 8192; tcph->seqnr = grub_cpu_to_be32 (socket->my_start_seq); - tcph->ack = grub_cpu_to_be32 (0); - tcph->flags = grub_cpu_to_be16 ((5 << 12) | TCP_SYN); + tcph->ack = grub_cpu_to_be32_compile_time (0); + tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_SYN); tcph->window = grub_cpu_to_be16 (socket->my_window); tcph->urgent = 0; tcph->src = grub_cpu_to_be16 (socket->in_port); @@ -664,8 +696,8 @@ grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, return err; tcph = (struct tcphdr *) nb2->data; - tcph->ack = grub_cpu_to_be32 (0); - tcph->flags = grub_cpu_to_be16 ((5 << 12)); + tcph->ack = grub_cpu_to_be32 (socket->their_cur_seq); + tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK); tcph->window = grub_cpu_to_be16 (socket->my_window); tcph->urgent = 0; err = grub_netbuff_put (nb2, fraglen); @@ -686,8 +718,9 @@ grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket, return err; tcph = (struct tcphdr *) nb->data; - tcph->ack = grub_cpu_to_be32 (0); - tcph->flags = grub_cpu_to_be16 ((5 << 12) | (push ? TCP_PUSH : 0)); + tcph->ack = grub_cpu_to_be32 (socket->their_cur_seq); + tcph->flags = (grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK) + | (push ? grub_cpu_to_be16_compile_time (TCP_PUSH) : 0)); tcph->window = grub_cpu_to_be16 (socket->my_window); tcph->urgent = 0; return tcp_send (nb, socket); @@ -815,11 +848,15 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, err = grub_priority_queue_push (sock->pq, &nb); if (err) - return err; + { + grub_netbuff_free (nb); + return err; + } { struct grub_net_buff **nb_top_p, *nb_top; int do_ack = 0; + int just_closed = 0; while (1) { nb_top_p = grub_priority_queue_top (sock->pq); @@ -829,6 +866,7 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, tcph = (struct tcphdr *) nb_top->data; if (grub_be_to_cpu32 (tcph->seqnr) >= sock->their_cur_seq) break; + grub_netbuff_free (nb_top); grub_priority_queue_pop (sock->pq); } if (grub_be_to_cpu32 (tcph->seqnr) != sock->their_cur_seq) @@ -845,15 +883,19 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, break; grub_priority_queue_pop (sock->pq); - err = grub_netbuff_pull (nb, (grub_be_to_cpu16 (tcph->flags) - >> 12) * sizeof (grub_uint32_t)); + err = grub_netbuff_pull (nb_top, (grub_be_to_cpu16 (tcph->flags) + >> 12) * sizeof (grub_uint32_t)); if (err) - return err; + { + grub_netbuff_free (nb_top); + return err; + } sock->their_cur_seq += (nb_top->tail - nb_top->data); if (grub_be_to_cpu16 (tcph->flags) & TCP_FIN) { sock->they_closed = 1; + just_closed = 1; sock->their_cur_seq++; do_ack = 1; } @@ -868,14 +910,19 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb, } if (do_ack) ack (sock); + while (sock->packs.first) + { + nb = sock->packs.first->nb; + if (sock->recv_hook) + sock->recv_hook (sock, sock->packs.first->nb, sock->hook_data); + else + grub_netbuff_free (nb); + grub_net_remove_packet (sock->packs.first); + } + + if (sock->fin_hook && just_closed) + sock->fin_hook (sock, sock->hook_data); } - while (sock->packs.first) - { - nb = sock->packs.first->nb; - if (sock->recv_hook) - sock->recv_hook (sock, sock->packs.first->nb, sock->hook_data); - grub_net_remove_packet (sock->packs.first); - } return GRUB_ERR_NONE; } diff --git a/include/grub/net/tcp.h b/include/grub/net/tcp.h index b25ceff1b..62bfd2eba 100644 --- a/include/grub/net/tcp.h +++ b/include/grub/net/tcp.h @@ -35,6 +35,8 @@ grub_net_tcp_open (char *server, void *data), void (*error_hook) (grub_net_tcp_socket_t sock, void *data), + void (*fin_hook) (grub_net_tcp_socket_t sock, + void *data), void *hook_data); grub_net_tcp_listen_t @@ -70,6 +72,8 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock, void *data), void (*error_hook) (grub_net_tcp_socket_t sock, void *data), + void (*fin_hook) (grub_net_tcp_socket_t sock, + void *data), void *hook_data); #endif diff --git a/include/grub/types.h b/include/grub/types.h index 61cfb0291..68f28d01d 100644 --- a/include/grub/types.h +++ b/include/grub/types.h @@ -214,6 +214,7 @@ static inline grub_uint64_t grub_swap_bytes64(grub_uint64_t x) # define grub_be_to_cpu16(x) ((grub_uint16_t) (x)) # define grub_be_to_cpu32(x) ((grub_uint32_t) (x)) # define grub_be_to_cpu64(x) ((grub_uint64_t) (x)) +# define grub_cpu_to_be16_compile_time(x) ((grub_uint16_t) (x)) # define grub_cpu_to_be32_compile_time(x) ((grub_uint32_t) (x)) # define grub_cpu_to_be64_compile_time(x) ((grub_uint64_t) (x)) # define grub_be_to_cpu64_compile_time(x) ((grub_uint64_t) (x)) @@ -232,6 +233,7 @@ static inline grub_uint64_t grub_swap_bytes64(grub_uint64_t x) # define grub_be_to_cpu16(x) grub_swap_bytes16(x) # define grub_be_to_cpu32(x) grub_swap_bytes32(x) # define grub_be_to_cpu64(x) grub_swap_bytes64(x) +# define grub_cpu_to_be16_compile_time(x) grub_swap_bytes16_compile_time(x) # define grub_cpu_to_be32_compile_time(x) grub_swap_bytes32_compile_time(x) # define grub_cpu_to_be64_compile_time(x) grub_swap_bytes64_compile_time(x) # define grub_be_to_cpu64_compile_time(x) grub_swap_bytes64_compile_time(x)