diff -r 000000000000 -r af10295192d8 tcpiputils/dhcp/src/DHCPIP4Control.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tcpiputils/dhcp/src/DHCPIP4Control.cpp Tue Jan 26 15:23:49 2010 +0200 @@ -0,0 +1,891 @@ +// 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 +#include +#include "netcfgextndhcpmsg.h" +#include +#include +#include +#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 ) + 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(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(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length() ); + msg.iRecord.ParseL(ptr); //no check necessary + DHCPv4::COptionNode* pOption = msg.iOptions.FindOption(static_cast(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(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length() ); + msg.iRecord.ParseL(ptr); //no check necessary + + TDhcp4RawOptionDataPckg pckg(*aDes); + DHCPv4::TDHCPOptionCodes opcode = static_cast(pckg.OpCode()); + + DHCPv4::COptionNode* pOption = msg.iOptions.FindOption(opcode); + if (!pOption) + { + User::Leave(KErrNotFound); + } + + TPtr8 ptr2(reinterpret_cast(aDes),aDes->Length()); + + pOption->SetBody(ptr2); + CleanupStack::PopAndDestroy(); // msg + } +#endif // SYMBIAN_NETWORKING_DHCPSERVER + +void CDHCPIP4Control::HandleGetSipServerAddrL(TDes8* aDes) + { + if (aDes->Length() < static_cast(sizeof(SSipServerAddr))) + { + User::Leave(KErrArgument); + } + + SSipServerAddr* sipServerAddr = + reinterpret_cast(const_cast(aDes->Ptr())); + + HBufC8* buf = NULL; + DHCPv4::CDHCPMessageHeaderIP4 msg(buf); + CleanupClosePushL(msg); + TPtr8 ptr(const_cast(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(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(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(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(sizeof(STftpServerAddr))) + { + User::Leave(KErrArgument); + } + + STftpServerAddr* tftpServerAddr = + reinterpret_cast(const_cast(aDes.Ptr())); + + HBufC8* buf = NULL; + DHCPv4::CDHCPMessageHeaderIP4 msg(buf); + CleanupClosePushL(msg); + TPtr8 ptr(const_cast(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(iValidMsg.Ptr()), iValidMsg.Length(), iValidMsg.Length()); + msg.iRecord.ParseL(ptr); + DHCPv4::COptionNode* tftpOption = msg.iOptions.FindOption(DHCPv4::EDHCPTftpServerName); + if(tftpOption) + { + if (aTftpSvrName.MaxLength()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(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(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;iGetItemLength(); + 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(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(sizeof(SSipServerDomain))) + { + User::Leave(KErrArgument); + } + + SSipServerDomain* sipServerDomain = + reinterpret_cast(const_cast(aDes->Ptr())); + + HBufC8* buf = NULL; + DHCPv4::CDHCPMessageHeaderIP4 msg(buf); + CleanupClosePushL(msg); + TPtr8 ptr(const_cast(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 ; + } +