/*
 * PXE daemon - enable the remote booting of PXE enabled machines.
 * Copyright (C) 2000 Tim Hurman (kano@kano.org.uk)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */
/******************************************************************************
 * packetstore.cc - decode and store a packet in memory                       *
 ******************************************************************************/

#include "packetstore.h"

/* some global variables */
const char *DHCP_types[DHCP_MAX_TYPES] = 
{
	"INVALID", "DHCPDISCOVER", "DHCPOFFER", "DHCPREQUEST",
	"DHCPDECLINE", "DHCPACK", "DHCPNACK", "DHCPRELEASE",
	"DHCPINFORM"
};

/******************************************************************************
 * PacketStore - null packet constructor                                      *
 ******************************************************************************/
PacketStore::PacketStore(LogFile *_logger)
{
	this->logger = _logger;
	head = head43 = NULL;
	Initalise();
}


/******************************************************************************
 * PacketStore - full packet analyse                                          *
 ******************************************************************************/
PacketStore::PacketStore(LogFile *_logger, struct sockaddr_in *_address,
	uint8_t *bootp_pkt, int bootp_pkt_len)
{
	this->logger = _logger;
	memcpy(&(this->address), _address, sizeof(this->address));
	ReadPacket(bootp_pkt, bootp_pkt_len);
}


/******************************************************************************
 * ~PacketStore - descructor                                                  *
 ******************************************************************************/
PacketStore::~PacketStore()
{
	Initalise();
}


/******************************************************************************
 * Initalise - init/blank a packet                                            *
 ******************************************************************************/
void
PacketStore::Initalise(void)
{
	option *optptr, *optnext;

	// basic stuff 
	op=htype=hlen=hops=0;
	secs=0;
	xid=0;
	memset(chaddr, 0, 16);
	sname[0] = file[0] = 0;
	magic_cookie = 0;
	
	memset(&ciaddr, 0, sizeof(ciaddr));
	memset(&yiaddr, 0, sizeof(yiaddr));
	memset(&siaddr, 0, sizeof(siaddr));
	memset(&giaddr, 0, sizeof(giaddr));
	memset(&address, 0, sizeof(giaddr));

	// options
	for(optptr=head; optptr != NULL; optptr=optnext)
	{
		optnext = optptr->next;
		delete[] optptr->data;
		delete optptr;
	}
	head = NULL;

	// options 43
	for(optptr=head43; optptr != NULL; optptr=optnext)
	{
		optnext = optptr->next;
		delete[] optptr->data;
		delete optptr;
	}
	head43 = NULL;
}


/******************************************************************************
 * ReadPacket - read a bootp packet                                           *
 ******************************************************************************/
