FreeRDP
surface.c
1 
20 #include <freerdp/config.h>
21 
22 #include "settings.h"
23 
24 #include <winpr/assert.h>
25 
26 #include <freerdp/utils/pcap.h>
27 #include <freerdp/log.h>
28 
29 #include "../cache/cache.h"
30 #include "surface.h"
31 
32 #define TAG FREERDP_TAG("core.surface")
33 
34 static BOOL update_recv_surfcmd_bitmap_header_ex(wStream* s, TS_COMPRESSED_BITMAP_HEADER_EX* header)
35 {
36  if (!s || !header)
37  return FALSE;
38 
39  if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
40  return FALSE;
41 
42  Stream_Read_UINT32(s, header->highUniqueId);
43  Stream_Read_UINT32(s, header->lowUniqueId);
44  Stream_Read_UINT64(s, header->tmMilliseconds);
45  Stream_Read_UINT64(s, header->tmSeconds);
46  return TRUE;
47 }
48 
49 static BOOL update_recv_surfcmd_bitmap_ex(wStream* s, TS_BITMAP_DATA_EX* bmp)
50 {
51  if (!s || !bmp)
52  return FALSE;
53 
54  if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
55  return FALSE;
56 
57  Stream_Read_UINT8(s, bmp->bpp);
58  Stream_Read_UINT8(s, bmp->flags);
59  Stream_Seek(s, 1); /* reserved */
60  Stream_Read_UINT8(s, bmp->codecID);
61  Stream_Read_UINT16(s, bmp->width);
62  Stream_Read_UINT16(s, bmp->height);
63  Stream_Read_UINT32(s, bmp->bitmapDataLength);
64 
65  if ((bmp->width == 0) || (bmp->height == 0))
66  {
67  WLog_ERR(TAG, "invalid size value width=%" PRIu16 ", height=%" PRIu16, bmp->width,
68  bmp->height);
69  return FALSE;
70  }
71 
72  if ((bmp->bpp < 1) || (bmp->bpp > 32))
73  {
74  WLog_ERR(TAG, "invalid bpp value %" PRIu32 "", bmp->bpp);
75  return FALSE;
76  }
77 
78  if (bmp->flags & EX_COMPRESSED_BITMAP_HEADER_PRESENT)
79  {
80  if (!update_recv_surfcmd_bitmap_header_ex(s, &bmp->exBitmapDataHeader))
81  return FALSE;
82  }
83 
84  bmp->bitmapData = Stream_Pointer(s);
85  if (!Stream_SafeSeek(s, bmp->bitmapDataLength))
86  {
87  WLog_ERR(TAG, "expected bitmapDataLength %" PRIu32 ", not enough data",
88  bmp->bitmapDataLength);
89  return FALSE;
90  }
91  return TRUE;
92 }
93 
94 static BOOL update_recv_surfcmd_is_rect_valid(const rdpContext* context,
95  const SURFACE_BITS_COMMAND* cmd)
96 {
97  WINPR_ASSERT(context);
98  WINPR_ASSERT(context->settings);
99  WINPR_ASSERT(cmd);
100 
101  /* We need a rectangle with left/top being smaller than right/bottom.
102  * Also do not allow empty rectangles. */
103  if ((cmd->destTop >= cmd->destBottom) || (cmd->destLeft >= cmd->destRight))
104  {
105  WLog_WARN(TAG,
106  "Empty surface bits command rectangle: %" PRIu16 "x%" PRIu16 "-%" PRIu16
107  "x%" PRIu16,
108  cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom);
109  return FALSE;
110  }
111 
112  /* The rectangle needs to fit into our session size */
113  if ((cmd->destRight > context->settings->DesktopWidth) ||
114  (cmd->destBottom > context->settings->DesktopHeight))
115  {
116  WLog_WARN(TAG,
117  "Invalid surface bits command rectangle: %" PRIu16 "x%" PRIu16 "-%" PRIu16
118  "x%" PRIu16 " does not fit %" PRIu32 "x%" PRIu32,
119  cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom,
120  context->settings->DesktopWidth, context->settings->DesktopHeight);
121  return FALSE;
122  }
123 
124  return TRUE;
125 }
126 
127 static BOOL update_recv_surfcmd_surface_bits(rdpUpdate* update, wStream* s, UINT16 cmdType)
128 {
129  BOOL rc = FALSE;
130  SURFACE_BITS_COMMAND cmd = { 0 };
131 
132  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
133  goto fail;
134 
135  cmd.cmdType = cmdType;
136  Stream_Read_UINT16(s, cmd.destLeft);
137  Stream_Read_UINT16(s, cmd.destTop);
138  Stream_Read_UINT16(s, cmd.destRight);
139  Stream_Read_UINT16(s, cmd.destBottom);
140 
141  if (!update_recv_surfcmd_is_rect_valid(update->context, &cmd))
142  goto fail;
143 
144  if (!update_recv_surfcmd_bitmap_ex(s, &cmd.bmp))
145  goto fail;
146 
147  if (!IFCALLRESULT(TRUE, update->SurfaceBits, update->context, &cmd))
148  {
149  WLog_DBG(TAG, "update->SurfaceBits implementation failed");
150  goto fail;
151  }
152 
153  rc = TRUE;
154 fail:
155  return rc;
156 }
157 
158 static BOOL update_recv_surfcmd_frame_marker(rdpUpdate* update, wStream* s)
159 {
160  SURFACE_FRAME_MARKER marker = { 0 };
161  rdp_update_internal* up = update_cast(update);
162 
163  WINPR_ASSERT(s);
164 
165  if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
166  return FALSE;
167 
168  Stream_Read_UINT16(s, marker.frameAction);
169  if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
170  WLog_WARN(TAG,
171  "[SERVER-BUG]: got %" PRIuz ", expected %" PRIuz
172  " bytes. [MS-RDPBCGR] 2.2.9.2.3 Frame Marker Command (TS_FRAME_MARKER) is "
173  "missing frameId, ignoring",
174  Stream_GetRemainingLength(s), 4);
175  else
176  Stream_Read_UINT32(s, marker.frameId);
177  WLog_Print(up->log, WLOG_DEBUG, "SurfaceFrameMarker: action: %s (%" PRIu32 ") id: %" PRIu32 "",
178  (!marker.frameAction) ? "Begin" : "End", marker.frameAction, marker.frameId);
179 
180  if (!update->SurfaceFrameMarker)
181  {
182  WINPR_ASSERT(update->context);
183  if (freerdp_settings_get_bool(update->context->settings, FreeRDP_DeactivateClientDecoding))
184  return TRUE;
185  WLog_ERR(TAG, "Missing callback update->SurfaceFrameMarker");
186  return FALSE;
187  }
188 
189  if (!update->SurfaceFrameMarker(update->context, &marker))
190  {
191  WLog_DBG(TAG, "update->SurfaceFrameMarker implementation failed");
192  return FALSE;
193  }
194 
195  return TRUE;
196 }
197 
198 int update_recv_surfcmds(rdpUpdate* update, wStream* s)
199 {
200  UINT16 cmdType = 0;
201  rdp_update_internal* up = update_cast(update);
202 
203  WINPR_ASSERT(s);
204 
205  while (Stream_GetRemainingLength(s) >= 2)
206  {
207  const size_t start = Stream_GetPosition(s);
208  const BYTE* mark = Stream_ConstPointer(s);
209 
210  Stream_Read_UINT16(s, cmdType);
211 
212  switch (cmdType)
213  {
214  case CMDTYPE_SET_SURFACE_BITS:
215  case CMDTYPE_STREAM_SURFACE_BITS:
216  if (!update_recv_surfcmd_surface_bits(update, s, cmdType))
217  return -1;
218 
219  break;
220 
221  case CMDTYPE_FRAME_MARKER:
222  if (!update_recv_surfcmd_frame_marker(update, s))
223  return -1;
224 
225  break;
226 
227  default:
228  WLog_ERR(TAG, "unknown cmdType 0x%04" PRIX16 "", cmdType);
229  return -1;
230  }
231 
232  if (up->dump_rfx)
233  {
234  const size_t size = Stream_GetPosition(s) - start;
235  /* TODO: treat return values */
236  pcap_add_record(up->pcap_rfx, mark, size);
237  pcap_flush(up->pcap_rfx);
238  }
239  }
240 
241  return 0;
242 }
243 
244 static BOOL update_write_surfcmd_bitmap_header_ex(wStream* s,
245  const TS_COMPRESSED_BITMAP_HEADER_EX* header)
246 {
247  if (!s || !header)
248  return FALSE;
249 
250  if (!Stream_EnsureRemainingCapacity(s, 24))
251  return FALSE;
252 
253  Stream_Write_UINT32(s, header->highUniqueId);
254  Stream_Write_UINT32(s, header->lowUniqueId);
255  Stream_Write_UINT64(s, header->tmMilliseconds);
256  Stream_Write_UINT64(s, header->tmSeconds);
257  return TRUE;
258 }
259 
260 static BOOL update_write_surfcmd_bitmap_ex(wStream* s, const TS_BITMAP_DATA_EX* bmp)
261 {
262  if (!s || !bmp)
263  return FALSE;
264 
265  if (!Stream_EnsureRemainingCapacity(s, 12))
266  return FALSE;
267 
268  if (bmp->codecID > UINT8_MAX)
269  {
270  WLog_ERR(TAG, "Invalid TS_BITMAP_DATA_EX::codecID=0x%04" PRIx16 "", bmp->codecID);
271  return FALSE;
272  }
273  Stream_Write_UINT8(s, bmp->bpp);
274  Stream_Write_UINT8(s, bmp->flags);
275  Stream_Write_UINT8(s, 0); /* reserved1, reserved2 */
276  Stream_Write_UINT8(s, (UINT8)bmp->codecID);
277  Stream_Write_UINT16(s, bmp->width);
278  Stream_Write_UINT16(s, bmp->height);
279  Stream_Write_UINT32(s, bmp->bitmapDataLength);
280 
281  if (bmp->flags & EX_COMPRESSED_BITMAP_HEADER_PRESENT)
282  {
283  if (!update_write_surfcmd_bitmap_header_ex(s, &bmp->exBitmapDataHeader))
284  return FALSE;
285  }
286 
287  if (!Stream_EnsureRemainingCapacity(s, bmp->bitmapDataLength))
288  return FALSE;
289 
290  Stream_Write(s, bmp->bitmapData, bmp->bitmapDataLength);
291  return TRUE;
292 }
293 
294 BOOL update_write_surfcmd_surface_bits(wStream* s, const SURFACE_BITS_COMMAND* cmd)
295 {
296  UINT16 cmdType = 0;
297  if (!Stream_EnsureRemainingCapacity(s, SURFCMD_SURFACE_BITS_HEADER_LENGTH))
298  return FALSE;
299 
300  cmdType = cmd->cmdType;
301  switch (cmdType)
302  {
303  case CMDTYPE_SET_SURFACE_BITS:
304  case CMDTYPE_STREAM_SURFACE_BITS:
305  break;
306  default:
307  WLog_WARN(TAG,
308  "SURFACE_BITS_COMMAND->cmdType 0x%04" PRIx16
309  " not allowed, correcting to 0x%04" PRIx16,
310  cmdType, CMDTYPE_STREAM_SURFACE_BITS);
311  cmdType = CMDTYPE_STREAM_SURFACE_BITS;
312  break;
313  }
314 
315  Stream_Write_UINT16(s, cmdType);
316  Stream_Write_UINT16(s, cmd->destLeft);
317  Stream_Write_UINT16(s, cmd->destTop);
318  Stream_Write_UINT16(s, cmd->destRight);
319  Stream_Write_UINT16(s, cmd->destBottom);
320  return update_write_surfcmd_bitmap_ex(s, &cmd->bmp);
321 }
322 
323 BOOL update_write_surfcmd_frame_marker(wStream* s, UINT16 frameAction, UINT32 frameId)
324 {
325  if (!Stream_EnsureRemainingCapacity(s, SURFCMD_FRAME_MARKER_LENGTH))
326  return FALSE;
327 
328  Stream_Write_UINT16(s, CMDTYPE_FRAME_MARKER);
329  Stream_Write_UINT16(s, frameAction);
330  Stream_Write_UINT32(s, frameId);
331  return TRUE;
332 }
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.