Files
xrdp/xrdp/xrdp_egfx.c
T
metalefty 7738d111d5 Merge commit from fork
CVE-2026-35512: Heap overflow in dynvc processing
2026-04-14 17:00:20 +09:00

1177 lines
36 KiB
C

/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2019
*
* 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.
*
* MS-RDPEGFX
*/
/**
*
* @file xrdp_egfx.c
* @brief Stream functions for the EGFX extension to the MSRDP protocol.
* @author Jay Sorg, Christopher Pitstick
*
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "arch.h"
#include "os_calls.h"
#include "parse.h"
#include "xrdp_egfx.h"
#include "libxrdp.h"
#include "xrdp_channel.h"
#include "xrdp_mm.h"
#include <limits.h>
#define MAX_PART_SIZE 0xFFFF
#define PACKET_COMPR_TYPE_RDP8 0x04 /* MS-RDPEGFX 2.2.5.3 */
/******************************************************************************/
int
xrdp_egfx_send_data(struct xrdp_egfx *egfx, const char *data, int bytes)
{
int error;
int to_send;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_send_data:");
if (bytes <= 1500)
{
error = libxrdp_drdynvc_data(egfx->session, egfx->channel_id,
data, bytes);
}
else
{
error = libxrdp_drdynvc_data_first(egfx->session, egfx->channel_id,
data, 1500, bytes);
data += 1500;
bytes -= 1500;
while ((bytes > 0) && (error == 0))
{
to_send = bytes;
if (to_send > 1500)
{
to_send = 1500;
}
error = libxrdp_drdynvc_data(egfx->session, egfx->channel_id,
data, to_send);
data += to_send;
bytes -= to_send;
}
}
return error;
}
/******************************************************************************/
int
xrdp_egfx_send_s(struct xrdp_egfx *egfx, struct stream *s)
{
int error;
int bytes;
if (s == NULL)
{
return 1;
}
bytes = (int) (s->end - s->data);
error = xrdp_egfx_send_data(egfx, s->data, bytes);
return error;
}
/******************************************************************************/
struct stream *
xrdp_egfx_create_surface(struct xrdp_egfx_bulk *bulk, int surface_id,
int width, int height, int pixel_format)
{
int bytes;
struct stream *s;
LOG_DEVEL(LOG_LEVEL_TRACE, "xrdp_egfx_create_surface:");
make_stream(s);
init_stream(s, 8192);
/* RDP_SEGMENTED_DATA */
out_uint8(s, 0xE0); /* descriptor = SINGLE */
/* RDP8_BULK_ENCODED_DATA */
out_uint8(s, PACKET_COMPR_TYPE_RDP8); /* header */
/* RDPGFX_HEADER */
out_uint16_le(s, XR_RDPGFX_CMDID_CREATESURFACE); /* cmdId */
out_uint16_le(s, 0); /* flags = 0 */
s_push_layer(s, iso_hdr, 4); /* pduLength, set later */
out_uint16_le(s, surface_id);
out_uint16_le(s, width);
out_uint16_le(s, height);
out_uint8(s, pixel_format);
s_mark_end(s);
bytes = (int) ((s->end - s->iso_hdr) + 4);
s_pop_layer(s, iso_hdr);
out_uint32_le(s, bytes);
return s;
}
/******************************************************************************/
int
xrdp_egfx_send_create_surface(struct xrdp_egfx *egfx, int surface_id,
int width, int height, int pixel_format)
{
int error;
struct stream *s;
LOG_DEVEL(LOG_LEVEL_TRACE, "xrdp_egfx_send_create_surface:");
s = xrdp_egfx_create_surface(egfx->bulk, surface_id, width, height,
pixel_format);
error = xrdp_egfx_send_s(egfx, s);
LOG_DEVEL(LOG_LEVEL_DEBUG, "xrdp_egfx_send_create_surface: xrdp_egfx_send_s "
"error %d", error);
free_stream(s);
return error;
}
/******************************************************************************/
struct stream *
xrdp_egfx_delete_surface(struct xrdp_egfx_bulk *bulk, int surface_id)
{
int bytes;
struct stream *s;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_delete_surface:");
make_stream(s);
init_stream(s, 8192);
/* RDP_SEGMENTED_DATA */
out_uint8(s, 0xE0); /* descriptor = SINGLE */
/* RDP8_BULK_ENCODED_DATA */
out_uint8(s, PACKET_COMPR_TYPE_RDP8); /* header */
/* RDPGFX_HEADER */
out_uint16_le(s, XR_RDPGFX_CMDID_DELETESURFACE); /* cmdId */
out_uint16_le(s, 0); /* flags = 0 */
s_push_layer(s, iso_hdr, 4); /* pduLength, set later */
out_uint16_le(s, surface_id);
s_mark_end(s);
bytes = (int) ((s->end - s->iso_hdr) + 4);
s_pop_layer(s, iso_hdr);
out_uint32_le(s, bytes);
return s;
}
/******************************************************************************/
int
xrdp_egfx_send_delete_surface(struct xrdp_egfx *egfx, int surface_id)
{
int error;
struct stream *s;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_send_delete_surface:");
s = xrdp_egfx_delete_surface(egfx->bulk, surface_id);
error = xrdp_egfx_send_s(egfx, s);
LOG_DEVEL(LOG_LEVEL_DEBUG, "xrdp_egfx_send_delete_surface: xrdp_egfx_send_s "
"error %d", error);
free_stream(s);
return error;
}
/******************************************************************************/
struct stream *
xrdp_egfx_map_surface(struct xrdp_egfx_bulk *bulk, int surface_id,
int x, int y)
{
int bytes;
struct stream *s;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_map_surface:");
make_stream(s);
init_stream(s, 8192);
/* RDP_SEGMENTED_DATA */
out_uint8(s, 0xE0); /* descriptor = SINGLE */
/* RDP8_BULK_ENCODED_DATA */
out_uint8(s, PACKET_COMPR_TYPE_RDP8); /* header */
/* RDPGFX_HEADER */
out_uint16_le(s, XR_RDPGFX_CMDID_MAPSURFACETOOUTPUT); /* cmdId */
out_uint16_le(s, 0); /* flags = 0 */
s_push_layer(s, iso_hdr, 4); /* pduLength, set later */
out_uint16_le(s, surface_id);
out_uint16_le(s, 0);
out_uint32_le(s, x);
out_uint32_le(s, y);
s_mark_end(s);
bytes = (int) ((s->end - s->iso_hdr) + 4);
s_pop_layer(s, iso_hdr);
out_uint32_le(s, bytes);
return s;
}
/******************************************************************************/
int
xrdp_egfx_send_map_surface(struct xrdp_egfx *egfx, int surface_id,
int x, int y)
{
int error;
struct stream *s;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_send_map_surface:");
s = xrdp_egfx_map_surface(egfx->bulk, surface_id, x, y);
error = xrdp_egfx_send_s(egfx, s);
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_send_map_surface: xrdp_egfx_send_s "
"error %d", error);
free_stream(s);
return error;
}
/******************************************************************************/
struct stream *
xrdp_egfx_fill_surface(struct xrdp_egfx_bulk *bulk, int surface_id,
int fill_color, int num_rects,
const struct xrdp_egfx_rect *rects)
{
int bytes;
int index;
struct stream *s;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_fill_surface:");
make_stream(s);
init_stream(s, 1024 + num_rects * 8);
/* RDP_SEGMENTED_DATA */
out_uint8(s, 0xE0); /* descriptor = SINGLE */
/* RDP8_BULK_ENCODED_DATA */
out_uint8(s, PACKET_COMPR_TYPE_RDP8); /* header */
/* RDPGFX_HEADER */
out_uint16_le(s, XR_RDPGFX_CMDID_SOLIDFILL); /* cmdId */
out_uint16_le(s, 0); /* flags = 0 */
s_push_layer(s, iso_hdr, 4); /* pduLength, set later */
out_uint16_le(s, surface_id);
out_uint32_le(s, fill_color);
out_uint16_le(s, num_rects);
for (index = 0; index < num_rects; index++)
{
out_uint16_le(s, rects[index].x1);
out_uint16_le(s, rects[index].y1);
out_uint16_le(s, rects[index].x2);
out_uint16_le(s, rects[index].y2);
}
s_mark_end(s);
bytes = (int) ((s->end - s->iso_hdr) + 4);
s_pop_layer(s, iso_hdr);
out_uint32_le(s, bytes);
return s;
}
/******************************************************************************/
int
xrdp_egfx_send_fill_surface(struct xrdp_egfx *egfx, int surface_id,
int fill_color, int num_rects,
const struct xrdp_egfx_rect *rects)
{
int error;
struct stream *s;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_send_fill_surface:");
s = xrdp_egfx_fill_surface(egfx->bulk, surface_id, fill_color,
num_rects, rects);
error = xrdp_egfx_send_s(egfx, s);
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_send_fill_surface: xrdp_egfx_send_s "
"error %d", error);
free_stream(s);
return error;
}
/******************************************************************************/
struct stream *
xrdp_egfx_surface_to_surface(struct xrdp_egfx_bulk *bulk, int src_surface_id,
int dst_surface_id,
const struct xrdp_egfx_rect *src_rect,
int num_dst_points,
const struct xrdp_egfx_point *dst_points)
{
int bytes;
int index;
struct stream *s;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_surface_to_surface:");
make_stream(s);
init_stream(s, 1024 + num_dst_points * 4);
/* RDP_SEGMENTED_DATA */
out_uint8(s, 0xE0); /* descriptor = SINGLE */
/* RDP8_BULK_ENCODED_DATA */
out_uint8(s, PACKET_COMPR_TYPE_RDP8); /* header */
/* RDPGFX_HEADER */
out_uint16_le(s, XR_RDPGFX_CMDID_SURFACETOSURFACE); /* cmdId */
out_uint16_le(s, 0); /* flags = 0 */
s_push_layer(s, iso_hdr, 4); /* pduLength, set later */
out_uint16_le(s, src_surface_id);
out_uint16_le(s, dst_surface_id);
out_uint16_le(s, src_rect->x1);
out_uint16_le(s, src_rect->y1);
out_uint16_le(s, src_rect->x2);
out_uint16_le(s, src_rect->y2);
out_uint16_le(s, num_dst_points);
for (index = 0; index < num_dst_points; index++)
{
out_uint16_le(s, dst_points[index].x);
out_uint16_le(s, dst_points[index].y);
}
s_mark_end(s);
bytes = (int) ((s->end - s->iso_hdr) + 4);
s_pop_layer(s, iso_hdr);
out_uint32_le(s, bytes);
return s;
}
/******************************************************************************/
int
xrdp_egfx_send_surface_to_surface(struct xrdp_egfx *egfx, int src_surface_id,
int dst_surface_id,
const struct xrdp_egfx_rect *src_rect,
int num_dst_points,
const struct xrdp_egfx_point *dst_points)
{
int error;
struct stream *s;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_send_surface_to_surface:");
s = xrdp_egfx_surface_to_surface(egfx->bulk, src_surface_id,
dst_surface_id, src_rect,
num_dst_points, dst_points);
error = xrdp_egfx_send_s(egfx, s);
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_send_surface_to_surface: xrdp_egfx_send_s "
"error %d", error);
free_stream(s);
return error;
}
/******************************************************************************/
struct stream *
xrdp_egfx_frame_start(struct xrdp_egfx_bulk *bulk, int frame_id, int timestamp)
{
int bytes;
struct stream *s;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_frame_start:");
make_stream(s);
init_stream(s, 8192);
/* RDP_SEGMENTED_DATA */
out_uint8(s, 0xE0); /* descriptor = SINGLE */
/* RDP8_BULK_ENCODED_DATA */
out_uint8(s, PACKET_COMPR_TYPE_RDP8); /* header */
/* RDPGFX_HEADER */
out_uint16_le(s, XR_RDPGFX_CMDID_STARTFRAME); /* cmdId */
out_uint16_le(s, 0); /* flags = 0 */
s_push_layer(s, iso_hdr, 4); /* pduLength, set later */
out_uint32_le(s, timestamp);
out_uint32_le(s, frame_id);
s_mark_end(s);
bytes = (int) ((s->end - s->iso_hdr) + 4);
s_pop_layer(s, iso_hdr);
out_uint32_le(s, bytes);
return s;
}
/******************************************************************************/
int
xrdp_egfx_send_frame_start(struct xrdp_egfx *egfx, int frame_id, int timestamp)
{
int error;
struct stream *s;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_send_frame_start:");
s = xrdp_egfx_frame_start(egfx->bulk, frame_id, timestamp);
error = xrdp_egfx_send_s(egfx, s);
if (error != 0)
{
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_send_frame_start: xrdp_egfx_send_s "
"error %d", error);
}
free_stream(s);
return error;
}
/******************************************************************************/
struct stream *
xrdp_egfx_frame_end(struct xrdp_egfx_bulk *bulk, int frame_id)
{
int bytes;
struct stream *s;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_frame_end:");
make_stream(s);
init_stream(s, 8192);
/* RDP_SEGMENTED_DATA */
out_uint8(s, 0xE0); /* descriptor = SINGLE */
/* RDP8_BULK_ENCODED_DATA */
out_uint8(s, PACKET_COMPR_TYPE_RDP8); /* header */
/* RDPGFX_HEADER */
out_uint16_le(s, XR_RDPGFX_CMDID_ENDFRAME); /* cmdId */
out_uint16_le(s, 0); /* flags = 0 */
s_push_layer(s, iso_hdr, 4); /* pduLength, set later */
out_uint32_le(s, frame_id);
s_mark_end(s);
bytes = (int) ((s->end - s->iso_hdr) + 4);
s_pop_layer(s, iso_hdr);
out_uint32_le(s, bytes);
return s;
}
/******************************************************************************/
int
xrdp_egfx_send_frame_end(struct xrdp_egfx *egfx, int frame_id)
{
int error;
struct stream *s;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_send_frame_end:");
s = xrdp_egfx_frame_end(egfx->bulk, frame_id);
error = xrdp_egfx_send_s(egfx, s);
if (error != 0)
{
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_send_frame_end: xrdp_egfx_send_s "
"error %d", error);
}
free_stream(s);
return error;
}
/******************************************************************************/
struct stream *
xrdp_egfx_capsconfirm(struct xrdp_egfx_bulk *bulk, int version, int flags)
{
int bytes;
struct stream *s;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_capsconfirm:");
make_stream(s);
init_stream(s, 8192);
/* RDP_SEGMENTED_DATA */
out_uint8(s, 0xE0); /* descriptor = SINGLE */
/* RDP8_BULK_ENCODED_DATA */
out_uint8(s, PACKET_COMPR_TYPE_RDP8); /* header */
/* RDPGFX_HEADER */
out_uint16_le(s, XR_RDPGFX_CMDID_CAPSCONFIRM); /* cmdId */
out_uint16_le(s, 0); /* flags = 0 */
s_push_layer(s, iso_hdr, 4); /* pduLength, set later */
out_uint32_le(s, version); /* version */
out_uint32_le(s, 4); /* capsDataLength */
out_uint32_le(s, flags);
s_mark_end(s);
bytes = (int) ((s->end - s->iso_hdr) + 4);
s_pop_layer(s, iso_hdr);
out_uint32_le(s, bytes);
return s;
}
/******************************************************************************/
int
xrdp_egfx_send_capsconfirm(struct xrdp_egfx *egfx, int version, int flags)
{
int error;
struct stream *s;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_send_capsconfirm:");
s = xrdp_egfx_capsconfirm(egfx->bulk, version, flags);
error = xrdp_egfx_send_s(egfx, s);
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_send_capsconfirm: xrdp_egfx_send_s "
"error %d", error);
free_stream(s);
return error;
}
/******************************************************************************/
struct stream *
xrdp_egfx_wire_to_surface1(struct xrdp_egfx_bulk *bulk, int surface_id,
int codec_id, int pixel_format,
struct xrdp_egfx_rect *dest_rect,
void *bitmap_data, int bitmap_data_length)
{
int bytes;
int index;
int segment_size;
int segment_count;
struct stream *s;
char *bitmap_data8;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_wire_to_surface1:");
make_stream(s);
bytes = bitmap_data_length + 8192;
bytes += 5 * (bitmap_data_length / MAX_PART_SIZE);
init_stream(s, bytes);
/* RDP_SEGMENTED_DATA */
out_uint8(s, 0xE1); /* descriptor = MULTIPART */
s_push_layer(s, iso_hdr, 2); /* segmentCount, set later */
out_uint32_le(s, 25 + bitmap_data_length); /* uncompressedSize */
/* RDP_DATA_SEGMENT */
out_uint32_le(s, 1 + 25); /* segmentArray size */
/* RDP8_BULK_ENCODED_DATA */
out_uint8(s, PACKET_COMPR_TYPE_RDP8); /* header */
/* RDPGFX_HEADER */
out_uint16_le(s, XR_RDPGFX_CMDID_WIRETOSURFACE_1); /* cmdId */
out_uint16_le(s, 0); /* flags = 0 */
out_uint32_le(s, 25 + bitmap_data_length); /* pduLength */
out_uint16_le(s, surface_id);
out_uint16_le(s, codec_id);
out_uint8(s, pixel_format);
out_uint16_le(s, dest_rect->x1);
out_uint16_le(s, dest_rect->y1);
out_uint16_le(s, dest_rect->x2);
out_uint16_le(s, dest_rect->y2);
out_uint32_le(s, bitmap_data_length);
segment_count = 1;
index = 0;
bitmap_data8 = (char *) bitmap_data;
while (index < bitmap_data_length)
{
segment_size = bitmap_data_length - index;
if (segment_size > MAX_PART_SIZE)
{
segment_size = MAX_PART_SIZE;
}
/* RDP_DATA_SEGMENT */
out_uint32_le(s, 1 + segment_size); /* segmentArray size */
/* RDP8_BULK_ENCODED_DATA */
out_uint8(s, PACKET_COMPR_TYPE_RDP8); /* header */
out_uint8a(s, bitmap_data8 + index, segment_size);
LOG_DEVEL(LOG_LEVEL_DEBUG, " segment index %d segment_size %d",
segment_count, segment_size);
index += segment_size;
segment_count++;
}
s_mark_end(s);
s_pop_layer(s, iso_hdr);
out_uint16_le(s, segment_count);
LOG_DEVEL(LOG_LEVEL_DEBUG, "xrdp_egfx_wire_to_surface1: segment_count %d",
segment_count);
return s;
}
/******************************************************************************/
int
xrdp_egfx_send_wire_to_surface1(struct xrdp_egfx *egfx, int surface_id,
int codec_id, int pixel_format,
struct xrdp_egfx_rect *dest_rect,
void *bitmap_data, int bitmap_data_length)
{
int error;
struct stream *s;
LOG_DEVEL(LOG_LEVEL_TRACE, "xrdp_egfx_send_wire_to_surface1:");
s = xrdp_egfx_wire_to_surface1(egfx->bulk, surface_id, codec_id,
pixel_format, dest_rect,
bitmap_data, bitmap_data_length);
error = xrdp_egfx_send_s(egfx, s);
LOG_DEVEL(LOG_LEVEL_DEBUG, "xrdp_egfx_send_wire_to_surface1: xrdp_egfx_send_s "
"error %d", error);
free_stream(s);
return error;
}
/******************************************************************************/
struct stream *
xrdp_egfx_wire_to_surface2(struct xrdp_egfx_bulk *bulk, int surface_id,
int codec_id, int codec_context_id,
int pixel_format,
void *bitmap_data, int bitmap_data_length)
{
int bytes;
int index;
int segment_size;
int segment_count;
struct stream *s;
char *bitmap_data8;
LOG_DEVEL(LOG_LEVEL_TRACE, "xrdp_egfx_wire_to_surface2:");
make_stream(s);
bytes = bitmap_data_length + 8192;
bytes += 5 * (bitmap_data_length / MAX_PART_SIZE);
init_stream(s, bytes);
/* RDP_SEGMENTED_DATA */
out_uint8(s, 0xE1); /* descriptor = MULTIPART */
s_push_layer(s, iso_hdr, 2); /* segmentCount, set later */
out_uint32_le(s, 21 + bitmap_data_length); /* uncompressedSize */
/* RDP_DATA_SEGMENT */
out_uint32_le(s, 1 + 21); /* segmentArray size */
/* RDP8_BULK_ENCODED_DATA */
out_uint8(s, PACKET_COMPR_TYPE_RDP8); /* header */
/* RDPGFX_HEADER */
out_uint16_le(s, XR_RDPGFX_CMDID_WIRETOSURFACE_2); /* cmdId */
out_uint16_le(s, 0); /* flags = 0 */
out_uint32_le(s, 21 + bitmap_data_length); /* pduLength */
out_uint16_le(s, surface_id);
out_uint16_le(s, codec_id);
out_uint32_le(s, codec_context_id);
out_uint8(s, pixel_format);
out_uint32_le(s, bitmap_data_length);
segment_count = 1;
index = 0;
bitmap_data8 = (char *) bitmap_data;
while (index < bitmap_data_length)
{
segment_size = bitmap_data_length - index;
if (segment_size > MAX_PART_SIZE)
{
segment_size = MAX_PART_SIZE;
}
/* RDP_DATA_SEGMENT */
out_uint32_le(s, 1 + segment_size); /* segmentArray size */
/* RDP8_BULK_ENCODED_DATA */
out_uint8(s, PACKET_COMPR_TYPE_RDP8); /* header */
out_uint8a(s, bitmap_data8 + index, segment_size);
LOG_DEVEL(LOG_LEVEL_DEBUG, " segment index %d segment_size %d",
segment_count, segment_size);
index += segment_size;
segment_count++;
}
s_mark_end(s);
s_pop_layer(s, iso_hdr);
out_uint16_le(s, segment_count);
LOG_DEVEL(LOG_LEVEL_DEBUG, "xrdp_egfx_wire_to_surface2: segment_count %d",
segment_count);
return s;
}
/******************************************************************************/
int
xrdp_egfx_send_wire_to_surface2(struct xrdp_egfx *egfx, int surface_id,
int codec_id, int codec_context_id,
int pixel_format,
void *bitmap_data, int bitmap_data_length)
{
int error;
struct stream *s;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_send_wire_to_surface2:");
s = xrdp_egfx_wire_to_surface2(egfx->bulk, surface_id, codec_id,
codec_context_id, pixel_format,
bitmap_data, bitmap_data_length);
error = xrdp_egfx_send_s(egfx, s);
LOG_DEVEL(LOG_LEVEL_DEBUG, "xrdp_egfx_send_wire_to_surface2: xrdp_egfx_send_s "
"error %d", error);
free_stream(s);
return error;
}
/******************************************************************************/
struct stream *
xrdp_egfx_reset_graphics(struct xrdp_egfx_bulk *bulk, int width, int height,
int monitor_count, struct monitor_info *mi)
{
int bytes;
int index;
struct stream *s;
LOG(LOG_LEVEL_INFO, "xrdp_egfx_reset_graphics:");
if (monitor_count > 16)
{
return NULL;
}
make_stream(s);
/* this should always be enough because limited to 16 monitors
and message is always 340 bytes */
init_stream(s, 8192);
/* RDP_SEGMENTED_DATA */
out_uint8(s, 0xE0); /* descriptor = SINGLE */
/* RDP8_BULK_ENCODED_DATA */
out_uint8(s, PACKET_COMPR_TYPE_RDP8); /* header */
/* RDPGFX_HEADER */
out_uint16_le(s, XR_RDPGFX_CMDID_RESETGRAPHICS); /* cmdId */
out_uint16_le(s, 0); /* flags = 0 */
out_uint32_le(s, 340); /* pduLength */
out_uint32_le(s, width);
out_uint32_le(s, height);
/*
TODO: Fix hack where working around 0 monitors is necessary.
In cases where multi-mon is not used for XRDP, internally 0 monitors
are represented, even though we implicitly have one: The entire session!
Some Microsoft clients (Mainly the Mac OS one) require that at least
one monitor be transmitted for EGFX to function, which is why this
change was necessary.
See https://github.com/neutrinolabs/xrdp/pull/2338#discussion_r944685856
https://github.com/neutrinolabs/xrdp/issues/2503
*/
out_uint32_le(s, monitor_count == 0 ? 1 : monitor_count);
if (monitor_count == 0)
{
out_uint32_le(s, 0);
out_uint32_le(s, 0);
out_uint32_le(s, width - 1);
out_uint32_le(s, height - 1);
out_uint32_le(s, 1);
monitor_count = 1;
}
else
{
for (index = 0; index < monitor_count; ++index)
{
out_uint32_le(s, mi[index].left);
out_uint32_le(s, mi[index].top);
out_uint32_le(s, mi[index].right);
out_uint32_le(s, mi[index].bottom);
out_uint32_le(s, mi[index].is_primary);
LOG(LOG_LEVEL_INFO, "xrdp_egfx_reset_graphics: (index %d) "
"monitor left %d top %d right %d bottom %d is_primary %d",
index, mi[index].left, mi[index].top,
mi[index].right, mi[index].bottom,
mi[index].is_primary);
}
}
LOG(LOG_LEVEL_INFO, "xrdp_egfx_reset_graphics: width %d height %d "
"monitorcount %d", width, height, monitor_count);
if (monitor_count < 16)
{
bytes = 340 - (20 + (monitor_count * 20));
out_uint8s(s, bytes);
}
s_mark_end(s);
return s;
}
/******************************************************************************/
int
xrdp_egfx_send_reset_graphics(struct xrdp_egfx *egfx, int width, int height,
int monitor_count, struct monitor_info *mi)
{
int error;
struct stream *s;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_send_reset_graphics:");
s = xrdp_egfx_reset_graphics(egfx->bulk, width, height, monitor_count, mi);
error = xrdp_egfx_send_s(egfx, s);
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_send_reset_graphics: xrdp_egfx_send_s "
"error %d", error);
free_stream(s);
return error;
}
/******************************************************************************/
/* RDPGFX_CMDID_FRAMEACKNOWLEDGE */
static int
xrdp_egfx_process_frame_ack(struct xrdp_egfx *egfx, struct stream *s)
{
uint32_t queueDepth;
uint32_t intframeId;
uint32_t totalFramesDecoded;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_process_frame_ack:");
if (!s_check_rem(s, 12))
{
return 1;
}
in_uint32_le(s, queueDepth);
in_uint32_le(s, intframeId);
in_uint32_le(s, totalFramesDecoded);
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_process_frame_ack: queueDepth %d"
" intframeId %d totalFramesDecoded %d",
queueDepth, intframeId, totalFramesDecoded);
if (egfx->frame_ack != NULL)
{
egfx->frame_ack(egfx->user, queueDepth, intframeId, totalFramesDecoded);
}
return 0;
}
/******************************************************************************/
/* RDPGFX_CMDID_CAPSADVERTISE */
static int
xrdp_egfx_process_capsadvertise(struct xrdp_egfx *egfx, struct stream *s)
{
int index;
int capsSetCount;
int caps_count;
int version;
int capsDataLength;
int flags;
char *holdp;
int *versions;
int *flagss;
int rv = 0;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_process_capsadvertise:");
if (egfx->caps_advertise == NULL)
{
return 0;
}
in_uint16_le(s, capsSetCount);
if ((capsSetCount < 1) || (capsSetCount > 1024))
{
return 1;
}
caps_count = 0;
versions = g_new(int, capsSetCount);
flagss = g_new(int, capsSetCount);
if (versions == NULL || flagss == NULL)
{
rv = 1;
}
else
{
for (index = 0; index < capsSetCount; index++)
{
if (!s_check_rem(s, 8))
{
rv = 1;
break;
}
in_uint32_le(s, version);
in_uint32_le(s, capsDataLength);
if (!s_check_rem(s, capsDataLength))
{
rv = 1;
break;
}
holdp = s->p;
// This implicity excludes caps version 101.
if (capsDataLength == 4)
{
in_uint32_le(s, flags);
versions[caps_count] = version;
flagss[caps_count] = flags;
caps_count++;
}
s->p = holdp + capsDataLength;
}
}
if (rv == 0 && caps_count > 0)
{
egfx->caps_advertise(egfx->user, caps_count, versions, flagss);
}
g_free(versions);
g_free(flagss);
return rv;
}
/******************************************************************************/
static int
xrdp_egfx_process(struct xrdp_egfx *egfx, struct stream *s)
{
int error;
int cmdId;
int flags;
int pduLength;
char *holdp;
char *holdend;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_process:");
error = 0;
while (s_check_rem(s, 8))
{
holdp = s->p;
holdend = s->end;
in_uint16_le(s, cmdId);
in_uint16_le(s, flags);
in_uint32_le(s, pduLength);
s->end = holdp + pduLength;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_process: cmdId 0x%x flags %d"
" pduLength %d", cmdId, flags, pduLength);
if (pduLength < 8)
{
return 1;
}
if (!s_check_rem(s, pduLength - 8))
{
return 1;
}
switch (cmdId)
{
case XR_RDPGFX_CMDID_FRAMEACKNOWLEDGE:
error = xrdp_egfx_process_frame_ack(egfx, s);
break;
case XR_RDPGFX_CMDID_CAPSADVERTISE:
error = xrdp_egfx_process_capsadvertise(egfx, s);
break;
case XR_RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE:
break;
/*
TODO: Handle Cache Import PDU here.
https://github.com/neutrinolabs/xrdp/issues/2502
case XR_RDPGFX_CMDID_CACHEIMPORTOFFER:
2.2.2.16 RDPGFX_CACHE_IMPORT_OFFER_PDU
Reply with
2.2.2.17 RDPGFX_CACHE_IMPORT_REPLY_PDU
*/
default:
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_process:"
" unknown cmdId 0x%x", cmdId);
break;
}
if (error != 0)
{
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_process: error %d", error);
return error;
}
s->p = holdp + pduLength;
s->end = holdend;
}
return error;
}
/******************************************************************************/
/* from client */
static int
xrdp_egfx_open_response(struct xrdp_process *id, int chan_id,
int creation_status)
{
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_open_response:");
return 0;
}
/******************************************************************************/
/* from client */
static int
xrdp_egfx_close_response(struct xrdp_process *id, int chan_id)
{
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_close_response:");
return 0;
}
/******************************************************************************/
/* from client */
static int
xrdp_egfx_data_first(struct xrdp_process *id, int chan_id,
char *data, int bytes, int total_bytes)
{
struct xrdp_egfx *egfx;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_data_first: bytes %d"
" total_bytes %d", bytes, total_bytes);
egfx = id->wm->mm->egfx;
if (egfx->s != NULL)
{
LOG(LOG_LEVEL_ERROR, "DYNVC_DATA_FIRST PDU received while"
" another stream is active on channel %d", chan_id);
return 1;
}
make_stream(egfx->s);
// Caller has checked total_bytes is >= 0 and bytes is < total_bytes
init_stream(egfx->s, total_bytes);
out_uint8a(egfx->s, data, bytes);
return 0;
}
/******************************************************************************/
/* from client */
static int
xrdp_egfx_data(struct xrdp_process *id, int chan_id, char *data, int bytes)
{
int error;
struct stream ls;
struct xrdp_wm *wm;
struct xrdp_mm *mm;
struct xrdp_egfx *egfx;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_data:");
if (id == NULL)
{
return 0;
}
wm = id->wm;
if (wm == NULL)
{
return 0;
}
mm = wm->mm;
if (mm == NULL)
{
return 0;
}
egfx = mm->egfx;
if (egfx == NULL)
{
return 0;
}
if (egfx->s == NULL)
{
g_memset(&ls, 0, sizeof(ls));
ls.data = data;
ls.size = bytes;
ls.p = data;
ls.end = data + bytes;
return xrdp_egfx_process(egfx, &ls);
}
if (!s_check_rem_out(egfx->s, bytes))
{
LOG(LOG_LEVEL_ERROR, "DYNVC_DATA PDU data overflow on channel %d",
chan_id);
return 1;
}
out_uint8a(egfx->s, data, bytes);
if (!s_check_rem_out(egfx->s, 1))
{
s_mark_end(egfx->s);
egfx->s->p = egfx->s->data;
error = xrdp_egfx_process(egfx, egfx->s);
free_stream(egfx->s);
egfx->s = NULL;
return error;
}
return 0;
}
/******************************************************************************/
int
xrdp_egfx_create(struct xrdp_mm *mm, struct xrdp_egfx **egfx)
{
int error;
struct xrdp_drdynvc_procs procs;
struct xrdp_egfx *self;
struct xrdp_process *process;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_create:");
self = g_new0(struct xrdp_egfx, 1);
if (self == NULL)
{
return 1;
}
procs.open_response = xrdp_egfx_open_response;
procs.close_response = xrdp_egfx_close_response;
procs.data_first = xrdp_egfx_data_first;
procs.data = xrdp_egfx_data;
process = mm->wm->pro_layer;
error = libxrdp_drdynvc_open(process->session,
"Microsoft::Windows::RDS::Graphics",
1, /* WTS_CHANNEL_OPTION_DYNAMIC */
&procs, &(self->channel_id));
LOG(LOG_LEVEL_INFO, "xrdp_egfx_create: error %d channel_id %d",
error, self->channel_id);
self->session = process->session;
self->surface_id = 0;
*egfx = self;
return 0;
}
/******************************************************************************/
int
xrdp_egfx_shutdown_delete_surface(struct xrdp_egfx *egfx)
{
int error;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_shutdown_delete_surface:");
if (egfx == 0)
{
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_shutdown_delete_surface:"
" EGFX is already null!");
return 0;
}
error = xrdp_egfx_send_delete_surface(egfx, egfx->surface_id);
if (error != 0)
{
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_shutdown_delete_surface:"
" xrdp_egfx_send_delete_surface failed %d", error);
}
return error;
}
/******************************************************************************/
int
xrdp_egfx_shutdown_close_connection(struct xrdp_egfx *egfx)
{
int error;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_shutdown_close_connection:");
if (egfx == 0)
{
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_shutdown_close_connection:"
" EGFX is already null!");
return 0;
}
error = libxrdp_drdynvc_close(egfx->session, egfx->channel_id);
if (error != 0)
{
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_shutdown_close_connection:"
" libxrdp_drdynvc_close failed %d", error);
return error;
}
// Ignore any messages we haven't processed yet
egfx->caps_advertise = NULL;
egfx->frame_ack = NULL;
return error;
}
/******************************************************************************/
int
xrdp_egfx_shutdown_delete(struct xrdp_egfx *egfx)
{
int error = 0;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_delete:");
if (egfx == 0)
{
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_delete: EGFX is already null!");
return 0;
}
g_free(egfx);
return error;
}
/******************************************************************************/
int
xrdp_egfx_shutdown_full(struct xrdp_egfx *egfx)
{
int error;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_shutdown_full:");
if (egfx == 0)
{
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_shutdown_full: EGFX is already null!");
return 0;
}
error = xrdp_egfx_shutdown_delete_surface(egfx);
if (error != 0)
{
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_shutdown_full:"
" xrdp_egfx_shutdown_delete_surface failed %d", error);
return error;
}
error = xrdp_egfx_shutdown_close_connection(egfx);
if (error != 0)
{
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_shutdown_full:"
" xrdp_egfx_shutdown_close_connection failed %d", error);
return error;
}
error = xrdp_egfx_shutdown_delete(egfx);
if (error != 0)
{
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_shutdown_full:"
" xrdp_egfx_shutdown_delete failed %d", error);
return error;
}
return error;
}