int
PacketStore::ReadPacket(uint8_t *bootp_pkt, int bootp_pkt_len)
{
	if(bootp_pkt_len < 300)
		throw new SysException(0, "PacketStore::ReadPacket",
			"Invalid packet length");

	op = bootp_pkt[0];
	htype = bootp_pkt[1];
	hlen = bootp_pkt[2];
	hops = bootp_pkt[3];

	// xid - store in network byte order
	xid = bootp_pkt[4];
	xid <<= 8;
	xid |= bootp_pkt[5];
	xid <<= 8;
	xid |= bootp_pkt[6];
	xid <<= 8;
	xid |= bootp_pkt[7];
	xid = htonl(xid);

	// no of secs
	secs = bootp_pkt[8];
	secs <<= 8;
	secs |= bootp_pkt[9];
	secs = htons(secs);

	// client IP addr (filled in by client)
	ciaddr.s_addr = bootp_pkt[12];
	ciaddr.s_addr <<= 8;
	ciaddr.s_addr |= bootp_pkt[13];
	ciaddr.s_addr <<= 8;
	ciaddr.s_addr |= bootp_pkt[14];
	ciaddr.s_addr <<= 8;
	ciaddr.s_addr |= bootp_pkt[15];
	ciaddr.s_addr = htonl(ciaddr.s_addr);

	// client IP addr (filled in by server)
	yiaddr.s_addr = bootp_pkt[16];
	yiaddr.s_addr <<= 8;
	yiaddr.s_addr |= bootp_pkt[17];
	yiaddr.s_addr <<= 8;
	yiaddr.s_addr |= bootp_pkt[18];
	yiaddr.s_addr <<= 8;
	yiaddr.s_addr |= bootp_pkt[19];
	yiaddr.s_addr = htonl(yiaddr.s_addr);

	// server IP address (filled in by server)
	siaddr.s_addr = bootp_pkt[20];
	siaddr.s_addr <<= 8;
	siaddr.s_addr |= bootp_pkt[21];
	siaddr.s_addr <<= 8;
	siaddr.s_addr |= bootp_pkt[22];
	siaddr.s_addr <<= 8;
	siaddr.s_addr |= bootp_pkt[23];
	siaddr.s_addr = htonl(siaddr.s_addr);

	// gateway IP address
	giaddr.s_addr = bootp_pkt[24];
	giaddr.s_addr <<= 8;
	giaddr.s_addr |= bootp_pkt[25];
	giaddr.s_addr <<= 8;
	giaddr.s_addr |= bootp_pkt[26];
	giaddr.s_addr <<= 8;
	giaddr.s_addr |= bootp_pkt[27];
	giaddr.s_addr = htonl(giaddr.s_addr);

	// client hardware address
	memcpy(chaddr, bootp_pkt+28, 16);

	// the servername
	if(bootp_pkt[107] != 0)
		throw new SysException(0, "PacketStore::ReadPacket",
			"Invalid servername");
	strcpy(sname, (char*)bootp_pkt+44);

	// the file
	if(bootp_pkt[235] != 0)
		throw new SysException(0, "PacketStore::ReadPacket",
			"Invalid filename");
	strcpy(file, (char*)bootp_pkt+108);

	// the magic cookie
	magic_cookie = bootp_pkt[236];
	magic_cookie <<= 8;
	magic_cookie |= bootp_pkt[237];
	magic_cookie <<= 8;
	magic_cookie |= bootp_pkt[238];
	magic_cookie <<= 8;
	magic_cookie |= bootp_pkt[239];
	magic_cookie = htonl(magic_cookie);

	if(bootp_pkt[236] == 0xff)
		return(0);
	
	// read the options
	head=head43=NULL;
	return(ReadOptions(bootp_pkt+240, bootp_pkt_len-240));
}


/******************************************************************************
 * print the packet to stdout                                                 *
 ******************************************************************************/
