// Copyright (c) 2004-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 IP6 highest level control plain
// 
//

/**
 @file DHCPIP6Control.cpp
 @internalTechnology
*/

#include "DHCPIP6Control.h"
#include "DHCPIP6States.h"
#include "DHCPServer.h"
#include "DHCPDb.h"
#include "NetCfgExtDhcpControl.h"
#include "DhcpIP6Msg.h"
#include "ExpireTimer.h"
#include "DomainNameDecoder.h"
#include <comms-infras/es_config.h>
#include "netcfgextndhcpmsg.h"
#include <nifman.h>
#include <f32file.h>
#include <comms-infras/metatype.h>
#include <comms-infras/metadata.h>
#ifdef SYMBIAN_NETWORKING_ADDRESS_PROVISION
#include "dhcphwaddrmanager.h"
#endif //SYMBIAN_NETWORKING_ADDRESS_PROVISION
using namespace DHCPv6;

struct TDHCPv6Persistent : public Meta::SMetaData
{
	TDHCPv6Persistent( SIdentityAssociationConfigInfoNA& aIdentityAssociationConfigInfoNA ) :
		iIdentityAssociationConfigInfoNA( &aIdentityAssociationConfigInfoNA )
		{
		iTaskStartedAt.HomeTime();
		}
	TTime iTaskStartedAt;
	SIdentityAssociationConfigInfoNA* iIdentityAssociationConfigInfoNA;

	DATA_VTABLE
};

START_ATTRIBUTE_TABLE( TDHCPv6Persistent, KDHCPv6Persinstence, KDHCPv6PersinstenceId )
	REGISTER_ATTRIBUTE( TDHCPv6Persistent, iIdentityAssociationConfigInfoNA, TMetaObjectPtr<SIdentityAssociationConfigInfoNA> )
	REGISTER_ATTRIBUTE( TDHCPv6Persistent, iTaskStartedAt, TMetaTime )
END_ATTRIBUTE_TABLE()


CDHCPIP6Control::~CDHCPIP6Control()
	{
	}

void CDHCPIP6Control::ConfigureL(const TConnectionInfo& aInfo, const RMessage2* aMessage)
/**
  * Open and attach to the RConnection
  *
  * @internalTechnology
  */
	{
   CDHCPControl::ConfigureL( aInfo, aMessage );
	__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPIP6Control::ConfigureL")));
	
	iDhcpDb = new (ELeave) CDHCPDb( TPtrC( SERVICE_IP6_DNS_ADDR_FROM_SERVER ),
													TPtrC( SERVICE_IP6_NAME_SERVER1 ),
													TPtrC( SERVICE_IP6_NAME_SERVER2 )); //for both version for the time being see the class TDHCPIP4Db comments
	TRAPD( err, FindInterfaceNameL(aInfo,KAfInet6) );
	if( err == KErrNone )
		{
		TBuf8<KMaxName> tempBuf;
		tempBuf.Copy( iInterfaceName );
		__CFLOG_1(KLogSubSysDHCP, KLogCode, _L8("CDHCPIP6Control::ConfigureL - FindInterfaceNameL found \"%S\""), &tempBuf);
		}
	else
		{
		__CFLOG_1(KLogSubSysDHCP, KLogCode, _L8("CDHCPIP6Control::ConfigureL - FindInterfaceNameL failed with error %d"), err);
		User::Leave( err );
		}
#ifdef SYMBIAN_NETWORKING_ADDRESS_PROVISION
	iDhcpHwAddrManager = CDhcpHwAddrManager::NewL();
	iDhcpStateMachine = CDHCPIP6StateMachine::NewL(iEsock, iConnection, iInterfaceName,iDhcpHwAddrManager);
#else
	iDhcpStateMachine = CDHCPIP6StateMachine::NewL(iEsock, iConnection, iInterfaceName);
#endif //SYMBIAN_NETWORKING_ADDRESS_PROVISION
	
	CDHCPIP6StateMachine* sm6 = static_cast<CDHCPIP6StateMachine*>(iDhcpStateMachine);
 	TDHCPv6Persistent pers(sm6->iInterfaceConfigInfo.iSIdentityAssociationConfigInfoNA);
	TBool bStaticAddress = !iDhcpDb->ReadL(*iDhcpStateMachine, pers);
	//initialise relevant data
	TInt pos = 0;
	const SIPAddressInfo* info = pers.iIdentityAssociationConfigInfoNA->GetValidAddressInfo( pos );
	TTimeIntervalSeconds seconds = info ? static_cast<TInt>(info->iValidLifeTime) : 0 ;
	iDhcpDb->iLeaseExpiresAt = pers.iTaskStartedAt + seconds;
	//the rest of the data we'll get back in reply msg
	CDHCPControl::ConfigureL(bStaticAddress);	
	}

