/**
* 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 <limits.h>
#define MAX_PART_SIZE 0 xFFFF
#define PACKET_COMPR_TYPE_RDP8 0 x04 /* 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, 0 xE0); /* 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, 0 xE0); /* 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, 0 xE0); /* 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, 0 xE0); /* 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, 0 xE0); /* 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, 0 xE0); /* 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, 0 xE0); /* 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, 0 xE0); /* 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, 0 xE1); /* 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, 0 xE1); /* 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, 0 xE0); /* 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(intptr_t id, int chan_id, int creation_status)
{
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_open_response:" );
return 0 ;
}
int
advance_resize_state_machine(struct xrdp_mm *mm,
enum display_resize_state new_state);
/******************************************************************************/
/* from client */
static int
xrdp_egfx_close_response(intptr_t id, int chan_id)
{
struct xrdp_process *process;
struct xrdp_mm *mm;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_close_response:" );
process = (struct xrdp_process *) id;
mm = process->wm->mm;
if (mm->resize_queue == 0 || mm->resize_queue->count <= 0 )
{
return 0 ;
}
if (mm->resize_data != NULL
&& mm->resize_data->state == WMRZ_EGFX_CONN_CLOSING)
{
LOG(LOG_LEVEL_DEBUG, "xrdp_egfx_close_response: egfx deleted." );
advance_resize_state_machine(mm, WMRZ_EGFX_CONN_CLOSED);
}
return 0 ;
}
/******************************************************************************/
/* from client */
static int
xrdp_egfx_data_first(intptr_t id, int chan_id, char *data, int bytes,
int total_bytes)
{
struct xrdp_process *process;
struct xrdp_egfx *egfx;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_data_first: bytes %d"
" total_bytes %d" , bytes, total_bytes);
process = (struct xrdp_process *) id;
egfx = process->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(intptr_t id, int chan_id, char *data, int bytes)
{
int error;
struct stream ls;
struct xrdp_process *process;
struct xrdp_wm *wm;
struct xrdp_mm *mm;
struct xrdp_egfx *egfx;
LOG(LOG_LEVEL_TRACE, "xrdp_egfx_data:" );
process = (struct xrdp_process *) id;
if (process == NULL)
{
return 0 ;
}
wm = process->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;
}
Messung V0.5 in Prozent C=93 H=93 G=92
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet am 2026-07-10)
¤
*© Formatika GbR, Deutschland