示例#1
0
文件: tftp.c 项目: Der-Jan/grub-zfs
static grub_err_t
ack (tftp_data_t data, grub_uint64_t block)
{
  struct tftphdr *tftph_ack;
  grub_uint8_t nbdata[512];
  struct grub_net_buff nb_ack;
  grub_err_t err;

  nb_ack.head = nbdata;
  nb_ack.end = nbdata + sizeof (nbdata);
  grub_netbuff_clear (&nb_ack);
  grub_netbuff_reserve (&nb_ack, 512);
  err = grub_netbuff_push (&nb_ack, sizeof (tftph_ack->opcode)
			   + sizeof (tftph_ack->u.ack.block));
  if (err)
    return err;

  tftph_ack = (struct tftphdr *) nb_ack.data;
  tftph_ack->opcode = grub_cpu_to_be16 (TFTP_ACK);
  tftph_ack->u.ack.block = grub_cpu_to_be16 (block);

  err = grub_net_send_udp_packet (data->sock, &nb_ack);
  if (err)
    return err;
  data->ack_sent = block;
  return GRUB_ERR_NONE;
}
示例#2
0
文件: scsi.c 项目: Spacy/grub-fuse
/* Send a SCSI request for DISK: write the data stored in BUF to SIZE
   sectors starting with SECTOR.  */
static grub_err_t
grub_scsi_write10 (grub_disk_t disk, grub_disk_addr_t sector,
		   grub_size_t size, char *buf)
{
  grub_scsi_t scsi;
  struct grub_scsi_write10 wr;
  grub_err_t err;
  grub_err_t err_sense;

  scsi = disk->data;

  wr.opcode = grub_scsi_cmd_write10;
  wr.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
  wr.lba = grub_cpu_to_be32 (sector);
  wr.reserved = 0;
  wr.size = grub_cpu_to_be16 (size);
  wr.reserved2 = 0;
  wr.pad = 0;

  err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);

  /* Each SCSI command should be followed by Request Sense.
     If not so, many devices STALLs or definitely freezes. */
  err_sense = grub_scsi_request_sense (scsi);
  if (err_sense != GRUB_ERR_NONE)
  	grub_errno = err;
  /* err_sense is ignored for now and Request Sense Data also... */

  return err;
}
示例#3
0
文件: udp.c 项目: pendor/grub-zfs
grub_err_t
grub_net_send_udp_packet (const grub_net_socket_t socket,
			  struct grub_net_buff *nb)
{
  struct udphdr *udph;
  grub_err_t err;

  err = grub_netbuff_push (nb, sizeof (*udph));
  if (err)
    return err;

  udph = (struct udphdr *) nb->data;
  udph->src = grub_cpu_to_be16 (socket->x_in_port);
  udph->dst = grub_cpu_to_be16 (socket->x_out_port);

  /* No chechksum. */
  udph->chksum = 0;
  udph->len = grub_cpu_to_be16 (nb->tail - nb->data);

  return grub_net_send_ip_packet (socket->x_inf, &(socket->x_out_nla), nb);
}
示例#4
0
文件: tftp.c 项目: Der-Jan/grub-zfs
static grub_err_t
tftp_close (struct grub_file *file)
{
  tftp_data_t data = file->data;

  if (data->sock)
    {
      grub_uint8_t nbdata[512];
      grub_err_t err;
      struct grub_net_buff nb_err;
      struct tftphdr *tftph;

      nb_err.head = nbdata;
      nb_err.end = nbdata + sizeof (nbdata);

      grub_netbuff_clear (&nb_err);
      grub_netbuff_reserve (&nb_err, 512);
      err = grub_netbuff_push (&nb_err, sizeof (tftph->opcode)
			       + sizeof (tftph->u.err.errcode)
			       + sizeof ("closed"));
      if (!err)
	{
	  tftph = (struct tftphdr *) nb_err.data;
	  tftph->opcode = grub_cpu_to_be16 (TFTP_ERROR);
	  tftph->u.err.errcode = grub_cpu_to_be16 (TFTP_EUNDEF);
	  grub_memcpy (tftph->u.err.errmsg, "closed", sizeof ("closed"));

	  err = grub_net_send_udp_packet (data->sock, &nb_err);
	}
      if (err)
	grub_print_error ();
      grub_net_udp_close (data->sock);
    }
  destroy_pq (data);
  grub_free (data);
  return GRUB_ERR_NONE;
}
示例#5
0
/* Send a SCSI request for DISK: write the data stored in BUF to SIZE
   sectors starting with SECTOR.  */
