#define _WINSOCK_DEPRECATED_NO_WARNINGS 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <process.h>

#include "j2534.h"    // j1699 project j2534 declarations
#include "j1699.h"    // j1699 project general declarations
#include "DHCP.h"     // j1699 project DHCP declarations


#define DHCP_TIMEOUT  5000  // 5 seconds

//define DHCP_OPTION_COUNT  10
#define DHCP_OPTION_COUNT  3
unsigned char aucOptionID[DHCP_OPTION_COUNT] =
{
	54,   // Server ID
	51,   // IP Address lease
	1,    // Subnet Mask
//	2,    // Time Offset
//	3,    // Available Routers
//	23,   // Default IP time-to-live
//	15,   // Domain Name
//	58,   // Renewal (T1) time value
//	59,   // Rebinding (T2) time value
//	6     // Available DNSs
};

t_stDHCPOptionLookup OptionType[] = 
{
	"Pad", _0_BYTES,  // Can be used to pad other options so that they are aligned to the word boundary; is not followed by length byte
	"Subnet mask", _4_BYTES,  // Client's subnet mask as per?RFC 950. If both the subnet mask and the router option (option 3) are included, the subnet mask option must be first.
	"Time offset", _4_BYTES,  // Offset of the client's subnet in seconds from Coordinated Universal Time (UTC). The offset is expressed as a two's complement 32-bit integer. A positive offset indicates a location east of the zero meridian and a negative offset indicates a location west of the zero meridian.
	"Router", MULT_OF_4_BYTES,  // Available routers, Should be listed in order of preference
	"Time server", MULT_OF_4_BYTES,  // Available?Time Protocol?servers to synchronise with, Should be listed in order of preference
	"Name server", MULT_OF_4_BYTES,  // Available?IEN 116?name servers, Should be listed in order of preference
	"Domain name server", MULT_OF_4_BYTES,  // Available?DNS?servers, Should be listed in order of preference
	"Log server", MULT_OF_4_BYTES,  // Available log servers, should be listed in order of preference
	"Cookie server", MULT_OF_4_BYTES,  // "fortune cookie" or "quote of the day", a pithy or humorous anecdote often sent as part of a logon process On large computers
	"LPR Server", MULT_OF_4_BYTES,  // A list of?Line Printer Daemon protocol?servers available to the client, Should be listed in order of preference
	"Impress server", MULT_OF_4_BYTES,  // A list of Imagen Impress servers available to the client, Should be listed in order of preference
	"Resource location server", MULT_OF_4_BYTES,  // A list of?Resource Location Protocol?servers available to the client, Should be listed in order of preference
	"Host name", MIN_1_BYTE,  // Name of the client. The name may be qualified with the local domain name.
	"Boot file size", _2_BYTES,  // Length of the boot image in 512B blocks
	"Merit dump file", MIN_1_BYTE,  // Path where crash dumps should be stored
	"Domain name", MIN_1_BYTE,  // 
	"Swap server", _4_BYTES,  // 
	"Root path", MIN_1_BYTE,  // 
	"Extensions path", MIN_1_BYTE,  // 
	"IP forwarding enable/disable", _1_BYTE,  // 
	"Non-local source routing enable/disable", _1_BYTE,  // 
	"Policy filter", MULT_OF_8_BYTES,  // 
	"Maximum datagram reassembly size", _2_BYTES,  // 
	"Default IP time-to-live", _1_BYTE,  // 
	"Path MTU aging timeout", _4_BYTES,  // 
	"Path MTU plateau table", MULT_OF_2_BYTES,  // 
	"Interface MTU", _2_BYTES,  // 
	"All subnets are local", _1_BYTE,  // 
	"Broadcast address", _4_BYTES,  // 
	"Perform mask discovery", _1_BYTE,  // 
	"Mask supplier", _1_BYTE,  // 
	"Perform router discovery", _1_BYTE,  // 
	"Router solicitation address", _4_BYTES,  // 
	"Static route", MULT_OF_8_BYTES,  // A list of destination/router pairs
	"Trailer encapsulation option", _1_BYTE,  // 
	"ARP cache timeout", _4_BYTES,  // 
	"Ethernet encapsulation", _1_BYTE,  // 
	"TCP default TTL", _1_BYTE,  // 
	"TCP keepalive interval", _4_BYTES,  // 
	"TCP keepalive garbage", _1_BYTE,  // 
	"Network information service domain", MIN_1_BYTE,  // 
	"Network information servers", MULT_OF_4_BYTES,  // 
	"Network Time Protocol (NTP) servers", MULT_OF_4_BYTES,  // 
	"Vendor-specific information", MIN_1_BYTE,  // 
	"NetBIOS over TCP/IP name server", MULT_OF_4_BYTES,  // 
	"NetBIOS over TCP/IP datagram Distribution Server", MULT_OF_4_BYTES,  // 
	"NetBIOS over TCP/IP node type", _1_BYTE,  // 
	"NetBIOS over TCP/IP scope", MIN_1_BYTE,  // 
	"X Window System font Server", MULT_OF_4_BYTES,  // 
	"X Window System display manager", MULT_OF_4_BYTES,  // 
	"Requested IP address", _4_BYTES,  // 
	"IP address lease time", _4_BYTES,  // 
	"Option overload", _1_BYTE,  // 
	"DHCP message type", _1_BYTE,  // 
	"Server identifier", _4_BYTES,  // 
	"Parameter request list", MIN_1_BYTE,  // 
	"Message", MIN_1_BYTE,  // 
	"Maximum DHCP message size", _2_BYTES,  // 
	"Renewal (T1) time value", _4_BYTES,  // 
	"Rebinding (T2) time value", _4_BYTES,  // 
	"Vendor class identifier", MIN_1_BYTE,  // 
	"Client identifier", MIN_2_BYTES,  // 
	"NetWare_IP Domain", MIN_1_BYTE,  // 
	"NetWare_IP Option", MIN_1_BYTE,  // 
	"Network Information Service + domain", MIN_1_BYTE,  // 
	"Network Information Service + servers", MULT_OF_4_BYTES,  // 
	"TFTP server name", MIN_1_BYTE,  // 
	"Bootfile name", MIN_1_BYTE,  // 
	"Mobile IP home agent", MULT_OF_4_BYTES,  // 
	"Simple Mail Transfer Protocol (SMTP) server", MULT_OF_4_BYTES,  // 
	"Post Office Protocol (POP3) server", MULT_OF_4_BYTES,  // 
	"Network News Transfer Protocol (NNTP) server", MULT_OF_4_BYTES,  // 
	"Default World Wide Web (WWW) server", MULT_OF_4_BYTES,  // 
	"Default Finger protocol Server", MULT_OF_4_BYTES,  // 
	"Default Internet Relay Chat (IRC) server", MULT_OF_4_BYTES,  // 
	"StreetTalk server", MULT_OF_4_BYTES,  // 
	"StreetTalk Directory Assistance (STDA) server", MULT_OF_4_BYTES,  // 
};
WORD OptionTypeSize = sizeof ( OptionType );