std::ostream&
operator<< (std::ostream& os, PacketStore &pkt)
{
	int i;
	option *optptr;

	// basic info
	os << "BOOTP type             : " << (int)pkt.op << "\n";
	os << "Hardware type          : " << (int)pkt.htype << "\n";
	os << "Hardware Length        : " << (int)pkt.hlen << "\n";
	os << "Hops                   : " << (int)pkt.hops << "\n";
	os << "Transaction ID         : 0x" << std::hex << ntohl(pkt.xid) <<
	  std::dec <<"\n";
	os << "Seconds                : " << ntohs(pkt.secs) << "\n";
	os << "Client IP (From Client): " << inet_ntoa(pkt.ciaddr) << "\n";
	os << "Client IP (From Server): " << inet_ntoa(pkt.yiaddr) << "\n";
	os << "Server IP              : " << inet_ntoa(pkt.siaddr) << "\n";
	os << "Gateway IP             : " << inet_ntoa(pkt.giaddr) << "\n";
	os << "Client Hardware address: " ;
	
	// client hardware addr
	for(i=0; i<pkt.hlen; i++)
		os << std::hex << (int)pkt.chaddr[i] << std::dec << ".";
	os << "\n";

	// string info
	os << "Server name            : " << pkt.sname << "\n";
	os << "Boot filename          : " << pkt.file << "\n";
	os << "Magic cookie           : 0x" << std::hex <<
	  ntohl(pkt.magic_cookie) << std::dec  << "\n";

	if(pkt.head == NULL)
		return os;

	// print the options
	os << "Options                :\n";

	for(optptr = pkt.head; optptr != NULL; optptr = optptr->next)
	{
		os << "Option major:" << (int)optptr->major_no
		   << " minor: " << (int)optptr->minor_no
		   << ", Length: " << (int)optptr->len << ", Data: ";
		for(i=0; i<optptr->len; i++)
			if((optptr->data[i] >0x20) && (optptr->data[i] < 0x7f))
				os << "[" << (int)optptr->data[i]
				   << "," << std::hex << (int)optptr->data[i] << std::dec
				   << "," << (char)optptr->data[i] << "] ";
			else
				os << "[" << (int)optptr->data[i]
				   << "," << std::hex << (int)optptr->data[i] << std::dec
				   << ", ] ";

		os << "\n";
	}

	if(pkt.head43 == NULL)
		return os;
			
	// option 43
	os << "Options (sub-packed)   :\n";
	for(optptr = pkt.head43; optptr != NULL; optptr = optptr->next)
	{
		os << "Option [43] " << (int)optptr->major_no
		   << " minor: " << (int)optptr->minor_no
		   << ", Length: " << (int)optptr->len << ", Data: ";
		for(i=0; i<optptr->len; i++)
			if((optptr->data[i] >0x20) && (optptr->data[i] < 0x7f))
				os << "[" << (int)optptr->data[i]
				   << "," << std::hex << (int)optptr->data[i] << std::dec 
				   << "," << (char)optptr->data[i] << "] ";
			else
				os << "[" << (int)optptr->data[i]
				   << "," << std::hex << (int)optptr->data[i] << std::dec
				   << ", ] ";

		os << "\n";
	}

	return os;
}


/******************************************************************************
 * ReadOptions - read all the options contained within a packet               *
 ******************************************************************************/
int
PacketStore::ReadOptions(uint8_t *bootp_pkt, int bootp_pkt_len)
{
	// everything will always be null here
	int pos;
	option *optptr = NULL;
	option *optprev = NULL;

	for(pos=0; pos < bootp_pkt_len; pos++)
	{
		if(bootp_pkt[pos] == 0)
			goto ReadOptions_next; // pad

		if(bootp_pkt[pos] == 255)
			break;	// end options

		// lots of sub options
		if(bootp_pkt[pos] == 43)
		{
			ReadOptions43(bootp_pkt+pos+2, bootp_pkt[pos+1]);
			pos += bootp_pkt[pos+1]+1;
			goto ReadOptions_next; // next
		}

		// default
		optptr = new option;
		optptr->major_no = bootp_pkt[pos++];
		optptr->minor_no = 0;
		optptr->len = bootp_pkt[pos++];
		optptr->data = new uint8_t[optptr->len];
		optptr->next = NULL;
		memcpy(optptr->data, bootp_pkt+pos, optptr->len);

		// increment pos, accounting to cyclic increment
		pos += (optptr->len-1);

		if(head != NULL)
		{
			optprev->next = optptr; // assign
			optprev = optprev->next; // advance
		}
		else
			optprev = head = optptr;

		ReadOptions_next:
		pos=pos;
	}

	return(0);
}


/******************************************************************************
 * ReadOptions43 - read all the options contained within a packet             *
 ******************************************************************************/