static grub_err_t
grub_scsi_write10 (grub_disk_t disk, grub_disk_addr_t sector,
                   grub_size_t size, char *buf)
{
    grub_scsi_t scsi;
    struct grub_scsi_write10 wr;

    scsi = disk->data;

    wr.opcode = grub_scsi_cmd_write10;
    wr.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
    wr.lba = grub_cpu_to_be32 (sector);
    wr.reserved = 0;
    wr.size = grub_cpu_to_be16 (size);
    wr.reserved2 = 0;
    wr.pad = 0;

    return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * 512, buf);
}
示例#6
0
/* Send a SCSI request for DISK: read SIZE sectors starting with
   sector SECTOR to BUF.  */
static grub_err_t
grub_scsi_read10 (grub_disk_t disk, grub_disk_addr_t sector,
                  grub_size_t size, char *buf)
{
    grub_scsi_t scsi;
    struct grub_scsi_read10 rd;

    scsi = disk->data;

    rd.opcode = grub_scsi_cmd_read10;
    rd.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
    rd.lba = grub_cpu_to_be32 (sector);
    rd.reserved = 0;
    rd.size = grub_cpu_to_be16 (size);
    rd.reserved2 = 0;
    rd.pad = 0;

    return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * 512, buf);
}
示例#7
0
文件: hfs.c 项目: P4N74/radare2
/* Find block BLOCK of the file FILE in the mounted UFS filesystem
   DATA.  The first 3 extents are described by DAT.  If cache is set,
   using caching to improve non-random reads.  */
