]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
several net bugfixes and improvements and fix some memory leaks
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Thu, 13 Oct 2011 16:31:53 +0000 (18:31 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Thu, 13 Oct 2011 16:31:53 +0000 (18:31 +0200)
grub-core/io/bufio.c
grub-core/kern/disk.c
grub-core/net/bootp.c
grub-core/net/http.c
grub-core/net/net.c
grub-core/net/tcp.c
include/grub/net/tcp.h
include/grub/types.h

index adb38af5617bb4ecc7b1e2c48db82f3c2038f21b..0d01e62f939059327097a9418f58ee9c5709690a 100644 (file)
@@ -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,82 +106,86 @@ 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;
-    }
-  else
-    {
-      bufio->file->offset = grub_divmod64 (file->offset, bufio->block_size,
-                                           &pos);
-      bufio->file->offset *= bufio->block_size;
     }
+  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;
 
-  if (pos + len >= bufio->block_size)
+  /* Now read between file->offset + res and bufio->buffer_at.  */
+  if (file->offset + res < bufio->buffer_at)
     {
-      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;
-        }
+      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;
+       }
     }
 
-  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);
+  /* 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;
 
-  grub_memcpy (buf, &bufio->buffer[pos], len);
+  if (len < bufio->buffer_len)
+    {
+      grub_memcpy (buf, &bufio->buffer[0], len);
+      res += len;
+    }
+  else
+    {
+      grub_memcpy (buf, &bufio->buffer[0], bufio->buffer_len);
+      res += bufio->buffer_len;
+    }
 
   return res;
 }
index f296b9d0faabb30a00bf62467be86f573f54ff12..460d8778f39916d90a74287c83c34bc89ef7f233 100644 (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;
 
   {
@@ -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;
   }
 }
index be79463ce46114f6bfbb368f571a1f0fd0eb7f0c..c41e9968ab8abb374bc6e4789bd531503c29808c 100644 (file)
@@ -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;
index e4664757c1d6d63df22377d8544b83450c716323..9f6628cb95540430b23046e0602925afd34d0864 100644 (file)
@@ -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
-       {
-         have_line = 0;
-         ptr = (char *) nb->tail;
-       }
-      t = grub_realloc (data->current_line,
-                       data->current_line_len + (ptr - (char *) nb->data));
-      if (!t)
+      char *ptr = (char *) nb->data;
+      if ((!data->headers_recv || data->in_chunk_len) && data->current_line)
        {
-         grub_netbuff_free (nb);
-         grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
-         return grub_errno;
-       }
+         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 = 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;
+         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)
-    {
-      char *ptr2;
-      ptr2 = grub_memchr (ptr, '\n', (char *) nb->tail - ptr);
-      if (!ptr2)
+      while (ptr < (char *) nb->tail && (!data->headers_recv
+                                        || data->in_chunk_len))
        {
-         data->current_line = grub_malloc ((char *) nb->tail - ptr);
-         if (!data->current_line)
+         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);
-             return grub_errno;
+             grub_netbuff_free (nb);
+             return err;
            }
-         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;
+         ptr = ptr2 + 1;
        }
-      err = parse_line (data, ptr, ptr2 - ptr);
-      if (err)
+
+      if (((char *) nb->tail - ptr) <= 0)
        {
-         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);
+         return GRUB_ERR_NONE;
+       } 
       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;
 }
index 2e8d9678ed7e7e46f867a55729d3d433ab9b8dd5..fe713f05c007176d3454d159ade1c025363843f7 100644 (file)
@@ -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)
index ae1dc57edabf91ef76062dc69814b5b924a75e99..579f94e993e2374a102f4d54588c4bfe251c831c 100644 (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,
                           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;
   }
index b25ceff1bf53d5aba402aca9ac2e571891e0cdbd..62bfd2eba17cb9d7672aa0d0235af25280307029 100644 (file)
@@ -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
index 61cfb0291ed88fc91415351fb27360128473bfc5..68f28d01d8500f63bfead58f1a5a2c9138f14f67 100644 (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_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)