networkprotocols/iphook/inhook6/src/flow.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 13 Oct 2010 16:17:27 +0300
branchRCL_3
changeset 75 c1029e558ef5
parent 0 af10295192d8
permissions -rw-r--r--
Revision: 201041 Kit: 201041

// 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:
// flow.cpp - IPv6/IPv4 flow information
//

#include <es_mbuf.h>
#include "flow.h"
//
//	COptionValue
//	************
//	(Only used internally in flow.cpp, DO NOT EXPORT! It has somewhat
//	"delicate" constructor requiring the extra allocation been done,
//	not checked... -- sma
//
class COptionValue : public CBase
	{
public:
	COptionValue(TInt aLevel, TInt aName, const TDesC8 &aOption) : iLevel(aLevel), iName(aName), iLength(aOption.Length())
		{
		TPtr8((TUint8 *)this + sizeof(COptionValue), iLength).Copy(aOption);
		}
	inline TPtrC8 Value() { return TPtr8((TUint8 *)this + sizeof(COptionValue), iLength, iLength); }
	COptionValue *iNext;		// Next value or NULL
	const TUint iLevel;			// Option level
	const TUint iName;			// Option name
	const TInt iLength;			// Option value length
	//
	// This is followed by the actual octets of the
	// option value. COptionValue class *CANNOT* be
	// subclassed!!!!
	};

// ************
// RFlowContext
// ************


EXPORT_C TInt RFlowContext::Open(MFlowManager *aManager, TUint aProtocol)
	/**
	* Creates a new empty and unconnected flow context and initialises the handle 
	* to point this context.
	*
	* The handle, which is used to create the flow context
	* through this method, will become the owner of the flow context.
	*
	* @param aManager
	*		Flow manager
	* @param aProtocol
	*		The upper layer protocol associated with the flow.
	*		This is also the value for the IP NextHeader/Protocol field, unless
	*		a hook adds some extension headers. This can also be set later using
	*		RFlowContext::SetProtocol method.
	* @return
	*		KErrNone, if the context was successfully created and attached to the handle.
	*/
	{
	iFlow = 0;
	TRAPD(err, iFlow = aManager->NewFlowL(this, aProtocol));
	return err;
	}

EXPORT_C TInt RFlowContext::ReOpen()
	/**
	* Allocates a new flow context to the handle using the parameters from
	* the existing flow context.
	*
	* If the handle is the only reference to the current context, the context is 
	* deleted after the new context has been created.
	*
	* This function is not for general use.
	*
	* @return
	*		KErrNone on success.
	*/
	{
	CFlowContext *old = iFlow;
	if (old)
		{
		iFlow = NULL;
		TRAPD(err, iFlow = old->iMgr->NewFlowL(this, *old));
		old->Close();
		return err;
		}
	else
		return KErrBadHandle;
	}

EXPORT_C TInt RFlowContext::Clone(const RFlowContext &aFlow)
	/**
	* Creates a new flow with the flow parameters copied from another flow.
	*
	* @param aFlow
	*		a flow to be duplicated
	*
	* @return
	*		System wide error codes
	*/
	{
	//
	// This sequence of actions below tries to do the cloning in
	// such a way, that it is safe even if the this == aFlow (in which
	// case this is same as plain "ReOpen()").
	//
	CFlowContext *old = iFlow;
	iFlow = aFlow.iFlow;
	iFlow->Open();
	TInt ret = ReOpen();
	if (old)
		old->Close();
	return ret;
	}


EXPORT_C TInt RFlowContext::Connect()
	/**
	* Forces a (re)connect on the flow.
	*
	* Connect activates the connection phase of the flow context.
	* Any previous connection is disconnected from the flow.
	*
	* @return
	*		the status of the flow. Any non-zero positive
	*		value indicates "pending" state: a connection
	*		is being setup but not yet ready (for example,
	*		a netdial process may have been activated, but
	*		it is still waiting for the user input).
	*/
	{
	if (iFlow)
		{
		iFlow->Connect();
		return iFlow->iStatus;
		}
	else
		return KErrBadHandle;
	}