// DHCP Message defaults
unsigned long ulSubnetMask       = 0x00FFFFFF;
unsigned long ulTimeOffset       = 0;
unsigned long ulLeaseTime        = 86400;
unsigned long ulRenewalTime      = 43200;
unsigned long ulRebindingTime    = 75600;
unsigned char aucClientID[7]     = { 0x01, 0xB0, 0x25, 0xaa, 0x2C, 0xBC, 0x1B };
unsigned char aucDNS[8]          = { 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4C, 0x4C };
unsigned char aucRequestList[14] = { 0x01, 0x03, 0x06, 0x0F, 0x1F, 0x21, 0x2B, 0x2C, 0x2E, 0x2F, 0x77, 0x79, 0xF9, 0xFC };
unsigned char ucTimetoLive       = 0x40;
char          acDomainName[21]   = "8.8.8.8";
char          acComputerName[16] = "Tester";
char          acVendorClass[9]   = "MSFT 5.0";
unsigned char aucDomainName[18]  = { 0x00, 0x00, 0x00 };

unsigned char aucDHCPMessage[MAX_DHCP_RSP];

t_stDHCPEntity  DHCPList[MAX_DHCP_ENTITIES];
BYTE            DHCP_Entity_Count = 0;

boolean         bDHCP_Server_On = TRUE;

