diff -r 000000000000 -r dfb7c4ff071f datacommsserver/esockserver/ssock/ss_intsock.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/datacommsserver/esockserver/ssock/ss_intsock.cpp Thu Dec 17 09:22:25 2009 +0200 @@ -0,0 +1,3237 @@ +// 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 +#include +#include +#include "es_mbufif.h" +#include +#include +#include +#include +#include +#include "ss_flowrequest.h" +#include "ss_sapfactshim.h" +#include +#include +#include + +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include +#endif + +#ifdef SYMBIAN_NETWORKING_UPS +#include // 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()) + { + // The first error would be from the flow request, any other error iIsFlowRequestPending must be false anyway + if (FlowRequestPending()) + { + TEBase::TError& errorMsg(static_cast(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& bindToMsg(static_cast(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(this), &aBindTo.iNodeId.Node()) ); +#endif + +#if defined(__GCCXML__) + CSubConnectionFlowBase* flow = reinterpret_cast( + reinterpret_cast( + aBindTo.iNodeId.Ptr() + ) + ); +#else + CSubConnectionFlowBase* flow = mcfnode_cast( + reinterpret_cast( + 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(this), &aBindTo.iNodeId.Node()) ) + User::Leave(KErrArgument); + } + + NM_LOG((KESockServerTag, _L8("CSocket %08x:\tSynchronous call: From=%08x To=%08x Func=BindToL"), + this, static_cast(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(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(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(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 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 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 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 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 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(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 && (iOptions & KOptBlocking)) + { + completeReq = EFalse; + } + } + } + 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 + 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; + } + 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; + } + 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()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()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(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* 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 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(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(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 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(info)); + +#ifdef SYMBIAN_NETWORKING_UPS + iSSP->SetOption(KSOLProvider, KSoSetPlatSecApi, TPckgC(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 +