EXPORT_C TInt RFlowContext::Open(MFlowManager *aManager, const TSockAddr &aDst,	const TSockAddr &aSrc, TUint aProtocol, TUint aIcmpType, TUint aIcmpCode)
	/**
	* Just a convenience function, which combines Open, SetRemoteAddr, SetLocalAddr, SetIcmpInfo and Connect.
	*
	* @param aManager	Flow Manager
	* @param aDst		Flow's destination address (and port)
	* @param aSrc		Flow's source address (and port)
	* @param aProtocol	The upper layer protocol associated with the flow
	*					(and the value for the IP  NextHeader/Protocol field,
	*					unless some hook adds extension headers)
	* @param aIcmpType	ICMP type (only sensible when protocol is ICMP)
	* @param aIcmpCode	ICMP code (only sensible when protocol is ICMP)
	*
	* @return
	*		current value of CFlowContext::iStatus.
	*/
	{
	TInt ret = Open(aManager, aProtocol);
	if (ret != KErrNone)
		return ret;
	else if (iFlow)
		{
		SetRemoteAddr(aDst);
		SetLocalAddr(aSrc);
		SetIcmpType(aIcmpType, aIcmpCode);
		iFlow->Connect();
		return iFlow->iStatus;
		}
	else
		return KErrBadHandle;
	}

EXPORT_C TInt RFlowContext::Open(RFlowContext &aFlow, RMBufPktInfo *aInfo)
	/**
	* Attaches the flow to a specified packet.
	*
	* This function is used only to attach a copy reference to the flow to an outgoing 
	* packet (RMBufSendInfo::iFlow).

	* This method is very specific and currently used only for attaching a copy reference
	* of the flow to an outgoing packet (iFlow member of the RMBufPacketInfo).
	*
	* The function checks aContext and if there have been no changes to the flow
	* parameters (as recorded by CFlowContext::iChanged) since the last connection,
	* the flow is assumed to be validly connected. Note that this means that if
	* the referenced flow is in an error state and if no flow parameters have been 
	* changed, this attach/open also fails. No reconnect attempt is made in that 
	* case.
	*
	* If the flow parameters have been changed since the last connect, the flow 
	* is reconnected. If there are additional references to the flow context, an 
	* implicit re-open (RFlowContext::ReOpen) is executed for the handle and the
	* flow parameters are copied  from the old flow context.
	* After this, the new flow is connected. Note that 
	* if a new cloned context is created, and if the context had a provider notify 
	* pointer (see RFlowContext::SetNotify()) set through this handle, the pointer
	* is automatically transferred to the new flow context, and removed from the old context. 
	*
	* If, after above processing, the referenced flow is ready to receive outgoing packets,
	* the flow context associated with the referenced handle is attached to the current handle.
	*
	* This design allows simple processing for the upper layers, as they don't need to worry
	* if the old flow is used by some packet still on the way out. Additionally, delaying the
	* "cloning" until actual attach/connect, may avoid unnecessary copying.
	*
	* The opened handle is not the owner of the flow context. This will only create an additional
	* reference to an existing flow context owned by someone else (usually aFlow).
	*
	* @param aFlow
	*		the "source flow" to be attached to the current handle
	* @retval aInfo
	*		An optional information block.
	*		If specified, then on a successful attach, the info members iProtocol,
	*		iSrcAddr and iDstAddr are initialized from the respective selector
	*		fields of the flow.
	* @return
	*		The return value of the Open is in general same as what would be returned
	*		by the Status call. However, when attach causes the actual connection of the flow,
	*		there exists error return codes which do not happen with Status call.
	*/
	{
	// FFS: It should be considered that this method is replaced with something like
	// Attach(aFlow) function in the RMBufSendInfo itself. That would be much cleaner
	// solution.
	iFlow = NULL;
	CFlowContext *flow = aFlow.iFlow;
	if (!flow)
		return KErrBadHandle;

	if (flow->iChanged)
		{
		//
		// The flow parameters have been changed
		//
		if (flow->iRefs > 0)
			{
			//
			// Need to allocate a new flow

			TInt err = aFlow.ReOpen();
			if (err != KErrNone)
				return err;
			flow = aFlow.iFlow;
			}
		// Assume: ConnectFlow will clean out all previous connection state! -- msa
		flow->Connect();
		}
	//
	// ...should this Refresh be done here or not? -- msa
	//
	if (flow->iStatus > EFlow_READY)
		flow->RefreshFlow();
	if (flow->iStatus == EFlow_READY)
		{
		//
		// Attach the flow to this handle
		//
		flow->Open();
		iFlow = flow;
		}
	//
	// Return current address information
	// (even though it in some cases might be random)
	//
	if (aInfo)
		{
		aInfo->iDstAddr = flow->RemoteAddr();
		aInfo->iSrcAddr = flow->LocalAddr();
		aInfo->iProtocol = flow->Protocol();
		}
	return flow->iStatus;
	}

