several net bugfixes and improvements and fix some memory leaks

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2011-10-13 18:31:53 +02:00
parent da1b289afc
commit bd40efbf0b
8 changed files with 365 additions and 180 deletions

View file

@ -35,6 +35,7 @@ struct grub_bufio
grub_file_t file; grub_file_t file;
grub_size_t block_size; grub_size_t block_size;
grub_size_t buffer_len; grub_size_t buffer_len;
grub_off_t buffer_at;
char buffer[0]; char buffer[0];
}; };
typedef struct grub_bufio *grub_bufio_t; typedef struct grub_bufio *grub_bufio_t;
@ -70,6 +71,7 @@ grub_bufio_open (grub_file_t io, int size)
bufio->file = io; bufio->file = io;
bufio->block_size = size; bufio->block_size = size;
bufio->buffer_len = 0; bufio->buffer_len = 0;
bufio->buffer_at = 0;
file->device = io->device; file->device = io->device;
file->offset = 0; file->offset = 0;
@ -104,83 +106,87 @@ grub_buffile_open (const char *name, int size)
static grub_ssize_t static grub_ssize_t
grub_bufio_read (grub_file_t file, char *buf, grub_size_t len) 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_bufio_t bufio = file->data;
grub_uint64_t pos;
if ((file->offset >= bufio->file->offset) && if (file->size == GRUB_FILE_SIZE_UNKNOWN)
(file->offset < bufio->file->offset + bufio->buffer_len)) 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_size_t n;
grub_uint64_t pos;
pos = file->offset - bufio->file->offset; pos = file->offset - bufio->buffer_at;
n = bufio->buffer_len - pos; n = bufio->buffer_len - pos;
if (n > len) if (n > len)
n = len; n = len;
grub_memcpy (buf, &bufio->buffer[pos], n); grub_memcpy (buf, &bufio->buffer[pos], n);
len -= n; len -= n;
if (! len) res += n;
return res;
buf += 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 else
{ {
bufio->file->offset = grub_divmod64 (file->offset, bufio->block_size, grub_memcpy (buf, &bufio->buffer[0], bufio->buffer_len);
&pos); res += bufio->buffer_len;
bufio->file->offset *= bufio->block_size;
} }
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; return res;
} }

View file

@ -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; 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_error_push ();
grub_dprintf ("disk", "%s read failed\n", disk->name); grub_dprintf ("disk", "%s read failed\n", disk->name);
grub_error_pop (); grub_error_pop ();
grub_free (tmp_buf);
return grub_errno; return grub_errno;
} }
grub_memcpy (buf, tmp_buf + offset, size); grub_memcpy (buf, tmp_buf + offset, size);
grub_free (tmp_buf);
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }
} }

View file

@ -79,6 +79,22 @@ parse_dhcp_vendor (const char *name, void *vend, int limit)
switch (tagtype) 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: case 12:
set_env_limn_ro (name, "hostname", (char *) ptr, taglength); set_env_limn_ro (name, "hostname", (char *) ptr, taglength);
break; break;

View file

