// Copyright (c) 2006-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:
// in_trans.cpp - transport layer protocol base
//

#include "in_trans.h"
#include "inet6log.h"

#include <e32math.h>
#include <comms-infras/nifif_internal.h>

// speed optimisations
#ifdef __ARMCC__
#pragma push
#pragma arm
#endif

#define SYMBIAN_NETWORKING_UPS

CProtocolInet6Transport::CProtocolInet6Transport()
	{
	// Initialize random seed value
	TTime  currentTime;
	currentTime.HomeTime();
	iSeed = currentTime.Int64() + User::TickCount();
	}


void CProtocolInet6Transport::InitL(TDesC& aTag)
	{
	CProtocolInet6Base::InitL(aTag);
	}


void CProtocolInet6Transport::StartL()
	{
	CProtocolInet6Base::StartL();
	}


CProtocolInet6Transport::~CProtocolInet6Transport()
	{
	}


TInt CProtocolInet6Transport::Send(RMBufChain& /*aPacket*/,CProtocolBase* /*aSourceProtocol=NULL*/)
	{
	Panic(EInet6Panic_NotSupported);
	return 0;
	}


//
// Bind request from a protocol above us
//
void CProtocolInet6Transport::BindL(CProtocolBase* /*aProtocol*/, TUint /*aId*/)
	{
	Panic(EInet6Panic_NotSupported);
	}


//
// Find SAP based on end-point addresses.
//
// This routine is rather involved, because it is used for a number of different
// purposes. The routine returns a "best match" socket provider based on source
// and destination socket addresses (IP address & port), or NULL if no mathing
// provider is found (see below for an explanation on how matches are ranked).
//
// The method supports the following forms:
//
//  o LocateProvider(minimum rank, local socket address);  or
//    LocateProvider(minimum rank, local socket address, unspecified socket address);
//
//    This form is used when binding a socket in order to check that the port is
//    not already in use. At least local port must be specified. Local address
//    may be unspecified.  The first provider that matches the local port (and
//    local address, if given) is returned.
//
//  o LocateProvider(minimum rank, local socket address, remote socket address);
//
//    This form is used in order to locate the correct socket provider, when
//    a transport PDU has been received. For this use, the complete local
//    and remote addresses must be specified.
//
//    Another use of this form is to verify the uniqueness of the source/destination
//    socket address pair when creating a new connection. This is only an issue if
//    the source port has been explicitly bound and local address reuse has been enabled.
//    In this case, the destination socket address MUST be unique among all connections
//    terminating in the same local socket address.
//
//    Note: for the latter use, local IP address may be left unspecified. In this case,
//    it matches all addresses. Local port is mandatory.
//
//  The minimum rank parameter specifies the minimum acceptable accuracy of the match.
//  The the match with the highest rank found is returned, or NULL if no match of
//  at least the requested rank is found.
//
//  Matches are ranked as follows (highest rank first):
//
//   EMatchExact or EMatchConnection:
//
//        Both local and remote socket addresses match. Local IP address may be
//        unspecified, if the connection is waiting for a flow to become ready.
//
//        This rank is only relevant if caller specified a remote address.
//        The returned socket provider handles a connected socket.
//
//   EMatchServerSpecAddr:
//
//        Local socket address matches and remote socket address is unspecified.
//
//        This means that the returned socket provider handles a server socket
//        that has been bound to a specific local address.
//
//   EMatchServerUnspecAddr:
//
//        Local port matches. Local IP address is unspecified. Remote socket address
//        is unspecified.
//
//        This means that the returned socket provider handles a server socket
//        that has been bound to an unspecified address.
//
//   EMatchLocalPort:
//
//        Local port must match. Local IP address or remote socket address are not tested.
//
//        This means that the returned socket provider will conflict with an attempted
//        bind() if the KSoReuseAddr socket option is not used.
//
#ifndef SYMBIAN_STRICT_EXPLICIT_SOCKET_BINDING
CProviderInet6Transport* CProtocolInet6Transport::LocateSap(TProviderMatchEnum aRank, TUint aFamily, 
const TInetAddr& aLocalAddr, const TInetAddr& aRemoteAddr, CProviderInet6Base *aSap)
#else
CProviderInet6Transport* CProtocolInet6Transport::LocateSap(TProviderMatchEnum aRank, TUint aFamily,
		const TInetAddr& aLocalAddr, const TInetAddr& aRemoteAddr, CProviderInet6Base *aSap,
		TUint32 aSourceIfIndex)