// Help function for address loading, returns
//  = 0, if address did not change
//  = 1, if address has changed
//
static TInt ChangedAddress(TInetAddr &aAddr, const TInetAddr &aNewAddr)
	{
	// "Normalize" new address
	// - convert IPv4 addresess into IPv4 mapped
	// - anything other then KAfInet6 will be set as None
	TInetAddr tmp(aNewAddr);
	if (tmp.Family() == KAfInet)
		{
		tmp.ConvertToV4Mapped();
		tmp.SetScope(0);	// [is already part of "convert" in newer insocks]
		}
	else if (tmp.Family() != KAfInet6)
		tmp.SetAddress(KInet6AddrNone);

	if (tmp.CmpAddr(aAddr) &&				// address and port same?
		tmp.FlowLabel() == aAddr.FlowLabel())
		{
		// If the scope id in new addres is zero, then the
		// scope id is not part the address compare.
		// (this will optimize unconnected sockets where application
		// gives destination without scope -- eliminates unnecessary
		// flow connect operations.
		if (tmp.Scope() == aAddr.Scope() || tmp.Scope() == 0)
			return 0;	// No Change to the previous value
		}

	// Address is being changed
	aAddr = tmp;
	return 1;
	}


/**
* @defgroup	setselectors	Flow selector fields
*
* The selector information currently includes the following:
* @li	local address and port
* @li	remote addresses and port
* @li	IP protocol number
* @li	ICMP (or some other protocol) type and code.
*
* When a method is used to set any of the selector fields, the new and current value are
* compared, and if there is a change, the CFlowContext::iChanged is set.
*
* Although the code does not verify it, changing any of the selector fields should
* be done only by the owner of the flow context.
*
* If the handle is not attached to a flow, all methods will just silently return
* without doing anything.
*
* @{
*/

EXPORT_C void RFlowContext::SetRemoteAddr(const TSockAddr &aAddr)
	/**
	* Sets the flow's remote address and port.
	*
	* @param aAddr
	*	An address and port of the selector.
	*	If an address is given in IPv4 format (KAfInet), it will be automaticly
	*	converted into IPv4 mapped format (KAfInet6) in the flow context.
	*	Note that the flow  itself does not record whether an IPv4 address was specified. 
	*/
	{
	if (iFlow && ChangedAddress(iFlow->iInfo.iRemote, TInetAddr::Cast(aAddr)))
		{
		if (!iFlow->IsLocalSet())
			//
			// If the local address is unspecified by the application,
			// changing the remote address must also reset the local address
			// back to the unspecified state, so that the system will reselect
			// a new local address which will match the new remote address!
			//
			// Due to IPSEC, even changing port may cause destination
			// change, if policy specifies tunneling for certain ports
			// [this None setting may be unnecessary, need to check -- msa]
			iFlow->iInfo.iLocal.SetAddress(KInet6AddrNone);
		iFlow->iChanged = 1;
		}
	}

EXPORT_C void RFlowContext::SetLocalAddr(const TSockAddr &aAddr)
	/**
	* Sets the flow's local address and port
	*
	* @param aAddr
	*	An address and port of the selector.
	*
	*	If an address is given in IPv4 format (KAfInet), it will be converted
	*	into IPv4 mapped format (KAfInet6) in the flow context. If address
	*	is unspecified, the CFlowContext::iLocalSet is cleared, and set otherwise.
	*	See also CFlowContext::IsLocalSet().
	*
	*	Note that the flow  itself does not record whether an IPv4 address
	*	was originally specified in KAfInet or KAfInet6 format.
	*/
	{
	if (iFlow && ChangedAddress(iFlow->iInfo.iLocal, TInetAddr::Cast(aAddr)))
		{
		iFlow->iInfo.iLocalSet = !(iFlow->iInfo.iLocal.IsUnspecified());
		iFlow->iChanged = 1;
		}
	}

