/*-------------------------------------------------------------------------
   DHCP function copy right information

   Copyright (C) 2006-2009. Tang, Li      <tangli@palmmicro.com>
                            Lin, Rongrong <woody@palmmicro.com>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This software 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
-------------------------------------------------------------------------*/

#include "version.h"
#include "type.h"
#include "ar168.h"
#include "core.h"
#include "apps.h"
#include "tcpip.h"

/* Dynamic Host Configuration Protocol (see RFC 2131, 1533) */
#define DHCP_SERVER_PORT	67
#define DHCP_CLIENT_PORT	68

#define DHCP_MIN_PACKET_SIZE	300

/* Field offset of an BOOTP&DHCP packet (following the UDP header) */
#define DHCP_OP			0	/* specifies whether the message is a request(1) or a reponse(2) */
#define DHCP_HTYPE		1	/* network hardware type (ehernet has type 1) */
#define DHCP_HLEN		2	/* length of hardware address length */
#define DHCP_HOPS		3	/* allow bootstraping across multiple routers */
#define DHCP_XID		4	/* an integer used to match response with request */
#define DHCP_SECS		8	/* number of seconds since the client boot */
#define DHCP_FLAGS		10	/* flags field */
#define DHCP_CIADDR		12	/* client know about its IP address */
#define DHCP_YIADDR		16	/* server assigned IP address */
#define DHCP_SIADDR		20	/* server IP address */
#define DHCP_GIADDR		24	/* router IP address */
#define DHCP_CHADDR		28	/* client hardware address */
#define DHCP_SNAME		44	/* server host name */
#define DHCP_FILE		108	/* boot file name */
#define DHCP_OPTIONS	236	/* DHCP options begins, 4 bytes magic cookie*/
#define DHCP_OPLIST		240	/* DHCP options list begins*/

/* DHCP options item = code + length + value */
#define DHCP_OPCODE_PADDING			0
#define DHCP_OPCODE_SUBNET_MASK		1
#define DHCP_OPCODE_DAY_TIME		2
#define DHCP_OPCODE_ROUTER_IP		3
#define DHCP_OPCODE_TIME_IP			4
#define DHCP_OPCODE_DNS_IP			6
#define DHCP_OPCODE_REQUESTED_IP	50
#define DHCP_OPCODE_LEASE_TIME		51	/* in units of seconde */
#define DHCP_OPCODE_OPTION_OVERLOAD	52
#define DHCP_OPCODE_MESSAGE_TYPE	53
#define DHCP_OPCODE_SERVER_ID		54	/* this is also server IP */ 
#define DHCP_OPCODE_PARAM_LIST		55
#define DHCP_OPCODE_MESSAGE			56	/* provide an error message */
#define DHCP_OPCODE_MAX_SIZE		57	/* minimum leagal value is 576 */
#define DHCP_OPCODE_RENEWAL_TIME	58	/* T1 */
#define DHCP_OPCODE_REBIND_TIME		59
#define DHCP_OPCODE_CLIENT_ID		61
#define DHCP_OPCODE_TFTP_SERVER		66
#define DHCP_OPCODE_RESERVED		128
#define DHCP_OPCODE_END				255

/* DHCP options overload item value */
#define DHCP_OVERLOAD_FILE		1
#define DHCP_OVERLOAD_SNAME		2
#define DHCP_OVERLOAD_BOTH		3

/* DHCP options message item value */
#define DHCP_MTYPE_DISCOVER		1
#define DHCP_MTYPE_OFFER		2
#define DHCP_MTYPE_REQUEST		3
#define DHCP_MTYPE_DECLINE		4
#define DHCP_MTYPE_ACK			5
#define DHCP_MTYPE_NACK			6
#define DHCP_MTYPE_RELEASE		7
#define DHCP_MTYPE_INFORM		8
#define DHCP_MTYPE_NONE			255

#define DHCP_OP_REQUEST		1
#define DHCP_OP_REPLY		2

#define DHCP_FLAG_BROADCAST		0x80

// Dhcp state
#define DHCP_STATE_INIT			0
#define DHCP_STATE_SELECTING	1
#define DHCP_STATE_REQUESTING	2
#define DHCP_STATE_BOUND		3
#define DHCP_STATE_RENEWING		4
#define DHCP_STATE_REBINDING	5
#define DHCP_STATE_CHECKING		6