#endif //SYMBIAN_STRICT_EXPLICIT_SOCKET_BINDING
	{
	ASSERT(aLocalAddr.Port() != KInetPortNone);

	CProviderInet6Transport *sapFound = NULL, *sap;
	TUint port = aLocalAddr.Port();
	TProviderMatchEnum rank = (TProviderMatchEnum)(aRank-1);

	// CProtocolInet6Base::LocateProvider() puts us into the right hashed queue.
	for (sap = (CProviderInet6Transport *)(aSap != NULL ? aSap : CProtocolInet6Base::LocateProvider(port));
			sap != NULL; sap = (CProviderInet6Transport *)sap->iNextSAP)
		{
		// If aSap non-NULL, return first match
		if (aSap != NULL && rank >= aRank)
			break;

		const TInetAddr& local = sap->iFlow.FlowContext()->LocalAddr();
		if (port != local.Port())
			continue;
#ifdef SYMBIAN_STRICT_EXPLICIT_SOCKET_BINDING
        // In case of loopback no need to lock interface
		if(!aLocalAddr.IsLoopback())
		    {	
		    // Port matches this SAP, so if this SAP has an interface that it is
		    // locked to, confirm the inbound packet is on that interface. That is,
		    // if the SAP is locked to an interface, this code ensures that the
		    // packet is only accepted if it is coming over the same interface
		    // that the SAP is locked to.
		    TScopeType scoping = sap->iFlow.FlowContext()->LockType();
		    TUint32 interfaceNum = sap->iFlow.FlowContext()->LockId();
		
		    // Check that the source interface index was supplied. Also check for lock id > 0.
		    // If lock id > 0 this implies that the interface is locked down for this SAP. Also
		    // ignore the magic value lock id of KNetworkIdFromAddress.
		    if ((aSourceIfIndex > 0) && (interfaceNum > 0) && (interfaceNum != KNetworkIdFromAddress))
			    {
			    // The sap currently being checked is locked to an interface. Check to
			    // see what type of lock (scoping) it is.
			    if (scoping == EScopeType_IF)
			   	    {
			   	    // This sap is locked to EScopeType_IF
	                if (interfaceNum != aSourceIfIndex)
	            	    {
	            	    // Not the right interface, go to next SAP
	                    continue;
	                    }
            	    }
			    else 
				    {
				    // Not locked to EScopeType_IF, so now need the network ID and IAP number from the
				    // interface, to check against one of those. Fetch this from the source interface
				    // based on the interface index.
				    const MInterface* iface = sap->iFlow.FlowContext()->Interfacer()->Interface(aSourceIfIndex);
				    if (iface)
					    {
					    if (scoping == EScopeType_IAP)
						    {
						    // This sap is locked to EScopeType_IAP. Check if the source interface
						    // is over the same IAP.
						    TUint id = iface->Scope(EScopeType_IAP);
						    if (id != interfaceNum)
							    {
							    // IAP does not match that required
							    continue;  
							    } 							
						    }
					    else if (scoping == EScopeType_NET )
						    { 
						    // This sap is locked to EScopeType_NET. Check if the source interface
						    // is over the same network ID.
						    TUint id = iface->Scope(EScopeType_NET);
						    if (id != interfaceNum) 
							    {
							    // Network ID does not match that required
							    continue;    
							    }
						    }
					    }
				    }
			    }
			}
#endif //SYMBIAN_STRICT_EXPLICIT_SOCKET_BINDING
		
		const CFlowContext *const flow = sap->iFlow.FlowContext();

		TBool localMatch = aLocalAddr.CmpAddr(local) &&
			(aLocalAddr.Scope() == 0 || aLocalAddr.Scope() == local.Scope());
		if (!sap->iSockFlags.iAddressSet || localMatch)
			{
			if (sap->iSockFlags.iConnected)
				{
				if (localMatch && aRemoteAddr.CmpAddr(flow->RemoteAddr())
					&& (aRemoteAddr.Scope() == 0 || aRemoteAddr.Scope() == flow->RemoteAddr().Scope()))
					{
					rank = EMatchExact;
					sapFound = sap;
					break;
					}
				}
			else
				{
				if (rank >= EMatchServerSpecAddr)
					continue;

				if (localMatch)
					{
					rank = EMatchServerSpecAddr;
					sapFound = sap;
					continue;
					}

				if (rank >= EMatchServerUnspecAddr)
					continue;

				if (sap->iSockFamily == aFamily || sap->iSockFamily == KAFUnspec || aFamily == KAFUnspec)
					{
					rank = EMatchServerUnspecAddr;
					sapFound = sap;
					continue;
					}
				}
			}

		if (rank >= EMatchLocalPort)
			continue;

		rank = EMatchLocalPort;
		sapFound = sap;
		}

#ifdef _LOG
	TBuf<50> src, dst;
	//TBuf<50> src2, dst2;
	aLocalAddr.OutputWithScope(src);
	aRemoteAddr.OutputWithScope(dst);
	Log::Printf(_L("\t%S LocateProvider({%S,%d} <--> {%S,%d})"),
		&ProtocolName(), &src, port, &dst, aRemoteAddr.Port());
	if (sapFound)
		{
		TBuf<50> src2, dst2;
		sapFound->iFlow.FlowContext()->LocalAddr().OutputWithScope(src2);
		sapFound->iFlow.FlowContext()->RemoteAddr().OutputWithScope(dst2);
		Log::Printf(_L("\t%S SAP[%u] found {%S,%d} <--> {%S,%d}, rank=%d, family=%x"),
			&ProtocolName(), (TInt)sapFound,
			&src2, port, &dst2, sapFound->iFlow.FlowContext()->RemotePort(),
			rank, aFamily);
		}
	else
		{
		Log::Printf(_L("\t%S SAP not found, best rank=%d, family=%x"), &ProtocolName(), rank, aFamily);
		}
#endif

	return (CProviderInet6Transport *)sapFound;
	}

