// Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// Implements the DHCP IP4 control plain
// 
//

/**
 @file DHCPIP4Control.cpp
 @internalTechnology
*/

#include "DHCPIP4Control.h"
#include "DHCPIP4States.h"
#include "DHCPIP4Msg.h"
#include "DHCPServer.h"
#include "DHCPConfigListener.h"
#include "DHCPDb.h"
#include "ExpireTimer.h"
#include "NetCfgExtDhcpControl.h"
#include "DomainNameDecoder.h"
#include <nifman.h>
#include <comms-infras/es_config.h>
#include "netcfgextndhcpmsg.h"
#include <f32file.h>
#include <comms-infras/metatype.h>
#include <comms-infras/metadata.h>
#ifdef SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
#include "DHCPIP4Msg.h" 
#ifdef SYMBIAN_NETWORKING_ADDRESS_PROVISION
#include "dhcphwaddrmanager.h"
#endif //SYMBIAN_NETWORKING_ADDRESS_PROVISION
using namespace DHCPv4;
#endif//SYMBIAN_NETWORKING_DHCP_MSG_HEADERS			

struct TDHCPv4Persistent : public Meta::SMetaData
{
	TDHCPv4Persistent() : 
		iTaskStartedAt( 0 ),
		iCurrentAddress(KInetAddrNone,KInetPortNone),
		iLeaseTime(0)
		{
		}

	TTime iTaskStartedAt;
	TInetAddr iCurrentAddress;
	TUint32 iLeaseTime;

	DATA_VTABLE
};

START_ATTRIBUTE_TABLE( TDHCPv4Persistent, KDHCPv4Persinstence, KDHCPv4PersinstenceId )
	REGISTER_ATTRIBUTE( TDHCPv4Persistent, iTaskStartedAt, TMetaTime )
	REGISTER_ATTRIBUTE( TDHCPv4Persistent, iCurrentAddress, TMeta<TInetAddr> )
	REGISTER_ATTRIBUTE( TDHCPv4Persistent, iLeaseTime, TMetaNumber )
END_ATTRIBUTE_TABLE()

CDHCPIP4Control::~CDHCPIP4Control()
	{
	}

void CDHCPIP4Control::ConfigureL(const TConnectionInfo& aInfo, const RMessage2* aMessage)
/**
  * Open and attach to the RConnection
  *
  * @internalTechnology
  */
	{
   CDHCPControl::ConfigureL( aInfo, aMessage );
	__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPIP4Control::ConfigureL")));
	
	iDhcpDb = new (ELeave) CDHCPDb( TPtrC( SERVICE_IP_DNS_ADDR_FROM_SERVER ),
													TPtrC( SERVICE_IP_NAME_SERVER1 ),
													TPtrC( SERVICE_IP_NAME_SERVER2 )); //for both version for the time being see the class TDHCPIP4Db coments
	FindInterfaceNameL(aInfo,KAfInet);
#ifdef SYMBIAN_NETWORKING_DHCPSERVER
#ifndef SYMBIAN_NETWORKING_ADDRESS_PROVISION
	iDhcpStateMachine = CDHCPIP4StateMachine::NewL(iEsock, iConnection, iInterfaceName,iDHCPServerImpl);
#else
	iDhcpHwAddrManager = CDhcpHwAddrManager::NewL();
	iDhcpStateMachine = CDHCPIP4StateMachine::NewL(iEsock, iConnection, iInterfaceName,iDhcpHwAddrManager, iDHCPServerImpl);
#endif //SYMBIAN_NETWORKING_ADDRESS_PROVISION
#else 
	iDhcpStateMachine = CDHCPIP4StateMachine::NewL(iEsock, iConnection, iInterfaceName);
#endif 	// SYMBIAN_NETWORKING_DHCPSERVER		
	TDHCPv4Persistent pers;
	iStaticAddress = !iDhcpDb->ReadL(*iDhcpStateMachine, pers);


	//initialise relevant data
	TTimeIntervalSeconds seconds = static_cast<TInt>(pers.iLeaseTime);
	iDhcpDb->iLeaseExpiresAt = pers.iTaskStartedAt + seconds;
	DhcpStateMachine()->iLeaseTime = pers.iLeaseTime;
	iDhcpStateMachine->SetCurrentAddress( pers.iCurrentAddress );
	DhcpStateMachine()->iMaxRetryCount = 2; //Max retry count for the first run is set to 2


	TInetAddr existingGlobalAddr = iDhcpStateMachine->GetInterfaceGlobalAddress();

	if( ! existingGlobalAddr.IsUnspecified() )
		{
		// there was an existing address on the interface, regardless of commsdat preference..
		//  so force inform.
		__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("Interface already has global address. Forcing inform mode.")));
		iDhcpStateMachine->SetCurrentAddress( existingGlobalAddr );
		iStaticAddress = ETrue;
		}
	else
		{
		if(iStaticAddress)
			{
			__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("Interface has a static address from commsdat. Proceeding in inform mode.")));
			}
		else
			{
			__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("Interface has no global address yet. Proceeding in discover mode.")));
			}
		}

	CDHCPControl::ConfigureL(iStaticAddress);
	}

