diff options
Diffstat (limited to 'libfreerdp/core/activation.c')
-rw-r--r-- | libfreerdp/core/activation.c | 810 |
1 files changed, 810 insertions, 0 deletions
diff --git a/libfreerdp/core/activation.c b/libfreerdp/core/activation.c new file mode 100644 index 0000000..5628533 --- /dev/null +++ b/libfreerdp/core/activation.c @@ -0,0 +1,810 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Activation Sequence + * + * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <freerdp/config.h> + +#include "settings.h" + +#include <winpr/assert.h> + +#include "activation.h" +#include "display.h" + +#define TAG FREERDP_TAG("core.activation") + +static BOOL rdp_recv_client_font_list_pdu(wStream* s); +static BOOL rdp_recv_client_persistent_key_list_pdu(wStream* s); +static BOOL rdp_send_server_font_map_pdu(rdpRdp* rdp); + +static BOOL rdp_write_synchronize_pdu(wStream* s, const rdpSettings* settings) +{ + const UINT32 PduSource = freerdp_settings_get_uint32(settings, FreeRDP_PduSource); + + if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 4)) + return FALSE; + Stream_Write_UINT16(s, SYNCMSGTYPE_SYNC); /* messageType (2 bytes) */ + Stream_Write_UINT16(s, PduSource); /* targetUser (2 bytes) */ + return TRUE; +} + +static BOOL rdp_recv_sync_pdu(rdpRdp* rdp, wStream* s, const char* what) +{ + UINT16 msgType = 0; + UINT16 targetUser = 0; + + WINPR_UNUSED(rdp); + if (!Stream_CheckAndLogRequiredLengthEx(TAG, WLOG_WARN, s, 4, 1, "%s(%s:%" PRIuz ") %s", + __func__, __FILE__, (size_t)__LINE__, what)) + return FALSE; + Stream_Read_UINT16(s, msgType); + if (msgType != SYNCMSGTYPE_SYNC) + { + WLog_WARN(TAG, "%s: Invalid messageType=0x%04" PRIx16 ", expected 0x%04" PRIx16, what, + msgType, SYNCMSGTYPE_SYNC); + return FALSE; + } + Stream_Read_UINT16(s, targetUser); + WLog_VRB(TAG, "%s: targetUser=0x%04" PRIx16, what, targetUser); + return TRUE; +} + +BOOL rdp_recv_server_synchronize_pdu(rdpRdp* rdp, wStream* s) +{ + if (!rdp_recv_sync_pdu(rdp, s, "[MS-RDPBCGR] 2.2.1.19 Server Synchronize PDU")) + return FALSE; + return rdp_finalize_set_flag(rdp, FINALIZE_SC_SYNCHRONIZE_PDU); +} + +BOOL rdp_send_server_synchronize_pdu(rdpRdp* rdp) +{ + wStream* s = rdp_data_pdu_init(rdp); + if (!s) + return FALSE; + + WINPR_ASSERT(rdp); + if (!rdp_write_synchronize_pdu(s, rdp->settings)) + { + Stream_Free(s, TRUE); + return FALSE; + } + + WINPR_ASSERT(rdp->mcs); + return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_SYNCHRONIZE, rdp->mcs->userId); +} + +BOOL rdp_recv_client_synchronize_pdu(rdpRdp* rdp, wStream* s) +{ + if (!rdp_recv_sync_pdu(rdp, s, "[MS-RDPBCGR] 2.2.1.14 Client Synchronize PDU")) + return FALSE; + return rdp_finalize_set_flag(rdp, FINALIZE_CS_SYNCHRONIZE_PDU); +} + +BOOL rdp_send_client_synchronize_pdu(rdpRdp* rdp) +{ + wStream* s = rdp_data_pdu_init(rdp); + if (!s) + return FALSE; + + WINPR_ASSERT(rdp); + if (!rdp_write_synchronize_pdu(s, rdp->settings)) + { + Stream_Free(s, TRUE); + return FALSE; + } + + WINPR_ASSERT(rdp->mcs); + return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_SYNCHRONIZE, rdp->mcs->userId); +} + +static BOOL rdp_recv_control_pdu(wStream* s, UINT16* action, UINT16* grantId, UINT32* controlId) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(action); + WINPR_ASSERT(grantId); + WINPR_ASSERT(controlId); + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return FALSE; + + Stream_Read_UINT16(s, *action); /* action (2 bytes) */ + Stream_Read_UINT16(s, *grantId); /* grantId (2 bytes) */ + Stream_Read_UINT32(s, *controlId); /* controlId (4 bytes) */ + return TRUE; +} + +static BOOL rdp_write_client_control_pdu(wStream* s, UINT16 action, UINT16 grantId, + UINT32 controlId) +{ + WINPR_ASSERT(s); + if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 8)) + return FALSE; + Stream_Write_UINT16(s, action); /* action (2 bytes) */ + Stream_Write_UINT16(s, grantId); /* grantId (2 bytes) */ + Stream_Write_UINT32(s, controlId); /* controlId (4 bytes) */ + return TRUE; +} + +BOOL rdp_recv_server_control_pdu(rdpRdp* rdp, wStream* s) +{ + UINT16 action = 0; + UINT16 grantId = 0; + UINT32 controlId = 0; + + WINPR_ASSERT(rdp); + WINPR_ASSERT(s); + + if (!rdp_recv_control_pdu(s, &action, &grantId, &controlId)) + return FALSE; + + switch (action) + { + case CTRLACTION_COOPERATE: + return rdp_finalize_set_flag(rdp, FINALIZE_SC_CONTROL_COOPERATE_PDU); + + case CTRLACTION_GRANTED_CONTROL: + rdp->resendFocus = TRUE; + return rdp_finalize_set_flag(rdp, FINALIZE_SC_CONTROL_GRANTED_PDU); + default: + { + char buffer[128] = { 0 }; + WLog_WARN(TAG, "Unexpected control PDU %s", + rdp_ctrlaction_string(action, buffer, sizeof(buffer))); + + return FALSE; + } + } +} + +BOOL rdp_send_server_control_cooperate_pdu(rdpRdp* rdp) +{ + wStream* s = rdp_data_pdu_init(rdp); + if (!s) + return FALSE; + if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 8)) + { + Stream_Free(s, TRUE); + return FALSE; + } + Stream_Write_UINT16(s, CTRLACTION_COOPERATE); /* action (2 bytes) */ + Stream_Write_UINT16(s, 0); /* grantId (2 bytes) */ + Stream_Write_UINT32(s, 0); /* controlId (4 bytes) */ + + WINPR_ASSERT(rdp->mcs); + return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_CONTROL, rdp->mcs->userId); +} + +BOOL rdp_send_server_control_granted_pdu(rdpRdp* rdp) +{ + wStream* s = rdp_data_pdu_init(rdp); + if (!s) + return FALSE; + if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 8)) + { + Stream_Free(s, TRUE); + return FALSE; + } + + WINPR_ASSERT(rdp->mcs); + Stream_Write_UINT16(s, CTRLACTION_GRANTED_CONTROL); /* action (2 bytes) */ + Stream_Write_UINT16(s, rdp->mcs->userId); /* grantId (2 bytes) */ + Stream_Write_UINT32(s, 0x03EA); /* controlId (4 bytes) */ + return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_CONTROL, rdp->mcs->userId); +} + +BOOL rdp_send_client_control_pdu(rdpRdp* rdp, UINT16 action) +{ + wStream* s = rdp_data_pdu_init(rdp); + UINT16 GrantId = 0; + UINT16 ControlId = 0; + + switch (action) + { + case CTRLACTION_COOPERATE: + case CTRLACTION_REQUEST_CONTROL: + break; + default: + WLog_WARN(TAG, + "Invalid client control PDU::action 0x%04" PRIx16 ", not allowed by client", + action); + return FALSE; + } + + if (!s) + return FALSE; + if (!rdp_write_client_control_pdu(s, action, GrantId, ControlId)) + { + Stream_Free(s, TRUE); + return FALSE; + } + + WINPR_ASSERT(rdp->mcs); + return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_CONTROL, rdp->mcs->userId); +} + +static BOOL rdp_write_client_persistent_key_list_pdu(wStream* s, + const RDP_BITMAP_PERSISTENT_INFO* info) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(info); + + if (!Stream_EnsureRemainingCapacity(s, 24)) + return FALSE; + + Stream_Write_UINT16(s, info->numEntriesCache0); /* numEntriesCache0 (2 bytes) */ + Stream_Write_UINT16(s, info->numEntriesCache1); /* numEntriesCache1 (2 bytes) */ + Stream_Write_UINT16(s, info->numEntriesCache2); /* numEntriesCache2 (2 bytes) */ + Stream_Write_UINT16(s, info->numEntriesCache3); /* numEntriesCache3 (2 bytes) */ + Stream_Write_UINT16(s, info->numEntriesCache4); /* numEntriesCache4 (2 bytes) */ + Stream_Write_UINT16(s, info->totalEntriesCache0); /* totalEntriesCache0 (2 bytes) */ + Stream_Write_UINT16(s, info->totalEntriesCache1); /* totalEntriesCache1 (2 bytes) */ + Stream_Write_UINT16(s, info->totalEntriesCache2); /* totalEntriesCache2 (2 bytes) */ + Stream_Write_UINT16(s, info->totalEntriesCache3); /* totalEntriesCache3 (2 bytes) */ + Stream_Write_UINT16(s, info->totalEntriesCache4); /* totalEntriesCache4 (2 bytes) */ + Stream_Write_UINT8(s, PERSIST_FIRST_PDU | PERSIST_LAST_PDU); /* bBitMask (1 byte) */ + Stream_Write_UINT8(s, 0); /* pad1 (1 byte) */ + Stream_Write_UINT16(s, 0); /* pad3 (2 bytes) */ + /* entries */ + + if (!Stream_EnsureRemainingCapacity(s, info->keyCount * 8ull)) + return FALSE; + + for (UINT32 index = 0; index < info->keyCount; index++) + { + const UINT32 key1 = (UINT32)info->keyList[index]; + const UINT32 key2 = (UINT32)(info->keyList[index] >> 32); + Stream_Write_UINT32(s, key1); + Stream_Write_UINT32(s, key2); + } + + return TRUE; +} + +static UINT32 rdp_load_persistent_key_list(rdpRdp* rdp, UINT64** pKeyList) +{ + int count = 0; + int status = 0; + UINT32 keyCount = 0; + UINT64* keyList = NULL; + rdpPersistentCache* persistent = NULL; + rdpSettings* settings = rdp->settings; + + *pKeyList = NULL; + + if (!freerdp_settings_get_bool(settings, FreeRDP_BitmapCachePersistEnabled)) + return 0; + + if (!settings->BitmapCachePersistFile) + return 0; + + persistent = persistent_cache_new(); + + if (!persistent) + return 0; + + status = persistent_cache_open(persistent, settings->BitmapCachePersistFile, FALSE, 0); + + if (status < 1) + goto error; + + count = persistent_cache_get_count(persistent); + + keyCount = (UINT32)count; + keyList = (UINT64*)malloc(keyCount * sizeof(UINT64)); + + if (!keyList) + goto error; + + for (int index = 0; index < count; index++) + { + PERSISTENT_CACHE_ENTRY cacheEntry = { 0 }; + + if (persistent_cache_read_entry(persistent, &cacheEntry) < 1) + continue; + + keyList[index] = cacheEntry.key64; + } + + *pKeyList = keyList; + + persistent_cache_free(persistent); + return keyCount; +error: + persistent_cache_free(persistent); + free(keyList); + return 0; +} + +BOOL rdp_send_client_persistent_key_list_pdu(rdpRdp* rdp) +{ + UINT32 keyMaxFrag = 2042; + UINT64* keyList = NULL; + RDP_BITMAP_PERSISTENT_INFO info = { 0 }; + rdpSettings* settings = rdp->settings; + UINT32 keyCount = rdp_load_persistent_key_list(rdp, &keyList); + + WLog_DBG(TAG, "Persistent Key List: TotalKeyCount: %" PRIu32 " MaxKeyFrag: %" PRIu32, keyCount, + keyMaxFrag); + + // MS-RDPBCGR recommends sending no more than 169 entries at once. + // In practice, sending more than 2042 entries at once triggers an error. + // It should be possible to advertise the entire client bitmap cache + // by sending multiple persistent key list PDUs, but the current code + // only bothers sending a single, smaller list of entries instead. + + if (keyCount > keyMaxFrag) + keyCount = keyMaxFrag; + + info.totalEntriesCache0 = settings->BitmapCacheV2CellInfo[0].numEntries; + info.totalEntriesCache1 = settings->BitmapCacheV2CellInfo[1].numEntries; + info.totalEntriesCache2 = settings->BitmapCacheV2CellInfo[2].numEntries; + info.totalEntriesCache3 = settings->BitmapCacheV2CellInfo[3].numEntries; + info.totalEntriesCache4 = settings->BitmapCacheV2CellInfo[4].numEntries; + + info.numEntriesCache0 = MIN(keyCount, info.totalEntriesCache0); + keyCount -= info.numEntriesCache0; + info.numEntriesCache1 = MIN(keyCount, info.totalEntriesCache1); + keyCount -= info.numEntriesCache1; + info.numEntriesCache2 = MIN(keyCount, info.totalEntriesCache2); + keyCount -= info.numEntriesCache2; + info.numEntriesCache3 = MIN(keyCount, info.totalEntriesCache3); + keyCount -= info.numEntriesCache3; + info.numEntriesCache4 = MIN(keyCount, info.totalEntriesCache4); + keyCount -= info.numEntriesCache4; + + info.totalEntriesCache0 = info.numEntriesCache0; + info.totalEntriesCache1 = info.numEntriesCache1; + info.totalEntriesCache2 = info.numEntriesCache2; + info.totalEntriesCache3 = info.numEntriesCache3; + info.totalEntriesCache4 = info.numEntriesCache4; + + keyCount = info.totalEntriesCache0 + info.totalEntriesCache1 + info.totalEntriesCache2 + + info.totalEntriesCache3 + info.totalEntriesCache4; + + info.keyCount = keyCount; + info.keyList = keyList; + + WLog_DBG(TAG, "persistentKeyList count: %" PRIu32, info.keyCount); + + WLog_DBG(TAG, + "numEntriesCache: [0]: %" PRIu16 " [1]: %" PRIu16 " [2]: %" PRIu16 " [3]: %" PRIu16 + " [4]: %" PRIu16, + info.numEntriesCache0, info.numEntriesCache1, info.numEntriesCache2, + info.numEntriesCache3, info.numEntriesCache4); + + WLog_DBG(TAG, + "totalEntriesCache: [0]: %" PRIu16 " [1]: %" PRIu16 " [2]: %" PRIu16 " [3]: %" PRIu16 + " [4]: %" PRIu16, + info.totalEntriesCache0, info.totalEntriesCache1, info.totalEntriesCache2, + info.totalEntriesCache3, info.totalEntriesCache4); + + wStream* s = rdp_data_pdu_init(rdp); + + if (!s) + { + free(keyList); + return FALSE; + } + + if (!rdp_write_client_persistent_key_list_pdu(s, &info)) + { + Stream_Free(s, TRUE); + free(keyList); + return FALSE; + } + + WINPR_ASSERT(rdp->mcs); + free(keyList); + + return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_BITMAP_CACHE_PERSISTENT_LIST, rdp->mcs->userId); +} + +BOOL rdp_recv_client_font_list_pdu(wStream* s) +{ + WINPR_ASSERT(s); + /* 2.2.1.18 Client Font List PDU */ + if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) + return FALSE; + + return Stream_SafeSeek(s, 8); +} + +BOOL rdp_recv_client_persistent_key_list_pdu(wStream* s) +{ + BYTE flags = 0; + size_t count = 0; + size_t total = 0; + UINT16 cache = 0; + + WINPR_ASSERT(s); + + /* 2.2.1.17.1 Persistent Key List PDU Data (TS_BITMAPCACHE_PERSISTENT_LIST_PDU) */ + if (!Stream_CheckAndLogRequiredLength(TAG, s, 21)) + { + WLog_ERR(TAG, "short TS_BITMAPCACHE_PERSISTENT_LIST_PDU, need 21 bytes, got %" PRIuz, + Stream_GetRemainingLength(s)); + return FALSE; + } + /* Read numEntriesCacheX for variable length data in PDU */ + for (size_t x = 0; x < 5; x++) + { + Stream_Read_UINT16(s, cache); + count += cache; + } + + /* Skip totalEntriesCacheX */ + for (size_t x = 0; x < 5; x++) + { + UINT16 tmp = 0; + Stream_Read_UINT16(s, tmp); + total += tmp; + } + + if (total > 262144) + { + WLog_ERR(TAG, + "TS_BITMAPCACHE_PERSISTENT_LIST_PDU::totalEntriesCacheX exceeds 262144 entries"); + return FALSE; + } + + Stream_Read_UINT8(s, flags); + if ((flags & ~(PERSIST_LAST_PDU | PERSIST_FIRST_PDU)) != 0) + { + WLog_ERR(TAG, + "TS_BITMAPCACHE_PERSISTENT_LIST_PDU::bBitMask has an invalid value of 0x%02" PRIx8, + flags); + return FALSE; + } + + /* Skip padding */ + if (!Stream_SafeSeek(s, 3)) + { + WLog_ERR(TAG, "short TS_BITMAPCACHE_PERSISTENT_LIST_PDU, need 3 bytes, got %" PRIuz, + Stream_GetRemainingLength(s)); + return FALSE; + } + /* Skip actual entries sent by client */ + if (!Stream_SafeSeek(s, count * sizeof(UINT64))) + { + WLog_ERR(TAG, + "short TS_BITMAPCACHE_PERSISTENT_LIST_PDU, need %" PRIuz " bytes, got %" PRIuz, + count * sizeof(UINT64), Stream_GetRemainingLength(s)); + return FALSE; + } + return TRUE; +} + +static BOOL rdp_write_client_font_list_pdu(wStream* s, UINT16 flags) +{ + WINPR_ASSERT(s); + + if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 8)) + return FALSE; + Stream_Write_UINT16(s, 0); /* numberFonts (2 bytes) */ + Stream_Write_UINT16(s, 0); /* totalNumFonts (2 bytes) */ + Stream_Write_UINT16(s, flags); /* listFlags (2 bytes) */ + Stream_Write_UINT16(s, 50); /* entrySize (2 bytes) */ + return TRUE; +} + +BOOL rdp_send_client_font_list_pdu(rdpRdp* rdp, UINT16 flags) +{ + wStream* s = rdp_data_pdu_init(rdp); + if (!s) + return FALSE; + if (!rdp_write_client_font_list_pdu(s, flags)) + { + Stream_Free(s, TRUE); + return FALSE; + } + + WINPR_ASSERT(rdp->mcs); + return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_FONT_LIST, rdp->mcs->userId); +} + +BOOL rdp_recv_font_map_pdu(rdpRdp* rdp, wStream* s) +{ + UINT16 numberEntries = 0; + UINT16 totalNumEntries = 0; + UINT16 mapFlags = 0; + UINT16 entrySize = 0; + + WINPR_ASSERT(rdp); + WINPR_ASSERT(rdp->settings); + WINPR_ASSERT(s); + WINPR_ASSERT(!freerdp_settings_get_bool(rdp->settings, FreeRDP_ServerMode)); + + /* Do not fail here, see https://github.com/FreeRDP/FreeRDP/issues/925 */ + if (Stream_CheckAndLogRequiredLength(TAG, s, 8)) + { + Stream_Read_UINT16(s, numberEntries); /* numberEntries (2 bytes) */ + if (numberEntries != 0) + WLog_WARN( + TAG, + "[MS-RDPBCGR] 2.2.1.22.1 Font Map PDU Data (TS_FONT_MAP_PDU)::numberEntries != 0 " + "[%" PRIu16 "]", + numberEntries); + Stream_Read_UINT16(s, totalNumEntries); /* totalNumEntries (2 bytes) */ + if (totalNumEntries != 0) + WLog_WARN( + TAG, + "[MS-RDPBCGR] 2.2.1.22.1 Font Map PDU Data (TS_FONT_MAP_PDU)::totalNumEntries != " + "0 [%" PRIu16 "]", + totalNumEntries); + Stream_Read_UINT16(s, mapFlags); /* mapFlags (2 bytes) */ + if (mapFlags != (FONTLIST_FIRST | FONTLIST_LAST)) + WLog_WARN( + TAG, + "[MS-RDPBCGR] 2.2.1.22.1 Font Map PDU Data (TS_FONT_MAP_PDU)::mapFlags != 0x0003 " + "(FONTLIST_FIRST | FONTLIST_LAST) " + "[0x%04" PRIx16 "]", + mapFlags); + Stream_Read_UINT16(s, entrySize); /* entrySize (2 bytes) */ + if (entrySize != 4) + WLog_WARN(TAG, + "[MS-RDPBCGR] 2.2.1.22.1 Font Map PDU Data (TS_FONT_MAP_PDU)::entrySize != 4 " + "[%" PRIu16 "]", + entrySize); + } + else + WLog_WARN(TAG, + "[MS-RDPBCGR] 2.2.1.22.1 Font Map PDU Data (TS_FONT_MAP_PDU) paylaod size is " + "0 instead of 8"); + + return rdp_finalize_set_flag(rdp, FINALIZE_SC_FONT_MAP_PDU); +} + +BOOL rdp_send_server_font_map_pdu(rdpRdp* rdp) +{ + wStream* s = rdp_data_pdu_init(rdp); + if (!s) + return FALSE; + if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 8)) + { + Stream_Free(s, TRUE); + return FALSE; + } + Stream_Write_UINT16(s, 0); /* numberEntries (2 bytes) */ + Stream_Write_UINT16(s, 0); /* totalNumEntries (2 bytes) */ + Stream_Write_UINT16(s, FONTLIST_FIRST | FONTLIST_LAST); /* mapFlags (2 bytes) */ + Stream_Write_UINT16(s, 4); /* entrySize (2 bytes) */ + + WINPR_ASSERT(rdp->mcs); + return rdp_send_data_pdu(rdp, s, DATA_PDU_TYPE_FONT_MAP, rdp->mcs->userId); +} + +BOOL rdp_recv_deactivate_all(rdpRdp* rdp, wStream* s) +{ + UINT16 lengthSourceDescriptor = 0; + + WINPR_ASSERT(rdp); + WINPR_ASSERT(s); + + if (rdp_get_state(rdp) == CONNECTION_STATE_ACTIVE) + { + if (!rdp_finalize_set_flag(rdp, FINALIZE_DEACTIVATE_REACTIVATE)) + return FALSE; + + rdp->was_deactivated = TRUE; + rdp->deactivated_height = freerdp_settings_get_uint32(rdp->settings, FreeRDP_DesktopHeight); + rdp->deactivated_width = freerdp_settings_get_uint32(rdp->settings, FreeRDP_DesktopWidth); + } + + /* + * Windows XP can send short DEACTIVATE_ALL PDU that doesn't contain + * the following fields. + */ + + WINPR_ASSERT(rdp->settings); + if (Stream_GetRemainingLength(s) > 0) + { + do + { + UINT32 ShareId = 0; + if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) + break; + + Stream_Read_UINT32(s, ShareId); /* shareId (4 bytes) */ + if (!freerdp_settings_set_uint32(rdp->settings, FreeRDP_ShareId, ShareId)) + return FALSE; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 2)) + break; + + Stream_Read_UINT16(s, lengthSourceDescriptor); /* lengthSourceDescriptor (2 bytes) */ + + if (!Stream_CheckAndLogRequiredLength(TAG, s, lengthSourceDescriptor)) + break; + + Stream_Seek(s, lengthSourceDescriptor); /* sourceDescriptor (should be 0x00) */ + } while (0); + } + + return rdp_client_transition_to_state(rdp, + CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE); +} + +BOOL rdp_send_deactivate_all(rdpRdp* rdp) +{ + wStream* s = rdp_send_stream_pdu_init(rdp); + BOOL status = FALSE; + + if (!s) + return FALSE; + + if (!Stream_CheckAndLogRequiredCapacity(TAG, (s), 7)) + goto fail; + + WINPR_ASSERT(rdp->settings); + const UINT32 ShareId = freerdp_settings_get_uint32(rdp->settings, FreeRDP_ShareId); + Stream_Write_UINT32(s, ShareId); /* shareId (4 bytes) */ + Stream_Write_UINT16(s, 1); /* lengthSourceDescriptor (2 bytes) */ + Stream_Write_UINT8(s, 0); /* sourceDescriptor (should be 0x00) */ + + WINPR_ASSERT(rdp->mcs); + status = rdp_send_pdu(rdp, s, PDU_TYPE_DEACTIVATE_ALL, rdp->mcs->userId); +fail: + Stream_Release(s); + return status; +} + +BOOL rdp_server_accept_client_control_pdu(rdpRdp* rdp, wStream* s) +{ + UINT16 action = 0; + UINT16 GrantId = 0; + UINT32 ControlId = 0; + const CONNECTION_STATE state = rdp_get_state(rdp); + + WINPR_ASSERT(rdp); + WINPR_ASSERT(s); + + if (!rdp_recv_control_pdu(s, &action, &GrantId, &ControlId)) + return FALSE; + + switch (action) + { + + case CTRLACTION_REQUEST_CONTROL: + if (!rdp_finalize_is_flag_set(rdp, FINALIZE_CS_CONTROL_COOPERATE_PDU)) + { + char abuffer[128] = { 0 }; + char buffer[1024] = { 0 }; + WLog_WARN(TAG, + "Received action=%s with GrantId=0x%04" PRIx16 ", ControlId=0x%08" PRIx32 + " in unexpected state %s [missing %s]", + rdp_ctrlaction_string(action, abuffer, sizeof(abuffer)), GrantId, + ControlId, rdp_state_string(state), + rdp_finalize_flags_to_str(FINALIZE_CS_CONTROL_COOPERATE_PDU, buffer, + sizeof(buffer))); + return FALSE; + } + if ((GrantId != 0) || (ControlId != 0)) + { + WLog_WARN(TAG, + "Received CTRLACTION_COOPERATE with GrantId=0x%04" PRIx16 + " != 0x00, ControlId=0x%08" PRIx32 " != 0x00", + GrantId, ControlId); + return FALSE; + } + return rdp_finalize_set_flag(rdp, FINALIZE_CS_CONTROL_REQUEST_PDU); + case CTRLACTION_COOPERATE: + if (!rdp_finalize_is_flag_set(rdp, FINALIZE_CS_SYNCHRONIZE_PDU)) + { + char abuffer[128] = { 0 }; + char buffer[1024] = { 0 }; + WLog_WARN( + TAG, + "Received action=%s with GrantId=0x%04" PRIx16 ", ControlId=0x%08" PRIx32 + " in unexpected state %s [missing %s]", + rdp_ctrlaction_string(action, abuffer, sizeof(abuffer)), GrantId, ControlId, + rdp_state_string(state), + rdp_finalize_flags_to_str(FINALIZE_CS_SYNCHRONIZE_PDU, buffer, sizeof(buffer))); + return FALSE; + } + if ((GrantId != 0) || (ControlId != 0)) + { + WLog_WARN(TAG, + "Received CTRLACTION_COOPERATE with GrantId=0x%04" PRIx16 + " != 0x00, ControlId=0x%08" PRIx32 " != 0x00", + GrantId, ControlId); + return FALSE; + } + return rdp_finalize_set_flag(rdp, FINALIZE_CS_CONTROL_COOPERATE_PDU); + default: + { + char abuffer[128] = { 0 }; + WLog_WARN(TAG, + "Received unexpected action=%s with GrantId=0x%04" PRIx16 + ", ControlId=0x%08" PRIx32, + rdp_ctrlaction_string(action, abuffer, sizeof(abuffer)), GrantId, ControlId); + return FALSE; + } + } + + return TRUE; +} + +BOOL rdp_server_accept_client_font_list_pdu(rdpRdp* rdp, wStream* s) +{ + rdpSettings* settings = NULL; + freerdp_peer* peer = NULL; + + WINPR_ASSERT(rdp); + WINPR_ASSERT(s); + + settings = rdp->settings; + WINPR_ASSERT(settings); + + WINPR_ASSERT(rdp->context); + peer = rdp->context->peer; + WINPR_ASSERT(peer); + + if (!rdp_recv_client_font_list_pdu(s)) + return FALSE; + rdp_finalize_set_flag(rdp, FINALIZE_CS_FONT_LIST_PDU); + + if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_FINALIZATION_CLIENT_FONT_MAP)) + return FALSE; + + if (!rdp_send_server_font_map_pdu(rdp)) + return FALSE; + + if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_ACTIVE)) + return FALSE; + + return TRUE; +} + +BOOL rdp_server_accept_client_persistent_key_list_pdu(rdpRdp* rdp, wStream* s) +{ + WINPR_ASSERT(rdp); + WINPR_ASSERT(s); + + if (!rdp_recv_client_persistent_key_list_pdu(s)) + return FALSE; + + rdp_finalize_set_flag(rdp, FINALIZE_CS_PERSISTENT_KEY_LIST_PDU); + // TODO: Actually do something with this + return TRUE; +} + +const char* rdp_ctrlaction_string(UINT16 action, char* buffer, size_t size) +{ + const char* actstr = NULL; + switch (action) + { + case CTRLACTION_COOPERATE: + actstr = "CTRLACTION_COOPERATE"; + break; + case CTRLACTION_DETACH: + actstr = "CTRLACTION_DETACH"; + break; + case CTRLACTION_GRANTED_CONTROL: + actstr = "CTRLACTION_GRANTED_CONTROL"; + break; + case CTRLACTION_REQUEST_CONTROL: + actstr = "CTRLACTION_REQUEST_CONTROL"; + break; + default: + actstr = "CTRLACTION_UNKNOWN"; + break; + } + + _snprintf(buffer, size, "%s [0x%04" PRIx16 "]", actstr, action); + return buffer; +} |