int
PacketStore::ReadOptions43(uint8_t *bootp_pkt, int bootp_pkt_len)
{
	// everything will always be null here
	int pos;
	option *optptr=NULL;
	option *optprev=NULL;

	for(pos=0; pos < bootp_pkt_len; pos++)
	{
		if(bootp_pkt[pos] == 0)
			goto ReadOptions43_next; // pad

		if(bootp_pkt[pos] == 255)
			break;	// end options

		// lots of sub options
		if(bootp_pkt[pos] == 43)
		{
			std::cerr << "Option 43 detected\n";
			break;
		}

		// default
		optptr = new option;
		optptr->major_no = 43;
		optptr->minor_no = bootp_pkt[pos++];
		optptr->len = bootp_pkt[pos++];
		optptr->data = new uint8_t[optptr->len];
		optptr->next = NULL;
		memcpy(optptr->data, bootp_pkt+pos, optptr->len);

		// increment pos, accounting to cyclic increment
		pos += (optptr->len-1);

		if(head43 != NULL)
		{
			optprev->next = optptr; // assign
			optprev = optprev->next; // advance
		}
		else
			optprev = head43 = optptr;

		ReadOptions43_next:
		pos=pos;
	}

	return(0);
}


/******************************************************************************
 * operator() - get an option from the list                                   *
 ******************************************************************************/
option *
PacketStore::operator()(int major_opt=0, int minor_opt=0)
{
	return(GetOption(major_opt, minor_opt));
}


/******************************************************************************
 * GetOption - get an option fromthe option list                              *
 ******************************************************************************/
option *
PacketStore::GetOption(int major_opt=0, int minor_opt=0)
{
	option *opt = new option;
	option *optptr;

	if(major_opt == 43)
		optptr = head43;
	else
		optptr = head;
	
	for(; optptr != NULL; optptr = optptr->next)
		if((major_opt == optptr->major_no) &&
		   (minor_opt == optptr->minor_no))
			break;
	
	if(optptr == NULL)
		return(NULL);

	opt->major_no = optptr->major_no;
	opt->minor_no = optptr->minor_no;
	opt->len = optptr->len;
	opt->next = NULL;
	opt->data = new uint8_t[opt->len];
	memcpy(opt->data, optptr->data, opt->len);

	return(opt);
}


/******************************************************************************
 * DelOption - delete an option from the list                                 *
 ******************************************************************************/
int
PacketStore::DelOption(int major_opt=0, int minor_opt=0)
{
	option *optptr, *optprev;

	if(major_opt == 43)
		optptr = optprev = head43;
	else
		optptr = optprev = head;

	for(; ((major_opt != optptr->major_no) &&
	       (minor_opt != optptr->minor_no)) ||
	       (optptr != NULL); optptr = optptr->next)
		optprev = optptr;

	if(optptr != NULL) // option found
	{
		// re-arrange the pointers
		if(optprev == optptr) // head
			if(optptr == head43)
				head43 = optptr->next;
			else
				head = optptr->next;
		else
			optprev->next = optptr->next;

		// delete the memory
		delete[] optptr->data;
		delete optptr;
	}
	else
		return(1); // no found

	return(0);
}


/******************************************************************************
 * AddOption - add an option to the list (overwrite)                          *
 ******************************************************************************/
int
PacketStore::AddOption(const option *opt)
{
	option *optptr, *optprev;

	if(opt->major_no == 43)
		optptr = optprev = head43;
	else
		optptr = optprev = head;

	// find the entry or null;
	while(optptr != NULL)
	{
		if((opt->major_no != optptr->major_no) &&
		   (opt->minor_no != optptr->minor_no))
			break;
		optprev = optptr;
		optptr = optptr->next;
	}

	if(optptr == NULL) // new item
	{
		optptr = new option;
		optptr->major_no = opt->major_no;
		optptr->minor_no = opt->minor_no;
		optptr->next = NULL;

		if((43 == opt->major_no) && (NULL == head43))
			head43 = optptr;
		else if(NULL == head)
			head = optptr;
		else
			optprev->next = optptr;
	}
	else  // old item
	{
		delete[] optptr->data;
	}

	optptr->len = opt->len;
	optptr->data = new uint8_t[optptr->len];
	memcpy(optptr->data, opt->data, opt->len);

	return(0);
}


/******************************************************************************
 * htois - host to intel order (short)                                        *
 ******************************************************************************/