void CDHCPIP4Control::Cancel()
	{
	iClientShouldCompleteWhenLinkLocalCreated = EFalse;

	// Call the base class.
	CDHCPControl::Cancel();
   }

void CDHCPIP4Control::BindingFinishedL()
	{
	CDHCPControl::BindingFinishedL();
	TDHCPv4Persistent pers;
	//get relevant data from statemachine
	pers.iTaskStartedAt = DhcpStateMachine()->iStartedAquisitionAt;
	pers.iCurrentAddress = DhcpStateMachine()->iCurrentAddress;
	pers.iLeaseTime = DhcpStateMachine()->iLeaseTime;
	TRAPD( ret, iDhcpDb->WriteL(*iDhcpStateMachine, pers) );
	if (ret!=KErrNone)
		{
		__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPIP4Control::BindingFinishedL error: %d"),ret));
		}
	}

void CDHCPIP4Control::TimerExpired()
/**
  * Called by the timer to signal that the timer has expired
  *
  * @internalTechnology
  */
	{
	__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPIP4Control::TimerExpired()")));

    CDHCPControl::TimerExpired();
	
	// In response to the rebind operation timing out, try to create
	// a link local to allow local addressing (may not be supported
	// by current interface's IPv4 link local option).  We cannot
	// create the link local earlier (e.g., after a renew attempt
	// times-out) because the link local will not be usable until
	// the DHCP assigned address has been removed (randomly
	// generated addresses are prioritised below non-randomly
	// generated addresses during selection in the TCP/IP stack).
	if( ( iState == EInitInProgress ) && iDhcpConfigListener && !iDhcpConfigListener->HaveLinkLocal() )
		{
		DhcpStateMachine()->CreateIPv4LinkLocal();
		}
	}

TInt CDHCPIP4Control::HandleClientRequestL(TUint aName, TInt aValue)
/**
  * Handles client requests made through RConnection
  * are handled here.  Currently its for renewing the lease time 
  * where client passes in a required timeout 
  * @see CDHCPControl::HandleClientRequestL
  *
  * @internalTechnology
  */
	{
	TBool deferAllowed = !iMessage;
	TBool defer = EFalse;
	
  	defer = CDHCPControl::HandleClientRequestL(aName, aValue);

	if (aName == KConnAddrRenew && iState == ERenewInProgress && deferAllowed)
		{//Rebind timeout
		TTimeIntervalSeconds timeOut;
		if(aValue > 0)
			{
			timeOut = aValue;
			}
		else
			{
			timeOut = (DhcpStateMachine()->iRebindTimeT2 - DhcpStateMachine()->iRenewalTimeT1 + 1);
			}
      	iTimer->After(timeOut, *this);
      	defer = ETrue;
		}
	
	return defer;
	}	

TInt CDHCPIP4Control::HandleClientRequestL(TUint aName)
/**
  * Handles client requests specific for IP4
  *
  * @see CDHCPControl::HandleClientRequestL
  * @internalTechnology
  */
	{
	return CDHCPIP4Control::HandleClientRequestL(aName,0);
	}