EXPORT_C void RFlowContext::SetProtocol(TUint aProtocol)
	/**
	* Sets the flow's protocol.
	* @param aProtocol
	*	The upper layer protocol associated with the flow (and the value for the IP
	*	NextHeader/Protocol field, unless some hook adds extension headers)
	*/
	{
	if (iFlow && iFlow->iInfo.iProtocol != aProtocol)
		{
		iFlow->iInfo.iProtocol = (TUint8)aProtocol;
		if (!iFlow->iInfo.iLocalSet)
			//
			// Need to reset source address to unselected also here...
			//
			// Due to IPSEC, even changing protocol, may cause destination
			// change, if policy specifies tunneling for certain protocols
			// [this None setting may be unnecessary, need to check -- msa]
			iFlow->iInfo.iLocal.SetAddress(KInet6AddrNone);

		iFlow->iChanged = 1;
		}
	}

EXPORT_C void RFlowContext::SetIcmpType(TUint aType, TUint aCode)
	/**
	* Sets the flow's ICMP type and code. Omitted aCode will set ICMP code in the
	* flow as zero.
	*
	* Note: Although this method is designed for ICMP, there are other protocols
	* which do not use ports, but some kind to type/code instead. Despite
	* it's name, this function can be used.
	*
	* @param aType		ICMP type
	* @param aCode		ICMP code
	*/
	{
	if (iFlow && (iFlow->iInfo.iIcmpType != aType || iFlow->iInfo.iIcmpCode != aCode))
		{
		iFlow->iInfo.iIcmpType = (TUint8)aType;
		iFlow->iInfo.iIcmpCode = (TUint8)aCode;
		if (!iFlow->iInfo.iLocalSet)
			//
			// Need to reset source address to unselected also here...
			//
			// Due to IPSEC, changing anything that affects selector,
			// may cause destination change (due tunnel), and thus
			// a new source address..
			// [this None setting may be unnecessary, need to check -- msa]
			iFlow->iInfo.iLocal.SetAddress(KInet6AddrNone);
		iFlow->iChanged = 1;
		}
	}


/** @} */	// End setselectors



EXPORT_C MProviderNotify * RFlowContext::SetNotify(MProviderNotify *aProvider)
	/**
	* Requesting notifications of events on the flow.
	*
	* A provider may request notification callbacks from events affecting the flow
	* by attaching a MProviderNotify instance to a flow context.
	* A flow can only have one such notifier at any time.
	*
	* Only the owner of the flow context is allowed to set this.
	*
	* The receiver of the notifications must implement the MProviderNotify mixin class:
	*
	* MProviderNotify::CanSend() is called when the flow might be ready to change
	* into the EFlow_READY status (this is only called when flow status is HOLD
	* or PENDING).
	*
	* MProviderNotify::Error is called whenever the flow is set into error state.
	* The aMask parameter follows the MSocketNotify (in <es_prot.h>) definitions.
	*
	* These two methods are a subset of the MSocketNotify,
	* and the intention is that, by default the SAP implementation using the flow
	* can just call the equivalent MSocketNotify method from these notifies.
	*
	* @param aProvider
	*		an object to receive the CanSend() or Error() upcalls,
	*		or NULL if cancel notifications
	* @return
	*		the pointer of the previous notifier, if any, is returned
	*		(or, the context was not attached, the method returns the
	*		aProvider).
	*/
	{
	// FFS: The notifier call should be suppressed while
	// iChanged is set, only the CanSend() notify?
	if (iFlow && iFlow->iOwner == this)
		{
		MProviderNotify *n = iFlow->iProvider;
		iFlow->iProvider = aProvider;
		return n;
		}
	else
		return aProvider;
	}