#ifdef _LOG
#include "tcp.h"
void CProtocolInet6Transport::LogProviders(TUint aPort)
	{
	CProviderInet6Base *sap;
	TBool title = EFalse;

	for (sap = CProtocolInet6Base::LocateProvider(aPort); sap != NULL; sap = sap->iNextSAP)
		{
		if (aPort != sap->iFlow.FlowContext()->LocalPort())
			continue;

		if (!title)
			{
			title = ETrue;
			Log::Printf(_L("Active TCBs on %S port %d:"), &ProtocolName(), aPort);
			}

		TBuf<50> src, dst;
		sap->iFlow.FlowContext()->LocalAddr().OutputWithScope(src);
		sap->iFlow.FlowContext()->RemoteAddr().OutputWithScope(dst);
		Log::Printf(_L("\t%S SAP[%u] {%S,%d} <--> {%S,%d} [reuse=%d]   %s"),
			&ProtocolName(), (TInt)sap,
			&src, sap->iFlow.FlowContext()->LocalPort(),
			&dst, sap->iFlow.FlowContext()->RemotePort(),
			((CProviderInet6Transport*)sap)->iSockFlags.iReuse,
			(sap->iFlow.FlowContext()->Protocol() == KProtocolInetTcp) ?
			((CProviderTCP6*)sap)->TcpState() : _S("UDP"));
		}
	}
#endif


//
// Pick a free port by random from the range [32768,61000].
// Random ephemeral port selection will help counter certain
// types of attacks.
//
TUint CProtocolInet6Transport::AssignAutoPort()
	{
	TUint i, port = KInetMinAutoPort + Random(KInetMaxAutoPort - KInetMinAutoPort + 1);

	for (i = KInetMinAutoPort; i <= KInetMaxAutoPort; i++)
		{
		if (CProtocolInet6Base::LocateProvider(port) == NULL)
			return port;
		if (++port > KInetMaxAutoPort)
			port = KInetMinAutoPort;
		}
	return KInetPortNone;
	}


TInt CProtocolInet6Transport::GetIniValue(	const TDesC &aSection,
											const TDesC &aName,
											TInt aDefault,
											TInt aMin,
											TInt aMax,
											TBool aBoundMode) const