#define DHCP_FLAGS_LEN		2
#define DHCP_SECS_LEN		2
#define DHCP_XID_LEN		4
#define MAGIC_COOKIE_LEN	4

// Dhcp Timer
#define DHCP_RETRY_TIMEOUT		6
#define DHCP_MAX_RETRY			5

const UCHAR _cMagicCookie[MAGIC_COOKIE_LEN] = {99, 130, 83, 99};
const UCHAR _cPacketHead[4] = {DHCP_OP_REQUEST, 1, HW_ALEN, 0};
const UCHAR _cBroadcastFlags[2] = {0x80, 0x00};

void DhcpRun(UDP_SOCKET pUcb);

UDP_SOCKET _pDhcpSocket;

UCHAR _iDhcpState, _iDhcpTimer, _iDhcpRetry;
UCHAR _pDhcpXID[DHCP_XID_LEN];

UCHAR _pDhcpServerIP[IP_ALEN];
UCHAR _pAssignedIP[IP_ALEN];

ULONG _lLeaseTime, _lT1, _lT2;
ULONG _lCurrent;

#ifdef OEM_AUTOTEST
UDP_SOCKET Dhcpd_pSocket;
void DhcpdRun(UDP_SOCKET pUcb);
#endif

void _SendPacket(UCHAR iOptionsLen)
{
	USHORT sLength;
	PCHAR pBuf;

	pBuf = Adapter_pUdpBuf;

	memcpy4(pBuf, _cPacketHead);
	memcpy4((PCHAR)(pBuf+DHCP_XID), _pDhcpXID);
	memset((PCHAR)(pBuf+DHCP_SECS), 0, DHCP_SECS_LEN);
	memset((PCHAR)(pBuf+DHCP_YIADDR), 0, DHCP_OPTIONS - DHCP_YIADDR);
	memcpy((PCHAR)(pBuf+DHCP_CHADDR), Sys_pMacAddress, HW_ALEN);
	memcpy4((PCHAR)(pBuf+DHCP_OPTIONS), _cMagicCookie);

	sLength = iOptionsLen;
	sLength += DHCP_OPLIST;
	if (sLength < DHCP_MIN_PACKET_SIZE)
	{
		memset((PCHAR)(pBuf + sLength), 0, (UCHAR)(DHCP_MIN_PACKET_SIZE - sLength));
		sLength = DHCP_MIN_PACKET_SIZE;
	}

	if (_iDhcpState == DHCP_STATE_RENEWING)
	{
		UdpSendTo(_pDhcpSocket, sLength, _pDhcpServerIP, DHCP_SERVER_PORT);
	}
	else
	{
		UdpSendTo(_pDhcpSocket, sLength, _pBroadcastIP, DHCP_SERVER_PORT);
	}
}

UCHAR _FillMsgType(PCHAR pBuf, UCHAR iMsgType)
{
	pBuf[0] = DHCP_OPCODE_MESSAGE_TYPE;
	pBuf[1] = 1;
	pBuf[2] = iMsgType;
	return 3;
}

UCHAR _FillClientId(PCHAR pBuf)
{
	pBuf[0] = DHCP_OPCODE_CLIENT_ID;
	pBuf[1] = 1 + HW_ALEN;
	pBuf[2] = 1;
	memcpy((PCHAR)(pBuf + 3), Sys_pMacAddress, HW_ALEN);
	return (3 + HW_ALEN);
}

#define PARAM_LIST_LEN	5
const UCHAR _cParamList[PARAM_LIST_LEN] = {DHCP_OPCODE_PARAM_LIST, 3, DHCP_OPCODE_SUBNET_MASK,	DHCP_OPCODE_ROUTER_IP, DHCP_OPCODE_DNS_IP}; 
UCHAR _FillParamList(PCHAR pBuf)
{
	memcpy(pBuf, _cParamList, PARAM_LIST_LEN);
	return (UCHAR)PARAM_LIST_LEN;
}