@ -36,8 +36,6 @@ enum
typedef struct http_data typedef struct http_data
{ {
grub_uint64_t file_size;
grub_uint64_t position;
char *current_line; char *current_line;
grub_size_t current_line_len; grub_size_t current_line_len;
int headers_recv; int headers_recv;
@ -47,18 +45,53 @@ typedef struct http_data
char *filename; char *filename;
grub_err_t err; grub_err_t err;
char *errmsg; char *errmsg;
int chunked;
grub_size_t chunk_rem;
int in_chunk_len;
} *http_data_t; } *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 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; char *end = ptr + len;
while (end > ptr && *(end - 1) == '\r') while (end > ptr && *(end - 1) == '\r')
end--; end--;
*end = 0; *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) if (ptr == end)
{ {
data->headers_recv = 1; data->headers_recv = 1;
if (data->chunked)
data->in_chunk_len = 2;
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }
@ -93,10 +126,17 @@ parse_line (http_data_t data, char *ptr, grub_size_t len)
== 0 && !data->size_recv) == 0 && !data->size_recv)
{ {
ptr += sizeof ("Content-Length: ") - 1; ptr += sizeof ("Content-Length: ") - 1;
data->file_size = grub_strtoull (ptr, &ptr, 10); file->size = grub_strtoull (ptr, &ptr, 10);
data->size_recv = 1; data->size_recv = 1;
return GRUB_ERR_NONE; 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; 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->current_line);
grub_free (data); grub_free (data);
file->device->net->eof = 1; file->device->net->eof = 1;
if (file->size == GRUB_FILE_SIZE_UNKNOWN)
file->size = have_ahead (file);
} }
static grub_err_t static grub_err_t
@ -122,94 +164,116 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)),
{ {
grub_file_t file = f; grub_file_t file = f;
http_data_t data = file->data; http_data_t data = file->data;
char *ptr = (char *) nb->data;
grub_err_t err; grub_err_t err;
if (!data->headers_recv && data->current_line) while (1)
{ {
int have_line = 1; char *ptr = (char *) nb->data;
char *t; if ((!data->headers_recv || data->in_chunk_len) && data->current_line)
ptr = grub_memchr (nb->data, '\n', nb->tail - nb->data);
if (ptr)
ptr++;
else
{ {
have_line = 0; int have_line = 1;
ptr = (char *) nb->tail; char *t;
} ptr = grub_memchr (nb->data, '\n', nb->tail - nb->data);
t = grub_realloc (data->current_line, if (ptr)
data->current_line_len + (ptr - (char *) nb->data)); ptr++;
if (!t) else
{ {
grub_netbuff_free (nb); have_line = 0;
grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); ptr = (char *) nb->tail;
return grub_errno; }
} t = grub_realloc (data->current_line,
data->current_line_len + (ptr - (char *) nb->data));
data->current_line = t; if (!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)
{ {
grub_netbuff_free (nb); grub_netbuff_free (nb);
grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
return grub_errno; 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); grub_netbuff_free (nb);
return GRUB_ERR_NONE; 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); err = grub_netbuff_pull (nb, ptr - (char *) nb->data);
if (err) if (err)
{ {
grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT); grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
grub_netbuff_free (nb); grub_netbuff_free (nb);
return err; 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 static grub_err_t
@ -294,7 +358,7 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial)
"\r\n"), "\r\n"),
"Content-Range: bytes %" PRIuGRUB_UINT64_T "-%" "Content-Range: bytes %" PRIuGRUB_UINT64_T "-%"
PRIuGRUB_UINT64_T "/%" PRIuGRUB_UINT64_T "\r\n\r\n", 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)); grub_netbuff_put (nb, grub_strlen ((char *) ptr));
} }
ptr = nb->tail; 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, data->sock = grub_net_tcp_open (file->device->net->server,
HTTP_PORT, http_receive, HTTP_PORT, http_receive,
http_err, http_err, http_err,
file); file);
if (!data->sock) 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); grub_net_tcp_close (old_data->sock, GRUB_NET_TCP_ABORT);
while (file->device->net->packs.first) 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)); data = grub_zalloc (sizeof (*data));
if (!data) if (!data)
return grub_errno; return grub_errno;
data->file_size = old_data->file_size;
data->size_recv = 1; data->size_recv = 1;
data->filename = old_data->filename; data->filename = old_data->filename;
if (!data->filename) if (!data->filename)
@ -367,6 +435,7 @@ http_seek (struct grub_file *file, grub_off_t off)
} }
grub_free (old_data); grub_free (old_data);
file->data = data;
err = http_establish (file, off, 0); err = http_establish (file, off, 0);
if (err) if (err)
{ {
@ -386,6 +455,7 @@ http_open (struct grub_file *file, const char *filename)
data = grub_zalloc (sizeof (*data)); data = grub_zalloc (sizeof (*data));
if (!data) if (!data)
return grub_errno; return grub_errno;
file->size = GRUB_FILE_SIZE_UNKNOWN;
data->filename = grub_strdup (filename); data->filename = grub_strdup (filename);
if (!data->filename) if (!data->filename)
@ -404,7 +474,6 @@ http_open (struct grub_file *file, const char *filename)
grub_free (data); grub_free (data);
return err; return err;
} }
file->size = data->file_size;
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }

View file

