/****************************************************************************/ // anmapi.c // // Network Manger // // Copyright(C) Microsoft Corporation 1997-1999 /****************************************************************************/ #include #pragma hdrstop #define TRC_GROUP TRC_GROUP_NETWORK #define TRC_FILE "anmapi" #define pTRCWd (pRealNMHandle->pWDHandle) #include #include #include #include #include #include #include #include /****************************************************************************/ /* Name: NM_GetDataSize */ /* */ /* Purpose: Returns size of per-instance NM data required */ /* */ /* Returns: size of data required */ /* */ /* Operation: NM stores per-instance data in a piece of memory allocated */ /* by WDW. This function returns the size of the data required. */ /* A pointer to this data (the 'NM Handle') is passed into all */ /* subsequent NM functions. */ /****************************************************************************/ unsigned RDPCALL NM_GetDataSize(void) { DC_BEGIN_FN("NM_GetDataSize"); DC_END_FN(); return(sizeof(NM_HANDLE_DATA)); } /* NM_GetDataSize */ /****************************************************************************/ /* Name: NM_Init */ /* */ /* Purpose: Initialize NM */ /* */ /* Returns: TRUE - Registered OK */ /* FALSE - Registration failed */ /* */ /* Params: pNMHandle - NM handle */ /* pSMHandle - SM handle, stored by NM and passed on callbacks */ /* to SM */ /* pWDHandle - WD handle, required for tracing */ /* hDomainKernel - MCS handle stored from MCSInitialize() and */ /* used to attach a user. */ /* */ /* Operation: Initialize NM: */ /* - initialize per-instance data */ /* - open channel for communication with PDMCS */ /****************************************************************************/ BOOL RDPCALL NM_Init(PVOID pNMHandle, PVOID pSMHandle, PTSHARE_WD pWDHandle, DomainHandle hDomainKernel) { PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle; DC_BEGIN_FN("NM_Init"); /************************************************************************/ /* WARNING: Don't trace before storing the WD Handle */ /************************************************************************/ pRealNMHandle->pWDHandle = pWDHandle; pRealNMHandle->pSMHandle = pSMHandle; pRealNMHandle->pContext = pWDHandle->pContext; pRealNMHandle->hDomain = hDomainKernel; DC_END_FN(); return(TRUE); } /* NM_Init */ /****************************************************************************/ /* Name: NM_Term */ /* */ /* Purpose: Terminate NM */ /* */ /* Params: pNMHandle - NM handle */ /* */ /* Operation: Terminate NM */ /* - close channel opened by NM_Init */ /****************************************************************************/ void RDPCALL NM_Term(PVOID pNMHandle) { PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle; unsigned i; PNM_CHANNEL_DATA pChannelData; DC_BEGIN_FN("NM_Term"); TRC_NRM((TB, "Terminate NM")); /************************************************************************/ /* Free any half-received virtual channel data */ /************************************************************************/ for (i = 0, pChannelData = pRealNMHandle->channelData; i < pRealNMHandle->channelArrayCount; i++, pChannelData++) { if (pChannelData->pData != NULL) { TRC_NRM((TB, "Free %p", pChannelData->pData)); COM_Free(pChannelData->pData); } } DC_END_FN(); } /* NM_Term */ /****************************************************************************/ /* Name: NM_Connect */ /* */ /* Purpose: Starts the process of connecting to the Client */ /* */ /* Returns: TRUE - Connect started OK */ /* FALSE - Connect failed to start */ /* */ /* Params: pNMHandle - NM handle */ /* pUserDataIn - user data received from Client */ /* */ /* Operation: Attach the user to the domain. PDMCS knows about only 1 */ /* domain, so that one is assumed. */ /* */ /* When the AttachUser completes, join two channels: */ /* - the dynamically allocated broadcast channel (ID returned on */ /* the SM_OnConnected callback) */ /* - the single-user channel for this user. */ /* */ /* Note that this function completes asynchronously. The caller */ /* must wait for a SM_OnConnected or SM_OnDisconnected */ /* callback to find out whether the Connect succeeded or failed. */ /****************************************************************************/ BOOL RDPCALL NM_Connect(PVOID pNMHandle, PRNS_UD_CS_NET pUserDataIn) { BOOL rc = FALSE; BOOLEAN bCompleted; MCSError MCSErr; ChannelHandle hChannel; PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle; unsigned i, j; unsigned userDataOutLength; PRNS_UD_SC_NET pUserDataOut = NULL; PCHANNEL_DEF pChannel; UINT16 *pMCSChannel; ChannelID ChID; UINT32 DataLenValidate; // for unalignment use purpose UserHandle UserHandleTemp; unsigned MaxSendSizeTemp; DC_BEGIN_FN("NM_Connect"); /************************************************************************/ /* Clear the connection status */ /************************************************************************/ pRealNMHandle->connectStatus = 0; /************************************************************************/ /* Save virtual channel data */ /************************************************************************/ if (pUserDataIn != NULL) { TRC_DATA_NRM("Net User Data", pUserDataIn, pUserDataIn->header.length); TRC_NRM((TB, "Protocol version %#x (%#x/%#x)", pRealNMHandle->pWDHandle->version, _RNS_MAJOR_VERSION(pRealNMHandle->pWDHandle->version), _RNS_MINOR_VERSION(pRealNMHandle->pWDHandle->version))); /********************************************************************/ /* Protocol version 0x00080002 used 2-byte channel data lengths. */ /* Protocol version 0x00080003 and higher use 4-byte data lengths. */ /* If this is a 2-byte version, ignore its virtual channels. */ /********************************************************************/ if (_RNS_MINOR_VERSION(pRealNMHandle->pWDHandle->version) >= 3) { // Validate channle count DataLenValidate = pUserDataIn->channelCount * sizeof(CHANNEL_DEF); DataLenValidate += sizeof(RNS_UD_CS_NET); if (DataLenValidate > pUserDataIn->header.length) { TRC_ERR((TB, "Error: Virtual channel data length %u too short for %u", pUserDataIn->header.length, DataLenValidate)); pRealNMHandle->channelCount = 0; pRealNMHandle->channelArrayCount = 0; WDW_LogAndDisconnect(pRealNMHandle->pWDHandle, TRUE, Log_RDP_VChannelDataTooShort, (PBYTE)pUserDataIn, pUserDataIn->header.length); DC_QUIT; } // we reserve channel 7 for RDPDD so we can allow only the // buffer size - 1 channels. if (pUserDataIn->channelCount > sizeof(pRealNMHandle->channelData) / sizeof(pRealNMHandle->channelData[0]) - 1) { TRC_ERR((TB, "Error: Too many virtual channels to join: %u.", pUserDataIn->channelCount)); pRealNMHandle->channelCount = 0; pRealNMHandle->channelArrayCount = 0; WDW_LogAndDisconnect(pRealNMHandle->pWDHandle, TRUE, Log_RDP_VChannelsTooMany, (PBYTE)pUserDataIn, pUserDataIn->header.length); DC_QUIT; } pRealNMHandle->channelCount = pUserDataIn->channelCount; pChannel = (PCHANNEL_DEF)(pUserDataIn + 1); for (i = 0, j = 0; i < pRealNMHandle->channelCount; i++, j++) { /************************************************************/ /* Channel 7 is used by RDPDD, so skip it */ /************************************************************/ if (i == WD_THINWIRE_CHANNEL) { j++; } /************************************************************/ /* Save channel data */ /************************************************************/ strncpy(pRealNMHandle->channelData[j].name, pChannel[i].name, CHANNEL_NAME_LEN); // make sure that the name is zero-terminated (pRealNMHandle->channelData[j].name)[CHANNEL_NAME_LEN] = 0; pRealNMHandle->channelData[j].flags = pChannel[i].options; TRC_NRM((TB, "Channel %d (was %d): %s", j, i, pChannel[i].name)); } pRealNMHandle->channelArrayCount = j; } else { TRC_ERR((TB, "Minor version %#x doesn't support 4-byte channel data lengths", _RNS_MINOR_VERSION(pRealNMHandle->pWDHandle->version))); pRealNMHandle->channelCount = 0; pRealNMHandle->channelArrayCount = 0; } } else { /********************************************************************/ /* No incoming user data = no virtual channels */ /********************************************************************/ TRC_NRM((TB, "No virtual channels")); pRealNMHandle->channelCount = 0; pRealNMHandle->channelArrayCount = 0; } /************************************************************************/ /* Allocate space for returned user data */ /************************************************************************/ userDataOutLength = (sizeof(RNS_UD_SC_NET) + (sizeof(UINT16) * pRealNMHandle->channelCount)); userDataOutLength = (unsigned)(DC_ROUND_UP_4(userDataOutLength)); pUserDataOut = COM_Malloc(userDataOutLength); if (pUserDataOut != NULL) { memset(pUserDataOut, 0, userDataOutLength); } else { TRC_ERR((TB, "Failed to alloc %d bytes for user data", userDataOutLength)); DC_QUIT; } /************************************************************************/ /* Make the attach-user request call. */ /************************************************************************/ TRC_NRM((TB, "Attach User")); UserHandleTemp = pRealNMHandle->hUser; MaxSendSizeTemp = pRealNMHandle->maxPDUSize; MCSErr = MCSAttachUserRequest(pRealNMHandle->hDomain, NM_MCSUserCallback, SM_MCSSendDataCallback, pNMHandle, &UserHandleTemp, &MaxSendSizeTemp, (BOOLEAN *)(&bCompleted)); pRealNMHandle->hUser = UserHandleTemp; pRealNMHandle->maxPDUSize = MaxSendSizeTemp; if (MCSErr == MCS_NO_ERROR) { TRC_NRM((TB, "AttachUser OK, hUser %p", pRealNMHandle->hUser)); TRC_ASSERT((bCompleted), (TB, "MCSAttachUser didn't complete synchronously")); // Extract extra information. pRealNMHandle->userID = MCSGetUserIDFromHandle(pRealNMHandle->hUser); pRealNMHandle->connectStatus |= NM_CONNECT_ATTACH; TRC_NRM((TB, "Attached as user %x, hUser %p", pRealNMHandle->userID, pRealNMHandle->hUser)); } else { TRC_ERR((TB, "Failed AttachUserRequest, MCSErr %d", MCSErr)); DC_QUIT; } /************************************************************************/ /* Join the broadcast channel */ /************************************************************************/ MCSErr = MCSChannelJoinRequest(pRealNMHandle->hUser, 0, &hChannel, &bCompleted); if (MCSErr == MCS_NO_ERROR) { TRC_ASSERT((bCompleted), (TB, "MCSChannelJoin didn't complete synchronously")); // Extract information. ChID = MCSGetChannelIDFromHandle(hChannel); pRealNMHandle->channelID = ChID; pRealNMHandle->hChannel = hChannel; pRealNMHandle->connectStatus |= NM_CONNECT_JOIN_BROADCAST; TRC_NRM((TB, "Joined broadcast channel %x (hChannel %p) OK", ChID, hChannel)); } else { TRC_ERR((TB, "Failed to send ChannelJoinRequest, MCSErr %d", MCSErr)); DC_QUIT; } /************************************************************************/ /* Join the user channel */ /************************************************************************/ MCSErr = MCSChannelJoinRequest(pRealNMHandle->hUser, pRealNMHandle->userID, &hChannel, &bCompleted); if (MCSErr == MCS_NO_ERROR) { TRC_ASSERT((bCompleted), (TB, "MCSChannelJoin didn't complete synchronously")); // Extract information. pRealNMHandle->connectStatus |= NM_CONNECT_JOIN_USER; TRC_NRM((TB, "Joined user channel (hChannel %p) OK", hChannel)); } else { TRC_ERR((TB, "Failed to send ChannelJoinRequest, MCSErr %d", MCSErr)); DC_QUIT; } /************************************************************************/ /* If no virtual channels, we're done. */ /************************************************************************/ if (pRealNMHandle->channelCount == 0) { TRC_NRM((TB, "No virtual channels to join")); rc = TRUE; DC_QUIT; } /************************************************************************/ /* Join virtual channels */ /************************************************************************/ for (i = 0; i < pRealNMHandle->channelArrayCount; i++) { if (i == WD_THINWIRE_CHANNEL) { TRC_NRM((TB, "Skip channel %d", WD_THINWIRE_CHANNEL)); continue; } MCSErr = MCSChannelJoinRequest(pRealNMHandle->hUser, 0, &hChannel, &bCompleted); if (MCSErr == MCS_NO_ERROR) { TRC_ASSERT((bCompleted), (TB, "MCSChannelJoin didn't complete synchronously")); ChID = MCSGetChannelIDFromHandle(hChannel); pRealNMHandle->channelData[i].MCSChannelID = (UINT16)ChID; TRC_NRM((TB, "Joined VC %d: %d (hChannel %p)", i, ChID, hChannel)); } else { TRC_ERR((TB, "ChannelJoinRequest failed, MCSErr %d", MCSErr)); DC_QUIT; } } /************************************************************************/ /* Everything completed OK */ /************************************************************************/ rc = TRUE; DC_EXIT_POINT: if (rc) { /********************************************************************/ /* Everything is OK - Complete the user data */ /********************************************************************/ pUserDataOut->header.type = RNS_UD_SC_NET_ID; pUserDataOut->header.length = (UINT16)userDataOutLength; pUserDataOut->MCSChannelID = (UINT16)pRealNMHandle->channelID; pUserDataOut->channelCount = (UINT16)pRealNMHandle->channelCount; pMCSChannel = (UINT16 *)(pUserDataOut + 1); TRC_NRM((TB, "Copy %d channels to user data out", pRealNMHandle->channelCount)); for (i = 0, j = 0; i < pRealNMHandle->channelCount; i++, j++) { if (i == WD_THINWIRE_CHANNEL) { TRC_NRM((TB, "Skip channel %d", WD_THINWIRE_CHANNEL)); j++; } pMCSChannel[i] = pRealNMHandle->channelData[j].MCSChannelID; TRC_NRM((TB, "Channel %d (%d) = %#x", i, j, pMCSChannel[i])); } /********************************************************************/ /* Tell SM we're connected now */ /********************************************************************/ TRC_NRM((TB, "Tell SM we're connecting")); SM_OnConnected(pRealNMHandle->pSMHandle, pRealNMHandle->userID, NM_CB_CONN_OK, pUserDataOut, pRealNMHandle->maxPDUSize); } else { /********************************************************************/ /* Something failed - abort the connection */ /********************************************************************/ TRC_NRM((TB, "Something failed - abort the connection")); NMAbortConnect(pRealNMHandle); } /************************************************************************/ /* Whether we succeeded or failed, we don't need the user data any */ /* more. */ /************************************************************************/ TRC_NRM((TB, "Free user data")); if (pUserDataOut != NULL) { COM_Free(pUserDataOut); } DC_END_FN(); return(rc); } /* NM_Connect */ /****************************************************************************/ /* Name: NM_GetMCSDomainInfo */ /* */ /* Purpose: Return the broadcast channel ID to allow shadowing of this */ /* share. */ /* */ /* Params: pNMHandle - NM handle */ /****************************************************************************/ ChannelID RDPCALL NM_GetMCSDomainInfo(PVOID pNMHandle) { PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle; return pRealNMHandle->channelID; } /****************************************************************************/ /* Name: NM_Disconnect */ /* */ /* Purpose: Disconnect from a Client */ /* */ /* Returns: TRUE - Disconnect started OK */ /* FALSE - Disconnect failed */ /* */ /* Params: pNMHandle - NM handle */ /* */ /* Operation: Detach the user from the domain. */ /* */ /* Note that this function completes asynchronously. The caller */ /* must wait for a SM_OnDisconnected callback to find whether */ /* the Disconnect succeeded or failed. */ /****************************************************************************/ BOOL RDPCALL NM_Disconnect(PVOID pNMHandle) { PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle; BOOL rc = TRUE; DC_BEGIN_FN("NM_Disconnect"); /************************************************************************/ /* Detach from MCS */ /************************************************************************/ if (pRealNMHandle->connectStatus & NM_CONNECT_ATTACH) { TRC_NRM((TB, "User attached, need to detach")); rc = NMDetachUserReq(pRealNMHandle); } DC_END_FN(); return(rc); } /* NM_Disconnect */ /****************************************************************************/ /* Name: NM_AllocBuffer */ /* */ /* Purpose: Acquire a buffer for transmission */ /* */ /* Returns: TRUE - Buffer acquired OK */ /* FALSE - No buffer available */ /* */ /* Params: pNMHandle - NM handle */ /* ppBuffer - Buffer acquired (returned) */ /* bufferSize - size of buffer required */ /* */ /* Operation: Get a buffer from ICA (via IcaBufferAlloc) */ /* This function is synchronous. */ /****************************************************************************/ NTSTATUS __fastcall NM_AllocBuffer(PVOID pNMHandle, PPVOID ppBuffer, UINT32 bufferSize, BOOLEAN fWait) { PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle; NTSTATUS status; POUTBUF pOutBuf; int i; UINT32 realBufferSize; DC_BEGIN_FN("NM_AllocBuffer"); /************************************************************************/ /* Calculate the actual size of data required. Includes a POUTBUF */ /* prefix to point back to the beginning of the OutBuf so we can send */ /* the right thing to MCS. */ /************************************************************************/ TRC_ASSERT((bufferSize < 16384), (TB,"Buffer req size %u will cause MCS fragmentation, unsupported", bufferSize)); realBufferSize = bufferSize + SendDataReqPrefixBytes + sizeof(POUTBUF); /************************************************************************/ /* Allocate an OutBuf */ /************************************************************************/ status = IcaBufferAlloc(pRealNMHandle->pContext, fWait, /* wait/not wait for a buffer */ FALSE, /* not a control buffer */ realBufferSize, NULL, /* no original buffer */ (PVOID *)(&pOutBuf)); if (status == STATUS_SUCCESS) { // NT_SUCCESS() does not fail STATUS_TIMEOUT /********************************************************************/ /* The OutBuf returned includes a data buffer pointer, which points */ /* to a buffer containing */ /* - a pointer to the beginning of the OutBuf (which we set here) */ /* - SendDataReqPrefixBytes */ /* - user data buffer (size bufferSize) */ /* Set the OutBuf pBuffer pointer to the beginning of the user data.*/ /* MCS requires this. */ /* Return to the user a pointer to the user data buffer. */ /********************************************************************/ *((POUTBUF *)pOutBuf->pBuffer) = pOutBuf; pOutBuf->pBuffer += SendDataReqPrefixBytes + sizeof(POUTBUF); *ppBuffer = pOutBuf->pBuffer; TRC_NRM((TB, "Alloc %d bytes OK", bufferSize)); } else { TRC_ERR((TB, "Failed to alloc %d bytes, status %x", bufferSize, status)); // // TODO - consider disconnect client here instead of SM_AllocBuffer(), // keep it in SM_AllocBuffer() so that we don't introduce any regression. // // // IcaBufferAlloc() returns STATUS_IO_TIMEOUT, STATUS_NO_MEMORY, and // IcaWaitForSingleObject() which returns KeWaitForSingleObject() or // STATUS_CTX_CLOSE_PENDING. SM_AllocBuffer() need to disconnect this client only // when error code is STATUS_IO_TIMEOUT so we keep this return code } DC_END_FN(); return status; } /* NM_AllocBuffer */ /****************************************************************************/ /* Name: NM_FreeBuffer */ /* */ /* Purpose: Free a transmit buffer */ /* */ /* Params: pNMHandle - NM handle */ /* pBuffer - Buffer to free */ /* */ /* Operation: Free a buffer (via IcaBufferFree) */ /* This function assumes the buffer was allocated using */ /* NM_AllocBuffer. */ /* */ /* This function is only for freeing buffers which are not sent. */ /* It should not be called for buffers which are sent - */ /* NM_SendData frees the buffer whether the Send succeeds or */ /* not. */ /* */ /* This function is synchronous. */ /****************************************************************************/ void __fastcall NM_FreeBuffer(PVOID pNMHandle, PVOID pBuffer) { POUTBUF pOutBuf; PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle; DC_BEGIN_FN("NM_FreeBuffer"); /************************************************************************/ /* Get the OutBuf pointer stored in prefix */ /************************************************************************/ pOutBuf = *((POUTBUF *) ((BYTE *)pBuffer - SendDataReqPrefixBytes - sizeof(POUTBUF))); /************************************************************************/ /* Free the buffer */ /************************************************************************/ IcaBufferFree(pRealNMHandle->pContext, pOutBuf); DC_END_FN(); } /* NM_FreeBuffer */ /****************************************************************************/ /* Name: NM_SendData */ /* */ /* Purpose: Send data to the appropriate net or pipe destination. */ /* */ /* Returns: TRUE - data sent OK */ /* FALSE - data not sent */ /* */ /* Params: pNMHandle - NM handle */ /* pData - data to send */ /* dataSize - length of data to send */ /* priority - priority to send the data on */ /* userID - user to send the data to (0 = broadcast data) */ /* FastPathOutputFlags - flags from higher layers. Low bit TRUE */ /* means send as fast-path output, using the rest of the */ /* /* */ /* Operation: Send the data. */ /* - The buffer holding the data must have been allocated using */ /* NM_AllocBuffer. */ /* - Return code FALSE means that a network error occurred. The */ /* caller need do nothing - an NM_DISCONNECTED callback will */ /* eventually arrive. */ /* - The buffer is ALWAYS freed. */ /* */ /* This function is synchronous. */ /****************************************************************************/ BOOL __fastcall NM_SendData( PVOID pNMHandle, PBYTE pData, UINT32 dataSize, UINT32 priority, UINT32 userID, UINT32 FastPathOutputFlags) { PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle; BOOL rc = TRUE; POUTBUF pOutBuf; MCSError MCSErr; DC_BEGIN_FN("NM_SendData"); /************************************************************************/ /* Get the OutBuf pointer stored in prefix */ /************************************************************************/ pOutBuf = *((POUTBUF *) ((BYTE *)pData - SendDataReqPrefixBytes - sizeof(POUTBUF))); /************************************************************************/ /* Complete the OutBuf. The pBuffer was already set up when the OutBuf */ /* was allocated. MCS needs the user data size set in the OutBuf. */ /************************************************************************/ pOutBuf->ByteCount = dataSize; // All but shadow passthru stacks get the data sent to the network. if (pRealNMHandle->pWDHandle->StackClass != Stack_Passthru) { if (FastPathOutputFlags & NM_SEND_FASTPATH_OUTPUT) { NTSTATUS Status; SD_RAWWRITE SdWrite; // Fast-path output skips MCS. We rewrite the security header // into the fast-path format, complete the OutBuf, and send // directly to the transport. For header format details, see // at128.h. Note we need to wait for the security header // to be written in SM before getting here in case this // is a passthru stack. // First, the 4-byte RNS_SECURITY_HEADER disappears, if present, // collapsed into the high bit of the first byte. // Note that the 8-byte MAC signature in RNS_SECURITY_HEADER1 // remains, if present. if (!(FastPathOutputFlags & NM_NO_SECURITY_HEADER)) { dataSize -= sizeof(RNS_SECURITY_HEADER); pData += sizeof(RNS_SECURITY_HEADER); } // Work backwards from where we are: First, the total packet // length including the header. if (dataSize <= 125) { // 1-byte form of length, high bit 0. dataSize += 2; pData -= 2; *(pData + 1) = (BYTE)dataSize; } else { // 2-byte form of length, first byte has high bit 1 and 7 // most significant bits. dataSize += 3; pData -= 3; *(pData + 1) = (BYTE)(0x80 | ((dataSize & 0x7F00) >> 8)); *(pData + 2) = (BYTE)(dataSize & 0xFF); } // The header byte. This includes TS_OUTPUT_FASTPATH_ACTION_FASTPATH // and TS_OUTPUT_FASTPATH_ENCRYPTED, if present in the // fast-path output flags. *pData = (BYTE)(TS_OUTPUT_FASTPATH_ACTION_FASTPATH | (FastPathOutputFlags & TS_OUTPUT_FASTPATH_ENCRYPTION_MASK)); // Set up the OutBuf with its final contents. pOutBuf->pBuffer = pData; pOutBuf->ByteCount = dataSize; // Send downward. SdWrite.pBuffer = NULL; SdWrite.ByteCount = 0; SdWrite.pOutBuf = pOutBuf; Status = IcaCallNextDriver(pRealNMHandle->pWDHandle->pContext, SD$RAWWRITE, &SdWrite); if (NT_SUCCESS(Status)) { // Increment protocol counters. pRealNMHandle->pWDHandle->pProtocolStatus->Output.WdFrames++; pRealNMHandle->pWDHandle->pProtocolStatus->Output.WdBytes += dataSize; } else { TRC_ERR((TB,"Failed IcaRawWrite to network, status=%X", Status)); rc = FALSE; // We do not free the OutBuf here, TD is supposed to do it. } } else { TRC_DBG((TB, "Send data on channel %x", userID)); MCSErr = MCSSendDataRequest(pRealNMHandle->hUser, userID == 0 ? pRealNMHandle->hChannel : NULL, NORMAL_SEND_DATA, (ChannelID)userID, (MCSPriority)priority, SEGMENTATION_BEGIN | SEGMENTATION_END, pOutBuf); if (MCSErr == MCS_NO_ERROR) { TRC_DATA_NRM("Send OK", pOutBuf, dataSize); } else { TRC_ERR((TB, "Failed to send OutBuf %p, buffer %p, MCSErr %x", pOutBuf, pData, MCSErr)); rc = FALSE; } } #ifdef DC_COUNTERS if (rc) { PTSHARE_WD m_pTSWd = pRealNMHandle->pWDHandle; if (dataSize > CORE_IN_COUNT[IN_MAX_PKT_SIZE]) { CORE_IN_COUNT[IN_MAX_PKT_SIZE] = dataSize; } CORE_IN_COUNT[IN_PKT_TOTAL_SENT]++; if (dataSize < 201) { CORE_IN_COUNT[IN_PKT_BYTE_SPREAD1]++; } else if (dataSize < 401) { CORE_IN_COUNT[IN_PKT_BYTE_SPREAD2]++; } else if (dataSize < 601) { CORE_IN_COUNT[IN_PKT_BYTE_SPREAD3]++; } else if (dataSize < 801) { CORE_IN_COUNT[IN_PKT_BYTE_SPREAD4]++; } else if (dataSize < 1001) { CORE_IN_COUNT[IN_PKT_BYTE_SPREAD5]++; } else if (dataSize < 1201 ) { CORE_IN_COUNT[IN_PKT_BYTE_SPREAD6]++; } else if (dataSize < 1401) { CORE_IN_COUNT[IN_PKT_BYTE_SPREAD7]++; } else if (dataSize < 1601) { CORE_IN_COUNT[IN_PKT_BYTE_SPREAD8]++; } else if (dataSize < 2001) { CORE_IN_COUNT[IN_PKT_BYTE_SPREAD9]++; } else if (dataSize < 4001) { CORE_IN_COUNT[IN_PKT_BYTE_SPREAD10]++; } else if (dataSize < 6001) { CORE_IN_COUNT[IN_PKT_BYTE_SPREAD11]++; } else if (dataSize < 8001) { CORE_IN_COUNT[IN_PKT_BYTE_SPREAD12]++; } else { CORE_IN_COUNT[IN_PKT_BYTE_SPREAD13]++; } } #endif } // Raw write the potentially encrypted data to the shadow stack else { SD_RAWWRITE SdWrite; NTSTATUS status; TRC_ASSERT((!(FastPathOutputFlags & NM_SEND_FASTPATH_OUTPUT)), (TB,"Fast-path output requested across shadow pipe!")); SdWrite.pOutBuf = pOutBuf; SdWrite.pBuffer = NULL; SdWrite.ByteCount = 0; status = IcaCallNextDriver(pRealNMHandle->pContext, SD$RAWWRITE, &SdWrite); if (status == STATUS_SUCCESS) { TRC_DBG((TB, "RawWrite: %ld bytes", dataSize)); } else { TRC_ERR((TB, "RawWrite failed: %lx", status)); } } DC_END_FN(); return rc; } /* NM_SendData */ /****************************************************************************/ /* Name: NM_MCSUserCallback */ /* */ /* Purpose: Direct callback from MCS */ /* */ /* Returns: nothing */ /* */ /* Params: hUser - should be our user handle */ /* Message - the callback type */ /* Params - Cast to the right type of parameter depending on */ /* callback */ /* UserDefined - our NM handle */ /* */ /* Operation: Called by MCS for callbacks. */ /* */ /* Processing depends on the callback type */ /* - MCS_DETACH_USER_INDICATION */ /* - call SM_OnDisconnected */ /* - all others are ignored. */ /****************************************************************************/ void __stdcall NM_MCSUserCallback(UserHandle hUser, unsigned Message, void *Params, void *UserDefined) { PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)UserDefined; DC_BEGIN_FN("NM_MCSUserCallback"); /************************************************************************/ /* First check that this is our UserHandle */ /************************************************************************/ ASSERT(hUser == pRealNMHandle->hUser); /************************************************************************/ /* If the Share Core is dead, don't do any of this */ /************************************************************************/ if (pRealNMHandle->dead) { TRC_ALT((TB, "Callback %s (%d) ignored because we're dead", Message == MCS_ATTACH_USER_CONFIRM ? "MCS_ATTACH_USER_CONFIRM" : Message == MCS_CHANNEL_JOIN_CONFIRM ? "MCS_CHANNEL_JOIN_CONFIRM" : Message == MCS_DETACH_USER_INDICATION ? "MCS_DETACH_USER_INDICATION" : Message == MCS_SEND_DATA_INDICATION ? "MCS_SEND_DATA_INDICATION" : "- Unknown - ", Message)); DC_QUIT; } /************************************************************************/ /* Handle callbacks we care about */ /************************************************************************/ switch (Message) { case MCS_DETACH_USER_INDICATION: { DetachUserIndication *pDUin; TRC_NRM((TB, "DetachUserIndication")); pDUin = (DetachUserIndication *)Params; NMDetachUserInd(pRealNMHandle, pDUin->Reason, pDUin->UserID); } break; default: { TRC_ERR((TB, "Unhandled MCS callback type %d", Message )); } break; } DC_EXIT_POINT: DC_END_FN(); } /* NM_MCSUserCallback */ /****************************************************************************/ /* Name: NM_Dead */ /****************************************************************************/ void RDPCALL NM_Dead(PVOID pNMHandle, BOOL dead) { PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle; DC_BEGIN_FN("NM_Dead"); TRC_NRM((TB, "NM Dead ? %s", pRealNMHandle->dead ? "Y" : "N")); pRealNMHandle->dead = dead; DC_END_FN(); } /* NM_Dead */ /****************************************************************************/ /* Name: NM_VirtualQueryBindings */ /* */ /* Purpose: Return virtual channel bindings to WD */ /* */ /* Params: pNMHandle - NM Handle */ /* pVBind - pointer to virtual bindings structure to fill in */ /* vBindLength - size (bytes) of virtual bindings structure */ /* pBytesReturned - size (bytes) of data returned */ /****************************************************************************/ NTSTATUS RDPCALL NM_VirtualQueryBindings(PVOID pNMHandle, PSD_VCBIND pVBind, ULONG vBindLength, PULONG pBytesReturned) { NTSTATUS status; PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle; USHORT virtualClass; UINT i; DC_BEGIN_FN("NM_VirtualQueryBindings"); /************************************************************************/ /* First see if we have any bindings to report */ /************************************************************************/ if (pRealNMHandle->channelCount == 0) { TRC_ALT((TB, "No Virtual Channels to report")); *pBytesReturned = 0; status = STATUS_SUCCESS; DC_QUIT; } /************************************************************************/ /* Check there is enough space to report them */ /************************************************************************/ *pBytesReturned = (pRealNMHandle->channelCount * sizeof(SD_VCBIND)); if (vBindLength < *pBytesReturned) { TRC_ERR((TB, "Not enough space for %d VCs: need/got %d/%d", pRealNMHandle->channelCount, *pBytesReturned, vBindLength)); status = STATUS_BUFFER_TOO_SMALL; DC_QUIT; } /************************************************************************/ /* Copy channel names and assign numbers */ /************************************************************************/ for (i = 0, virtualClass = 0; i < pRealNMHandle->channelCount; i++, virtualClass++, pVBind++) { /********************************************************************/ /* Can't use channel 7 as it's used by RDPDD */ /********************************************************************/ if (i == WD_THINWIRE_CHANNEL) { TRC_NRM((TB, "Skip channel %d", i)); virtualClass++; } strcpy(pVBind->VirtualName, pRealNMHandle->channelData[virtualClass].name); pVBind->VirtualClass = virtualClass; pVBind->Flags = 0; if (pRealNMHandle->channelData[virtualClass].flags & CHANNEL_OPTION_REMOTE_CONTROL_PERSISTENT) { pVBind->Flags |= SD_CHANNEL_FLAG_SHADOW_PERSISTENT; } TRC_NRM((TB, "Assigned channel %d to %s", pVBind->VirtualClass, pVBind->VirtualName)); } /************************************************************************/ /* That's all */ /************************************************************************/ status = STATUS_SUCCESS; DC_EXIT_POINT: DC_END_FN(); return(status); } /* NM_VirtualQueryBindings */ /****************************************************************************/ /* Name: NM_MCSChannelToVirtual */ /* */ /* Purpose: Convert an MCS channel ID into a virtual channel ID */ /* */ /* Returns: Virtual Channel ID */ /* */ /* Params: pNMHandle - NM Handle */ /* channelID - MCS Channel ID */ /* ppChannelData - data stored for this channel (returned) */ /****************************************************************************/ VIRTUALCHANNELCLASS RDPCALL NM_MCSChannelToVirtual( PVOID pNMHandle, UINT16 channelID, PPNM_CHANNEL_DATA ppChannelData) { PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle; PNM_CHANNEL_DATA pChannelData; unsigned i; VIRTUALCHANNELCLASS rc; DC_BEGIN_FN("NM_MCSChannelToVirtual"); /************************************************************************/ /* Find this MCS Channel */ /************************************************************************/ TRC_DBG((TB, "Find MCS channel %hx", channelID)); for (i = 0, pChannelData = pRealNMHandle->channelData; i < pRealNMHandle->channelArrayCount; i++, pChannelData++) { TRC_DBG((TB, "Compare entry %d: %hx", i, pChannelData->MCSChannelID)); if (pChannelData->MCSChannelID == channelID) { rc = i; *ppChannelData = pChannelData; TRC_NRM((TB, "MCS channel %hx is VC %d", channelID, rc)); DC_QUIT; } } /************************************************************************/ /* If we get here, we failed to find a match */ /************************************************************************/ TRC_NRM((TB, "No match for MCS channel ID %hx", channelID)); rc = -1; *ppChannelData = NULL; DC_EXIT_POINT: DC_END_FN(); return(rc); } /* NM_MCSChannelToVirtual */ /****************************************************************************/ /* Name: NM_VirtualChannelToMCS */ /* */ /* Purpose: Convert a virtual channel ID into an MCS channel ID */ /* */ /* Returns: MCS Channel ID */ /* */ /* Params: pNMHandle - NM Handle */ /* channelID - virtual channel ID */ /* ppChannelData - data stored for this channel (returned) */ /****************************************************************************/ INT16 RDPCALL NM_VirtualChannelToMCS(PVOID pNMHandle, VIRTUALCHANNELCLASS channelID, PPNM_CHANNEL_DATA ppChannelData) { PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle; INT16 rc; DC_BEGIN_FN("NM_VirtualChannelToMCS"); /************************************************************************/ /* Check the virtual channel is in range */ /************************************************************************/ if (channelID >= (VIRTUALCHANNELCLASS)(pRealNMHandle->channelArrayCount)) { TRC_ERR((TB, "Unknown virtual channel %d", channelID)); rc = -1; DC_QUIT; } /************************************************************************/ /* Find this Virtual Channel */ /************************************************************************/ rc = pRealNMHandle->channelData[channelID].MCSChannelID; *ppChannelData = &(pRealNMHandle->channelData[channelID]); TRC_NRM((TB, "Virtual channel %d = MCS Channel %hx", channelID, rc)); DC_EXIT_POINT: DC_END_FN(); return(rc); } /* NM_VirtualChannelToMCS */ /****************************************************************************/ /* Name: NM_QueryChannels */ /* */ /* Purpose: Return virtual channel data */ /* */ /* Returns: TRUE/FALSE */ /* */ /* Params: pNMHandle - NM Handle */ /* pOutbuf - buffer to receive output data */ /* outbufLength - size of outbuf */ /* pBytesReturned - amount of data returned */ /****************************************************************************/ NTSTATUS RDPCALL NM_QueryChannels(PVOID pNMHandle, PVOID pOutbuf, unsigned outbufLength, PULONG pBytesReturned) { NTSTATUS status; PCHANNEL_CONNECT_IN pChannelConnect; PCHANNEL_CONNECT_DEF pChannelDef; PNM_HANDLE_DATA pRealNMHandle = (PNM_HANDLE_DATA)pNMHandle; unsigned bytesNeeded; unsigned i; DC_BEGIN_FN("NM_QueryChannels"); /************************************************************************/ /* Check enough space has been supplied */ /************************************************************************/ bytesNeeded = sizeof(CHANNEL_CONNECT_IN) + (pRealNMHandle->channelArrayCount * sizeof(CHANNEL_CONNECT_DEF)); if (outbufLength < bytesNeeded) { TRC_ERR((TB, "Not enough space: need/got %d/%d", bytesNeeded, outbufLength)); status = STATUS_BUFFER_TOO_SMALL; DC_QUIT; } /************************************************************************/ /* Complete the returned data */ /************************************************************************/ pChannelConnect = (PCHANNEL_CONNECT_IN)pOutbuf; pChannelConnect->channelCount = pRealNMHandle->channelArrayCount; pChannelDef = (PCHANNEL_CONNECT_DEF)(pChannelConnect + 1); for (i = 0; i < pRealNMHandle->channelArrayCount; i++) { strcpy(pChannelDef[i].name, pRealNMHandle->channelData[i].name); pChannelDef[i].ID = i; } /************************************************************************/ /* Return status and bytesReturned */ /************************************************************************/ *pBytesReturned = bytesNeeded; status = STATUS_SUCCESS; DC_EXIT_POINT: DC_END_FN(); return(status); } /* NM_QueryChannels */