extern unsigned int  BindDelay;
extern unsigned int  BindCountMax;


/*******************************************************************************
**
**  Function:  BuildDHCPResponse
**
**  Purpose:   
**
*******************************************************************************/
int BuildDHCPResponse ( unsigned long ulSiaddr )
{
	int               iOffset = sizeof ( t_stDHCPMessage ) - 1;
	t_stDHCPMessage  *pstDHCPMessage = (t_stDHCPMessage *)aucDHCPMessage;
	unsigned long     ulTemp;


	// Initialize the message structure to zero values
	memset ( &aucDHCPMessage, 0, MAX_DHCP_RSP );

	// Set the non zero fixed values
	pstDHCPMessage->ucOp = 2;        // BOOT REPLY
	pstDHCPMessage->ucHType = 1;     // ETHERNET
	pstDHCPMessage->ucHLen = 6;
//	pstDHCPMessage->ulSiaddr = ulSiaddr;
	pstDHCPMessage->ulMagicCookie = htonl(DHCPMAGICCOOKIE);
	pstDHCPMessage->stOption.ucType = 53;
	pstDHCPMessage->stOption.ucLength = 1;
	pstDHCPMessage->stOption.ucValue = 2;  // offer by default

	for ( int j = 0;
	      j < DHCP_OPTION_COUNT;
	      j++ )
	{
		int             iByteCount = 0;
		unsigned char  *pucData = NULL;

		switch ( aucOptionID[j] )
		{
			case 1:
				iByteCount = 4;
				pucData = (unsigned char*)&ulSubnetMask;
				break;

			case 2:
				iByteCount = 4;
				ulTemp = htonl ( ulTimeOffset );
				pucData = (unsigned char*)&ulTemp;
				break;

			case 3:
			case 54:
				iByteCount = 4;
				pucData = (unsigned char*)&ulSiaddr;
				break;

			case 6:
				iByteCount = 8;
				pucData = aucDNS;
				break;

			case 15:
				iByteCount = 20;
				pucData = (unsigned char*)acDomainName;
				break;

			case 23:
				iByteCount = 1;
				pucData = &ucTimetoLive;
				break;

			case 51:
				iByteCount = 4;
				ulTemp = htonl ( ulLeaseTime );
				pucData = (unsigned char*)&ulTemp;
				break;

			case 58:
				iByteCount = 4;
				ulTemp = htonl ( ulRenewalTime );
				pucData = (unsigned char*)&ulTemp;
				break;

			case 59:
				iByteCount = 4;
				ulTemp = htonl ( ulRebindingTime );
				pucData = (unsigned char*)&ulTemp;
				break;
		}  // end switch ( aucOptionID[j] )

		if ( iByteCount )
		{
			aucDHCPMessage[iOffset++] = aucOptionID[j];
			aucDHCPMessage[iOffset++] = iByteCount;
			memcpy ( &aucDHCPMessage[iOffset], pucData, iByteCount );
			iOffset += iByteCount;
		}
	}  // end for ( j < DHCP_OPTION_COUNT )

	aucDHCPMessage[iOffset++] = 0xFF;

	return iOffset;
}