/**
Reads integer value for ini parameters from tcpip.ini file.

@param aSection		Section under which ini parameter belongs ([xxx] tag).
@param aName		Name of the ini parameter.
@param aDefault		Default value for the parameter, if not given in ini file.
@param aMin			Minimum limit for the parameter value.
@param aMax			Maximum limit for the parameter value.
@param aBoundMode	If ETrue, values that exceed min or max are truncated back to the min/max
					boundary. If EFalse, exceeding values are considered invalid, and default
					is used for those parameters.

@return	Integer value for the parameter.
*/											
	{
	LOG(_LIT(KFormat, "\t[%S] %S = %d"));
	LOG(_LIT(KFormatInv, "\t[%S] %S = %d is invalid"));

	TInt value;
	if (!Interfacer()->FindVar(aSection, aName, value))
		{
		value = aDefault;
		}
	else
		{		
		if (value < aMin)
			{
			LOG(Log::Printf(KFormatInv, &aSection, &aName, value));
			value = aBoundMode ? aMin : aDefault;
			}
		else if (value > aMax)
			{
			LOG(Log::Printf(KFormatInv, &aSection, &aName, value));
			value = aBoundMode ? aMax : aDefault;
			}
		}
	LOG(Log::Printf(KFormat, &aSection, &aName, value));
	return value;
	}


//
// Transport Layer Socket Providers
//

CProviderInet6Transport::CProviderInet6Transport(CProtocolInet6Base *aProtocol)
	: CProviderInet6Base(aProtocol)
	{
	iSockFlags.iRecvClose       = EFalse;
	iSockFlags.iSendClose       = EFalse;
	iSockFlags.iConnected       = EFalse;
	iSockFlags.iFlowStopped     = EFalse;
	iSockFlags.iNotify          = ETrue;
	iSockFlags.iReuse           = EFalse;
	iSockFlags.iAttached        = ETrue;
//	iSockFlags.iRawMode         = EFalse;
//	iSockFlags.iHeaderIncluded  = EFalse;
	iSockFamily                 = KAFUnspec;
	iAppFamily                  = KAFUnspec;
	}

CProviderInet6Transport::~CProviderInet6Transport()
	{
	}


void CProviderInet6Transport::LocalName(TSockAddr &aAddr) const
	{
	aAddr = iFlow.FlowContext()->LocalAddr();
	if (iAppFamily == KAfInet)
		TInetAddr::Cast(aAddr).ConvertToV4();
	}


TInt CProviderInet6Transport::SetLocalName(TSockAddr &aAddr)
	{
#ifdef _LOG
	TBuf<50> a; TInetAddr::Cast(aAddr).OutputWithScope(a);
	LOG(Log::Printf(_L("\t%S SAP[%u] SetLocalName({%S,%d}) [reuse=%d]"),
		&ProtocolName(), (TInt)this,
		&a, aAddr.Port(), iSockFlags.iReuse));
#endif

	LOG(((CProtocolInet6Transport*)iProtocol)->LogProviders(aAddr.Port()));

	TInetAddr addr = aAddr;

	// Bound already?
	if (iFlow.FlowContext()->LocalPort() != KInetPortNone)
		return KErrAlreadyExists;

	// Convert an IPv4 address to a V4 Mapped IPv6 address
	if (addr.Family() == KAfInet)
		addr.ConvertToV4Mapped();

	// Check source address
	if (addr.Family() != KAFUnspec)
		{
		iSockFamily = addr.IsV4Mapped() ? KAfInet : KAfInet6;
		if (!addr.IsUnspecified())
			{
			if (addr.Scope() == 0)
				{
				// Check address and pick a default scope id
				addr.SetScope(iProtocol->Interfacer()->LocalScope(addr.Ip6Address(),
					iFlow.FlowContext()->LockId(), iFlow.FlowContext()->LockType()));
				if (addr.Scope() == 0)
					return KErrNotFound;
				}
			else
				{
				// Check scoped address
				if (addr.Scope() != iProtocol->Interfacer()->LocalScope(addr.Ip6Address(),
					addr.Scope(), (TScopeType)(addr.Ip6Address().Scope() - 1)))
					return KErrNotFound;
				}
			}
		}

	// Autobind requested?
	if (addr.Port() == KInetPortNone)
		{
		addr.SetPort(((CProtocolInet6Transport*)iProtocol)->AssignAutoPort());
		if (addr.Port() == KInetPortNone)
			return KErrInUse;
		}
	else
		{
		if (addr.Port() > 65535)
			return KErrTooBig;

		if (!iSockFlags.iReuse && Protocol()->LocateSap(EMatchLocalPort, aAddr.Family(), addr))
			return KErrInUse;
		}

	iAppFamily = aAddr.Family();  // aAddr here to get the original family
	CProviderInet6Base::SetLocalName(addr);
	iSockFlags.iAddressSet = iFlow.FlowContext()->IsLocalSet();
	iProtocol->BindProvider(this);
	return KErrNone;
	}