uint16_t
PacketStore::htois(uint16_t value)
{
	uint8_t t1, t2;
	uint16_t val;

	// convert to a standard
	val = htons(value);

	// check if we need to go any further
	// since Intel order != network order
	if(val != value)
		return(value);

	// we now have a standard, can you read this Intel?, this is
	// STANDARD!

	// we know that Intel ordering is the opposite to network byte ordering
	// so just swap bytes all the way through
	// out
	t1 = (val & 0x00ff);
	t2 = (val & 0xff00) >> 8;
	// in
	val = (t1<<8);
	val |= t2;
	// we are now in a non-standard
	return val;
}


/******************************************************************************
 * htoil - host to intel order (long)                                         *
 ******************************************************************************/
uint32_t
PacketStore::htoil(uint32_t value)
{
	uint8_t t1, t2, t3, t4;
	uint32_t val;

	// convert to a standard
	val = htonl(value);

	// check if we need to go any further
	// since Intel order != network order
	if(val != value)
		return(value);

	// we now have a standard, can you read this Intel?, this is
	// STANDARD!

	// we know that Intel ordering is the opposite to network byte ordering
	// so just swap bytes all the way through
	// out
	t1 = (val & 0x000000ff);
	t2 = (val & 0x0000ff00) >> 8;
	t3 = (val & 0x00ff0000) >> 16;
	t4 = (val & 0xff000000) >> 24;
	// in
	val = (t1<<24);
	val |= (t2<<16);
	val |= (t3<<8);
	val |= (t4);

	return(val);
}


/******************************************************************************
 * SetAddress - set the address fiel of the packet (from/to)                  *
 ******************************************************************************/
void
PacketStore::SetAddress(const struct sockaddr_in *_address)
{
	memcpy(&address, _address, sizeof(address));
}


/******************************************************************************
 * MakeReply - generate a reply to an incoming request                        *
 ******************************************************************************/
int
PacketStore::MakeReply(PacketStore &request, Options *config,
	struct sockaddr_in *server_addr)
{
	// local vars
	option *opt = NULL;
	option *req_list = NULL;
	int pkttype = 0;
	uint16_t reqCSA;

	// first, check for a valid bootp packet
	if(BOOTREQUEST != request.op)
	{
		logger->Event(LEVEL_INFO, "MakeReply", 1,
		  "Packet is not a bootp request");
		return(-1);
	}

	// valid client system arch?
	reqCSA = request.GetCSA();
	if((uint16_t)-1 == reqCSA)
	{
		logger->Event(LEVEL_INFO, "MakeReply", 1,
		  "packet contains an unknown client system architecture");
		return(-1);
	}
	
	// see if there are any menus for this packet
	if((uint16_t)-1 == config->CheckMenu(reqCSA))
		return(-1);

	// next, check what the packet type was
	opt = request(43,71);
	if(opt == NULL)
	{
		logger->Event(LEVEL_INFO, "MakeReply", 1,
		  "Received proxy DHCP packet");
		pkttype = 1;
	}
	else
	{
		delete[] opt->data;
		delete opt;
		logger->Event(LEVEL_INFO, "MakeReply", 1,
		  "Received PXE request packet");
		pkttype = 2;
	}

	// was it a valid request packet?
	opt = request(53);
	if((NULL == opt) || (DHCPREQUEST != opt->data[0]))
	{
		logger->Event(LEVEL_INFO, "MakeReply", 1,
		  "Packet does not contain a valid DHCP message type");
		return(-1);
	}
	opt->data[0] = DHCPACK;
	opt->len = 1;
	AddOption(opt);
	delete[] opt->data;
	delete opt;

	// is the param request list ok?
	req_list = request(55);
	if(NULL == req_list)
	{
		logger->Event(LEVEL_INFO, "MakeReply", 1,
			"No parameter request list found");
		return(-1);
	}

	// good packet ok, build reply
	memset(file, 0, 128);
	memset(sname, 0, 64);

