/**MOD+**********************************************************************/ /* Module: cchannel.c */ /* */ /* Purpose: Virtual Channel Client functions */ /* */ /* Copyright(C) Microsoft Corporation 1997 */ /* */ /****************************************************************************/ /* Changes: */ /* $Log$ */ /* */ /**MOD-**********************************************************************/ /****************************************************************************/ /****************************************************************************/ /* HEADERS */ /****************************************************************************/ /****************************************************************************/ #include extern "C" { #define TRC_GROUP TRC_GROUP_NETWORK #define TRC_FILE "cchannel" #include } #include "autil.h" #include "wui.h" #include "sl.h" #include "nc.h" #include "cd.h" #include "cchan.h" CChan* CChan::pStaticClientInstance = NULL; /****************************************************************************/ /****************************************************************************/ /* Constants */ /****************************************************************************/ /****************************************************************************/ #define CHANNEL_MSG_SEND 1 #define CHANNEL_MSG_SUSPEND 2 #ifndef UNREFERENCED_PARAMETER #define UNREFERENCED_PARAMETER(P) (P) #endif #define WEBCTRL_DLL_NAME TEXT("mstscax.dll") #ifdef DEBUG_CCHAN_COMPRESSION _inline ULONG DbgUserPrint(TCHAR* Format, ...) { va_list arglist; TCHAR Buffer[512]; ULONG retval; // // Format the output into a buffer and then print it. // va_start(arglist, Format); retval = _vsntprintf(Buffer, sizeof(Buffer), Format, arglist); if (retval != -1) { OutputDebugString(Buffer); } return retval; } #endif CChan::CChan(CObjs* objs) { DC_BEGIN_FN("CChan"); _pClientObjs = objs; _pInitHandle = NULL; _pFirstWrite = NULL; _pLastWrite = NULL; _connected = CONNECTION_NONE; _inChannelEntry = FALSE; _ChannelInitCalled = FALSE; _channelCount = 0; _fCapsVCCompressionSupported = FALSE; _fCompressChannels = FALSE; if(!CChan::pStaticClientInstance) { // // Legacy addins can only talk to the initial instance of the client // CChan::pStaticClientInstance = this; } _pMPPCContext = NULL; _CompressFlushes = 0; _fCompressionFlushed = 0; _pUserOutBuf = NULL; _fNeedToResetContext = TRUE; _fLockInitalized = FALSE; #ifdef DEBUG_CCHAN_COMPRESSION _pDbgRcvDecompr8K = NULL; _fDbgVCTriedAllocRecvContext = FALSE; _fDbgAllocFailedForVCRecvContext = TRUE; #endif _iDbgCompressFailedCount = 0; _iChanSuspendCount = 0; _iChanResumeCount = 0; _iChanCapsRecvdCount = 0; DC_END_FN(); } CChan::~CChan() { if(_fLockInitalized) { DeleteCriticalSection(&_VirtualChannelInitLock); } if(this == CChan::pStaticClientInstance) { CChan::pStaticClientInstance = NULL; } } VOID CChan::SetCapabilities(LONG caps) { DC_BEGIN_FN("SetCapabilities"); // // Determine if we can send compressed VC data to the server // NOTE: for a few whistler builds the server supported 64K // compressed channels from the client, but this capability // has been removed to enhance server scalability. // // The capability in the other direction, e.g can the server send // us compressed data is something the client exposes to the server // _iChanCapsRecvdCount++; _fCapsVCCompressionSupported = (caps & TS_VCCAPS_COMPRESSION_8K) ? TRUE : FALSE; TRC_NRM((TB,_T("VC Caps, compression supported: %d"), _fCapsVCCompressionSupported)); _fCompressChannels = (_fCapsVCCompressionSupported & _pUi->UI_GetCompress()); TRC_NRM((TB,_T("Compress virtual channels: %d"), _fCompressChannels)); DC_END_FN(); } void CChan::OnDeviceChange(ULONG_PTR params) { PDEVICE_PARAMS deviceParams = (PDEVICE_PARAMS)params; if (deviceParams->deviceObj != NULL) { deviceParams->deviceObj->OnDeviceChange(deviceParams->wParam, deviceParams->lParam); } } /****************************************************************************/ /****************************************************************************/ /* API Functions */ /****************************************************************************/ /****************************************************************************/ /****************************************************************************/ /* VirtualChannelInit - see cchannel.h */ /****OC-*********************************************************************/ UINT VCAPITYPE DCEXPORT VirtualChannelInitEx( PVOID lpUserParam, PVOID pInitHandle, PCHANNEL_DEF pChannel, INT channelCount, DWORD versionRequested, PCHANNEL_INIT_EVENT_EX_FN pChannelInitEventProcEx) { DC_BEGIN_FN("VirtualChannelInitEx"); UINT rc = CHANNEL_RC_OK; /************************************************************************/ /* Check parameters */ /************************************************************************/ if (NULL == pInitHandle) { rc = CHANNEL_RC_BAD_INIT_HANDLE; DC_QUIT; } if (((PCHANNEL_INIT_HANDLE)pInitHandle)->pInst == NULL) { TRC_ERR((TB, _T("Null Init Handle"))); rc = CHANNEL_RC_BAD_INIT_HANDLE; DC_QUIT; } rc = ((PCHANNEL_INIT_HANDLE)pInitHandle)->pInst->IntVirtualChannelInit( lpUserParam, NULL, pChannel, channelCount, versionRequested, NULL, pChannelInitEventProcEx); DC_EXIT_POINT: return (rc); DC_END_FN(); } UINT VCAPITYPE DCEXPORT VirtualChannelInit( PVOID * ppInitHandle, PCHANNEL_DEF pChannel, INT channelCount, DWORD versionRequested, PCHANNEL_INIT_EVENT_FN pChannelInitEventProc) { DC_BEGIN_FN("VirtualChannelInit"); UINT rc = CHANNEL_RC_OK; /************************************************************************/ /* Check parameters */ /************************************************************************/ TRC_ASSERT( CChan::pStaticClientInstance, (TB, _T("CChan::pStaticClientInstance is NULL in VirtualChannelInit\n"))); if (NULL == CChan::pStaticClientInstance) { rc = CHANNEL_RC_INVALID_INSTANCE; DC_QUIT; } rc = (CChan::pStaticClientInstance)->IntVirtualChannelInit( NULL, ppInitHandle, pChannel, channelCount, versionRequested, pChannelInitEventProc, NULL); DC_EXIT_POINT: return (rc); DC_END_FN(); } UINT VCAPITYPE DCEXPORT CChan::IntVirtualChannelInit( PVOID pParam, PVOID * ppInitHandle, PCHANNEL_DEF pChannel, INT channelCount, DWORD versionRequested, PCHANNEL_INIT_EVENT_FN pChannelInitEventProc, PCHANNEL_INIT_EVENT_EX_FN pChannelInitEventProcEx) { UINT rc = CHANNEL_RC_OK; INT i, j, k; PCHANNEL_INIT_HANDLE pRealInitHandle; DC_BEGIN_FN("IntVirtualChannelInit"); UNREFERENCED_PARAMETER( versionRequested ); EnterCriticalSection(&_VirtualChannelInitLock); /************************************************************************/ /* Check parameters */ /************************************************************************/ if( versionRequested > VIRTUAL_CHANNEL_VERSION_WIN2000) { rc = CHANNEL_RC_UNSUPPORTED_VERSION; DC_QUIT; } // // ppInitHandle is not used by the EX version of the API // if (pChannelInitEventProc && ppInitHandle == NULL) { rc = CHANNEL_RC_BAD_INIT_HANDLE; DC_QUIT; } if(pChannelInitEventProc && IsBadWritePtr(ppInitHandle, sizeof(PVOID))) { rc = CHANNEL_RC_BAD_INIT_HANDLE; DC_QUIT; } if (pChannel == NULL) { rc = CHANNEL_RC_BAD_CHANNEL; DC_QUIT; } if(channelCount <= 0) { rc = CHANNEL_RC_BAD_CHANNEL; DC_QUIT; } if ((IsBadReadPtr(pChannel, channelCount * sizeof(CHANNEL_DEF))) || (IsBadWritePtr(pChannel, channelCount * sizeof(CHANNEL_DEF)))) { rc = CHANNEL_RC_BAD_CHANNEL; DC_QUIT; } if ((_channelCount + channelCount) > CHANNEL_MAX_COUNT) { rc = CHANNEL_RC_TOO_MANY_CHANNELS; DC_QUIT; } for (i = 0; i < channelCount; i++) { for (j = 0; j <= CHANNEL_NAME_LEN; j++) { if (pChannel[i].name[j] == '\0') { break; } } if (!j || j > CHANNEL_NAME_LEN) { /****************************************************************/ /* There was no terminating null in this channel name string */ /* or the channel name was zero length */ /****************************************************************/ rc = CHANNEL_RC_BAD_CHANNEL; DC_QUIT; } } if (pChannelInitEventProc == NULL && pChannelInitEventProcEx == NULL) { rc = CHANNEL_RC_BAD_PROC; DC_QUIT; } /************************************************************************/ /* Check state */ /************************************************************************/ if (_connected != CONNECTION_NONE) { rc = CHANNEL_RC_ALREADY_CONNECTED; DC_QUIT; } if (!_inChannelEntry) { TRC_ERR((TB,_T("VirtualChannelInit called outside VirtualChannelEntry"))); rc = CHANNEL_RC_NOT_IN_VIRTUALCHANNELENTRY; DC_QUIT; } /************************************************************************/ /* Save the fact that VirtualChannelInit has been called, so that */ /* IntChannelLoad knows it's done. */ /************************************************************************/ _ChannelInitCalled = TRUE; /************************************************************************/ /* Initialize a handle (allocated by VirtualChannelEntry) */ /************************************************************************/ pRealInitHandle = _newInitHandle; pRealInitHandle->pInitEventFn = pChannelInitEventProc; pRealInitHandle->pInitEventExFn = pChannelInitEventProcEx; pRealInitHandle->channelCount = channelCount; pRealInitHandle->dwFlags = 0; if(pChannelInitEventProcEx) { pRealInitHandle->lpParam = pParam; pRealInitHandle->fUsingExApi = TRUE; } else { pRealInitHandle->fUsingExApi = FALSE; } /************************************************************************/ /* Process all Channel data */ /************************************************************************/ for (i = 0, j = _channelCount; i < channelCount; i++) { /********************************************************************/ /* Assume it's going to be OK */ /********************************************************************/ pChannel[i].options |= CHANNEL_OPTION_INITIALIZED; /********************************************************************/ /* Check for duplicate names */ /********************************************************************/ for (k = 0; k < j; k++) { #ifdef UNICODE TRC_DBG((TB, _T("Test %S (#%d) for (case insensitive) dup with %S ((#%d)"), pChannel[i].name, i, _channel[k].name, k)); #else TRC_DBG((TB, _T("Test %s (#%d) for (case insensitive) dup with %s ((#%d)"), pChannel[i].name, i, _channel[k].name, k)); #endif if (0 == DC_ASTRNICMP(pChannel[i].name, _channel[k].name, CHANNEL_NAME_LEN)) { /************************************************************/ /* Tell the caller this channel is not initialized */ /************************************************************/ #ifdef UNICODE TRC_ERR((TB, _T("Dup channel name %S (#%d/#%d)"), pChannel[i].name, i, k)); #else TRC_ERR((TB, _T("Dup channel name %s (#%d/#%d)"), pChannel[i].name, i, k)); #endif pChannel[i].options &= (~(CHANNEL_OPTION_INITIALIZED)); pRealInitHandle->channelCount--; break; } } if (pChannel[i].options & CHANNEL_OPTION_INITIALIZED) { /****************************************************************/ /* Channel is OK - save its data */ /****************************************************************/ DC_MEMCPY(_channel[j].name, pChannel[i].name, CHANNEL_NAME_LEN); //name length is CHANNEL_NAME_LEN+1, ensure null termination _channel[j].name[CHANNEL_NAME_LEN] = 0; /****************************************************************/ /* Channels are lower case */ /****************************************************************/ DC_ACSLWR(_channel[j].name); _channel[j].options = pChannel[i].options; #ifdef UNICODE TRC_NRM((TB, _T("Channel #%d, %S"), i, _channel[j].name)); #else TRC_NRM((TB, _T("Channel #%d, %s"), i, _channel[j].name)); #endif _channelData[j].pOpenEventFn = NULL; _channelData[j].pOpenEventExFn = NULL; _channelData[j].MCSChannelID = 0; _channelData[j].pInitHandle = pRealInitHandle; _channelData[j].status = CHANNEL_STATUS_CLOSED; _channelData[j].priority = (_channel[j].options & CHANNEL_OPTION_PRI_HIGH) ? TS_HIGHPRIORITY: (_channel[j].options & CHANNEL_OPTION_PRI_MED) ? TS_MEDPRIORITY: TS_LOWPRIORITY; TRC_NRM((TB, _T(" Priority %d"), _channelData[j].priority)); //Ignore all flags, channels always encrypt _channelData[j].SLFlags = RNS_SEC_ENCRYPT; _channelData[j].VCFlags = (_channel[j].options & CHANNEL_OPTION_SHOW_PROTOCOL) ? CHANNEL_FLAG_SHOW_PROTOCOL : 0; #ifdef UNICODE TRC_NRM((TB, _T("Channel %S has %s shadow persistent option"), _channel[j].name, (_channel[j].options & CHANNEL_OPTION_REMOTE_CONTROL_PERSISTENT)? _T(""): _T("NO"))); #else TRC_NRM((TB, _T("Channel %S has %s shadow persistent option"), _channel[j].name, (_channel[j].options & CHANNEL_OPTION_REMOTE_CONTROL_PERSISTENT) ? _T(""): _T("NO"))); #endif if (_channel[j].options & CHANNEL_OPTION_REMOTE_CONTROL_PERSISTENT) { _channelData[j].VCFlags |= CHANNEL_FLAG_SHADOW_PERSISTENT; // if one channel is shadow persistent then the whole plugin is too. pRealInitHandle->dwFlags |= CHANNEL_FLAG_SHADOW_PERSISTENT; } TRC_NRM((TB, _T("VC Flags: %#x"), _channelData[j].VCFlags)); j++; } } _channelCount += pRealInitHandle->channelCount; /************************************************************************/ /* Set return code */ /************************************************************************/ if(!pRealInitHandle->fUsingExApi) { *ppInitHandle = pRealInitHandle; TRC_NRM((TB, _T("Return handle %p"), *ppInitHandle)); } rc = CHANNEL_RC_OK; DC_EXIT_POINT: LeaveCriticalSection(&_VirtualChannelInitLock); DC_END_FN(); return(rc); } /* VirtualChannelInit */ /****************************************************************************/ /* VirtualChannelOpen - see cchannel.h */ /****************************************************************************/ UINT VCAPITYPE DCEXPORT VirtualChannelOpen( PVOID pInitHandle, PDWORD pOpenHandle, PCHAR pChannelName, PCHANNEL_OPEN_EVENT_FN pChannelOpenEventProc) { DC_BEGIN_FN("VirtualChannelOpen"); UINT rc = CHANNEL_RC_OK; if (pInitHandle == NULL) { TRC_ERR((TB, _T("Null Init Handle"))); rc = CHANNEL_RC_BAD_INIT_HANDLE; DC_QUIT; } if (((PCHANNEL_INIT_HANDLE)pInitHandle)->pInst == NULL) { TRC_ERR((TB, _T("Null Init Handle"))); rc = CHANNEL_RC_BAD_INIT_HANDLE; DC_QUIT; } rc = ((PCHANNEL_INIT_HANDLE)pInitHandle)->pInst->IntVirtualChannelOpen(pInitHandle, pOpenHandle, pChannelName, pChannelOpenEventProc, NULL); DC_EXIT_POINT: return (rc); DC_END_FN(); } UINT VCAPITYPE DCEXPORT VirtualChannelOpenEx( PVOID pInitHandle, PDWORD pOpenHandle, PCHAR pChannelName, PCHANNEL_OPEN_EVENT_EX_FN pChannelOpenEventProcEx) { DC_BEGIN_FN("VirtualChannelOpenEx"); UINT rc = CHANNEL_RC_OK; if (pInitHandle == NULL) { TRC_ERR((TB, _T("Null Init Handle"))); rc = CHANNEL_RC_BAD_INIT_HANDLE; DC_QUIT; } if (((PCHANNEL_INIT_HANDLE)pInitHandle)->pInst == NULL) { TRC_ERR((TB, _T("Null Init Handle"))); rc = CHANNEL_RC_BAD_INIT_HANDLE; DC_QUIT; } rc = ((PCHANNEL_INIT_HANDLE)pInitHandle)->pInst->IntVirtualChannelOpen(pInitHandle, pOpenHandle, pChannelName, NULL, pChannelOpenEventProcEx); DC_EXIT_POINT: return (rc); DC_END_FN(); } UINT VCAPITYPE CChan::IntVirtualChannelOpen( PVOID pInitHandle, PDWORD pOpenHandle, PCHAR pChannelName, PCHANNEL_OPEN_EVENT_FN pChannelOpenEventProc, PCHANNEL_OPEN_EVENT_EX_FN pChannelOpenEventProcEx) { PCHANNEL_INIT_HANDLE pRealInitHandle; UINT channelID; UINT rc = CHANNEL_RC_OK; DC_BEGIN_FN("IntVirtualChannelOpen"); pRealInitHandle = (PCHANNEL_INIT_HANDLE)pInitHandle; /************************************************************************/ /* Check parameters */ /************************************************************************/ if (pInitHandle == NULL) { TRC_ERR((TB, _T("Null Init Handle"))); rc = CHANNEL_RC_BAD_INIT_HANDLE; DC_QUIT; } if (pRealInitHandle->signature != CHANNEL_INIT_SIGNATURE) { TRC_ERR((TB, _T("Invalid init handle signature %#lx"), pRealInitHandle->signature)); rc = CHANNEL_RC_BAD_INIT_HANDLE; DC_QUIT; } if (pOpenHandle == NULL) { TRC_ERR((TB, _T("NULL Open Handle"))); rc = CHANNEL_RC_BAD_CHANNEL_HANDLE; DC_QUIT; } if(pRealInitHandle->fUsingExApi) { if (pChannelOpenEventProcEx == NULL) { rc = CHANNEL_RC_BAD_PROC; DC_QUIT; } } else { if (pChannelOpenEventProc == NULL) { rc = CHANNEL_RC_BAD_PROC; DC_QUIT; } } /************************************************************************/ /* Check connection state */ /************************************************************************/ if ((_connected != CONNECTION_VC) && (_connected != CONNECTION_SUSPENDED)) { TRC_ERR((TB, _T("Not yet connected"))); rc = CHANNEL_RC_NOT_CONNECTED; DC_QUIT; } /************************************************************************/ /* Find the requested channel */ /* channel names are lowercase but do a case insensitve cmp */ /* Just incase an older plugin passed in an upper case name (doc was */ /* not clear channel names had to be lower case */ /************************************************************************/ for (channelID = 0; channelID < _channelCount; channelID++) { if (0 == DC_ASTRNICMP(pChannelName, _channel[channelID].name, CHANNEL_NAME_LEN)) { break; } } if (channelID == _channelCount) { #ifdef UNICODE TRC_ERR((TB, _T("Unregistered channel %S"), pChannelName)); #else TRC_ERR((TB, _T("Unregistered channel %s"), pChannelName)); #endif rc = CHANNEL_RC_UNKNOWN_CHANNEL_NAME; DC_QUIT; } /************************************************************************/ /* Check this channel is registered by this user */ /************************************************************************/ if (_channelData[channelID].pInitHandle != pInitHandle) { #ifdef UNICODE TRC_ERR((TB, _T("Channel %S not registered to this user"), pChannelName)); #else TRC_ERR((TB, _T("Channel %s not registered to this user"), pChannelName)); #endif rc = CHANNEL_RC_UNKNOWN_CHANNEL_NAME; DC_QUIT; } /************************************************************************/ /* Check that this channel is not already open */ /************************************************************************/ if (_channelData[channelID].status == CHANNEL_STATUS_OPEN) { #ifdef UNICODE TRC_ERR((TB, _T("Channel %S already open"), pChannelName)); #else TRC_ERR((TB, _T("Channel %s already open"), pChannelName)); #endif rc = CHANNEL_RC_ALREADY_OPEN; DC_QUIT; } /************************************************************************/ /* Well, everything seems to be in order. Mark the channel as open and */ /* return its index as the handle. */ /************************************************************************/ _channelData[channelID].status = CHANNEL_STATUS_OPEN; _channelData[channelID].pOpenEventFn = pChannelOpenEventProc; _channelData[channelID].pOpenEventExFn = pChannelOpenEventProcEx; *pOpenHandle = channelID; rc = CHANNEL_RC_OK; DC_EXIT_POINT: DC_END_FN(); return(rc); } /* VirtualChannelOpen */ /****************************************************************************/ /* VirtualChannelClose - see cchannel.h */ /****************************************************************************/ UINT VCAPITYPE DCEXPORT VirtualChannelClose(DWORD openHandle) { UINT rc = CHANNEL_RC_OK; DC_BEGIN_FN("VirtualChannelClose"); TRC_ASSERT( CChan::pStaticClientInstance, (TB, _T("CChan::pStaticClientInstance is NULL in VirtualChannelInit\n"))); if (NULL == CChan::pStaticClientInstance) { rc = CHANNEL_RC_INVALID_INSTANCE; DC_QUIT; } else { rc = (CChan::pStaticClientInstance)->IntVirtualChannelClose(openHandle); } DC_END_FN(); DC_EXIT_POINT: return rc; } UINT VCAPITYPE DCEXPORT VirtualChannelCloseEx(LPVOID pInitHandle, DWORD openHandle) { DC_BEGIN_FN("VirtualChannelCloseEx"); UINT rc = CHANNEL_RC_OK; /************************************************************************/ /* Check parameters */ /************************************************************************/ if (NULL == pInitHandle) { return CHANNEL_RC_NULL_DATA; DC_QUIT; } if (((PCHANNEL_INIT_HANDLE)pInitHandle)->pInst == NULL) { TRC_ERR((TB, _T("Null Init Handle"))); return CHANNEL_RC_BAD_INIT_HANDLE; DC_QUIT; } rc = ((PCHANNEL_INIT_HANDLE)pInitHandle)->pInst->IntVirtualChannelClose( openHandle); DC_EXIT_POINT: DC_END_FN(); return rc; } UINT VCAPITYPE DCEXPORT CChan::IntVirtualChannelClose(DWORD openHandle) { UINT rc = CHANNEL_RC_OK; DWORD chanIndex; DC_BEGIN_FN("VirtualChannelClose"); chanIndex = openHandle; /************************************************************************/ /* Check parameters */ /************************************************************************/ if (chanIndex >= _channelCount) { TRC_ERR((TB, _T("Invalid handle %ul ...(channel index portion '%ul' invalid)"), openHandle, chanIndex)); rc = CHANNEL_RC_BAD_CHANNEL_HANDLE; DC_QUIT; } /************************************************************************/ /* Check we're connected */ /************************************************************************/ if ((_connected != CONNECTION_VC) && (_connected != CONNECTION_SUSPENDED)) { TRC_ALT((TB, _T("Not connected"))); rc = CHANNEL_RC_NOT_CONNECTED; DC_QUIT; } /************************************************************************/ /* Check channel status */ /************************************************************************/ if (_channelData[chanIndex].status != CHANNEL_STATUS_OPEN) { TRC_ERR((TB, _T("Channel %ul not open"), chanIndex)); rc = CHANNEL_RC_NOT_OPEN; DC_QUIT; } /************************************************************************/ /* Close the channel */ /************************************************************************/ TRC_NRM((TB, _T("Close channel %ul"), chanIndex)); _channelData[chanIndex].status = CHANNEL_STATUS_CLOSED; _channelData[chanIndex].pOpenEventFn = NULL; _channelData[chanIndex].pOpenEventExFn = NULL; /************************************************************************/ /* Er, that's it */ /************************************************************************/ rc = CHANNEL_RC_OK; DC_EXIT_POINT: DC_END_FN(); return(rc); } /* VirtualChannelClose */ /****************************************************************************/ /* VirtualChannelWrite - see cchannel.h */ /****************************************************************************/ UINT VCAPITYPE DCEXPORT VirtualChannelWrite(DWORD openHandle, LPVOID pData, ULONG dataLength, LPVOID pUserData) { DC_BEGIN_FN("VirtualChannelWrite"); UINT rc = CHANNEL_RC_OK; TRC_ASSERT( CChan::pStaticClientInstance, (TB, _T("CChan::pStaticClientInstance is NULL in VirtualChannelInit\n"))); if (NULL == CChan::pStaticClientInstance) { rc = CHANNEL_RC_INVALID_INSTANCE; DC_QUIT; } else { rc = (CChan::pStaticClientInstance)->IntVirtualChannelWrite(openHandle, pData, dataLength, pUserData); } DC_END_FN(); DC_EXIT_POINT: return rc; } UINT VCAPITYPE DCEXPORT VirtualChannelWriteEx(LPVOID pInitHandle, DWORD openHandle, LPVOID pData, ULONG dataLength, LPVOID pUserData) { DC_BEGIN_FN("VirtualChannelWriteEx"); /************************************************************************/ /* Check parameters */ /************************************************************************/ UINT rc = CHANNEL_RC_OK; if (NULL == pInitHandle) { rc = CHANNEL_RC_NULL_DATA; DC_QUIT; } if (((PCHANNEL_INIT_HANDLE)pInitHandle)->pInst == NULL) { TRC_ERR((TB, _T("Null Init Handle"))); rc = CHANNEL_RC_BAD_INIT_HANDLE; DC_QUIT; } rc = ((PCHANNEL_INIT_HANDLE)pInitHandle)->pInst->IntVirtualChannelWrite( openHandle, pData, dataLength, pUserData); DC_EXIT_POINT: DC_END_FN(); return rc; } UINT VCAPITYPE DCEXPORT CChan::IntVirtualChannelWrite(DWORD openHandle, LPVOID pData, ULONG dataLength, LPVOID pUserData) { UINT rc = CHANNEL_RC_OK; PCHANNEL_WRITE_DECOUPLE pDecouple; UINT chanIndex; DC_BEGIN_FN("VirtualChannelWrite"); chanIndex = openHandle; TRC_DBG((TB, _T("Got channel index: %ul from handle: %d"), chanIndex, openHandle)); /************************************************************************/ /* Check that we're connected */ /************************************************************************/ if ((_connected != CONNECTION_VC) && (_connected != CONNECTION_SUSPENDED)) { TRC_ERR((TB, _T("Not connected"))); rc = CHANNEL_RC_NOT_CONNECTED; DC_QUIT; } /************************************************************************/ /* Check that this channel is open */ /************************************************************************/ if(chanIndex > _channelCount) { TRC_ERR((TB, _T("Invalid channel index %ul from handle %ul"), chanIndex, openHandle)); rc = CHANNEL_RC_BAD_CHANNEL_HANDLE; DC_QUIT; } /************************************************************************/ /* Check that this channel is open */ /************************************************************************/ if (_channelData[chanIndex].status != CHANNEL_STATUS_OPEN) { TRC_ERR((TB, _T("Channel %ul not open"), chanIndex)); rc = CHANNEL_RC_BAD_CHANNEL_HANDLE; DC_QUIT; } /************************************************************************/ /* Check parameters */ /************************************************************************/ if (!pData) { TRC_ERR((TB, _T("No data passed"))); rc = CHANNEL_RC_NULL_DATA; DC_QUIT; } if (dataLength == 0) { TRC_ERR((TB, _T("Zero data length"))); rc = CHANNEL_RC_ZERO_LENGTH; DC_QUIT; } /************************************************************************/ /* Queue the write operation */ /************************************************************************/ pDecouple = (PCHANNEL_WRITE_DECOUPLE) UT_Malloc(_pUt, sizeof(CHANNEL_WRITE_DECOUPLE)); if (pDecouple == NULL) { TRC_ERR((TB, _T("Failed to allocate decouple structure"))); rc = CHANNEL_RC_NO_MEMORY; DC_QUIT; } TRC_NRM((TB, _T("Decouple structure allocated at %p"), pDecouple)); pDecouple->signature = CHANNEL_DECOUPLE_SIGNATURE; pDecouple->pData = pData; pDecouple->pNextData = pData; pDecouple->dataLength = dataLength; pDecouple->dataLeft = dataLength; pDecouple->dataSent = 0; pDecouple->openHandle = openHandle; pDecouple->pUserData = pUserData; pDecouple->pNext = NULL; pDecouple->pPrev = NULL; pDecouple->chanOptions = _channel[chanIndex].options; pDecouple->flags = _channelData[chanIndex].VCFlags | CHANNEL_FLAG_FIRST; /************************************************************************/ /* Pass the request to the SND thread */ /************************************************************************/ TRC_NRM((TB, _T("Decouple, pass %p -> %p"), &pDecouple, pDecouple)); _pCd->CD_DecoupleNotification(CD_SND_COMPONENT, this, CD_NOTIFICATION_FUNC(CChan,IntChannelWrite), &pDecouple, sizeof(pDecouple)); /************************************************************************/ /* All done! */ /************************************************************************/ rc = CHANNEL_RC_OK; DC_EXIT_POINT: DC_END_FN(); return(rc); } /* VirtualChannelWrite */ /****************************************************************************/ /****************************************************************************/ /* Callback Functions */ /****************************************************************************/ /****************************************************************************/ /**PROC+*********************************************************************/ /* Name: ChannelOnInitialized */ /* */ /* Purpose: Called when MSTSC completes initialization */ /* */ /* Returns: none */ /* */ /* Params: none */ /* */ /**PROC-*********************************************************************/ DCVOID DCCALLBACK CChan::ChannelOnInitialized(DCVOID) { DC_BEGIN_FN("ChannelOnInitialized"); /************************************************************************/ /* Call Initialized callbacks */ /************************************************************************/ TRC_NRM((TB, _T("Call callbacks ..."))); IntChannelCallCallbacks(CHANNEL_EVENT_INITIALIZED, NULL, 0); DC_END_FN(); return; } /* ChannelOnInitialized */ /**PROC+*********************************************************************/ /* Name: ChannelOnTerminating */ /* */ /* Purpose: Call when MSTSC is terminating */ /* */ /* Returns: none */ /* */ /* Params: none */ /* */ /**PROC-*********************************************************************/ DCVOID DCCALLBACK CChan::ChannelOnTerminating(DCVOID) { PCHANNEL_INIT_HANDLE pInitHandle; PCHANNEL_INIT_HANDLE pFreeHandle; DC_BEGIN_FN("ChannelOnTerminating"); /************************************************************************/ /* Loop through all handles */ /************************************************************************/ pInitHandle = _pInitHandle; while (pInitHandle != NULL) { TRC_NRM((TB, _T("Terminate handle %p"), pInitHandle)); /********************************************************************/ /* Call the terminated callback */ /********************************************************************/ if(pInitHandle->fUsingExApi) { pInitHandle->pInitEventExFn( pInitHandle->lpParam, pInitHandle, CHANNEL_EVENT_TERMINATED, NULL, 0); } else { pInitHandle->pInitEventFn(pInitHandle, CHANNEL_EVENT_TERMINATED, NULL, 0); } /********************************************************************/ /* Free the library */ /********************************************************************/ FreeLibrary(pInitHandle->hMod); /********************************************************************/ /* Free the handle */ /********************************************************************/ pFreeHandle = pInitHandle; pInitHandle = pInitHandle->pNext; pFreeHandle->signature = 0; UT_Free(_pUt, pFreeHandle); } if(_pMPPCContext) { UT_Free(_pUt, _pMPPCContext); _pMPPCContext = NULL; } if(_pUserOutBuf) { UT_Free(_pUt, _pUserOutBuf); _pUserOutBuf = NULL; } /************************************************************************/ /* Clear key data */ /************************************************************************/ _pInitHandle = NULL; _channelCount = 0; _connected = CONNECTION_NONE; DC_END_FN(); DC_EXIT_POINT: return; } /* ChannelOnTerminating */ /**PROC+*********************************************************************/ /* Name: ChannelOnConnected */ /* */ /* Purpose: Called when a connection is established */ /* */ /* Returns: None */ /* */ /* Params: channelID - T128 MCS channel ID */ /* serverVersion - software version of Server */ /* pUserData - Server-Client Net user data */ /* userDataLength - length of user data */ /* */ /**PROC-*********************************************************************/ DCVOID DCCALLBACK CChan::ChannelOnConnected(DCUINT channelID, DCUINT32 serverVersion, PDCVOID pUserData, DCUINT userDataLength) { PRNS_UD_SC_NET pNetUserData; PDCUINT16 pMCSChannel; UINT i; UINT expectedLength; UINT event; DCTCHAR serverName[UT_MAX_ADDRESS_LENGTH]; DC_BEGIN_FN("ChannelOnConnected"); UNREFERENCED_PARAMETER( channelID ); #ifndef DC_DEBUG UNREFERENCED_PARAMETER( userDataLength ); #endif /************************************************************************/ /* Check Server software version */ /************************************************************************/ if (_RNS_MINOR_VERSION(serverVersion) < 2) { TRC_ALT((TB, _T("Old Server - no channel support"))); event = CHANNEL_EVENT_V1_CONNECTED; _connected = CONNECTION_V1; } else { TRC_NRM((TB, _T("New Server version - channels supported"))); /********************************************************************/ /* Set up local pointers */ /********************************************************************/ pNetUserData = (PRNS_UD_SC_NET)pUserData; pMCSChannel = (PDCUINT16)(pNetUserData + 1); /********************************************************************/ /* Check parameters */ /********************************************************************/ TRC_ASSERT((pNetUserData->channelCount == _channelCount), (TB, _T("Channel count changed by Server: was %hd, is %d"), _channelCount, pNetUserData->channelCount)); expectedLength = sizeof(RNS_UD_SC_NET) + (pNetUserData->channelCount * sizeof(DCUINT16)); if (userDataLength < expectedLength) { TRC_ABORT((TB,_T("SC NET user data too short - is %d, expect %d"), userDataLength, expectedLength)); _pSl->SL_DropLinkImmediate(SL_ERR_INVALIDPACKETFORMAT); DC_QUIT; } /********************************************************************/ /* Save channel data */ /********************************************************************/ for (i = 0; i < _channelCount; i++) { _channelData[i].MCSChannelID = pMCSChannel[i]; } /********************************************************************/ /* Update our state */ /********************************************************************/ _connected = CONNECTION_VC; event = CHANNEL_EVENT_CONNECTED; } /************************************************************************/ /* Call Connected callbacks */ /************************************************************************/ _pUi->UI_GetServerName(serverName, SIZE_TCHARS(serverName)); IntChannelCallCallbacks(event, serverName, UT_MAX_ADDRESS_LENGTH); DC_EXIT_POINT: DC_END_FN(); return; } /* ChannelOnConnected */ /**PROC+*********************************************************************/ /* Name: ChannelOnDisconnected */ /* */ /* Purpose: Called when a session is disconnected */ /* */ /* Returns: none */ /* */ /* Params: reason - disconnect reason code */ /* */ /* */ /**PROC-*********************************************************************/ DCVOID DCCALLBACK CChan::ChannelOnDisconnected(DCUINT reason) { UINT i; DC_BEGIN_FN("ChannelOnDisconnected"); UNREFERENCED_PARAMETER( reason ); /************************************************************************/ /* Don't do anything if we haven't told the callbacks we're connected. */ /************************************************************************/ if (_connected == CONNECTION_NONE) { TRC_ALT((TB, _T("Disconnected callback when not connected"))); DC_QUIT; } /************************************************************************/ /* Change state */ /************************************************************************/ _connected = CONNECTION_NONE; /************************************************************************/ /* Call Disconnected callbacks */ /************************************************************************/ TRC_NRM((TB, _T("Call disconnected callbacks"))); IntChannelCallCallbacks(CHANNEL_EVENT_DISCONNECTED, NULL, 0); /************************************************************************/ /* Disconnection implies that all channels are closed */ /************************************************************************/ for (i = 0; i < _channelCount; i++) { TRC_NRM((TB, _T("'Close' channel %d"), i)); _channelData[i].status = CHANNEL_STATUS_CLOSED; } /************************************************************************/ /* Switch to SND thread to cancel all outstanding sends */ /************************************************************************/ _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this, CD_NOTIFICATION_FUNC(CChan,IntChannelCancelSend), CHANNEL_MSG_SEND); DC_EXIT_POINT: DC_END_FN(); return; } /* ChannelOnDisconnected */ /**PROC+*********************************************************************/ /* Name: ChannelOnSuspended */ /* */ /* Purpose: Called when a session is suspended (shadow client) */ /* */ /* Returns: none */ /* */ /* Params: reason - disconnect reason code */ /* */ /* */ /**PROC-*********************************************************************/ DCVOID DCCALLBACK CChan::ChannelOnSuspended(DCUINT reason) { UINT i; DC_BEGIN_FN("ChannelOnDisconnected"); UNREFERENCED_PARAMETER( reason ); /************************************************************************/ /* Don't do anything if we haven't told the callbacks we're connected. */ /************************************************************************/ if (_connected == CONNECTION_NONE) { TRC_ALT((TB, _T("Disconnected callback when not connected"))); DC_QUIT; } _iChanSuspendCount++; /************************************************************************/ /* Change state */ /************************************************************************/ _connected = CONNECTION_SUSPENDED; /************************************************************************/ /* Call Disconnected callbacks */ /************************************************************************/ TRC_NRM((TB, _T("Call disconnected callbacks"))); IntChannelCallCallbacks(CHANNEL_EVENT_REMOTE_CONTROL_START, NULL, 0); /************************************************************************/ /* Disconnection implies that all channels are closed */ /************************************************************************/ for (i = 0; i < _channelCount; i++) { TRC_NRM((TB, _T("'Close' channel %d"), i)); // If a plug-in specified at least one of its channels shadow persistent, // then it will be notified with the shadow start event instead of the // disconnected event. In this case the plug-in is supposed to shutdown // only its non-shadow-persistent channels. So close only channels that // are not shadow persistent. if (!(_channelData[i].VCFlags & CHANNEL_FLAG_SHADOW_PERSISTENT)) _channelData[i].status = CHANNEL_STATUS_CLOSED; } /************************************************************************/ /* Switch to SND thread to cancel all outstanding sends */ /************************************************************************/ _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this, CD_NOTIFICATION_FUNC(CChan,IntChannelCancelSend), CHANNEL_MSG_SUSPEND); DC_EXIT_POINT: DC_END_FN(); return; } /* ChannelOnSuspended */ /**PROC+*********************************************************************/ /* Name: ChannelOnPacketReceived */ /* */ /* Purpose: Called when data is received from the Server */ /* */ /* Returns: none */ /* */ /* Params: pData - data received */ /* dataLen - length of data received */ /* flags - security flags (meaningless to this function) */ /* channelID - ID of channel on which data received */ /* priority - priority on which data was sent */ /* */ /**PROC-*********************************************************************/ DCVOID DCCALLBACK CChan::ChannelOnPacketReceived(PDCUINT8 pData, DCUINT dataLen, DCUINT flags, DCUINT channelID, DCUINT priority) { UINT i; PCHANNEL_PDU_HEADER pHdr; UINT32 len; DCTCHAR serverName[UT_MAX_ADDRESS_LENGTH]; UINT32 Hdrflags; UINT32 Hdrlength; DC_BEGIN_FN("ChannelOnPacketReceived"); UNREFERENCED_PARAMETER( priority ); UNREFERENCED_PARAMETER( flags ); /************************************************************************/ /* First of all, handle suspend/resume messages */ /************************************************************************/ if (dataLen < sizeof(CHANNEL_PDU_HEADER)) { TRC_ERR((TB,_T("Not enough data: 0x%x need at least: 0x%x"), dataLen, sizeof(CHANNEL_PDU_HEADER))); DC_QUIT; } pHdr = (PCHANNEL_PDU_HEADER)pData; memcpy(&Hdrflags,(UNALIGNED UINT32 *)&(pHdr->flags),sizeof(Hdrflags)); memcpy(&Hdrlength,(UNALIGNED UINT32 *)&(pHdr->length),sizeof(Hdrlength)); if (Hdrflags & CHANNEL_FLAG_SUSPEND) { TRC_ALT((TB, _T("VC suspended"))); /********************************************************************/ /* Treat as a disconnection */ /********************************************************************/ ChannelOnSuspended(0); DC_QUIT; } if (Hdrflags & CHANNEL_FLAG_RESUME) { TRC_ALT((TB, _T("VC resumed"))); /********************************************************************/ /* Update our state */ /********************************************************************/ _connected = CONNECTION_VC; _iChanResumeCount++; /********************************************************************/ /* Call Connected callbacks */ /********************************************************************/ _pUi->UI_GetServerName(serverName, SIZE_TCHARS(serverName)); IntChannelCallCallbacks(CHANNEL_EVENT_REMOTE_CONTROL_STOP, serverName, UT_MAX_ADDRESS_LENGTH); DC_QUIT; } /************************************************************************/ /* Check we're still connected */ /************************************************************************/ if ((_connected != CONNECTION_VC) && (_connected != CONNECTION_SUSPENDED)) { TRC_ASSERT((_connected != CONNECTION_V1), (TB,_T("Channel data received from V1 Server!"))); TRC_NRM((TB, _T("Discard packet received when we're not connected"))); DC_QUIT; } /************************************************************************/ /* Find the channel data for this channel */ /************************************************************************/ for (i = 0; i < _channelCount; i++) { if (_channelData[i].MCSChannelID == channelID) { // // Note it's important to do the decompression even if the channel // is closed otherwise the context could get out of sync // /****************************************************************/ /* call the callback */ /****************************************************************/ TRC_NRM((TB, _T("MCS channel %x = channel %d"), channelID, i)); pData = (PDCUINT8)(pHdr + 1); len = dataLen - sizeof(CHANNEL_PDU_HEADER); UCHAR vcCompressFlags = (Hdrflags >> VC_FLAG_COMPRESS_SHIFT) & VC_FLAG_COMPRESS_MASK; //Data that is returned to user PDCUINT8 pVCUserData = pData; UINT32 cbVCUserDataLen = len; if(vcCompressFlags & PACKET_COMPRESSED) { UCHAR *buf; int bufSize; //Decompress channel data if(vcCompressFlags & PACKET_FLUSHED) { initrecvcontext (&_pUi->_UI.Context1, (RecvContext2_Generic*)_pUi->_UI.pRecvContext2, PACKET_COMPR_TYPE_64K); } #ifdef DC_DEBUG //Update compression stats (debug only) _cbCompressedBytesRecvd += len; #endif if (decompress(pData, len, (vcCompressFlags & PACKET_AT_FRONT), &buf, &bufSize, &_pUi->_UI.Context1, (RecvContext2_Generic*)_pUi->_UI.pRecvContext2, vcCompressFlags & PACKET_COMPR_TYPE_MASK)) { if(!_pUserOutBuf) { _pUserOutBuf = (PUCHAR)UT_Malloc(_pUt, VC_USER_OUTBUF); } TRC_ASSERT(_pUserOutBuf, (TB,_T("_pUserOutBuf is NULL"))); TRC_ASSERT((bufSize < VC_USER_OUTBUF), (TB,_T("Decompressed buffer to big!!!"))); if(_pUserOutBuf && (bufSize < VC_USER_OUTBUF)) { // // Make a copy of the buffer as we can't hand off // a pointer to the decompression context to the // user because a badly behaved plugin can corrupt // the decompression context causing all sorts of // horrible and hard to debug problems. // memcpy(_pUserOutBuf, buf, bufSize); pVCUserData = _pUserOutBuf; cbVCUserDataLen = bufSize; } else { DC_QUIT; } } else { TRC_ABORT((TB, _T("Decompression FAILURE!!!"))); _pSl->SL_DropLinkImmediate(SL_ERR_INVALIDPACKETFORMAT); DC_QUIT; } } #ifdef DC_DEBUG //Update compression stats (debug only) _cbBytesRecvd += len; _cbDecompressedBytesRecvd += cbVCUserDataLen; #endif // // Turn off header flags to hide internal // protocol info from user // Hdrflags &= ~VC_FLAG_PRIVATE_PROTOCOL_MASK; // // Drop the packet at the last moment if the channel is closed // if (_channelData[i].status != CHANNEL_STATUS_OPEN) { TRC_ALT((TB, _T("Data received on un-opened channel %x"), channelID)); DC_QUIT; } if(_channelData[i].pInitHandle->fUsingExApi) { _channelData[i].pOpenEventExFn( _channelData[i].pInitHandle->lpParam, i, CHANNEL_EVENT_DATA_RECEIVED, pVCUserData, cbVCUserDataLen, Hdrlength, Hdrflags); } else { _channelData[i].pOpenEventFn(i, CHANNEL_EVENT_DATA_RECEIVED, pVCUserData, cbVCUserDataLen, Hdrlength, Hdrflags); } DC_QUIT; } } /************************************************************************/ /* If we get here, we didn't find this channel */ /************************************************************************/ TRC_ALT((TB, _T("Data received on unknown channel %x"), channelID)); DC_EXIT_POINT: DC_END_FN(); return; } /* ChannelOnPacketReceived */ /**PROC+*********************************************************************/ /* Name: ChannelOnBufferAvailable */ /* */ /* Purpose: Called when a buffer is available, after a write has failed */ /* because no buffer was available */ /* */ /* Returns: none */ /* */ /* Params: none */ /* */ /**PROC-*********************************************************************/ DCVOID DCCALLBACK CChan::ChannelOnBufferAvailable(DCVOID) { DC_BEGIN_FN("ChannelOnBufferAvailable"); /************************************************************************/ /* Kick the send process into restarting */ /************************************************************************/ TRC_NRM((TB, _T("Write pending %p"), _pFirstWrite)); _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this, CD_NOTIFICATION_FUNC(CChan,IntChannelSend), CHANNEL_MSG_SEND); DC_END_FN(); return; } /* ChannelOnBufferAvailable */ /**PROC+*********************************************************************/ /* Name: ChannelOnConnecting */ /* */ /* Purpose: Called when a connection is being established - returns */ /* virtual channel user data to be sent to the Server */ /* */ /* Returns: None */ /* */ /* Params: ppChannel (returned) - virtual channel user data */ /* pChannelCount (returned) - number of channels returned */ /* */ /**PROC-*********************************************************************/ DCVOID DCCALLBACK CChan::ChannelOnConnecting(PPCHANNEL_DEF ppChannel, PDCUINT32 pChannelCount) { DC_BEGIN_FN("ChannelOnConnecting"); //Reset the context on each new connection _fNeedToResetContext = TRUE; *ppChannel = _channel; *pChannelCount = _channelCount; DC_END_FN(); return; } /* ChannelOnConnecting */ /**PROC+*********************************************************************/ /* Name: ChannelOnInitializing */ /* */ /* Purpose: Called when MSTSC Network Layer is initializing - loads all */ /* configured application DLLs */ /* */ /* Returns: none */ /* */ /* Params: none */ /* */ /**PROC-*********************************************************************/ DCVOID DCCALLBACK CChan::ChannelOnInitializing(DCVOID) { HINSTANCE hInst; PDCTCHAR szAddinDllList = NULL; DWORD status = ERROR_SUCCESS; PRDPDR_DATA pdrInitData = NULL; DC_BEGIN_FN("ChannelOnInitializing"); // // Initialize private member pointers // _pCd = _pClientObjs->_pCdObject; _pSl = _pClientObjs->_pSlObject; _pUt = _pClientObjs->_pUtObject; _pUi = _pClientObjs->_pUiObject; /************************************************************************/ /* Create the exported function table */ /************************************************************************/ _channelEntryPoints.cbSize = sizeof(CHANNEL_ENTRY_POINTS); _channelEntryPoints.protocolVersion = VIRTUAL_CHANNEL_VERSION_WIN2000; hInst = _pUi->UI_GetInstanceHandle(); _channelEntryPoints.pVirtualChannelInit = (PVIRTUALCHANNELINIT)(MakeProcInstance ((FARPROC)VirtualChannelInit, hInst)); _channelEntryPoints.pVirtualChannelOpen = (PVIRTUALCHANNELOPEN)(MakeProcInstance ((FARPROC)VirtualChannelOpen, hInst)); _channelEntryPoints.pVirtualChannelClose = (PVIRTUALCHANNELCLOSE)(MakeProcInstance ((FARPROC)VirtualChannelClose, hInst)); _channelEntryPoints.pVirtualChannelWrite = (PVIRTUALCHANNELWRITE)(MakeProcInstance ((FARPROC)VirtualChannelWrite, hInst)); _channelEntryPointsEx.cbSize = sizeof(CHANNEL_ENTRY_POINTS); _channelEntryPointsEx.protocolVersion = VIRTUAL_CHANNEL_VERSION_WIN2000; _channelEntryPointsEx.pVirtualChannelInitEx = (PVIRTUALCHANNELINITEX)(MakeProcInstance ((FARPROC)VirtualChannelInitEx, hInst)); _channelEntryPointsEx.pVirtualChannelOpenEx = (PVIRTUALCHANNELOPENEX)(MakeProcInstance ((FARPROC)VirtualChannelOpenEx, hInst)); _channelEntryPointsEx.pVirtualChannelCloseEx = (PVIRTUALCHANNELCLOSEEX)(MakeProcInstance ((FARPROC)VirtualChannelCloseEx, hInst)); _channelEntryPointsEx.pVirtualChannelWriteEx = (PVIRTUALCHANNELWRITEEX)(MakeProcInstance ((FARPROC)VirtualChannelWriteEx, hInst)); // // Initialize the single instance VC critical // section lock. This is used to ensure only one // VC plugin at a time can be in the Initialize fn. // // __try { InitializeCriticalSection(&_VirtualChannelInitLock); } __except(EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); } if(ERROR_SUCCESS == status) { _fLockInitalized = TRUE; } else { // // Without the lock we can't ensure we won't get re-entered // because the API's did not make it clear that we did // not support re-entrancy in the VirtualChannelInit fn // so this is fatal just bail out and don't load plugins. // _fLockInitalized = FALSE; TRC_ERR((TB,_T("InitializeCriticalSection failed 0x%x.") _T("NOT LOADING PLUGINS"),status)); DC_QUIT; } // // RDPDR is statically linked in // // Initialize and pass in initialization info to rdpdr // rdpdr keeps a pointer to this struct accross connections // (because there is no clean way to pass it back in again) // On each connection, the core reinitalizes the struct settings. _pUi->UI_InitRdpDrSettings(); pdrInitData = _pUi->UI_GetRdpDrInitData(); if(!_pUi->_UI.fDisableInternalRdpDr) { if(!IntChannelInitAddin( NULL, RDPDR_VirtualChannelEntryEx, NULL,WEBCTRL_DLL_NAME, pdrInitData)) { TRC_ERR((TB, _T("Failed to load internal addin 'RDPDR'"))); } } else { TRC_NRM((TB, _T("NOT LOADING Internal RDPDR, fDisableInternalRdpDr is set"))); } // // Ts ActiveX control's exposed interfaces to the virtual channel API // are also statically linked in // if(!IntChannelInitAddin( NULL, MSTSCAX_VirtualChannelEntryEx, NULL, WEBCTRL_DLL_NAME, (PVOID)_pUi->_UI.pUnkAxControlInstance)) { TRC_NRM((TB, _T("Internal addin (scriptable vchans) did not load: possibly none requested"))); } //Get a comma separated list of dll's to load (passed down from control) szAddinDllList = _pUi->UI_GetVChanAddinList(); if(!szAddinDllList) { TRC_DBG((TB, _T("Not loading any external plugins"))); DC_QUIT; } else { PDCTCHAR szTok = NULL; DCUINT len = DC_TSTRLEN(szAddinDllList); PDCTCHAR szCopyAddinList = (PDCTCHAR) UT_Malloc( _pUt,sizeof(DCTCHAR) * (len+1)); TRC_ASSERT(szCopyAddinList, (TB, _T("Could not allocate mem for addin list"))); if(!szCopyAddinList) { DC_QUIT; } StringCchCopy(szCopyAddinList, len+1, szAddinDllList); szTok = DC_TSTRTOK( szCopyAddinList, _T(",")); while(szTok) { // // Load the DLL // if (_tcsicmp(szTok, _T("rdpdr.dll"))) { IntChannelLoad( szTok); } else { // // Don't load the crusty rdpdr.dll since // we have a newer better one built-in // TRC_ERR((TB,_T("Skiping load of rdpdr.dll"))); } szTok = DC_TSTRTOK( NULL, _T(",")); } UT_Free( _pUt, szCopyAddinList); } #ifdef DC_DEBUG //Debug compression counters _cbBytesRecvd = 0; _cbCompressedBytesRecvd = 0; _cbDecompressedBytesRecvd = 0; _cbTotalBytesUserAskSend = 0; _cbTotalBytesSent = 0; _cbComprInput = 0; _cbComprOutput = 0; #endif DC_EXIT_POINT: DC_END_FN(); return; } /* ChannelOnInitializing */ /****************************************************************************/ /****************************************************************************/ /* Internal Functions */ /****************************************************************************/ /****************************************************************************/ /**PROC+*********************************************************************/ /* Name: IntChannelCallCallbacks */ /* */ /* Purpose: Call all ChannelInitEvent callbacks with a specified event */ /* */ /* Returns: none */ /* */ /* Params: event - event to pass to callbacks */ /* pData - additional data */ /* dataLength - length of additional data */ /* */ /**PROC-*********************************************************************/ DCVOID DCINTERNAL CChan::IntChannelCallCallbacks(DCUINT event, PDCVOID pData, DCUINT dataLength) { PCHANNEL_INIT_HANDLE pInitHandle; DCUINT altEvent, sentEvent; if (event == CHANNEL_EVENT_REMOTE_CONTROL_START) { altEvent = CHANNEL_EVENT_DISCONNECTED; } else if (event == CHANNEL_EVENT_REMOTE_CONTROL_STOP) { altEvent = CHANNEL_EVENT_CONNECTED; } else { altEvent = event; } DC_BEGIN_FN("IntChannelCallCallbacks"); pInitHandle = _pInitHandle; while (pInitHandle != NULL) { if (pInitHandle->dwFlags & CHANNEL_FLAG_SHADOW_PERSISTENT) { // The plug-in supports the new events, don't change it. sentEvent = event; } else { // No support or the new event is not wanted. sentEvent = altEvent; } if(pInitHandle->fUsingExApi) { TRC_NRM((TB, _T("Call callback (Ex) at %p, handle %p, event %d"), pInitHandle->pInitEventExFn, pInitHandle, sentEvent)); pInitHandle->pInitEventExFn(pInitHandle->lpParam, pInitHandle, sentEvent, pData, dataLength); } else { TRC_NRM((TB, _T("Call callback at %p, handle %p, event %d"), pInitHandle->pInitEventFn, pInitHandle, sentEvent)); pInitHandle->pInitEventFn(pInitHandle, sentEvent, pData, dataLength); } pInitHandle = pInitHandle->pNext; } DC_END_FN(); return; } /* IntChannelCallCallbacks */ /**PROC+*********************************************************************/ /* Name: IntChannelFreeLibrary */ /* */ /* Purpose: Decoupled function to unload a DLL */ /* */ /* Returns: none */ /* */ /* Params: value - hMod of DLL to free */ /* */ /**PROC-*********************************************************************/ DCVOID DCINTERNAL CChan::IntChannelFreeLibrary(DCUINT value) { BOOL bRc; DC_BEGIN_FN("IntChannelFreeLibrary"); if(value) { // // Statically linked extenstions (e.g RDPDR) have a null hModule // #ifdef OS_WIN32 bRc = FreeLibrary( #ifndef OS_WINCE (HMODULE)ULongToPtr(value) #else (HMODULE)value #endif ); if (bRc) { TRC_NRM((TB, _T("Free library %#x OK"), value)); } else { TRC_ERR((TB, _T("Failed to free library %#x"), value)); } #else //OS_WIN32 FreeLibrary((HMODULE)value); #endif // OS_WIN32 } DC_END_FN(); return; } /* IntChannelFreeLibrary */ // // IntChannelCompressData // Compressed the buffer directly into the outbuf. // Caller MUST decide if input buf is in size range for compression // and should handle copying over the buffer directly in that case. // // Params: // pSrcData - input buffer // cbSrcLen - length of input buffer // pOutBuf - output buffer // pcbOutLen- compressed output size // Returns: // Compression result (see compress() fn) // UCHAR CChan::IntChannelCompressData(UCHAR* pSrcData, ULONG cbSrcLen, UCHAR* pOutBuf, ULONG* pcbOutLen) { UCHAR compressResult = 0; ULONG CompressedSize = cbSrcLen; DC_BEGIN_FN("IntChannelCompressData"); TRC_ASSERT(((cbSrcLen > VC_MIN_COMPRESS_INPUT_BUF) && (cbSrcLen < VC_MAX_COMPRESSED_BUFFER)), (TB,_T("Compression src len out of range: %d"), cbSrcLen)); TRC_ASSERT(_pMPPCContext,(TB,_T("_pMPPCContext is null"))); //Attempt to compress directly into the outbuf compressResult = compress(pSrcData, pOutBuf, &CompressedSize, _pMPPCContext); if(compressResult & PACKET_COMPRESSED) { //Successful compression. TRC_ASSERT((CompressedSize >= CompressedSize), (TB,_T("Compression created larger size than uncompr"))); compressResult |= _fCompressionFlushed; _fCompressionFlushed = 0; #ifdef DC_DEBUG //Compr counters _cbComprInput += cbSrcLen; _cbComprOutput += CompressedSize; #endif } else if(compressResult & PACKET_FLUSHED) { //Overran compression history, copy over the //uncompressed buffer. _fCompressionFlushed = PACKET_FLUSHED; memcpy(pOutBuf, pSrcData, cbSrcLen); _CompressFlushes++; } else { TRC_ALT((TB, _T("Compression FAILURE"))); } DC_END_FN(); *pcbOutLen = CompressedSize; return compressResult; } /**PROC+*********************************************************************/ /* Name: IntChannelSend */ /* */ /* Purpose: Internal function to send data to the Server */ /* */ /* Returns: None */ /* */ /* Params: value - message passed from caller */ /* */ /* Operation: Called on SND thread */ /* */ /**PROC-*********************************************************************/ DCVOID DCINTERNAL CChan::IntChannelSend(ULONG_PTR value) { PCHANNEL_WRITE_DECOUPLE pDecouple; DCBOOL bRc; PDCUINT8 pBuffer; SL_BUFHND bufHnd; PCHANNEL_PDU_HEADER pHdr; ULONG thisLength; DWORD chanIndex = 0xFDFDFDFD; ULONG cbOutLen = 0; UCHAR compressResult = 0; BOOL fNeedToDirectCopy = TRUE; DC_BEGIN_FN("IntChannelSend"); // // CD passes param as PVOID // #ifndef DC_DEBUG UNREFERENCED_PARAMETER(value); #endif /************************************************************************/ /* Assert parameters */ /************************************************************************/ TRC_ASSERT((value == CHANNEL_MSG_SEND), (TB, _T("Unexpected value %d"), value)); /************************************************************************/ /* Exit immediately if there's nothing to do. */ /************************************************************************/ if (_pFirstWrite == NULL) { TRC_NRM((TB, _T("Nothing to do"))); DC_QUIT; } TRC_ASSERT((_pFirstWrite->signature == CHANNEL_DECOUPLE_SIGNATURE), (TB,_T("Invalid first signature %#lx"), _pFirstWrite->signature)); TRC_ASSERT((_pLastWrite->signature == CHANNEL_DECOUPLE_SIGNATURE), (TB,_T("Invalid last signature %#lx"), _pLastWrite->signature)); /************************************************************************/ /* Get the next queued request */ /************************************************************************/ pDecouple = _pFirstWrite; /************************************************************************/ /* Calculate the length to send */ /************************************************************************/ thisLength = CHANNEL_CHUNK_LENGTH; /************************************************************************/ /* Truncate the data sent if we're about to send more than is left */ /************************************************************************/ if (thisLength >= pDecouple->dataLeft) { thisLength = pDecouple->dataLeft; pDecouple->flags |= CHANNEL_FLAG_LAST; } TRC_NRM((TB, _T("pDecouple %p, src %p, this %lu, left %lu, flags %#lx"), pDecouple, pDecouple->pNextData, thisLength, pDecouple->dataLeft, pDecouple->flags)); /************************************************************************/ /* Get a buffer */ /************************************************************************/ bRc = _pSl->SL_GetBuffer(thisLength + sizeof(CHANNEL_PDU_HEADER), &pBuffer, &bufHnd); if (!bRc) { /********************************************************************/ /* Failed to get a buffer. This is not entirely unexpected and is */ /* most likely simply due to back-pressure. The write will be */ /* retried when a buffer becomes available (signalled by a call to */ /* ChannelOnBufferAvailable). */ /********************************************************************/ TRC_NRM((TB, _T("Failed to get %d-byte buffer"), thisLength + sizeof(CHANNEL_PDU_HEADER))); DC_QUIT; } /************************************************************************/ /* Fill in the Channel PDU */ /************************************************************************/ pHdr = (PCHANNEL_PDU_HEADER)pBuffer; memcpy((UNALIGNED UINT32 *)&(pHdr->length), &(pDecouple->dataLength),sizeof(pDecouple->dataLength)); memcpy((UNALIGNED UINT32 *)&(pHdr->flags), &(pDecouple->flags),sizeof(pDecouple->flags)); cbOutLen = thisLength; compressResult = 0; fNeedToDirectCopy = TRUE; if(_fCompressChannels && (pDecouple->chanOptions & CHANNEL_OPTION_COMPRESS_RDP)) { if((thisLength > VC_MIN_COMPRESS_INPUT_BUF) && (thisLength < VC_MAX_COMPRESSED_BUFFER)) { //Compress the packet if(!_pMPPCContext) { //Deferred init of the send context _pMPPCContext = (SendContext*) UT_Malloc(_pUt, VC_MAX_COMPRESSED_BUFFER+ sizeof(SendContext)); if(!_pMPPCContext) { #ifdef OS_WINCE //Applies to OS_WINNT too _pSl->SL_FreeBuffer(bufHnd); #endif TRC_ERR((TB,_T("Failed to alloc MPPC send context"))); DC_QUIT; } _fNeedToResetContext = TRUE; } #ifdef DEBUG_CCHAN_COMPRESSION if (!_pDbgRcvDecompr8K) { _fDbgVCTriedAllocRecvContext = TRUE; _pDbgRcvDecompr8K = (RecvContext2_8K*) LocalAlloc(LPTR, sizeof(RecvContext2_8K)); if (_pDbgRcvDecompr8K) { _pDbgRcvDecompr8K->cbSize = sizeof(RecvContext2_8K); initrecvcontext(&_DbgRcvContext1, (RecvContext2_Generic*)_pDbgRcvDecompr8K, PACKET_COMPR_TYPE_8K); } else { _fDbgAllocFailedForVCRecvContext = TRUE; TRC_ERR((TB,_T("Fail to alloc debug decompression context"))); DC_QUIT; } } #endif if(_fNeedToResetContext) { // //Reset the context at the start of every connection. // // Server only supports 8K compression from client // i.e. it will only decompress with 8K of history // initsendcontext(_pMPPCContext, PACKET_COMPR_TYPE_8K); _fNeedToResetContext = FALSE; } TRC_ASSERT((_pMPPCContext), (TB,_T("_pMPPCContext is null"))); compressResult = IntChannelCompressData( (UCHAR*)pDecouple->pNextData, thisLength, (UCHAR*)(pHdr+1), &cbOutLen ); if(0 != compressResult) { #ifdef DEBUG_CCHAN_COMPRESSION // // debug: decompresss the packet // PUCHAR pDecompOutBuf = NULL; INT cbDecompLen; if (compressResult & PACKET_COMPRESSED) { if (compressResult & PACKET_FLUSHED) { initrecvcontext(&_DbgRcvContext1, (RecvContext2_Generic*)_pDbgRcvDecompr8K, PACKET_COMPR_TYPE_8K); } if (decompress((PUCHAR)(pHdr+1), cbOutLen, (compressResult & PACKET_AT_FRONT), //0 start &pDecompOutBuf, &cbDecompLen, &_DbgRcvContext1, (RecvContext2_Generic*)_pDbgRcvDecompr8K, PACKET_COMPR_TYPE_8K)) { if (cbDecompLen != thisLength) { DbgUserPrint(_T("Decompress check failed. Inlen!=outlen\n")); DbgUserPrint(_T("Mail tsstress - orig len %d, decompressed len %d\n"), thisLength,cbDecompLen); DbgUserPrint(_T("pHdr 0x%x, inlen %d\n"), pHdr, thisLength); DbgUserPrint(_T("compression result %d\n"),compressResult); DbgUserPrint(_T("pDecompOutBuf 0x%x, cbDecompLen %d\n"), pDecompOutBuf, cbDecompLen); DebugBreak(); } if (memcmp(pDecompOutBuf, (PUCHAR)(pDecouple->pNextData), cbDecompLen)) { DbgUserPrint(_T("Decompressed buffer does not match original!")); DbgUserPrint(_T("Mail tsstress!")); DbgUserPrint(_T("pHdr 0x%x, inlen %d\n"), pHdr, thisLength); DbgUserPrint(_T("compression result %d\n"),compressResult); DbgUserPrint(_T("pDecompOutBuf 0x%x, cbDecompLen %d\n"), pDecompOutBuf, cbDecompLen); DebugBreak(); } } else { DbgUserPrint(_T("Decompression check failed!")); DbgUserPrint(_T("Mail tsstress!")); DbgUserPrint(_T("pHdr 0x%x, inlen %d\n"),pHdr, thisLength); DbgUserPrint(_T("compression result %d\n"),compressResult); DbgUserPrint(_T("pDecompOutBuf 0x%x, cbDecompLen %d\n"), pDecompOutBuf, cbDecompLen); DebugBreak(); } } #endif //Update the VC packet header flags with the compression info UINT32 newFlags = (pDecouple->flags | ((compressResult & VC_FLAG_COMPRESS_MASK) << VC_FLAG_COMPRESS_SHIFT)); memcpy((UNALIGNED UINT32 *)&(pHdr->flags), &newFlags,sizeof(newFlags)); //Succesfully compressed no need to direct copy fNeedToDirectCopy = FALSE; cbOutLen += sizeof(CHANNEL_PDU_HEADER); } else { _iDbgCompressFailedCount++; #ifdef DEBUG_CCHAN_COMPRESSION DebugBreak(); #endif TRC_ERR((TB, _T("IntChannelCompressData failed"))); _pSl->SL_FreeBuffer(bufHnd); DC_QUIT; } } } //Copy buffer directly if compression not enabled //or the buffer does not fit size range for compression if(fNeedToDirectCopy) { DC_MEMCPY(pHdr+1, pDecouple->pNextData, thisLength); cbOutLen = thisLength + sizeof(CHANNEL_PDU_HEADER); } #ifdef DC_DEBUG //Compr counters _cbTotalBytesUserAskSend += thisLength; _cbTotalBytesSent += cbOutLen; #endif TRC_DATA_DBG("Send channel data", pBuffer, cbOutLen); /************************************************************************/ /* Get the channel index */ /************************************************************************/ chanIndex = pDecouple->openHandle; /************************************************************************/ /* Send the Channel PDU */ /************************************************************************/ _pSl->SL_SendPacket(pBuffer, cbOutLen, _channelData[chanIndex].SLFlags, bufHnd, _pUi->UI_GetClientMCSID(), _channelData[chanIndex].MCSChannelID, _channelData[chanIndex].priority); /************************************************************************/ /* Set up for next iteration */ /************************************************************************/ pDecouple->pNextData = ((HPDCUINT8)(pDecouple->pNextData)) + thisLength; pDecouple->dataLeft -= thisLength; pDecouple->dataSent += thisLength; pDecouple->flags = _channelData[chanIndex].VCFlags; TRC_NRM((TB, _T("Done write %p, src %p, sent %lu, left %lu, flags %#lx"), pDecouple, pDecouple->pNextData, pDecouple->dataSent, pDecouple->dataLeft, pDecouple->flags)); /************************************************************************/ /* See if we've finished this operation */ /************************************************************************/ if (pDecouple->dataLeft <= 0) { /********************************************************************/ /* Remove the operation from the queue */ /********************************************************************/ _pFirstWrite = pDecouple->pNext; if (_pFirstWrite == NULL) { TRC_NRM((TB, _T("Finished last write"))); _pLastWrite = NULL; } else { TRC_NRM((TB, _T("New first in queue: %p"), _pFirstWrite)); _pFirstWrite->pPrev = NULL; } /********************************************************************/ /* Operation complete - call the callback */ /********************************************************************/ TRC_NRM((TB, _T("Write %p complete"), pDecouple)); if(_channelData[chanIndex].pInitHandle->fUsingExApi) { TRC_ASSERT((NULL != _channelData[chanIndex].pOpenEventExFn), (TB, _T("Callback %p, handle %ld"), _channelData[chanIndex].pOpenEventExFn, pDecouple->openHandle)); /************************************************************************/ /* Is the channel still open? This was checked in */ /* IntVirtualChannelWrite, but that was before the post to this thread. */ /* If the VC was closed, it's possible we no longer have callback */ /* pointers. */ /************************************************************************/ if (NULL != _channelData[chanIndex].pOpenEventExFn) { _channelData[chanIndex].pOpenEventExFn( _channelData[chanIndex].pInitHandle->lpParam, pDecouple->openHandle, CHANNEL_EVENT_WRITE_COMPLETE, pDecouple->pUserData, 0, 0, 0); } } else { TRC_ASSERT((NULL != _channelData[chanIndex].pOpenEventFn), (TB, _T("Callback %p, handle %ld"), _channelData[chanIndex].pOpenEventFn, pDecouple->openHandle)); if (NULL != _channelData[chanIndex].pOpenEventFn) { _channelData[chanIndex].pOpenEventFn( pDecouple->openHandle, CHANNEL_EVENT_WRITE_COMPLETE, pDecouple->pUserData, 0, 0, 0); } } /********************************************************************/ /* Free the request */ /********************************************************************/ UT_Free( _pUt,pDecouple); } /************************************************************************/ /* Kick the process again if there's anything left to do */ /************************************************************************/ if (_pFirstWrite != NULL) { TRC_NRM((TB, _T("More work to do %p"), _pFirstWrite)); _pCd->CD_DecoupleSimpleNotification(CD_SND_COMPONENT, this, CD_NOTIFICATION_FUNC(CChan,IntChannelSend), CHANNEL_MSG_SEND); } /************************************************************************/ /* Note that if we failed to get a buffer above, we won't kick the */ /* process into continuing. This is done later, on receipt of an */ /* OnBufferAvailable callback from SL. */ /************************************************************************/ DC_EXIT_POINT: DC_END_FN(); return; } /* IntChannelSend */ /**PROC+*********************************************************************/ /* Name: IntChannelWrite */ /* */ /* Purpose: Start writing data to the Server */ /* */ /* Returns: none */ /* */ /* Params: pData - CHANNEL_WRITE_DECOUPLE structure */ /* dataLength - length of pData */ /* */ /* Operation: Called on SND thread */ /* */ /**PROC-*********************************************************************/ DCVOID DCINTERNAL CChan::IntChannelWrite(PDCVOID pData, DCUINT dataLength) { PCHANNEL_WRITE_DECOUPLE pDecouple; DC_BEGIN_FN("IntChannelWrite"); #ifndef DC_DEBUG UNREFERENCED_PARAMETER(dataLength); #endif /************************************************************************/ /* Check parameters */ /************************************************************************/ TRC_ASSERT((dataLength == sizeof(PCHANNEL_WRITE_DECOUPLE)), (TB, _T("Wrong size data: is/expect %d/%d"), dataLength, sizeof(PCHANNEL_WRITE_DECOUPLE))); TRC_ASSERT((((_pFirstWrite == NULL) && (_pLastWrite == NULL)) || ((_pFirstWrite != NULL) && (_pLastWrite != NULL))), (TB,_T("Invalid queue, pFirst %p, pLast %p"), _pFirstWrite, _pLastWrite)); pDecouple = *((PPCHANNEL_WRITE_DECOUPLE)pData); TRC_NRM((TB, _T("Receive %p -> %p"), pData, pDecouple)); TRC_ASSERT((pDecouple->signature == CHANNEL_DECOUPLE_SIGNATURE), (TB,_T("Invalid decouple signature %#lx"), pDecouple->signature)); /************************************************************************/ /* Add this request to the queue */ /************************************************************************/ if (_pFirstWrite == NULL) { /********************************************************************/ /* Empty queue */ /********************************************************************/ TRC_NRM((TB, _T("Empty queue"))); _pFirstWrite = pDecouple; _pLastWrite = pDecouple; } else { /********************************************************************/ /* Non-empty queue */ /********************************************************************/ TRC_NRM((TB, _T("Non-empty queue: first %p, last %p"), _pFirstWrite, _pLastWrite)); pDecouple->pPrev = _pLastWrite; _pLastWrite->pNext = pDecouple; _pLastWrite = pDecouple; } TRC_ASSERT((_pFirstWrite->signature == CHANNEL_DECOUPLE_SIGNATURE), (TB,_T("Invalid first signature %#lx"), _pFirstWrite->signature)); TRC_ASSERT((_pLastWrite->signature == CHANNEL_DECOUPLE_SIGNATURE), (TB,_T("Invalid last signature %#lx"), _pLastWrite->signature)); /************************************************************************/ /* Try to send the data */ /************************************************************************/ IntChannelSend(CHANNEL_MSG_SEND); DC_END_FN(); return; } /* IntChannelWrite */ /**PROC+*********************************************************************/ /* Name: IntChannelLoad */ /* */ /* Purpose: Load an Addin */ /* */ /* Returns: none */ /* */ /* Params: DLLName - name of Addin DLL to load */ /* */ /**PROC-*********************************************************************/ DCVOID DCINTERNAL CChan::IntChannelLoad(PDCTCHAR DLLName) { DCBOOL rc = FALSE; PVIRTUALCHANNELENTRY pChannelEntry; PVIRTUALCHANNELENTRYEX pChannelEntryEx; HMODULE hMod; PCHANNEL_INIT_HANDLE pAddin; DC_BEGIN_FN("IntChannelLoad"); /************************************************************************/ /* Load the DLL */ /************************************************************************/ hMod = LoadLibrary(DLLName); if (!hMod) { TRC_ERR((TB, _T("Failed to load %s"), DLLName)); DC_QUIT; } TRC_NRM((TB, _T("Loaded %s (%p)"), DLLName, hMod)); /************************************************************************/ /* Search the already-loaded Addins in case this is a duplicate */ /************************************************************************/ for (pAddin = _pInitHandle; pAddin != NULL; pAddin = pAddin->pNext) { TRC_DBG((TB, _T("Compare %s, %p, %p"), DLLName, pAddin->hMod, hMod)); if (pAddin->hMod == hMod) { TRC_ERR((TB, _T("Reloading %s (%p)"), DLLName, hMod)); DC_QUIT; } } /************************************************************************/ /* DLL loaded OK - find its VirtualChannelEntry function */ /************************************************************************/ // // First try to find the Ex entry point // pChannelEntryEx = (PVIRTUALCHANNELENTRYEX)GetProcAddress(hMod, CE_WIDETEXT("VirtualChannelEntryEx")); if(pChannelEntryEx) { TRC_NRM((TB,_T("Found EX entry point, Init using ex api: %s"), DLLName)); IntChannelInitAddin( NULL, pChannelEntryEx, hMod, DLLName, NULL); } else { // // Only try to load legacy DLL's from the first instance // if( CChan::pStaticClientInstance == this) { TRC_NRM((TB,_T("Did not find EX entry point, looking for old api: %s"), DLLName)); pChannelEntry = (PVIRTUALCHANNELENTRY)GetProcAddress(hMod, CE_WIDETEXT("VirtualChannelEntry")); if (pChannelEntry == NULL) { TRC_ERR((TB, _T("Failed to find VirtualChannelEntry in %s"), DLLName)); DC_QUIT; } IntChannelInitAddin( pChannelEntry, NULL, hMod, DLLName, NULL); } } DC_EXIT_POINT: DC_END_FN(); return; } /* IntChannelLoad */ /**PROC+*********************************************************************/ /* Name: IntChannelInitAddin */ /* */ /* Purpose: Initialize addin given it's entry point */ /* */ /* Returns: Success flag */ /* */ /* Params: pChannelEntry - Addin entry point */ /* */ /**PROC-*********************************************************************/ DCBOOL DCINTERNAL CChan::IntChannelInitAddin(PVIRTUALCHANNELENTRY pChannelEntry, PVIRTUALCHANNELENTRYEX pChannelEntryEx, HMODULE hMod, PDCTCHAR DLLName, PVOID pPassParamToEx) { DCBOOL rc = FALSE; PCHANNEL_ENTRY_POINTS pTempEntryPoints = NULL; PCHANNEL_ENTRY_POINTS_EX pTempEntryPointsEx = NULL; UINT i=0; DC_BEGIN_FN("IntChannelInitAddin"); _newInitHandle = NULL; if (pChannelEntry == NULL && pChannelEntryEx == NULL) { TRC_ERR((TB, _T("Invalid VirtualChannelEntry"))); DC_QUIT; } if (DLLName == NULL) { TRC_ERR((TB, _T("Invalid DLLName"))); DC_QUIT; } TRC_NRM((TB, _T("VirtualChannelEntry at %p"), pChannelEntry)); TRC_NRM((TB, _T("VirtualChannelEntryEx at %p"), pChannelEntryEx)); /************************************************************************/ /* Allocate and initialize a handle */ /************************************************************************/ _newInitHandle = (PCHANNEL_INIT_HANDLE)UT_Malloc( _pUt,sizeof(CHANNEL_INIT_HANDLE)); if (_newInitHandle == NULL) { TRC_ERR((TB, _T("Failed to allocate handle"))); DC_QUIT; } _newInitHandle->signature = CHANNEL_INIT_SIGNATURE; _newInitHandle->hMod = hMod; _newInitHandle->pInst = this; // //ChannelCount for this addin is marked as 0 now //it will be updated by the plugin's calls to VirtualChannelInit //if VirtualChannelEntry returns false, this count will be used //to rollback any created channels. // _newInitHandle->channelCount = 0; // // Internal addin's can get params passed back down // today this is used so the control can pass it's internal // an interface pointer to the virtual channel scripting addin // _newInitHandle->lpInternalAddinParam = pPassParamToEx; /************************************************************************/ /* Allocate and fill a temporary structure in which to pass the entry */ /* points. This keeps our global entry points structure safe from */ /* badly-behaved addins that could overwrite it and stop other addins */ /* from working correctly. Note that addins must copy this structure */ /* -- it is only valid during this call to VirtualChannelEntry. */ /************************************************************************/ if(pChannelEntryEx) { pTempEntryPointsEx = (PCHANNEL_ENTRY_POINTS_EX)UT_Malloc( _pUt,sizeof(CHANNEL_ENTRY_POINTS_EX)); if (pTempEntryPointsEx == NULL) { TRC_ERR((TB, _T("Failed to allocate temporary entry points (Ex) structure"))); DC_QUIT; } DC_MEMCPY(pTempEntryPointsEx, &_channelEntryPointsEx, sizeof(CHANNEL_ENTRY_POINTS_EX)); } else { pTempEntryPoints = (PCHANNEL_ENTRY_POINTS)UT_Malloc( _pUt,sizeof(CHANNEL_ENTRY_POINTS)); if (pTempEntryPoints == NULL) { TRC_ERR((TB, _T("Failed to allocate temporary entry points structure"))); DC_QUIT; } DC_MEMCPY(pTempEntryPoints, &_channelEntryPoints, sizeof(CHANNEL_ENTRY_POINTS)); } /************************************************************************/ /* Call VirtualChannelEntry */ /************************************************************************/ _ChannelInitCalled = FALSE; _inChannelEntry = TRUE; if(pChannelEntryEx) { // // Pass the adddin a pointer to the new init handle // rc = pChannelEntryEx(pTempEntryPointsEx, _newInitHandle); } else { rc = pChannelEntry(pTempEntryPoints); } _inChannelEntry = FALSE; if (!rc) { TRC_NRM((TB, _T("ChannelEntry aborted"))); DC_QUIT; } /************************************************************************/ /* Make sure that the Addin called VirtualChannelInit from */ /* VirtualChannelEntry */ /************************************************************************/ if (!_ChannelInitCalled) { TRC_ERR((TB, _T("Addin %s didn't call VirtualChannelInit"), DLLName)); rc = FALSE; DC_QUIT; } /************************************************************************/ /* Everything OK - insert this handle into chain of Init Handles */ /************************************************************************/ _newInitHandle->pPrev = NULL; _newInitHandle->pNext = _pInitHandle; if (_pInitHandle != NULL) { _pInitHandle->pPrev = _newInitHandle; } _pInitHandle = _newInitHandle; rc = TRUE; DC_EXIT_POINT: if (!rc) { TRC_NRM((TB, _T("Something failed - tidy up"))); if (hMod) { TRC_NRM((TB, _T("Free the library"))); FreeLibrary(hMod); } if (_newInitHandle) { // // Remove any channel entries that were created // for this plugin. These should be consecutive channels at the tail // of the channels array. // if(_newInitHandle->channelCount) { UINT startRemoveIdx = _channelCount - _newInitHandle->channelCount; TRC_ASSERT((startRemoveIdx < _channelCount), (TB,_T("startRemoveIdx for channel cleanup is invalid"))); if(startRemoveIdx < _channelCount) { // // Rollback creation of virtual channels // for( i=startRemoveIdx; i<_channelCount; i++) { TRC_ASSERT((_channelData[i].pInitHandle == _newInitHandle), (TB,_T("_channelData[i].pInitHandle != _newInitHandle on rollback"))); if(_channelData[i].pInitHandle == _newInitHandle) { _channel[i].options = ~CHANNEL_OPTION_INITIALIZED; DC_MEMSET(_channel[i].name, 0, CHANNEL_NAME_LEN+1); _channelData[i].pOpenEventExFn = NULL; _channelData[i].pOpenEventFn = NULL; _channelData[i].status = CHANNEL_STATUS_CLOSED; } else { break; } } _channelCount -= _newInitHandle->channelCount; } } TRC_NRM((TB, _T("Free unused handle"))); UT_Free( _pUt,_newInitHandle); } } if (pTempEntryPoints) { UT_Free( _pUt,pTempEntryPoints); } if (pTempEntryPointsEx) { UT_Free( _pUt, pTempEntryPointsEx); } DC_END_FN(); return rc; } /* IntChannelInitAddin */ /**PROC+*********************************************************************/ /* Name: IntChannelCancelSend */ /* */ /* Purpose: Cancel outstanding send requests */ /* */ /* Returns: none */ /* */ /* Params: value - message passed from caller */ /* */ /* Operation: Called on SND thread */ /* */ /* */ /**PROC-*********************************************************************/ DCVOID DCINTERNAL CChan::IntChannelCancelSend(ULONG_PTR value) { PCHANNEL_WRITE_DECOUPLE pDecouple; PCHANNEL_WRITE_DECOUPLE pFree; DWORD chanIndex = 0xFDFDFDFD; DC_BEGIN_FN("IntChannelCancelSend"); //UNREFERENCED_PARAMETER( value ); pDecouple = _pFirstWrite; while (pDecouple != NULL) { TRC_ASSERT((pDecouple->signature == CHANNEL_DECOUPLE_SIGNATURE), (TB,_T("Invalid decouple signature %#lx"), pDecouple->signature)); chanIndex = pDecouple->openHandle; if ((value == CHANNEL_MSG_SUSPEND) && (_channelData[chanIndex].VCFlags & CHANNEL_FLAG_SHADOW_PERSISTENT)) { // skip this one as it should not be closed pDecouple = pDecouple->pNext; continue; } /********************************************************************/ /* Call the callback */ /********************************************************************/ TRC_NRM((TB, _T("Write %p cancelled"), pDecouple)); if(_channelData[chanIndex].pInitHandle->fUsingExApi) { _channelData[chanIndex].pOpenEventExFn( _channelData[chanIndex].pInitHandle->lpParam, pDecouple->openHandle, CHANNEL_EVENT_WRITE_CANCELLED, pDecouple->pUserData, 0, 0, 0); } else { _channelData[chanIndex].pOpenEventFn( pDecouple->openHandle, CHANNEL_EVENT_WRITE_CANCELLED, pDecouple->pUserData, 0, 0, 0); } /********************************************************************/ /* Free the decouple structure */ /********************************************************************/ pFree = pDecouple; pDecouple = pDecouple->pNext; if (pDecouple) { pDecouple->pPrev = pFree->pPrev; } else { _pLastWrite = pFree->pPrev; } if (pFree->pPrev) { pFree->pPrev->pNext = pDecouple; } else { _pFirstWrite = pDecouple; } pFree->signature = 0; UT_Free( _pUt,pFree); } if (value != CHANNEL_MSG_SUSPEND) { _pFirstWrite = NULL; _pLastWrite = NULL; } DC_END_FN(); return; } /* IntChannelCancelSend */