void CProviderInet6Transport::RemName(TSockAddr &aAddr) const
	{
	aAddr = iFlow.FlowContext()->RemoteAddr();
	if (iAppFamily == KAfInet)
		TInetAddr::Cast(aAddr).ConvertToV4();
	}


TInt CProviderInet6Transport::SetRemName(TSockAddr &aAddr)
	{
	//__ASSERT_DEBUG(!iSockFlags.iConnected && iFlow.FlowContext()->RemoteAddr().IsUnspecified() && iFlow.FlowContext()->RemotePort() == KInetPortNone,
	//		 Panic(EInet6Panic_NotSupported));
	TInetAddr addr = aAddr; 
	
#ifdef _LOG
	TBuf<50> a; addr.OutputWithScope(a);
	LOG(Log::Printf(_L("\t%S SAP[%u] SetRemName({%S,%d})"), &ProtocolName(), (TInt)this, &a, addr.Port());)
#endif
	TUint port = addr.Port(); 
	if (port < 1)
		return KErrGeneral;
	else if (port > 65535)
		return KErrTooBig;

	if (addr.IsUnspecified())
		return KErrBadName;
	
	if (addr.Family() == KAfInet)
		addr.ConvertToV4Mapped();
	
	TInt family = addr.IsV4Mapped() ? KAfInet : KAfInet6;

	if(iSockFamily == KAFUnspec)
		iSockFamily = family;
	else if (iSockFamily != family)
		return KErrBadName;

	iFlow.SetRemoteAddr(addr);
	iAppFamily = aAddr.Family();

	iSockFlags.iConnected = ETrue;
	return KErrNone;
	}


void CProviderInet6Transport::AutoBind()
	{
	//__ASSERT_DEBUG(iFlow.FlowContext()->LocalPort()==KInetPortNone, Panic(EInet6Panic_BadBind));
	TInetAddr localAddr;

	// Socket is already bound?
	if (iFlow.FlowContext()->LocalPort()!=KInetPortNone)
		return;

	localAddr.SetPort(((CProtocolInet6Transport *)iProtocol)->AssignAutoPort());
	if (localAddr.Port()==KInetPortNone)
		{
		Error(KErrInUse);
		return;
		}

	iFlow.SetLocalAddr(localAddr);

#ifdef _LOG
	TBuf<50> addr; iFlow.FlowContext()->LocalAddr().OutputWithScope(addr);
	Log::Printf(_L("\t%S SAP[%u] AutoBind() --> {%S,%d}"), &ProtocolName(), (TInt)this, &addr, iFlow.FlowContext()->LocalPort());
#endif

	iProtocol->BindProvider(this);
	}

TInt CProviderInet6Transport::SetOption(TUint aLevel, TUint aName, const TDesC8& aOption)
	{
	TInt ret = KErrNotSupported;

	if (aLevel == KSolInetIp && aName == KSoReuseAddr)
		{
		TInt intValue;
		ret = GetOptionInt(aOption, intValue);
		if (ret == KErrNone)
			iSockFlags.iReuse = intValue ? TRUE : FALSE;
		#ifdef _LOG 
      	  Log::Printf(_L("SetOpt\t%S SAP[%u] KSoReuseAddr = %d err=%d"), 
      		           &ProtocolName(), (TInt)this, (TInt)iSockFlags.iReuse, ret); 
        #endif 
		}

	if (ret == KErrNotSupported)
		ret = CProviderInet6Base::SetOption(aLevel, aName, aOption);
	
#ifdef SYMBIAN_NETWORKING_UPS	
	if (ret == KErrNone && aLevel == KSOLProvider && aName == (TUint) KSoConnectionInfo)
		{
		iConnectionInfoReceived = 1;
		}
#endif	
	
	return ret;
	}


