#include <printk.h>
#include "etherboot.h"
#include "nic.h"

static unsigned long xid;

#define eth_poll() nic.poll(&nic)
#define eth_transmit(a,b,c,d) nic.transmit(&nic,a,b,c, (char *) d)

int decode_rfc1533(unsigned char *p, int block, int len, int eof)
{
	return(-1);	// don't support it yet, just define it!
}

#define NO_DHCP_SUPPORT
#define EMERGENCYDISKBOOT

#define getchar() ttys0_rx_char()

static const unsigned char broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
static unsigned long     netmask;

struct arptable_t        arptable[MAX_ARP];

struct bootpd_t bootp_data;

/***************************************************************************
getdec, a simple sort of atoi
***************************************************************************/
int getdec(char **ptr)
{
	char *p = *ptr;
	int ret=0;
	if ((*p < '0') || (*p > '9')) return(-1);
	while ((*p >= '0') && (*p <= '9')) {
		ret = ret*10 + (*p - '0');
		p++;
	}
	*ptr = p;
	return(ret);
}

/**************************************************************************
STRCASECMP (not entirely correct, but this will do for our purposes)
**************************************************************************/
int strcasecmp(char *a, char *b)
{
	while (*a && *b && (*a & ~0x20) == (*b & ~0x20)) {a++; b++; }
	return((*a & ~0x20) - (*b & ~0x20));
}


/**************************************************************************
DEFAULT_NETMASK - Return default netmask for IP address
**************************************************************************/
static inline unsigned long default_netmask(void)
{
	int net = ntohl(arptable[ARP_CLIENT].ipaddr.s_addr) >> 24;
	if (net <= 127)
		return(htonl(0xff000000));
	else if (net < 192)
		return(htonl(0xffff0000));
	else
		return(htonl(0xffffff00));
}

/**************************************************************************
IPCHKSUM - Checksum IP Header
**************************************************************************/
static unsigned short ipchksum(unsigned short *ip, int len)
{
	unsigned long sum = 0;
	len >>= 1;
	while (len--) {
		sum += *(ip++);
		if (sum > 0xFFFF)
			sum -= 0xFFFF;
	}
	return((~sum) & 0x0000FFFF);
}

/**************************************************************************
RFC951_SLEEP - sleep for expotentially longer times
**************************************************************************/
void rfc951_sleep(int exp)
{
	static long seed = 0;
	long q;
	unsigned long tmo;

#ifdef BACKOFF_LIMIT
	if (exp > BACKOFF_LIMIT)
		exp = BACKOFF_LIMIT;
#endif
	if (!seed) /* Initialize linear congruential generator */
		seed = currticks() + *(long *)&arptable[ARP_CLIENT].node
		       + ((short *)arptable[ARP_CLIENT].node)[2];
	/* simplified version of the LCG given in Bruce Scheier's
	   "Applied Cryptography" */
	q = seed/53668;
	if ((seed = 40014*(seed-53668*q) - 12211*q) < 0) seed += 2147483563l;
	/* compute mask */
	for (tmo = 63; tmo <= 60*TICKS_PER_SEC && --exp > 0; tmo = 2*tmo+1);
	/* sleep */
	printk_info("<sleep>\n");
	for (tmo = (tmo&seed)+currticks(); currticks() < tmo; )
	return;
}