#define MAX_SIZE_LEN	4
const UCHAR _cMaxSize[MAX_SIZE_LEN] = {DHCP_OPCODE_MAX_SIZE, 2, 4, 0};
UCHAR _FillMaxSize(PCHAR pBuf)
{
	memcpy4(pBuf, _cMaxSize);
	return (UCHAR)MAX_SIZE_LEN;
}

UCHAR _FillIPAddr(PCHAR pBuf, PCHAR pIPAddr, UCHAR iOpCode)
{
	pBuf[0] = iOpCode;
	pBuf[1] = IP_ALEN;
	memcpy4((PCHAR)(pBuf + 2), pIPAddr);
	return (UCHAR)(2 + IP_ALEN);
}

void _DhcpAck()
{
	UdpDebugString("DHCP bound");
	UdpClose(_pDhcpSocket);

	Net_bConnected = TRUE;
	Dhcp_bRenewing = FALSE;
	_lCurrent = 0;
	_iDhcpState = DHCP_STATE_BOUND;
}

void _DhcpNack()
{
	UdpClose(_pDhcpSocket);
	_iDhcpState = DHCP_STATE_INIT;
	Net_bConnected = FALSE;
	Dhcp_bRenewing = FALSE;
#ifdef OEM_IP20
	TaskUIHandler(UI_STBY_DISPLAY_IDS2, IDS_DHCP_FAILED);
#else
	TaskUIHandler(UI_STBY_DISPLAY_IDS0, IDS_DHCP_FAILED);
#endif
}

void _DhcpDone()
{
	memcpy4(Sys_pIpAddress, _pAssignedIP); 
	_DhcpAck();
	TaskUIHandler(UI_NET_CONNECTED, 0);
}

void DhcpOpenUdp()
{
	_pDhcpSocket = UdpListen(DHCP_CLIENT_PORT, NET_BANK_OFFSET, (USHORT)DhcpRun);
}

void DhcpInit()
{
	rand_array(_pDhcpXID, DHCP_XID_LEN);
	_iDhcpState = DHCP_STATE_INIT;
	_iDhcpRetry = 0;
	_lLeaseTime = 0;
	_lCurrent = 0;

	DhcpOpenUdp();
}

#ifdef OEM_INNOMEDIA
#define INNOMEDIA_PARAM_LEN		11
const UCHAR _cInnomediaParam[INNOMEDIA_PARAM_LEN] = {60, 9, 'i','n','n','o','m','e','d','i','a'};
#endif
void DhcpDiscover()
{
	UCHAR iLen;
	PCHAR pOptions;
	PCHAR pBuf;

	pBuf = Adapter_pUdpBuf;
	pOptions = (PCHAR)(pBuf + DHCP_OPLIST);
	iLen = _FillMsgType(pOptions, DHCP_MTYPE_DISCOVER);
	iLen += _FillClientId((PCHAR)(pOptions + iLen));
	iLen += _FillMaxSize((PCHAR)(pOptions + iLen));
#ifdef OEM_INNOMEDIA
	memcpy((PCHAR)(pOptions + iLen), _cInnomediaParam, INNOMEDIA_PARAM_LEN);
	iLen += INNOMEDIA_PARAM_LEN;
#endif
	pOptions[iLen] = DHCP_OPCODE_END;
	iLen ++;

	// set flags and ciaddr
	pBuf[DHCP_FLAGS] = DHCP_FLAG_BROADCAST;
	pBuf[DHCP_FLAGS+1] = 0;
	memset((PCHAR)(pBuf+DHCP_CIADDR), 0, IP_ALEN);

	_SendPacket(iLen);
	_iDhcpState = DHCP_STATE_SELECTING;
	// Set this timer to avoid users to wait overly long before give up
	_iDhcpTimer = 0;
}

void DhcpDecline()
{
	UCHAR iLen;
	PCHAR pOptions;
	PCHAR pBuf;

	pBuf = Adapter_pUdpBuf;
	pOptions = (PCHAR)(pBuf + DHCP_OPLIST);
	iLen = _FillMsgType(pOptions, DHCP_MTYPE_DECLINE);
	iLen += _FillIPAddr((PCHAR)(pOptions + iLen), _pAssignedIP, DHCP_OPCODE_REQUESTED_IP);
	iLen += _FillIPAddr((PCHAR)(pOptions + iLen) ,_pDhcpServerIP, DHCP_OPCODE_SERVER_ID);
	iLen += _FillClientId((PCHAR)(pOptions + iLen));
	pOptions[iLen] = DHCP_OPCODE_END;
	iLen ++;
	// set flags and ciaddr
	pBuf[DHCP_FLAGS] = 0;
	pBuf[DHCP_FLAGS+1] = 0;
	memset((PCHAR)(pBuf+DHCP_CIADDR), 0, IP_ALEN);

	_SendPacket(iLen);
}