/*******************************************************************************
**
**  Function:  DHCPServer
**
**  Purpose:   
**
*******************************************************************************/
STATUS DHCPServer ( void )
{
	TIMEVAL  stTimeVal;
	FD_SET   stReadFD;
	FD_SET   stExceptFD;
	int      iStatus;

	BYTE     aucReadMessage[PACKET_SIZE];  // received messge data
//	SOCKADDR_IN      remote_sin;  // received message data source address
//	int              addr_len;    // length of source address

	boolean  bIgnore = FALSE;

	DWORD    ulNextAddress;       // the next address to be assigned
	INT      iRspLen;             // length of DHCP response message

	DWORD    DHCPEndTimestampMsecs;

	char    *pErrorString;
	BYTE     State      = DHCPDISCOVER;

	unsigned short  BindCount = 0;


	////////////////////////////////////////////////
	// Create a UDP socket
	////////////////////////////////////////////////
	do
	{
		Sleep ( BindDelay );
		if ( OpenSocket ( &stDHCPSocketData ) == PASS )
		{
			break;
		}
		else
		{
			CloseSocket ( &stDHCPSocketData);
		}
	}
	while ( ++BindCount < BindCountMax );
	if ( stDHCPSocketData.iError != 0 )
	{
		return stDHCPSocketData.iError;
	}

	// Setup the address for the the broadcast request
	stDHCPSocketData.remote_sin.sin_family = AF_INET;
	stDHCPSocketData.remote_sin.sin_addr.S_un.S_addr = INADDR_BROADCAST;
	stDHCPSocketData.remote_sin.sin_port = htons ( DHCP_RMT_PORT );  // port number for the client


	////////////////////////////////////////////////
	// construct the base DCHP message
	////////////////////////////////////////////////
	iRspLen = BuildDHCPResponse ( stDHCPSocketData.pAddr->ulIPv4Address );

	//  offered addresses start at tester's address +1
	ulNextAddress = stDHCPSocketData.pAddr->ulIPv4Address + 0x01000000;


	////////////////////////////////////////////////
	// clear DHCP Entity structure array
	////////////////////////////////////////////////
	// if this is the first run, clear everything
	if ( DHCP_Entity_Count == 0 )
	{
		for ( int i = 0;
		      i < MAX_DHCP_ENTITIES;
		      i++ )
		{
			memset ( &DHCPList[i], 0, sizeof ( t_stDHCPEntity ) );
		}
	}
	// later runs, just clear transaction ID
	else
	{
		for ( int i = 0;
		      i < DHCP_Entity_Count;
		      i++ )
		{
			memset ( &DHCPList[i].TransID[0], 0, sizeof ( DHCPList[i].TransID ) );
		}
	}


	////////////////////////////////////////////////
	// handle DHCP transactions
	////////////////////////////////////////////////
	Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
	      "Waiting for DHCP transactions ...\n" );

	DHCPEndTimestampMsecs = GetTickCount ( ) + DHCP_TIMEOUT;  // Get the end time for DHCP message check
	do
	{
		// Check to see if the vehicle was found on another address or the 
		// operation is aborted
		if ( stDHCPSocketData.bStop )
		{
			CloseSocket ( &stDHCPSocketData );
			return FAIL;
		}

		// check for timeout
		stTimeVal.tv_sec = 1000 / 1000;
		stTimeVal.tv_usec = (1000 % 1000) * 1000;

		FD_ZERO ( &stReadFD );
		FD_SET ( stDHCPSocketData.hSock, &stReadFD );

		FD_ZERO ( &stExceptFD );
		FD_SET ( stDHCPSocketData.hSock, &stExceptFD );

		// Returns when there is data to be read or/and exception or a timeout
		iStatus = select ( 1, &stReadFD, NULL, &stExceptFD, &stTimeVal );

		// If error 
		if ( iStatus == SOCKET_ERROR )
		{
			stDHCPSocketData.iError = WSAGetLastError();
			pErrorString = GetEthernetErrorString ( stDHCPSocketData.iError );
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "DHCP Socket select Error: %d: %s\n",
			      stDHCPSocketData.iError,
			      pErrorString );
//			return FAIL;
		}

		// if not Timeout, read data and act accordingly
		else if ( iStatus != 0 )
		{
			// read the message from client and copy it in buffer
			int iBytesRead = recv ( stDHCPSocketData.hSock, aucReadMessage, sizeof(aucReadMessage) - 1, 0 ); 
//			int iBytesRead = recvfrom ( stDHCPSocketData.hSock, aucReadMessage, sizeof(aucReadMessage) - 1, 0,
//			                            (struct sockaddr *)&remote_sin, &addr_len ); 
			if ( iBytesRead == 0 )
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "DHCP Socket read: 0 bytes\n" );

			}
			
			else if ( iBytesRead < 0 )
			{
				stDHCPSocketData.iError = -iBytesRead;
				pErrorString = GetEthernetErrorString ( stDHCPSocketData.iError );
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "DHCP Socket read Error: %d: %s\n",
				      stDHCPSocketData.iError,
				      pErrorString );