void CDHCPIP6Control::BindingFinishedL()
 	{
	CDHCPControl::BindingFinishedL();
	//see case EDeclineInitialisedInProgress as to how the persistent
	//data is written
  	}

void CDHCPIP6Control::TaskCompleteL(TInt aError)
/**
  * Signals the end of a task
  * and decides what we should do when
  *
  * @internalTechnology
  */
	{
	__CFLOG_VAR((KLogSubSysDHCP, KLogCode, 
				 _L8("CDHCPIP6Control::TaskCompleteL (%d) with error = %d") ,
				 iState, aError));

	DhcpStateMachine()->Cancel();
	TState prevState = iState;
	switch (iState)
		{
		case EDeclineInitialisedInProgress:
			{
			TDHCPv6Persistent pers(DhcpStateMachine()->iInterfaceConfigInfo.iSIdentityAssociationConfigInfoNA);
			//get relevant data from statemachine
			pers.iTaskStartedAt = DhcpStateMachine()->iStartedAquisitionAt;
			TRAPD( ret, iDhcpDb->WriteL(*iDhcpStateMachine, pers) );
			if (ret!=KErrNone)
				{
				__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPIP6Control::TaskCompleteL error: %d"),ret));
				}
			iState = EInitialised;
			break;
			}
		case EReconfigureInProgress:
			//in the case the following fn leave we've run out of memory => we will not respond
			//to the reconfigure message. However we can still use our address(es) and wait
			//for renew timer to expire
			if ( aError == KErrNone )
				{
				CDHCPMessageHeaderIP6* v6Msg = DhcpStateMachine()->DhcpMessage();
				COptionNode* pNode = v6Msg->GetOptions().FindOption( EReconfMsg );
				if (pNode)
					{
					TInt nCode = pNode->GetBigEndian();
					switch ( nCode )
						{
						case EReconfigureInformRequest:
							DhcpStateMachine()->StartInformL( this, EFalse /*static address is either already set or not used*/ );
							iState = EInformInProgress;
							//cancel the renew timer (if any) after succesfully starting the above tasks
							iTimer->Cancel();
							break;
						case EReconfigureRenew:
							DhcpStateMachine()->StartRenewL( this,0 );
							iState = ERenewInProgress;
							//cancel the renew timer (if any) after succesfully starting the above tasks
							iTimer->Cancel();
							break;
						default:
							// keep restarting reconfigure
							DhcpStateMachine()->StartReconfigureL( this );
						}
					}
				else
					{
					// keep restarting reconfigure
					DhcpStateMachine()->StartReconfigureL( this );
					}
			}
			else
				{
				//keep restarting reconfigure until a valid reconfigure arrives or
				//renew timer expires or interface goes down
				DhcpStateMachine()->StartReconfigureL( this );
				}
			break;
		case ERebindInProgress:
		case EInitInProgress:
			iTimer->Cancel();

			if ( aError != KErrNone )
				{//decline all
				DhcpStateMachine()->iInterfaceConfigInfo.SetAddressStatus( KAddrIndexAll, EMarkForDecline );
				}
			iState = EInitInProgress; //take the same action as for EInitInProgress
			CDHCPControl::TaskCompleteL(aError);
			break;
		case ERenewInProgress:
			iTimer->Cancel();
			if (KErrNone != aError)
				{
				//Renew process has failed => start rebind
				DhcpStateMachine()->StartRebindL( this );
				iState = ERebindInProgress;
				break;
				}
			//fall through if no error
		default:
			{
			CDHCPControl::TaskCompleteL(aError);
			}
		};
	//if the statemachine history indicates CDHCPState::EBinding and aError==KErrNone we have to check whether to
	//decline any addresses that haven't been verified by the stack as valid ones. If so we initiate decline
	//to decline the invalid addresses. In case at least one address is valid we go to reconfigure state after the decline.
	//=> amend UML and doc accordingly.
	if (DhcpStateMachine()->History() & CDHCPState::EBinding && aError == KErrNone )
		{//see CDHCPIP6StateMachine::StartDeclineL
		DhcpStateMachine()->StartDeclineL( this );
		iState = EDeclineInitialisedInProgress;
		//not changing CDHCPIP6Control state we are initialised and timer is running
		}
	else if ( iState == EInitialised || (iState == EEnd && prevState == EInformInProgress))
		{//we don't want this to leave
#if defined(DHCP_RECONFIGURE_NO_AUTHENTICATION)
		//we don't support reconfigure until we've sorted out authentication
		TRAPD(ret, DhcpStateMachine()->StartReconfigureL( this ));
		if (ret!=KErrNone)
			{
			__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPIP6Control::TaskCompleteL error: %d"),ret));
			}
		iState = EReconfigureInProgress;
#endif
		}
	}