void _DhcpRequest()
{
	UCHAR iLen;
	PCHAR pOptions;
	PCHAR pBuf;

	pBuf = Adapter_pUdpBuf;
	pOptions = (PCHAR)(pBuf + DHCP_OPLIST);
	iLen = _FillMsgType(pOptions, DHCP_MTYPE_REQUEST);
	iLen += _FillIPAddr((PCHAR)(pOptions + iLen), _pAssignedIP, DHCP_OPCODE_REQUESTED_IP);
	iLen += _FillIPAddr((PCHAR)(pOptions + iLen) ,_pDhcpServerIP, DHCP_OPCODE_SERVER_ID);
	iLen += _FillClientId((PCHAR)(pOptions + iLen));
	iLen += _FillParamList((PCHAR)(pOptions + iLen));
	iLen += _FillMaxSize((PCHAR)(pOptions + iLen));
	pOptions[iLen] = DHCP_OPCODE_END;
	iLen ++;
	// set flags and ciaddr
	pBuf[DHCP_FLAGS] = DHCP_FLAG_BROADCAST;
	pBuf[DHCP_FLAGS+1] = 0;
	memset((PCHAR)(pBuf+DHCP_CIADDR), 0, IP_ALEN);

	_SendPacket(iLen);
	_iDhcpTimer = 0;
}

void _DhcpReqRenew()
{
	UCHAR iLen;
	PCHAR pOptions;
	PCHAR pBuf;

	pBuf = Adapter_pUdpBuf;
	pOptions = (PCHAR)(pBuf + DHCP_OPLIST);
	iLen = _FillMsgType(pOptions, DHCP_MTYPE_REQUEST);
	iLen += _FillClientId((PCHAR)(pOptions + iLen));
	iLen += _FillParamList((PCHAR)(pOptions + iLen));
	iLen += _FillMaxSize((PCHAR)(pOptions + iLen));
	pOptions[iLen] = DHCP_OPCODE_END;
	iLen ++;
	// set flags and ciaddr
//	pBuf[DHCP_FLAGS] = 0;
//	pBuf[DHCP_FLAGS+1] = 0;
	USHORT2PCHAR(0, (PCHAR)(pBuf + DHCP_FLAGS));
	memcpy4((PCHAR)(pBuf + DHCP_CIADDR), Sys_pIpAddress);

	_SendPacket(iLen);
}