//				return FAIL;
			}

			else if ( iBytesRead > 0 )
			{
				// print buffer which contains the client message
				DisplayUDPMessage ( (BYTE*)&aucReadMessage, iBytesRead, TRUE );

				t_stDHCPMessage* pstDHCPRxMsg = (t_stDHCPMessage*)aucReadMessage;
				// if this is a DHCP message
				if ( pstDHCPRxMsg->ulMagicCookie == htonl ( DHCPMAGICCOOKIE) )
				{
					// print decoded DHCP data
					DisplayDHCPMessage ( (t_stDHCPMessage*)aucReadMessage );

					// if received a DHCPDISCOVER or DHCPREQUEST message, build a response
					if ( pstDHCPRxMsg->stOption.ucValue == DHCPDISCOVER ||
					     pstDHCPRxMsg->stOption.ucValue == DHCPREQUEST )
					{
						t_stDHCPMessage* pstDHCPTxMsg = (t_stDHCPMessage*)aucDHCPMessage;
						bIgnore = FALSE;

						// if received a DHCPDISCOVER, build a DHCPOFFER response
						if ( pstDHCPRxMsg->stOption.ucValue == DHCPDISCOVER )
						{
							int i;
							
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "Received a DHCP Discover Message.\n" );
							pstDHCPTxMsg->stOption.ucValue = DHCPOFFER;

							// if this DHCP Discover message is from the tester, ignore it
							if ( memcmp ( &pstDHCPRxMsg->aucChaddr, &AdapterInfo.MAC_Address, 6 ) == 0 )
							{
								Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "DHCP Discover Message from Tester IGNORED\n" );
								bIgnore = TRUE;
							}

							else
							{
								// check if this MAC (hardware) address already has an IP address assigned
								for ( i = 0;
								      i < DHCP_Entity_Count;
								      i++ )
								{

									// if this transaction ID address has already been responded to, ignore it
									if ( memcmp ( &pstDHCPRxMsg->ulXId, &DHCPList[i].TransID, 4 ) == 0 )
									{
										Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
										      "DHCP Discover Message with same transaction ID IGNORED\n" );
										bIgnore = TRUE;

										break;
									}

									// if this MAC address already has an IP address assigned, copy it to the ACK message
									else if ( memcmp ( &pstDHCPRxMsg->aucChaddr, &DHCPList[i].MacAddr, 6 ) == 0 )
									{
										memcpy ( &pstDHCPTxMsg->ulYiaddr, &DHCPList[i].IPAddr, 4 );

										break;
									}
								}

								// if this MAC (hardware) address doesn't already have an entry,
								// add it and copy it to the ACK message
								if ( bIgnore == FALSE &&
								     i == DHCP_Entity_Count )
								{
									memcpy ( &DHCPList[i].MacAddr, &pstDHCPRxMsg->aucChaddr, 6 );
									memcpy ( &DHCPList[i].TransID, &pstDHCPRxMsg->ulXId, 4 );
									while ( Ping ( ulNextAddress ) != 0 &&
									        (ulNextAddress & 0xFF000000) != 0xFF000000 )
									{
										ulNextAddress = ulNextAddress + 0x01000000;
									}
									memcpy ( &DHCPList[i].IPAddr, &ulNextAddress, 4 );
									DHCPList[i].Status = 0;
									DHCP_Entity_Count++;

									pstDHCPTxMsg->ulYiaddr = ulNextAddress;

									ulNextAddress = ulNextAddress + 0x01000000;
								}
							}
						}
						// if received a DHCPREQUEST, build a DHCPACK response
						else
						{
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "Received a DHCP Request Message.\n" );

							pstDHCPTxMsg->stOption.ucValue = DHCPACK;
						}

						if ( bIgnore == FALSE )
						{
							memcpy ( &pstDHCPTxMsg->aucChaddr, &pstDHCPRxMsg->aucChaddr, 6 );
							pstDHCPTxMsg->ulXId = pstDHCPRxMsg->ulXId;
							pstDHCPTxMsg->usFlags = pstDHCPRxMsg->usFlags;

							// print buffer which contains the client message
							DisplayUDPMessage ( (BYTE*)&aucDHCPMessage, iRspLen, FALSE );
							// print decoded DHCP data
							DisplayDHCPMessage ( (t_stDHCPMessage*)aucDHCPMessage );

							int iBytesWritten = sendto ( stDHCPSocketData.hSock, (char*)&aucDHCPMessage, iRspLen, 0,
							                             (SOCKADDR*)&stDHCPSocketData.remote_sin, sizeof(SOCKADDR_IN) );
							if ( iBytesWritten != iRspLen )
							{
								stDHCPSocketData.iError = WSAGetLastError();
								pErrorString = GetEthernetErrorString ( stDHCPSocketData.iError );
								Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "DHCP Socket write Error: %d: %s\n",
								      stTCPSocketData.iError,
								      pErrorString );
//								return FAIL;
							}

							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "Sent a DHCP %s Message.\n",
							      pstDHCPTxMsg->stOption.ucValue == DHCPOFFER ? "Offer" : "ACK" );

							if (pstDHCPTxMsg->stOption.ucValue == DHCPACK )
							{
								Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "DHCP transactions complete.\n" );
							}
						}  // end if ( bIgnore == FALSE )
					}  // end if ( DHCPDISCOVER or DHCPREQUEST message )
					// otherwise, handle non-DHCPDISCOVER and non-DHCPREQUEST message
					else
					{
						Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
						      "Received Non-DORA DHCP UDP Message\n" );
					}
				}
				// otherwise received a non-DHCP messge
				else
				{
					Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
					      "A non-DHCP UDP message (unable to decode)\n" );
				}
			}  // end if ( iBytesRead > 0 )
		}  // end if ( iStatus != 0 )
	}
	while ( GetTickCount ( ) < DHCPEndTimestampMsecs );

	// completed DHCP transactions, no longer need DHCP socket
	CloseSocket ( &stDHCPSocketData );

	return PASS;
}




