diff options
Diffstat (limited to 'libfreerdp/utils/rdpdr_utils.c')
-rw-r--r-- | libfreerdp/utils/rdpdr_utils.c | 598 |
1 files changed, 598 insertions, 0 deletions
diff --git a/libfreerdp/utils/rdpdr_utils.c b/libfreerdp/utils/rdpdr_utils.c new file mode 100644 index 0000000..28cc97f --- /dev/null +++ b/libfreerdp/utils/rdpdr_utils.c @@ -0,0 +1,598 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * SCard utility functions + * + * Copyright 2021 Armin Novak <armin.novak@thincast.com> + * Copyright 2021 Thincast Technologies GmbH + * + * 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 <winpr/wlog.h> +#include <winpr/print.h> +#include <winpr/smartcard.h> + +#include <freerdp/utils/rdpdr_utils.h> +#include <freerdp/channels/scard.h> +#include <freerdp/channels/rdpdr.h> + +#include <freerdp/log.h> +#define TAG FREERDP_TAG("utils.scard") + +LONG scard_log_status_error(const char* tag, const char* what, LONG status) +{ + if (status != SCARD_S_SUCCESS) + { + DWORD level = WLOG_ERROR; + switch (status) + { + case SCARD_E_TIMEOUT: + level = WLOG_DEBUG; + break; + case SCARD_E_NO_READERS_AVAILABLE: + level = WLOG_INFO; + break; + default: + break; + } + WLog_Print(WLog_Get(tag), level, "%s failed with error %s [%" PRId32 "]", what, + SCardGetErrorString(status), status); + } + return status; +} + +const char* scard_get_ioctl_string(UINT32 ioControlCode, BOOL funcName) +{ + switch (ioControlCode) + { + case SCARD_IOCTL_ESTABLISHCONTEXT: + return funcName ? "SCardEstablishContext" : "SCARD_IOCTL_ESTABLISHCONTEXT"; + + case SCARD_IOCTL_RELEASECONTEXT: + return funcName ? "SCardReleaseContext" : "SCARD_IOCTL_RELEASECONTEXT"; + + case SCARD_IOCTL_ISVALIDCONTEXT: + return funcName ? "SCardIsValidContext" : "SCARD_IOCTL_ISVALIDCONTEXT"; + + case SCARD_IOCTL_LISTREADERGROUPSA: + return funcName ? "SCardListReaderGroupsA" : "SCARD_IOCTL_LISTREADERGROUPSA"; + + case SCARD_IOCTL_LISTREADERGROUPSW: + return funcName ? "SCardListReaderGroupsW" : "SCARD_IOCTL_LISTREADERGROUPSW"; + + case SCARD_IOCTL_LISTREADERSA: + return funcName ? "SCardListReadersA" : "SCARD_IOCTL_LISTREADERSA"; + + case SCARD_IOCTL_LISTREADERSW: + return funcName ? "SCardListReadersW" : "SCARD_IOCTL_LISTREADERSW"; + + case SCARD_IOCTL_INTRODUCEREADERGROUPA: + return funcName ? "SCardIntroduceReaderGroupA" : "SCARD_IOCTL_INTRODUCEREADERGROUPA"; + + case SCARD_IOCTL_INTRODUCEREADERGROUPW: + return funcName ? "SCardIntroduceReaderGroupW" : "SCARD_IOCTL_INTRODUCEREADERGROUPW"; + + case SCARD_IOCTL_FORGETREADERGROUPA: + return funcName ? "SCardForgetReaderGroupA" : "SCARD_IOCTL_FORGETREADERGROUPA"; + + case SCARD_IOCTL_FORGETREADERGROUPW: + return funcName ? "SCardForgetReaderGroupW" : "SCARD_IOCTL_FORGETREADERGROUPW"; + + case SCARD_IOCTL_INTRODUCEREADERA: + return funcName ? "SCardIntroduceReaderA" : "SCARD_IOCTL_INTRODUCEREADERA"; + + case SCARD_IOCTL_INTRODUCEREADERW: + return funcName ? "SCardIntroduceReaderW" : "SCARD_IOCTL_INTRODUCEREADERW"; + + case SCARD_IOCTL_FORGETREADERA: + return funcName ? "SCardForgetReaderA" : "SCARD_IOCTL_FORGETREADERA"; + + case SCARD_IOCTL_FORGETREADERW: + return funcName ? "SCardForgetReaderW" : "SCARD_IOCTL_FORGETREADERW"; + + case SCARD_IOCTL_ADDREADERTOGROUPA: + return funcName ? "SCardAddReaderToGroupA" : "SCARD_IOCTL_ADDREADERTOGROUPA"; + + case SCARD_IOCTL_ADDREADERTOGROUPW: + return funcName ? "SCardAddReaderToGroupW" : "SCARD_IOCTL_ADDREADERTOGROUPW"; + + case SCARD_IOCTL_REMOVEREADERFROMGROUPA: + return funcName ? "SCardRemoveReaderFromGroupA" : "SCARD_IOCTL_REMOVEREADERFROMGROUPA"; + + case SCARD_IOCTL_REMOVEREADERFROMGROUPW: + return funcName ? "SCardRemoveReaderFromGroupW" : "SCARD_IOCTL_REMOVEREADERFROMGROUPW"; + + case SCARD_IOCTL_LOCATECARDSA: + return funcName ? "SCardLocateCardsA" : "SCARD_IOCTL_LOCATECARDSA"; + + case SCARD_IOCTL_LOCATECARDSW: + return funcName ? "SCardLocateCardsW" : "SCARD_IOCTL_LOCATECARDSW"; + + case SCARD_IOCTL_GETSTATUSCHANGEA: + return funcName ? "SCardGetStatusChangeA" : "SCARD_IOCTL_GETSTATUSCHANGEA"; + + case SCARD_IOCTL_GETSTATUSCHANGEW: + return funcName ? "SCardGetStatusChangeW" : "SCARD_IOCTL_GETSTATUSCHANGEW"; + + case SCARD_IOCTL_CANCEL: + return funcName ? "SCardCancel" : "SCARD_IOCTL_CANCEL"; + + case SCARD_IOCTL_CONNECTA: + return funcName ? "SCardConnectA" : "SCARD_IOCTL_CONNECTA"; + + case SCARD_IOCTL_CONNECTW: + return funcName ? "SCardConnectW" : "SCARD_IOCTL_CONNECTW"; + + case SCARD_IOCTL_RECONNECT: + return funcName ? "SCardReconnect" : "SCARD_IOCTL_RECONNECT"; + + case SCARD_IOCTL_DISCONNECT: + return funcName ? "SCardDisconnect" : "SCARD_IOCTL_DISCONNECT"; + + case SCARD_IOCTL_BEGINTRANSACTION: + return funcName ? "SCardBeginTransaction" : "SCARD_IOCTL_BEGINTRANSACTION"; + + case SCARD_IOCTL_ENDTRANSACTION: + return funcName ? "SCardEndTransaction" : "SCARD_IOCTL_ENDTRANSACTION"; + + case SCARD_IOCTL_STATE: + return funcName ? "SCardState" : "SCARD_IOCTL_STATE"; + + case SCARD_IOCTL_STATUSA: + return funcName ? "SCardStatusA" : "SCARD_IOCTL_STATUSA"; + + case SCARD_IOCTL_STATUSW: + return funcName ? "SCardStatusW" : "SCARD_IOCTL_STATUSW"; + + case SCARD_IOCTL_TRANSMIT: + return funcName ? "SCardTransmit" : "SCARD_IOCTL_TRANSMIT"; + + case SCARD_IOCTL_CONTROL: + return funcName ? "SCardControl" : "SCARD_IOCTL_CONTROL"; + + case SCARD_IOCTL_GETATTRIB: + return funcName ? "SCardGetAttrib" : "SCARD_IOCTL_GETATTRIB"; + + case SCARD_IOCTL_SETATTRIB: + return funcName ? "SCardSetAttrib" : "SCARD_IOCTL_SETATTRIB"; + + case SCARD_IOCTL_ACCESSSTARTEDEVENT: + return funcName ? "SCardAccessStartedEvent" : "SCARD_IOCTL_ACCESSSTARTEDEVENT"; + + case SCARD_IOCTL_LOCATECARDSBYATRA: + return funcName ? "SCardLocateCardsByATRA" : "SCARD_IOCTL_LOCATECARDSBYATRA"; + + case SCARD_IOCTL_LOCATECARDSBYATRW: + return funcName ? "SCardLocateCardsByATRB" : "SCARD_IOCTL_LOCATECARDSBYATRW"; + + case SCARD_IOCTL_READCACHEA: + return funcName ? "SCardReadCacheA" : "SCARD_IOCTL_READCACHEA"; + + case SCARD_IOCTL_READCACHEW: + return funcName ? "SCardReadCacheW" : "SCARD_IOCTL_READCACHEW"; + + case SCARD_IOCTL_WRITECACHEA: + return funcName ? "SCardWriteCacheA" : "SCARD_IOCTL_WRITECACHEA"; + + case SCARD_IOCTL_WRITECACHEW: + return funcName ? "SCardWriteCacheW" : "SCARD_IOCTL_WRITECACHEW"; + + case SCARD_IOCTL_GETTRANSMITCOUNT: + return funcName ? "SCardGetTransmitCount" : "SCARD_IOCTL_GETTRANSMITCOUNT"; + + case SCARD_IOCTL_RELEASETARTEDEVENT: + return funcName ? "SCardReleaseStartedEvent" : "SCARD_IOCTL_RELEASETARTEDEVENT"; + + case SCARD_IOCTL_GETREADERICON: + return funcName ? "SCardGetReaderIcon" : "SCARD_IOCTL_GETREADERICON"; + + case SCARD_IOCTL_GETDEVICETYPEID: + return funcName ? "SCardGetDeviceTypeId" : "SCARD_IOCTL_GETDEVICETYPEID"; + + default: + return funcName ? "SCardUnknown" : "SCARD_IOCTL_UNKNOWN"; + } +} + +const char* rdpdr_component_string(UINT16 component) +{ + switch (component) + { + case RDPDR_CTYP_PRN: + return "RDPDR_CTYP_PRN"; + case RDPDR_CTYP_CORE: + return "RDPDR_CTYP_CORE"; + default: + return "UNKNOWN"; + } +} + +const char* rdpdr_packetid_string(UINT16 packetid) +{ + switch (packetid) + { + case PAKID_CORE_SERVER_ANNOUNCE: + return "PAKID_CORE_SERVER_ANNOUNCE"; + case PAKID_CORE_CLIENTID_CONFIRM: + return "PAKID_CORE_CLIENTID_CONFIRM"; + case PAKID_CORE_CLIENT_NAME: + return "PAKID_CORE_CLIENT_NAME"; + case PAKID_CORE_DEVICELIST_ANNOUNCE: + return "PAKID_CORE_DEVICELIST_ANNOUNCE"; + case PAKID_CORE_DEVICE_REPLY: + return "PAKID_CORE_DEVICE_REPLY"; + case PAKID_CORE_DEVICE_IOREQUEST: + return "PAKID_CORE_DEVICE_IOREQUEST"; + case PAKID_CORE_DEVICE_IOCOMPLETION: + return "PAKID_CORE_DEVICE_IOCOMPLETION"; + case PAKID_CORE_SERVER_CAPABILITY: + return "PAKID_CORE_SERVER_CAPABILITY"; + case PAKID_CORE_CLIENT_CAPABILITY: + return "PAKID_CORE_CLIENT_CAPABILITY"; + case PAKID_CORE_DEVICELIST_REMOVE: + return "PAKID_CORE_DEVICELIST_REMOVE"; + case PAKID_CORE_USER_LOGGEDON: + return "PAKID_CORE_USER_LOGGEDON"; + case PAKID_PRN_CACHE_DATA: + return "PAKID_PRN_CACHE_DATA"; + case PAKID_PRN_USING_XPS: + return "PAKID_PRN_USING_XPS"; + default: + return "UNKNOWN"; + } +} + +BOOL rdpdr_write_iocompletion_header(wStream* out, UINT32 DeviceId, UINT32 CompletionId, + UINT32 ioStatus) +{ + WINPR_ASSERT(out); + Stream_SetPosition(out, 0); + if (!Stream_EnsureRemainingCapacity(out, 16)) + return FALSE; + Stream_Write_UINT16(out, RDPDR_CTYP_CORE); /* Component (2 bytes) */ + Stream_Write_UINT16(out, PAKID_CORE_DEVICE_IOCOMPLETION); /* PacketId (2 bytes) */ + Stream_Write_UINT32(out, DeviceId); /* DeviceId (4 bytes) */ + Stream_Write_UINT32(out, CompletionId); /* CompletionId (4 bytes) */ + Stream_Write_UINT32(out, ioStatus); /* IoStatus (4 bytes) */ + + return TRUE; +} + +static void rdpdr_dump_packet(wLog* log, DWORD lvl, wStream* s, const char* custom, BOOL send) +{ + if (!WLog_IsLevelActive(log, lvl)) + return; + + const size_t gpos = Stream_GetPosition(s); + const size_t pos = send ? Stream_GetPosition(s) : Stream_Length(s); + + UINT16 component = 0; + UINT16 packetid = 0; + + Stream_SetPosition(s, 0); + + if (pos >= 2) + Stream_Read_UINT16(s, component); + if (pos >= 4) + Stream_Read_UINT16(s, packetid); + + switch (packetid) + { + case PAKID_CORE_SERVER_ANNOUNCE: + case PAKID_CORE_CLIENTID_CONFIRM: + { + UINT16 versionMajor = 0; + UINT16 versionMinor = 0; + UINT32 clientID = 0; + + if (pos >= 6) + Stream_Read_UINT16(s, versionMajor); + if (pos >= 8) + Stream_Read_UINT16(s, versionMinor); + if (pos >= 12) + Stream_Read_UINT32(s, clientID); + WLog_Print(log, lvl, + "%s [%s | %s] [version:%" PRIu16 ".%" PRIu16 "][id:0x%08" PRIx32 + "] -> %" PRIuz, + custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid), + versionMajor, versionMinor, clientID, pos); + } + break; + case PAKID_CORE_CLIENT_NAME: + { + char name[256] = { 0 }; + UINT32 unicodeFlag = 0; + UINT32 codePage = 0; + UINT32 computerNameLen = 0; + if (pos >= 8) + Stream_Read_UINT32(s, unicodeFlag); + if (pos >= 12) + Stream_Read_UINT32(s, codePage); + if (pos >= 16) + Stream_Read_UINT32(s, computerNameLen); + if (pos >= 16 + computerNameLen) + { + if (unicodeFlag == 0) + Stream_Read(s, name, MIN(sizeof(name), computerNameLen)); + else + ConvertWCharNToUtf8(Stream_ConstPointer(s), computerNameLen / sizeof(WCHAR), + name, sizeof(name)); + } + WLog_Print(log, lvl, + "%s [%s | %s] [ucs:%" PRIu32 "|cp:%" PRIu32 "][len:0x%08" PRIx32 + "] '%s' -> %" PRIuz, + custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid), + unicodeFlag, codePage, computerNameLen, name, pos); + } + break; + + case PAKID_CORE_DEVICE_IOREQUEST: + { + UINT32 CompletionId = 0; + UINT32 deviceID = 0; + UINT32 FileId = 0; + UINT32 MajorFunction = 0; + UINT32 MinorFunction = 0; + + if (pos >= 8) + Stream_Read_UINT32(s, deviceID); + if (pos >= 12) + Stream_Read_UINT32(s, FileId); + if (pos >= 16) + Stream_Read_UINT32(s, CompletionId); + if (pos >= 20) + Stream_Read_UINT32(s, MajorFunction); + if (pos >= 24) + Stream_Read_UINT32(s, MinorFunction); + WLog_Print(log, lvl, + "%s [%s | %s] [0x%08" PRIx32 "] FileId=0x%08" PRIx32 + ", CompletionId=0x%08" PRIx32 ", MajorFunction=0x%08" PRIx32 + ", MinorFunction=0x%08" PRIx32 " -> %" PRIuz, + custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid), + deviceID, FileId, CompletionId, MajorFunction, MinorFunction, pos); + } + break; + case PAKID_CORE_DEVICE_IOCOMPLETION: + { + UINT32 completionID = 0; + UINT32 ioStatus = 0; + UINT32 deviceID = 0; + if (pos >= 8) + Stream_Read_UINT32(s, deviceID); + if (pos >= 12) + Stream_Read_UINT32(s, completionID); + if (pos >= 16) + Stream_Read_UINT32(s, ioStatus); + + WLog_Print(log, lvl, + "%s [%s | %s] [0x%08" PRIx32 "] completionID=0x%08" PRIx32 + ", ioStatus=0x%08" PRIx32 " -> %" PRIuz, + custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid), + deviceID, completionID, ioStatus, pos); + } + break; + case PAKID_CORE_DEVICE_REPLY: + { + UINT32 deviceID = 0; + UINT32 status = 0; + + if (pos >= 8) + Stream_Read_UINT32(s, deviceID); + if (pos >= 12) + Stream_Read_UINT32(s, status); + WLog_Print(log, lvl, + "%s [%s | %s] [id:0x%08" PRIx32 ",status=0x%08" PRIx32 "] -> %" PRIuz, + custom, rdpdr_component_string(component), rdpdr_packetid_string(packetid), + deviceID, status, pos); + } + break; + case PAKID_CORE_CLIENT_CAPABILITY: + case PAKID_CORE_SERVER_CAPABILITY: + { + UINT16 numCapabilities = 0; + if (pos >= 6) + Stream_Read_UINT16(s, numCapabilities); + if (pos >= 8) + Stream_Seek_UINT16(s); /* padding */ + WLog_Print(log, lvl, "%s [%s | %s] [caps:%" PRIu16 "] -> %" PRIuz, custom, + rdpdr_component_string(component), rdpdr_packetid_string(packetid), + numCapabilities, pos); + for (UINT16 x = 0; x < numCapabilities; x++) + { + RDPDR_CAPABILITY_HEADER header = { 0 }; + const UINT error = rdpdr_read_capset_header(log, s, &header); + if (error == CHANNEL_RC_OK) + Stream_Seek(s, header.CapabilityLength); + } + } + break; + case PAKID_CORE_DEVICELIST_ANNOUNCE: + { + size_t offset = 8; + UINT32 count = 0; + + if (pos >= offset) + Stream_Read_UINT32(s, count); + + WLog_Print(log, lvl, "%s [%s | %s] [%" PRIu32 "] -> %" PRIuz, custom, + rdpdr_component_string(component), rdpdr_packetid_string(packetid), count, + pos); + + for (UINT32 x = 0; x < count; x++) + { + RdpdrDevice device = { 0 }; + + offset += 20; + if (pos >= offset) + { + Stream_Read_UINT32(s, device.DeviceType); /* DeviceType (4 bytes) */ + Stream_Read_UINT32(s, device.DeviceId); /* DeviceId (4 bytes) */ + Stream_Read(s, device.PreferredDosName, 8); /* PreferredDosName (8 bytes) */ + Stream_Read_UINT32(s, device.DeviceDataLength); /* DeviceDataLength (4 bytes) */ + device.DeviceData = Stream_Pointer(s); + } + offset += device.DeviceDataLength; + + WLog_Print(log, lvl, + "%s [announce][%" PRIu32 "] %s [0x%08" PRIx32 + "] '%s' [DeviceDataLength=%" PRIu32 "]", + custom, x, freerdp_rdpdr_dtyp_string(device.DeviceType), device.DeviceId, + device.PreferredDosName, device.DeviceDataLength); + } + } + break; + case PAKID_CORE_DEVICELIST_REMOVE: + { + size_t offset = 8; + UINT32 count = 0; + + if (pos >= offset) + Stream_Read_UINT32(s, count); + + WLog_Print(log, lvl, "%s [%s | %s] [%" PRIu32 "] -> %" PRIuz, custom, + rdpdr_component_string(component), rdpdr_packetid_string(packetid), count, + pos); + + for (UINT32 x = 0; x < count; x++) + { + UINT32 id = 0; + + offset += 4; + if (pos >= offset) + Stream_Read_UINT32(s, id); + + WLog_Print(log, lvl, "%s [remove][%" PRIu32 "] id=%" PRIu32, custom, x, id); + } + } + break; + case PAKID_CORE_USER_LOGGEDON: + WLog_Print(log, lvl, "%s [%s | %s] -> %" PRIuz, custom, + rdpdr_component_string(component), rdpdr_packetid_string(packetid), pos); + break; + default: + { + WLog_Print(log, lvl, "%s [%s | %s] -> %" PRIuz, custom, + rdpdr_component_string(component), rdpdr_packetid_string(packetid), pos); + } + break; + } + + // winpr_HexLogDump(log, lvl, Stream_Buffer(s), pos); + Stream_SetPosition(s, gpos); +} + +void rdpdr_dump_received_packet(wLog* log, DWORD lvl, wStream* s, const char* custom) +{ + rdpdr_dump_packet(log, lvl, s, custom, FALSE); +} + +void rdpdr_dump_send_packet(wLog* log, DWORD lvl, wStream* s, const char* custom) +{ + rdpdr_dump_packet(log, lvl, s, custom, TRUE); +} + +const char* rdpdr_irp_string(UINT32 major) +{ + switch (major) + { + case IRP_MJ_CREATE: + return "IRP_MJ_CREATE"; + case IRP_MJ_CLOSE: + return "IRP_MJ_CLOSE"; + case IRP_MJ_READ: + return "IRP_MJ_READ"; + case IRP_MJ_WRITE: + return "IRP_MJ_WRITE"; + case IRP_MJ_DEVICE_CONTROL: + return "IRP_MJ_DEVICE_CONTROL"; + case IRP_MJ_QUERY_VOLUME_INFORMATION: + return "IRP_MJ_QUERY_VOLUME_INFORMATION"; + case IRP_MJ_SET_VOLUME_INFORMATION: + return "IRP_MJ_SET_VOLUME_INFORMATION"; + case IRP_MJ_QUERY_INFORMATION: + return "IRP_MJ_QUERY_INFORMATION"; + case IRP_MJ_SET_INFORMATION: + return "IRP_MJ_SET_INFORMATION"; + case IRP_MJ_DIRECTORY_CONTROL: + return "IRP_MJ_DIRECTORY_CONTROL"; + case IRP_MJ_LOCK_CONTROL: + return "IRP_MJ_LOCK_CONTROL"; + default: + return "IRP_UNKNOWN"; + } +} + +const char* rdpdr_cap_type_string(UINT16 capability) +{ + switch (capability) + { + case CAP_GENERAL_TYPE: + return "CAP_GENERAL_TYPE"; + case CAP_PRINTER_TYPE: + return "CAP_PRINTER_TYPE"; + case CAP_PORT_TYPE: + return "CAP_PORT_TYPE"; + case CAP_DRIVE_TYPE: + return "CAP_DRIVE_TYPE"; + case CAP_SMARTCARD_TYPE: + return "CAP_SMARTCARD_TYPE"; + default: + return "CAP_UNKNOWN"; + } +} + +UINT rdpdr_read_capset_header(wLog* log, wStream* s, RDPDR_CAPABILITY_HEADER* header) +{ + WINPR_ASSERT(header); + if (!Stream_CheckAndLogRequiredLengthWLog(log, s, 8)) + return ERROR_INVALID_DATA; + + Stream_Read_UINT16(s, header->CapabilityType); /* CapabilityType (2 bytes) */ + Stream_Read_UINT16(s, header->CapabilityLength); /* CapabilityLength (2 bytes) */ + Stream_Read_UINT32(s, header->Version); /* Version (4 bytes) */ + + WLog_Print(log, WLOG_TRACE, + "capability %s [0x%04" PRIx16 "] got version %" PRIu32 ", length %" PRIu16, + rdpdr_cap_type_string(header->CapabilityType), header->CapabilityType, + header->Version, header->CapabilityLength); + if (header->CapabilityLength < 8) + { + WLog_Print(log, WLOG_ERROR, "capability %s got short length %" PRIu32, + rdpdr_cap_type_string(header->CapabilityType), header->CapabilityLength); + return ERROR_INVALID_DATA; + } + header->CapabilityLength -= 8; + if (!Stream_CheckAndLogRequiredLengthWLog(log, s, header->CapabilityLength)) + return ERROR_INVALID_DATA; + return CHANNEL_RC_OK; +} + +UINT rdpdr_write_capset_header(wLog* log, wStream* s, const RDPDR_CAPABILITY_HEADER* header) +{ + WINPR_ASSERT(header); + WINPR_ASSERT(header->CapabilityLength >= 8); + + if (!Stream_EnsureRemainingCapacity(s, header->CapabilityLength)) + { + WLog_Print(log, WLOG_ERROR, "not enough data in stream!"); + return ERROR_INVALID_DATA; + } + + WLog_Print(log, WLOG_TRACE, "writing capability %s version %" PRIu32 ", length %" PRIu16, + rdpdr_cap_type_string(header->CapabilityType), header->Version, + header->CapabilityLength); + Stream_Write_UINT16(s, header->CapabilityType); /* CapabilityType (2 bytes) */ + Stream_Write_UINT16(s, header->CapabilityLength); /* CapabilityLength (2 bytes) */ + Stream_Write_UINT32(s, header->Version); /* Version (4 bytes) */ + return CHANNEL_RC_OK; +} |