void DhcpRun(UDP_SOCKET pUcb)
{
	UCHAR iLength, iType, iMsgType;
	PCHAR pOption;
	PCHAR pBuf;
	USHORT sIndex;
	UCHAR pServerIP[IP_ALEN];
	UCHAR pSubnetMask[IP_ALEN];
	UCHAR pRouteIP[IP_ALEN];
	UCHAR pDnsIP[IP_ALEN];
	UCHAR pDnsIP2[IP_ALEN];
	BOOLEAN bDns2;
	ULONG lLeaseTime, lT1, lT2;
#ifdef OEM_INNOMEDIA
	BOOLEAN bHasProvUrl;
#endif

	if (pUcb != _pDhcpSocket)	return;

	pBuf = Adapter_pUdpBuf;

	if (_iDhcpState == DHCP_STATE_CHECKING || 
		pBuf[DHCP_OP] != DHCP_OP_REPLY || 
		memcmp((PCHAR)(pBuf + DHCP_XID), _pDhcpXID, DHCP_XID_LEN) ||
		memcmp((PCHAR)(pBuf + DHCP_CHADDR), Sys_pMacAddress, HW_ALEN))
	{
		return;
	}

	if (_iDhcpState == DHCP_STATE_REQUESTING || _iDhcpState == DHCP_STATE_RENEWING)
	{
		if (IsValidIP((PCHAR)(pBuf + DHCP_GIADDR)))
		{
			if (memcmp(_pDhcpServerIP, (PCHAR)(pBuf + DHCP_SIADDR), IP_ALEN))
			{
				return;
			}

		}
		else
		{
			if (memcmp(_pDhcpServerIP, UdpGetDstIP(pUcb), IP_ALEN))
			{
				return;
			}
		}
	}

#ifdef OEM_INNOMEDIA
	bHasProvUrl = FALSE;
#endif
	sIndex = DHCP_OPLIST;
	lT1 = 0;
	lT2 = 0;
	bDns2 = FALSE;
	iMsgType = DHCP_MTYPE_NONE;
	lLeaseTime = 0;
	while (sIndex < pUcb->sLen)
	{
		iType = pBuf[sIndex];
		if (iType == DHCP_OPCODE_END)
		{
			break;
		}
		if (iType == DHCP_OPCODE_PADDING)
		{
			sIndex ++;
			continue;
		}
		else
		{
			iLength = pBuf[sIndex+1];
		}
		sIndex += 2;
		pOption = (PCHAR)(pBuf + sIndex);
		switch (iType)
		{
		case DHCP_OPCODE_MESSAGE_TYPE:
			iMsgType = pOption[0];
			break;

		case DHCP_OPCODE_SERVER_ID:
			memcpy4(pServerIP, pOption);
			break;

		case DHCP_OPCODE_SUBNET_MASK:
			memcpy4(pSubnetMask, pOption);
			break;

		case DHCP_OPCODE_ROUTER_IP:
			memcpy4(pRouteIP, pOption);
			break;

		case DHCP_OPCODE_DNS_IP:
			memcpy4(pDnsIP, pOption);
			if (iLength > IP_ALEN)
			{
				memcpy4(pDnsIP2, (PCHAR)(pOption+IP_ALEN));
				bDns2 = TRUE;
			}
			break;

		case DHCP_OPCODE_LEASE_TIME:
			lLeaseTime = PCHAR2ULONG(pOption);
			if (!lT1)	lT1 = rr_32x8(lLeaseTime, 1);
			if (!lT2)	lT2 = lLeaseTime - rr_32x8(lLeaseTime, 3);
			break;

		case DHCP_OPCODE_MESSAGE:
			break;

		case DHCP_OPCODE_RENEWAL_TIME:
			lT1 = PCHAR2ULONG(pOption);
			break;

		case DHCP_OPCODE_REBIND_TIME:
			lT2 = PCHAR2ULONG(pOption);
			break;
#ifdef OEM_INNOMEDIA
		case DHCP_OPCODE_TFTP_SERVER:
			if (iLength > 7 && !memcmp_str(pOption, "http://"))
			{
				iLength -= 7;
				pOption += 7;
				if (Sys_pProvUrl)
				{
					free(Sys_pProvUrl);
				}
				Sys_pProvUrl = (PCHAR)malloc(iLength + 1);
				memcpy(Sys_pProvUrl, pOption, iLength);
				Sys_pProvUrl[iLength] = 0;
				bHasProvUrl = TRUE;
			}
			break;
#endif
		default:
			// other reserved options are in pOption, iType, sLen
			break;
		}
		sIndex += iLength;
	}

	// Respond message
	switch (_iDhcpState)
	{
	case DHCP_STATE_SELECTING:
		if (iMsgType == DHCP_MTYPE_OFFER)
		{
#ifdef OEM_INNOMEDIA
			if (bHasProvUrl)
#endif
			{
				_iDhcpState = DHCP_STATE_REQUESTING;
				_iDhcpRetry = 0;
				memcpy4(_pAssignedIP, (PCHAR)(pBuf + DHCP_YIADDR));
				memcpy4(_pDhcpServerIP, pServerIP);
				_DhcpRequest();
			}
		}
		break;

	case DHCP_STATE_REQUESTING:
		if (iMsgType == DHCP_MTYPE_ACK)
		{
			memcpy4(_pDhcpServerIP, pServerIP);
			memcpy4(_pAssignedIP, (PCHAR)(pBuf + DHCP_YIADDR));
			memcpy4(Sys_pSubnetMask, pSubnetMask);
			memcpy4(Sys_pRouterIp, pRouteIP);
			if (OptionsGetByte(OPT_DNS_TYPE))
			{
				memcpy4(Sys_pDnsIp, pDnsIP);
				if (bDns2)
				{
					memcpy4(Sys_pDnsIp2, pDnsIP2);
				}
				else
				{
					memset(Sys_pDnsIp2, 0, IP_ALEN);
				}
			}
			_lLeaseTime = lLeaseTime;
			_lT1 = lT1;
			_lT2 = lT2;

			// for now, escape IP checking first
			_DhcpDone();
		}
		else if (iMsgType == DHCP_MTYPE_NACK)
		{
			_DhcpNack();
		}
		break;

	case DHCP_STATE_RENEWING:
	case DHCP_STATE_REBINDING:
		if (iMsgType == DHCP_MTYPE_ACK)
		{
			_lLeaseTime = lLeaseTime;
			_lT1 = lT1;
			_lT2 = lT2;
			_DhcpAck();
		}
		else if (iMsgType == DHCP_MTYPE_NACK)
		{
			_DhcpNack();
		}
		break;
	}
}