TInt CDHCPIP6Control::HandleClientRequestL(TUint aName, TInt aValue)
	{
	TBool deferAllowed = !iMessage;
	
	if (iDhcpStateMachine)
		{
	   	switch (aName)
			{
			case KConnAddrRenew:
				if (deferAllowed && iDhcpStateMachine->IsGettingCfgInfoOnly())
					{
					iTimer->Cancel();
					iDhcpStateMachine->Cancel();
					iDhcpStateMachine->StartInformL(this,EFalse);
					iState = EInformInProgress;
					return ETrue;
					}
				break;
			default:
				break;
			}
		}
	return CDHCPControl::HandleClientRequestL(aName,aValue);		
	}
	
	
TInt CDHCPIP6Control::HandleClientRequestL(TUint aName)
	{
	return CDHCPIP6Control::HandleClientRequestL(aName,0);
	}

void CDHCPIP6Control::GetRawOptionDataL(TUint aOpCode, TPtr8& aPtr )
	{
	HBufC8* buf = NULL;
	DHCPv6::CDHCPMessageHeaderIP6 msg(buf);
	CleanupClosePushL(msg);
	TPtr8 ptr( const_cast<TUint8*>(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length() );
	msg.iRecord.ParseL(ptr); //no check necessary
    DHCPv6::COptionNode* pOption = msg.GetOptions().FindOption(aOpCode);
    if (!pOption)
      {
#ifdef SYMBIAN_TCPIPDHCP_UPDATE
      // The option is not found try to get the option through DHCP INFORM message 
      //by calling RequestInformOrCompleteCallL
      CleanupStack::PopAndDestroy();
	  TUint opcode = aOpCode;
	  TPtr8 optPtr(reinterpret_cast<TUint8*>(&opcode),1,1);
	  return(RequestInformOrCompleteCallL(optPtr));
#else	  
	  User::Leave(KErrNotFound);  
#endif //SYMBIAN_TCPIPDHCP_UPDATE	  
      }
    ptr.Set(pOption->GetBodyDes());
    if (ptr.Length() > aPtr.MaxLength())
      {
      User::Leave(KErrOverflow);
      }
    aPtr.Copy(ptr);
    CleanupStack::PopAndDestroy();
	}

/**
    Get raw option data (IP6 version).
    @param  pointer to the buffer descriptor for getting option data
*/
void CDHCPIP6Control::HandleGetRawOptionDataL(TDes8* aDes)
	{
	TDhcp6RawOptionDataPckg pckg(*aDes);
		
	TUint16 opCode = pckg.OpCode();
	TPtr8 buf(pckg.Buf());

	GetRawOptionDataL(opCode, buf);
	pckg.SetBufLengthL(buf.Length());
	}

#ifdef SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
void CDHCPIP6Control::GetDhcpHdrSiaddrL(TDes8& /*aNxtAddress*/)
	{
	//presently empty implementation.
	//PREQ 1647& 1648 implmentation is only for IPv4
	}
	
void CDHCPIP6Control::GetDhcpHdrSnameL(TDes8& /*aHdrSvrName*/)
	{
	//presently empty implementation.
	//PREQ 1647& 1648 implmentation is only for IPv4
	}
void CDHCPIP6Control::HandleGetTftpServerNameL(TDes8& /*aDes*/)
	{
	//presently empty implementation.
	//PREQ 1647& 1648 implmentation is only for IPv4
	}
void CDHCPIP6Control::HandleGetTftpServerAddrL(TDes8& /*aDes*/)
	{
	//presently empty implementation.
	//PREQ 1647& 1648 implmentation is only for IPv4
	}
#endif
#ifdef SYMBIAN_TCPIPDHCP_UPDATE
void CDHCPIP6Control::RequestInformOrCompleteCallL(TDes8& 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);
			}
		//Cancel if there is any outstanding request(DHCP reconfigure will cancelled)
		iDhcpStateMachine->Cancel();
		static_cast<CDHCPIP6StateMachine*>(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);	
		}
	}
