void guac_rdpdr_fs_process_set_rename_info(guac_rdpdr_device* device, wStream* input_stream, int file_id, int completion_id, int length) { int result; int filename_length; wStream* output_stream; char destination_path[GUAC_RDP_FS_MAX_PATH]; /* Read structure */ Stream_Seek_UINT8(input_stream); /* ReplaceIfExists */ Stream_Seek_UINT8(input_stream); /* RootDirectory */ Stream_Read_UINT32(input_stream, filename_length); /* FileNameLength */ /* Convert name to UTF-8 */ guac_rdp_utf16_to_utf8(Stream_Pointer(input_stream), filename_length/2, destination_path, sizeof(destination_path)); GUAC_RDP_DEBUG(2, "[file_id=%i] destination_path=\"%s\"", file_id, destination_path); /* If file moving to \Download folder, start stream, do not move */ if (strncmp(destination_path, "\\Download\\", 10) == 0) { guac_rdp_fs_file* file; /* Get file */ file = guac_rdp_fs_get_file((guac_rdp_fs*) device->data, file_id); if (file == NULL) return; /* Initiate download, pretend move succeeded */ guac_rdpdr_start_download(device, file->absolute_path); output_stream = guac_rdpdr_new_io_completion(device, completion_id, STATUS_SUCCESS, 4); } /* Otherwise, rename as requested */ else { result = guac_rdp_fs_rename((guac_rdp_fs*) device->data, file_id, destination_path); if (result < 0) output_stream = guac_rdpdr_new_io_completion(device, completion_id, guac_rdp_fs_get_status(result), 4); else output_stream = guac_rdpdr_new_io_completion(device, completion_id, STATUS_SUCCESS, 4); } Stream_Write_UINT32(output_stream, length); svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream); }
static BOOL TestStream_Seek(void) { BOOL rc = FALSE; wStream* s = Stream_New(NULL, 100); if (!s) goto out; if (s->pointer != s->buffer) goto out; Stream_Seek(s, 5); if (s->pointer != s->buffer + 5) goto out; Stream_Seek_UINT8(s); if (s->pointer != s->buffer + 6) goto out; Stream_Seek_UINT16(s); if (s->pointer != s->buffer + 8) goto out; Stream_Seek_UINT32(s); if (s->pointer != s->buffer + 12) goto out; Stream_Seek_UINT64(s); if (s->pointer != s->buffer + 20) goto out; rc = TRUE; out: Stream_Free(s, TRUE); return rc; }
BOOL rdp_read_share_data_header(wStream* s, UINT16* length, BYTE* type, UINT32* share_id, BYTE *compressed_type, UINT16 *compressed_len) { if (Stream_GetRemainingLength(s) < 12) return FALSE; /* Share Data Header */ Stream_Read_UINT32(s, *share_id); /* shareId (4 bytes) */ Stream_Seek_UINT8(s); /* pad1 (1 byte) */ Stream_Seek_UINT8(s); /* streamId (1 byte) */ Stream_Read_UINT16(s, *length); /* uncompressedLength (2 bytes) */ Stream_Read_UINT8(s, *type); /* pduType2, Data PDU Type (1 byte) */ Stream_Read_UINT8(s, *compressed_type); /* compressedType (1 byte) */ Stream_Read_UINT16(s, *compressed_len); /* compressedLength (2 bytes) */ return TRUE; }
static BOOL rfx_process_message_region(RFX_CONTEXT* context, RFX_MESSAGE* message, wStream* s) { int i; if (Stream_GetRemainingLength(s) < 3) { DEBUG_WARN("RfxMessageRegion packet too small"); return FALSE; } Stream_Seek_UINT8(s); /* regionFlags (1 byte) */ Stream_Read_UINT16(s, message->numRects); /* numRects (2 bytes) */ if (message->numRects < 1) { /* Unfortunately, it isn't documented. It seems that server asks to clip whole session when numRects = 0. Issue: https://github.com/FreeRDP/FreeRDP/issues/1738 */ DEBUG_WARN("no rects. Clip whole session."); message->numRects = 1; message->rects = (RFX_RECT*) realloc(message->rects, message->numRects * sizeof(RFX_RECT)); if (!message->rects) return FALSE; message->rects->x = 0; message->rects->y = 0; message->rects->width = context->width; message->rects->height = context->height; return TRUE; } if (Stream_GetRemainingLength(s) < (size_t) (8 * message->numRects)) { DEBUG_WARN("RfxMessageRegion packet too small for num_rects=%d", message->numRects); return FALSE; } message->rects = (RFX_RECT*) realloc(message->rects, message->numRects * sizeof(RFX_RECT)); if (!message->rects) return FALSE; /* rects */ for (i = 0; i < message->numRects; i++) { /* RFX_RECT */ Stream_Read_UINT16(s, message->rects[i].x); /* x (2 bytes) */ Stream_Read_UINT16(s, message->rects[i].y); /* y (2 bytes) */ Stream_Read_UINT16(s, message->rects[i].width); /* width (2 bytes) */ Stream_Read_UINT16(s, message->rects[i].height); /* height (2 bytes) */ WLog_Print(context->priv->log, WLOG_DEBUG, "rect %d (x,y=%d,%d w,h=%d %d).", i, message->rects[i].x, message->rects[i].y, message->rects[i].width, message->rects[i].height); } return TRUE; }
static void wts_write_drdynvc_header(wStream* s, BYTE Cmd, UINT32 ChannelId) { BYTE* bm; int cbChId; Stream_GetPointer(s, bm); Stream_Seek_UINT8(s); cbChId = wts_write_variable_uint(s, ChannelId); *bm = ((Cmd & 0x0F) << 4) | cbChId; }
static UINT video_data_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream *s) { VIDEO_CHANNEL_CALLBACK* callback = (VIDEO_CHANNEL_CALLBACK*) pChannelCallback; VIDEO_PLUGIN* video; VideoClientContext *context; UINT32 cbSize, packetType; TSMM_VIDEO_DATA data; video = (VIDEO_PLUGIN*) callback->plugin; context = (VideoClientContext *)video->wtsPlugin.pInterface; if (Stream_GetRemainingLength(s) < 4) return ERROR_INVALID_DATA; Stream_Read_UINT32(s, cbSize); if (cbSize < 8 || Stream_GetRemainingLength(s) < (cbSize-4)) { WLog_ERR(TAG, "invalid cbSize"); return ERROR_INVALID_DATA; } Stream_Read_UINT32(s, packetType); if (packetType != TSMM_PACKET_TYPE_VIDEO_DATA) { WLog_ERR(TAG, "only expecting VIDEO_DATA on the data channel"); return ERROR_INVALID_DATA; } if (Stream_GetRemainingLength(s) < 32) { WLog_ERR(TAG, "not enough bytes for a TSMM_VIDEO_DATA"); return ERROR_INVALID_DATA; } Stream_Read_UINT8(s, data.PresentationId); Stream_Read_UINT8(s, data.Version); Stream_Read_UINT8(s, data.Flags); Stream_Seek_UINT8(s); /* reserved */ Stream_Read_UINT64(s, data.hnsTimestamp); Stream_Read_UINT64(s, data.hnsDuration); Stream_Read_UINT16(s, data.CurrentPacketIndex); Stream_Read_UINT16(s, data.PacketsInSample); Stream_Read_UINT32(s, data.SampleNumber); Stream_Read_UINT32(s, data.cbSample); data.pSample = Stream_Pointer(s); /* WLog_DBG(TAG, "videoData: id:%"PRIu8" version:%"PRIu8" flags:0x%"PRIx8" timestamp=%"PRIu64" duration=%"PRIu64 " curPacketIndex:%"PRIu16" packetInSample:%"PRIu16" sampleNumber:%"PRIu32" cbSample:%"PRIu32"", data.PresentationId, data.Version, data.Flags, data.hnsTimestamp, data.hnsDuration, data.CurrentPacketIndex, data.PacketsInSample, data.SampleNumber, data.cbSample); */ return video_VideoData(context, &data); }
BOOL update_read_pointer_color(wStream* s, POINTER_COLOR_UPDATE* pointer_color) { if (Stream_GetRemainingLength(s) < 14) return FALSE; Stream_Read_UINT16(s, pointer_color->cacheIndex); /* cacheIndex (2 bytes) */ Stream_Read_UINT16(s, pointer_color->xPos); /* xPos (2 bytes) */ Stream_Read_UINT16(s, pointer_color->yPos); /* yPos (2 bytes) */ Stream_Read_UINT16(s, pointer_color->width); /* width (2 bytes) */ Stream_Read_UINT16(s, pointer_color->height); /* height (2 bytes) */ Stream_Read_UINT16(s, pointer_color->lengthAndMask); /* lengthAndMask (2 bytes) */ Stream_Read_UINT16(s, pointer_color->lengthXorMask); /* lengthXorMask (2 bytes) */ /** * There does not seem to be any documentation on why * xPos / yPos can be larger than width / height * so it is missing in documentation or a bug in implementation * 2.2.9.1.1.4.4 Color Pointer Update (TS_COLORPOINTERATTRIBUTE) */ if (pointer_color->xPos >= pointer_color->width) pointer_color->xPos = 0; if (pointer_color->yPos >= pointer_color->height) pointer_color->yPos = 0; if (pointer_color->lengthXorMask > 0) { if (Stream_GetRemainingLength(s) < pointer_color->lengthXorMask) return FALSE; if (!pointer_color->xorMaskData) pointer_color->xorMaskData = malloc(pointer_color->lengthXorMask); else pointer_color->xorMaskData = realloc(pointer_color->xorMaskData, pointer_color->lengthXorMask); Stream_Read(s, pointer_color->xorMaskData, pointer_color->lengthXorMask); } if (pointer_color->lengthAndMask > 0) { if (Stream_GetRemainingLength(s) < pointer_color->lengthAndMask) return FALSE; if (!pointer_color->andMaskData) pointer_color->andMaskData = malloc(pointer_color->lengthAndMask); else pointer_color->andMaskData = realloc(pointer_color->andMaskData, pointer_color->lengthAndMask); Stream_Read(s, pointer_color->andMaskData, pointer_color->lengthAndMask); } if (Stream_GetRemainingLength(s) > 0) Stream_Seek_UINT8(s); /* pad (1 byte) */ return TRUE; }
BOOL nego_read_request(rdpNego* nego, wStream* s) { BYTE li; BYTE c; BYTE type; tpkt_read_header(s); if (!tpdu_read_connection_request(s, &li)) return FALSE; if (li != Stream_GetRemainingLength(s) + 6) { fprintf(stderr, "Incorrect TPDU length indicator.\n"); return FALSE; } if (Stream_GetRemainingLength(s) > 8) { /* Optional routingToken or cookie, ending with CR+LF */ while (Stream_GetRemainingLength(s) > 0) { Stream_Read_UINT8(s, c); if (c != '\x0D') continue; Stream_Peek_UINT8(s, c); if (c != '\x0A') continue; Stream_Seek_UINT8(s); break; } } if (Stream_GetRemainingLength(s) >= 8) { /* rdpNegData (optional) */ Stream_Read_UINT8(s, type); /* Type */ if (type != TYPE_RDP_NEG_REQ) { fprintf(stderr, "Incorrect negotiation request type %d\n", type); return FALSE; } nego_process_negotiation_request(nego, s); } return TRUE; }
void guac_rdpsnd_process_receive(rdpSvcPlugin* plugin, wStream* input_stream) { guac_rdpsndPlugin* rdpsnd = (guac_rdpsndPlugin*) plugin; guac_rdpsnd_pdu_header header; /* Get audio stream from plugin */ audio_stream* audio = rdpsnd->audio; /* Read RDPSND PDU header */ Stream_Read_UINT8(input_stream, header.message_type); Stream_Seek_UINT8(input_stream); Stream_Read_UINT16(input_stream, header.body_size); /* * If next PDU is SNDWAVE (due to receiving WaveInfo PDU previously), * ignore the header and parse as a Wave PDU. */ if (rdpsnd->next_pdu_is_wave) { guac_rdpsnd_wave_handler(rdpsnd, audio, input_stream, &header); return; } /* Dispatch message to standard handlers */ switch (header.message_type) { /* Server Audio Formats and Version PDU */ case SNDC_FORMATS: guac_rdpsnd_formats_handler(rdpsnd, audio, input_stream, &header); break; /* Training PDU */ case SNDC_TRAINING: guac_rdpsnd_training_handler(rdpsnd, audio, input_stream, &header); break; /* WaveInfo PDU */ case SNDC_WAVE: guac_rdpsnd_wave_info_handler(rdpsnd, audio, input_stream, &header); break; /* Close PDU */ case SNDC_CLOSE: guac_rdpsnd_close_handler(rdpsnd, audio, input_stream, &header); break; } }
static BOOL rdpsnd_server_recv_waveconfirm(RdpsndServerContext* context, wStream* s) { UINT16 timestamp = 0; BYTE confirmBlockNum = 0; if (Stream_GetRemainingLength(s) < 4) return FALSE; Stream_Read_UINT16(s, timestamp); Stream_Read_UINT8(s, confirmBlockNum); Stream_Seek_UINT8(s); return TRUE; }
static void wts_read_drdynvc_capabilities_response(rdpPeerChannel* channel, UINT32 length) { UINT16 Version; if (length < 3) return; Stream_Seek_UINT8(channel->receiveData); /* Pad (1 byte) */ Stream_Read_UINT16(channel->receiveData, Version); DEBUG_DVC("Version: %d", Version); channel->vcm->drdynvc_state = DRDYNVC_STATE_READY; }
static BOOL rfx_process_message_region(RFX_CONTEXT* context, RFX_MESSAGE* message, wStream* s) { int i; if (Stream_GetRemainingLength(s) < 3) { DEBUG_WARN("RfxMessageRegion packet too small"); return FALSE; } Stream_Seek_UINT8(s); /* regionFlags (1 byte) */ Stream_Read_UINT16(s, message->num_rects); /* numRects (2 bytes) */ if (message->num_rects < 1) { DEBUG_WARN("no rects."); return TRUE; } if (Stream_GetRemainingLength(s) < 8 * message->num_rects) { DEBUG_WARN("RfxMessageRegion packet too small for num_rects=%d", message->num_rects); return FALSE; } if (message->rects != NULL) message->rects = (RFX_RECT*) realloc(message->rects, message->num_rects * sizeof(RFX_RECT)); else message->rects = (RFX_RECT*) malloc(message->num_rects * sizeof(RFX_RECT)); /* rects */ for (i = 0; i < message->num_rects; i++) { /* RFX_RECT */ Stream_Read_UINT16(s, message->rects[i].x); /* x (2 bytes) */ Stream_Read_UINT16(s, message->rects[i].y); /* y (2 bytes) */ Stream_Read_UINT16(s, message->rects[i].width); /* width (2 bytes) */ Stream_Read_UINT16(s, message->rects[i].height); /* height (2 bytes) */ DEBUG_RFX("rect %d (%d %d %d %d).", i, message->rects[i].x, message->rects[i].y, message->rects[i].width, message->rects[i].height); } return TRUE; }
/** * Function description * * @return 0 on success, otherwise a Win32 error code */ static UINT rdpsnd_server_recv_waveconfirm(RdpsndServerContext* context, wStream* s) { UINT16 timestamp; BYTE confirmBlockNum; UINT error = CHANNEL_RC_OK; if (Stream_GetRemainingLength(s) < 4) { WLog_ERR(TAG, "not enought data in stream!"); return ERROR_INVALID_DATA; } Stream_Read_UINT16(s, timestamp); Stream_Read_UINT8(s, confirmBlockNum); Stream_Seek_UINT8(s); IFCALLRET(context->ConfirmBlock, error, context, confirmBlockNum, timestamp); if (error) WLog_ERR(TAG, "context->ConfirmBlock failed with error %lu", error); return error; }
BOOL WINAPI FreeRDP_WTSVirtualChannelWrite(HANDLE hChannelHandle, PCHAR Buffer, ULONG Length, PULONG pBytesWritten) { wStream* s; int cbLen; int cbChId; int first; BYTE* buffer; UINT32 length; UINT32 written; rdpPeerChannel* channel = (rdpPeerChannel*) hChannelHandle; if (!channel) return FALSE; if (channel->channelType == RDP_PEER_CHANNEL_TYPE_SVC) { length = Length; buffer = (BYTE*) malloc(length); CopyMemory(buffer, Buffer, length); wts_queue_send_item(channel, buffer, length); } else if (!channel->vcm->drdynvc_channel || (channel->vcm->drdynvc_state != DRDYNVC_STATE_READY)) { DEBUG_DVC("drdynvc not ready"); return FALSE; } else { first = TRUE; while (Length > 0) { s = Stream_New(NULL, channel->client->settings->VirtualChannelChunkSize); buffer = Stream_Buffer(s); Stream_Seek_UINT8(s); cbChId = wts_write_variable_uint(s, channel->channelId); if (first && (Length > (UINT32) Stream_GetRemainingLength(s))) { cbLen = wts_write_variable_uint(s, Length); buffer[0] = (DATA_FIRST_PDU << 4) | (cbLen << 2) | cbChId; } else { buffer[0] = (DATA_PDU << 4) | cbChId; } first = FALSE; written = Stream_GetRemainingLength(s); if (written > Length) written = Length; Stream_Write(s, Buffer, written); length = Stream_GetPosition(s); Stream_Free(s, FALSE); Length -= written; Buffer += written; wts_queue_send_item(channel->vcm->drdynvc_channel, buffer, length); } } if (pBytesWritten) *pBytesWritten = Length; return TRUE; }
/** * Function description * * @return 0 on success, otherwise a Win32 error code */ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s) { int i, num_known_format = 0; UINT32 flags, vol, pitch; UINT16 udpPort; BYTE lastblock; UINT error = CHANNEL_RC_OK; if (Stream_GetRemainingLength(s) < 20) { WLog_ERR(TAG, "not enought data in stream!"); return ERROR_INVALID_DATA; } Stream_Read_UINT32(s, flags); /* dwFlags */ Stream_Read_UINT32(s, vol); /* dwVolume */ Stream_Read_UINT32(s, pitch); /* dwPitch */ Stream_Read_UINT16(s, udpPort); /* wDGramPort */ Stream_Read_UINT16(s, context->num_client_formats); /* wNumberOfFormats */ Stream_Read_UINT8(s, lastblock); /* cLastBlockConfirmed */ Stream_Read_UINT16(s, context->clientVersion); /* wVersion */ Stream_Seek_UINT8(s); /* bPad */ /* this check is only a guess as cbSize can influence the size of a format record */ if (Stream_GetRemainingLength(s) < context->num_client_formats * 18) { WLog_ERR(TAG, "not enought data in stream!"); return ERROR_INVALID_DATA; } if (!context->num_client_formats) { WLog_ERR(TAG, "client doesn't support any format!"); return ERROR_INTERNAL_ERROR; } context->client_formats = (AUDIO_FORMAT *)calloc(context->num_client_formats, sizeof(AUDIO_FORMAT)); if (!context->client_formats) { WLog_ERR(TAG, "calloc failed!"); return CHANNEL_RC_NO_MEMORY; } for (i = 0; i < context->num_client_formats; i++) { if (Stream_GetRemainingLength(s) < 18) { WLog_ERR(TAG, "not enought data in stream!"); error = ERROR_INVALID_DATA; goto out_free; } Stream_Read_UINT16(s, context->client_formats[i].wFormatTag); Stream_Read_UINT16(s, context->client_formats[i].nChannels); Stream_Read_UINT32(s, context->client_formats[i].nSamplesPerSec); Stream_Read_UINT32(s, context->client_formats[i].nAvgBytesPerSec); Stream_Read_UINT16(s, context->client_formats[i].nBlockAlign); Stream_Read_UINT16(s, context->client_formats[i].wBitsPerSample); Stream_Read_UINT16(s, context->client_formats[i].cbSize); if (context->client_formats[i].cbSize > 0) { if (!Stream_SafeSeek(s, context->client_formats[i].cbSize)) { WLog_ERR(TAG, "Stream_SafeSeek failed!"); error = ERROR_INTERNAL_ERROR; goto out_free; } } if (context->client_formats[i].wFormatTag != 0) { //lets call this a known format //TODO: actually look through our own list of known formats num_known_format++; } } if (!context->num_client_formats) { WLog_ERR(TAG, "client doesn't support any known format!"); goto out_free; } return CHANNEL_RC_OK; out_free: free(context->client_formats); return error; }
/** * Function description * * @return 0 on success, otherwise a Win32 error code */ UINT rdpsnd_server_handle_messages(RdpsndServerContext *context) { DWORD bytesReturned; UINT ret = CHANNEL_RC_OK; RdpsndServerPrivate *priv = context->priv; wStream *s = priv->input_stream; if (!WTSVirtualChannelRead(priv->ChannelHandle, 0, (PCHAR)Stream_Pointer(s), priv->expectedBytes, &bytesReturned)) { if (GetLastError() == ERROR_NO_DATA) return ERROR_NO_DATA; WLog_ERR(TAG, "channel connection closed"); return ERROR_INTERNAL_ERROR; } priv->expectedBytes -= bytesReturned; Stream_Seek(s, bytesReturned); if (priv->expectedBytes) return CHANNEL_RC_OK; Stream_SealLength(s); Stream_SetPosition(s, 0); if (priv->waitingHeader) { /* header case */ Stream_Read_UINT8(s, priv->msgType); Stream_Seek_UINT8(s); /* bPad */ Stream_Read_UINT16(s, priv->expectedBytes); priv->waitingHeader = FALSE; Stream_SetPosition(s, 0); if (priv->expectedBytes) { if (!Stream_EnsureCapacity(s, priv->expectedBytes)) { WLog_ERR(TAG, "Stream_EnsureCapacity failed!"); return CHANNEL_RC_NO_MEMORY; } return CHANNEL_RC_OK; } } /* when here we have the header + the body */ #ifdef WITH_DEBUG_SND WLog_DBG(TAG, "message type %d", priv->msgType); #endif priv->expectedBytes = 4; priv->waitingHeader = TRUE; switch (priv->msgType) { case SNDC_WAVECONFIRM: ret = rdpsnd_server_recv_waveconfirm(context, s); break; case SNDC_FORMATS: ret = rdpsnd_server_recv_formats(context, s); if ((ret == CHANNEL_RC_OK) && (context->clientVersion < 6)) IFCALL(context->Activated, context); break; case SNDC_QUALITYMODE: ret = rdpsnd_server_recv_quality_mode(context, s); Stream_SetPosition(s, 0); /* in case the Activated callback tries to treat some messages */ if ((ret == CHANNEL_RC_OK) && (context->clientVersion >= 6)) IFCALL(context->Activated, context); break; default: WLog_ERR(TAG, "UNKOWN MESSAGE TYPE!! (%#0X)", priv->msgType); ret = ERROR_INVALID_DATA; break; } Stream_SetPosition(s, 0); return ret; }
BOOL rdpsnd_server_handle_messages(RdpsndServerContext *context) { DWORD bytesReturned; BOOL ret; RdpsndServerPrivate *priv = context->priv; wStream *s = priv->input_stream; if (!WTSVirtualChannelRead(priv->ChannelHandle, 0, (PCHAR)Stream_Pointer(s), priv->expectedBytes, &bytesReturned)) { if (GetLastError() == ERROR_NO_DATA) return TRUE; CLOG_ERR( "%s: channel connection closed\n", __FUNCTION__); return FALSE; } priv->expectedBytes -= bytesReturned; Stream_Seek(s, bytesReturned); if (priv->expectedBytes) return TRUE; Stream_SetPosition(s, 0); if (priv->waitingHeader) { /* header case */ Stream_Read_UINT8(s, priv->msgType); Stream_Seek_UINT8(s); /* bPad */ Stream_Read_UINT16(s, priv->expectedBytes); priv->waitingHeader = FALSE; Stream_SetPosition(s, 0); if (priv->expectedBytes) { Stream_EnsureCapacity(s, priv->expectedBytes); return TRUE; } } /* when here we have the header + the body */ #ifdef WITH_DEBUG_SND CLOG_ERR( "%s: message type %d\n", __FUNCTION__, priv->msgType); #endif priv->expectedBytes = 4; priv->waitingHeader = TRUE; switch (priv->msgType) { case SNDC_WAVECONFIRM: ret = rdpsnd_server_recv_waveconfirm(context, s); break; case SNDC_FORMATS: ret = rdpsnd_server_recv_formats(context, s); break; case SNDC_QUALITYMODE: ret = rdpsnd_server_recv_quality_mode(context, s); Stream_SetPosition(s, 0); /* in case the Activated callback tries to treat some messages */ if (ret) { IFCALL(context->Activated, context); } break; default: CLOG_ERR( "%s: UNKOWN MESSAGE TYPE!! (%#0X)\n\n", __FUNCTION__, priv->msgType); ret = FALSE; break; } Stream_SetPosition(s, 0); return ret; }
static BOOL rfx_process_message_region(RFX_CONTEXT* context, RFX_MESSAGE* message, wStream* s, UINT16* pExpectedBlockType) { int i; UINT16 regionType; UINT16 numTileSets; if (*pExpectedBlockType != WBT_REGION) { WLog_ERR(TAG, "%s: message unexpeced", __FUNCTION__); return FALSE; } *pExpectedBlockType = WBT_EXTENSION; if (Stream_GetRemainingLength(s) < 3) { WLog_ERR(TAG, "%s: packet too small (regionFlags/numRects)", __FUNCTION__); return FALSE; } Stream_Seek_UINT8(s); /* regionFlags (1 byte) */ Stream_Read_UINT16(s, message->numRects); /* numRects (2 bytes) */ if (message->numRects < 1) { /* If numRects is zero the decoder must generate a rectangle with coordinates (0, 0, width, height). See [MS-RDPRFX] (revision >= 17.0) 2.2.2.3.3 TS_RFX_REGION https://msdn.microsoft.com/en-us/library/ff635233.aspx */ if (!(message->rects = (RFX_RECT*) malloc(sizeof(RFX_RECT)))) return FALSE; message->numRects = 1; message->rects->x = 0; message->rects->y = 0; message->rects->width = context->width; message->rects->height = context->height; return TRUE; } if (Stream_GetRemainingLength(s) < (size_t) (8 * message->numRects)) { WLog_ERR(TAG, "%s: packet too small for num_rects=%d", __FUNCTION__, message->numRects); return FALSE; } if (!(message->rects = (RFX_RECT*) calloc(message->numRects, sizeof(RFX_RECT)))) return FALSE; /* rects */ for (i = 0; i < message->numRects; i++) { /* RFX_RECT */ Stream_Read_UINT16(s, message->rects[i].x); /* x (2 bytes) */ Stream_Read_UINT16(s, message->rects[i].y); /* y (2 bytes) */ Stream_Read_UINT16(s, message->rects[i].width); /* width (2 bytes) */ Stream_Read_UINT16(s, message->rects[i].height); /* height (2 bytes) */ WLog_Print(context->priv->log, WLOG_DEBUG, "rect %d (x,y=%d,%d w,h=%d %d).", i, message->rects[i].x, message->rects[i].y, message->rects[i].width, message->rects[i].height); } if (Stream_GetRemainingLength(s) < 4) { WLog_ERR(TAG, "%s: packet too small (regionType/numTileSets)", __FUNCTION__); return FALSE; } Stream_Read_UINT16(s, regionType); /* regionType (2 bytes): MUST be set to CBT_REGION (0xCAC1) */ Stream_Read_UINT16(s, numTileSets); /* numTilesets (2 bytes): MUST be set to 0x0001. */ if (regionType != CBT_REGION) { WLog_ERR(TAG, "%s: invalid region type 0x%04X", __FUNCTION__, regionType); return TRUE; } if (numTileSets != 0x0001) { WLog_ERR(TAG, "%s: invalid number of tilesets (%u)", __FUNCTION__, numTileSets); return FALSE; } return TRUE; }
BOOL gcc_read_client_core_data(wStream* s, rdpSettings* settings, UINT16 blockLength) { char* str = NULL; UINT32 version; UINT32 color_depth; UINT16 colorDepth = 0; UINT16 postBeta2ColorDepth = 0; UINT16 highColorDepth = 0; UINT16 supportedColorDepths = 0; UINT32 serverSelectedProtocol = 0; /* Length of all required fields, until imeFileName */ if (blockLength < 128) return FALSE; Stream_Read_UINT32(s, version); /* version */ settings->RdpVersion = (version == RDP_VERSION_4 ? 4 : 7); Stream_Read_UINT16(s, settings->DesktopWidth); /* DesktopWidth */ Stream_Read_UINT16(s, settings->DesktopHeight); /* DesktopHeight */ Stream_Read_UINT16(s, colorDepth); /* ColorDepth */ Stream_Seek_UINT16(s); /* SASSequence (Secure Access Sequence) */ Stream_Read_UINT32(s, settings->KeyboardLayout); /* KeyboardLayout */ Stream_Read_UINT32(s, settings->ClientBuild); /* ClientBuild */ /* clientName (32 bytes, null-terminated unicode, truncated to 15 characters) */ ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(s), 32 / 2, &str, 0, NULL, NULL); Stream_Seek(s, 32); sprintf_s(settings->ClientHostname, 31, "%s", str); settings->ClientHostname[31] = 0; free(str); str = NULL; Stream_Read_UINT32(s, settings->KeyboardType); /* KeyboardType */ Stream_Read_UINT32(s, settings->KeyboardSubType); /* KeyboardSubType */ Stream_Read_UINT32(s, settings->KeyboardFunctionKey); /* KeyboardFunctionKey */ Stream_Seek(s, 64); /* imeFileName */ blockLength -= 128; /** * The following fields are all optional. If one field is present, all of the preceding * fields MUST also be present. If one field is not present, all of the subsequent fields * MUST NOT be present. * We must check the bytes left before reading each field. */ do { if (blockLength < 2) break; Stream_Read_UINT16(s, postBeta2ColorDepth); /* postBeta2ColorDepth */ blockLength -= 2; if (blockLength < 2) break; Stream_Seek_UINT16(s); /* clientProductID */ blockLength -= 2; if (blockLength < 4) break; Stream_Seek_UINT32(s); /* serialNumber */ blockLength -= 4; if (blockLength < 2) break; Stream_Read_UINT16(s, highColorDepth); /* highColorDepth */ blockLength -= 2; if (blockLength < 2) break; Stream_Read_UINT16(s, supportedColorDepths); /* supportedColorDepths */ blockLength -= 2; if (blockLength < 2) break; Stream_Read_UINT16(s, settings->EarlyCapabilityFlags); /* earlyCapabilityFlags */ blockLength -= 2; if (blockLength < 64) break; ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(s), 64 / 2, &str, 0, NULL, NULL); Stream_Seek(s, 64); sprintf_s(settings->ClientProductId, 32, "%s", str); free(str); blockLength -= 64; if (blockLength < 1) break; Stream_Read_UINT8(s, settings->PerformanceFlags); /* connectionType */ blockLength -= 1; if (blockLength < 1) break; Stream_Seek_UINT8(s); /* pad1octet */ blockLength -= 1; if (blockLength < 4) break; Stream_Read_UINT32(s, serverSelectedProtocol); /* serverSelectedProtocol */ blockLength -= 4; if (settings->SelectedProtocol != serverSelectedProtocol) return FALSE; } while (0); if (highColorDepth > 0) { if (settings->EarlyCapabilityFlags & RNS_UD_CS_WANT_32BPP_SESSION) color_depth = 32; else color_depth = highColorDepth; } else if (postBeta2ColorDepth > 0) { switch (postBeta2ColorDepth) { case RNS_UD_COLOR_4BPP: color_depth = 4; break; case RNS_UD_COLOR_8BPP: color_depth = 8; break; case RNS_UD_COLOR_16BPP_555: color_depth = 15; break; case RNS_UD_COLOR_16BPP_565: color_depth = 16; break; case RNS_UD_COLOR_24BPP: color_depth = 24; break; default: return FALSE; } } else { switch (colorDepth) { case RNS_UD_COLOR_4BPP: color_depth = 4; break; case RNS_UD_COLOR_8BPP: color_depth = 8; break; default: return FALSE; } } /* * If we are in server mode, accept client's color depth only if * it is smaller than ours. This is what Windows server does. */ if ((color_depth < settings->ColorDepth) || !settings->ServerMode) settings->ColorDepth = color_depth; return TRUE; }
BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UINT32 Length, wStream* input) { char* s = NULL; mode_t m; UINT64 size; int status; char* fullpath; struct STAT st; #if defined(__linux__) && !defined(ANDROID) || defined(sun) struct timespec tv[2]; #else struct timeval tv[2]; #endif UINT64 LastWriteTime; UINT32 FileAttributes; UINT32 FileNameLength; m = 0; switch (FsInformationClass) { case FileBasicInformation: /* http://msdn.microsoft.com/en-us/library/cc232094.aspx */ Stream_Seek_UINT64(input); /* CreationTime */ Stream_Seek_UINT64(input); /* LastAccessTime */ Stream_Read_UINT64(input, LastWriteTime); Stream_Seek_UINT64(input); /* ChangeTime */ Stream_Read_UINT32(input, FileAttributes); if (FSTAT(file->fd, &st) != 0) return FALSE; tv[0].tv_sec = st.st_atime; tv[1].tv_sec = (LastWriteTime > 0 ? FILE_TIME_RDP_TO_SYSTEM(LastWriteTime) : st.st_mtime); #ifndef WIN32 /* TODO on win32 */ #ifdef ANDROID tv[0].tv_usec = 0; tv[1].tv_usec = 0; utimes(file->fullpath, tv); #elif defined (__linux__) || defined (sun) tv[0].tv_nsec = 0; tv[1].tv_nsec = 0; futimens(file->fd, tv); #else tv[0].tv_usec = 0; tv[1].tv_usec = 0; futimes(file->fd, tv); #endif if (FileAttributes > 0) { m = st.st_mode; if ((FileAttributes & FILE_ATTRIBUTE_READONLY) == 0) m |= S_IWUSR; else m &= ~S_IWUSR; if (m != st.st_mode) fchmod(file->fd, st.st_mode); } #endif break; case FileEndOfFileInformation: /* http://msdn.microsoft.com/en-us/library/cc232067.aspx */ case FileAllocationInformation: /* http://msdn.microsoft.com/en-us/library/cc232076.aspx */ Stream_Read_UINT64(input, size); #ifndef _WIN32 if (ftruncate(file->fd, size) != 0) return FALSE; #endif break; case FileDispositionInformation: /* http://msdn.microsoft.com/en-us/library/cc232098.aspx */ /* http://msdn.microsoft.com/en-us/library/cc241371.aspx */ if (file->is_dir && !dir_empty(file->fullpath)) break; if (Length) Stream_Read_UINT8(input, file->delete_pending); else file->delete_pending = 1; break; case FileRenameInformation: /* http://msdn.microsoft.com/en-us/library/cc232085.aspx */ Stream_Seek_UINT8(input); /* ReplaceIfExists */ Stream_Seek_UINT8(input); /* RootDirectory */ Stream_Read_UINT32(input, FileNameLength); status = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(input), FileNameLength / 2, &s, 0, NULL, NULL); if (status < 1) if (!(s = (char*) calloc(1, 1))) { WLog_ERR(TAG, "calloc failed!"); return FALSE; } fullpath = drive_file_combine_fullpath(file->basepath, s); if (!fullpath) { WLog_ERR(TAG, "drive_file_combine_fullpath failed!"); free (s); return FALSE; } free(s); #ifdef _WIN32 if (file->fd) close(file->fd); #endif if (rename(file->fullpath, fullpath) == 0) { drive_file_set_fullpath(file, fullpath); #ifdef _WIN32 file->fd = OPEN(fullpath, O_RDWR | O_BINARY); #endif } else { free(fullpath); return FALSE; } break; default: return FALSE; } return TRUE; }
void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd, wStream* input_stream, guac_rdpsnd_pdu_header* header) { int server_format_count; int server_version; int i; wStream* output_stream; int output_body_size; unsigned char* output_stream_end; /* Get associated client data */ guac_client* client = rdpsnd->client; guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; /* Get audio stream from client data */ guac_audio_stream* audio = rdp_client->audio; /* Format header */ Stream_Seek(input_stream, 14); Stream_Read_UINT16(input_stream, server_format_count); Stream_Seek_UINT8(input_stream); Stream_Read_UINT16(input_stream, server_version); Stream_Seek_UINT8(input_stream); /* Initialize Client Audio Formats and Version PDU */ output_stream = Stream_New(NULL, 24); Stream_Write_UINT8(output_stream, SNDC_FORMATS); Stream_Write_UINT8(output_stream, 0); /* Fill in body size later */ Stream_Seek_UINT16(output_stream); /* offset = 0x02 */ /* Flags, volume, and pitch */ Stream_Write_UINT32(output_stream, TSSNDCAPS_ALIVE); Stream_Write_UINT32(output_stream, 0); Stream_Write_UINT32(output_stream, 0); /* Datagram port (UDP) */ Stream_Write_UINT16(output_stream, 0); /* Fill in format count later */ Stream_Seek_UINT16(output_stream); /* offset = 0x12 */ /* Version and padding */ Stream_Write_UINT8(output_stream, 0); Stream_Write_UINT16(output_stream, 6); Stream_Write_UINT8(output_stream, 0); /* Check each server format, respond if supported and audio is enabled */ if (audio != NULL) { for (i=0; i < server_format_count; i++) { unsigned char* format_start; int format_tag; int channels; int rate; int bps; int body_size; /* Remember position in stream */ Stream_GetPointer(input_stream, format_start); /* Read format */ Stream_Read_UINT16(input_stream, format_tag); Stream_Read_UINT16(input_stream, channels); Stream_Read_UINT32(input_stream, rate); Stream_Seek_UINT32(input_stream); Stream_Seek_UINT16(input_stream); Stream_Read_UINT16(input_stream, bps); /* Skip past extra data */ Stream_Read_UINT16(input_stream, body_size); Stream_Seek(input_stream, body_size); /* If PCM, accept */ if (format_tag == WAVE_FORMAT_PCM) { /* If can fit another format, accept it */ if (rdpsnd->format_count < GUAC_RDP_MAX_FORMATS) { /* Add channel */ int current = rdpsnd->format_count++; rdpsnd->formats[current].rate = rate; rdpsnd->formats[current].channels = channels; rdpsnd->formats[current].bps = bps; /* Log format */ guac_client_log(client, GUAC_LOG_INFO, "Accepted format: %i-bit PCM with %i channels at " "%i Hz", bps, channels, rate); /* Ensure audio stream is configured to use accepted * format */ guac_audio_stream_reset(audio, NULL, rate, channels, bps); /* Queue format for sending as accepted */ Stream_EnsureRemainingCapacity(output_stream, 18 + body_size); Stream_Write(output_stream, format_start, 18 + body_size); /* * BEWARE that using Stream_EnsureRemainingCapacity means * that any pointers returned via Stream_GetPointer on * output_stream are invalid. */ } /* Otherwise, log that we dropped one */ else guac_client_log(client, GUAC_LOG_INFO, "Dropped valid format: %i-bit PCM with %i " "channels at %i Hz", bps, channels, rate); } } } /* Otherwise, ignore all supported formats as we do not intend to actually * receive audio */ else guac_client_log(client, GUAC_LOG_DEBUG, "Audio explicitly disabled. Ignoring supported formats."); /* Calculate size of PDU */ output_body_size = Stream_GetPosition(output_stream) - 4; Stream_GetPointer(output_stream, output_stream_end); /* Set body size */ Stream_SetPosition(output_stream, 0x02); Stream_Write_UINT16(output_stream, output_body_size); /* Set format count */ Stream_SetPosition(output_stream, 0x12); Stream_Write_UINT16(output_stream, rdpsnd->format_count); /* Reposition cursor at end (necessary for message send) */ Stream_SetPointer(output_stream, output_stream_end); /* Send accepted formats */ pthread_mutex_lock(&(rdp_client->rdp_lock)); svc_plugin_send((rdpSvcPlugin*)rdpsnd, output_stream); /* If version greater than 6, must send Quality Mode PDU */ if (server_version >= 6) { /* Always send High Quality for now */ output_stream = Stream_New(NULL, 8); Stream_Write_UINT8(output_stream, SNDC_QUALITYMODE); Stream_Write_UINT8(output_stream, 0); Stream_Write_UINT16(output_stream, 4); Stream_Write_UINT16(output_stream, HIGH_QUALITY); Stream_Write_UINT16(output_stream, 0); svc_plugin_send((rdpSvcPlugin*)rdpsnd, output_stream); } pthread_mutex_unlock(&(rdp_client->rdp_lock)); }
static BOOL rfx_process_message_tileset(RFX_CONTEXT* context, RFX_MESSAGE* message, wStream* s, UINT16* pExpecedBlockType) { BOOL rc; int i, close_cnt; int pos; BYTE quant; RFX_TILE* tile; UINT32* quants; UINT16 subtype; UINT32 blockLen; UINT32 blockType; UINT32 tilesDataSize; PTP_WORK* work_objects = NULL; RFX_TILE_PROCESS_WORK_PARAM* params = NULL; void *pmem; if (*pExpecedBlockType != WBT_EXTENSION) { WLog_ERR(TAG, "%s: message unexpeced", __FUNCTION__); return FALSE; } *pExpecedBlockType = WBT_FRAME_END; if (Stream_GetRemainingLength(s) < 14) { WLog_ERR(TAG, "RfxMessageTileSet packet too small"); return FALSE; } Stream_Read_UINT16(s, subtype); /* subtype (2 bytes) must be set to CBT_TILESET (0xCAC2) */ if (subtype != CBT_TILESET) { WLog_ERR(TAG, "invalid subtype, expected CBT_TILESET."); return FALSE; } Stream_Seek_UINT16(s); /* idx (2 bytes), must be set to 0x0000 */ Stream_Seek_UINT16(s); /* properties (2 bytes) */ Stream_Read_UINT8(s, context->numQuant); /* numQuant (1 byte) */ Stream_Seek_UINT8(s); /* tileSize (1 byte), must be set to 0x40 */ if (context->numQuant < 1) { WLog_ERR(TAG, "no quantization value."); return FALSE; } Stream_Read_UINT16(s, message->numTiles); /* numTiles (2 bytes) */ if (message->numTiles < 1) { WLog_ERR(TAG, "no tiles."); return FALSE; } Stream_Read_UINT32(s, tilesDataSize); /* tilesDataSize (4 bytes) */ if (!(pmem = realloc((void*) context->quants, context->numQuant * 10 * sizeof(UINT32)))) return FALSE; quants = context->quants = (UINT32*) pmem; /* quantVals */ if (Stream_GetRemainingLength(s) < (size_t) (context->numQuant * 5)) { WLog_ERR(TAG, "RfxMessageTileSet packet too small for num_quants=%d", context->numQuant); return FALSE; } for (i = 0; i < context->numQuant; i++) { /* RFX_CODEC_QUANT */ Stream_Read_UINT8(s, quant); *quants++ = (quant & 0x0F); *quants++ = (quant >> 4); Stream_Read_UINT8(s, quant); *quants++ = (quant & 0x0F); *quants++ = (quant >> 4); Stream_Read_UINT8(s, quant); *quants++ = (quant & 0x0F); *quants++ = (quant >> 4); Stream_Read_UINT8(s, quant); *quants++ = (quant & 0x0F); *quants++ = (quant >> 4); Stream_Read_UINT8(s, quant); *quants++ = (quant & 0x0F); *quants++ = (quant >> 4); WLog_Print(context->priv->log, WLOG_DEBUG, "quant %d (%d %d %d %d %d %d %d %d %d %d).", i, context->quants[i * 10], context->quants[i * 10 + 1], context->quants[i * 10 + 2], context->quants[i * 10 + 3], context->quants[i * 10 + 4], context->quants[i * 10 + 5], context->quants[i * 10 + 6], context->quants[i * 10 + 7], context->quants[i * 10 + 8], context->quants[i * 10 + 9]); } if (!(message->tiles = (RFX_TILE**) calloc(message->numTiles, sizeof(RFX_TILE*)))) { message->numTiles = 0; return FALSE; } if (context->priv->UseThreads) { work_objects = (PTP_WORK*) calloc(message->numTiles, sizeof(PTP_WORK)); params = (RFX_TILE_PROCESS_WORK_PARAM*) calloc(message->numTiles, sizeof(RFX_TILE_PROCESS_WORK_PARAM)); if (!work_objects) { free(params); return FALSE; } if (!params) { free(work_objects); return FALSE; } } /* tiles */ close_cnt = 0; rc = TRUE; for (i = 0; i < message->numTiles; i++) { if (!(tile = (RFX_TILE*) ObjectPool_Take(context->priv->TilePool))) { WLog_ERR(TAG, "RfxMessageTileSet failed to get tile from object pool"); rc = FALSE; break; } message->tiles[i] = tile; /* RFX_TILE */ if (Stream_GetRemainingLength(s) < 6) { WLog_ERR(TAG, "RfxMessageTileSet packet too small to read tile %d/%d", i, message->numTiles); rc = FALSE; break; } Stream_Read_UINT16(s, blockType); /* blockType (2 bytes), must be set to CBT_TILE (0xCAC3) */ Stream_Read_UINT32(s, blockLen); /* blockLen (4 bytes) */ if (Stream_GetRemainingLength(s) < blockLen - 6) { WLog_ERR(TAG, "RfxMessageTileSet not enough bytes to read tile %d/%d with blocklen=%d", i, message->numTiles, blockLen); rc = FALSE; break; } pos = Stream_GetPosition(s) - 6 + blockLen; if (blockType != CBT_TILE) { WLog_ERR(TAG, "unknown block type 0x%X, expected CBT_TILE (0xCAC3).", blockType); rc = FALSE; break; } Stream_Read_UINT8(s, tile->quantIdxY); /* quantIdxY (1 byte) */ Stream_Read_UINT8(s, tile->quantIdxCb); /* quantIdxCb (1 byte) */ Stream_Read_UINT8(s, tile->quantIdxCr); /* quantIdxCr (1 byte) */ Stream_Read_UINT16(s, tile->xIdx); /* xIdx (2 bytes) */ Stream_Read_UINT16(s, tile->yIdx); /* yIdx (2 bytes) */ Stream_Read_UINT16(s, tile->YLen); /* YLen (2 bytes) */ Stream_Read_UINT16(s, tile->CbLen); /* CbLen (2 bytes) */ Stream_Read_UINT16(s, tile->CrLen); /* CrLen (2 bytes) */ Stream_GetPointer(s, tile->YData); Stream_Seek(s, tile->YLen); Stream_GetPointer(s, tile->CbData); Stream_Seek(s, tile->CbLen); Stream_GetPointer(s, tile->CrData); Stream_Seek(s, tile->CrLen); tile->x = tile->xIdx * 64; tile->y = tile->yIdx * 64; if (context->priv->UseThreads) { assert(params); params[i].context = context; params[i].tile = message->tiles[i]; if (!(work_objects[i] = CreateThreadpoolWork((PTP_WORK_CALLBACK) rfx_process_message_tile_work_callback, (void*) ¶ms[i], &context->priv->ThreadPoolEnv))) { WLog_ERR(TAG, "CreateThreadpoolWork failed."); rc = FALSE; break; } SubmitThreadpoolWork(work_objects[i]); close_cnt = i + 1; } else { rfx_decode_rgb(context, tile, tile->data, 64 * 4); } Stream_SetPosition(s, pos); } if (context->priv->UseThreads) { for (i = 0; i < close_cnt; i++) { WaitForThreadpoolWorkCallbacks(work_objects[i], FALSE); CloseThreadpoolWork(work_objects[i]); } free(work_objects); free(params); } for (i = 0; i < message->numTiles; i++) { if (!(tile = message->tiles[i])) continue; tile->YLen = tile->CbLen = tile->CrLen = 0; tile->YData = tile->CbData = tile->CrData = NULL; } return rc; }
static BOOL rfx_process_message_region(RFX_CONTEXT* context, RFX_MESSAGE* message, wStream* s, UINT16* pExpectedBlockType) { int i; UINT16 regionType; UINT16 numTileSets; if (*pExpectedBlockType != WBT_REGION) { WLog_ERR(TAG, "%s: message unexpeced", __FUNCTION__); return FALSE; } *pExpectedBlockType = WBT_EXTENSION; if (Stream_GetRemainingLength(s) < 3) { WLog_ERR(TAG, "%s: packet too small (regionFlags/numRects)", __FUNCTION__); return FALSE; } Stream_Seek_UINT8(s); /* regionFlags (1 byte) */ Stream_Read_UINT16(s, message->numRects); /* numRects (2 bytes) */ if (message->numRects < 1) { /* Unfortunately, it isn't documented. It seems that server asks to clip whole session when numRects = 0. Issue: https://github.com/FreeRDP/FreeRDP/issues/1738 */ WLog_ERR(TAG, "no rects. Clip whole session."); if (!(message->rects = (RFX_RECT*) malloc(sizeof(RFX_RECT)))) return FALSE; message->numRects = 1; message->rects->x = 0; message->rects->y = 0; message->rects->width = context->width; message->rects->height = context->height; return TRUE; } if (Stream_GetRemainingLength(s) < (size_t) (8 * message->numRects)) { WLog_ERR(TAG, "%s: packet too small for num_rects=%d", __FUNCTION__, message->numRects); return FALSE; } if (!(message->rects = (RFX_RECT*) calloc(message->numRects, sizeof(RFX_RECT)))) return FALSE; /* rects */ for (i = 0; i < message->numRects; i++) { /* RFX_RECT */ Stream_Read_UINT16(s, message->rects[i].x); /* x (2 bytes) */ Stream_Read_UINT16(s, message->rects[i].y); /* y (2 bytes) */ Stream_Read_UINT16(s, message->rects[i].width); /* width (2 bytes) */ Stream_Read_UINT16(s, message->rects[i].height); /* height (2 bytes) */ WLog_Print(context->priv->log, WLOG_DEBUG, "rect %d (x,y=%d,%d w,h=%d %d).", i, message->rects[i].x, message->rects[i].y, message->rects[i].width, message->rects[i].height); } if (Stream_GetRemainingLength(s) < 4) { WLog_ERR(TAG, "%s: packet too small (regionType/numTileSets)", __FUNCTION__); return FALSE; } Stream_Read_UINT16(s, regionType); /* regionType (2 bytes): MUST be set to CBT_REGION (0xCAC1) */ Stream_Read_UINT16(s, numTileSets); /* numTilesets (2 bytes): MUST be set to 0x0001. */ if (regionType != CBT_REGION) { WLog_ERR(TAG, "%s: invalid region type 0x%04X", __FUNCTION__, regionType); return TRUE; } if (numTileSets != 0x0001) { WLog_ERR(TAG, "%s: invalid number of tilesets (%u)", __FUNCTION__, numTileSets); return FALSE; } return TRUE; }
BOOL gcc_read_client_core_data(wStream* s, rdpMcs* mcs, UINT16 blockLength) { char* str = NULL; UINT32 version; BYTE connectionType = 0; UINT32 clientColorDepth; UINT16 colorDepth = 0; UINT16 postBeta2ColorDepth = 0; UINT16 highColorDepth = 0; UINT16 supportedColorDepths = 0; UINT32 serverSelectedProtocol = 0; UINT32 desktopPhysicalWidth = 0; UINT32 desktopPhysicalHeight = 0; UINT16 desktopOrientation = 0; UINT32 desktopScaleFactor = 0; UINT32 deviceScaleFactor = 0; UINT16 earlyCapabilityFlags = 0; rdpSettings* settings = mcs->settings; /* Length of all required fields, until imeFileName */ if (blockLength < 128) return FALSE; Stream_Read_UINT32(s, version); /* version (4 bytes) */ settings->RdpVersion = (version == RDP_VERSION_4 ? 4 : 7); Stream_Read_UINT16(s, settings->DesktopWidth); /* DesktopWidth (2 bytes) */ Stream_Read_UINT16(s, settings->DesktopHeight); /* DesktopHeight (2 bytes) */ Stream_Read_UINT16(s, colorDepth); /* ColorDepth (2 bytes) */ Stream_Seek_UINT16(s); /* SASSequence (Secure Access Sequence) (2 bytes) */ Stream_Read_UINT32(s, settings->KeyboardLayout); /* KeyboardLayout (4 bytes) */ Stream_Read_UINT32(s, settings->ClientBuild); /* ClientBuild (4 bytes) */ /* clientName (32 bytes, null-terminated unicode, truncated to 15 characters) */ ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(s), 32 / 2, &str, 0, NULL, NULL); Stream_Seek(s, 32); sprintf_s(settings->ClientHostname, 31, "%s", str); settings->ClientHostname[31] = 0; free(str); str = NULL; Stream_Read_UINT32(s, settings->KeyboardType); /* KeyboardType (4 bytes) */ Stream_Read_UINT32(s, settings->KeyboardSubType); /* KeyboardSubType (4 bytes) */ Stream_Read_UINT32(s, settings->KeyboardFunctionKey); /* KeyboardFunctionKey (4 bytes) */ WLog_DBG(TAG, "KeyboardLayout=%x, KeyboardType=%x, KeyboardSubType=%x, KeyboardFunctionKey=%x", settings->KeyboardLayout, settings->KeyboardType, settings->KeyboardSubType, settings->KeyboardFunctionKey); Stream_Seek(s, 64); /* imeFileName (64 bytes) */ blockLength -= 128; /** * The following fields are all optional. If one field is present, all of the preceding * fields MUST also be present. If one field is not present, all of the subsequent fields * MUST NOT be present. * We must check the bytes left before reading each field. */ do { if (blockLength < 2) break; Stream_Read_UINT16(s, postBeta2ColorDepth); /* postBeta2ColorDepth (2 bytes) */ blockLength -= 2; if (blockLength < 2) break; Stream_Seek_UINT16(s); /* clientProductID (2 bytes) */ blockLength -= 2; if (blockLength < 4) break; Stream_Seek_UINT32(s); /* serialNumber (4 bytes) */ blockLength -= 4; if (blockLength < 2) break; Stream_Read_UINT16(s, highColorDepth); /* highColorDepth (2 bytes) */ blockLength -= 2; if (blockLength < 2) break; Stream_Read_UINT16(s, supportedColorDepths); /* supportedColorDepths (2 bytes) */ blockLength -= 2; if (blockLength < 2) break; Stream_Read_UINT16(s, earlyCapabilityFlags); /* earlyCapabilityFlags (2 bytes) */ settings->EarlyCapabilityFlags = (UINT32) earlyCapabilityFlags; blockLength -= 2; if (blockLength < 64) break; ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(s), 64 / 2, &str, 0, NULL, NULL); Stream_Seek(s, 64); /* clientDigProductId (64 bytes) */ sprintf_s(settings->ClientProductId, 32, "%s", str); free(str); blockLength -= 64; if (blockLength < 1) break; Stream_Read_UINT8(s, connectionType); /* connectionType (1 byte) */ blockLength -= 1; if (blockLength < 1) break; Stream_Seek_UINT8(s); /* pad1octet (1 byte) */ blockLength -= 1; if (blockLength < 4) break; Stream_Read_UINT32(s, serverSelectedProtocol); /* serverSelectedProtocol (4 bytes) */ blockLength -= 4; if (blockLength < 4) break; Stream_Read_UINT32(s, desktopPhysicalWidth); /* desktopPhysicalWidth (4 bytes) */ blockLength -= 4; if (blockLength < 4) break; Stream_Read_UINT32(s, desktopPhysicalHeight); /* desktopPhysicalHeight (4 bytes) */ blockLength -= 4; if (blockLength < 2) break; Stream_Read_UINT16(s, desktopOrientation); /* desktopOrientation (2 bytes) */ blockLength -= 2; if (blockLength < 4) break; Stream_Read_UINT32(s, desktopScaleFactor); /* desktopScaleFactor (4 bytes) */ blockLength -= 4; if (blockLength < 4) break; Stream_Read_UINT32(s, deviceScaleFactor); /* deviceScaleFactor (4 bytes) */ blockLength -= 4; if (settings->SelectedProtocol != serverSelectedProtocol) return FALSE; } while (0); if (highColorDepth > 0) { if (earlyCapabilityFlags & RNS_UD_CS_WANT_32BPP_SESSION) clientColorDepth = 32; else clientColorDepth = highColorDepth; } else if (postBeta2ColorDepth > 0) { switch (postBeta2ColorDepth) { case RNS_UD_COLOR_4BPP: clientColorDepth = 4; break; case RNS_UD_COLOR_8BPP: clientColorDepth = 8; break; case RNS_UD_COLOR_16BPP_555: clientColorDepth = 15; break; case RNS_UD_COLOR_16BPP_565: clientColorDepth = 16; break; case RNS_UD_COLOR_24BPP: clientColorDepth = 24; break; default: return FALSE; } } else { switch (colorDepth) { case RNS_UD_COLOR_4BPP: clientColorDepth = 4; break; case RNS_UD_COLOR_8BPP: clientColorDepth = 8; break; default: return FALSE; } } /* * If we are in server mode, accept client's color depth only if * it is smaller than ours. This is what Windows server does. */ if ((clientColorDepth < settings->ColorDepth) || !settings->ServerMode) settings->ColorDepth = clientColorDepth; if (settings->NetworkAutoDetect) settings->NetworkAutoDetect = (earlyCapabilityFlags & RNS_UD_CS_SUPPORT_NETWORK_AUTODETECT) ? TRUE : FALSE; if (settings->SupportHeartbeatPdu) settings->SupportHeartbeatPdu = (earlyCapabilityFlags & RNS_UD_CS_SUPPORT_HEARTBEAT_PDU) ? TRUE : FALSE; if (settings->SupportGraphicsPipeline) settings->SupportGraphicsPipeline = (earlyCapabilityFlags & RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL) ? TRUE : FALSE; if (settings->SupportDynamicTimeZone) settings->SupportDynamicTimeZone = (earlyCapabilityFlags & RNS_UD_CS_SUPPORT_DYNAMIC_TIME_ZONE) ? TRUE : FALSE; if (!(earlyCapabilityFlags & RNS_UD_CS_VALID_CONNECTION_TYPE)) connectionType = 0; settings->SupportErrorInfoPdu = earlyCapabilityFlags & RNS_UD_CS_SUPPORT_ERRINFO_PDU; settings->ConnectionType = connectionType; return TRUE; }
BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UINT32 Length, wStream* input) { char* s = NULL; mode_t m; UINT64 size; int status; char* fullpath; struct STAT st; struct timeval tv[2]; UINT64 LastWriteTime; UINT32 FileAttributes; UINT32 FileNameLength; m = 0; switch (FsInformationClass) { case FileBasicInformation: /* http://msdn.microsoft.com/en-us/library/cc232094.aspx */ Stream_Seek_UINT64(input); /* CreationTime */ Stream_Seek_UINT64(input); /* LastAccessTime */ Stream_Read_UINT64(input, LastWriteTime); Stream_Seek_UINT64(input); /* ChangeTime */ Stream_Read_UINT32(input, FileAttributes); if (FSTAT(file->fd, &st) != 0) return FALSE; tv[0].tv_sec = st.st_atime; tv[0].tv_usec = 0; tv[1].tv_sec = (LastWriteTime > 0 ? FILE_TIME_RDP_TO_SYSTEM(LastWriteTime) : st.st_mtime); tv[1].tv_usec = 0; #ifndef WIN32 /* TODO on win32 */ #ifdef ANDROID utimes(file->fullpath, tv); #else futimes(file->fd, tv); #endif if (FileAttributes > 0) { m = st.st_mode; if ((FileAttributes & FILE_ATTRIBUTE_READONLY) == 0) m |= S_IWUSR; else m &= ~S_IWUSR; if (m != st.st_mode) fchmod(file->fd, m); } #endif break; case FileEndOfFileInformation: /* http://msdn.microsoft.com/en-us/library/cc232067.aspx */ case FileAllocationInformation: /* http://msdn.microsoft.com/en-us/library/cc232076.aspx */ Stream_Read_UINT64(input, size); if (ftruncate(file->fd, size) != 0) return FALSE; break; case FileDispositionInformation: /* http://msdn.microsoft.com/en-us/library/cc232098.aspx */ /* http://msdn.microsoft.com/en-us/library/cc241371.aspx */ if (Length) Stream_Read_UINT8(input, file->delete_pending); else file->delete_pending = 1; if (file->delete_pending && file->is_dir) { /* mstsc causes this to FAIL if the directory is not empty, * and that's what the server is expecting. If we wait for * the close to flag a failure, cut and paste of a folder * will lose the folder's contents. */ int status; status = rmdir(file->fullpath); if (status == 0) { /* Put it back so the normal pending delete will work. */ mkdir(file->fullpath, 0755); } else { return FALSE; } } break; case FileRenameInformation: /* http://msdn.microsoft.com/en-us/library/cc232085.aspx */ Stream_Seek_UINT8(input); /* ReplaceIfExists */ Stream_Seek_UINT8(input); /* RootDirectory */ Stream_Read_UINT32(input, FileNameLength); status = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(input), FileNameLength / 2, &s, 0, NULL, NULL); if (status < 1) s = (char*) calloc(1, 1); fullpath = drive_file_combine_fullpath(file->basepath, s); free(s); /* TODO rename does not work on win32 */ if (rename(file->fullpath, fullpath) == 0) { DEBUG_SVC("renamed %s to %s", file->fullpath, fullpath); drive_file_set_fullpath(file, fullpath); } else { DEBUG_WARN("rename %s to %s failed, errno = %d", file->fullpath, fullpath, errno); free(fullpath); return FALSE; } break; default: DEBUG_WARN("invalid FsInformationClass %d", FsInformationClass); return FALSE; } return TRUE; }
static BOOL rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s) { int i, num_known_format = 0; UINT32 flags, vol, pitch; UINT16 udpPort, version; BYTE lastblock; if (Stream_GetRemainingLength(s) < 20) return FALSE; Stream_Read_UINT32(s, flags); /* dwFlags */ Stream_Read_UINT32(s, vol); /* dwVolume */ Stream_Read_UINT32(s, pitch); /* dwPitch */ Stream_Read_UINT16(s, udpPort); /* wDGramPort */ Stream_Read_UINT16(s, context->num_client_formats); /* wNumberOfFormats */ Stream_Read_UINT8(s, lastblock); /* cLastBlockConfirmed */ Stream_Read_UINT16(s, version); /* wVersion */ Stream_Seek_UINT8(s); /* bPad */ /* this check is only a guess as cbSize can influence the size of a format record */ if (Stream_GetRemainingLength(s) < context->num_client_formats * 18) return FALSE; if (!context->num_client_formats) { fprintf(stderr, "%s: client doesn't support any format!\n", __FUNCTION__); return FALSE; } context->client_formats = (AUDIO_FORMAT *)calloc(context->num_client_formats, sizeof(AUDIO_FORMAT)); if (!context->client_formats) return FALSE; for (i = 0; i < context->num_client_formats; i++) { if (Stream_GetRemainingLength(s) < 18) goto out_free; Stream_Read_UINT16(s, context->client_formats[i].wFormatTag); Stream_Read_UINT16(s, context->client_formats[i].nChannels); Stream_Read_UINT32(s, context->client_formats[i].nSamplesPerSec); Stream_Read_UINT32(s, context->client_formats[i].nAvgBytesPerSec); Stream_Read_UINT16(s, context->client_formats[i].nBlockAlign); Stream_Read_UINT16(s, context->client_formats[i].wBitsPerSample); Stream_Read_UINT16(s, context->client_formats[i].cbSize); if (context->client_formats[i].cbSize > 0) { if (!Stream_SafeSeek(s, context->client_formats[i].cbSize)) goto out_free; } if (context->client_formats[i].wFormatTag != 0) { //lets call this a known format //TODO: actually look through our own list of known formats num_known_format++; } } if (!context->num_client_formats) { fprintf(stderr, "%s: client doesn't support any known format!\n", __FUNCTION__); goto out_free; } return TRUE; out_free: free(context->client_formats); return FALSE; }
static void* rdpsnd_server_thread(void* arg) { wStream* s; DWORD status; DWORD nCount; void* buffer; BYTE msgType; UINT16 BodySize; HANDLE events[8]; HANDLE ChannelEvent; DWORD BytesReturned; RdpsndServerContext* context; BOOL doRun; context = (RdpsndServerContext *)arg; buffer = NULL; BytesReturned = 0; ChannelEvent = NULL; s = Stream_New(NULL, 4096); if (!s) return NULL; if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle, &buffer, &BytesReturned)) { if (BytesReturned == sizeof(HANDLE)) CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE)); WTSFreeMemory(buffer); } nCount = 0; if (ChannelEvent) events[nCount++] = ChannelEvent; events[nCount++] = context->priv->StopEvent; if (!rdpsnd_server_send_formats(context, s)) goto out; doRun = TRUE; while (doRun) { status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); if (WaitForSingleObject(context->priv->StopEvent, 0) == WAIT_OBJECT_0) break; Stream_SetPosition(s, 0); if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0, (PCHAR)Stream_Buffer(s), Stream_Capacity(s), &BytesReturned)) { if (!BytesReturned) break; Stream_EnsureRemainingCapacity(s, BytesReturned); if (!WTSVirtualChannelRead(context->priv->ChannelHandle, 0, (PCHAR)Stream_Buffer(s), Stream_Capacity(s), &BytesReturned)) break; } if (Stream_GetRemainingLength(s) < 4) break; Stream_Read_UINT8(s, msgType); Stream_Seek_UINT8(s); /* bPad */ Stream_Read_UINT16(s, BodySize); if (Stream_GetRemainingLength(s) < BodySize) break; switch (msgType) { case SNDC_WAVECONFIRM: doRun = rdpsnd_server_recv_waveconfirm(context, s); break; case SNDC_QUALITYMODE: doRun = rdpsnd_server_recv_quality_mode(context, s); break; case SNDC_FORMATS: doRun = rdpsnd_server_recv_formats(context, s); if (doRun) { IFCALL(context->Activated, context); } break; default: fprintf(stderr, "%s: UNKOWN MESSAGE TYPE!! (%#0X)\n\n", __FUNCTION__, msgType); break; } } out: Stream_Free(s, TRUE); return NULL; }
static BOOL rfx_process_message_tileset(RFX_CONTEXT* context, RFX_MESSAGE* message, wStream* s) { int i; int pos; BYTE quant; UINT32* quants; UINT16 subtype; UINT32 blockLen; UINT32 blockType; UINT32 tilesDataSize; PTP_WORK* work_objects = NULL; RFX_TILE_WORK_PARAM* params = NULL; if (Stream_GetRemainingLength(s) < 14) { DEBUG_WARN("RfxMessageTileSet packet too small"); return FALSE; } Stream_Read_UINT16(s, subtype); /* subtype (2 bytes) must be set to CBT_TILESET (0xCAC2) */ if (subtype != CBT_TILESET) { DEBUG_WARN("invalid subtype, expected CBT_TILESET."); return FALSE; } Stream_Seek_UINT16(s); /* idx (2 bytes), must be set to 0x0000 */ Stream_Seek_UINT16(s); /* properties (2 bytes) */ Stream_Read_UINT8(s, context->num_quants); /* numQuant (1 byte) */ Stream_Seek_UINT8(s); /* tileSize (1 byte), must be set to 0x40 */ if (context->num_quants < 1) { DEBUG_WARN("no quantization value."); return TRUE; } Stream_Read_UINT16(s, message->num_tiles); /* numTiles (2 bytes) */ if (message->num_tiles < 1) { DEBUG_WARN("no tiles."); return TRUE; } Stream_Read_UINT32(s, tilesDataSize); /* tilesDataSize (4 bytes) */ if (context->quants != NULL) context->quants = (UINT32*) realloc((void*) context->quants, context->num_quants * 10 * sizeof(UINT32)); else context->quants = (UINT32*) malloc(context->num_quants * 10 * sizeof(UINT32)); quants = context->quants; /* quantVals */ if (Stream_GetRemainingLength(s) < context->num_quants * 5) { DEBUG_WARN("RfxMessageTileSet packet too small for num_quants=%d", context->num_quants); return FALSE; } for (i = 0; i < context->num_quants; i++) { /* RFX_CODEC_QUANT */ Stream_Read_UINT8(s, quant); *quants++ = (quant & 0x0F); *quants++ = (quant >> 4); Stream_Read_UINT8(s, quant); *quants++ = (quant & 0x0F); *quants++ = (quant >> 4); Stream_Read_UINT8(s, quant); *quants++ = (quant & 0x0F); *quants++ = (quant >> 4); Stream_Read_UINT8(s, quant); *quants++ = (quant & 0x0F); *quants++ = (quant >> 4); Stream_Read_UINT8(s, quant); *quants++ = (quant & 0x0F); *quants++ = (quant >> 4); DEBUG_RFX("quant %d (%d %d %d %d %d %d %d %d %d %d).", i, context->quants[i * 10], context->quants[i * 10 + 1], context->quants[i * 10 + 2], context->quants[i * 10 + 3], context->quants[i * 10 + 4], context->quants[i * 10 + 5], context->quants[i * 10 + 6], context->quants[i * 10 + 7], context->quants[i * 10 + 8], context->quants[i * 10 + 9]); } message->tiles = (RFX_TILE**) malloc(sizeof(RFX_TILE*) * message->num_tiles); ZeroMemory(message->tiles, sizeof(RFX_TILE*) * message->num_tiles); if (context->priv->UseThreads) { work_objects = (PTP_WORK*) malloc(sizeof(PTP_WORK) * message->num_tiles); params = (RFX_TILE_WORK_PARAM*) malloc(sizeof(RFX_TILE_WORK_PARAM) * message->num_tiles); } /* tiles */ for (i = 0; i < message->num_tiles; i++) { /* RFX_TILE */ if (Stream_GetRemainingLength(s) < 6) { DEBUG_WARN("RfxMessageTileSet packet too small to read tile %d/%d", i, message->num_tiles); return FALSE; } Stream_Read_UINT16(s, blockType); /* blockType (2 bytes), must be set to CBT_TILE (0xCAC3) */ Stream_Read_UINT32(s, blockLen); /* blockLen (4 bytes) */ if (Stream_GetRemainingLength(s) < blockLen - 6) { DEBUG_WARN("RfxMessageTileSet not enough bytes to read tile %d/%d with blocklen=%d", i, message->num_tiles, blockLen); return FALSE; } pos = Stream_GetPosition(s) - 6 + blockLen; if (blockType != CBT_TILE) { DEBUG_WARN("unknown block type 0x%X, expected CBT_TILE (0xCAC3).", blockType); break; } message->tiles[i] = rfx_tile_pool_take(context); if (context->priv->UseThreads) { params[i].context = context; params[i].tile = message->tiles[i]; CopyMemory(&(params[i].s), s, sizeof(wStream)); work_objects[i] = CreateThreadpoolWork((PTP_WORK_CALLBACK) rfx_process_message_tile_work_callback, (void*) ¶ms[i], &context->priv->ThreadPoolEnv); SubmitThreadpoolWork(work_objects[i]); } else { rfx_process_message_tile(context, message->tiles[i], s); } Stream_SetPosition(s, pos); } if (context->priv->UseThreads) { for (i = 0; i < message->num_tiles; i++) { WaitForThreadpoolWorkCallbacks(work_objects[i], FALSE); CloseThreadpoolWork(work_objects[i]); } free(work_objects); free(params); } return TRUE; }