	// copy over the basic info
	op = BOOTREPLY;
	htype = request.htype;
	hlen = request.hlen;
	hops = request.hops;
	xid = request.xid;
	secs = request.secs;
	magic_cookie = htonl(MAGIC_COOKIE);

	// slightly harder
	memcpy(chaddr, request.chaddr, 16);
	memset(&ciaddr, 0, sizeof(ciaddr));
	memset(&yiaddr, 0, sizeof(yiaddr));
	memcpy(&siaddr, &(server_addr->sin_addr), sizeof(siaddr));
	memset(&giaddr, 0, sizeof(yiaddr));

	// set the destination address
	memcpy(&address, &(request.address), sizeof(address));

	// good, now go through the option list
	for(int i=0; i < req_list->len; i++)
		switch(req_list->data[i])
		{
		case 43:
			if(-1 == GenOpt43(config, reqCSA, request, pkttype))
				return(-1);
			break;
		case 54:
			GenOpt54();
			break;
		case 60:
			GenOpt60();
			break;
		}

	return(0);
}


/******************************************************************************
 * GenOpt43 - generate reply options                                          *
 ******************************************************************************/
int
PacketStore::GenOpt43(Options *config, int reqCSA, PacketStore &request,
 int pkttype)
{
	option opt, *optptr;
	struct sockaddr_in t_addr;
	char *tmpc;
	int arch_id, menu_id;
	uint8_t req_layer;
	int tmpi;

	opt.major_no = 43;

	// Proxy DHCP packet
	if(1 == pkttype)
	{

		// minor 1
		opt.minor_no = 1;
		t_addr.sin_addr.s_addr = htonl(config->GetMTFTPAddr());
		opt.len = sizeof(t_addr.sin_addr.s_addr);
		opt.data = new uint8_t[opt.len];
		memcpy(opt.data, &(t_addr.sin_addr.s_addr), opt.len);
		AddOption(&opt);
		delete [] opt.data;

		// minor 2 - n.b. Intel byte ordering - do intel have no common sense?
		opt.minor_no = 2;
		t_addr.sin_port = htois(config->GetMTFTPcport());
		opt.len = sizeof(t_addr.sin_port);
		opt.data = new uint8_t[opt.len];
		memcpy(opt.data, &(t_addr.sin_port), opt.len);
		AddOption(&opt);
		delete [] opt.data;

		// minor 3 - again Intel's bel^Wbig endian
		opt.minor_no = 3;
		t_addr.sin_port = htois(config->GetMTFTPsport());
		opt.len = sizeof(t_addr.sin_port);
		opt.data = new uint8_t[opt.len];
		memcpy(opt.data, &(t_addr.sin_port), opt.len);
		AddOption(&opt);
		delete [] opt.data;

		// minor 4
		opt.minor_no = 4;
		opt.len = 1;
		opt.data = new uint8_t[opt.len];
		opt.data[0] = MTFTP_OPEN_TIMEOUT;
		AddOption(&opt);

		// minor 5
		opt.minor_no = 5;
		opt.data[0] = MTFTP_REOPEN_TIMEOUT;
		AddOption(&opt);

		// minor 6
		opt.minor_no = 6;
		opt.data[0] = 0;
		// check for disabled broad/multicast
		if(0 == config->UseMulticast())
			opt.data[0] |= DISABLE_MULTICAST;
		if(0 == config->UseBroadcast())
			opt.data[0] |= DISABLE_BROADCAST;
		AddOption(&opt);
		delete [] opt.data;

		// minor 7
		opt.minor_no = 7;
		t_addr.sin_addr.s_addr = htonl(config->GetMulticast());
		opt.len = sizeof(t_addr.sin_addr.s_addr);
		opt.data = new uint8_t[opt.len];
		memcpy(opt.data, &(t_addr.sin_addr.s_addr), opt.len);
		AddOption(&opt);
		delete [] opt.data;

		// minor 9 - will always be ok, as we checked the menus earlier
		optptr = config->MakeBootMenu(reqCSA, &arch_id, &menu_id);
		optptr->major_no = 43;
		optptr->minor_no = 9;
		AddOption(optptr);
		delete [] optptr->data;
		delete optptr;
	
		// minor 10 - the menu
		opt.minor_no = 10;
		tmpc = config->GetMenuPrompt();
		opt.len = strlen(tmpc)+1;
		opt.data = new uint8_t[opt.len];
		opt.data[0] = config->GetMenuTimeout();
		memcpy(opt.data+1, tmpc, opt.len-1);
		AddOption(&opt);
		delete [] opt.data;
		delete [] tmpc;

		// minor 71
		opt.minor_no = 71;
		opt.len = 4;
		opt.data = new uint8_t[4];
		tmpi = htons(PXE_SERVER_TYPE);
		memcpy(opt.data, &tmpi, 2);
		opt.data[2] = 0;
		req_layer = opt.data[3] = config->GetMinLayer(arch_id, menu_id);
		AddOption(&opt);
		delete [] opt.data;

	}
	else
	// PXE layer request
	{
		arch_id = reqCSA;

		// get option 71 fromthe request packet
		optptr = request(43, 71);
		menu_id = optptr->data[0];
		menu_id <<= 8;
		menu_id |= optptr->data[1];
		// don't worry about the third byte, it is for 'credentials'
		req_layer = optptr->data[3];

		// make the reply option
		opt.minor_no = 71;
		opt.len = 4;
		opt.data = new uint8_t[opt.len];
		memcpy(opt.data, optptr->data, 2);
		opt.data[2] = 0;

		delete [] optptr->data;
		delete optptr;
		// which layer to get next/any
		if(config->CheckLayer(menu_id, arch_id, req_layer) == 0)
		{
			opt.data[3] = req_layer;
		}
		else
		{
			delete [] opt.data;
			return(-1);
		}
		AddOption(&opt);
		delete [] opt.data;
	}

	// make filename
	tmpc = config->MakeFilename(menu_id, arch_id, req_layer);
	if(strlen(tmpc) > 127) // gotta think of a better way
		tmpi =  127;
	else
		tmpi = strlen(tmpc);

	memcpy(file, tmpc, tmpi);
	file[tmpi] = 0;
	delete [] tmpc;

	// cool, send the reply
	return(0);
}