#endif //SYMBIAN_TCPIPDHCP_UPDATE

#ifdef SYMBIAN_TCPIPDHCP_UPDATE 
void CDHCPIP6Control::HandleGetMultipleParamsL(TDes8& aDes)
#else
void CDHCPIP6Control::HandleGetMultipleParamsL(TDes8& /*aDes */)
#endif //SYMBIAN_TCPIPDHCP_UPDATE
/**
  * This function will be called when the application want to retrieve multiple 
  * options from the server by using option ORO(option 6) 
  *
  * @see RFC 3315 sec 22.7
  * @internalTechnology
  */
	{
#ifdef SYMBIAN_TCPIPDHCP_UPDATE	
	HBufC8 *saveBuf=NULL, *buffer=NULL;
	TUint16 *headerPtr;
	TInt numOfOpcodes=0;
	TInt totalLength=0,opcodeLength=aDes.Length();
	TInt maxLength=aDes.MaxLength();
	TUint16 opcode;
	TBool allFound=ETrue;
	
	DHCPv6::CDHCPMessageHeaderIP6 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
		
		DHCPv6::COptionNode* findNode = msgSaved.GetOptions().FindOption(opcode);
		if (findNode )
			{
			//get the opcode length
			TInt bufLength=findNode->GetItemLength();
			totalLength+=bufLength;
			//The opcode buffer length is greater than maximum length through overflow error
			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 = reinterpret_cast<TUint16*>(findNode->Ptr()); 
			++numOfOpcodes;
			//Append the opcode information to the buffer
			buffer->Des().Append(reinterpret_cast<const TUint8*>(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 || totalLength<=0)
			{
			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
		{
		//If the option is not found then trigger the information request
		TPtr8 opcodePtr(const_cast<TUint8*>(aDes.Ptr()),opcodeLength);
		opcodePtr.SetLength(opcodeLength);
		RequestInformOrCompleteCallL(opcodePtr);	
		}	
	
	if (buffer)
		{
		CleanupStack::PopAndDestroy(buffer);
		}

	CleanupStack::PopAndDestroy(&msgSaved);
#endif //SYMBIAN_TCPIPDHCP_UPDATE
	}

TInt CDHCPIP6Control::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
  */
	{
#ifdef SYMBIAN_TCPIPDHCP_UPDATE
	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;
			}

	default:
		__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("InformCompleteRequestHandlerL : default unhandled optino Name")));
		break;	
	
		}
		iDhcpStateMachine->iDhcpInformAckPending=EFalse;
		CleanupStack::PopAndDestroy(buff);
		return KErrNone;
