networkprotocols/iphook/inhook6/src/flow.cpp
changeset 0 af10295192d8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/networkprotocols/iphook/inhook6/src/flow.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,900 @@
+// 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;
+	}