/**************************************************************************
AWAIT_REPLY - Wait until we get a response for our request
**************************************************************************/
int await_reply(int type, int ival, void *ptr, int timeout)
{
	unsigned long time;
	struct	iphdr *ip;
	struct	udphdr *udp;
	struct	arprequest *arpreply;
	struct	bootp_t *bootpreply;
	struct	rpc_t *rpc;
	unsigned short ptype;

	unsigned int protohdrlen = ETH_HLEN + sizeof(struct iphdr) +
				sizeof(struct udphdr);
	time = timeout + currticks();
	/* The timeout check is done below.  The timeout is only checked if
	 * there is no packet in the Rx queue.  This assumes that eth_poll()
	 * needs a negligible amount of time.  */
	for (;;) {
		if (eth_poll()) {	/* We have something! */
					/* Check for ARP - No IP hdr */
			if (nic.packetlen >= ETH_HLEN) {
				ptype = ((unsigned short) nic.packet[12]) << 8
					| ((unsigned short) nic.packet[13]);
			} else continue; /* what else could we do with it? */
			if ((nic.packetlen >= ETH_HLEN +
				sizeof(struct arprequest)) &&
			   (ptype == ARP) ) {
				unsigned long tmp;

				arpreply = (struct arprequest *)
					&nic.packet[ETH_HLEN];
				if ((arpreply->opcode == htons(ARP_REPLY)) &&
				   !memcmp(arpreply->sipaddr, ptr, sizeof(in_addr)) &&
				   (type == AWAIT_ARP)) {
					memcpy(arptable[ival].node, arpreply->shwaddr, ETH_ALEN);
					return(1);
				}
				memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr));
				if ((arpreply->opcode == htons(ARP_REQUEST)) &&
					(tmp == arptable[ARP_CLIENT].ipaddr.s_addr)) {
					arpreply->opcode = htons(ARP_REPLY);
					memcpy(arpreply->tipaddr, arpreply->sipaddr, sizeof(in_addr));
					memcpy(arpreply->thwaddr, arpreply->shwaddr, ETH_ALEN);
					memcpy(arpreply->sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr));
					memcpy(arpreply->shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
					eth_transmit(arpreply->thwaddr, ARP,
						sizeof(struct  arprequest),
						(char *) arpreply);
#ifdef	MDEBUG
					memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr));
					printf("Sent ARP reply to: %I\n",tmp);
#endif	/* MDEBUG */
				}
				continue;
			}

			if (type == AWAIT_QDRAIN) {
				continue;
			}

					/* Check for RARP - No IP hdr */
			if ((type == AWAIT_RARP) &&
			   (nic.packetlen >= ETH_HLEN +
				sizeof(struct arprequest)) &&
			   (ptype == RARP)) {
				arpreply = (struct arprequest *)
					&nic.packet[ETH_HLEN];
				if ((arpreply->opcode == htons(RARP_REPLY)) &&
				   !memcmp(arpreply->thwaddr, ptr, ETH_ALEN)) {
					memcpy(arptable[ARP_SERVER].node, arpreply->shwaddr, ETH_ALEN);
					memcpy(& arptable[ARP_SERVER].ipaddr, arpreply->sipaddr, sizeof(in_addr));
					memcpy(& arptable[ARP_CLIENT].ipaddr, arpreply->tipaddr, sizeof(in_addr));
					return(1);
				}
				continue;
			}

					/* Anything else has IP header */
			if ((nic.packetlen < protohdrlen) ||
			   (ptype != IP) ) continue;
			ip = (struct iphdr *)&nic.packet[ETH_HLEN];
			if ((ip->verhdrlen != 0x45) ||
				ipchksum((unsigned short *)ip, sizeof(struct iphdr)) ||
				(ip->protocol != IP_UDP)) continue;
			udp = (struct udphdr *)&nic.packet[ETH_HLEN +
				sizeof(struct iphdr)];

					/* BOOTP ? */
			bootpreply = (struct bootp_t *)&nic.packet[ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr)];
			if ((type == AWAIT_BOOTP) &&
			   (nic.packetlen >= (ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr) +
#ifdef	NO_DHCP_SUPPORT
			     sizeof(struct bootp_t))) &&
#else
			     sizeof(struct bootp_t))-DHCP_OPT_LEN) &&
