// Copyright (c) 2007-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:
//
#define SYMBIAN_NETWORKING_UPS
#include <es_prot.h>
#include <comms-infras/ss_roles.h>
#include <comms-infras/ss_log.h>
#include "es_mbufif.h"
#include <ss_glob.h>
#include <ss_protprov.h>
#include <comms-infras/ss_sapshim.h>
#include <comms-infras/ss_intsock.h>
#include <in_sock.h>
#include "ss_flowrequest.h"
#include "ss_sapfactshim.h"
#include <comms-infras/ss_nodemessages_dataclient.h>
#include <comms-infras/cfmacro.h>
#include <comms-infras/ss_msgintercept.h>
#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include <es_sock_internal.h>
#endif
#ifdef SYMBIAN_NETWORKING_UPS
#include <comms-infras/ss_platsec_apiext.h> // MPlatSecApiExt
#endif //SYMBIAN_NETWORKING_UPS
#ifdef _DEBUG
// Panic category for "absolutely impossible!" vanilla ASSERT()-type panics from this module
// (if it could happen through user error then you should give it an explicit, documented, category + code)
_LIT(KSpecAssert_ESockSSocksntsck, "ESockSSocksntsck");
#endif
using namespace ESock;
using namespace Messages;
using namespace Factories;
using namespace Den;
/**
@internalComponent
*/
const TUint KAllSelectFlags = (KSockSelectRead | KSockSelectWrite | KSockSelectExcept);
#define MSG_PRM(prmIndex) (prmIndex)
ASocket::ASocket(TInt aSocketType)
: iSocketType(aSocketType),
iIsFlowRequestPending(EFalse)
/**
Constructor - set up default options.
*/
{
}
const RNodeInterface* ASocket::ServiceProvider() const
{
return iServiceProvider.IsOpen()? &iServiceProvider : NULL;
}
TBool ASocket::GetFlowAndSCPR(Messages::TNodeId& aFlow, Messages::TNodeId& aSCPR) const
{
if (iFlowBinder)
{
aFlow = iFlowBinder->Flow()->Id();
aSCPR = iFlowBinder->Flow()->ControlProvider().RecipientId();
return ETrue;
}
return EFalse;
}
void ASocket::ReceivedL(const TRuntimeCtxId& aSender, const TNodeId& aRecipient, TSignatureBase& aMessage)
{
(void) aRecipient;
ESOCK_DEBUG_MESSAGE_INTERCEPT(aSender, aMessage, aRecipient);
if (aMessage.IsMessage<TEBase::TError>())
{
// The first error would be from the flow request, any other error iIsFlowRequestPending must be false anyway
if (FlowRequestPending())
{
TEBase::TError& errorMsg(static_cast<TEBase::TError&>(aMessage));
CompleteFlowRequestMessage(errorMsg.iValue);
SetFlowRequestPending(EFalse);
}
//We are no longer needed and the client has been completed too, tear us down.
InitiateDestruction();
}
else if (aMessage.IsMessage<TCFDataClient::TBindTo>())
{
TCFDataClient::TBindTo& bindToMsg(static_cast<TCFDataClient::TBindTo&>(aMessage));
TInt err;
if (IsClosing())
{
err = KErrAbort;
}
else
{
TRAP(err,BindToL(bindToMsg));
}
RClientInterface::OpenPostMessageClose(Id(), aSender, TCFDataClient::TBindToComplete(err).CRef());
CompleteFlowRequestMessage(err);
SetFlowRequestPending(EFalse);
if ( IsClosing () )
InitiateDestruction ();
}
else
{
__ASSERT_DEBUG(EFalse, User::Panic(KSpecAssert_ESockSSocksntsck, 1));
}
}
void ASocket::BindToL(const TCFDataClient::TBindTo& aBindTo)
{
__ASSERT_DEBUG(iSSP==NULL, User::Panic(KSpecAssert_ESockSSocksntsck, 2));
#ifdef SYMBIAN_NETWORKING_UPS
LOG(ESockLog::Printf(_L8("CSocket %08x:\tBindToL(%08x)"), this, &aBindTo.iNodeId.Node()) );
LOG(ESockLogExternal::Printf(KCFNodeTag, KESockFlowTag, _L8("CSocket %08x:\tSynchronous call: Sender=%08x, Recipient=%08x, Function=BindToL"), this, static_cast<Messages::ANode*>(this), &aBindTo.iNodeId.Node()) );
#endif
#if defined(__GCCXML__)
CSubConnectionFlowBase* flow = reinterpret_cast<CSubConnectionFlowBase*>(
reinterpret_cast<Messages::ANode*>(
aBindTo.iNodeId.Ptr()
)
);
#else
CSubConnectionFlowBase* flow = mcfnode_cast<CSubConnectionFlowBase>(
reinterpret_cast<Messages::ANode*>(
aBindTo.iNodeId.Ptr()
)
);
#endif
if (flow==NULL)
{
NM_LOG((KESockServerTag, _L8("CSocket %08x:\tSynchronous call: From=%08x To=%08x Func=BindToL Error flow is null"),
this, static_cast<Messages::ANode*>(this), &aBindTo.iNodeId.Node()) )
User::Leave(KErrArgument);
}
NM_LOG((KESockServerTag, _L8("CSocket %08x:\tSynchronous call: From=%08x To=%08x Func=BindToL"),
this, static_cast<Messages::ANode*>(this), &aBindTo.iNodeId.Node()) )
// Perform the binding to the flow below
iFlowBinder = flow->GetBinderControlL();
iServiceProvider.Open(iFlowBinder->Flow()->Id());
iSSP = iFlowBinder->GetControlL(iProtocolInfo->iSockType, *this);
iSSPData = iFlowBinder->BindL(*this);
if(RequiresOwnerInfo())
{
CommunicateOwner();
}
// ask the socket provider to perform security policy checking
TInt ret = SecurityCheck();
NM_LOG((KESockServerTag, _L8("CSocket %08x:\tSynchronous call: From=%08x To=%08x Func=SecurityCheck Result Error Code=%d"),
this, static_cast<Messages::ANode*>(this), &aBindTo.iNodeId.Node(), ret) )
User::LeaveIfError(ret);
iSSP->Start();
}
void ASocket::AcceptSetupL(const ASocket& aParentSocket, CSubConnectionFlowBase& aFlow)
/**
Copy constructor type thing for Accept
Takes all the useful info from a parent socket, sets up the supplied SSP and
Other default values.
*/
{
iOptions = aParentSocket.iOptions;
iProtocolInfo = aParentSocket.iProtocolInfo;
__ASSERT_DEBUG(iFlowBinder==NULL, User::Panic(KSpecAssert_ESockSSocksntsck, 3));
iFlowBinder = aFlow.GetBinderControlL();
iSSP = iFlowBinder->GetControlL(iProtocolInfo->iSockType, *this);
iSSPData = iFlowBinder->BindL(*this);
iIsBound=ETrue;
iRecBufSize=aParentSocket.iRecBufSize;
iSendBufSize=aParentSocket.iSendBufSize;
}
void ASocket::Create(TServerProtocolDesc *aServiceInfo)
/**
Create for all sockets.
*/
{
/** We create non connection orinted sockets "Open" and sockets without an ssp "NULL" */
if (!aServiceInfo)
{
SetState(ESStateNull);
UnregisterSelf(); // Null sockets need no cookie (and subsequently trying to deregister from a different Accept()ing Player is problematic and adds no benefit
}
else
{
iProtocolInfo=aServiceInfo;
if (!IsConnectionOriented())
{
SetState(ESStateOpen);
}
}
iOptions=KESocketDefaultOptions;
iIsBound=EFalse;
#ifdef ESOCK_GULP
// allocate extra memory for experimental gulp implementation
iRecBufSize=KSocketDefaultBufferSize * 4;
#else
iRecBufSize=KSocketDefaultBufferSize;
#endif
iSendBufSize=KSocketDefaultBufferSize;
}
ASocket::~ASocket()
/**
Socket destructor. Will ensure that the SSP (and any accept queue) is shutdown before destroying it.
Automatically completes all outstanding requests.
*/
{
iServiceProvider.Close();
delete iDisconnectData;
iDatagramTail.Free(); // we should normally have done this in closing
iSendData.Free(); // we should normally have done this in closing
if (iAcceptQ)
{
while (iAcceptQ->Count())
{
TAcceptQEntry a;
iAcceptQ->Remove(&a);
// the SSPs on an accept queue shouldn't have been Start()ed so we don't need to shut them down.
// a.iSSP->DeleteMeNow(); // destruction no longer synchronous & vertical
CTransportFlowShim* shimFlow = factoryobject_cast<CTransportFlowShim>(a.iSSP);
// The SAP is no longer owned by the listener socket.
shimFlow->iListenerControlNotify = NULL;
shimFlow->InitDestroy();
delete a.iConnectData;
}
delete iAcceptQ;
}
delete iNextAcceptQ;
delete iAllocAsync;
delete iCurrentMsg;
delete iReadMsg;
delete iWriteMsg;
delete iBlockedCloseMsg;
delete iBlockedConnectMsg;
delete iBlockedIoctlMsg;
delete iBlockedSetLocalNameMsg;
}
void ASocket::InitiateDestruction()
{
SetClosing();
if (iSSP)
{
if ((State()==ESStateOpeningActive || State()==ESStateOpeningPassive
|| State()==ESStateOpen || State()==ESStateConnected
|| State()==ESStateBinding
))
{
iSSP->Shutdown(MSessionControl::EImmediate);
}
__ASSERT_DEBUG(iFlowBinder, User::Panic(KSpecAssert_ESockSSocksntsck, 4));
iFlowBinder->Unbind();
}
/*
CSubConnectionFlowBase* flow = iFlowBinder->Flow();
if(!flow->HasControlPlane())
{
CNetworkFlow* netFlow = static_cast<CNetworkFlow*>(flow);
delete netFlow;
iSSP = NULL;
delete this;
}
}
else
{
// Either stillborn with OOM or after Shutdown() has detached
delete this;
}
*/
}
void ASocket::ShutdownL(RSocket::TShutdown aType, TBool aDisconnectData)
/**
Terminate the protocol
*/
{
if (State()==ESStateShuttingDown)
{
PanicSocketClient(EShutDownTwice);
return;
}
if (!CheckRunningAndSetReturn())
{
return;
}
MSessionControl::TCloseType h=MSessionControl::ENormal;
if (SupportsGracefulClose())
{
switch (aType)
{
case RSocket::ENormal:
CompleteWrite(KErrCancel);
CompleteRead(KErrCancel);
h=MSessionControl::ENormal;
iBlockedOperations|=(EReadStopped|EWriteStopped);
break;
case RSocket::EStopInput:
CompleteRead(KErrCancel);
h=MSessionControl::EStopInput;
iBlockedOperations|=EReadStopped;
iDatagramTail.Free();
break;
case RSocket::EStopOutput:
h=MSessionControl::EStopOutput;
iBlockedOperations|=EWriteStopped;
CompleteWrite(KErrCancel);
break;
case RSocket::EImmediate:
h=MSessionControl::EImmediate;
CompleteWrite(KErrCancel);
CompleteRead(KErrCancel);
iDatagramTail.Free();
break;
}
}
else
{
CompleteWrite(KErrCancel);
CompleteRead(KErrCancel);
h=MSessionControl::EImmediate;
}
CompleteConnect(KErrCancel);
CompleteSetLocalName(KErrCancel);
SetState(ESStateShuttingDown);
if(h!=MSessionControl::EImmediate)
{
SetBlockedClose();
DontCompleteCurrentRequest();
}
if (CanSendDisconnectData() && h!=MSessionControl::EImmediate && aDisconnectData)
{
TDes8& sendBuf = *BorrowTemporaryBufferL(iSendBufSize);
ReadParamL(ESocketCurrentMessage,MSG_PRM(1),sendBuf);
iSSP->Shutdown(h,sendBuf);
}
else
{
iSSP->Shutdown(h);
}
if (iErrorOperationMask & MSessionControlNotify::EErrorClose)
{
SetReturn(iError);
ClearErrorIfNotFatal(); //Sets iError = 0;
iErrorOperationMask = 0;
}
// OK to access socket because a CanClose would set the socket state to dead
else if (h==MSessionControl::EImmediate)
{
// Set state to dead - let close delete the socket
SetState(ESStateDead);
}
else
{
if (!(iOptions & KOptBlocking) && IsBlockedClose())
{
CompleteClose(KErrWouldBlock);
}
}
}
TBool ASocket::CloseSocket()
/**
A Client has closed our RSocket - or we're being closed by the session because our client has exited
*/
{
SetClosing();
TBool immediateClose = EFalse;
switch (State())
{
case ESStateNull:
case ESStateAccepting:
case ESStateCreated:
case ESStateError:
case ESStateDead:
case ESStateDisconnected:
immediateClose = ETrue;
break;
case ESStateShuttingDown:
CompleteClose(KErrCancel);
SetReturn(KErrAlreadyExists);
SetState(ESStateClosing);
immediateClose = ETrue;
break;
case ESStateOpeningActive:
case ESStateOpeningPassive:
case ESStateOpen:
case ESStateConnected:
case ESStateBinding:
{
SetState(ESStateClosing);
TBool graceful = SupportsGracefulClose() && iSSP;
// CanClose will be called irrespective of whether the socket
// supports graceful close or not.
// ASocket can take care of the ownership of deletion via InitiateDestruction.
// Also, CanClose will call InitiateDestruction if we are in ESStateClosing.
// We already set to ESStateClosing.
SetBlockedClose();
DontCompleteCurrentRequest(); // Block closes regardless of NonBlocking mode state
if (graceful)
{
iSSP->Shutdown(MSessionControl::ENormal);
}
else
{
if (iSSP)
{
iSSP->Shutdown(MSessionControl::EImmediate);
}
CanClose(MSessionControlNotify::EDelete);
}
}
break;
case ESStateClosing:
PanicSocketClient(ECloseTwice);
break;
}
return immediateClose;
}
void ASocket::BindL()
/**
Set local address from a client request.
*/
{
if (!CheckRunningAndSetReturn())
{
return;
}
if (IsConnectionOriented() && iIsBound)
{
SetReturn(KErrAlreadyExists);
}
else
if (State() == ESStateBinding)
{
SetReturn(KErrInUse);
}
else
{
TSockAddr a;
ReadParamL(ESocketCurrentMessage,MSG_PRM(0),a);
// Set to blocked before call to SetLocalName. This allows for immediate callback,
// as some conditions may not require asynchronous processing.
SetBlockedSetLocalName();
DontCompleteCurrentRequest();
// Save off the state for transition later.
iNextState = State();
SetState(ESStateBinding);
iSSP->SetLocalName(a);
}
}
void ASocket::AutoBind()
{
iSSP->AutoBind();
iIsBound=ETrue;
}
void ASocket::ConnectL(TBool aConnectData)
/**
Active open the socket - from a client request.
*/
{
TSockAddr a;
TInt ret=KErrNone;
switch (State())
{
case ESStateOpeningActive:
case ESStateOpeningPassive:
PanicSocketClient(EConnectingAlready);
break;
case ESStateBinding:
SetReturn(KErrInUse);
break;
case ESStateConnected:
if (IsConnectionOriented())
{
if(IsBlockedConnect())
{
PanicSocketClient(EConnectingAlready);
}
SetReturn(KErrAlreadyExists);
return;
}
// Fall through - non connection oriented sockets can call Connect as much as they like.
case ESStateOpen:
case ESStateCreated:
{
ReadParamL(ESocketCurrentMessage,MSG_PRM(0),a);
ret=iSSP->SetRemName(a);
if (ret!=KErrNone)
{
SetReturn(ret);
return;
}
if(!iIsBound)
{
SetBlockedConnect();
DontCompleteCurrentRequest();
if (!IsConnectionOriented())
{
iNextState = ESStateConnected;
}
else
{
iConnectData = aConnectData;
iNextState = ESStateOpeningActive;
}
SetState(ESStateBinding);
AutoBind();
if (!(iOptions&KOptBlocking) && IsBlockedConnect())
{
CompleteConnect(KErrWouldBlock);
}
return;
}
if (!IsConnectionOriented())
{
SetState(ESStateConnected);
TryToCompleteSelectIoctl();
}
else
{
SetBlockedConnect();
DontCompleteCurrentRequest();
SetState(ESStateOpeningActive);
if (CanSendConnectData() && aConnectData)
{
TDes8& sendBuf = *BorrowTemporaryBufferL(iSendBufSize);
ReadParamL(ESocketCurrentMessage,MSG_PRM(1),sendBuf);
iSSP->ActiveOpen(sendBuf);
}
else
{
iSSP->ActiveOpen();
}
if (!(iOptions&KOptBlocking) && IsBlockedConnect())
{
CompleteConnect(KErrWouldBlock);
}
}
}
break;
case ESStateDisconnected:
case ESStateShuttingDown:
case ESStateClosing:
case ESStateDead:
SetReturn(KErrBadHandle);
break;
case ESStateError:
SetReturn(iError);
return;
default:
PanicSocketClient(ECannotConnect);
//Fault(EBadState);
break;
}
}
//indexes of the data elements in RMessage for Get/SetOpt
#define OPT_NAME_INDEX 0
#define OPT_OPTION_INDEX 1 //for the option value
#define OPT_OPT_LENGTH_INDEX 2
#define OPT_OPT_LEVEL_INDEX 3
/**
Set a socket option from a client request
*/
void ASocket::SetSockOptionL(TInt aOptionName, TInt aOptionLength, TInt aOptionLevel)
{
if (aOptionName & KSocketInternalOptionBit)
{
SetReturn(KErrAccessDenied);
return;
}
if (aOptionLevel==KSOLSocket)
{
TBufC8<sizeof(TUint)> smallOptionBuf;
TPtr8 option=smallOptionBuf.Des();
switch(aOptionName)
{
case KSOSendBuf:
{
ReadParamL(ESocketCurrentMessage,MSG_PRM(OPT_OPTION_INDEX),option);
iSendBufSize=*(TUint*)smallOptionBuf.Ptr();
// Even though we don't respect the sendbuf size for transfers we need to
// be able to echo the value back to a GetOpt() - also it's used for
// sizing the Connect/Shutdown_WithData operations
if (iSendBufSize==KSocketBufSizeUndefined)
{
iSendBufSize=KSocketDefaultBufferSize;
iOptions&=~KOptSendBufSet;
break;
}
iOptions|=KOptSendBufSet;
break;
}
case KSORecvBuf:
{
ReadParamL(ESocketCurrentMessage,MSG_PRM(OPT_OPTION_INDEX),option);
iRecBufSize=*(TUint*)smallOptionBuf.Ptr();
// Even though we don't respect the recvbuf size for transfers we need to
// be able to echo the value back to a GetOpt()
if (iRecBufSize==KSocketBufSizeUndefined)
{
iOptions&=~KOptRecBufSet;
break;
}
iOptions|=KOptRecBufSet;
break;
}
case KSODebug:
{
ReadParamL(ESocketCurrentMessage,MSG_PRM(OPT_OPTION_INDEX),option);
TBool b;
TPtr8 p((TUint8*)&b,sizeof(iSendBufSize),sizeof(iSendBufSize));
ReadParamL(ESocketCurrentMessage,MSG_PRM(OPT_OPTION_INDEX),p);
if(*(TBool*)option.Ptr())
{
iOptions|=KOptDebug;
}
else
{
iOptions&=~KOptDebug;
}
break;
}
case KSONonBlockingIO:
iOptions &= ~KOptBlocking;
break;
case KSOBlockingIO:
iOptions |= KOptBlocking;
break;
//-- enable socket transfer to another process with capabilities check.
//-- socket option must be TPckgBuf<TSecurityPolicy>, TSecurityPolicy in turn must contain the capabilities set for the
//-- process that is going to receive this socket as a result of RSocket::Transfer().
case KSOEnableTransfer:
{
if(State() == ESStateNull || State() == ESStateAccepting)
{
User::Leave(KErrNotReady);
}
//-- read security information (capabilities set) from the client
TSecurityPolicyBuf secPolicyBuf;
ReadParamL(ESocketCurrentMessage,MSG_PRM(OPT_OPTION_INDEX),secPolicyBuf);
//-- store the capabilities set for the future check in ProtocolManager::TransferSocketL()
iSecTransferEnabled=EFalse;
User::LeaveIfError(iTransferSecPolicy.Set(secPolicyBuf));
iSecTransferEnabled=ETrue; //-- indication that the RSocket::Transfer() security information is ready to check
}
break;
//-- Disable socket transfer by indicating that the transfer security information is not valid
case KSODisableTransfer:
iSecTransferEnabled=EFalse;
break;
default:
SetReturn(KErrNotSupported);
}
}
else
{
if (CheckRunningAndSetReturn())
{
HBufC8* optionBuf=NULL;
optionBuf=HBufC8::NewMaxLC(aOptionLength);
TPtr8 option=optionBuf->Des();
ReadParamL(ESocketCurrentMessage,MSG_PRM(OPT_OPTION_INDEX),option);
SetReturn(iSSP->SetOption(aOptionLevel,aOptionName,option));
CleanupStack::PopAndDestroy(optionBuf);
}
}
}
void ASocket::GetSockOptionL(TInt aOptionName, TInt aOptionLength, TInt aOptionLevel, TInt aWriteBack)
/**
Read a socket option from us (or attempt the protocol if we don't support the option)
*/
{
if (aOptionName&KSocketInternalOptionBit)
{
SetReturn(KErrAccessDenied);
return;
}
if (aOptionLevel==KSOLSocket)
{
// Most KSOLSocket options use argument1 as an out parameter
switch(aOptionName)
{
case KSOSendBuf:
case KSORecvBuf:
case KSODebug:
case KSONonBlockingIO:
case KSOBlockingIO:
case KSOSelectPoll:
{
if(aWriteBack)
{
// Retrieve the required value
TUint optionVal;
switch(aOptionName)
{
case KSOSendBuf:
{
optionVal =iSendBufSize;
break;
}
case KSORecvBuf:
{
optionVal =iRecBufSize;
break;
}
case KSODebug:
{
optionVal =(iOptions&KOptDebug);
break;
}
case KSONonBlockingIO:
{
optionVal =!(iOptions&KOptBlocking);
break;
}
case KSOBlockingIO:
{
optionVal =iOptions&KOptBlocking;
break;
}
case KSOSelectPoll:
{
if(CheckRunningAndSetReturn())
{
optionVal =SelectConditionsReady();
}
else
{
aWriteBack = EFalse;
}
break;
}
// coverity [dead_error_begin] :
default:
// the default should never be called and is there to catch coding errors.
__ASSERT_DEBUG(0, User::Panic(KSpecAssert_ESockSSocksntsck, 5));
}
if(aWriteBack)
{
TPckg <TUint> option(optionVal);
WriteParamL(ESocketCurrentMessage,MSG_PRM(OPT_OPTION_INDEX),option);
}
}
else
{
// Somehow they passed a NULL second argument - should we panic them?
SetReturn(KErrArgument);
}
break;
}
case KSOSelectLastError:
{
SetReturn(iError);
if(aWriteBack)
{
TPckg <TUint> option(iError);
WriteParamL(ESocketCurrentMessage,MSG_PRM(OPT_OPTION_INDEX),option);
}
else
{
// Somehow they passed a NULL second argument - should we panic them?
SetReturn(KErrArgument);
}
ClearErrorIfNotFatal();
break;
}
case KSOReadBytesPending:
case KSOUrgentDataOffset:
{
if(!CheckRunningAndSetReturn())
{
break;
}
if(aOptionName==((TInt)KSOUrgentDataOffset) && !CanSendUrgentData())
{
SetReturn(KErrNotSupported);
break;
}
TSockXfrLength len;
len()=iRecOffset;
SetReturn(iSSP->GetOption(aOptionLevel,aOptionName,len));
if (aWriteBack)
{
WriteParamL(ESocketCurrentMessage,MSG_PRM(OPT_OPTION_INDEX),len);
}
break;
}
default:
SetReturn(KErrNotSupported);
}
}
else
{
if(CheckRunningAndSetReturn())
{
if (aWriteBack)
{
RBuf8 optionBuf;
optionBuf.CreateMaxL(aOptionLength);
CleanupClosePushL(optionBuf);
TPtr8 option=optionBuf.MidTPtr(0);
ReadParamL(ESocketCurrentMessage,MSG_PRM(OPT_OPTION_INDEX),option);
SetReturn(iSSP->GetOption(aOptionLevel,aOptionName,option));
WriteParamL(ESocketCurrentMessage,MSG_PRM(OPT_OPTION_INDEX),option);
CleanupStack::PopAndDestroy(&optionBuf);
}
else
{
SetReturn(KErrArgument);
}
}
}
}
void ASocket::IoctlL(TInt aOptionName, TInt aOptionLength, TInt aOptionLevel, TBool aReadOption)
/**
Perform an Ioctl from a user request
*/
{
if (IsBlockedIoctl())
{
PanicSocketClient(ETwoIoctls);
return;
}
if (aOptionName&KInternalIoctlBit)
{
SetReturn(KErrAccessDenied);
return;
}
if (aOptionLevel==KSOLSocket)
{
if(aOptionName==(TInt)KIOctlSelect)
{
if(!CheckRunningAndSetReturn())
{
return;
}
TPckgBuf<TUint> flags;
ReadParamL(ESocketCurrentMessage,MSG_PRM(1), flags);
iSelectFlags=flags();
if((iSelectFlags&KAllSelectFlags)!=0)
{
SetBlockedIoctl();
DontCompleteCurrentRequest();
TryToCompleteSelectIoctl();
}
else
{
SetReturn(KErrArgument);
}
}
else
{
SetReturn(KErrNotSupported);
}
}
else
{
// if(!(iOptions & KOptBlocking))
// {
// SetReturn(KErrWouldBlock);
// return;
// }
// if (CheckRunningAndSetReturn()==EFalse)
// return;
if(iSSP == NULL)
{
SetReturn(KErrNotSupported);
return;
}
if (aReadOption)
{
if(aOptionLength < 0)
{
User::Leave(aOptionLength);
}
HBufC8* ioctlBuf=HBufC8::NewLC(aOptionLength);
TPtr8 des=ioctlBuf->Des();
ReadParamL(ESocketCurrentMessage,MSG_PRM(1),des);
// only set ioctl blocked if successfull allocation
SetBlockedIoctl();
DontCompleteCurrentRequest();
iSSP->Ioctl(aOptionLevel,aOptionName,&des);
CleanupStack::PopAndDestroy(ioctlBuf);
}
else
{
SetBlockedIoctl();
DontCompleteCurrentRequest();
iSSP->Ioctl(aOptionLevel,aOptionName,NULL);
}
}
LOG(
if (IsBlockedIoctl())
{
ESockLog::Printf(KESockSessDetailTag, _L8("ASocket::IoctlL blocking") );
}
)
}
void ASocket::CancelIoctl()
/**
Cancel a pending Ioctl and notify the protocol.
*/
{
if(IsBlockedIoctl())
{
TInt optionLevel = 0;
TInt optionName = 0;
if(!iSelectFlags)
{
optionLevel = iBlockedIoctlMsg->ReadInt(2);
optionName = iBlockedIoctlMsg->ReadInt(0);
}
CompleteIoctl(KErrCancel);
if(iSelectFlags)
{
iSelectFlags=0;
}
else
{
__ASSERT_ALWAYS(iSSP!=NULL, Panic(ENullSap));
iSSP->CancelIoctl(optionLevel, optionName);
}
}
}
void ASocket::GetDisconnectDataL( ) const
/**
Get disconnect data if any is present on the socket.
*/
{
if (CanSendDisconnectData())
{
if(iDisconnectDataError!=KErrNone)
{
SetReturn(iDisconnectDataError);
}
else if (iDisconnectData)
{
TPtr8 des=iDisconnectData->Des();
const_cast<ASocket*>(this)->WriteParamL(ESocketCurrentMessage,MSG_PRM(0),des);
}
else
{
SetReturn(KErrNotFound);
}
}
else
{
SetReturn(KErrNotSupported);
}
}
static const TInt KPaddingForHeaders = 48;
void ASocket::SendL(TInt aXferLenArg, TInt aAddrArg, TInt aSendByteCount, TInt aSendFlags, TBool aUseMBufs)
/**
Common message service for Send, SendTo and Write.
*/
{
if (IsBlockedWrite())
{
PanicSocketClient(EWritingAlready);
return;
}
if (!CheckRunningAndSetReturn())
{
return;
}
// Judge If the socket is connected during a connection-oriented service.
if (IsConnectionOriented() && iState != ESStateConnected)
{
LOG( ESockLog::Printf(_L("ASocket %08x SendL() KErrNotReady"), this ) );
SetReturn(KErrNotReady);
return;
}
if(iBlockedOperations & EWriteStopped)
{
if(aXferLenArg >= 0)
{
iXferLength() = 0;
WriteParamL(ESocketCurrentMessage, aXferLenArg, iXferLength);
}
SetReturn(KErrEof);
return;
}
// Set whether we are sending RMbufChain or not
iSendUseMBufs = aUseMBufs;
iSendByteCount=aSendByteCount;
iSendXferLenIdx = aXferLenArg;
iSendToAddrIdx = aAddrArg;
if (iSendToAddrIdx >= 0)
{
if(IsConnectionOriented())
{
PanicSocketClient(ECantSendToOnConnection);
return;
}
}
else // Not ESendTo...
{
if(State() != ESStateConnected && State() != ESStateShuttingDown && iSendToAddrIdx != ASocket::KWriteNoAddrArg)
{
LOG( ESockLog::Printf(_L("ASocket %08x SendL() 2 KErrNotReady"), this ) );
SetReturn(KErrNotReady);
return;
}
}
iSendFlags = aSendFlags;
if(iSendFlags)
{
if(iSendFlags&KSocketInternalWriteBit)
{
SetReturn(KErrAccessDenied);
return;
}
if ((iSendFlags&KSockWriteUrgent) && !CanSendUrgentData())
{
SetReturn(KErrNotSupported);
return;
}
}
__ASSERT_DEBUG(iSendData.IsEmpty(), Fault(EBufsLeftInSendData)); // no pathway should leave this populated after Write completes
iSendData.Free(); // but paranoia is cheap
if(iSendByteCount<0)
{
PanicSocketClient(EBadDescriptorLength);
return;
}
#ifdef SYMBIAN_NETWORKING_PERFMETRICS
IncludePerformanceData(-1, -1, iSendByteCount);
#endif
if (iIsBound==EFalse)
{
if (State() == ESStateBinding)
{
SetReturn(KErrInUse);
return;
}
AutoBind();
}
iSendOffset=0;
DoSend(ETrue);
}
void ASocket::DoSend(TBool aInitialRequest)
{
AMessage* msg = aInitialRequest? iCurrentMsg: iWriteMsg;
ASSERT(msg);
TInt ret = KErrNone;
TBool completeReq = ETrue;
if (IsStream())
{
ret = FillStreamProtocol(msg);
if(iSendByteCount > 0)
{
// Still data to write - flow-off or error
if(ret != KErrNone)
{
if(ret == KErrNoMBufs)
{
ret = RequestAsyncMBufAllocation(CWaitForMBufs::ECanSend, iSendByteCount);
}
if(ret != KErrNone)
{
ret = KErrNoMemory;
}
}
if(ret == KErrNone)
{
if(iOptions & KOptBlocking)
{
completeReq = EFalse;
}
else if(iBlockedOperations & EWriteFlowedOff)
{
//it should return KErrWouldBlock instead of KErrNone
//but it returns KErrNone for backward compatibility.
iSendData.Free();
}
}
}
}
else // !IsStream()
{
if (aInitialRequest && ((iProtocolInfo->iMessageSize!=KSocketMessageSizeNoLimit) && (iProtocolInfo->iMessageSize!=KSocketMessageSizeUndefined) && (iSendByteCount>iProtocolInfo->iMessageSize)) ||
((iOptions&KOptSendBufSet) && iSendByteCount>iSendBufSize))
{
SetReturn(KErrTooBig);
return;
}
RMBufChain data;
TInt nwr = KErrNone;
TInt actualSendLen = 0; // total size of data accepted by protocol upon datagram write
TInt actualBufSize = 0;
if(aInitialRequest || iSendData.IsEmpty())
{
if (iSendUseMBufs)
{
ret = msg->ReadMBuf(MSG_PRM(2),data);
actualSendLen = data.Length();
actualBufSize = data.First()->Size();
}
else
{
// Possible outcomes:
// (1) We fail to create the MBuf chain with the client data:
// (a) with KErrNoMbufs - we start an asynchronous allocation and treat it as a flow-off
// (b) with some other -ve error code - we treat this as KErrNoMemory and complete the client
// (2) SAP fails to write the mbuf chain:
// (a) with KErrNoMbufs - we start an asynchronous allocation on its behalf and treat it as a flow-off
// (b) with zero, ie flow-off: we don't complete client
// (c) with some other -ve error code - we treat this as KErrNoMemory and complete the client
// (3) SAP write succeeds and returns > 1 - we complete the client with KErrNone
// get MBuf chain immediately, copy avoiding iSendBuf if possible
ret = data.Alloc(iSendByteCount + KPaddingForHeaders, iAllocator);
if(ret == KErrNone)
{
RMBuf* first = data.First();
if(first->Size() >= (iSendByteCount + KPaddingForHeaders))
{
// Read directly into buffer
first->SetData(KPaddingForHeaders, iSendByteCount);
TPtr8 des(NULL, 0);
des.Set(first->Ptr(), iSendByteCount, iSendByteCount);
ret = msg->ReadDes(MSG_PRM(2),des);
actualSendLen = des.Length();
actualBufSize = actualSendLen;
}
else
{
// Have to use local descriptor to avoid multiple IPC round trips
TDes8* sendBuf = BorrowTemporaryBuffer(iSendByteCount);
if(!sendBuf)
{
// We can't allocate a buffer as big as the client so risk losing data
// they've indicated as precious if it overflows. Failing immediately
// without trying is the compatible option
ret = KErrNoMemory;
}
else
{
if(first->Length() > KPaddingForHeaders)
{
first->AdjustStart(KPaddingForHeaders);
}
else
{
data.TrimStart(KPaddingForHeaders);
}
ret = msg->ReadDes(MSG_PRM(2), *sendBuf); // read failure exceedingly rare so don't bother checking return yet
actualSendLen = sendBuf->Length();
actualBufSize = actualSendLen;
data.CopyIn(*sendBuf);
}
}
}
}
}
else
{
// Resume with the RMBufChain we already fabricated
data.Assign(iSendData);
if (iSendUseMBufs)
{
actualSendLen = data.Length();
actualBufSize = data.First()->Size();
}
else
{
actualSendLen = data.Length();
actualBufSize = actualSendLen;
}
}
if (ret == KErrNone)
{
// Clear the error operation mask. If a protocol errors the socket whilst in the
// context of the CServProviderBase::Write(...) down call, we will see it below.
iErrorOperationMask = 0;
if (iSendToAddrIdx < 0)
{
nwr = iSSPData->Write(data, iSendFlags, NULL);
}
else
{
TSockAddr addr;
ret = msg->ReadDes(iSendToAddrIdx, addr);
if(ret == KErrNone)
{
nwr = iSSPData->Write(data, iSendFlags, &addr);
}
}
if (nwr == 0)
{
// Flow unable to accept data but not erroring
// WriteFlowedOff
if(iOptions & KOptBlocking)
{
completeReq = EFalse;
iSendData.Assign(data);
}
}
if (iErrorOperationMask & (MSessionControlNotify::EErrorSend))
{
// Protocol has errored the socket. Complete the current operation with the error.
// If the error is not fatal, then it is assumed to be for the current operation only,
// so clear down the error.
nwr = iError;
completeReq = ETrue;
ClearErrorIfNotFatal(); //Sets iError = 0;
iErrorOperationMask = 0;
iSendData.Free();
}
if(nwr < 0)
{
ret = nwr;
}
else if(iSendXferLenIdx >= 0)
{
iSendOffset = actualSendLen - data.Length();
}
if (!iSendUseMBufs)
{
// nwr > 0 == Sending using RMBufChain where we have to ensure the data is
// freed once the Send is succeeded
// Protocols must remove buffers they want to use
data.Free();
}
else
{
if (nwr > 0)
{
msg->InitMBuf(MSG_PRM(2));
}
}
}
if(ret != KErrNone)
{
if(ret == KErrNoMBufs)
{
ret = RequestAsyncMBufAllocation(CWaitForMBufs::ECanSend, actualBufSize);
if(ret != KErrNone)
{
ret = KErrNoMemory;
}
}
if(ret == KErrNone)
{
if(iOptions & KOptBlocking)
{
completeReq = EFalse;
}
else
{
ret = KErrWouldBlock;
}
}
}
}
if(completeReq)
{
// Write completed
if(iSendXferLenIdx >= 0)
{
iXferLength() = iSendOffset;
TInt lenRet = msg->WriteDes(iSendXferLenIdx, iXferLength);
if(lenRet != KErrNone && ret == KErrNone)
{
ret = lenRet;
}
}
// KErrBadDescriptor means that the client has already been panicked; we must
// avoid further completion of this message
if(ret == KErrBadDescriptor)
{
iBlockedOperations&=~EBlockedWrite;
}
else
{
if(aInitialRequest)
{
SetReturn(ret);
}
else
{
CompleteWrite(ret);
}
}
}
else
{
if(aInitialRequest)
{
// Doesn't matter if the client has already been panicked; we're acquiring the now-NULL msg
iWriteMsg->AcquireMessage(msg);
SetBlockedWrite();
iBlockedOperations|=EWriteFlowedOff;
DontCompleteCurrentRequest();
}
}
}
void ASocket::RecvL(TInt aXferLenArg, TInt aAddrArg, TInt aReadByteCount, TInt aReadFlags, TBool aUseMBufs, TInt aRecvOneOrMore)
/**
Common service routine from Recv, RecvFrom and Read.
*/
{
if (IsBlockedRead())
{
PanicSocketClient(EReadingAlready);
return;
}
if (!CheckRunningAndSetReturn())
{
return;
}
if (IsConnectionOriented())
{
if (State()!=ESStateConnected && State()!=ESStateShuttingDown)
{
LOG( ESockLog::Printf(_L("ASocket %08x RecvL() KErrNotReady"), this ) );
SetReturn(KErrNotReady);
return;
}
if (iIsBound==EFalse)
{
if (State() == ESStateBinding)
{
SetReturn(KErrInUse);
return;
}
AutoBind();
}
if (aAddrArg >= 0)
{
SetReturn(KErrNotSupported);
return;
}
}
iRecvOneOrMore = aRecvOneOrMore;
if(iRecvOneOrMore && !IsStream())
{
SetReturn(KErrNotSupported);
return;
}
// Set whether we are going to receive in RMBufChain way
iRecvUseMBufs = aUseMBufs;
iRecByteCount=aReadByteCount;
if(iRecByteCount<0)
{
PanicSocketClient(EBadDescriptorLength);
User::Leave(KErrBadDescriptor);
}
iRecvFlags=aReadFlags;
if(iRecvFlags)
{
if(iRecvFlags&KSocketInternalReadBit)
{
SetReturn(KErrAccessDenied);
return;
}
if (iRecvFlags&KSockReadPeek && !SupportsPeek())
{
SetReturn(KErrNotSupported);
return;
}
}
iRecvFromAddrIdx = aAddrArg;
iRecvXferLenIdx=aXferLenArg;
#ifdef SYMBIAN_NETWORKING_PERFMETRICS
IncludePerformanceData(-1, iRecByteCount, -1);
#endif
iRecOffset=0;
DoRecv(ETrue);
}
void ASocket::DoRecv(TBool aInitialRequest)
{
AMessage* msg = aInitialRequest? iCurrentMsg: iReadMsg;
if(aInitialRequest || IsBlockedRead())
{
TInt errReadProtocol = KErrNone;
TBool completeReq = ETrue;
if((iBlockedOperations&EReadStopped) && iDataAvailable<=0)
{
TInt ret = msg->WriteDes(MSG_PRM(2), KNullDesC8);
iRecOffset = 0;
errReadProtocol = KErrEof;
}
else if (IsStream())
{
errReadProtocol = DrainStreamProtocol(msg);
if(errReadProtocol == KErrNone)
{
if(iRecvOneOrMore && iRecOffset > 0) // i.e. data received
{
iRecByteCount=0;
}
if (iRecByteCount>0) // Still data expected
{
if(!(iBlockedOperations&EReadStopped) || iDataAvailable>0)
{
if ((iOptions&KOptBlocking))
{
// This error condition can never be reached under the current structure. Further investigation into how other teams,
// such as Bluetooth, handle memory errors in this situation is needed before restructuring this method.
//if(errReadProtocol == KErrNoMBufs)
// {
// // Block unless request fails
// errReadProtocol = RequestAsyncMBufAllocation(CWaitForMBufs::ENewData, iRecByteCount/*iRecBuf.MaxLength()*/) == KErrNone;
// completeReq = errReadProtocol == KErrNone;
// }
//else
if(iError != KErrNone)
{
// Don't block as protocol is already erroring client
errReadProtocol = iError;
// If the socket error is not fatal, then reset it so that only the current
// receive operation is completed with the error (not every subsequent one).
ClearErrorIfNotFatal();
}
else
{
completeReq = EFalse;
}
}
else
{
errReadProtocol = KErrWouldBlock;
}
}
else
{
errReadProtocol = KErrEof;
}
}
}
}
else
{ // !IsStream()
// If the previous call to Recv used the KSockReadContinuation method and left iDatagramTail empty (everything
// up to the end of the packet was read) then tailContinuation will be false this time round - this will cause
// the reading of new data from iSSP below.
TBool tailContinuation = (iRecvFlags & KSockReadContinuation) != 0 && !iDatagramTail.IsEmpty();
TBool needToBlock = !tailContinuation && iDataAvailable <= 0;
errReadProtocol = KErrNone;
if(aInitialRequest)
{
//-- if we have a read from the socket with flag KSockReadPeek set
//-- and if we need to block, set the NewData() recursion counter to 1.
//-- otherwise, reset it, it will not be checked then.
if(needToBlock && (iRecvFlags & KSockReadPeek))
{
iBlockOnPeekReadCnt = 1;
}
else
{
iBlockOnPeekReadCnt = 0;
}
}
if (!needToBlock)
{
TDes8* recBuf = BorrowTemporaryBuffer(iRecByteCount);
if(!recBuf)
{
// We can't allocate a buffer as big as the client so risk losing data
// they've indicated as precious if it overflows. Failing immediately
// without trying is the compatible option
errReadProtocol = KErrNoMemory;
}
// Only read from the provider if we can't use the previous tail
else if(!tailContinuation)
{
// Free it only if the chain is empty. For RMBufChain read, the receiveer must have been freed it.
// and we don't need to free it
if(!iDatagramTail.IsEmpty() && !iRecvUseMBufs)
{
iDatagramTail.Free();
}
if (iRecvFromAddrIdx < 0)
{
errReadProtocol = iSSPData->GetData(iDatagramTail, iRecByteCount | KGetDataWholeDatagram, iRecvFlags);
}
else
{
errReadProtocol = iSSPData->GetData(iDatagramTail, iRecByteCount | KGetDataWholeDatagram, iRecvFlags, &iRecvFromAddr);
}
}
// Protocols must either deliver the request or return an error and deliver nothing
//__ASSERT_DEBUG( (errReadProtocol < 0 && data.IsEmpty()), Panic(EBadDataCount));
TInt trimTailAmount = 0;
if(errReadProtocol < 0)
{
// Protocol has failed to deliver, so we'll have to block. If it's a KErrNoMBufs (a likely case) then we kick off
// an asynch allocation for amount and upon its completion try again. Of course we cannot know whether this is the
// number of mbufs that the protocol wanted but it's a good guess. If it's some other error then it's up to the
// protocol to tell us when to retry (by signalling with NewData(0)). If it was some error that the protocol
// couldn't handle at all then it should have set the error upon the socket rather than expecting us to magically
// "do the right thing"
needToBlock = ETrue;
}
else
{
if (iRecvUseMBufs)
{
// We don't trim anything, when we do a RMBufChain read with KSockReadPeek is set
// KSockReadPeek is upto the SAP shim
errReadProtocol = msg->WriteMBuf(MSG_PRM(2),iDatagramTail);
if(errReadProtocol == KErrNone)
{
iDatagramTail.Init();
// Just remove the chain that we have given back from the tail. When using MBufs the
// client always gets the entire datagram anyway (the datagram tail mechanism is there
// just so someone using a descriptor can do a continuation read if it overflows, so they
// don't have to use 64k buffer just in case some loony soaks them with a jumbogram).
// So there's no trimming done & no need to discriminate between peek & normal.
}
}
else
{
TPtr8 des(recBuf->MidTPtr(0, iRecByteCount));
if (!iDatagramTail.IsEmpty())
{
// intermediate descriptor copy
RMBuf* first = iDatagramTail.First();
if(!first->Next())
{
TInt copyLen = Min(first->Length(), iRecByteCount);
des.Set(first->Ptr(), copyLen, copyLen);
}
else
{
iDatagramTail.CopyOut(des, 0);
}
}
else
{
des.SetLength(0);
}
errReadProtocol = msg->WriteDes(MSG_PRM(2),des);
if((errReadProtocol == KErrNone && iRecvFlags & KSockReadPeek) == 0)
{
// Note the amount of data to trim after writing it to the client
trimTailAmount = des.Length();
}
}
#ifdef SYMBIAN_NETWORKING_PERFMETRICS
IncludePerformanceData(trimTailAmount, -1, -1);
#endif
if(errReadProtocol == KErrNone)
{
if (trimTailAmount != 0)
{
iDatagramTail.TrimStart(trimTailAmount);
}
if(iRecvXferLenIdx >= 0)
{
// Number of bytes remaining. (store for later write)
iRecOffset = iDatagramTail.Length();
}
if(iRecvFromAddrIdx >= 0)
{
errReadProtocol = msg->WriteDes(iRecvFromAddrIdx, iRecvFromAddr);
}
if(errReadProtocol == KErrNone && !tailContinuation)
{
// When using a non-stream based socket the value iDataAvailable refers to the number of packets available.
// In the case where tail continuation is in use the packet may not fully have been dealt with yet (more
// could be read from it next time) and so the value should not be decremented yet. This is the case even
// if all the data up to the end of the packet has been read (the above call to TrimStart will leave
// iDatagramTail empty and then on the next call of this method tailContinuation will be set to false).
iDataAvailable--;
}
}
}
}
if(needToBlock)
{
if (iOptions & KOptBlocking)
{
if (errReadProtocol == KErrNoMBufs)
{
// Block unless request fails
needToBlock = RequestAsyncMBufAllocation(CWaitForMBufs::ENewData, iRecByteCount) == KErrNone;
}
else if (iError != KErrNone)
{
// Don't block as protocol is already erroring client
needToBlock = EFalse;
errReadProtocol = iError;
// If the socket error is not fatal, then reset it so that only the current
// receive operation is completed with the error (not every subsequent one).
ClearErrorIfNotFatal();
}
else if (errReadProtocol != KErrNone)
{
needToBlock = EFalse;
}
completeReq = !needToBlock;
}
else
{
errReadProtocol = KErrWouldBlock;
}
}
}
if(completeReq)
{
// Read completed
if(iRecvXferLenIdx >= 0)
{
iXferLength() = iRecOffset;
TInt ret = msg->WriteDes(iRecvXferLenIdx, iXferLength);
if(ret != KErrNone && errReadProtocol == KErrNone)
{
errReadProtocol = ret;
}
}
// KErrBadDescriptor means that the client has already been panicked; we must
// avoid further completion of this message
if(errReadProtocol == KErrBadDescriptor)
{
iBlockedOperations&=~EBlockedRead;
}
else
{
if(aInitialRequest)
{
SetReturn(errReadProtocol);
}
else
{
CompleteRead(errReadProtocol);
}
}
}
else
{
if(aInitialRequest)
{
// Doesn't matter if the client has already been panicked; we're acquiring the now-NULL msg
iReadMsg->AcquireMessage(msg);
SetBlockedRead();
DontCompleteCurrentRequest();
}
}
}
TryToCompleteSelectIoctl();
}
void ASocket::ListenL(TInt aQlen, TBool aConnectionData)
/**
Listen request service
Listen is a dead and, clients can't do anything with the socket other than Accept after a listen.
*/
{
switch (State())
{
case ESStateConnected:
case ESStateOpeningActive:
case ESStateBinding:
SetReturn(KErrInUse);
return;
case ESStateOpeningPassive:
PanicSocketClient(EAlreadyListening);
//lint -fallthrough
case ESStateShuttingDown:
case ESStateClosing:
case ESStateDead:
SetReturn(KErrBadHandle);
return;
case ESStateDisconnected:
SetReturn(KErrDisconnected);
return;
case ESStateError:
SetReturn(iError);
return;
case ESStateNull:
case ESStateAccepting:
PanicSocketClient(ESockBadHandle);
return;
case ESStateCreated:
case ESStateOpen:
default:
break;
}
if (!IsConnectionOriented())
{
PanicSocketClient(EListenNeedsConnection);
return;
}
if (!iIsBound)
{
SetReturn(KErrBadName);
return;
}
if(!iAcceptQ)
{
iAcceptQ = new (ELeave) CCirBuf<TAcceptQEntry>;
}
CleanupStack::PushL(iAcceptQ);
iAcceptQ->SetLengthL(aQlen);
// Create a second queue we can transfer the contents to if we need to remove an SAP
// from the middle of the ring buffer.
if(!iNextAcceptQ)
{
iNextAcceptQ = new (ELeave) CCirBuf<TAcceptQEntry>;
}
CleanupStack::PushL(iNextAcceptQ);
iNextAcceptQ->SetLengthL(aQlen);
TInt ret;
if (aConnectionData)
{
TDes8& xferBuf = *BorrowTemporaryBufferL(iSendBufSize);
xferBuf.SetLength(iSendBufSize);
ReadParamL(ESocketCurrentMessage, MSG_PRM(1), xferBuf);
ret=iSSP->PassiveOpen(aQlen, xferBuf);
}
else
{
ret=iSSP->PassiveOpen(aQlen);
}
if (ret==KErrNone)
{
SetState(ESStateOpeningPassive);
}
else
{
SetReturn(ret);
}
CleanupStack::Pop(iNextAcceptQ);
CleanupStack::Pop(iAcceptQ);
}
void ASocket::Accept()
/**
Accept a connection on a listening socket.
*/
{
if ( State()!=ESStateOpeningPassive ) // protocol may have generated an error on the accept
{ // SAP for some bizzare reason. We can't test just (State()==ESStateError)
// 'cos if reconnection iState could be whatever (see ASocket::Error)
//so check if we'r listenning at all
SetReturn(iError != KErrNone ? iError : KErrNotReady);
return;
}
if ( !iAcceptQ || !iNextAcceptQ )
{
PanicSocketClient(ENotListening);
return;
}
if (IsBlockedConnect())
{
PanicSocketClient(EAcceptTwice);
return;
}
ASocket* acceptingSocket = InitiateAcceptingSocket();
if ( !acceptingSocket ) // The acceptingSocket will be NULL if we got a panic. return here.
return;
if (!iAcceptQ->Count())
{
if (!(iOptions & KOptBlocking))
{
SetReturn(KErrWouldBlock);
return;
}
SetBlockedConnect();
DontCompleteCurrentRequest();
return;
}
// If the queue isn't empty setting the blocked connect and calling DoCompleteAccept will work it's magic - this saves us code
SetBlockedConnect();
DoCompleteAccept();
DontCompleteCurrentRequest(); // Don't want to complete twice.
}
void ASocket::LocalNameL()
/**
Get local name for a client request
*/
{
if (!CheckRunningAndSetReturn())
{
return;
}
if (!iIsBound)
{
SetReturn(KErrNotFound);
return;
}
else
{
TSockAddr addr;
iSSP->LocalName(addr);
WriteParamL(ESocketCurrentMessage,MSG_PRM(0),addr);
}
}
void ASocket::RemoteNameL()
/**
Get remote name for a cleient request
*/
{
if (!CheckRunningAndSetReturn())
{
return;
}
if (State()==ESStateConnected || State()==ESStateOpeningActive || State()==ESStateShuttingDown)
{
TSockAddr addr;
iSSP->RemName(addr);
WriteParamL(ESocketCurrentMessage,MSG_PRM(0),addr);
}
else
{
SetReturn(KErrNotFound);
return;
}
}
void ASocket::NewData(TUint aCount)
/**
Called from a protocol to indicate that new data is available for reading
*/
{
__ASSERT_DEBUG(State()!=ESStateDead,Panic(EBadStateUpCall));
// __ASSERT_DEBUG(State()!=ESStateCreated,Panic(EBadStateUpCall)); // no longer forced into shuttingdown state upon half-close
__ASSERT_DEBUG(State()!=ESStateClosing,Panic(EBadStateUpCall));
__ASSERT_DEBUG(!(iBlockedOperations&EReadStopped),Panic(EBadStateUpCall));
__ASSERT_DEBUG(State()!=ESStateError,Panic(EBadStateUpCall));
__ASSERT_DEBUG(State()!=ESStateDisconnected,Panic(EBadStateUpCall));
// zero is used in v1.5 to resume after reneging __ASSERT_DEBUG(aCount>0,Panic(EBadDataCount));
if(aCount==KNewDataEndofData)
{
iBlockedOperations |= EReadStopped;
}
else
{
iDataAvailable+=aCount;
__ASSERT_DEBUG(iDataAvailable>=0,Panic(EBadDataCount));
}
//-- check the recursion level counter.
//-- if it is 0, there were no recursion entry to itself, continue.
//-- if it is 1, there was blocked read from socket with KSockReadPeek set, increase it and continue.
//-- if it is 2, it means that function has been called from itself via NewData()-> [iSSP->CetData()] -> NewData()
//-- return to stop the infinite recursion.
if(iBlockOnPeekReadCnt >0)
{
if(iBlockOnPeekReadCnt >= 2)
{
iBlockOnPeekReadCnt = 0;
return;
}
else
{
iBlockOnPeekReadCnt ++;
}
}
DoRecv(EFalse);
}
void ASocket::CanSend()
/**
Called from protocol to indicate new buffer space has become available
*/
{
__ASSERT_DEBUG(State()!=ESStateDead,Panic(EBadStateUpCall));
__ASSERT_DEBUG(State()!=ESStateCreated,Panic(EBadStateUpCall));
__ASSERT_DEBUG(State()!=ESStateClosing,Panic(EBadStateUpCall));
__ASSERT_DEBUG(State()!=ESStateShuttingDown,Panic(EBadStateUpCall));
__ASSERT_DEBUG(State()!=ESStateError,Panic(EBadStateUpCall));
__ASSERT_DEBUG(State()!=ESStateDisconnected,Panic(EBadStateUpCall));
TBool writeflowedoff = iBlockedOperations&EWriteFlowedOff;
iBlockedOperations &= ~EWriteFlowedOff;
if(IsBlockedWrite())
{
DoSend(EFalse);
}
if(writeflowedoff && !(iBlockedOperations&EWriteFlowedOff))
{
TryToCompleteSelectIoctl();
}
}
void ASocket::ConnectComplete()
/**
Called from protocol to indicate that an active open has completed
*/
{
//__ASSERT_DEBUG(State()==ESStateOpeningActive,Panic(EBadStateUpCall));
if (IsConnectionOriented())
{
SetState(ESStateConnected);
//__ASSERT_DEBUG(IsBlockedConnect(), Panic(EUnexpectedConnect));
// assertion is invalid because operation could have been cancelled MarkT
CompleteConnect(KErrNone);
}
TryToCompleteSelectIoctl();
}
void ASocket::ConnectComplete(const TDesC8& aConnectData)
/**
Called from protocol to indicate that an active open has completed with connection data
*/
{
__ASSERT_ALWAYS(CanSendConnectData(),Panic(EUnexpectedConnectData));
//__ASSERT_DEBUG(IsBlockedConnect(),Panic(EUnexpectedConnect));
// assertion is invalid because operation could have been cancelled
if ( !iBlockedConnectMsg->IsNull (MSG_PRM(2)) )
{
TInt ret = iBlockedConnectMsg->WriteDes(MSG_PRM(2),aConnectData);
if(ret!= KErrNone)
{
return;
}
}
ConnectComplete();
}
void ASocket::ConnectComplete(CSubConnectionFlowBase& anSSP)
/**
Called from protocol to indicate that a passive open has completed
*/
{
__ASSERT_DEBUG(State()==ESStateOpeningPassive,Panic(EBadStateUpCall));
__ASSERT_ALWAYS(iAcceptQ, Panic(ENotListeningSocket));
__ASSERT_ALWAYS(iAcceptQ->Count()<iAcceptQ->Length(), Panic(EAcceptQueFull));
TAcceptQEntry n;
n.iSSP = &anSSP;
n.iConnectData=NULL;
n.iConnectDataError=KErrNone;
iAcceptQ->Add(&n);
DoCompleteAccept();
//LOG( ESockLog::Printf(_L("ASocket::ConnectComplete iAcceptQ->=%d, provider =%u)"), iAcceptQ->Count(), (void*)&anSSP ) );
TryToCompleteSelectIoctl();
}
void ASocket::ConnectComplete(CSubConnectionFlowBase& anSSP,const TDesC8& aConnectData)
/**
Called from protocol to indicate that a passive open has completed with connection data
*/
{
__ASSERT_ALWAYS(CanSendConnectData(),Panic(EUnexpectedConnectData));
__ASSERT_ALWAYS(iAcceptQ, Panic(ENotListeningSocket));
__ASSERT_ALWAYS(iAcceptQ->Count()<iAcceptQ->Length(), Panic(EAcceptQueFull));
TAcceptQEntry n;
n.iSSP = &anSSP;
n.iConnectDataError = KErrNone;
n.iConnectData=aConnectData.Alloc();
if(!n.iConnectData)
{
n.iConnectDataError = KErrNoMemory;
}
iAcceptQ->Add(&n);
DoCompleteAccept();
TryToCompleteSelectIoctl();
}
void ASocket::DoCompleteAccept()
/**
Common dequeue for accept upcalls
*/
{
__ASSERT_DEBUG(iAcceptQ->Count(),Fault(EBadAcceptQueue));
if (!IsBlockedConnect())
{
return;
}
TAcceptQEntry a;
iAcceptQ->Remove(&a);
ASocket* newCon=GetAcceptingSocket();
CTransportFlowShim* shimFlow = factoryobject_cast<CTransportFlowShim>(a.iSSP);
__ASSERT_DEBUG(shimFlow, User::Panic(KSpecAssert_ESockSSocksntsck, 6));
// The SAP is no longer owned by the listener socket.
shimFlow->iListenerControlNotify = NULL;
if (newCon == NULL)
{
// Delete the flow created for accept
shimFlow->InitDestroy();
CompleteConnect(KErrCancel);
return;
}
TRAPD(ret, newCon->AcceptSetupL(*this,*a.iSSP));
//!!!there was a defect where by deleting iSSP here the TCP PRT would crash. Steve Butler
//fixed it => check it snce aiSSP would be deleted had AcceptSetupL left!!!
if (ret != KErrNone)
{
CompleteConnect(ret);
return;
}
/* goes into AcceptSetupL vitual fn
if (newCon->State()!=ESStateAccepting && newCon->State()!=ESStateNull)
{
//Session()->PanicSocketClient(iBlockedConnect,KESockClientPanic,EBadAccept);
CWorkerSession::PanicSocketClient(iBlockedConnect,KESockClientPanic,EBadAccept);
iSession->DontCompleteCurrentRequest();
return;
}
*/
newCon->SetState(ESStateConnected);
if (a.iConnectData)// && iBlockedConnect.Ptr2()) checked in WriteParam
{
TPtr8 des=a.iConnectData->Des();
ret = iBlockedConnectMsg->WriteDes(MSG_PRM(2),des,0);
if(ret!= KErrNone)
{
return;
}
}
delete a.iConnectData;
// Call SecurityCheck() on the new SAP to communicate the MProvdSecurityChecker
// pointer to it. It is a somewhat bizarre situation if the PRT returns an error
// to this as, in reality, the PRT should not make a ConnectComplete() upcall in the
// first place only to fail the subsequent SecurityCheck() downcall. The PRT should check
// security beforehand and, on failure, simply not call ConnectComplete(). However, if we
// get an error, then complete the RSocket::Accept() operation on the listening socket with
// the error and place the accept socket in the error state.
ret = newCon->SecurityCheck();
if (ret != KErrNone)
{
LOG( ESockLog::Printf(_L("ASocket %08x:\tDoCompleteAccept(): socket %08x returned security check error %d"), this, a.iSSP, ret ) );
a.iConnectDataError = ret;
newCon->Error(ret, MSessionControlNotify::EErrorFatal);
}
newCon->iSSP->Start();
CompleteConnect(a.iConnectDataError);
}
void ASocket::CanClose(MSessionControlNotify::TDelete aDelete)
/**
Called from protocol to indicate that a graceful close has completed
*/
{
(void)aDelete;
//__ASSERT_ALWAYS(IsBlockedClose(),Panic(EUnexpectedClose));
// assertion is invalid because operation could have been cancelled MarkT
__ASSERT_DEBUG(State()==ESStateClosing||State()==ESStateShuttingDown,Panic(EBadStateUpCall));
CompleteClose(KErrNone);
if (iFlowBinder)
{
iFlowBinder->Unbind();
}
// Flowbinder takes care of itself from now onwards; forget all downwards pointers
iSSP = NULL;
iSSPData = NULL;
iFlowBinder = NULL;
iServiceProvider.Close();
/* Flow binder takes care of this SAP behaviour
if(aDelete==MSessionControlNotify::EDetach)
{
iSSP = NULL; // Stop d'tor deleting the SSP.
iFlowBinder = NULL;iSSPData = NULL;
}
*/
if (State()==ESStateClosing)
{
SetState(ESStateDead);
// delete this; must not access member data after executing this statement
InitiateDestruction();
}
else
{
SetState(ESStateDead);
}
}
void ASocket::CanClose(const TDesC8& aDisconnectData, MSessionControlNotify::TDelete aDelete)
/**
Called from protocol to indicate that a graceful close has completed with disconnect data
*/
{
//__ASSERT_ALWAYS(IsBlockedClose(),Panic(EUnexpectedClose));
// assertion is invalid because operation could have been cancelled MarkT
// or could be non-blocking close
__ASSERT_ALWAYS(CanSendDisconnectData(),Panic(EUnexpectedDisconnectData));
TInt ret = iBlockedCloseMsg->WriteDes(MSG_PRM(2),aDisconnectData);
if(ret!= KErrNone)
{
return;
}
CanClose(aDelete);
}
void ASocket::Disconnect()
/**
Called from protocol to indicate that a disconnect has been initiated - prob from the remote host
*/
{
__ASSERT_ALWAYS(IsConnectionOriented(),Panic(EBadDisconnect));
__ASSERT_DEBUG(State()==ESStateConnected || State()==ESStateShuttingDown,Panic(EBadStateUpCall));
iDisconnectDataError=KErrNone;
SetState(CanReconnect() ? ESStateCreated : ESStateDisconnected);
CompleteWrite(KErrDisconnected);
CompleteRead(KErrDisconnected);
CompleteConnect(KErrDisconnected);
TryToCompleteSelectIoctl();
}
void ASocket::Disconnect(TDesC8& aDisconnectData)
/**
Called from protocol to indicate that a disconnect has been initiated - prob from the remote host - with disconnect data
*/
{
__ASSERT_DEBUG(IsConnectionOriented(),Panic(EBadDisconnect));
__ASSERT_DEBUG(State()==ESStateConnected || State()==ESStateShuttingDown,Panic(EBadStateUpCall));
Disconnect();
iDisconnectData=aDisconnectData.Alloc();
if(!iDisconnectData)
{
iDisconnectDataError = KErrNoMemory;
}
else
{
iDisconnectDataError = KErrNone;
}
}
void ASocket::DisconnectFromListener(CSubConnectionFlowBase& aSSP)
/**
Called from protocol to indicate that a disconnect has been initiated for an SAP which has not been
accepted yet and is waiting in the accept queue of a listening socket
*/
{
#ifdef _DEBUG
TBool found = EFalse;
#endif
while(iAcceptQ->Count())
{
// Remove the entry from the old queue.
TAcceptQEntry a;
iAcceptQ->Remove(&a);
// Add it to the new queue if it is not the one we want to remove.
if(a.iSSP != &aSSP)
{
iNextAcceptQ->Add(&a);
}
#ifdef _DEBUG
else
{
found = ETrue;
}
#endif
}
// Swap the queues.
CCirBuf<TAcceptQEntry>* temp = iNextAcceptQ;
iNextAcceptQ = iAcceptQ;
iAcceptQ = temp;
__ASSERT_DEBUG(found,Panic(EBadDisconnectFromListener));
}
void ASocket::IoctlComplete(TDesC8 *aBuf)
/**
Called from protocol to indicate that an outstanding ioctl has completes
*/
{
if (aBuf && !iBlockedIoctlMsg->IsNull (MSG_PRM(1)))
{
TInt ret = iBlockedIoctlMsg->WriteDes(MSG_PRM(1),*aBuf);
if(ret!= KErrNone)
{
return;
}
}
CompleteIoctl(KErrNone);
}
void ASocket::SetLocalNameComplete()
/**
Called from protocol to indicate that an outstanding Bind has completed
*/
{
if (State() == ESStateBinding)
{
// A SetLocalName or Autobind had just completed.
SetState(iNextState); // Move to next state.
iIsBound=ETrue;
switch(iNextState)
{
case ESStateCreated:
case ESStateOpen:
CompleteSetLocalName(KErrNone);
break;
case ESStateConnected:
// UDP case
TryToCompleteSelectIoctl();
CompleteConnect(KErrNone);
break;
case ESStateOpeningActive:
if (CanSendConnectData() && iConnectData)
{
TDes8* sendBuf = BorrowTemporaryBuffer(KMaxStreamChunk);
TInt ret;
if(!sendBuf)
{
ret = KErrNoMemory;
}
else
{
ret = GetUserMessage(ESocketConnectMessage)->ReadDes(MSG_PRM(1), *sendBuf);
}
if (ret == KErrNone)
{
iSSP->ActiveOpen(*sendBuf);
}
else
{
CompleteConnect(ret);
}
}
else
{
iSSP->ActiveOpen();
}
break;
default:
break;
}
}
}
void ASocket::CancelConnect()
{
if (State()==ESStateDead)
{
PanicSocketClient(ESockBadHandle);
return;
}
CompleteConnect(KErrCancel);
iSSP->Shutdown(MSessionControl::EImmediate);
if (CanReconnect())
{
// Reset socket state
if (IsConnectionOriented())
{
SetState(ESStateCreated);
}
else
{
SetState(ESStateOpen);
}
}
else
{
SetState(ESStateError);
}
}
void ASocket::CancelAccept()
{
CompleteConnect(KErrCancel);
}
void ASocket::CancelAll()
/**
Cancel All pending requests
*/
{
CancelIoctl();
CompleteWrite(KErrCancel);
CompleteRead(KErrCancel);
if (IsBlockedConnect())
{
if (State()==ESStateOpeningPassive)
{
CancelAccept();
}
else
{
CancelConnect();
}
}
CompleteClose(KErrCancel);
CompleteSetLocalName(KErrCancel);
}
void ASocket::GetInfoL()
/**
Get protocol info for this socket
*/
{
if (State()==ESStateNull || State()==ESStateAccepting)
{
SetReturn(KErrNotFound);
return;
}
__ASSERT_DEBUG(iProtocolInfo, User::Panic(KSpecAssert_ESockSSocksntsck, 7));
TPckg<TProtocolDesc> p(*iProtocolInfo);
WriteParamL(ESocketCurrentMessage,MSG_PRM(0),p,0);
}
TInt ASocket::Error(TInt aError, TUint aOperationMask)
/**
Error up call from SSP, carries a bitmask indicating exactly which operations to signal the error on.
*/
{
#if defined(SYMBIAN_NETWORKING_LEGACY_COMPATIBILITY_SUPPORT)
// Support for the IP stack's just-in-time resetting of a shutdown socket's state is is provided by allowing it
// to notify via a pseudo-error. This is all in support of regrettable behaviour that after a shutdown TCP and UDP
// sockets must complete reads & writes with a different error code to all other sockets.
if(aOperationMask == MSessionControlNotify::EErrorLegacySupportRequest)
{
SetState((TSocketState) aError);
return KErrNone;
}
#endif
if (aOperationMask == MSessionControlNotify::EErrorCompleteConnect)
{
CompleteConnect(aError);
}
else
{
DoError(aError, aOperationMask, ETrue);
}
return State() == ESStateError ? KErrGeneral : KErrNone;
}
void ASocket::DoError(TInt aError, TUint aOperationMask, TBool /*aDeleteInterface*/)
{
__ASSERT_DEBUG(iState!=ESStateDead,Panic(EBadStateUpCall));
// __ASSERT_DEBUG(iState!=ESStateError,Panic(EBadStateUpCall));
//__ASSERT_DEBUG(iState!=ESStateDisconnected,Panic(EBadStateUpCall));
//__ASSERT_DEBUG(anError!=KErrNone,Panic(EBadErrorCall));
// completionMask remembers whether there were any pending operations that were not completed,
// and whether the error is fatal (i.e. the socket permanently returns an error).
TUint completionMask = aOperationMask & MSessionControlNotify::EErrorFatal;
if (aOperationMask & MSessionControlNotify::EErrorClose)
{
if (!CompleteClose(aError))
{
completionMask |= MSessionControlNotify::EErrorClose;
}
}
if (aOperationMask & MSessionControlNotify::EErrorRecv)
{
if (!CompleteRead(aError))
{
completionMask |= MSessionControlNotify::EErrorRecv;
}
}
if (aOperationMask & MSessionControlNotify::EErrorSend)
{
if (!CompleteWrite(aError))
{
completionMask |= MSessionControlNotify::EErrorSend;
}
}
if ((aOperationMask&MSessionControlNotify::EErrorIoctl) && !iSelectFlags)
{
CompleteIoctl(aError);
}
if (aOperationMask & MSessionControlNotify::EErrorCompleteSetLocalName)
{
CompleteSetLocalName(aError);
}
iError = aError;
if (completionMask)
{
iErrorOperationMask = aOperationMask & completionMask;
}
if (State() == ESStateBinding && ((iNextState == ESStateCreated) || (iNextState == ESStateOpen)))
{
// If SetLocalName in progress then do not put the socket to
// a sticky error state.
iError = KErrNone;
// Reset socket state.
if (!IsConnectionOriented())
{
SetState(ESStateOpen);
}
else
{
SetState(ESStateCreated);
}
}
else
if (aOperationMask & MSessionControlNotify::EErrorConnect || aOperationMask&MSessionControlNotify::EErrorFatal)
{
CompleteConnect(aError);
// KErrNoMemory not treated as a fatal error
if (aError == KErrBindersInvalid || (!CanReconnect() && aError != KErrNoMemory))
{
// Fatal error
iError = aError;
SetState(ESStateError);
}
else
{
// Reset socket state
if (!IsConnectionOriented())
{
SetState(ESStateOpen);
}
else
{
SetState(ESStateCreated);
}
}
}
if (iError == KErrBindersInvalid)
{
__ASSERT_DEBUG(iFlowBinder, User::Panic(KSpecAssert_ESockSSocksntsck, 8));
iFlowBinder->Unbind();
iFlowBinder = NULL;
iSSP = NULL;
iSSPData = NULL;
iServiceProvider.Close();
}
TryToCompleteSelectIoctl();
}
TInt ASocket::DrainStreamProtocol(AMessage* aMsg)
/**
Drain a stream based protocol into client buffers.
*/
{
__ASSERT_DEBUG(iDataAvailable>=0,Panic(EBadByteCount));
__ASSERT_DEBUG(iRecByteCount>=0,Panic(EBadByteCount));
TDes8* recBuf = NULL;
if(!iRecvUseMBufs)
{
TInt xferBufSize = Min((TInt) KMaxStreamChunk, Min(iDataAvailable, iRecByteCount));
recBuf = BorrowTemporaryBuffer(xferBufSize);
if(!recBuf)
{
return KErrNoMemory;
}
}
#ifdef SYMBIAN_NETWORKING_PERFMETRICS
TInt rxActual = 0;
#endif
RMBufChain data;
TInt ret = KErrNone;
do
{
TInt xfrLen;
if (iRecvUseMBufs)
{
// We are using RMBufChain. So we can read the complete data that is available
xfrLen = iDataAvailable;
}
else
{
xfrLen = Min(recBuf->MaxLength(), Min(iDataAvailable, iRecByteCount));
}
if (xfrLen==0)
{
return KErrNone;
}
RMBufChain data;
ret = iSSPData->GetData(data, xfrLen, iRecvFlags);
if (ret == 0 && iBlockedOperations&EReadStopped)
{
/* Look for the special case, where the SSP renegs on the amount of
* data available after an EStopInput Shutdown, as that can stop us
* ever exiting this while loop. */
iDataAvailable = 0;
return KErrEof;
}
if(ret >= 0)
{
// Now we received something.
if (iRecvUseMBufs)
{
ret = aMsg->WriteMBuf(MSG_PRM(2), data );
// No need to delete as the write operation has taken the ownership of our RMBufChain
}
else
{
// Default assumption is multi-buf chain
TPtr8 des(const_cast<TUint8*>(recBuf->Ptr()), xfrLen, xfrLen);
if(!data.First()->Next())
{
// Actually data is all in one buf; repoint descriptor at that
des.Set(data.First()->Ptr(), data.First()->Length(), data.First()->Length());
}
else
{
data.CopyOut(des, 0);
}
xfrLen = des.Length();
ret = aMsg->WriteDes(MSG_PRM(2), des, iRecOffset);
if ( ret == KErrNone )
{
data.Free(); // Free the chain
}
}
if(ret == KErrNone)
{
#ifdef SYMBIAN_NETWORKING_PERFMETRICS
rxActual += ipcLen;
#endif
iRecOffset += xfrLen;
iDataAvailable -= xfrLen;
iRecByteCount -= xfrLen;
}
}
else if(xfrLen <= 0)
{
// Failed to read or write on this iteration; we're done
break;
}
} while(ret == KErrNone);
#ifdef SYMBIAN_NETWORKING_PERFMETRICS
IncludePerformanceData(rxActual, -1, -1);
#endif
return ret;
}
TInt ASocket::FillStreamProtocol(AMessage* aMsg)
/**
Fill a stream protocol from client buffers
*/
{
__ASSERT_DEBUG(iSendByteCount>=0,Panic(EBadByteCount));
TPtr8 des(NULL, 0);
TDes8* sendBuf;
if(!iSendUseMBufs)
{
sendBuf = BorrowTemporaryBuffer(Min((TInt) KMaxStreamChunk, iSendByteCount));
if(!sendBuf)
{
return KErrNoMemory;
}
}
else
{
sendBuf = NULL;
}
RMBufChain data;
data.Assign(iSendData); // pick up any flowed-off chain
TInt xfrLen = data.Length();
do
{
TInt err = KErrNone;
if(data.IsEmpty())
{
// Need to pick up more data from client
if (!iSendUseMBufs)
{
xfrLen = Min(sendBuf->Size(), iSendByteCount);
err = data.Alloc(xfrLen, iAllocator);
if(err == KErrNone)
{
RMBuf* first = data.First();
if(first->Length() >= xfrLen)
{
des.Set(first->Ptr(), xfrLen, xfrLen);
err = aMsg->ReadDes(MSG_PRM(2),des, iSendOffset);
}
else
{
des.Set(const_cast<TUint8*>(sendBuf->Ptr()), xfrLen, xfrLen);
err = aMsg->ReadDes(MSG_PRM(2), des, iSendOffset);
data.CopyIn(des); // read failure exceedingly rare so don't bother checking return yet
}
if(err != KErrNone)
{
data.Free();
}
}
}
else
{
err = aMsg->ReadMBuf(MSG_PRM(2), data);
if(err == KErrNone)
{
xfrLen = data.Length();
}
}
}
else
{
xfrLen = data.Length();
}
if(err != KErrNone)
{
// Failed getting data from the client; bail now without further disturbing the state
return err;
}
err = iSSPData->Write(data, iSendFlags, NULL);
// If a protocol takes only part of the chain they must update it so that we have the new head
__ASSERT_DEBUG(err <= 0 || err == (xfrLen - data.Length()), Panic(EBadByteCount));
if(err <= 0) // protocol error or flow-off
{
if(err == 0)
{
iSendData.Assign(data); // keep the MBuf chain for flow-on. In future we could be cleverer and later free this when MBufs are running low
iBlockedOperations|=EWriteFlowedOff;
}
else
{
data.Free();
}
return err;
}
iSendOffset += err;
iSendByteCount -= err;
} while(iSendByteCount > 0);
// We successfully completed.
if ( iSendUseMBufs )
{
// Protection against possible non-deletion by SAP
data.Free ();
// We have to init the RMBufChain, so that caller see that everything send
aMsg->InitMBuf(MSG_PRM(2));
}
return KErrNone;
}
TBool ASocket::CheckRunningAndSetReturn()
/**
Check a common set of states and set the return value is required.
*/
{
switch(State())
{
case ESStateNull:
case ESStateAccepting:
PanicSocketClient(ENullSocket);
//lint -fallthrough
case ESStateDead:
case ESStateClosing:
SetReturn(KErrBadHandle);
return EFalse;
case ESStateDisconnected:
SetReturn(KErrDisconnected);
return EFalse;
case ESStateError:
SetReturn(iError);
return EFalse;
default:
return ETrue;
}
}
/**
Complete any outstanding read operation.
@param aError error code with which to complete the pending operation
@return ETrue if a pending operation was completed, else EFalse.
*/
TBool ASocket::CompleteRead(TInt aError)
{
if (IsBlockedRead())
{
if(iAllocAsync)
{
iAllocAsync->CancelNewData();
}
iBlockedOperations&=~EBlockedRead;
iReadMsg->CompleteMessage(aError);
return ETrue;
}
else
{
return EFalse;
}
}
/**
Complete any outstanding write operation.
@param aError error code with which to complete the pending operation
@return ETrue if a pending operation was completed, else EFalse.
*/
TBool ASocket::CompleteWrite(TInt aError)
{
if (IsBlockedWrite())
{
if(iAllocAsync)
{
iAllocAsync->CancelCanSend();
}
iBlockedOperations&=~EBlockedWrite;
iWriteMsg->CompleteMessage(aError);
iSendData.Free();
return ETrue;
}
else
{
return EFalse;
}
}
void ASocket::CompleteConnect(TInt aError)
{
if (IsBlockedConnect())
{
iBlockedOperations&=~EBlockedConnect;
iBlockedConnectMsg->CompleteMessage(aError);
}
}
TBool ASocket::CompleteClose(TInt aError)
{
if (IsBlockedClose())
{
iBlockedOperations&=~EBlockedClose;
iBlockedCloseMsg->CompleteMessage(aError);
return ETrue;
}
return EFalse;
}
void ASocket::CompleteIoctl(TInt aError)
{
if (IsBlockedIoctl())
{
iBlockedOperations&=~EBlockedIoctl;
iBlockedIoctlMsg->CompleteMessage(aError);
// LOG(ESockLog::Printf(KESockSessDetailTag, _L8("ASocket::CompleteIoctl completing %08x "), iBlockedIoctl.Handle()) );
}
}
void ASocket::CompleteSetLocalName( TInt aError )
{
if (IsBlockedSetLocalName())
{
// SetLocalName has completed.
iBlockedOperations &= ~EBlockedSetLocalName;
iBlockedSetLocalNameMsg->CompleteMessage(aError);
}
}
TUint ASocket::SelectConditionsReady()
/**
Which select conditions are ready
*/
{
TUint flags=0;
// Exceptional condition
if(iError!=KErrNone)
{
flags|=KSockSelectExcept;
}
// Ready to read - either data or an accept is ready
if(iDataAvailable
|| ((iSelectFlags & KSockReadContinuation) != 0 && !iDatagramTail.IsEmpty())
|| (State()==ESStateOpeningPassive && iAcceptQ->Count())
|| (iBlockedOperations&EReadStopped))
{
flags|=KSockSelectRead;
}
// Ready to write - must be connected and cannot be blocking
if((State()==ESStateConnected || State()==ESStateOpen || State()==ESStateShuttingDown)
&& !(iBlockedOperations&EWriteFlowedOff) )
{
flags|=KSockSelectWrite;
}
return flags;
}
void ASocket::TryToCompleteSelectIoctl()
/**
have a go at completing the select ioctl
*/
{
if(iSelectFlags)
{
TUint ready = (iSelectFlags & SelectConditionsReady());
if(ready)
{
TPckg<TUint> buf(ready);
IoctlComplete(&buf);
iSelectFlags=0;
}
}
}
TBool ASocket::CheckReadStopped()
/**
Has reading stopped
*/
{
if((iBlockedOperations&EReadStopped) && iDataAvailable<=0)
{
TPtrC8 ptr(0,0);
TInt ret = iReadMsg->WriteDes(MSG_PRM(2), ptr);
if(iRecvXferLenIdx >= 0)
{
iXferLength() = 0;
ret = iReadMsg->WriteDes(iRecvXferLenIdx, iXferLength);
if(ret!= KErrNone)
{
return EFalse;
}
}
return ETrue;
}
return EFalse;
}
void ASocket::CommunicateOwner()
/**
Communicate Thread ID, Process ID and UID to SAP.
*/
{
if (RequiresOwnerInfo())
{
TSoOwnerInfo info;
// Not using more Networking specific macro SYMBIAN_NETWORKING_UPS because this support
// is required by UPS functionality outside of Networking subsystem as well.
GetOwnerInfo(info.iProcessId, info, info.iThreadId);
iSSP->SetOption(KSOLProvider, KSoOwnerInfo, TPckgC<TSoOwnerInfo>(info));
#ifdef SYMBIAN_NETWORKING_UPS
iSSP->SetOption(KSOLProvider, KSoSetPlatSecApi, TPckgC<const MPlatsecApiExt*>(this));
#endif
}
}
TInt ASocket::GetConnectionSocketInfo(TConnectionSocketInfo& aInfo) const
/**
Return information about this socket
*/
{
__ASSERT_DEBUG(iSSP, User::Panic(KSpecAssert_ESockSSocksntsck, 10));
if(iIsBound)
{
iSSP->LocalName(aInfo.iSourceAddress);
}
else
{
return KErrNotFound;
}
if (State()==ESStateConnected || State()==ESStateOpeningActive || State()==ESStateShuttingDown)
{
iSSP->RemName(aInfo.iDestinationAddress);
}
else
{
return KErrNotFound;
}
__ASSERT_DEBUG(iProtocolInfo, User::Panic(KSpecAssert_ESockSSocksntsck, 11));
aInfo.iAddressFamily = iProtocolInfo->iAddrFamily;
aInfo.iProtocol = iProtocolInfo->iProtocol;
return KErrNone;
}
TInt ASocket::RequestAsyncMBufAllocation(TInt aSignal, TUint aSize)
{
TInt ret = KErrNone;
if(!iAllocAsync)
{
TRAP(ret, iAllocAsync = CWaitForMBufs::NewL(this));
if(ret != KErrNone)
{
// This second-chance mechanism has failed; it's now reasonable just to error the client
Error(KErrNoMemory, aSignal == CWaitForMBufs::ECanSend? MSessionControlNotify::EErrorSend: MSessionControlNotify::EErrorRecv);
}
}
if(aSignal == CWaitForMBufs::ENewData)
{
iAllocAsync->AllocWaitAndSignalNewData(aSize);
}
else
{
if(aSignal == CWaitForMBufs::ECanSend)
{
iAllocAsync->AllocWaitAndSignalCanSend(aSize);
}
}
return ret;
}
void ASocket::ReadParamL(TSocketMessage aMessage, TInt aSrcParamIndex,TDes8 &aDes,TInt anOffset)
{
User::LeaveIfError(GetUserMessage(aMessage)->ReadDes(aSrcParamIndex, aDes, anOffset));
}
void ASocket::ReadParamL(TSocketMessage aMessage, TInt aSrcParamIndex, RMBufChain& aBufChain)
{
User::LeaveIfError(GetUserMessage(aMessage)->ReadMBuf(aSrcParamIndex, aBufChain));
}
void ASocket::WriteParamL(TSocketMessage aMessage, TInt aDstParamIndex,const TDesC8& aDes,TInt anOffset)
{
User::LeaveIfError(GetUserMessage(aMessage)->WriteDes(aDstParamIndex, aDes, anOffset));
}
void ASocket::WriteParamL(TSocketMessage aMessage, TInt aDstParamIndex, RMBufChain& aBufChain)
{
User::LeaveIfError(GetUserMessage(aMessage)->WriteMBuf(aDstParamIndex, aBufChain));
}
AMessage* ASocket::GetUserMessage( TSocketMessage aMessage ) const
{
AMessage* msg = NULL;
switch (aMessage)
{
case ESocketCurrentMessage:
msg = iCurrentMsg;
break;
case ESocketReadMessage:
msg = iReadMsg;
break;
case ESocketWriteMessage:
msg = iWriteMsg;
break;
case ESocketCloseMessage:
msg = iBlockedCloseMsg;
break;
case ESocketIoCtlMessage:
msg = iBlockedIoctlMsg;
break;
case ESocketConnectMessage:
msg = iBlockedConnectMsg;
break;
case ESocketSetLocalNameMessage:
msg = iBlockedSetLocalNameMsg;
break;
default:
__ASSERT_DEBUG(0, User::Panic(KSpecAssert_ESockSSocksntsck, 12));
}
return msg;
}
void ASocket::SetUserMessage ( TSocketMessage aMessageType, AMessage* aMessage )
{
switch (aMessageType)
{
case ESocketCurrentMessage:
iCurrentMsg = aMessage;
break;
case ESocketReadMessage:
iReadMsg = aMessage;
break;
case ESocketWriteMessage:
iWriteMsg = aMessage;
break;
case ESocketCloseMessage:
iBlockedCloseMsg = aMessage;
break;
case ESocketIoCtlMessage:
iBlockedIoctlMsg = aMessage;
break;
case ESocketConnectMessage:
iBlockedConnectMsg = aMessage;
break;
case ESocketSetLocalNameMessage:
iBlockedSetLocalNameMsg = aMessage;
break;
default:
__ASSERT_DEBUG(0, User::Panic(KSpecAssert_ESockSSocksntsck, 13));
}
}
#ifdef SYMBIAN_NETWORKING_UPS
TInt ASocket::GetProcessAndThreadId(TProcessId& aProcessId, TThreadId& aThreadId) const
/**
Retrieve the process and thread id associated with the pending request.
*/
{
TSocketMessage msg = ESocketCurrentMessage;
if (IsBlockedConnect())
{
msg = ESocketConnectMessage;
}
else
if (IsBlockedWrite())
{
msg = ESocketWriteMessage;
}
if (msg != ESocketCurrentMessage || (iState == ESStateCreated || iState == ESStateOpen))
{
// Guard against an invalid current message - especially for a Connect/CancelConnect scenario
// whereby the socket is in the ESStateError state. Needs revisiting.
return GetUserMessage(msg)->GetProcessAndThreadId(aProcessId, aThreadId);
}
else
{
return KErrUnknown;
}
}
TInt AMessage::GetProcessAndThreadId(TProcessId& /*aProcessId*/, TThreadId& /*aThreadId*/) const
{
return KErrNotSupported;
}
#endif