#else
		return EFalse;
#endif //SYMBIAN_TCPIPDHCP_UPDATE
	}
void CDHCPIP6Control::HandleGetSipServerAddrL(TDes8* aDes)
	{	
	SSipServerAddr* sipServerAddr = 
		reinterpret_cast<SSipServerAddr*>(const_cast<TUint8*>(aDes->Ptr()));

	HBufC8* buf = NULL;
	DHCPv6::CDHCPMessageHeaderIP6 msg(buf);
	CleanupClosePushL(msg);
	TPtr8 ptr(const_cast<TUint8*>(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length());
	msg.iRecord.ParseL(ptr);

	DHCPv6::COptionList* optionList = &(msg.GetOptions());
	
	CDHCPOptionSipServerAddrs* sipServerAddrs = 
		static_cast<CDHCPOptionSipServerAddrs*>(optionList->FindOption(ESipServerA));
	
	TBool ret = EFalse;

	if(sipServerAddrs)
		{
		ret = sipServerAddrs->GetSipServerAddr(sipServerAddr->index, 
			sipServerAddr->address);
		}

	User::LeaveIfError(ret ? KErrNone : KErrNotFound);
	CleanupStack::PopAndDestroy();
	}

void CDHCPIP6Control::HandleGetSipServerDomainL(TDes8* aDes)
	{	
	SSipServerDomain* sipServerDomain = 
		reinterpret_cast<SSipServerDomain*>(const_cast<TUint8*>(aDes->Ptr()));

	HBufC8* buf = NULL;
	DHCPv6::CDHCPMessageHeaderIP6 msg(buf);
	CleanupClosePushL(msg);
	TPtr8 ptr(const_cast<TUint8*>(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length());
	msg.iRecord.ParseL(ptr);

	DHCPv6::COptionList* optionList = &(msg.GetOptions());
	
	CDHCPOptionSipServerDomains* sipServerDomains = 
		static_cast<CDHCPOptionSipServerDomains*>(optionList->FindOption(ESipServerD));

	if(sipServerDomains)
		{
		CDomainNameCodec* domainNameDecoder = new(ELeave) CDomainNameCodec();
		CleanupStack::PushL(domainNameDecoder);
		TPtr8 ptr = sipServerDomains->GetBodyDes();
		domainNameDecoder->DecodeL(ptr);

		if(sipServerDomain->index < (TInt)domainNameDecoder->NumDomainNames())
			{
			TDomainName domainName = (*domainNameDecoder)[sipServerDomain->index];
			sipServerDomain->domainName.Copy(domainName);
			}
		else
			{
			User::Leave(KErrNotFound);
			}
			
		CleanupStack::PopAndDestroy();
		}
	else
		{
		User::Leave(KErrNotFound);
		}
		
	CleanupStack::PopAndDestroy();
	}
#ifdef SYMBIAN_TCPIPDHCP_UPDATE
void CDHCPIP6Control::HandleGetDomainSearchListL(TDes8* aDes)
/**
  * This function will be called when the application want to retrieve the 
  * list of Domain names from the DomainSearchList option (option 24)by using 
  * KConnGetDomainSearchList variable.
  *
  * @see RFC 3646,
  * @internalTechnology
  */
	{
	SDomainSearchList* domainsearchlist = 
			reinterpret_cast<SDomainSearchList*>(const_cast<TUint8*>(aDes->Ptr()));

	HBufC8* buf = NULL;
	DHCPv6::CDHCPMessageHeaderIP6 msg(buf);
	CleanupClosePushL(msg);
	TPtr8 ptr(const_cast<TUint8*>(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length());
	msg.iRecord.ParseL(ptr);

	DHCPv6::COptionList* optionList = &(msg.GetOptions());
	
	CDHCPOptionDomainSearchList* domainsearchlistoption = 
		static_cast<CDHCPOptionDomainSearchList*>(optionList->FindOption(EDomainList));
		
	if(domainsearchlistoption)
		{
		if(domainsearchlist->index >= 0)
			{
			CDomainNameCodec* domainNameDecoder = new(ELeave) CDomainNameCodec();
			CleanupStack::PushL(domainNameDecoder);
			TPtr8 ptr = domainsearchlistoption->GetBodyDes();
			domainNameDecoder->DecodeL(ptr);
		
			if(domainsearchlist->index < (TInt)domainNameDecoder->NumDomainNames())
				{
				TDomainName domainName = (*domainNameDecoder)[domainsearchlist->index];
				domainsearchlist->domainname.Copy(domainName);
				__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPIP6Control::HandleGetDomainSearchListL -index = %d  domain name  = \"%S\" "), domainsearchlist->index,&(domainsearchlist->domainname)));
				}
			else
				{
				__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPIP6Control::HandleGetDomainSearchListL: Client tried to fetch an out-of-range domain names %d (I only know about %d)"), domainsearchlist->index , (TInt)domainNameDecoder->NumDomainNames()));
				User::Leave(KErrNotFound);
				}
			
			CleanupStack::PopAndDestroy(domainNameDecoder);
			}
		else
			{
			__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPIP6Control::HandleGetDomainSearchListL: index is  not >=0 %d "), domainsearchlist->index));
			User::Leave(KErrNotFound);
			}
		}
	else
		{
		__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPIP6Control::HandleGetDomainSearchListL option is not found ")));
		User::Leave(KErrNotFound);
		}
	
	CleanupStack::PopAndDestroy(&msg);
	}