#endif	/* NO_DHCP_SUPPORT */
			   (udp->dest == htons(BOOTP_CLIENT)) &&
			   (bootpreply->bp_op == BOOTP_REPLY) &&
			   (bootpreply->bp_xid == xid) &&
			   (memcmp(broadcast, bootpreply->bp_hwaddr, ETH_ALEN) ==0 ||
			    memcmp(arptable[ARP_CLIENT].node, bootpreply->bp_hwaddr, ETH_ALEN) == 0)) {
				arptable[ARP_CLIENT].ipaddr.s_addr =
					bootpreply->bp_yiaddr.s_addr;
#ifndef	NO_DHCP_SUPPORT
				dhcp_addr.s_addr = bootpreply->bp_yiaddr.s_addr;
#endif	/* NO_DHCP_SUPPORT */
				netmask = default_netmask();
				arptable[ARP_SERVER].ipaddr.s_addr =
					bootpreply->bp_siaddr.s_addr;
				memset(arptable[ARP_SERVER].node, 0, ETH_ALEN);  /* Kill arp */
				arptable[ARP_GATEWAY].ipaddr.s_addr =
					bootpreply->bp_giaddr.s_addr;
				memset(arptable[ARP_GATEWAY].node, 0, ETH_ALEN);  /* Kill arp */
				/* bootpreply->bp_file will be copied to KERNEL_BUF in the memcpy */
				memcpy((char *)BOOTP_DATA_ADDR, (char *)bootpreply, sizeof(struct bootpd_t));
				decode_rfc1533(BOOTP_DATA_ADDR->bootp_reply.bp_vend,
#ifdef	NO_DHCP_SUPPORT
				       0, BOOTP_VENDOR_LEN + MAX_BOOTP_EXTLEN, 1);
#else
				       0, DHCP_OPT_LEN + MAX_BOOTP_EXTLEN, 1);
#endif	/* NO_DHCP_SUPPORT */
#ifdef	REQUIRE_VCI_ETHERBOOT
				if (!vci_etherboot)
					return (0);
#endif

				return(1);
			}

#ifdef	DOWNLOAD_PROTO_TFTP
					/* TFTP ? */
			if ((type == AWAIT_TFTP) &&
				(ntohs(udp->dest) == ival)) return(1);
#endif	/* DOWNLOAD_PROTO_TFTP */

#ifdef	DOWNLOAD_PROTO_NFS
					/* RPC ? */
			rpc = (struct rpc_t *)&nic.packet[ETH_HLEN];
			if ((type == AWAIT_RPC) &&
			    (ntohs(udp->dest) == ival) &&
			    (*(unsigned long *)ptr == ntohl(rpc->u.reply.id)) &&
			    (ntohl(rpc->u.reply.type) == MSG_REPLY)) {
				return (1);
			}
#endif	/* DOWNLOAD_PROTO_NFS */

		} else {
			/* Check for abort key only if the Rx queue is empty -
			 * as long as we have something to process, don't
			 * assume that something failed.  It is unlikely that
			 * we have no processing time left between packets.  */
			if (iskey() && (getchar() == ESC))
#ifdef	EMERGENCYDISKBOOT
				return(-1);
#else
				longjmp(jmp_bootmenu, -1);
#endif
			/* Do the timeout after at least a full queue walk.  */
			if ((timeout == 0) || (currticks() > time)) {
				break;
			}
		}
	}
	return(0);
}


