// 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 DHCPv4 Message format
//
//
/**
@file DHCPIP4Msg.cpp
@internalTechnology
*/
#include "DHCPIP4Msg.h"
#include "DHCP_Std.h"
using namespace DHCPv4;
COptionNode::~COptionNode()
{
delete iNext;
}
struct SOptionHeader
{
TInt iCode;
TInt iExpLength; // length of the payload of the option in case the payload has a fixed length
TInt iAlignment; // alignment of the payload
TInt iMinLength; // length of the whole option (including code & length i present)
#ifdef __FLOG_ACTIVE
const TText8* iName;
#endif
};
#ifdef SYMBIAN_NETWORKING_DHCPSERVER
#ifdef SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
const TInt KNumberOfSupportedOptions = 20;
#else
const TInt KNumberOfSupportedOptions = 17;
#endif
#else
#ifdef SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
const TInt KNumberOfSupportedOptions = 19;
#else
const TInt KNumberOfSupportedOptions = 16;
#endif
#endif
#ifdef __FLOG_ACTIVE
const SOptionHeader KOptions[KNumberOfSupportedOptions]=
{ //iCode, iExpLength, iAlignment, iMinLength iName
{EDHCPSubnetMask, 4, 1, 6, _S8( "EDHCPSubnetMask" )}, //must be the first option
{EDHCPRouter, 0, 4, 6, _S8( "EDHCPRouter" )},
#ifdef SYMBIAN_NETWORKING_DHCPSERVER
{EDHCPNameServer, 0, 4, 6, _S8( "EDHCPNameServer" )},
#endif // #ifdef SYMBIAN_NETWORKING_DHCPSERVER
{EDHCPDomainNameServer, 0, 4, 6, _S8( "EDHCPDomainNameServer" )},
{EDHCPHostName, 0, 1, 3, _S8( "EDHCPHostName" )},
{EDHCPDomainName, 0, 1, 3, _S8( "EDHCPDomainName" )},
{EDHCPBroadcastAddr, 4, 4, 6, _S8( "EDHCPBroadcastAddr" )},
{EDHCPRequestedIPAddr, 4, 4, 6, _S8( "EDHCPRequestedIPAddr" )},
{EDHCPLeaseTime, 4, 4, 6, _S8( "EDHCPLeaseTime" )},
{EDHCPMessageType, 1, 1, 3, _S8( "EDHCPMessageType" )},
{EDHCPServerID, 4, 4, 6, _S8( "EDHCPServerID" )},
{EDHCPParameterReqList, 0, 1, 3, _S8( "EDHCPParameterReqList" )},
{EDHCPMaxMsgSize, 2, 2, 4, _S8( "EDHCPMaxMsgSize" )},
{EDHCPRenewalT1, 4, 4, 6, _S8( "EDHCPRenewalT1" )},
{EDHCPRebindT2, 4, 4, 6, _S8( "EDHCPRebindT2" )},
// {EDHCPAuthentication, 0, 1, 12, _S8( "EDHCPAuthentication" )}, not yet
{EDHCPSIPServers, 0, 1, 3, _S8( "EDHCPSIPServers" )},
#ifdef SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
{EDHCPTftpServerName, 0, 1, 3, _S8( "EDHCPTftpServerName")},
{EDHCPOptionOverload, 0, 1, 3, _S8( "EDHCPOptionOverload")},
{EDHCPTftpServers, 0, 1, 3, _S8( "EDHCPTftpServers" )},
#endif // SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
{EDHCPClientID, 0, 1, 4, _S8( "EDHCPClientID" )}
};
void COptionNode::Dump(const TDesC& aTag, const TDesC& aFile)
{
RFileLogger f;
if (f.Connect() == KErrNone)
{
f.CreateLog(aTag, aFile, EFileLoggingModeAppend);
TInt i = 0;
while (KOptions[i].iCode != OpCode() && ++i < KNumberOfSupportedOptions){/*do nothing*/}
TInt n = iPtr8[1];
if (i < KNumberOfSupportedOptions)
{
f.WriteFormat(_L8("Option Code: %s, length: %d"), KOptions[i].iName, n);
}
else
{
f.WriteFormat(_L( "Option Code: %d, length %d" ), OpCode(), n );
}
if (GetItemLength() > 2)
{
f.HexDump(NULL, NULL, GetBodyPtr(), GetLength());
}
f.CloseLog();
f.Close();
}
}
#else
const SOptionHeader KOptions[KNumberOfSupportedOptions]=
{ //iCode, iExpLength, iAlignment, iMinLength
{EDHCPSubnetMask, 4, 1, 6}, //must be the first option
{EDHCPRouter, 0, 4, 6},
#ifdef SYMBIAN_NETWORKING_DHCPSERVER
{EDHCPNameServer, 0, 4, 6},
#endif // SYMBIAN_NETWORKING_DHCPSERVER
{EDHCPDomainNameServer, 0, 4, 6},
{EDHCPHostName, 0, 1, 3},
{EDHCPDomainName, 0, 1, 3},
{EDHCPBroadcastAddr, 4, 4, 6},
{EDHCPRequestedIPAddr, 4, 4, 6},
{EDHCPLeaseTime, 4, 4, 6},
{EDHCPMessageType, 1, 1, 3},
{EDHCPServerID, 4, 4, 6},
{EDHCPParameterReqList, 0, 1, 3},
{EDHCPMaxMsgSize, 2, 2, 4},
{EDHCPRenewalT1, 4, 4, 6},
{EDHCPRebindT2, 4, 4, 6},
{EDHCPClientID, 0, 1, 4},
#ifdef SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
{EDHCPTftpServerName, 0, 1, 3},
{EDHCPOptionOverload, 0, 1, 3},
{EDHCPTftpServers, 0, 1, 3},
#endif // SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
{EDHCPSIPServers, 0, 1, 3}
};
#endif
void COptionList::ParseL(TPtr8& aDes8)
/**
* Parse message to set pointers into descriptor buffer
* at locations of each option supplied in a response msg
*
* @internalTechnology
*/
{
__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("COptionList::ParseL")));
ASSERT(!iRecord.iFirst);
COptionNode** ppNode = reinterpret_cast<COptionNode**>(&iRecord.iFirst);
TUint8 nCode = *aDes8.Ptr();
COptionNode* pNode;
TInt i, nItemLen, nHeaderLength;
while (nCode != EDHCPEnd && aDes8.Length() > 0)
{
for (i = 0; KOptions[i].iCode != nCode && ++i < KNumberOfSupportedOptions;) {}
nHeaderLength = (nCode == EDHCPPad) ? KEDHCPPadLength : KOptionHeaderLength;
pNode = new(ELeave) COptionNode(nHeaderLength);
/* ppNode is a pointer to a pointer. It has not got assigned any dynamically allocated memory. But coverity has misinterpreted it an issue.*/
// coverity [SYMBIAN.CLEANUP STACK]
// coverity [leave_without_push]
CleanupStack::PushL(pNode);
/* pNode can not be Null here as the memmory assignment is done using new(ELeave). Thus, there is no need of checking for Null, explicitly .*/
// coverity[deref_ptr_in_call]
pNode->ParseL(aDes8);
nItemLen = pNode->GetItemLength();
if (aDes8.Length() > 0)
{
nCode = *aDes8.Ptr();
}
else
{
__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("COptionList::ParseL Option list ended without EDHCPEnd flag")));
}
if (i < KNumberOfSupportedOptions)
{
// now do some sanity checks and leave if bad results
TInt nPayloadLength = nItemLen - nHeaderLength;
*ppNode = pNode;
if (nItemLen < KOptions[i].iMinLength) // min length check
{
__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("COptionList::ParseL BadDescriptor 2")));
*ppNode = NULL;
User::Leave(KErrBadDescriptor);
}
if (nPayloadLength % KOptions[i].iAlignment != 0) // alignment check
{
__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("COptionList::ParseL BadDescriptor 3")));
*ppNode = NULL;
User::Leave(KErrBadDescriptor);
}
if (KOptions[i].iExpLength > 0 && nPayloadLength != KOptions[i].iExpLength) //expected length check
{
__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("COptionList::ParseL BadDescriptor 4")));
*ppNode = NULL;
User::Leave(KErrBadDescriptor);
}
ppNode = reinterpret_cast<COptionNode**>(&(pNode->iNext));
CleanupStack::Pop(pNode);
}
else
{
#ifdef SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
//make sure we parse everything becz we support every option now
if (pNode)
{
*ppNode = pNode;
ppNode = reinterpret_cast<COptionNode**>(&(pNode->iNext));
CleanupStack::Pop(pNode);
}
else
{
#endif // SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("COptionList::ParseL Code Not Found")));
/* ppNode is a pointer to a pointer. It has not got assigned any dynamically allocated memory. But coverity has misinterpreted it an issue.*/
// coverity [SYMBIAN.CLEANUP STACK]
// coverity [memory_leak]
CleanupStack::PopAndDestroy(pNode);
pNode=NULL;
#ifdef SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
}
#endif // SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
}
}
}
COptionNode* COptionList::AddNodeL(TInt aInitialLength, TInt aHeaderLength)
/**
* Create a new node for an option in the message
*
* @internalTechnology
*/
{
COptionNode* pNode = new(ELeave) COptionNode(aHeaderLength);
AddNode(pNode);
pNode->Header().SetInitialValue(aInitialLength);
return pNode;
}
COptionNode* COptionList::FindOption(TUint8 aOpCode) const
/**
* Find the location of an option in the message and return a pointer to it
*
* @internalTechnology
*/
{
COptionNode* pNode = static_cast<COptionNode*>(iRecord.iFirst);
while (pNode && pNode->OpCode() != aOpCode)
{
pNode = static_cast<COptionNode*>(pNode->iNext);
}
return pNode;
}
TInt COptionList::NumberOfItemsInAddressListOption(TUint8 aOpCode) const
/**
* Return the number of addresses
* from the specified server option.
*
* @internalTechnology
*/
{
COptionNode* pNode = FindOption(aOpCode);
return pNode ? pNode->GetLength()/KIp4AddrByteLength : 0;
}
TUint32 COptionList::GetAddressFromAddressListOption(TUint8 aOpCode,TInt aPos) const
/**
* Return the address at position aPos in the specified server option code
* in TUint32 format.
*
* @param aPos Indicates which one of the addresses in the list to return
* @return TUint32 The address
*
* @internalTechnology
*/
{
COptionNode* pNode = FindOption(aOpCode);
return pNode ? BigEndian::Get32(pNode->GetBodyPtr() + aPos * KIp4AddrByteLength) : 0;
}
TUint32 COptionList::GetValueBigEndian(TUint8 aOpCode) const
/**
* Return a value from the message in big endian format
*
* @internalTechnology
*/
{
COptionNode* pNode = FindOption(aOpCode);
return pNode ? pNode->GetBigEndian() : 0;
}
TInt COptionList::NumberOfDomainServers() const
/**
* Return the number of domain name server addresses
* from the domain name server option. There are
* expected to be more than one.
*
* @internalTechnology
*/
{
return NumberOfItemsInAddressListOption(EDHCPDomainNameServer);
}
TUint32 COptionList::GetDomainNameServer(TInt aPos) const
/**
* Return the domain name server address at position aPos
* in TUint32 format.
*
* @param aPos Indicates which one of the domain name server addresses to return
* @return TUint32 The address of the domain name server
*
* @internalTechnology
*/
{
return GetAddressFromAddressListOption(EDHCPDomainNameServer,aPos);
}
TBool COptionList::CopyHostNameL(HBufC8*& aBuf8) const
/**
* Return a copy of the host name given in the message
*
* @param aBuf8 A descriptor for the host name to be written into
* @return TBool If the copy was successful or not
*
* @internalTechnology
*/
{
COptionNode* pNode = FindOption(EDHCPHostName);
if (pNode)
{
pNode->CopyBodyToL(aBuf8);
}
return pNode != NULL;
}
TBool COptionList::CopyDomainNameL(HBufC8*& aBuf8) const
/**
* Return a copy of the domain name given in the message
*
* @param aBuf8 A descriptor for the domain name to be written into
* @return TBool If the copy was successful or not
*/
{
COptionNode* pNode = FindOption(EDHCPDomainName);
if (pNode)
{
pNode->CopyBodyToL(aBuf8);
}
return pNode != NULL;
}
CDHCPMessageHeaderIP4::CDHCPMessageHeaderIP4(HBufC8*& aMsg) :
CDHCPMessageHeader(&iXid, 4, aMsg), //| op (1) | htype (1) | hlen (1) | hops (1) |
iXid(&iSecs, 4), iSecs(&iFlags, 2), iFlags(&iCiaddr, 2),
iCiaddr(&iYiaddr, 4), iYiaddr(&iSiaddr, 4), iSiaddr(&iGiaddr, 4),
iGiaddr(&iChaddr, 4), iChaddr(&iSname, 16), iSname(&iFile, 64),
iFile(&iCookie, 128), iCookie(&iOptions, 4),
iOptions(NULL)
{
#ifdef _FLOG_ACTIVE
iName = _L("op ht hl ho");
iXid.iName = _L("Xid");
iSecs.iName = _L("Secs");
iFlags.iName = _L("Flags");
iCiaddr.iName = _L("Ciaddr");
iYiaddr.iName = _L("Yiaddr");
iSiaddr.iName = _L("Siaddr");
iGiaddr.iName = _L("Giaddr");
iChaddr.iName = _L("Chaddr");
iSname.iName = _L("Sname");
iFile.iName = _L("File");
iCookie.iName = _L("Cookie");
#endif
}
CDHCPMessageHeaderIP4::~CDHCPMessageHeaderIP4()
{
Close();
}
COptionNode* CDHCPMessageHeaderIP4::AddOptionL(TDHCPOptionCodes aOpCode, TInt aLength)
/**
* First stage of adding an option to the message. Calls AddNode to create space
* for message.
*
* @param aOpCode The opcode of the options to be added
* @param aLength The length of the option data
* @return COptionNode* The pointer to the option in the message
*
@ @internalTechnology
*/
{
COptionNode* pNode = iOptions.AddNodeL(aLength, aLength > 0 ? KOptionHeaderLength : 1);
TPtr8 ptr = iMsg->Des();
pNode->InitialiseL(ptr);
pNode->SetOpCode(static_cast<TUint8>(aOpCode));
return pNode;
}
#ifdef SYMBIAN_NETWORKING_DHCPSERVER
void CDHCPMessageHeaderIP4::ParseClientMsgL()
{
__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPMessageHeaderIP4::ParseL")));
TPtr8 ptr = iMsg->Des();
iRecord.ParseL(ptr);
// now it is parsed, first check whether this is actually a REPLY message from a DHCP Server...
if (GetOpCode() != EDHCPBootRequest)
{
User::Leave(KErrNotFound);
}
}
void CDHCPMessageHeaderIP4::GetCHAddr(TSockAddr& aSockAddr)
/**
* Retrieve Your IP Address from message
*
* @internalTechnology *
*/
{
aSockAddr.SetFamily(KAFUnspec);
aSockAddr.SetLength(KHwAddrOffset);
aSockAddr.Append(iChaddr.GetBodyDes());
}
#ifdef SYMBIAN_NETWORKING_ADDRESS_PROVISION
void CDHCPMessageHeaderIP4::GetClientHwAddr(TSockAddr& aSockAddr)
{
/**
* Return the Client Hardware Address.
*
* @internalTechnology
*/
aSockAddr.Append(iChaddr.GetBodyDes());
}
#endif //SYMBIAN_NETWORKING_ADDRESS_PROVISION
#endif // SYMBIAN_NETWORKING_DHCPSERVER
#ifdef SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
void CDHCPMessageHeaderIP4::FinishL(TDesC8& aClientId, const TDesC8* aOptionsPtr)
/**
* Put finishing touches to message for sending. Mainly used for sending DHCPINFORM message
* Puts the requested parameter list to 'Parameter Request List'
* If aFlag is set to TRUE, then all parameters have to requested including default parameters+extra requested parameters
* Basically copies in the magic cookie (99.130.83.99)
* and the end marker then set the length of the descriptor
* as we have been pushing data into the descriptor manually
*
* @see RFC 2131 for explanation of the magic cookie!
* @param aClientId The client ID string to be added to the message
* @param aOptionsPtr Contains a list of opcodes to be sent in the parameter list of the DHCPINFORM message
*
* @internalTechnology
*/
#else
void CDHCPMessageHeaderIP4::FinishL(TDesC8& aClientId)
/**
* Put finishing touches to message for sending
* Basically copies in the magic cookie (99.130.83.99)
* and the end marker then set the length of the descriptor
* as we have been pushing data into the descriptor manually
*
* @param aClientId The client ID string to be added to the message
* @see RFC 2131 for explanation of the magic cookie!
*
* @internalTechnology
*/
#endif //SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
{
__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPMessageBase::FinishL")));
#ifdef SYMBIAN_NETWORKING_DHCPSERVER
if(!iDHCPServerImpl)
{
#endif // SYMBIAN_NETWORKING_DHCPSERVER
TUint8 reqListArray[KDhcpParameterRequestListLen] = {EDHCPHostName, EDHCPDomainNameServer,
EDHCPDomainName, EDHCPSubnetMask, EDHCPRouter, EDHCPBroadcastAddr, EDHCPSIPServers};
TPtr8 ptr(reqListArray, KDhcpParameterRequestListLen, KDhcpParameterRequestListLen);
// +++++++++++++++++++++++ Client ID +++++++++++++++++++++++++++++++++++++++++/
AddOptionL(EDHCPClientID, aClientId.Length())->GetBodyDes().Copy(aClientId);
#ifdef SYMBIAN_NETWORKING_DHCP_MSG_HEADERS
RBuf8 appendOpCodeList;
appendOpCodeList.CreateL(ptr);
if (aOptionsPtr)
{
TInt optLen=aOptionsPtr->Length();
appendOpCodeList.ReAllocL(KDhcpParameterRequestListLen+optLen);
appendOpCodeList.Append(aOptionsPtr->Ptr(),optLen);
}
AddOptionL(EDHCPParameterReqList, appendOpCodeList.Length())->GetBodyDes().Copy(appendOpCodeList);
appendOpCodeList.Close();
#else
// +++++++++++++++++++++++ Parameter Request List ++++++++++++++++++++++++++++/
AddOptionL(EDHCPParameterReqList, ptr.Length())->GetBodyDes().Copy(ptr);
#endif
// +++++++++++++++++++++++ Maximum message size (2 bytes) ++++++++++++++++++++/
AddOptionL(EDHCPMaxMsgSize, 2)->SetBigEndian(KDhcpMaxMsgSizeIP4);
#ifdef SYMBIAN_NETWORKING_DHCPSERVER
}
#endif // SYMBIAN_NETWORKING_DHCPSERVER
TInetAddr magic;
_LIT(magicCookie, "99.83.130.99");
User::LeaveIfError(magic.Input(magicCookie)); // essential the magic cookie is correct
iCookie.SetLittleEndian(magic.Address());
ASSERT(!iOptions.FindOption(EDHCPEnd));
AddOptionL(EDHCPEnd, 0);
//add padding if msg shorter than 300 bytes
TInt len=iMsg->Length();
TPtr8 des=iMsg->Des();
if (len<300)
{
des.AppendFill(EDHCPPad, 300-len);
}
}
void CDHCPMessageHeaderIP4::ParseL()
/**
* When a response is received to a message
* this function will unpack the response by creating
* option objects which can be easily used to retrieve
* the values returned in these options. Providing
* this functionality hides the nasty descriptor operations.
*
* Each option class has a pointer set to the start point
* of their data in the response message.
*
* There is no need to explicitly unpack the mandatory fields
* of the DHCP message that has been returned. Accessor functions
* simply extract the data sraight out of the decsriptor.
*
* @internalTechnology
*/
{
__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPMessageHeaderIP4::ParseL")));
TPtr8 ptr = iMsg->Des();
iRecord.ParseL(ptr);
// now it is parsed, first check whether this is actually a REPLY message from a DHCP Server...
if (GetOpCode() != EDHCPBootReply)
{
User::Leave(KErrNotFound);
}
}
void CDHCPMessageHeaderIP4::SetHeader(TUint8 aOpCode,TUint8 aType,TUint8 aLength,TUint8 aHops)
/**
* Sets Op Code in message
*
* @internalTechnology
*
*/
{
__CFLOG_VAR((KLogSubSysDHCP, KLogCode, _L8("CDHCPMessageHeaderIP4::SetHeader")));
TUint8* ptr = &(GetBodyPtr()[0]);
*ptr = aOpCode;
*++ptr = aType;
*++ptr = aLength;
*++ptr = aHops;
}
void CDHCPMessageHeaderIP4::SetCHAddr(TSockAddr& aAddr)
/**
* Sets If Hardware Address in message
*
* @internalTechnology
*
*/
{
TUint len = aAddr.Length()-KHwAddrOffset;
if(len > KIp4ChAddrMaxLength) { len = KIp4ChAddrMaxLength; }
iChaddr.GetBodyDes().Copy(aAddr.Mid(KHwAddrOffset, len));
}
void CDnsUpdateOption::ToStringL(RBuf8& aBuf8) const
/**
* Writes DNS update option data to a string suitable for sending out
* on to the network
*
* @internalTechnology
*
*/
{
RBuf8 encodedDomainName;
encodedDomainName.CleanupClosePushL();
TDomainNameArray domainNames;
CleanupClosePushL(domainNames);
domainNames.AppendL(iDomainName);
CDomainNameCodec* domainNameEncoder = new(ELeave) CDomainNameCodec();
CleanupStack::PushL(domainNameEncoder);
domainNameEncoder->EncodeL(domainNames, encodedDomainName);
CleanupStack::PopAndDestroy(domainNameEncoder);
CleanupStack::PopAndDestroy(&domainNames); // closes the RBuf8
aBuf8.Zero();
aBuf8.ReAllocL( 3 + encodedDomainName.Length() );
aBuf8.Append((TChar)(iFlags.Value()));
aBuf8.Append((TChar)iRCode1);
aBuf8.Append((TChar)iRCode2);
aBuf8.Append(encodedDomainName);
CleanupStack::PopAndDestroy(&encodedDomainName); // closes the array
}