/*******************************************************************************
**
**  Function:  DisplayDHCPMessage
**
**  Purpose:   
**
*******************************************************************************/
void DisplayUDPMessage ( BYTE *pMsg, WORD MsgLen, boolean bRx )
{
	char  DataString[PACKET_SIZE * 3];


	Log ( BLANK, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "\n\n" );

	DataString[0] = (BYTE)NULL;
	sprintf_s ( &DataString[strlen ( DataString )], (PACKET_SIZE * 3) - strlen ( DataString ),
	          "%s UDP Msg: %10luusec ",
	           bRx ? "Rx" : "Tx",
	           GetTickCount ( ) );
	for ( int n = 0; n < MsgLen; n++ )
	{
		sprintf_s ( &DataString[strlen ( DataString )], (PACKET_SIZE * 3) - strlen ( DataString ),
		            " %02X", pMsg[n] );
	}

	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "%s\n\n",
	      DataString );
}


/*******************************************************************************
**
**  Function:  DisplayDHCPMessage
**
**  Purpose:   
**
*******************************************************************************/
void DisplayDHCPMessage ( t_stDHCPMessage* pstDHCPMsg )
{
	BYTE            n;
	BYTE            count = 1;
	t_stDHCPOption *pOpt;
	BYTE           *pValue;
	char            String[PACKET_SIZE * 3];

	Log ( BLANK, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "\n" );
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "DHCP message Decode:\n" );
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "OpCode:     %d\n",    pstDHCPMsg->ucOp );
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "HType:      %d\n",    pstDHCPMsg->ucHType );
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "HLen:       %d\n",    pstDHCPMsg->ucHLen );
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "Hops:       %d\n",    pstDHCPMsg->ucHops );
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "XId:        %ld\n",   pstDHCPMsg->ulXId );
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "Secs:       %d\n",    pstDHCPMsg->usSecs );
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "Flags:     $%02X%02X\n",     pstDHCPMsg->usFlags  & 0xFF, (pstDHCPMsg->usFlags>>8)  & 0xFF );
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "Ciaddr:     %d.%d.%d.%d\n",  pstDHCPMsg->ulCiaddr & 0xFF, (pstDHCPMsg->ulCiaddr>>8) & 0xFF, (pstDHCPMsg->ulCiaddr>>16) & 0xFF, (pstDHCPMsg->ulCiaddr>>24) & 0xFF );
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "Yiaddr:     %d.%d.%d.%d\n",  pstDHCPMsg->ulYiaddr & 0xFF, (pstDHCPMsg->ulYiaddr>>8) & 0xFF, (pstDHCPMsg->ulYiaddr>>16) & 0xFF, (pstDHCPMsg->ulYiaddr>>24) & 0xFF );
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "Siaddr:     %d.%d.%d.%d\n",  pstDHCPMsg->ulSiaddr & 0xFF, (pstDHCPMsg->ulSiaddr>>8) & 0xFF, (pstDHCPMsg->ulSiaddr>>16) & 0xFF, (pstDHCPMsg->ulSiaddr>>24) & 0xFF );
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "Giaddr:     %d.%d.%d.%d\n",  pstDHCPMsg->ulGiaddr & 0xFF, (pstDHCPMsg->ulGiaddr>>8) & 0xFF, (pstDHCPMsg->ulGiaddr>>16) & 0xFF, (pstDHCPMsg->ulGiaddr>>24) & 0xFF );
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "Chaddr:     %d.%d.%d.%d.%d.%d\n",  pstDHCPMsg->aucChaddr[0], pstDHCPMsg->aucChaddr[1], pstDHCPMsg->aucChaddr[2], pstDHCPMsg->aucChaddr[3], pstDHCPMsg->aucChaddr[4], pstDHCPMsg->aucChaddr[5] );
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "Sname:      %s\n",  pstDHCPMsg->acSname );
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "File:       %s\n",  pstDHCPMsg->acFile );
	pOpt = &pstDHCPMsg->stOption;
	while ( pOpt->ucType != 0xFF && count < 100 )
	{
		String[0] = (BYTE)NULL;
		sprintf_s ( &String[strlen ( String )], (PACKET_SIZE * 3) - strlen ( String ),
		            "Option %3d:  Type: %3d  Length: %3d  Value(s):",
		             count++,   pOpt->ucType, pOpt->ucLength );
		pValue = &pOpt->ucValue;
		for ( n = 0; n < pOpt->ucLength; n++ )
		{
			sprintf_s ( &String[strlen ( String )], (PACKET_SIZE * 3) - strlen ( String ),
			            " %3d",
			            *pValue++ );
		}
		Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
		      "%s\n", String );
		pOpt = (t_stDHCPOption *)pValue;
	}
	Log ( BLANK, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "\n" );
}