/**************************************************************************
RARP - Get my IP address and load information
**************************************************************************/
int rarp(void)
{
	int retry;

	/* arp and rarp requests share the same packet structure. */
	struct arprequest rarpreq;

	memset(&rarpreq, 0, sizeof(rarpreq));

	rarpreq.hwtype = htons(1);
	rarpreq.protocol = htons(IP);
	rarpreq.hwlen = ETH_ALEN;
	rarpreq.protolen = 4;
	rarpreq.opcode = htons(RARP_REQUEST);
	memcpy(&rarpreq.shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
	/* sipaddr is already zeroed out */
	memcpy(&rarpreq.thwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
	/* tipaddr is already zeroed out */

	for (retry = 0; retry < MAX_ARP_RETRIES; rfc951_sleep(++retry)) {
		eth_transmit(broadcast, RARP, sizeof(rarpreq), &rarpreq);

		if (await_reply(AWAIT_RARP, 0, rarpreq.shwaddr, TIMEOUT))
			break;
	}

	if (retry < MAX_ARP_RETRIES) {
		(void)sprintf(KERNEL_BUF, DEFAULT_KERNELPATH, arptable[ARP_CLIENT].ipaddr);

		return (1);
	}
	return (0);
}

/**************************************************************************
UDP_TRANSMIT - Send a UDP datagram
**************************************************************************/
int udp_transmit(unsigned long destip, unsigned int srcsock,
	unsigned int destsock, int len, const void *buf)
{
	struct iphdr *ip;
	struct udphdr *udp;
	struct arprequest arpreq;
	int arpentry, i;
	int retry;

	ip = (struct iphdr *)buf;
	udp = (struct udphdr *)((char *)buf + sizeof(struct iphdr));
	ip->verhdrlen = 0x45;
	ip->service = 0;
	ip->len = htons(len);
	ip->ident = 0;
	ip->frags = 0;
	ip->ttl = 60;
	ip->protocol = IP_UDP;
	ip->chksum = 0;
	ip->src.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr;
	ip->dest.s_addr = destip;
	ip->chksum = ipchksum((unsigned short *)buf, sizeof(struct iphdr));
	udp->src = htons(srcsock);
	udp->dest = htons(destsock);
	udp->len = htons(len - sizeof(struct iphdr));
	udp->chksum = 0;
	if (destip == IP_BROADCAST) {
		eth_transmit(broadcast, IP, len, buf);
	} else {
		if (((destip & netmask) !=
			(arptable[ARP_CLIENT].ipaddr.s_addr & netmask)) &&
			arptable[ARP_GATEWAY].ipaddr.s_addr)
				destip = arptable[ARP_GATEWAY].ipaddr.s_addr;
		for(arpentry = 0; arpentry<MAX_ARP; arpentry++)
			if (arptable[arpentry].ipaddr.s_addr == destip) break;
		if (arpentry == MAX_ARP) {
			printk_info("%I is not in my arp table!\n", destip);
			return(0);
		}
		for (i = 0; i < ETH_ALEN; i++)
			if (arptable[arpentry].node[i])
				break;
		if (i == ETH_ALEN) {	/* Need to do arp request */
			arpreq.hwtype = htons(1);
			arpreq.protocol = htons(IP);
			arpreq.hwlen = ETH_ALEN;
			arpreq.protolen = 4;
			arpreq.opcode = htons(ARP_REQUEST);
			memcpy(arpreq.shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN);
			memcpy(arpreq.sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr));
			memset(arpreq.thwaddr, 0, ETH_ALEN);
			memcpy(arpreq.tipaddr, &destip, sizeof(in_addr));
			for (retry = 1; retry <= MAX_ARP_RETRIES; retry++) {
				eth_transmit(broadcast, ARP, sizeof(arpreq),
					&arpreq);
				if (await_reply(AWAIT_ARP, arpentry,
					arpreq.tipaddr, TIMEOUT)) goto xmit;
				rfc951_sleep(retry);
				/* We have slept for a while - the packet may
				 * have arrived by now.  If not, we have at
				 * least some room in the Rx buffer for the
				 * next reply.  */
				if (await_reply(AWAIT_ARP, arpentry,
					arpreq.tipaddr, 0)) goto xmit;
			}
			return(0);
		}
xmit:
		eth_transmit(arptable[arpentry].node, IP, len, buf);
	}
	return(1);
}

/**************************************************************************
TFTP - Download extended BOOTP data, or kernel image
**************************************************************************/
int tftp(const char *name, int (*fnc)(unsigned char *, int, int, int))
{
	int             retry = 0;
	static unsigned short iport = 2000;
	unsigned short  oport;
	unsigned short  len, block = 0, prevblock = 0;
	int		bcounter = 0;
	struct tftp_t  *tr;
	struct tftpreq_t tp;
	int		rc;
	int		packetsize = TFTP_DEFAULTSIZE_PACKET;

	/* Clear out the Rx queue first.  It contains nothing of interest,
	 * except possibly ARP requests from the DHCP/TFTP server.  We use
	 * polling throughout Etherboot, so some time may have passed since we
	 * last polled the receive queue, which may now be filled with
	 * broadcast packets.  This will cause the reply to the packets we are
	 * about to send to be lost immediately.  Not very clever.  */
	await_reply(AWAIT_QDRAIN, 0, NULL, 0);

	tp.opcode = htons(TFTP_RRQ);
	/* Warning: the following assumes the layout of bootp_t.
	   But that's fixed by the IP, UDP and BOOTP specs. */
	len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) +
		sprintf((char *)tp.u.rrq, "%s%coctet%cblksize%c%d",
		name, 0, 0, 0, TFTP_MAX_PACKET) + 1;
	if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
		TFTP_PORT, len, &tp))
		return (0);
	for (;;)
	{
#ifdef	CONGESTED
		if (!await_reply(AWAIT_TFTP, iport, NULL, (block ? TFTP_REXMT : TIMEOUT)))
#else
		if (!await_reply(AWAIT_TFTP, iport, NULL, TIMEOUT))
#endif
		{
			if (!block && retry++ < MAX_TFTP_RETRIES)
			{	/* maybe initial request was lost */
				rfc951_sleep(retry);
				if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
					++iport, TFTP_PORT, len, &tp))
					return (0);
				continue;
			}
