1/*
2 *  GRUB  --  GRand Unified Bootloader
3 *  Copyright (C) 2000,2001,2002,2004  Free Software Foundation, Inc.
4 *
5 *  This program is free software; you can redistribute it and/or modify
6 *  it under the terms of the GNU General Public License as published by
7 *  the Free Software Foundation; either version 2 of the License, or
8 *  (at your option) any later version.
9 *
10 *  This program is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 *  GNU General Public License for more details.
14 *
15 *  You should have received a copy of the GNU General Public License
16 *  along with this program; if not, write to the Free Software
17 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20/* Based on "src/main.c" in etherboot-4.5.8.  */
21/**************************************************************************
22ETHERBOOT -  BOOTP/TFTP Bootstrap Program
23
24Author: Martin Renters
25  Date: Dec/93
26
27**************************************************************************/
28
29/* #define TFTP_DEBUG	1 */
30
31#include <filesys.h>
32
33#define GRUB	1
34#include <etherboot.h>
35#include <nic.h>
36
37static int retry;
38static unsigned short iport = 2000;
39static unsigned short oport;
40static unsigned short block, prevblock;
41static int bcounter;
42static struct tftp_t tp, saved_tp;
43static int packetsize;
44static int buf_eof, buf_read;
45static int saved_filepos;
46static unsigned short len, saved_len;
47static char *buf;
48
49/* Fill the buffer by receiving the data via the TFTP protocol.  */
50static int
51buf_fill (int abort)
52{
53#ifdef TFTP_DEBUG
54  grub_printf ("buf_fill (%d)\n", abort);
55#endif
56
57  while (! buf_eof && (buf_read + packetsize <= FSYS_BUFLEN))
58    {
59      struct tftp_t *tr;
60      long timeout;
61
62#ifdef CONGESTED
63      timeout = rfc2131_sleep_interval (block ? TFTP_REXMT : TIMEOUT, retry);
64#else
65      timeout = rfc2131_sleep_interval (TIMEOUT, retry);
66#endif
67
68      if (! await_reply (AWAIT_TFTP, iport, NULL, timeout))
69	{
70	  if (ip_abort)
71	    return 0;
72
73	  if (! block && retry++ < MAX_TFTP_RETRIES)
74	    {
75	      /* Maybe initial request was lost.  */
76#ifdef TFTP_DEBUG
77	      grub_printf ("Maybe initial request was lost.\n");
78#endif
79	      if (! udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
80				  ++iport, TFTP_PORT, len, &tp))
81		return 0;
82
83	      continue;
84	    }
85
86#ifdef CONGESTED
87	  if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
88	    {
89	      /* We resend our last ack.  */
90# ifdef TFTP_DEBUG
91	      grub_printf ("<REXMT>\n");
92# endif
93	      udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
94			    iport, oport,
95			    TFTP_MIN_PACKET, &tp);
96	      continue;
97	    }
98#endif
99	  /* Timeout.  */
100	  return 0;
101	}
102
103      tr = (struct tftp_t *) &nic.packet[ETH_HLEN];
104      if (tr->opcode == ntohs (TFTP_ERROR))
105	{
106	  grub_printf ("TFTP error %d (%s)\n",
107		       ntohs (tr->u.err.errcode),
108		       tr->u.err.errmsg);
109	  return 0;
110	}
111
112      if (tr->opcode == ntohs (TFTP_OACK))
113	{
114	  char *p = tr->u.oack.data, *e;
115
116#ifdef TFTP_DEBUG
117	  grub_printf ("OACK ");
118#endif
119	  /* Shouldn't happen.  */
120	  if (prevblock)
121	    {
122	      /* Ignore it.  */
123	      grub_printf ("%s:%d: warning: PREVBLOCK != 0 (0x%x)\n",
124			   __FILE__, __LINE__, prevblock);
125	      continue;
126	    }
127
128	  len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 2;
129	  if (len > TFTP_MAX_PACKET)
130	    goto noak;
131
132	  e = p + len;
133	  while (*p != '\000' && p < e)
134	    {
135	      if (! grub_strcmp ("blksize", p))
136		{
137		  p += 8;
138		  if ((packetsize = getdec (&p)) < TFTP_DEFAULTSIZE_PACKET)
139		    goto noak;
140#ifdef TFTP_DEBUG
141		  grub_printf ("blksize = %d\n", packetsize);
142#endif
143		}
144	      else if (! grub_strcmp ("tsize", p))
145		{
146		  p += 6;
147		  if ((filemax = getdec (&p)) < 0)
148		    {
149		      filemax = -1;
150		      goto noak;
151		    }
152#ifdef TFTP_DEBUG
153		  grub_printf ("tsize = %d\n", filemax);
154#endif
155		}
156	      else
157		{
158		noak:
159#ifdef TFTP_DEBUG
160		  grub_printf ("NOAK\n");
161#endif
162		  tp.opcode = htons (TFTP_ERROR);
163		  tp.u.err.errcode = 8;
164		  len = (grub_sprintf ((char *) tp.u.err.errmsg,
165				       "RFC1782 error")
166			 + sizeof (tp.ip) + sizeof (tp.udp)
167			 + sizeof (tp.opcode) + sizeof (tp.u.err.errcode)
168			 + 1);
169		  udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr,
170				iport, ntohs (tr->udp.src),
171				len, &tp);
172		  return 0;
173		}
174
175	      while (p < e && *p)
176		p++;
177
178	      if (p < e)
179		p++;
180	    }
181
182	  if (p > e)
183	    goto noak;
184
185	  /* This ensures that the packet does not get processed as
186	     data!  */
187	  block = tp.u.ack.block = 0;
188	}
189      else if (tr->opcode == ntohs (TFTP_DATA))
190	{
191#ifdef TFTP_DEBUG
192	  grub_printf ("DATA ");
193#endif
194	  len = ntohs (tr->udp.len) - sizeof (struct udphdr) - 4;
195
196	  /* Shouldn't happen.  */
197	  if (len > packetsize)
198	    {
199	      /* Ignore it.  */
200	      grub_printf ("%s:%d: warning: LEN > PACKETSIZE (0x%x > 0x%x)\n",
201			   __FILE__, __LINE__, len, packetsize);
202	      continue;
203	    }
204
205	  block = ntohs (tp.u.ack.block = tr->u.data.block);
206	}
207      else
208	/* Neither TFTP_OACK nor TFTP_DATA.  */
209	break;
210
211      if ((block || bcounter) && (block != prevblock + (unsigned short) 1))
212	/* Block order should be continuous */
213	tp.u.ack.block = htons (block = prevblock);
214
215      /* Should be continuous.  */
216      tp.opcode = abort ? htons (TFTP_ERROR) : htons (TFTP_ACK);
217      oport = ntohs (tr->udp.src);
218
219#ifdef TFTP_DEBUG
220      grub_printf ("ACK\n");
221#endif
222      /* Ack.  */
223      udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, iport,
224		    oport, TFTP_MIN_PACKET, &tp);
225
226      if (abort)
227	{
228	  buf_eof = 1;
229	  break;
230	}
231
232      /* Retransmission or OACK.  */
233      if ((unsigned short) (block - prevblock) != 1)
234	/* Don't process.  */
235	continue;
236
237      prevblock = block;
238      /* Is it the right place to zero the timer?  */
239      retry = 0;
240
241      /* In GRUB, this variable doesn't play any important role at all,
242	 but use it for consistency with Etherboot.  */
243      bcounter++;
244
245      /* Copy the downloaded data to the buffer.  */
246      grub_memmove (buf + buf_read, tr->u.data.download, len);
247      buf_read += len;
248
249      /* End of data.  */
250      if (len < packetsize)
251	buf_eof = 1;
252    }
253
254  return 1;
255}
256
257/* Send the RRQ whose length is LEN.  */
258static int
259send_rrq (void)
260{
261  /* Initialize some variables.  */
262  retry = 0;
263  block = 0;
264  prevblock = 0;
265  packetsize = TFTP_DEFAULTSIZE_PACKET;
266  bcounter = 0;
267
268  buf = (char *) FSYS_BUF;
269  buf_eof = 0;
270  buf_read = 0;
271  saved_filepos = 0;
272
273  /* Clear out the Rx queue first.  It contains nothing of interest,
274   * except possibly ARP requests from the DHCP/TFTP server.  We use
275   * polling throughout Etherboot, so some time may have passed since we
276   * last polled the receive queue, which may now be filled with
277   * broadcast packets.  This will cause the reply to the packets we are
278   * about to send to be lost immediately.  Not very clever.  */
279  await_reply (AWAIT_QDRAIN, 0, NULL, 0);
280
281#ifdef TFTP_DEBUG
282  grub_printf ("send_rrq ()\n");
283  {
284    int i;
285    char *p;
286
287    for (i = 0, p = (char *) &tp; i < len; i++)
288      if (p[i] >= ' ' && p[i] <= '~')
289	grub_putchar (p[i]);
290      else
291	grub_printf ("\\%x", (unsigned) p[i]);
292
293    grub_putchar ('\n');
294  }
295#endif
296  /* Send the packet.  */
297  return udp_transmit (arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
298		       TFTP_PORT, len, &tp);
299}
300
301/* Mount the network drive. If the drive is ready, return one, otherwise
302   return zero.  */
303int
304tftp_mount (void)
305{
306  /* Check if the current drive is the network drive.  */
307  if (current_drive != NETWORK_DRIVE)
308    return 0;
309
310  /* If the drive is not initialized yet, abort.  */
311  if (! network_ready)
312    return 0;
313
314  return 1;
315}
316
317/* Read up to SIZE bytes, returned in ADDR.  */
318int
319tftp_read (char *addr, int size)
320{
321  /* How many bytes is read?  */
322  int ret = 0;
323
324#ifdef TFTP_DEBUG
325  grub_printf ("tftp_read (0x%x, %d)\n", (int) addr, size);
326#endif
327
328  if (filepos < saved_filepos)
329    {
330      /* Uggh.. FILEPOS has been moved backwards. So reopen the file.  */
331      buf_read = 0;
332      buf_fill (1);
333      grub_memmove ((char *) &tp, (char *) &saved_tp, saved_len);
334      len = saved_len;
335#ifdef TFTP_DEBUG
336      {
337	int i;
338	grub_printf ("opcode = 0x%x, rrq = ", (unsigned long) tp.opcode);
339	for (i = 0; i < TFTP_DEFAULTSIZE_PACKET; i++)
340	  {
341	    if (tp.u.rrq[i] >= ' ' && tp.u.rrq[i] <= '~')
342	      grub_putchar (tp.u.rrq[i]);
343	    else
344	      grub_putchar ('*');
345	  }
346	grub_putchar ('\n');
347      }
348#endif
349
350      if (! send_rrq ())
351	{
352	  errnum = ERR_WRITE;
353	  return 0;
354	}
355    }
356
357  while (size > 0)
358    {
359      int amt = buf_read + saved_filepos - filepos;
360
361      /* If the length that can be copied from the buffer is over the
362	 requested size, cut it down.  */
363      if (amt > size)
364	amt = size;
365
366      if (amt > 0)
367	{
368	  /* Copy the buffer to the supplied memory space.  */
369	  grub_memmove (addr, buf + filepos - saved_filepos, amt);
370	  size -= amt;
371	  addr += amt;
372	  filepos += amt;
373	  ret += amt;
374
375	  /* If the size of the empty space becomes small, move the unused
376	     data forwards.  */
377	  if (filepos - saved_filepos > FSYS_BUFLEN / 2)
378	    {
379	      grub_memmove (buf, buf + FSYS_BUFLEN / 2, FSYS_BUFLEN / 2);
380	      buf_read -= FSYS_BUFLEN / 2;
381	      saved_filepos += FSYS_BUFLEN / 2;
382	    }
383	}
384      else
385	{
386	  /* Skip the whole buffer.  */
387	  saved_filepos += buf_read;
388	  buf_read = 0;
389	}
390
391      /* Read the data.  */
392      if (size > 0 && ! buf_fill (0))
393	{
394	  errnum = ERR_READ;
395	  return 0;
396	}
397
398      /* Sanity check.  */
399      if (size > 0 && buf_read == 0)
400	{
401	  errnum = ERR_READ;
402	  return 0;
403	}
404    }
405
406  return ret;
407}
408
409/* Check if the file DIRNAME really exists. Get the size and save it in
410   FILEMAX.  */
411int
412tftp_dir (char *dirname)
413{
414  int ch;
415
416#ifdef TFTP_DEBUG
417  grub_printf ("tftp_dir (%s)\n", dirname);
418#endif
419
420  /* In TFTP, there is no way to know what files exist.  */
421  if (print_possibilities)
422    return 1;
423
424  /* Don't know the size yet.  */
425  filemax = -1;
426
427 reopen:
428  /* Construct the TFTP request packet.  */
429  tp.opcode = htons (TFTP_RRQ);
430  /* Terminate the filename.  */
431  ch = nul_terminate (dirname);
432  /* Make the request string (octet, blksize and tsize).  */
433  len = (grub_sprintf ((char *) tp.u.rrq,
434		       "%s%coctet%cblksize%c%d%ctsize%c0",
435		       dirname, 0, 0, 0, TFTP_MAX_PACKET, 0, 0)
436	 + sizeof (tp.ip) + sizeof (tp.udp) + sizeof (tp.opcode) + 1);
437  /* Restore the original DIRNAME.  */
438  dirname[grub_strlen (dirname)] = ch;
439  /* Save the TFTP packet so that we can reopen the file later.  */
440  grub_memmove ((char *) &saved_tp, (char *) &tp, len);
441  saved_len = len;
442  if (! send_rrq ())
443    {
444      errnum = ERR_WRITE;
445      return 0;
446    }
447
448  /* Read the data.  */
449  if (! buf_fill (0))
450    {
451      errnum = ERR_FILE_NOT_FOUND;
452      return 0;
453    }
454
455  if (filemax == -1)
456    {
457      /* The server doesn't support the "tsize" option, so we must read
458	 the file twice...  */
459
460      /* Zero the size of the file.  */
461      filemax = 0;
462      do
463	{
464	  /* Add the length of the downloaded data.  */
465	  filemax += buf_read;
466	  /* Reset the offset. Just discard the contents of the buffer.  */
467	  buf_read = 0;
468	  /* Read the data.  */
469	  if (! buf_fill (0))
470	    {
471	      errnum = ERR_READ;
472	      return 0;
473	    }
474	}
475      while (! buf_eof);
476
477      /* Maybe a few amounts of data remains.  */
478      filemax += buf_read;
479
480      /* Retry the open instruction.  */
481      goto reopen;
482    }
483
484  return 1;
485}
486
487/* Close the file.  */
488void
489tftp_close (void)
490{
491#ifdef TFTP_DEBUG
492  grub_printf ("tftp_close ()\n");
493#endif
494
495  buf_read = 0;
496  buf_fill (1);
497}
498