void DhcpTimer()
{
	if (_iDhcpState == DHCP_STATE_CHECKING || _iDhcpState == DHCP_STATE_INIT)
	{
		return;
	}
	if (_iDhcpState < DHCP_STATE_BOUND)
	{
		_iDhcpTimer ++;
		if (_iDhcpTimer >= DHCP_RETRY_TIMEOUT)
		{
			_iDhcpTimer = 0;
			if (_iDhcpRetry < DHCP_MAX_RETRY)
			{
				_iDhcpRetry ++;
				if (_iDhcpState == DHCP_STATE_REQUESTING)
				{
					_DhcpRequest();
				}
				else
				{
					DhcpDiscover();
				}
			}
			else
			{
				_DhcpNack();
			}
		}
	}
	else
	{
		_lCurrent ++;

		if (_iDhcpState == DHCP_STATE_BOUND)
		{
			if (_lCurrent >= _lT1)
			{
				UdpDebugString("DHCP renewing");
				Dhcp_bRenewing = TRUE;
				_iDhcpState = DHCP_STATE_RENEWING;
				DhcpOpenUdp();
				_DhcpReqRenew();
				_iDhcpRetry = 0;
			}
		}
		else if (_iDhcpState == DHCP_STATE_RENEWING)
		{
			if (_lCurrent >= _lT2)
			{
				UdpDebugString("DHCP rebinding");
				_iDhcpState = DHCP_STATE_REBINDING;
				_iDhcpRetry = 0;
				DhcpOpenUdp();
				_DhcpReqRenew();
				Dhcp_bRenewing = TRUE;
			}
			else
			{
				if (_iDhcpRetry < DHCP_MAX_RETRY)
				{
					_DhcpReqRenew();
					_iDhcpRetry ++;
				}
			}
		}
		else if (_iDhcpState == DHCP_STATE_REBINDING)
		{
			if (_lCurrent >= _lLeaseTime)
			{
				DhcpInit();
				DhcpDiscover();
			}
			else
			{
				if (_iDhcpRetry < DHCP_MAX_RETRY)
				{
					_DhcpReqRenew();
					_iDhcpRetry ++;
				}
			}
		}
	}
}

#ifdef OEM_AUTOTEST
const UCHAR _cClientIP[IP_ALEN] = {192, 168, 1, 200};
const UCHAR _cPacketReplyHead[4] = {DHCP_OP_REPLY, 1, HW_ALEN, 0};


UCHAR _FillLeaseTime(PCHAR pBuf, ULONG lLeaseTime)
{
	pBuf[0] = DHCP_OPCODE_LEASE_TIME;
	pBuf[1] = 4;
	ULONG2PCHAR(lLeaseTime, (PCHAR)(pBuf + 2));
	return 6;
}

void DhcpdInit()
{
	Dhcpd_pSocket = UdpListen(DHCP_SERVER_PORT, NET_BANK_OFFSET, (USHORT)DhcpdRun);
}