#ifdef	CONGESTED
			if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
			{	/* we resend our last ack */
#ifdef	MDEBUG
				printk_info("<REXMT>\n");
#endif
				udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
					iport, oport,
					TFTP_MIN_PACKET, &tp);
				continue;
			}
#endif
			break;	/* timeout */
		}
		tr = (struct tftp_t *)&nic.packet[ETH_HLEN];
		if (tr->opcode == ntohs(TFTP_ERROR))
		{
			printk_info("TFTP error %d (%s)\n",
			       ntohs(tr->u.err.errcode),
			       tr->u.err.errmsg);
			break;
		}

		if (tr->opcode == ntohs(TFTP_OACK)) {
			char *p = tr->u.oack.data, *e;

			if (prevblock)		/* shouldn't happen */
				continue;	/* ignore it */
			len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
			if (len > TFTP_MAX_PACKET)
				goto noak;
			e = p + len;
			while (*p != '\000' && p < e) {
				if (!strcasecmp("blksize", p)) {
					p += 8;
					if ((packetsize = getdec(&p)) <
					    TFTP_DEFAULTSIZE_PACKET)
						goto noak;
					while (p < e && *p) p++;
					if (p < e)
						p++;
				}
				else {
				noak:
					tp.opcode = htons(TFTP_ERROR);
					tp.u.err.errcode = 8;
/*
 *	Warning: the following assumes the layout of bootp_t.
 *	But that's fixed by the IP, UDP and BOOTP specs.
 */
					len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) + sizeof(tp.u.err.errcode) +
						sprintf((char *)tp.u.err.errmsg,
						"RFC1782 error") + 1;
					udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
						     iport, ntohs(tr->udp.src),
						     len, &tp);
					return (0);
				}
			}
			if (p > e)
				goto noak;
			block = tp.u.ack.block = 0; /* this ensures, that */
						/* the packet does not get */
						/* processed as data! */
		}
		else if (tr->opcode == htons(TFTP_DATA)) {
			len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
			if (len > packetsize)	/* shouldn't happen */
				continue;	/* ignore it */
			block = ntohs(tp.u.ack.block = tr->u.data.block); }
		else /* neither TFTP_OACK nor TFTP_DATA */
			break;

		if ((block || bcounter) && (block != prevblock+1)) {
			/* Block order should be continuous */
			tp.u.ack.block = htons(block = prevblock);
		}
		tp.opcode = htons(TFTP_ACK);
		oport = ntohs(tr->udp.src);
		udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport,
			oport, TFTP_MIN_PACKET, &tp);	/* ack */
		if ((unsigned short)(block-prevblock) != 1) {
			/* Retransmission or OACK, don't process via callback
			 * and don't change the value of prevblock.  */
			continue;
		}
		prevblock = block;
		retry = 0;	/* It's the right place to zero the timer? */
		if ((rc = fnc(tr->u.data.download,
			      ++bcounter, len, len < packetsize)) >= 0)
			return(rc);
		if (len < packetsize)		/* End of data */
			return (1);
	}
	return (0);
}