TInt CProviderInet6Transport::GetOption(TUint aLevel, TUint aName, TDes8& aOption) const
	{
	TInt ret = KErrNotSupported;

	if (aLevel == KSolInetIp && aName == KSoReuseAddr)
		ret = SetOptionInt(aOption, iSockFlags.iReuse);

	if (ret == KErrNotSupported)
		ret = CProviderInet6Base::GetOption(aLevel, aName, aOption);

	return ret;
	}

void CProviderInet6Transport::CanSend()
	{
	LOG(Log::Printf(_L("\t%S SAP[%u] CanSend()"), &ProtocolName(), (TInt)this));
	// Do not enable data flow if the protocol doesn't want it.
	if(!iSockFlags.iFlowStopped && iSockFlags.iNotify)
		CProviderInet6Base::CanSend();
	}

void CProviderInet6Transport::Error(TInt aError, TUint aOperationMask)
	{
	if (iSockFlags.iNotify)
		CProviderInet6Base::Error(aError, aOperationMask);
	if (FatalState())
		iSockFlags.iNotify = EFalse;
	}

void CProviderInet6Transport::IcmpError(TInt aError, TUint aOperationMask, TInt aType, TInt aCode,
		const TInetAddr& aSrcAddr, const TInetAddr& aDstAddr, const TInetAddr& aErrAddr)
	{
	CProviderInet6Base::SaveIcmpError(aType, aCode, aSrcAddr, aDstAddr, aErrAddr);
	if (iAppFamily == KAfInet)
		{
		iLastError.iSrcAddr.ConvertToV4();
		iLastError.iDstAddr.ConvertToV4();
		iLastError.iErrAddr.ConvertToV4();
		}

	if (iSockFlags.iReportIcmp && aError != KErrNone)
		Error(aError, aOperationMask);
	}

#ifdef SYMBIAN_NETWORKING_UPS
TBool CProviderInet6Transport::ConnectionInfoSet()
	{
	return static_cast<TBool>(iConnectionInfoReceived);
	}


TBool CProviderInet6Transport::HasSocket()	
	{
	if (iSocket == NULL)
		{
	   	return EFalse;
	    }
	else
		{
		return ETrue;	
		}	
	}

void *CProviderInet6Transport::GetApiL(const TDesC8& aApiName, TUint* aVersion)
	{
	if (aApiName == _L8("MProviderBindings"))
		{
	    return EXPORT_API_L(MProviderBindings, static_cast<MProviderBindings*>(this), aVersion);
		}

	return NULL;	
	}
#endif

void RMBufSockQ::AppendL(const TDesC8& aData, TInt aLen)
	{
	if (aLen <= 0)
		return;

	RMBufChain seg;
	seg.AllocL(aLen);
	seg.CopyIn(aData);
	RMBufChain::Append(seg);
	}

TInt RMBufSockQ::AppendDes(const TDesC8& aData, TInt aLen)
	{
	if (aLen <= 0)
		return KErrNone;

	RMBufChain seg;
	TInt err = seg.Alloc(aLen);
	if(err == KErrNone)
		{
		seg.CopyIn(aData);
		RMBufChain::Append(seg);
		}
	return err;
	}


//
// Append at least aLength bytes from aChain.
// Leave the remainder in aChain. Return number
// of bytes removed from aChain.
//
TInt RMBufSockQ::AppendAtLeast(RMBufChain& aChain, TInt aLength)
	{
	TInt o, n;
	RMBuf* m, *p;

	if (!aChain.Goto(aLength, m, o, n, p))
		{
		aLength = aChain.Length();
		RMBufChain::Append(aChain);
		return aLength;
		}

	if (o != m->Offset())
		{
		p = m;
		m = m->Next();
		aLength += n;
		}

	p->Unlink();
	RMBufChain::Append(aChain);
	aChain = m;

	return aLength;
	}

//
// Remove at most aLength bytes from the queue.
// Place the result in aChain. Return number
// of bytes removed from the queue.
//
TInt RMBufSockQ::RemoveAtMost(RMBufChain& aChain, TInt aLength)
	{
	TInt o, n;
	RMBuf* m, *p;

	if (aLength <= 0 || iNext == NULL)
		return 0;

	if (!Goto(aLength, m, o, n, p))
		{
		aChain = iNext;
		iNext = NULL;
		return aChain.Length();
		}

	p->Unlink();
	aChain = iNext;
	iNext = m;

	return aLength + m->Offset() - o;
	}

