--- /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;
+ }