int             retry = 0;
static unsigned short iport = 2000;
unsigned short  oport;
unsigned short  len, block = 0, prevblock = 0;
int		bcounter = 0;
struct tftp_t  *tr;
struct tftpreq_t tp;
int		rc;
int		packetsize = TFTP_DEFAULTSIZE_PACKET;

int tftp_init(const char *name)
{
	retry = 0;
	iport = 2000;
	block = 0;
	prevblock = 0;
	bcounter = 0;
	packetsize = TFTP_DEFAULTSIZE_PACKET;

	/* Clear out the Rx queue first.  It contains nothing of interest,
	 * except possibly ARP requests from the DHCP/TFTP server.  We use
	 * polling throughout Etherboot, so some time may have passed since we
	 * last polled the receive queue, which may now be filled with
	 * broadcast packets.  This will cause the reply to the packets we are
	 * about to send to be lost immediately.  Not very clever.  */
	await_reply(AWAIT_QDRAIN, 0, NULL, 0);

	tp.opcode = htons(TFTP_RRQ);
	/* Warning: the following assumes the layout of bootp_t.
	   But that's fixed by the IP, UDP and BOOTP specs. */
	len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) +
		sprintf((char *)tp.u.rrq, "%s%coctet%cblksize%c%d",
		name, 0, 0, 0, TFTP_MAX_PACKET) + 1;
	if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, ++iport,
		TFTP_PORT, len, &tp))
		return (-1);
#ifdef	CONGESTED
	while (!await_reply(AWAIT_TFTP, iport, NULL, (block ? TFTP_REXMT : TIMEOUT)))
#else
	while (!await_reply(AWAIT_TFTP, iport, NULL, TIMEOUT))
#endif
	{
		if (!block && retry++ < MAX_TFTP_RETRIES)
		{	/* maybe initial request was lost */
			rfc951_sleep(retry);
			if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
				++iport, TFTP_PORT, len, &tp))
				return (-1);
			continue;
		}
#ifdef	CONGESTED
		if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
		{	/* we resend our last ack */
#ifdef	MDEBUG
			printk_info("<REXMT>\n");
#endif
			udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
				iport, oport,
				TFTP_MIN_PACKET, &tp);
			continue;
		}
#endif
		break;	/* timeout */
	}