EXPORT_C void RFlowContext::Close()
	/**
	* Closes an open handle.
	*
	* This must be called when the handle is no longer required.
	* Close detaches the actual flow context from this handle.
	* If this handle was the last reference to the context, the context is
	* destroyed.
 	*/
	{
	if (iFlow)
		{
		if (iFlow->iOwner == this)
			iFlow->iProvider = NULL;
		// Do a "safe" close, by removing the flow context
		// reference from the handle before closing.
		CFlowContext &flow = *iFlow;
		iFlow = NULL;
		flow.Close();
		}
	}

EXPORT_C TInt RFlowContext::Status()
	/**
	* Gets the status of the flow.
	*
	* This function attempts to return the current "effective" status
	* of the flow based on current settings. Thus, if the flows parameters
	* have been changed since the last connect, the function does an implicit
	* (re)connect first.
	*
	* For documentation on values, see CFlowContext::Status.
	*
	* @return
	*		KErrBadHandle, if handle is not attached to a flow context. Other returns
	*		are defined by CFlowContext::Status.
	*/
	{
	if (!iFlow)
		return KErrBadHandle;

	// If the flow is in error state (< 0), the Status() alone will not
	// clear it (changed bit is not effective!).
	if (iFlow->iStatus >= 0 && iFlow->iChanged)
		{
		//
		// The flow parameters have been changed
		//
		if (iFlow->iRefs > 0)
			{
			//
			// Need to allocate a new flow

			TInt err = ReOpen();
			if (err != KErrNone)
				return err;
			}
		// Assume: ConnectFlow will clean out all previous connection state! -- msa
		iFlow->Connect();
		}
	return iFlow->Status();
	}

EXPORT_C CNifIfBase * RFlowContext::Interface() const
	/**
	* Gets a pointer to the network interface object at the end of the flow.
	*
	* @return	Network interface object (or NULL).
	*/
	{
	return iFlow ? iFlow->Interface() : NULL;
	}

EXPORT_C void RFlowContext::Grab(RFlowContext &aContext)
	/**
	* Moves the flow context from one handle to another.
	*
	* This operation does not increase or decrease the references.
	* The grabbed handle will become unattached.
	*
	* This function is not for general use.
	* @param aContext
	*		Handle from which to move the context
	*/
	{
	iFlow = aContext.iFlow;
	// FFS: Should check that the aContext is not the owner and disallow grab in such
	// situation (or disable notifier callback, if set). If a aContext
	// is the owner of the CFlowContext and notifier is set, then this pointer
	// is left "dangling"! Currently only used to detach from the packet, which
	// is never the owner? Suggested code (disable notifier):
	if (iFlow && iFlow->iOwner == &aContext)
		iFlow->iProvider = NULL;
	aContext.iFlow = NULL;
	}

EXPORT_C void RFlowContext::Copy(RFlowContext &aContext)
	/**
	* Copies the specified flow context to the handle and updates the
	* flow's reference count.
	* The call never fails.
	*
	* @param	aContext
	*	Handle to the flow context to copy
	*/
	{
	iFlow = aContext.iFlow;
	if (iFlow)
		iFlow->Open();
	}

#ifdef SYMBIAN_TCPIPDHCP_UPDATE
//RFC 4861 Section 7.2.2
//Checks whether Neighbour Discovery is currently pending
EXPORT_C TBool RFlowContext::IsNdResolutionPending()
	{
	TBool bFlag = EFalse;
	if (iFlow)
		{
		bFlag = iFlow->IsNdPacketPendingResolution();  			
		}
	return bFlag;	
	}
#endif //SYMBIAN_TCPIPDHCP_UPDATE

// ************
// CFlowContext (methods)
// ************
//
EXPORT_C CFlowContext::CFlowContext(const void *aOwner, MFlowManager *aManager) : iOwner(aOwner), iMgr(aManager), iStatus(KErrNotReady)
	/**
	* Constructor.
	*
	* The contructor (returned pointer) counts as a reference. The user does
	* not need to issue a separate Open() to update the reference count. When
	* the flow pointer is not needed, it must be released by a Close() call
	* (and not by a delete operator).
	* The Close() deletes the CFlowContext automaticly, when the last reference
	* is removed. The delete must not be used.
	*
	* @note
	*	The CFlowContext should be handled through the RFlowContext class, which
	*	takes care of the Open() and Close() calls correctly.
	*
	* @param aOwner
	*	Flow owner identifier. This is untyped, but can be used for 
	*	comparisions against other owner identifier.
	* @param aManager
	*	The flow manager that created this object
	*/
	{
	}

