Store and return previous pxe discover requests and pxe/binl replies
instead of always building the same reply from scratch.
Also use three different buffers for clients which expect to manipulate
all three replies concurrently.
Also always reset the filename to the last filename specified, RIS
depends on this odd behaviour.

Index: etherboot-5.4.3/src/core/nic.c
===================================================================
--- etherboot-5.4.3.orig/src/core/nic.c	2008-07-31 12:32:07.000000000 -0700
+++ etherboot-5.4.3/src/core/nic.c	2008-07-31 12:32:12.000000000 -0700
@@ -47,7 +47,12 @@
 static const unsigned char broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
 static const in_addr zeroIP = { 0L };
 
+struct bootpd_t bootp_req;
+unsigned int bootp_req_len = 0;
 struct bootpd_t bootp_data;
+unsigned int bootp_data_len = 0;
+struct bootpd_t proxy_bootp_data;
+unsigned int proxy_bootp_data_len = 0;
 
 #ifdef PXE_DHCP_STRICT
 #define MAX_BOOT_MENU 5
@@ -1137,6 +1142,9 @@
 		}
 		/* we only solicited this packet for the options and the bp_file */
 		memcpy((char*)&bootp_data, (char*)bootpreply, sizeof(struct bootpd_t));
+		bootp_data_len = nic.packetlen -
+			(ETH_HLEN + sizeof(struct iphdr) +
+			 sizeof(struct udphdr));
 		memcpy(KERNEL_BUF, bootpreply->bp_file, sizeof(KERNEL_BUF));
 		return 1;
 	}
@@ -1166,6 +1174,9 @@
 		netmask = default_netmask();
 		/* bootpreply->bp_file will be copied to KERNEL_BUF in the memcpy */
 		memcpy((char *)&bootp_data, (char *)bootpreply, sizeof(struct bootpd_t));