@ -469,6 +469,8 @@ match_net (const grub_net_network_level_netaddress_t *net,
case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4: case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4:
{ {
grub_uint32_t mask = (0xffffffffU << (32 - net->ipv4.masksize)); 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) return ((grub_be_to_cpu32 (net->ipv4.base) & mask)
== (grub_be_to_cpu32 (addr->ipv4) & mask)); == (grub_be_to_cpu32 (addr->ipv4) & mask));
} }
@ -554,6 +556,39 @@ grub_net_resolve_net_address (const char *name,
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_err_t
grub_net_route_address (grub_net_network_level_address_t addr, grub_net_route_address (grub_net_network_level_address_t addr,
grub_net_network_level_address_t *gateway, 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++) for (depth = 0; depth < routecnt + 2; depth++)
{ {
struct grub_net_route *bestroute = NULL;
FOR_NET_ROUTES(route) FOR_NET_ROUTES(route)
{ {
if (depth && prot != route->prot) if (depth && prot != route->prot)
continue; continue;
if (!match_net (&route->target, &curtarget)) if (!match_net (&route->target, &curtarget))
continue; continue;
if (route_cmp (route, bestroute) > 0)
if (route->is_gateway) bestroute = route;
{
if (depth == 0)
*gateway = route->gw;
curtarget = route->gw;
break;
}
*interf = route->interface;
return GRUB_ERR_NONE;
} }
if (route == NULL) if (bestroute == NULL)
return grub_error (GRUB_ERR_NET_NO_ROUTE, "destination unreachable"); 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"); 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; char *ptr = buf;
grub_size_t amount, total = 0; grub_size_t amount, total = 0;
int try = 0; int try = 0;
while (try <= GRUB_NET_TRIES) while (try <= GRUB_NET_TRIES)
{ {
while (net->packs.first) while (net->packs.first)

View file

@ -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, grub_err_t (*recv_hook) (grub_net_tcp_socket_t sock, struct grub_net_buff *nb,
void *recv); void *recv);
void (*error_hook) (grub_net_tcp_socket_t sock, 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; void *hook_data;
grub_net_network_level_address_t out_nla, gw; grub_net_network_level_address_t out_nla, gw;
struct grub_net_network_level_interface *inf; 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; struct tcphdr *tcph_fin;
grub_err_t err; grub_err_t err;
sock->i_closed = 1;
if (discard_received != GRUB_NET_TCP_CONTINUE_RECEIVING) 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) nb_fin = grub_netbuff_alloc (sizeof (*tcph_fin)
+ GRUB_NET_OUR_MAX_IP_HEADER_SIZE + GRUB_NET_OUR_MAX_IP_HEADER_SIZE
@ -263,13 +274,12 @@ grub_net_tcp_close (grub_net_tcp_socket_t sock,
return; return;
} }
tcph_fin = (void *) nb_fin->data; tcph_fin = (void *) nb_fin->data;
tcph_fin->ack = grub_cpu_to_be32 (0); tcph_fin->ack = grub_cpu_to_be32 (sock->their_cur_seq);
tcph_fin->flags = grub_cpu_to_be16 ((5 << 12) | TCP_FIN); tcph_fin->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_FIN
tcph_fin->window = grub_cpu_to_be16 (0); | TCP_ACK);
tcph_fin->window = grub_cpu_to_be16_compile_time (0);
tcph_fin->urgent = 0; tcph_fin->urgent = 0;
err = tcp_send (nb_fin, sock); err = tcp_send (nb_fin, sock);
if (discard_received == GRUB_NET_TCP_ABORT)
sock->i_reseted = 1;
if (err) if (err)
{ {
grub_netbuff_free (nb_fin); 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; tcph_ack = (void *) nb_ack->data;
if (res) if (res)
{ {
tcph_ack->ack = grub_cpu_to_be32 (0); tcph_ack->ack = grub_cpu_to_be32_compile_time (0);
tcph_ack->flags = grub_cpu_to_be16 ((5 << 12) | TCP_RST); tcph_ack->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_RST);
tcph_ack->window = grub_cpu_to_be16 (0); tcph_ack->window = grub_cpu_to_be16_compile_time (0);
} }
else else
{ {
tcph_ack->ack = grub_cpu_to_be32 (sock->their_cur_seq); 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->window = grub_cpu_to_be16 (sock->my_window);
} }
tcph_ack->urgent = 0; tcph_ack->urgent = 0;
@ -354,6 +364,7 @@ grub_net_tcp_retransmit (void)
struct unacked *unack; struct unacked *unack;
for (unack = sock->unack_first; unack; unack = unack->next) for (unack = sock->unack_first; unack; unack = unack->next)
{ {
struct tcphdr *tcph;
grub_uint8_t *nbd; grub_uint8_t *nbd;
grub_err_t err; grub_err_t err;
@ -368,6 +379,18 @@ grub_net_tcp_retransmit (void)
unack->try_count++; unack->try_count++;
unack->last_try = ctime; unack->last_try = ctime;
nbd = unack->nb->data; 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), err = grub_net_send_ip_packet (sock->inf, &(sock->out_nla),
&(sock->gw), unack->nb, &(sock->gw), unack->nb,
GRUB_NET_IP_TCP); GRUB_NET_IP_TCP);
@ -447,7 +470,10 @@ destroy_pq (grub_net_tcp_socket_t sock)
{ {
struct grub_net_buff **nb_p; struct grub_net_buff **nb_p;
while ((nb_p = grub_priority_queue_top (sock->pq))) 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); grub_priority_queue_destroy (sock->pq);
} }
@ -459,6 +485,8 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock,
void *data), void *data),
void (*error_hook) (grub_net_tcp_socket_t sock, void (*error_hook) (grub_net_tcp_socket_t sock,
void *data), void *data),
void (*fin_hook) (grub_net_tcp_socket_t sock,
void *data),
void *hook_data) void *hook_data)
{ {
struct grub_net_buff *nb_ack; 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->recv_hook = recv_hook;
sock->error_hook = error_hook; sock->error_hook = error_hook;
sock->fin_hook = fin_hook;
sock->hook_data = hook_data; sock->hook_data = hook_data;
nb_ack = grub_netbuff_alloc (sizeof (*tcph) nb_ack = grub_netbuff_alloc (sizeof (*tcph)
+ GRUB_NET_OUR_MAX_IP_HEADER_SIZE + 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 = (void *) nb_ack->data;
tcph->ack = grub_cpu_to_be32 (sock->their_cur_seq); 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->window = grub_cpu_to_be16 (sock->my_window);
tcph->urgent = 0; tcph->urgent = 0;
sock->established = 1; sock->established = 1;
@ -509,6 +538,8 @@ grub_net_tcp_open (char *server,
void *data), void *data),
void (*error_hook) (grub_net_tcp_socket_t sock, void (*error_hook) (grub_net_tcp_socket_t sock,
void *data), void *data),
void (*fin_hook) (grub_net_tcp_socket_t sock,
void *data),
void *hook_data) void *hook_data)
{ {
grub_err_t err; grub_err_t err;
@ -548,6 +579,7 @@ grub_net_tcp_open (char *server,
socket->in_port = in_port++; socket->in_port = in_port++;
socket->recv_hook = recv_hook; socket->recv_hook = recv_hook;
socket->error_hook = error_hook; socket->error_hook = error_hook;
socket->fin_hook = fin_hook;
socket->hook_data = hook_data; socket->hook_data = hook_data;
nb = grub_netbuff_alloc (sizeof (*tcph) + 128); 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_cur_seq = socket->my_start_seq + 1;
socket->my_window = 8192; socket->my_window = 8192;
tcph->seqnr = grub_cpu_to_be32 (socket->my_start_seq); tcph->seqnr = grub_cpu_to_be32 (socket->my_start_seq);
tcph->ack = grub_cpu_to_be32 (0); tcph->ack = grub_cpu_to_be32_compile_time (0);
tcph->flags = grub_cpu_to_be16 ((5 << 12) | TCP_SYN); tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_SYN);
tcph->window = grub_cpu_to_be16 (socket->my_window); tcph->window = grub_cpu_to_be16 (socket->my_window);
tcph->urgent = 0; tcph->urgent = 0;
tcph->src = grub_cpu_to_be16 (socket->in_port); 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; return err;
tcph = (struct tcphdr *) nb2->data; tcph = (struct tcphdr *) nb2->data;
tcph->ack = grub_cpu_to_be32 (0); tcph->ack = grub_cpu_to_be32 (socket->their_cur_seq);
tcph->flags = grub_cpu_to_be16 ((5 << 12)); tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_ACK);
tcph->window = grub_cpu_to_be16 (socket->my_window); tcph->window = grub_cpu_to_be16 (socket->my_window);
tcph->urgent = 0; tcph->urgent = 0;
err = grub_netbuff_put (nb2, fraglen); err = grub_netbuff_put (nb2, fraglen);
@ -686,8 +718,9 @@ grub_net_send_tcp_packet (const grub_net_tcp_socket_t socket,
return err; return err;
tcph = (struct tcphdr *) nb->data; tcph = (struct tcphdr *) nb->data;
tcph->ack = grub_cpu_to_be32 (0); tcph->ack = grub_cpu_to_be32 (socket->their_cur_seq);
tcph->flags = grub_cpu_to_be16 ((5 << 12) | (push ? TCP_PUSH : 0)); 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->window = grub_cpu_to_be16 (socket->my_window);
tcph->urgent = 0; tcph->urgent = 0;
return tcp_send (nb, socket); 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); err = grub_priority_queue_push (sock->pq, &nb);
if (err) if (err)
return err; {
grub_netbuff_free (nb);
return err;
}
{ {
struct grub_net_buff **nb_top_p, *nb_top; struct grub_net_buff **nb_top_p, *nb_top;
int do_ack = 0; int do_ack = 0;
int just_closed = 0;
while (1) while (1)
{ {
nb_top_p = grub_priority_queue_top (sock->pq); 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; tcph = (struct tcphdr *) nb_top->data;
if (grub_be_to_cpu32 (tcph->seqnr) >= sock->their_cur_seq) if (grub_be_to_cpu32 (tcph->seqnr) >= sock->their_cur_seq)
break; break;
grub_netbuff_free (nb_top);
grub_priority_queue_pop (sock->pq); grub_priority_queue_pop (sock->pq);
} }
if (grub_be_to_cpu32 (tcph->seqnr) != sock->their_cur_seq) 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; break;
grub_priority_queue_pop (sock->pq); grub_priority_queue_pop (sock->pq);
err = grub_netbuff_pull (nb, (grub_be_to_cpu16 (tcph->flags) err = grub_netbuff_pull (nb_top, (grub_be_to_cpu16 (tcph->flags)
>> 12) * sizeof (grub_uint32_t)); >> 12) * sizeof (grub_uint32_t));
if (err) if (err)
return err; {
grub_netbuff_free (nb_top);
return err;
}
sock->their_cur_seq += (nb_top->tail - nb_top->data); sock->their_cur_seq += (nb_top->tail - nb_top->data);
if (grub_be_to_cpu16 (tcph->flags) & TCP_FIN) if (grub_be_to_cpu16 (tcph->flags) & TCP_FIN)
{ {
sock->they_closed = 1; sock->they_closed = 1;
just_closed = 1;
sock->their_cur_seq++; sock->their_cur_seq++;
do_ack = 1; do_ack = 1;
} }
@ -868,14 +910,19 @@ grub_net_recv_tcp_packet (struct grub_net_buff *nb,
} }
if (do_ack) if (do_ack)
ack (sock); 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; return GRUB_ERR_NONE;
} }

View file

@ -35,6 +35,8 @@ grub_net_tcp_open (char *server,
void *data), void *data),
void (*error_hook) (grub_net_tcp_socket_t sock, void (*error_hook) (grub_net_tcp_socket_t sock,
void *data), void *data),
void (*fin_hook) (grub_net_tcp_socket_t sock,
void *data),
void *hook_data); void *hook_data);
grub_net_tcp_listen_t grub_net_tcp_listen_t
@ -70,6 +72,8 @@ grub_net_tcp_accept (grub_net_tcp_socket_t sock,
void *data), void *data),
void (*error_hook) (grub_net_tcp_socket_t sock, void (*error_hook) (grub_net_tcp_socket_t sock,
void *data), void *data),
void (*fin_hook) (grub_net_tcp_socket_t sock,
void *data),
void *hook_data); void *hook_data);
#endif #endif

View file

@ -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_cpu16(x) ((grub_uint16_t) (x))
# define grub_be_to_cpu32(x) ((grub_uint32_t) (x)) # define grub_be_to_cpu32(x) ((grub_uint32_t) (x))
# define grub_be_to_cpu64(x) ((grub_uint64_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_be32_compile_time(x) ((grub_uint32_t) (x))
# define grub_cpu_to_be64_compile_time(x) ((grub_uint64_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)) # 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_cpu16(x) grub_swap_bytes16(x)
# define grub_be_to_cpu32(x) grub_swap_bytes32(x) # define grub_be_to_cpu32(x) grub_swap_bytes32(x)
# define grub_be_to_cpu64(x) grub_swap_bytes64(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_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_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) # define grub_be_to_cpu64_compile_time(x) grub_swap_bytes64_compile_time(x)