/******************************************************************************
 * GenOpt54 - generate reply options                                          *
 ******************************************************************************/
int
PacketStore::GenOpt54(void)
{
	option opt;

	opt.len = sizeof(siaddr.s_addr);

	opt.major_no = 54;
	opt.minor_no = 0;
	opt.data = new uint8_t[opt.len];
	memcpy(opt.data, &(siaddr.s_addr), opt.len);
	AddOption(&opt);
	delete [] opt.data;
	return(0);
}


/******************************************************************************
 * GenOpt60 - generate reply options                                          *
 ******************************************************************************/
int
PacketStore::GenOpt60(void)
{
	option opt;
	opt.major_no=60;
	opt.minor_no=0;
	opt.len = strlen(DHCP_T60);
	opt.data = new uint8_t[opt.len];
	memcpy(opt.data, DHCP_T60, opt.len);
	AddOption(&opt);
	delete [] opt.data;
	return(0);
}


/******************************************************************************
 * GetCSA - get the Client System Archetecture of the packet                  *
 ******************************************************************************/
uint16_t
PacketStore::GetCSA(void)
{
	uint16_t csa1, csa2;
	option *opt;
	char csac[6];
	int csa1_nok=0;
	int csa2_nok=0;

	// should have received two copies of the csa, compare
	csa1 = csa2 = 0;

	// the binary packed copy
	opt = GetOption(93);
	if(NULL == opt)
		csa1_nok = 1;
	else
	{
		memcpy(&csa1, opt->data, 2);
		csa1 = ntohs(csa1);
		delete [] opt->data;
		delete opt;
	}

	// get the ascii packed version
	opt = GetOption(60);
	if(NULL == opt)
		csa2_nok = 1;
	else if(32 != opt->len) {
		csa2_nok = 1;
		delete [] opt->data;
		delete opt;
	} else {
		memcpy(csac, opt->data+15, 5);
		csac[5] = 0;
		csa2 = atoi(csac);
		delete [] opt->data;
		delete opt;
	}

	// check details
	if((0 == csa1_nok) && (0 == csa2_nok ) && (csa1 == csa2))
		return(checkCSA(csa1));
	else if ((1 == csa1_nok) && (0 == csa2_nok ))
		return(checkCSA(csa2));
	else if ((0 == csa1_nok) && (1 == csa2_nok ))
		return(checkCSA(csa1));
	
	return((uint16_t)-1);
}