/**
    Get raw option data (IP4 version).
    @param  pointer to the buffer descriptor for getting option data
*/
void CDHCPIP4Control::HandleGetRawOptionDataL(TDes8* aDes)
{
	TDhcp4RawOptionDataPckg pckg(*aDes);
		
	TUint8 opCode = pckg.OpCode();
	TPtr8 buf(pckg.Buf());
	GetRawOptionDataL(opCode, buf);
#ifdef SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
	if (!iDhcpStateMachine->iDhcpInformAckPending)   
		{
#endif // SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
	pckg.SetBufLengthL(buf.Length());
#ifdef SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
		}
#endif // SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
}

void CDHCPIP4Control::TaskCompleteL( TInt aError )
/**
  * TaskCompleteL function
  *
  * if this method returns ETrue then aStateMachine deletes itself.
  * In this case it does not ever return ETrue.
  * Called upon completion or when suspended.
  *
  * @see CStateMachine::iSuspendRequest comment
  * @internalTechnology
  */
	{
	// In response to the initialisation operation timing out,
	// try to create a link local to allow local addressing (may
	// not be supported by current interface's IPv4 link local
	// option).
	if( ( aError == KErrTimedOut ) &&
		( iState == EInitInProgress ) &&
		!iStaticAddress &&
		iDhcpConfigListener &&
		!iDhcpConfigListener->HaveLinkLocal() )
		{
		if( DhcpStateMachine()->CreateIPv4LinkLocal() == KErrNone )
			{
			iClientShouldCompleteWhenLinkLocalCreated = ETrue;
			//Link Local Address has been taken & return here itself
			CDHCPControl::TaskCompleteL(aError);
			return;
			}
		}
	if((DhcpStateMachine()->iRetryDhcpIpCount >= KDHCPv4MaxRetryCount) && iState == EDeclineInProgress )
		{
		if( DhcpStateMachine()->CreateIPv4LinkLocal() == KErrNone )
			{
			iClientShouldCompleteWhenLinkLocalCreated = ETrue;
			//Link Local Address has been taken & return here itself
 			CDHCPControl::TaskCompleteL(KErrNone);
 			return;
			}
		
		}
	// Call the base class.
	CDHCPControl::TaskCompleteL( aError );
	}
	
void CDHCPIP4Control::LinkLocalCreated()
/**
  * LinkLocalCreated function
  *
  * If iClientShouldCompleteWhenLinkLocalCreated is ETrue we need to complete the
  * client messages as the link local has been created and the interface is now
  * ready for use.
  *
  * @internalTechnology
  */
	{
	if( iClientShouldCompleteWhenLinkLocalCreated )
		{
		iClientShouldCompleteWhenLinkLocalCreated = EFalse;
		
		CompleteClientConfigureMessage(KErrNone);
		// don't complete any outstanding ioctl yet..
		}
	}