void DhcpdRun(UDP_SOCKET pUcb)
{
	PCHAR pSendBuf;
	PCHAR pBuf;
	PCHAR pOptions;
	UCHAR iMsgType, iType, iLen;
	USHORT sIndex;

	if (pUcb != Dhcpd_pSocket)
	{
		return;
	}

	pBuf = Adapter_pUdpBuf;

	if (pBuf[DHCP_OP] != DHCP_OP_REQUEST)
	{
		return;
	}
	sIndex = DHCP_OPLIST;
	iMsgType = DHCP_MTYPE_NONE;
	while (sIndex < pUcb->sLen)
	{
		iType = pBuf[sIndex];
		if (iType == DHCP_OPCODE_END)
		{
			break;
		}
		if (iType == DHCP_OPCODE_PADDING)
		{
			sIndex ++;
			continue;
		}
		else
		{
			iLen = pBuf[sIndex+1];
		}
		sIndex += 2;
		pOptions = (PCHAR)(pBuf + sIndex);
		if (iType == DHCP_OPCODE_MESSAGE_TYPE)
		{
			iMsgType = pOptions[0];
			break;
		}
		sIndex += iLen;
	}
	
	if (iMsgType == DHCP_MTYPE_NONE)
	{
		return;
	}

	pSendBuf = pBuf;
	pOptions = (PCHAR)(pSendBuf + DHCP_OPLIST);
	if (iMsgType == DHCP_MTYPE_DISCOVER)
	{
		iLen = _FillMsgType(pOptions, DHCP_MTYPE_OFFER);
	}
	else
	{
		iLen = _FillMsgType(pOptions, DHCP_MTYPE_ACK);
	}
	iLen += _FillLeaseTime((PCHAR)(pOptions + iLen), 86400);
	iLen += _FillIPAddr((PCHAR)(pOptions + iLen), Sys_pSubnetMask, DHCP_OPCODE_SUBNET_MASK);
	iLen += _FillIPAddr((PCHAR)(pOptions + iLen), Sys_pDnsIp, DHCP_OPCODE_DNS_IP);
	iLen += _FillIPAddr((PCHAR)(pOptions + iLen), Sys_pRouterIp, DHCP_OPCODE_ROUTER_IP);
	iLen += _FillIPAddr((PCHAR)(pOptions + iLen), Sys_pIpAddress, DHCP_OPCODE_SERVER_ID);
	pOptions[iLen] = DHCP_OPCODE_END;
	iLen ++;

	memcpy4(pSendBuf, _cPacketReplyHead);
//	memcpy4((PCHAR)(pSendBuf+DHCP_XID), (PCHAR)(pBuf+DHCP_XID));
	memset((PCHAR)(pSendBuf+DHCP_SECS), 0, DHCP_SECS_LEN);
	pSendBuf[DHCP_FLAGS] = DHCP_FLAG_BROADCAST;
	pSendBuf[DHCP_FLAGS+1] = 0;
	memset((PCHAR)(pSendBuf+DHCP_CIADDR), 0, IP_ALEN);
	memcpy4((PCHAR)(pSendBuf+DHCP_YIADDR), _cClientIP);
	memset((PCHAR)(pSendBuf+DHCP_SIADDR), 0, IP_ALEN);
	memset((PCHAR)(pSendBuf+DHCP_GIADDR), 0, IP_ALEN);
//	memcpy((PCHAR)(pSendBuf+DHCP_CHADDR), (PCHAR)(pBuf+DHCP_CHADDR), HW_ALEN);
	memset((PCHAR)(pSendBuf+DHCP_SNAME), 0, DHCP_OPTIONS - DHCP_SNAME);
	memcpy4((PCHAR)(pSendBuf+DHCP_OPTIONS), _cMagicCookie);

	sIndex = iLen;
	sIndex += DHCP_OPLIST;
	if (sIndex < DHCP_MIN_PACKET_SIZE)
	{
		memset((PCHAR)(pSendBuf + sIndex), 0, (UCHAR)(DHCP_MIN_PACKET_SIZE - sIndex));
		sIndex = DHCP_MIN_PACKET_SIZE;
	}
	UdpSendTo(Dhcpd_pSocket, sIndex, _pBroadcastIP, DHCP_CLIENT_PORT);
}
#endif

