linklayerprotocols/pppnif/SPPP/PPPOPT.CPP
changeset 0 af10295192d8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/linklayerprotocols/pppnif/SPPP/PPPOPT.CPP	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,709 @@
+// Copyright (c) 1997-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:
+//
+
+#include "PPPBASE.H"
+
+// Options are based on aligned MBuf Chains.
+// Steps are taken to guarantee that the first 128 bytes
+// of an option are held in the first MBuf
+
+RPppOption::RPppOption()
+	{
+	}
+
+RPppOption::RPppOption(RMBufChain& aChain)
+//
+// Note: this empties the original chain
+//
+	{
+	Assign(aChain);
+	Align(KMBufSmallSize);
+	}
+
+void RPppOption::SetL(TUint8 aType, const TAny* aPtr, TInt aLen)
+	{
+
+	aLen += 2;
+
+	if (aLen>MaxLength())
+		{
+		Free();
+		AllocL(aLen);
+		}
+	else
+		SetLength(aLen);
+
+	First()->Put(aType, 0);
+	First()->Put((TUint8)aLen, 1);
+	if (aLen>0)
+		CopyIn(TPtrC8((TUint8*)aPtr, aLen-2), 2);
+	}
+
+void RPppOption::SetValueLength(TInt aLen)
+	{
+	aLen += 2;
+	ASSERT(aLen<=KMBufSmallSize);
+	First()->Put((TUint8)aLen, 1);
+	First()->SetLength(aLen);
+	}
+
+TUint8 RPppOption::OptType() const
+	{
+	return First()->Get(0);	
+	}
+
+void RPppOption::SetType(const TUint8 aType)
+	{
+	TUint8* Ptr;
+
+	Ptr = First()->Ptr();
+	*Ptr = aType;	
+	}
+
+TInt RPppOption::ValueLength() const
+	{
+	return First()->Get(1)-2;	
+	}
+
+TUint8* RPppOption::ValuePtr()
+	{
+	return First()->Ptr()+2;
+	}
+
+const TUint8* RPppOption::ValuePtr() const
+	{
+	return First()->Ptr()+2;
+	}
+
+/**
+Re-aligns the underlying storage in an MBuf chain
+droppng any MBufs that are no longer needed
+DOES NOT preserve data.
+*/
+TInt RPppOption::SetLength(TInt aLength)
+	{
+	RMBuf* p = NULL;
+	RMBuf* m = First();
+	while (m!=NULL && aLength>0)
+		{
+		TInt n = m->Size();
+		aLength -= n;
+		m->SetData(0, n);
+		if (aLength<0)
+			m->AdjustEnd(aLength);
+		p = m;
+		m = m->Next();
+		}
+	if (m!=NULL && p!=NULL)
+		{
+		p->Unlink();
+		m->Free();
+		}
+	if (aLength>0)
+		return KErrGeneral;
+	return KErrNone;
+	}
+
+/**
+Return the size of underlying storage in an MBuf chain
+*/
+TInt RPppOption::MaxLength() const
+	{
+	TInt len = 0;
+	const RMBuf* m = First();
+	while (m!=NULL)
+		{
+		len += m->Size();
+		m = m->Next();
+		}
+	return len;
+	}
+
+void RPppOptionList::SetL(RMBufChain& aPacket, TInt aLength)
+	{
+	CleanupStack::PushL(*this);
+	CleanupStack::PushL(aPacket);
+
+	if (aLength==0)
+		aLength = aPacket.Length();
+
+	RMBufChain tmp;
+ 	while (aLength>=2)
+		{
+		aPacket.Align(2);
+		TInt len = aPacket.First()->Get(1);
+		
+		// Carlson97 (p.41) suggests that option length of 0 or 1 
+		// should be treated as 2 for the sake of verifying 
+		// the packet integrity when doing the length checks:
+		//
+		// "If, however, and option has an improper Len field for that
+		// type of option, but all of the lengths otherwise add up correctly, then
+		// the option should, according to the RFC 1661, be included in a Configure Nak message
+		// with the Len field changed to the proper length. (Dealing with a Len field set to 00 or 01 is a grey 
+		// area in the standard. I recommend treating this as as though it had been 02 for the sake of verifying 
+		// the packet integrity when doing the length checks; if these checks succeed, a Configure Nak should be 
+		// returned with the correct Len field for those options. ...)"
+		//
+		// This targets cases where the option fields have correct length, but the option Len value is incorrect.
+		// That is, the packet can be correctly parsed.
+		// Setting option len to 2 may result in either overruning the packet length during parsing, 
+		// or in parsing the packet incorrectly (incorrect option ID, values, etc). Both cases are checked for later.		 
+		if (len < 2)
+			len = 2;
+		
+		if (len > aLength) // The option extends beyond the end of the packet.
+			User::Leave(KErrCorrupt);
+
+		aPacket.SplitL(len, tmp);
+		Append(aPacket);
+		aPacket.Assign(tmp);
+		aLength -= len;
+		}
+	// If there is anything left over, then the list and packet
+	// are discarded as we probably have a truncated option.
+	if (aLength>0)
+		User::Leave(KErrCorrupt);
+
+	CleanupStack::Pop(2);
+	}
+
+TBool RPppOptionList::IsSubsetOf(RPppOptionList& aList)
+/**
+Check if the current option list is a subset of the OptionsList presented (aList).
+
+All the options in the current list must be contained and must be in the same order as the options in aList
+All the option arguments must also be identical
+
+@param aList option list to compare against.
+@return ETrue if option lists match, else EFalse
+*/
+	{
+	TMBufPktQIter iter1(*this);
+	TMBufPktQIter iter2(aList);
+
+	RPppOption opt1;
+	RPppOption opt2;
+	
+	opt1 = iter1++;
+	opt2 = iter2++;
+
+	while (!opt1.IsEmpty())
+		{
+		if (opt2.IsEmpty())
+			{
+			return EFalse;
+			}
+		if (opt1.OptType() == opt2.OptType())
+			{	
+			if (opt1.ValueLength() == opt2.ValueLength() &&
+				Mem::Compare(opt1.ValuePtr(), opt1.ValueLength(), opt2.ValuePtr(), opt2.ValueLength()) == 0) //Arguments are the same
+				{
+				opt1 = iter1++; //We can move on to the next option aList is incremented later
+				}
+			else
+				{
+				return EFalse; //option arguemts are different therefore comparison fails
+				}
+			//if the option types are different then we move on to the next option in aList
+			}
+		opt2 = iter2++;
+		}
+	return ETrue;	
+	}
+
+TBool RPppOptionList::EqualTo(RPppOptionList& aList)
+/**
+Check if the current option list exactly matches that of the option list specified as argument.
+
+The options in each list must have the same type in the same order and have the same value.
+If one option list has extra options at the end, then this also counts as a mismatch.
+Note that aList should really be a const, but this would require propagation of the constness
+to several related classes.
+
+@param aList option list to compare against.
+@return ETrue if option lists match, else EFalse
+*/
+	{
+	TMBufPktQIter iter1(*this);
+	TMBufPktQIter iter2(aList);
+
+	RPppOption opt1;
+	RPppOption opt2;
+	
+	opt1 = iter1++;
+	opt2 = iter2++;
+
+	while (!opt1.IsEmpty() && !opt2.IsEmpty())
+		{
+		if (opt1.OptType() != opt2.OptType() || opt1.ValueLength() != opt2.ValueLength() ||
+			Mem::Compare(opt1.ValuePtr(), opt1.ValueLength(), opt2.ValuePtr(), opt2.ValueLength()) != 0)
+			{
+			return EFalse;
+			}
+		opt1 = iter1++;
+		opt2 = iter2++;
+		}
+
+	// finally, check for the case where one option list has extra options
+	
+	return (opt1.IsEmpty() && opt2.IsEmpty());
+	}
+
+/**
+Build an MBuf chain by copying the underlying packet queue,
+leaving space on the front for a protocol header.
+*/
+TInt RPppOptionList::CopyL(RMBufChain& aPacket, TInt aHdrSize) const
+	{
+	__ASSERT_ALWAYS(aHdrSize<KMBufSmallSize, PppPanic(EPppPanic_PacketHeaderTooBig));
+
+	RMBufChain i;
+
+	// Get total length of chain needed
+	TInt pktlen, len = 0;
+	i = First();
+	while (!i.IsEmpty())
+		{
+		len += i.Length();
+		i = i.Next();
+		}
+	
+	// Allocate the chain
+	pktlen = len+aHdrSize;
+	aPacket.AllocL(pktlen);
+	
+	// Do the copy
+	TInt n, n1, n2;
+	TUint8* p1, * p2;
+	RMBuf* m1, * m2;
+
+	i = First();
+	m1 = i.First();
+	p1 = m1->Ptr();
+	n1 = m1->Length();
+
+	m2 = aPacket.First();
+	p2 = m2->Ptr();
+	n2 = m2->Length();
+
+	if (aHdrSize>0)
+		{
+		if (aHdrSize==KMBufSmallSize)
+			{
+			m2 = m2->Next();
+			p2 = m2->Ptr();
+			n2 = m2->Length();
+			}
+		else
+			{
+			p2 += aHdrSize;		
+			n2 -= aHdrSize;
+			}
+		}
+
+	while (len>0)
+		{
+		n = n1<n2 ? n1 : n2;
+		Mem::Copy(p2, p1, n);
+		if (n1 -= n, n1==0)
+			{
+			if (m1 = m1->Next(), m1==NULL)
+				{
+				i = i.Next();
+				m1 = i.First();
+				}
+			if (m1!=NULL)
+				{
+				p1 = m1->Ptr();
+				n1 = m1->Length();
+				}
+			}
+		else
+			p1 += n;
+		if (n2 -= n, n2==0)
+			{
+			if (m2 = m2->Next(), m2!=NULL)
+				{
+				p2 = m2->Ptr();
+				n2 = m2->Length();
+				}
+			}
+		else
+			p2 += n;
+		len -= n;
+		}
+
+	return pktlen;	
+	}
+
+/**
+For each option in aReplaceList, delete the original in this list
+and replace it with the given one.
+*/
+TInt RPppOptionList::ReplaceOptions(RPppOptionList& aReplaceList)
+	{
+	RPppOption opt;
+	while (aReplaceList.Remove(opt))
+		{
+		TInt err;
+		if (err = ReplaceOption(opt), err!=KErrNone)
+			return err;
+		}
+	return KErrNone;
+	}
+
+/**
+For each option in aRejectList, delete the original in this list
+*/
+TInt RPppOptionList::RemoveOptions(RPppOptionList& aRejectList)
+	{
+	RPppOption opt;
+	while (aRejectList.Remove(opt))
+		{
+		TInt err;
+		if (err = RemoveOption(opt), err!=KErrNone)
+			return err;
+		opt.Free();
+		}
+	return KErrNone;
+	}
+
+TInt RPppOptionList::ReplaceOption(RPppOption& aOption)
+//
+//
+//
+	{
+	TMBufPktQIter iter(*this);
+	RPppOption opt;
+	while (opt = iter++, !opt.IsEmpty())
+		{
+		if (opt.OptType()==aOption.OptType())
+			{
+			opt.SetValueLength(aOption.ValueLength());
+			Mem::Copy(opt.ValuePtr(), aOption.ValuePtr(), aOption.ValueLength());
+			return KErrNone;
+			}
+		}
+	return KErrNotFound;
+	}
+
+
+TInt RPppOptionList::RemoveOption(RPppOption& aOption)
+//
+//
+//
+	{
+	TMBufPktQIter iter(*this);
+	RPppOption opt;
+	while (opt = iter.Current(), !opt.IsEmpty())
+		{
+		if (opt.OptType()==aOption.OptType())
+			{
+			opt.Init();
+			iter.Remove(opt);
+			opt.Free();
+			return KErrNone;
+			}
+		iter++;
+		}
+	return KErrNotFound;
+	}
+
+
+/**
+Calculate the FCS of an Option list
+Used for detecting non-convergence
+*/
+void RPppOptionList::Crc32(TPppFcs32& aFcs, TBool aInitFcs)
+	{
+	if (aInitFcs)
+		aFcs.Init();
+	
+	RPppOption opt;
+	TMBufPktQIter iter(*this);
+	
+	while (opt = iter++, !opt.IsEmpty())
+		{
+		RMBuf* m = opt.First();
+		while (m!=NULL)
+			{
+			aFcs.Calc(m->Ptr(), m->EndPtr());
+			m = m->Next();
+			}
+		}
+	}
+
+/**
+Creates an options packet from an options list
+
+@param aPacket Return value for the constructed packet
+@param aPppId Protocol (e.g. LCP, IPCP etc).
+@param aType Type field of packet (e.g. Config-Request etc).
+@param aId Id field of packet
+@param aCreateEmptyPacket Set to ETrue if an empty packet is required.
+@see RFC1661, 5.
+@see MPppFsm::NewPacket
+*/
+void RPppOptionList::CreatePacketL(RMBufPacket& aPacket, TUint aPppId, TUint8 aType, TUint8 aId, TBool aCreateEmptyPacket)
+	{
+	RMBufPktInfo* info=NULL;
+	TInt len;
+
+	CleanupStack::PushL(aPacket);
+	if (aCreateEmptyPacket)
+		{
+		// An empty packet contains just a header (Type (1) + Id (1) + Length (2))
+		len = 4;
+		aPacket.AllocL(len);
+		}
+	else
+		{
+	len = CopyL(aPacket, 4);
+		}
+	info = aPacket.NewInfoL();
+	CleanupStack::Pop();
+
+	info->iLength = len;
+	TPppAddr::Cast(info->iDstAddr).SetProtocol(aPppId);
+	TUint8* ptr = aPacket.First()->Ptr();
+	*ptr++ = aType;
+	*ptr++ = aId;
+	BigEndian::Put16(ptr, (TUint16)len);
+	aPacket.Pack();
+	}
+
+void RPppOptionList::CreateAndAddL(TUint8 aType)
+	{
+	RPppOption opt;
+	opt.SetL(aType, NULL, 0);
+	Append(opt);
+	}
+
+void RPppOptionList::CreateAndAddL(TUint8 aType, TUint8 aValue)
+	{
+	RPppOption opt;
+	opt.SetL(aType, &aValue, sizeof(aValue));
+	Append(opt);
+	}
+
+void RPppOptionList::CreateAndAddL(TUint8 aType, TUint16 aValue)
+	{
+	RPppOption opt;
+	TUint8 swapped[2];
+	// swap the byte order if necessary
+	BigEndian::Put16(swapped, aValue);
+	opt.SetL(aType, swapped, sizeof(swapped));
+	Append(opt);
+	}
+
+void RPppOptionList::CreateAndAddL(TUint8 aType, TUint32 aValue)
+	{
+	RPppOption opt;
+	TUint8 swapped[4];
+	// swap the byte order if necessary
+	BigEndian::Put32(swapped, aValue);
+	opt.SetL(aType, swapped, sizeof(swapped));
+	Append(opt);
+	}
+
+void RPppOptionList::CreateAndAddL(TUint8 aType, const TUint8 * aBuf, TInt aLen )
+	{
+	RPppOption opt;
+	opt.SetL(aType, aBuf, aLen);
+	Append(opt);
+	}
+
+void RPppOptionList::CreateAndAddL(TUint8 aType, TPtrC8& aValue )
+	{
+	RPppOption opt;
+	opt.SetL(aType, aValue.Ptr(), aValue.Length());
+	Append(opt);
+	}
+
+
+
+MPppOptionsExtender::MPppOptionsExtender()
+	{
+	iExtOptHandlerList.SetOffset(_FOFF(MPppOptionHandler, iOptHandlerLink));
+	}
+
+void MPppOptionsExtender::ExtOptRegister(MPppOptionHandler* aHandler)
+	{
+	iExtOptHandlerList.AddLast(*aHandler);
+	}
+
+void MPppOptionsExtender::ExtOptDeregister(MPppOptionHandler* aHandler)
+	{
+	aHandler->iOptHandlerLink.Deque();
+	}
+
+void MPppOptionsExtender::ExtOptNegotiationStarted()
+	{
+	TDblQueIter<MPppOptionHandler> iter(iExtOptHandlerList);
+	MPppOptionHandler* handler;
+	
+	while (handler = iter++, handler!=NULL)
+		handler->OptNegotiationStarted();
+	}
+
+void MPppOptionsExtender::ExtOptNegotiationAborted()
+	{
+	TDblQueIter<MPppOptionHandler> iter(iExtOptHandlerList);
+	MPppOptionHandler* handler;
+	
+	while (handler = iter++, handler!=NULL)
+		handler->OptNegotiationAborted();
+	}
+
+void MPppOptionsExtender::ExtOptNegotiationComplete()
+	{
+	TDblQueIter<MPppOptionHandler> iter(iExtOptHandlerList);
+	MPppOptionHandler* handler;
+	
+	while (handler = iter++, handler!=NULL)
+		handler->OptNegotiationComplete();
+	}
+
+void MPppOptionsExtender::ExtOptFillinConfigRequestL(RPppOptionList& aRequestList)
+	{
+	TDblQueIter<MPppOptionHandler> iter(iExtOptHandlerList);
+	MPppOptionHandler* handler;
+	while (handler = iter++, handler!=NULL)
+		handler->OptFillinConfigRequestL(aRequestList);
+	}
+
+void MPppOptionsExtender::ExtOptCheckConfigRequest(RPppOption& aOption, RPppOptionList &aAckList, RPppOptionList &aNakList, RPppOptionList &aRejList)
+	{ 
+	MPppOptionHandler* handler = ExtOptLookup(aOption.OptType());
+	TPppOptResponse res = (handler==NULL) ? EPppOptReject : handler->OptCheckConfigRequest(aOption);
+	switch (res)
+		{
+	case EPppOptAck:
+		aAckList.Append(aOption);
+		break;
+	case EPppOptNak:
+		aNakList.Append(aOption);
+		break;
+	case EPppOptReject:
+		aRejList.Append(aOption);
+		break;
+		}
+	}
+
+void MPppOptionsExtender::ExtOptApplyConfigRequest(RPppOption& aOption)
+	{
+	MPppOptionHandler* handler = ExtOptLookup(aOption.OptType());
+	if (handler!=NULL)
+		handler->OptApplyConfigRequest(aOption);
+	}
+
+void MPppOptionsExtender::ExtOptRecvConfigAck(RPppOption& aOption)
+	{
+	MPppOptionHandler* handler = ExtOptLookup(aOption.OptType());
+	if (handler!=NULL)
+		handler->OptRecvConfigAck(aOption);
+	}
+
+void MPppOptionsExtender::ExtOptRecvConfigNak(RPppOption& aOption, RPppOptionList& aReqList)
+	{
+	MPppOptionHandler* handler = ExtOptLookup(aOption.OptType());
+	if (handler!=NULL)
+		handler->OptRecvConfigNak(aOption, aReqList);
+	}
+
+void MPppOptionsExtender::ExtOptRecvConfigReject(RPppOption& aOption, RPppOptionList& aReqList)
+	{
+	MPppOptionHandler* handler = ExtOptLookup(aOption.OptType());
+	if (handler!=NULL)
+		handler->OptRecvConfigReject(aOption, aReqList);
+	}
+
+MPppOptionHandler* MPppOptionsExtender::ExtOptLookup(TUint8 aOptId)
+	{
+	TDblQueIter<MPppOptionHandler> iter(iExtOptHandlerList);
+	MPppOptionHandler* handler;
+	
+	while (handler = iter++, handler!=NULL)
+		{
+		TInt i;
+		for (i=0; i<handler->iNumOptions; i++)
+			{
+			if (handler->iOptionList[i]==aOptId)
+				return handler;
+			}
+		}
+	return NULL;
+	}
+
+MPppOptionHandler::MPppOptionHandler()
+	{
+	iNumOptions = 0;
+	iOptionList = NULL;
+	}
+
+void MPppOptionHandler::OptRegister(MPppOptionsExtender* aExtender, const TUint8* aOptList, TInt aNumOpts)
+	{
+	iNumOptions = aNumOpts;
+	iOptionList = aOptList;
+	aExtender->ExtOptRegister(this);
+	}
+
+// Default Implementations of option handler functions
+
+void MPppOptionHandler::OptNegotiationStarted()
+	{
+	return;
+	}
+
+void MPppOptionHandler::OptNegotiationAborted()
+	{
+	return;
+	}
+
+void MPppOptionHandler::OptNegotiationComplete()
+	{
+	return;
+	}
+
+void MPppOptionHandler::OptFillinConfigRequestL(RPppOptionList& /*aRequestList*/)
+	{
+	return;
+	}
+
+TPppOptResponse MPppOptionHandler::OptCheckConfigRequest(RPppOption& /*aOption*/)
+	{
+	return EPppOptReject;
+	}
+
+void MPppOptionHandler::OptApplyConfigRequest(RPppOption& /*aOption*/)
+	{
+	return;
+	}
+
+void MPppOptionHandler::OptRecvConfigAck(RPppOption& /*aOption*/)
+	{
+	return;
+	}
+
+void MPppOptionHandler::OptRecvConfigNak(RPppOption& aOption, RPppOptionList& aReqList)
+	{
+	aReqList.ReplaceOption(aOption);
+	}
+
+void MPppOptionHandler::OptRecvConfigReject(RPppOption& aOption, RPppOptionList& aReqList)
+	{
+	aReqList.RemoveOption(aOption);
+	}