EXPORT_C CFlowContext::CFlowContext(const void *aOwner, MFlowManager *aManager, CFlowContext &aFlow)
  : iOwner(aOwner), iMgr(aManager), iStatus(KErrNotReady)
	/**
	* Constructor, copying the parameters of an existing flow.
	*
	* The contructed flow must be connected before it can be used
	* (regardless of the state of the copied flow).
	* @param aOwner
	*	Flow owner identifier. This is untyped, but can be used for 
	*	comparisions against other owner identifier.
	* @param aManager
	*	The flow manager that created this object
	* @param aFlow
	*	The flow from which to copy parameters
	*/
	{
	//
	// Copy/transfer upper layer specific information
	// into the new flow
	//
	iInfo = aFlow.iInfo;
	//
	// If the source flow has the same owner, then transfer the
	// notifier and option storage into the new flow context
	//
	if (iOwner == aFlow.iOwner)
		{
		iProvider = aFlow.iProvider;
		aFlow.iProvider = NULL;
		iStorage = aFlow.iStorage;
		aFlow.iStorage = NULL;
		}
	}


// Destructor should not be exported, it should be private! -- msa
EXPORT_C CFlowContext::~CFlowContext()
	/** Destructor. */
	{
	while (iStorage)
		{
		COptionValue *o = iStorage;
		iStorage = o->iNext;
		delete o;
		}
	//
	// Release associated RMBUF's, if left over by someone
	//
	iHead.iPacket.Free();
	iHead.iIcmp.Free();
	}



EXPORT_C void CFlowContext::Close()
	/**
	* Decrements a reference count on the context.
	*
	* The function deletes the CFlowContext object when the last reference is removed.
	*/
	{
	if (--iRefs < 0)
		delete this;
	}

EXPORT_C TInt CFlowContext::Status()
	/**
	* Refresh and return current status.
	*
	* This function is for upper layer users (flow owner) only, because it includes
	* conditional side effects through a call to the CFlowContext::RefreshFlow,
	* if the status not ready.
	*
	* A hook must not use this method for flows that it doesn't own itself..
	*
	* @return
	*		the general rules about the returned value are:
	* @li	< 0, the flow is in unrecoverable error state. No further output can be done
	*		over it and the only way to recover from this is to cause a reconnect.
	* @li	= 0, the flow is ready for output, data can be sent over it. (#EFlow_READY)
	* @li	> 0, the flow is temporarily blocked and waiting for event. When the event occurs,
	*		the SAP is notified if it has setup for it (see SetNotify method).
	* @li	A few specific values have been defined as follows:
	* @par	Flow_PENDING
	*		Not ready for data yet. This occurs normally at the connection
	*		startup, when the connection to outside is being established (netdial),
	*		but it can also occur at later stage (for example, when IPSEC needs to
	*		negotiate a new key).
	* @par EFlow_HOLD
	*		This state is set when the interface reports congestion
	*		(cannot receive more data for a while).
	*/
	{
	//
	// The rationale here goes something...
	// in normal situation the iStatus == EFlow_READY and this non-virtual
	// method is very light to execute. For the exceptional situation a
	// heavier virtual RefreshFlow() is called to check if flow state can
	// be changed (this may include initializing the source address).
	// "Normal" exceptions are
	//	EFlow_PENGING ( > EFlow_READY)
	//		usually means that the interface is not yet fully and/or the source
	//		address to be use cannot yet be decided. The refresh will verify this
	//		and update the source if situation has changed.
	//	EFlow_HOLD
	//		the interface has reported that it is unable to receive more packet
	//		packets
	//
	//	For this to work, it requires
	//		1) when interface goes into HOLD, all affected flows must be marked with
	//		HOLD state
	//		2) if a SAP keeps opened flow handle, it MUST use Status() call before
	//		sending of each packet.
	//
	//	Refresh is only called for PENDING states. Once the flow enters error
	//	(< EFlow_READY), it cannot be used for sending data!
	//
	if (iStatus > EFlow_READY)
		RefreshFlow();
	return iStatus;
	}