void CDHCPIP4Control::GetRawOptionDataL(TUint aOpCode, TPtr8& aPtr)
	{
#ifdef SYMBIAN_NETWORKING_DHCPSERVER
	if(iValidMsg.Length()==0 && iDHCPServerImpl)
		{
		GetRawOptionDataFromDNSBufL(aPtr);
		return;
		}
#endif // SYMBIAN_NETWORKING_DHCPSERVER
	HBufC8* buf = NULL;
	DHCPv4::CDHCPMessageHeaderIP4 msg(buf);
	CleanupClosePushL(msg);
	TPtr8 ptr( const_cast<TUint8*>(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length() );
	msg.iRecord.ParseL(ptr); //no check necessary
	DHCPv4::COptionNode* pOption = msg.iOptions.FindOption(static_cast<TUint8>(aOpCode));
	if (!pOption)
		{
#ifdef SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
		CleanupStack::PopAndDestroy();
		TUint8 tempOpt=aOpCode;
		TPtr8 optPtr(&tempOpt,1,1);
		return(RequestInformOrCompleteCallL(optPtr));
#else
		User::Leave(KErrNotFound);
#endif // SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
		}
	ptr.Set(pOption->GetBodyDes());
	if (ptr.Length() > aPtr.MaxLength())
		{
		User::Leave(KErrOverflow);
		}
	aPtr.Copy(ptr);
	CleanupStack::PopAndDestroy();
	}

#ifdef SYMBIAN_NETWORKING_DHCPSERVER

void CDHCPIP4Control::GetRawOptionDataFromDNSBufL(TPtr8& aPtr)
	{
	if(iValidMsg.Length() == 0)
		{
		if (iDNSRawOption->Des().Length() > aPtr.MaxLength())
			{
			User::Leave(KErrOverflow);
			}
		aPtr.Copy(iDNSRawOption->Des());
		}
	}

void CDHCPIP4Control::HandleSetRawOptionCodeL(TDes8* aDes)
	{
	if(iValidMsg.Length() == 0)
		{
		iDNSRawOption = HBufC8::NewL(aDes->Length());
		iDNSRawOption->Des() = *aDes;
		}
	else
		{
		SetRawOptionCodeL(aDes);	
		}
	}

void CDHCPIP4Control::SetRawOptionCodeL(TDes8* aDes)
	{
	HBufC8* buf = NULL;
	DHCPv4::CDHCPMessageHeaderIP4 msg(buf);
	CleanupClosePushL(msg);
	TPtr8 ptr( const_cast<TUint8*>(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length() );
	msg.iRecord.ParseL(ptr); //no check necessary
	
	TDhcp4RawOptionDataPckg pckg(*aDes);
	DHCPv4::TDHCPOptionCodes opcode = static_cast<DHCPv4::TDHCPOptionCodes>(pckg.OpCode());
	
	DHCPv4::COptionNode* pOption = msg.iOptions.FindOption(opcode);
	if (!pOption)
		{
		User::Leave(KErrNotFound);
		}
		
	TPtr8 ptr2(reinterpret_cast<TUint8*>(aDes),aDes->Length());
	
	pOption->SetBody(ptr2);	
	CleanupStack::PopAndDestroy(); // msg
	}
#endif // SYMBIAN_NETWORKING_DHCPSERVER

void CDHCPIP4Control::HandleGetSipServerAddrL(TDes8* aDes)
	{	
	if (aDes->Length() < static_cast<TInt>(sizeof(SSipServerAddr)))
		{
		User::Leave(KErrArgument);
		}

	SSipServerAddr* sipServerAddr = 
		reinterpret_cast<SSipServerAddr*>(const_cast<TUint8*>(aDes->Ptr()));

	HBufC8* buf = NULL;
	DHCPv4::CDHCPMessageHeaderIP4 msg(buf);
	CleanupClosePushL(msg);
	TPtr8 ptr(const_cast<TUint8*>(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length());
	msg.iRecord.ParseL(ptr);
	DHCPv4::COptionNode* sipServerOption = msg.iOptions.FindOption(DHCPv4::EDHCPSIPServers);

	if(sipServerOption)
		{
		TUint8* headerPtr = sipServerOption->Ptr();		
		
		TUint encoding = headerPtr[DHCPv4::KDhcpSipEncodingOffset];
		TUint length = headerPtr[DHCPv4::KDhcpSipLengthOffset];
		
		if(encoding != DHCPv4::KDhcpSipEncodingAddresses)
			{
			__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("HandleGetSipServerAddrL: Client trying to fetch a SIP address when only a SIP domain is known")));
			User::Leave( KErrNotFound );
			}
		if((TUint)sipServerAddr->index >= (length / KIp4AddrByteLength))
			{
			__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("HandleGetSipServerAddrL: Client tried to fetch an out-of-range SIP address %d (I only know about %d)"),(TUint)sipServerAddr->index, (length / KIp4AddrByteLength)));
			User::Leave( KErrNotFound );
			}
		
		TUint addrOffset = sipServerAddr->index * KIp4AddrByteLength;
		
		TUint8* bodyPtr = sipServerOption->GetBodyPtr();
		TUint32 copiedValue;
		// Grab ip address from option body at given offset, +1 used to skip the encoding boolean used for sip
		Mem::Copy(&copiedValue,(bodyPtr + addrOffset+1),4);  
		// swap around ip address as in BigEndian form
		copiedValue = ByteOrder::Swap32(copiedValue); 
		sipServerAddr->address.SetAddress(copiedValue);   
		}
	else
		{
		__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("HandleGetSipServerAddrL: Client tried to fetch a SIP option but none has (yet) been received")));
		User::Leave(KErrNotFound);
		}	
	CleanupStack::PopAndDestroy();
	}