+		bootp_data_len = nic.packetlen -
+			(ETH_HLEN + sizeof(struct iphdr) +
+			 sizeof(struct udphdr));
 		decode_rfc1533(bootp_data.bootp_reply.bp_vend, 0,
 #ifdef	NO_DHCP_SUPPORT
 			       BOOTP_VENDOR_LEN + MAX_BOOTP_EXTLEN,
@@ -1183,6 +1194,13 @@
 		memset(arptable[ARP_PROXYDHCP].node, 0, ETH_ALEN);	/* Kill arp */
 		/* Grab only the bootfile name from a ProxyDHCP packet */
 		memcpy(KERNEL_BUF, bootpreply->bp_file, sizeof(KERNEL_BUF));
+		memcpy((char *)&proxy_bootp_data, (char *)bootpreply,
+		       sizeof(struct bootpd_t));
+		proxy_bootp_data_len = nic.packetlen -
+			(ETH_HLEN + sizeof(struct iphdr) +
+			 sizeof(struct udphdr));
+		if (proxy_bootp_data_len > sizeof(struct bootpd_t))
+			proxy_bootp_data_len = sizeof(struct bootpd_t);
 		/* Grab server address from rfc1533 Server Identifier field*/
 		decode_rfc1533_proxydhcp(bootpreply->bp_vend,
 					 DHCP_OPT_LEN + MAX_BOOTP_EXTLEN);
@@ -1234,6 +1252,8 @@
 	memcpy(bp_vend, dhcp_machine_info, DHCP_MACHINE_INFO_SIZE);
 	bp_vend += DHCP_MACHINE_INFO_SIZE;
 	*bp_vend++ = RFC1533_END;
+	memcpy((char*)&bootp_req, &ip.bp, sizeof(struct bootpd_t));
+	bootp_req_len = bp_vend - (unsigned char *)&ip.bp;
 #endif	/* NO_DHCP_SUPPORT */
 
 	for (retry = 0; retry < MAX_BOOTP_RETRIES; ) {
@@ -1292,6 +1312,8 @@
 		memcpy(bp_vend, dhcp_machine_info, DHCP_MACHINE_INFO_SIZE);
 		bp_vend += DHCP_MACHINE_INFO_SIZE;
 		*bp_vend++ = RFC1533_END;
+		memcpy((char*)&bootp_req, &ip.bp, sizeof(struct bootpd_t));
+		bootp_req_len = bp_vend - (unsigned char *)&ip.bp;
 		for (reqretry = 0; reqretry < MAX_BOOTP_RETRIES; ) {
 			unsigned long timeout;
 
Index: etherboot-5.4.3/src/core/pxe_export.c
===================================================================
--- etherboot-5.4.3.orig/src/core/pxe_export.c	2008-07-31 12:32:11.000000000 -0700
+++ etherboot-5.4.3/src/core/pxe_export.c	2008-07-31 12:32:12.000000000 -0700
@@ -1092,10 +1092,58 @@
  */
 PXENV_EXIT_t pxenv_get_cached_info ( t_PXENV_GET_CACHED_INFO
 				     *get_cached_info ) {
-	BOOTPLAYER *cached_info = &pxe_stack->cached_info;
-	DBG ( "PXENV_GET_CACHED_INFO %d", get_cached_info->PacketType );
+	static int prepared[3];
+	BOOTPLAYER *cached_info;
+	int len = sizeof(*cached_info);
+	DBG ( "PXENV_GET_CACHED_INFO %d buf %x len %x",
+	      get_cached_info->PacketType, get_cached_info->Buffer,
+	      get_cached_info->BufferSize );
 	ENSURE_READY ( get_cached_info );
 
+	if (get_cached_info->PacketType <= 0 ||
+	    get_cached_info->PacketType >= 4) {
+		get_cached_info->Status = PXENV_STATUS_UNDI_INVALID_STATE;
+		return PXENV_EXIT_FAILURE;
+	}
+
+	cached_info =
+	  &pxe_stack->pxe_cached_info[get_cached_info->PacketType - 1];
+
+	if (get_cached_info->PacketType == 1 && bootp_req_len) {
+		DBG ( " bootp request %d %d %d", len, bootp_req_len,
+		      sizeof(struct bootpd_t));
+		if (prepared[0] == 0) {
+			memset(cached_info, 0, sizeof(cached_info));
+			memcpy(cached_info, (char *)&bootp_req, bootp_req_len);
+			prepared[0] = 1;
+		}
+		len = bootp_req_len;
+		goto copy;
+	}
+	if (get_cached_info->PacketType == 2 && bootp_data_len) {
+		DBG ( " bootp packet %d %d %d", len, bootp_data_len,
+		      sizeof(struct bootpd_t));
+		if (prepared[1] == 0) {
+			memset(cached_info, 0, sizeof(cached_info));
+			memcpy(cached_info, (char *)&bootp_data,
+			       bootp_data_len);
+			prepared[1] = 1;
+		}
+		goto copy;
+	}
+	if (get_cached_info->PacketType == 3 && proxy_bootp_data_len) {
+		DBG ( " proxy bootp packet %d %d %d", len,
+		      proxy_bootp_data_len, sizeof(struct bootpd_t));
+		DBG ( " %x %x %x", cached_info->opcode, cached_info->Hardware,
+		      cached_info->ident);
+		if (prepared[2] == 0) {
+			memset(cached_info, 0, sizeof(cached_info));
+			memcpy(cached_info, (char *)&proxy_bootp_data,
+			       proxy_bootp_data_len);
+			prepared[2] = 1;
+		}
+		goto copy;
+	}
 	/* Fill in cached_info structure in our pxe_stack */
 
 	/* I don't think there's actually any way we can be called in
@@ -1121,12 +1169,13 @@
 	memcpy ( cached_info->CAddr, arptable[ARP_CLIENT].node, ETH_ALEN );
 	/* Nullify server name */
 	cached_info->Sname[0] = '\0';
-	memcpy ( cached_info->bootfile, KERNEL_BUF,
-		 sizeof(cached_info->bootfile) );
 	/* Copy DHCP vendor options */
 	memcpy ( &cached_info->vendor.d, bootp_data.bootp_reply.bp_vend,
 		 sizeof(cached_info->vendor.d) );
 	
+ copy:
+	memcpy ( cached_info->bootfile, KERNEL_BUF,
+		 sizeof(cached_info->bootfile) );
 #if 0
 	/* The case in which the caller doesn't supply a buffer is
 	 * really awkward to support given that we have multiple
@@ -1166,10 +1215,10 @@
 	if ( IS_NULL_SEGOFF16 ( get_cached_info->Buffer ) || (get_cached_info->BufferSize == 0)) {
 		/* Point back to our buffer */
 		PTR_TO_SEGOFF16 ( cached_info, get_cached_info->Buffer );
-		get_cached_info->BufferSize = sizeof(*cached_info);
+		get_cached_info->BufferSize = len;
 	} else {
 		/* Copy to user buffer */
-		size_t size = sizeof(*cached_info);
+		size_t size = len;
 		void *buffer = SEGOFF16_TO_PTR ( get_cached_info->Buffer );
 		if ( get_cached_info->BufferSize < size )
 			size = get_cached_info->BufferSize;
Index: etherboot-5.4.3/src/include/etherboot.h
===================================================================
--- etherboot-5.4.3.orig/src/include/etherboot.h	2008-07-31 12:32:01.000000000 -0700
+++ etherboot-5.4.3/src/include/etherboot.h	2008-07-31 12:32:12.000000000 -0700
@@ -448,7 +448,12 @@
 extern int hostnamelen;
 extern unsigned char *addparam;
 extern int addparamlen;
+extern struct bootpd_t bootp_req;
+extern unsigned int bootp_req_len;
 extern struct bootpd_t bootp_data;
+extern unsigned int bootp_data_len;
+extern struct bootpd_t proxy_bootp_data;
+extern unsigned int proxy_bootp_data_len;
 extern unsigned char *end_of_rfc1533;
 #ifdef	IMAGE_FREEBSD
 extern int freebsd_howto;
Index: etherboot-5.4.3/src/include/pxe.h
===================================================================
--- etherboot-5.4.3.orig/src/include/pxe.h	2008-07-31 12:32:01.000000000 -0700
+++ etherboot-5.4.3/src/include/pxe.h	2008-07-31 12:32:12.000000000 -0700
@@ -909,7 +909,6 @@
 	pxenv_t		pxenv	__attribute__ ((aligned(16)));
 	pxe_stack_state_t state;
 	union {
-		BOOTPLAYER	cached_info;
 		char		packet[ETH_FRAME_LEN];
 		struct {
 			uint32_t magic_cookie;
@@ -923,6 +922,7 @@
 			uint32_t bufferlen;
 		} readfile;
 	};
+	BOOTPLAYER pxe_cached_info[3];
 	struct {}	arch_data __attribute__ ((aligned(16)));
 } pxe_stack_t;
 