// we have recieved reply. see if it's any good.

	tr = (struct tftp_t *)&nic.packet[ETH_HLEN];
	if (tr->opcode == ntohs(TFTP_ERROR)) {
		printk_info("TFTP error %d (%s)\n",
		       ntohs(tr->u.err.errcode),
		       tr->u.err.errmsg);

		return(-ntohs(tr->u.err.errcode));
	}

	if (tr->opcode == ntohs(TFTP_OACK)) {
		char *p = tr->u.oack.data, *e;

		if (prevblock)		/* shouldn't happen */
			return(-1);	/* ignore it */
		len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 2;
		if (len > TFTP_MAX_PACKET)
			goto noak;
		e = p + len;
		while (*p != '\000' && p < e) {
			if (!strcasecmp("blksize", p)) {
				p += 8;
				if ((packetsize = getdec(&p)) <
				    TFTP_DEFAULTSIZE_PACKET)
					goto noak;
				while (p < e && *p) p++;
				if (p < e)
					p++;
			}
			else {
			noak:
				tp.opcode = htons(TFTP_ERROR);
				tp.u.err.errcode = 8;
/*
 *	Warning: the following assumes the layout of bootp_t.
 *	But that's fixed by the IP, UDP and BOOTP specs.
 */
				len = sizeof(tp.ip) + sizeof(tp.udp) + sizeof(tp.opcode) + sizeof(tp.u.err.errcode) +
					sprintf((char *)tp.u.err.errmsg,
					"RFC1782 error") + 1;
				udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
					     iport, ntohs(tr->udp.src),
					     len, &tp);
				return (-1);
			}
		}
		if (p > e)
			goto noak;
		block = tp.u.ack.block = 0; /* this ensures, that */
					/* the packet does not get */
					/* processed as data! */
	}
	return(0);
}
int tftp_fetchone(char *buffer)
{
	/* Clear out the Rx queue first.  It contains nothing of interest,
	 * except possibly ARP requests from the DHCP/TFTP server.  We use
	 * polling throughout Etherboot, so some time may have passed since we
	 * last polled the receive queue, which may now be filled with
	 * broadcast packets.  This will cause the reply to the packets we are
	 * about to send to be lost immediately.  Not very clever.  */
retry:
	await_reply(AWAIT_QDRAIN, 0, NULL, 0);

	tp.opcode = htons(TFTP_ACK);
	oport = ntohs(tr->udp.src);
	udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr, iport,
		oport, TFTP_MIN_PACKET, &tp);	/* ack */

#ifdef	CONGESTED
	while (!await_reply(AWAIT_TFTP, iport, NULL, (block ? TFTP_REXMT : TIMEOUT)))
#else
	while (!await_reply(AWAIT_TFTP, iport, NULL, TIMEOUT))
#endif
	{
		if (!block && retry++ < MAX_TFTP_RETRIES)
		{	/* maybe initial request was lost */
			rfc951_sleep(retry);
			if (!udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
				++iport, TFTP_PORT, len, &tp))
				return (-1);
			continue;
		}
#ifdef	CONGESTED
		if (block && ((retry += TFTP_REXMT) < TFTP_TIMEOUT))
		{	/* we resend our last ack */
#ifdef	MDEBUG
			printk_info("<REXMT>\n");
#endif
			udp_transmit(arptable[ARP_SERVER].ipaddr.s_addr,
				iport, oport,
				TFTP_MIN_PACKET, &tp);
			continue;
		}
#endif
		break;	/* timeout */
	}

	tr = (struct tftp_t *)&nic.packet[ETH_HLEN];
	if (tr->opcode == ntohs(TFTP_ERROR))
	{
		printk_info("TFTP error %d (%s)\n",
		       ntohs(tr->u.err.errcode),
		       tr->u.err.errmsg);
		return(-tr->u.err.errcode);
	}

	if (tr->opcode == ntohs(TFTP_OACK))
		goto retry;	// session is already started! try again.

	else if (tr->opcode == htons(TFTP_DATA)) {
		len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
		if (len > packetsize)	/* shouldn't happen */
			goto retry; 	/* bad thing, skip it and try again! */
		block = ntohs(tp.u.ack.block = tr->u.data.block); 

	} else /* neither TFTP_OACK nor TFTP_DATA */
		goto retry; // whatever it is, it's invalid! try again.

	if ((block || bcounter) && (block != prevblock+1)) {
		/* Block order should be continuous */
		tp.u.ack.block = htons(block = prevblock);
		goto retry;
	}

	prevblock = block;
	retry = 0;	/* It's the right place to zero the timer? */
	
	
	memcpy(buffer,tr->u.data.download, len);
	return(len);
}