#ifdef SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
void CDHCPIP4Control::GetDhcpHdrSiaddrL(TDes8& aNxtAddress)
/**
  * The GetDhcpHdrSiaddr function
  *
  * Fetches the Siaddr
  *
  * @param aNxtAddress Next server IPAddress fetched from the DHCP Server
  * @internalTechnology
  */
	{
	if (aNxtAddress.Length() < static_cast<TInt>(sizeof(TConnectionAddress)))
		{
		User::Leave(KErrArgument);
		}
	TConnectionAddress* ptr = (TConnectionAddress*)aNxtAddress.Ptr();
	TInetAddr* addrPtr = new(&(ptr->iAddr))TInetAddr;	
	//ptr->iAddr is just some memory in aDes. There is no guarantee that it will be a
	//valid TInetAddr (or even a valid TDes) so what we do here is just run a constructor
	//on this already valid memory block and we are now guaranteed to have a valid 
	//TInetAddr - NO MEMORY IS ACTUALLY ALLOCATED BY NEW HERE - see base code for more 
	//details	
	
	//Parse the iValidMsg
	HBufC8* buf = NULL;
	DHCPv4::CDHCPMessageHeaderIP4 msg(buf);
	CleanupClosePushL(msg);
	TPtr8 msgPtr(const_cast<TUint8*>(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length());
	msg.iRecord.ParseL(msgPtr);
	
	//Extract the server address
	addrPtr->SetAddress(msg.GetSIAddr());
	
	CleanupStack::PopAndDestroy();
	}

void CDHCPIP4Control::GetDhcpHdrSnameL(TDes8& aHdrSvrName)
/**
  * The GetDhcpHdrSname function
  *
  * Fetches the Sname
  *
  * @param aHdrSvrName Next server name fetched from the DHCP Server. 
  * @internalTechnology
  */
	{
	HBufC8* buf = NULL;
	DHCPv4::CDHCPMessageHeaderIP4 msg(buf);
	CleanupClosePushL(msg);
	TPtr8 ptr(const_cast<TUint8*>(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length());
	msg.iRecord.ParseL(ptr);
	if (msg.GetSName().Length() > aHdrSvrName.MaxLength())
	{
		User::Leave(KErrOverflow);
	}
	aHdrSvrName=msg.GetSName();
	//SetLength otherwise received string length will be 64 by default, truncate to end of string.
	aHdrSvrName.SetLength(aHdrSvrName.Locate('\0'));

	CleanupStack::PopAndDestroy(&msg);
	}
	
void CDHCPIP4Control::HandleGetTftpServerAddrL(TDes8& aDes) 
/**
  * The HandleGetTftpServerAddrL function
  *
  * Fetches the Tftp Server Address
  * @param aDes Buffer returned contains a list of ip addresses each of 4 bytes.
  *
  * @internalTechnology
  */
	{
	if (aDes.Length() < static_cast<TInt>(sizeof(STftpServerAddr)))
		{
		User::Leave(KErrArgument);
		}

	STftpServerAddr* tftpServerAddr = 
		reinterpret_cast<STftpServerAddr*>(const_cast<TUint8*>(aDes.Ptr()));

	HBufC8* buf = NULL;
	DHCPv4::CDHCPMessageHeaderIP4 msg(buf);
	CleanupClosePushL(msg);
	TPtr8 ptr(const_cast<TUint8*>(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length());
	msg.iRecord.ParseL(ptr);
	DHCPv4::COptionNode* tftpServerOption = msg.iOptions.FindOption(DHCPv4::EDHCPTftpServers);
	if(tftpServerOption)
		{
		
		TUint8 len=tftpServerOption->GetItemLength()-2;//subtract 2 bytes becz length include message opcode and message opcode length 
		
		TUint addrOffset = tftpServerAddr->index * KIp4AddrByteLength;
		
		if (addrOffset>len)
			{
			__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("HandleGetTftpServerAddrL: Client tried to fetch a invalid index of Tftp Address which is not present")));
			iDhcpStateMachine->iDhcpInformAckPending=EFalse;
			User::Leave(KErrNotFound);	
			
			}
		
		TUint8* bodyPtr = tftpServerOption->GetBodyPtr();
				
		TUint32 copiedValue;
		// Grab ip address from option body at given offset.
		Mem::Copy(&copiedValue,(bodyPtr + addrOffset),4);  
		// swap around ip address as in BigEndian form/		copiedValue = ByteOrder::Swap32(copiedValue); 
		copiedValue = ByteOrder::Swap32(copiedValue); 
		tftpServerAddr->address.SetAddress(copiedValue);   
		}
	else //check inform to be made or not..or return
		{
		CleanupStack::PopAndDestroy(); //pop buffer ..we are going to return immediately
		TUint8 opt=DHCPv4::EDHCPTftpServers;
		TPtr8 optPtr(&opt,1,1);
		return(RequestInformOrCompleteCallL(optPtr));
		}	
	iDhcpStateMachine->iDhcpInformAckPending=EFalse;
	CleanupStack::PopAndDestroy();
	
}

void CDHCPIP4Control::HandleGetTftpServerNameL(TDes8& aTftpSvrName)
/**
  * The HandleGetTftpServerName function
  *
  * Fetches the Tftp Server Name
  *
  * @param aTftpSvrName Tftp Server Name fetched from the DHCP Server.
  * @internalTechnology
  */
	{
	HBufC8* buf = NULL;
	DHCPv4::CDHCPMessageHeaderIP4 msg(buf);
	CleanupClosePushL(msg);
	TPtr8 ptr(const_cast<TUint8*>(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length());
	msg.iRecord.ParseL(ptr);
	DHCPv4::COptionNode* tftpOption = msg.iOptions.FindOption(DHCPv4::EDHCPTftpServerName);
	if(tftpOption)
		{
		if (aTftpSvrName.MaxLength()<tftpOption->GetItemLength())
			{
			iDhcpStateMachine->iDhcpInformAckPending=EFalse;
			User::Leave(KErrOverflow);
			}
		else	
			{
			aTftpSvrName.Copy(tftpOption->GetBodyDes());
			}
		}
	else 
		{
		//To check if DHCP Sname Option is overloaded
		tftpOption = msg.iOptions.FindOption(DHCPv4::EDHCPOptionOverload);
		if(tftpOption)
			{
			if(	msg.iOptions.GetOptionOverload() == DHCPv4::EDHCPSname)
				{
				GetDhcpHdrSnameL(aTftpSvrName);
				}
			}
		else//now issue Inform Request for option 66 or return now..
			{
			CleanupStack::PopAndDestroy(); //pop buffer ..we are going to return immediately
			TUint8 opt=DHCPv4::EDHCPTftpServerName;
			TPtr8 optPtr(&opt,1,1);
			return(RequestInformOrCompleteCallL(optPtr));
			}
		}
		iDhcpStateMachine->iDhcpInformAckPending=EFalse;
		CleanupStack::PopAndDestroy();	
	}	

void CDHCPIP4Control::RequestInformOrCompleteCallL(TPtr8& aOpcodePtr)	
/**
  * The RequestInformOrCompleteCallL function
  *
  * Checks DHCPINFORM message should be sent or not
  * @param aOpcodePtr  pointer to the opcode list to be updated  in iSavedExtraParameters
  * @internalTechnology
  */
	{

	if (!iDhcpStateMachine->iDhcpInformAckPending)
		{
		if (!iDhcpStateMachine->iSavedExtraParameters.Length())
			{
			iDhcpStateMachine->iSavedExtraParameters.CreateL(aOpcodePtr);
			}
		else
			{
			iDhcpStateMachine->iSavedExtraParameters.ReAllocL(iDhcpStateMachine->iSavedExtraParameters.Length()+aOpcodePtr.Length());
			iDhcpStateMachine->iSavedExtraParameters.Append(aOpcodePtr);
			}	
	
			static_cast<CDHCPIP4StateMachine*>(iDhcpStateMachine)->StartInformL( this );
			iDhcpStateMachine->iDhcpInformAckPending=ETrue;
  			iState = EInformInProgress;
		}
	else
		{
		__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8(" RequestInformOrCompleteCallL::Client tried to fetch a  option but none has (yet) been received")));
		iDhcpStateMachine->iDhcpInformAckPending=EFalse;
		User::Leave(KErrNotFound);	
		}
	}
	
	