/******************************************************************************
 * checkCSA - see if we are supporting the type of arch                       *
 ******************************************************************************/
uint16_t
PacketStore::checkCSA(uint16_t reqCSA)
{
	int i;
	for(i=0; (uint16_t)-1 != CSA_types[i].arch_id; i++)
		if(CSA_types[i].arch_id == reqCSA)
			break;

	return(CSA_types[i].arch_id);
}


/******************************************************************************
 * MakeReply - pack the packet into a raw data form                           *
 ******************************************************************************/
bootp_packet_t *
PacketStore::PackPacket(void)
{
	int pktlen, pos, len43;
	option *optptr;
	bootp_packet_t *opt = new bootp_packet_t;

	// first go through the packet and see how big it is
	pktlen = 241; // 255 closing tag
	optptr = head;
	while(NULL != optptr)
	{
		pktlen += optptr->len+2; // <tag>,<len>,<data>
		optptr = optptr->next;
	}
	optptr = head43;
	if(NULL != optptr)
		pktlen += 3; // 43,<len>,...,255
	len43 = 0;
	while(NULL != optptr)
	{
		
		pktlen += optptr->len+2; // <tag>,<len>,<data>
		len43 = pktlen;
		optptr = optptr->next;
	}
	
	// initalise
	opt->data = new uint8_t[pktlen];
	pos = 0;

	// basic packet info
	opt->data[0] = op;
	opt->data[1] = htype;
	opt->data[2] = hlen;
	opt->data[3] = hops;
	memcpy(opt->data+4, &xid, 4);
	memcpy(opt->data+8, &secs, 2);
	memset(opt->data+10, 0, 2);

	// the addresses
	memcpy(opt->data+12, &(ciaddr.s_addr), 4);
	memcpy(opt->data+16, &(yiaddr.s_addr), 4);
	memcpy(opt->data+20, &(siaddr.s_addr), 4);
	memcpy(opt->data+24, &(giaddr.s_addr), 4);

	// hardware addr
	memcpy(opt->data+28, chaddr, 16);
	// server name
	memcpy(opt->data+44, sname, 64);
	// filename
	memcpy(opt->data+108, file, 128);

	// magic cookie
	memcpy(opt->data+236, &magic_cookie, 4);

	// basic options
	pos = 240;
	optptr = head;
	while(NULL != optptr)
	{
		opt->data[pos] = optptr->major_no;
		opt->data[pos+1] = optptr->len;
		memcpy(opt->data+pos+2, optptr->data, optptr->len);
		pos += optptr->len+2;
		optptr = optptr->next;
	}
	// option 43
	optptr = head43;
	if(NULL != optptr)
	{
		opt->data[pos] = 43;
		opt->data[pos+1] = len43;
		pos+=2;
	}
	while(NULL != optptr)
	{
		opt->data[pos] = optptr->minor_no;
		opt->data[pos+1] = optptr->len;
		memcpy(opt->data+pos+2, optptr->data, optptr->len);
		pos += optptr->len+2;
		optptr = optptr->next;
	}
	if(NULL != head43)
	{
		opt->data[pos] = 255;
		pos++;
	}

	// close the packet off
	opt->data[pos] = 255;
	opt->len=pos;

	return(opt);
}


syntax highlighted by Code2HTML, v. 0.9.1