//
// Copy aLength bytes from aQueue at aOffset
//
void TDualBufPtr::CopyInL(const RMBufChain& aQueue, TInt aOffset, TInt aLength)
	{
	switch(iType)
		{
	case ERMBufChain:
		if (aLength > 0)
			aQueue.CopyL(*iChain, aOffset, aLength);
		break;

	case EDes8:
		iDesc->SetLength(aLength);
		if (aLength > 0)
			aQueue.CopyOut(*iDesc, aOffset);
		break;

	default:
		Panic(EInet6Panic_NotSupported);
		}
	}

//
// Copy aLength bytes from aQueue at aOffset
//
TInt TDualBufPtr::CopyIn(const RMBufChain& aQueue, TInt aOffset, TInt aLength)
	{
	switch(iType)
		{
	case ERMBufChain:
		if (aLength > 0)
			return aQueue.Copy(*iChain, aOffset, aLength);
		break;

	case EDes8:
		iDesc->SetLength(aLength);
		if (aLength > 0)
			aQueue.CopyOut(*iDesc, aOffset);
		break;

	default:
		Panic(EInet6Panic_NotSupported);
		}
	return KErrNone;
	}

//
// Copy into aChain at aOffset
//
void TDualBufPtr::CopyOut(RMBufChain& aChain, TInt aOffset) const
	{
	switch(iType)
		{
	case EDes8:
	case EDesC8:
		aChain.CopyIn(*iDesc, aOffset);
		break;

	default:
		Panic(EInet6Panic_NotSupported);
		}
	}

//
// Remove aLength bytes from the beginning of aQueue.
//
TInt TDualBufPtr::Consume(RMBufChain& aQueue, TInt aLength, RMBufAllocator& aAllocator)
	{
	RMBufChain tail;
	TInt err;

	switch(iType)
		{
	case ERMBufChain:
		if (aLength > 0)
			{
			err = aQueue.Split(aLength, tail, aAllocator);
			if (err == KErrNone)
				{
				iChain->Assign(aQueue);
				aQueue.Assign(tail);
				}
			else
				{
				aLength = RMBufSockQ::Cast(aQueue).RemoveAtMost(*iChain, aLength);
				LOG(Log::Printf(_L("TDualBufPtr::Consume(): Alloc problem. Returning less")));
				}
			}
		break;

	case EDes8:
		iDesc->SetLength(aLength);
		if (aLength > 0)
			{
			aQueue.CopyOut(*iDesc);
			aQueue.TrimStart(aLength);
			}
		break;

	default:
		Panic(EInet6Panic_NoData);
		}
	return aLength;
	}

//
// Append at least aLength bytes to aQueue
//
TInt TDualBufPtr::AppendL(RMBufChain& aQueue, TInt aLength)
	{
	switch(iType)
		{
	case ERMBufChain:
		aLength = RMBufSockQ::Cast(aQueue).AppendAtLeast(*iChain, aLength);
		break;

	case EDes8:
	case EDesC8:
		RMBufSockQ::Cast(aQueue).AppendL(*iDesc, aLength);
		break;

	default:
		Panic(EInet6Panic_NoData);
		}

	return aLength;
	}

//
// Append at least aLength bytes to aQueue. Panics if used with des-tyoe
//
TInt TDualBufPtr::Append(RMBufChain& aQueue, TInt aLength)
	{
	switch(iType)
		{
	case ERMBufChain:
		aLength = RMBufSockQ::Cast(aQueue).AppendAtLeast(*iChain, aLength);
		break;

	default:
		Panic(EInet6Panic_NoData);
		}

	return aLength;
	}

//
// Free buffer
//
void TDualBufPtr::Free()
	{
	switch(iType)
		{
	case ERMBufChain:
		iChain->Free();
		break;

	case EDes8:
		iDesc->SetLength(0);
		break;

	default:
		Panic(EInet6Panic_NoData);
		}
	}

#ifdef __ARMCC__
#pragma pop
#endif