EXPORT_C void CFlowContext::SetStatus(TInt aStatus)
	/**
	* Sets (suggests) the flow status value.
	*
	* The function allows a hook to suggest a value for the flow status.
	* The flow does not necessarily accept this value.
	*
	* This function should never be used by the flow owner.
	*
	* The function uses the following rules (in the listed order) to
	* determine the new status value:
	* -# if the new status is an error ( < 0), it will become the flow status
	* and all hooks are removed throuh a call to Disconnect().
	* A hook must be careful here, as this will cause a call to
	* the MFlowHook::Close. Also, the MProviderNotify::Error is called,
	* if the notifier is attached to the flow.
	* -# if the old status is an error ( < 0), it cannot be changed to any
	* non-error status (SetStatus is ignored),
	* -# if the new status is #EFlow_READY and the old flow status was
	* not READY (was > 0), the flow status is NOT changed, but the attached upper
	* layer is notified (MProviderNotify::CanSend). This will eventually cause a
	* flow refresh where the correct value of the status will be determined.
	* -# if the new status is pending/hold/blocked (> 0), it will become the
	* flow status.
	*
	* The idea behind above definition is that the hook can suggest a status
	* from its local view of the flow, and SetStatus makes the "global"
	* decision, and activates necessary callbacks if any is required.
	*
	* @param aStatus
	*	The suggested status of the flow: either a system wide error 
	*	code or a #TFlowStatus value.
	*/
	{
	if (aStatus < 0)
		{
		// New status is Error status, set it
		// and notify SAP. The previous status does not
		// matter.
		iStatus = aStatus;
		Disconnect();		// ...removes the hooks
		if (iProvider)
			iProvider->Error(aStatus);
		}
	else if (iStatus > 0)
		{
		// Old status is "pending/hold/blocked"
		if (aStatus > 0)
			iStatus = aStatus;		// Allow any overrides > 0 (!= EFlow_READY)
		else if (iProvider)
			iProvider->CanSend();
		}
	else if (iStatus == EFlow_READY)
		// New status is either READY or PENDING
		iStatus = aStatus;
	// When none of the above conditions apply, old status
	// is Error state and cannot be overridden by any
	// non-Error status.
	}

EXPORT_C TInt CFlowContext::StoreOption(TUint aLevel, TUint aName, const TDesC8 &aOption)
	/**
	* Stores a flow context-specific option.
	*
	* The function allows outbound flow hook modules to store the options that they 
	* support. Sockets clients can ultimately query these options through RSocket::GetOpt().
	*
	* If the specified (level, name) pair already exists in the storage, the new 
	* value replaces the old value.
	*
	* @param aLevel the option level
	* @param aName the option name
	* @param aOption the option value
	*
	* @return
	*		@li KErrNone on success,
	*		@li KErrNoMemory, if option cannot stored due to memory shortage
	*/
	{
	COptionValue **h, *o;
	const TInt length = aOption.Length();
	for (h = &iStorage; (o = *h) != NULL; h = &o->iNext)
		{
		if (o->iLevel == aLevel && o->iName == aName)
			{
			// Replacing a value of existing option. Just delete
			// the previous allocation (and insert new as if this
			// was not present).
			*h = o->iNext;
			delete o;
			break;
			}
		}
	//
	// Attach the new option to the front of the list
	//
	o = new (length) COptionValue(aLevel, aName, aOption);
	if (o)
		{
		o->iNext = iStorage;
		iStorage = o;
		return KErrNone;
		}
	else
		return KErrNoMemory;
	}

EXPORT_C TInt CFlowContext::RetrieveOption(TUint aLevel, TUint aName, TDes8 &aOption) const
	/**
	* Gets a flow context-specific option.
	*
	* The option must have been previously stored with StoreOption().
	* Retrieve value from flow
	*
	* @param aLevel The option level
	* @param aName The option name
	* @retval aOption The option value
	*
	* @return
	*		@li KErrNone on success,
	*		@li KErrNotFound, if the value is not stored
	*/
	{
	for (COptionValue *o = iStorage; o != NULL; o = o->iNext)
		{
		if (o->iLevel == aLevel && o->iName == aName)
			{
			aOption = o->Value();
			return KErrNone;
			}
		}
	return KErrNotFound;
	}