void CDHCPIP4Control::HandleGetMultipleParamsL(TDes8& aDes)
/**
  * The HandleGetMultipleParamsL function
  *
  * Fetches the required raw option parameter buffer.
  * Buffer contains list of parameter options followed by msg opcode, len, data..
  * If any of the parameters are not found, DHCPINFORM message will be sent.
  *	Filled Returned Buffer is as shown in fig..
  * @code
  *		  -----------------------------------------------------
  *		 |No of |      |Data   |      |       |Data   |        |
  *	 	 |OpCode|OP1   |length1|Data1 |  OP2  |Length2|Data2   |
  *		 |      |      |       |      |       |       |        |
  *		  ----------------------------------------------------- 
  * @endcode
  * @param aDes Pointer to the buffer descriptor for getting option data
  * @internalTechnology
  */
	{
	HBufC8 *saveBuf=NULL, *buffer=NULL;
	TUint8 *headerPtr;
	TInt numOfOpcodes=0;
	TInt totalLength=0,opcodeLength=aDes.Length();
	TInt maxLength=aDes.MaxLength();
	TUint8 opcode;
	TBool allFound=ETrue;
	
	DHCPv4::CDHCPMessageHeaderIP4 msgSaved(saveBuf);
	TPtr8 savedPtr(const_cast<TUint8*>(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length());
	CleanupClosePushL(msgSaved);
	msgSaved.iRecord.ParseL(savedPtr);


	//The below for loop checks all the required opcode data is present or not
	//The message opcode data is not present in the iValidMsg buffer,the corresponding 
	//opcodes are stored in iCurrentParameters for sending it in DHCPINFORM message
	for(TInt i=0;i<opcodeLength;i++)
		{
		opcode=*(aDes.Ptr()+i); //search required opcode is present or not, one by one
		DHCPv4::COptionNode* findNode = msgSaved.iOptions.FindOption(opcode);
		if (findNode )
			{
			TInt bufLength=findNode->GetItemLength();
			totalLength+=bufLength;
			if ((totalLength+1) > maxLength)
				{
				totalLength-=bufLength;
				continue; //current buffer is too big..so hope next buffer is small
				}
			if (!buffer)
				{
				buffer=HBufC8::NewLC(totalLength + 1);//+1 is extra byte to store number of opcodes
				buffer->Des().Append(KNoChar);
				}
			else
				{
				buffer=buffer->ReAllocL(totalLength + 1);
				CleanupStack::Pop();//buffer as ptr address has changed
		        CleanupStack::PushL(buffer);
				}
			headerPtr = findNode->Ptr(); 
			++numOfOpcodes;
			buffer->Des().Append(headerPtr,bufLength);
			}
		else
			{
			//If atleast one opcode, among the requested, is not found then request through 
			//DHCP INFORM message by calling RequestInformOrCompleteCallL
			allFound=EFalse;
			}
		}
	
	if (allFound ) //everything is present..just return call now itself..
		{
		if ((totalLength + 1) > maxLength)
			{
			User::Leave(KErrOverflow);
			}
		if(buffer) // buffer would be NULL only when aDes.Length = 0 which is a rare scenario. But still check it to avoid NULL pointer dereference.
			{
			aDes.Copy(buffer->Ptr(), buffer->Length());
			TBuf8<1> dummy;
			dummy.FillZ(1);
			dummy[0]=numOfOpcodes;
			aDes.Replace(0,1,dummy);//update number of opcodes collected
			}
		}
	else
		{
		TPtr8 opcodePtr(const_cast<TUint8*>(aDes.Ptr()),opcodeLength);
		opcodePtr.SetLength(opcodeLength);
		RequestInformOrCompleteCallL(opcodePtr);	
		}	
	
	if (buffer)
		{
		CleanupStack::PopAndDestroy(buffer);
		}

	CleanupStack::PopAndDestroy(&msgSaved);

	}


TInt CDHCPIP4Control::InformCompleteRequestHandlerL()
/**
  * The InformCompleteRequestHandlerL function
  * 
  * The function will be called after the DHCPACK message is received, and 
  * iValidMsg buffer has been updated. 
  * Looks for the requested options present or not and appropriately calls 
  * the function handlers
  *
  * @return KErrNone if the required data option is found else corresponding error code.
  * @internalTechnology
  */
	{

	TUint optionName = iMessage->Int1();
	TInt length      = iMessage->Int3();

	HBufC8 *buff=HBufC8::NewMaxLC(length) ;
	TPtr8 ptr(buff->Des());
	iMessage->ReadL(2, ptr);

	
	switch(optionName)
		{
		case KConnGetDhcpRawOptionData:
			{
			HandleGetRawOptionDataL(&ptr);
			iMessage->WriteL(2,ptr);
			break;
			}
		
		case KConnDhcpGetMultipleParams :
			{
			HandleGetMultipleParamsL(ptr);
			iMessage->WriteL(2,ptr);
			break;
			}
	
		case KConnGetTftpServerAddr:
			{
			HandleGetTftpServerAddrL(ptr);
			iMessage->WriteL(2,ptr);
			break;
			}
		
		case KConnGetTftpServerName:
			{
			HandleGetTftpServerNameL(ptr);
			iMessage->WriteL(2,ptr);
			break;
			}
		
		default:
			__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("InformCompleteRequestHandlerL : default unhandled optino Name")));
			break;	
	
		}
	iDhcpStateMachine->iDhcpInformAckPending=EFalse;
	CleanupStack::PopAndDestroy(buff);
	return KErrNone;
	 
	}