static unsigned int
grub_hfs_block (struct grub_hfs_data *data, grub_hfs_datarecord_t dat,
		int file, int block, int cache)
{
  grub_hfs_datarecord_t dr;
  int pos = 0;
  struct grub_hfs_extent_key key;

  int tree = 0;
  static int cache_file = 0;
  static int cache_pos = 0;
  static grub_hfs_datarecord_t cache_dr;

  grub_memcpy (dr, dat, sizeof (dr));

  key.forktype = 0;
  key.fileid = grub_cpu_to_be32 (file);

  if (cache && cache_file == file  && block > cache_pos)
    {
      pos = cache_pos;
      key.first_block = grub_cpu_to_be16 (pos);
      grub_memcpy (dr, cache_dr, sizeof (cache_dr));
    }

  for (;;)
    {
      int i;

      /* Try all 3 extents.  */
      for (i = 0; i < 3; i++)
	{
	  /* Check if the block is stored in this extent.  */
	  if (grub_be_to_cpu16 (dr[i].count) + pos > block)
	    {
	      int first = grub_be_to_cpu16 (dr[i].first_block);

	      /* If the cache is enabled, store the current position
		 in the tree.  */
	      if (tree && cache)
		{
		  cache_file = file;
		  cache_pos = pos;
		  grub_memcpy (cache_dr, dr, sizeof (cache_dr));
		}

	      return (grub_be_to_cpu16 (data->sblock.first_block)
		      + (first + block - pos) * GRUB_HFS_BLKS);
	    }

	  /* Try the next extent.  */
	  pos += grub_be_to_cpu16 (dr[i].count);
	}

      /* Lookup the block in the extent overflow file.  */
      key.first_block = grub_cpu_to_be16 (pos);
      tree = 1;
      grub_hfs_find_node (data, (char *) &key, data->ext_root,
			  1, (char *) &dr, sizeof (dr));
      if (grub_errno)
	return 0;
    }
}
示例#8
0
文件: arp.c 项目: Arvian/GRUB2
grub_err_t
grub_net_arp_send_request (struct grub_net_network_level_interface *inf,
			   const grub_net_network_level_address_t *proto_addr)
{
  struct grub_net_buff nb;
  struct arphdr *arp_header;
  grub_net_link_level_address_t target_hw_addr;
  grub_uint8_t *aux, arp_data[128];
  grub_err_t err;
  int i;
  grub_size_t addrlen;
  grub_uint16_t etherpro;
  grub_uint8_t *nbd;

  if (proto_addr->type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4)
    {
      addrlen = 4;
      etherpro = GRUB_NET_ETHERTYPE_IP;
    }
  else
    return grub_error (GRUB_ERR_BUG, "unsupported address family");

  /* Build a request packet.  */
  nb.head = arp_data;
  nb.end = arp_data + sizeof (arp_data);
  grub_netbuff_clear (&nb);
  grub_netbuff_reserve (&nb, 128);

  err = grub_netbuff_push (&nb, sizeof (*arp_header) + 2 * (6 + addrlen));
  if (err)
    return err;

  arp_header = (struct arphdr *) nb.data;
  arp_header->hrd = grub_cpu_to_be16 (GRUB_NET_ARPHRD_ETHERNET);
  arp_header->hln = 6;
  arp_header->pro = grub_cpu_to_be16 (etherpro);
  arp_header->pln = addrlen;
  arp_header->op = grub_cpu_to_be16 (ARP_REQUEST);
  aux = (grub_uint8_t *) arp_header + sizeof (*arp_header);
  /* Sender hardware address.  */
  grub_memcpy (aux, &inf->hwaddress.mac, 6);

  aux += 6;
  /* Sender protocol address */
  grub_memcpy (aux, &inf->address.ipv4, 4);
  aux += addrlen;
  /* Target hardware address */
  for (i = 0; i < 6; i++)
    aux[i] = 0x00;
  aux += 6;
  /* Target protocol address */
  grub_memcpy (aux, &proto_addr->ipv4, 4);
  grub_memset (&target_hw_addr.mac, 0xff, 6);

  nbd = nb.data;
  send_ethernet_packet (inf, &nb, target_hw_addr, GRUB_NET_ETHERTYPE_ARP);
  for (i = 0; i < GRUB_NET_TRIES; i++)
    {
      if (grub_net_link_layer_resolve_check (inf, proto_addr))
	return GRUB_ERR_NONE;
      pending_req = proto_addr->ipv4;
      have_pending = 0;
      grub_net_poll_cards (GRUB_NET_INTERVAL, &have_pending);
      if (grub_net_link_layer_resolve_check (inf, proto_addr))
	return GRUB_ERR_NONE;
      nb.data = nbd;
      send_ethernet_packet (inf, &nb, target_hw_addr, GRUB_NET_ETHERTYPE_ARP);
    }

  return GRUB_ERR_NONE;
}
示例#9
0
文件: dns.c 项目: Der-Jan/grub-zfs
grub_err_t
grub_net_dns_lookup (const char *name,
		     const struct grub_net_network_level_address *servers,
		     grub_size_t n_servers,
		     grub_size_t *naddresses,
		     struct grub_net_network_level_address **addresses,
		     int cache)
{
  grub_size_t send_servers = 0;
  grub_size_t i, j;
  struct grub_net_buff *nb;
  grub_net_udp_socket_t *sockets;
  grub_uint8_t *optr;
  const char *iptr;
  struct dns_header *head;
  static grub_uint16_t id = 1;
  grub_uint8_t *qtypeptr;
  grub_err_t err = GRUB_ERR_NONE;
  struct recv_data data = {naddresses, addresses, cache,
			   grub_cpu_to_be16 (id++), 0, 0, name, 0};
  grub_uint8_t *nbd;
  int have_server = 0;

  if (!servers)
    {
      servers = dns_servers;
      n_servers = dns_nservers;
    }

  if (!n_servers)
    return grub_error (GRUB_ERR_BAD_ARGUMENT,
		       N_("no DNS servers configured"));

  *naddresses = 0;
  if (cache)
    {
      int h;
      h = hash (name);
      if (dns_cache[h].name && grub_strcmp (dns_cache[h].name, name) == 0
	  && grub_get_time_ms () < dns_cache[h].limit_time)
	{
	  grub_dprintf ("dns", "retrieved from cache\n");
	  *addresses = grub_malloc (dns_cache[h].naddresses
				    * sizeof ((*addresses)[0]));
	  if (!*addresses)
	    return grub_errno;
	  *naddresses = dns_cache[h].naddresses;
	  grub_memcpy (*addresses, dns_cache[h].addresses,
		       dns_cache[h].naddresses
		       * sizeof ((*addresses)[0]));
	  return GRUB_ERR_NONE;
	}
    }

  sockets = grub_malloc (sizeof (sockets[0]) * n_servers);
  if (!sockets)
    return grub_errno;

  data.name = grub_strdup (name);
  if (!data.name)
    {
      grub_free (sockets);
      return grub_errno;
    }

  nb = grub_netbuff_alloc (GRUB_NET_OUR_MAX_IP_HEADER_SIZE
			   + GRUB_NET_MAX_LINK_HEADER_SIZE
			   + GRUB_NET_UDP_HEADER_SIZE
			   + sizeof (struct dns_header)
			   + grub_strlen (name) + 2 + 4);
  if (!nb)
    {
      grub_free (sockets);
      grub_free (data.name);
      return grub_errno;
    }
  grub_netbuff_reserve (nb, GRUB_NET_OUR_MAX_IP_HEADER_SIZE
			+ GRUB_NET_MAX_LINK_HEADER_SIZE
			+ GRUB_NET_UDP_HEADER_SIZE);
  grub_netbuff_put (nb, sizeof (struct dns_header)
		    + grub_strlen (name) + 2 + 4);
  head = (struct dns_header *) nb->data;
  optr = (grub_uint8_t *) (head + 1);
  for (iptr = name; *iptr; )
    {
      const char *dot;
      dot = grub_strchr (iptr, '.');
      if (!dot)
	dot = iptr + grub_strlen (iptr);
      if ((dot - iptr) >= 64)
	{
	  grub_free (sockets);
	  grub_free (data.name);
	  return grub_error (GRUB_ERR_BAD_ARGUMENT,
			     N_("domain name component is too long"));
	}
      *optr = (dot - iptr);
      optr++;
      grub_memcpy (optr, iptr, dot - iptr);
      optr += dot - iptr;
      iptr = dot;
      if (*iptr)
	iptr++;
    }
  *optr++ = 0;

  /* Type.  */
  *optr++ = 0;
  qtypeptr = optr++;

  /* Class.  */
  *optr++ = 0;
  *optr++ = 1;

  head->id = data.id;
  head->flags = FLAGS_RD;
  head->ra_z_r_code = 0;
  head->qdcount = grub_cpu_to_be16_compile_time (1);
  head->ancount = grub_cpu_to_be16_compile_time (0);
  head->nscount = grub_cpu_to_be16_compile_time (0);
  head->arcount = grub_cpu_to_be16_compile_time (0);

  nbd = nb->data;

  for (i = 0; i < n_servers * 4; i++)
    {
      /* Connect to a next server.  */
      while (!(i & 1) && send_servers < n_servers)
	{
	  sockets[send_servers] = grub_net_udp_open (servers[send_servers],
						     DNS_PORT,
						     recv_hook,
						     &data);
	  send_servers++;
	  if (!sockets[send_servers - 1])
	    {
	      err = grub_errno;
	      grub_errno = GRUB_ERR_NONE;
	    }
	  else
	    {
	      have_server = 1;
	      break;
	    }
	}
      if (!have_server)
	goto out;
      if (*data.naddresses)
	goto out;
      for (j = 0; j < send_servers; j++)
	{
          grub_err_t err2;
          if (!sockets[j])
            continue;
          nb->data = nbd;

          grub_size_t t = 0;
          do
            {
              if (servers[j].option == DNS_OPTION_IPV4 ||
                 ((servers[j].option == DNS_OPTION_PREFER_IPV4) && (t++ == 0)) ||
                 ((servers[j].option == DNS_OPTION_PREFER_IPV6) && (t++ == 1)))
                *qtypeptr = GRUB_DNS_QTYPE_A;
              else
                *qtypeptr = GRUB_DNS_QTYPE_AAAA;

              grub_dprintf ("dns", "QTYPE: %u QNAME: %s\n", *qtypeptr, name);

              err2 = grub_net_send_udp_packet (sockets[j], nb);
              if (err2)
                {
                  grub_errno = GRUB_ERR_NONE;
                  err = err2;
                }
              if (*data.naddresses)
                goto out;
            }
          while (t == 1);
	}
      grub_net_poll_cards (200, &data.stop);
    }
 out:
  grub_free (data.name);
  grub_netbuff_free (nb);
  for (j = 0; j < send_servers; j++)
    grub_net_udp_close (sockets[j]);
  
  grub_free (sockets);

  if (*data.naddresses)
    return GRUB_ERR_NONE;
  if (data.dns_err)
    return grub_error (GRUB_ERR_NET_NO_DOMAIN,
		       N_("no DNS record found"));
    
  if (err)
    {
      grub_errno = err;
      return err;
    }
  return grub_error (GRUB_ERR_TIMEOUT,
		     N_("no DNS reply received"));
}
示例#10
0
文件: dns.c 项目: Der-Jan/grub-zfs
static grub_err_t 
recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)),
	   struct grub_net_buff *nb,
	   void *data_)
{
  struct dns_header *head;
  struct recv_data *data = data_;
  int i, j;
  grub_uint8_t *ptr, *reparse_ptr;
  int redirect_cnt = 0;
  char *redirect_save = NULL;
  grub_uint32_t ttl_all = ~0U;

  head = (struct dns_header *) nb->data;
  ptr = (grub_uint8_t *) (head + 1);
  if (ptr >= nb->tail)
    {
      grub_netbuff_free (nb);
      return GRUB_ERR_NONE;
    }
  
  if (head->id != data->id)
    {
      grub_netbuff_free (nb);
      return GRUB_ERR_NONE;
    }
  if (!(head->flags & FLAGS_RESPONSE) || (head->flags & FLAGS_OPCODE))
    {
      grub_netbuff_free (nb);
      return GRUB_ERR_NONE;
    }
  if (head->ra_z_r_code & ERRCODE_MASK)
    {
      data->dns_err = 1;
      grub_netbuff_free (nb);
      return GRUB_ERR_NONE;
    }
  for (i = 0; i < grub_cpu_to_be16 (head->qdcount); i++)
    {
      if (ptr >= nb->tail)
	{
	  grub_netbuff_free (nb);
	  return GRUB_ERR_NONE;
	}
      while (ptr < nb->tail && !((*ptr & 0xc0) || *ptr == 0))
	ptr += *ptr + 1;
      if (ptr < nb->tail && (*ptr & 0xc0))
	ptr++;
      ptr++;
      ptr += 4;
    }
  *data->addresses = grub_malloc (sizeof ((*data->addresses)[0])
				 * grub_cpu_to_be16 (head->ancount));
  if (!*data->addresses)
    {
      grub_errno = GRUB_ERR_NONE;
      grub_netbuff_free (nb);
      return GRUB_ERR_NONE;
    }
  reparse_ptr = ptr;
 reparse:
  for (i = 0, ptr = reparse_ptr; i < grub_cpu_to_be16 (head->ancount); i++)
    {
      int ignored = 0;
      grub_uint8_t class;
      grub_uint32_t ttl = 0;
      grub_uint16_t length;
      if (ptr >= nb->tail)
	{
	  if (!*data->naddresses)
	    grub_free (*data->addresses);
	  return GRUB_ERR_NONE;
	}
      ignored = !check_name (ptr, nb->data, nb->tail, data->name);
      while (ptr < nb->tail && !((*ptr & 0xc0) || *ptr == 0))
	ptr += *ptr + 1;
      if (ptr < nb->tail && (*ptr & 0xc0))
	ptr++;
      ptr++;
      if (ptr + 10 >= nb->tail)
	{
	  if (!*data->naddresses)
	    grub_free (*data->addresses);
	  grub_netbuff_free (nb);
	  return GRUB_ERR_NONE;
	}
      if (*ptr++ != 0)
	ignored = 1;
      class = *ptr++;
      if (*ptr++ != 0)
	ignored = 1;
      if (*ptr++ != 1)
	ignored = 1;
      for (j = 0; j < 4; j++)
	{
	  ttl <<= 8;
	  ttl |= *ptr++;
	}
      length = *ptr++ << 8;
      length |= *ptr++;
      if (ptr + length > nb->tail)
	{
	  if (!*data->naddresses)
	    grub_free (*data->addresses);
	  grub_netbuff_free (nb);
	  return GRUB_ERR_NONE;
	}
      if (!ignored)
	{
	  if (ttl_all > ttl)
	    ttl_all = ttl;
	  switch (class)
	    {
	    case DNS_CLASS_A:
	      if (length != 4)
		break;
	      (*data->addresses)[*data->naddresses].type
		= GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
	      grub_memcpy (&(*data->addresses)[*data->naddresses].ipv4,
			   ptr, 4);
	      (*data->naddresses)++;
	      data->stop = 1;
	      break;
	    case DNS_CLASS_AAAA:
	      if (length != 16)
		break;
	      (*data->addresses)[*data->naddresses].type
		= GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
	      grub_memcpy (&(*data->addresses)[*data->naddresses].ipv6,
			   ptr, 16);
	      (*data->naddresses)++;
	      data->stop = 1;
	      break;
	    case DNS_CLASS_CNAME:
	      if (!(redirect_cnt & (redirect_cnt - 1)))
		{
		  grub_free (redirect_save);
		  redirect_save = data->name;
		}
	      else
		grub_free (data->name);
	      redirect_cnt++;
	      data->name = get_name (ptr, nb->data, nb->tail);
	      if (!data->name)
		{
		  data->dns_err = 1;
		  grub_errno = 0;
		  return GRUB_ERR_NONE;
		}
	      grub_dprintf ("dns", "CNAME %s\n", data->name);
	      if (grub_strcmp (redirect_save, data->name) == 0)
		{
		  data->dns_err = 1;
		  grub_free (redirect_save);
		  return GRUB_ERR_NONE;
		}
	      goto reparse;
	    }
	}
      ptr += length;
    }
  if (ttl_all && *data->naddresses && data->cache)
    {
      int h;
      grub_dprintf ("dns", "caching for %d seconds\n", ttl_all);
      h = hash (data->oname);
      grub_free (dns_cache[h].name);
      dns_cache[h].name = 0;
      grub_free (dns_cache[h].addresses);
      dns_cache[h].addresses = 0;
      dns_cache[h].name = grub_strdup (data->oname);
      dns_cache[h].naddresses = *data->naddresses;
      dns_cache[h].addresses = grub_malloc (*data->naddresses
					    * sizeof (dns_cache[h].addresses[0]));
      dns_cache[h].limit_time = grub_get_time_ms () + 1000 * ttl_all;
      if (!dns_cache[h].addresses || !dns_cache[h].name)
	{
	  grub_free (dns_cache[h].name);
	  dns_cache[h].name = 0;
	  grub_free (dns_cache[h].addresses);
	  dns_cache[h].addresses = 0;
	}
      grub_memcpy (dns_cache[h].addresses, *data->addresses,
		   *data->naddresses
		   * sizeof (dns_cache[h].addresses[0]));
    }
  grub_netbuff_free (nb);
  grub_free (redirect_save);
  return GRUB_ERR_NONE;
}
示例#11
0
文件: tftp.c 项目: Der-Jan/grub-zfs
static grub_err_t
tftp_open (struct grub_file *file, const char *filename)
{
  struct tftphdr *tftph;
  char *rrq;
  int i;
  int rrqlen;
  int hdrlen;
  grub_uint8_t open_data[1500];
  struct grub_net_buff nb;
  tftp_data_t data;
  grub_err_t err;
  grub_uint8_t *nbd;
  grub_net_network_level_address_t addr;

  data = grub_zalloc (sizeof (*data));
  if (!data)
    return grub_errno;

  nb.head = open_data;
  nb.end = open_data + sizeof (open_data);
  grub_netbuff_clear (&nb);

  grub_netbuff_reserve (&nb, 1500);
  err = grub_netbuff_push (&nb, sizeof (*tftph));
  if (err)
    return err;

  tftph = (struct tftphdr *) nb.data;

  rrq = (char *) tftph->u.rrq;
  rrqlen = 0;

  tftph->opcode = grub_cpu_to_be16 (TFTP_RRQ);
  grub_strcpy (rrq, filename);
  rrqlen += grub_strlen (filename) + 1;
  rrq += grub_strlen (filename) + 1;

  grub_strcpy (rrq, "octet");
  rrqlen += grub_strlen ("octet") + 1;
  rrq += grub_strlen ("octet") + 1;

  grub_strcpy (rrq, "blksize");
  rrqlen += grub_strlen ("blksize") + 1;
  rrq += grub_strlen ("blksize") + 1;

  grub_strcpy (rrq, "1024");
  rrqlen += grub_strlen ("1024") + 1;
  rrq += grub_strlen ("1024") + 1;

  grub_strcpy (rrq, "tsize");
  rrqlen += grub_strlen ("tsize") + 1;
  rrq += grub_strlen ("tsize") + 1;

  grub_strcpy (rrq, "0");
  rrqlen += grub_strlen ("0") + 1;
  rrq += grub_strlen ("0") + 1;
  hdrlen = sizeof (tftph->opcode) + rrqlen;

  err = grub_netbuff_unput (&nb, nb.tail - (nb.data + hdrlen));
  if (err)
    return err;

  file->not_easily_seekable = 1;
  file->data = data;

  data->pq = grub_priority_queue_new (sizeof (struct grub_net_buff *), cmp);
  if (!data->pq)
    return grub_errno;

  err = grub_net_resolve_address (file->device->net->server, &addr);
  if (err)
    {
      destroy_pq (data);
      return err;
    }

  data->sock = grub_net_udp_open (addr,
				  TFTP_SERVER_PORT, tftp_receive,
				  file);
  if (!data->sock)
    {
      destroy_pq (data);
      return grub_errno;
    }

  /* Receive OACK packet.  */
  nbd = nb.data;
  for (i = 0; i < GRUB_NET_TRIES; i++)
    {
      nb.data = nbd;
      err = grub_net_send_udp_packet (data->sock, &nb);
      if (err)
	{
	  grub_net_udp_close (data->sock);
	  destroy_pq (data);
	  return err;
	}
      grub_net_poll_cards (GRUB_NET_INTERVAL + (i * GRUB_NET_INTERVAL_ADDITION),
                           &data->have_oack);
      if (data->have_oack)
	break;
    }

  if (!data->have_oack)
    grub_error (GRUB_ERR_TIMEOUT, N_("time out opening `%s'"), filename);
  else
    grub_error_load (&data->save_err);
  if (grub_errno)
    {
      grub_net_udp_close (data->sock);
      destroy_pq (data);
      return grub_errno;
    }

  file->size = data->file_size;

  return GRUB_ERR_NONE;
}