void CDHCPIP6Control::HandleGetDNSServerListL(TDes8* aDes)
/**
  * This function will be called when the application want to retrieve the 
  * list of IPV6 addresses of DNS recursive name server from the
  * DNS Recursive Name Server option (option 23)by using 
  * KConnGetDNSServerList variable.
  *
  * @see RFC 3646,
  * @internalTechnology
  */	
	{
	SDNSServerAddr* dnsServerAddr = 
			reinterpret_cast<SDNSServerAddr*>(const_cast<TUint8*>(aDes->Ptr()));

	HBufC8* buf = NULL;
	DHCPv6::CDHCPMessageHeaderIP6 msg(buf);
	CleanupClosePushL(msg);
	TPtr8 ptr(const_cast<TUint8*>(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length());
	msg.iRecord.ParseL(ptr);

	DHCPv6::COptionList* optionList = &(msg.GetOptions());
	
	CDHCPOptionDNSServers* dnsServerAddrsoption = 
		static_cast<CDHCPOptionDNSServers*>(optionList->FindOption(EDNSServers));
	
	TInt ret = KErrNone;
	if(dnsServerAddrsoption)
		{
		if(dnsServerAddr->index >=0)
			{
			ret = dnsServerAddrsoption->GetDomainNameServer(dnsServerAddr->index, 
				dnsServerAddr->addres);
			__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPIP6Control::HandleGetDNSServerListL -index = %d  DNS server address = \"%S\" "), ret,&(dnsServerAddr->addres)));
			}
		else
			{
			__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPIP6Control::HandleGetDNSServerListL -index is out of range ")));
			ret = KErrNotFound;
			}
		}
	else
		{
		__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPIP6Control::HandleGetDNSServerListL option is not found ")));
		ret = KErrNotFound;	
		}
	
	User::LeaveIfError(ret);
	CleanupStack::PopAndDestroy(&msg);
	
	}
#endif //SYMBIAN_TCPIPDHCP_UPDATE