#endif//SYMBIAN_NETWORKING_DHCP_MSG_HEADERS


void CDHCPIP4Control::HandleGetSipServerDomainL(TDes8* aDes)
	{
	if (aDes->Length() < static_cast<TInt>(sizeof(SSipServerDomain)))
		{
		User::Leave(KErrArgument);
		}

	SSipServerDomain* sipServerDomain = 
		reinterpret_cast<SSipServerDomain*>(const_cast<TUint8*>(aDes->Ptr()));

	HBufC8* buf = NULL;
	DHCPv4::CDHCPMessageHeaderIP4 msg(buf);
	CleanupClosePushL(msg);
	TPtr8 ptr(const_cast<TUint8*>(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length());
	msg.iRecord.ParseL(ptr);
	DHCPv4::COptionNode* sipServerOption = msg.iOptions.FindOption(DHCPv4::EDHCPSIPServers);

	if(sipServerOption)
		{
		TUint8* headerPtr = sipServerOption->Ptr();
		
		TUint encoding = headerPtr[DHCPv4::KDhcpSipEncodingOffset];

		if(encoding != DHCPv4::KDhcpSipEncodingDomains)
			{
			__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("HandleGetSipServerDomainL: Client trying to fetch a SIP domain when only a SIP address is known")));
			User::Leave( KErrNotFound );
			}

		CDomainNameCodec* domainNameDecoder = new(ELeave) CDomainNameCodec();
		CleanupStack::PushL(domainNameDecoder);
		TPtr8 ptr = sipServerOption->GetBodyDes();
		ptr.Set((TUint8*)(ptr.Ptr()+1),ptr.Length()-1,ptr.MaxLength()-1);
		domainNameDecoder->DecodeL(ptr);

		if(sipServerDomain->index < (TInt)domainNameDecoder->NumDomainNames())
			{
			TDomainName domainName = (*domainNameDecoder)[sipServerDomain->index];
			sipServerDomain->domainName.Copy(domainName);
			}
		else
			{
			__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("HandleGetSipServerDomainL: Client tried to fetch an out-of-range SIP domain %d (I only know about %d)"), sipServerDomain->index , (TInt)domainNameDecoder->NumDomainNames()));
			User::Leave(KErrNotFound);
			}

		CleanupStack::PopAndDestroy();
		}
	else
		{
		__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("HandleGetSipServerDomainL: Client tried to fetch a SIP option but none has (yet) been received")));
		User::Leave(KErrNotFound);
		}
	CleanupStack::PopAndDestroy();
	}



TBool CDHCPIP4Control::ShouldInformAfterFailedInit(void)
	{
	// if has no global address, should not inform
	return iDhcpStateMachine->GetInterfaceGlobalAddress().IsUnspecified() ? EFalse : ETrue ;
	}

