/src/FreeRDP/libfreerdp/core/surface.c
Line | Count | Source |
1 | | /** |
2 | | * FreeRDP: A Remote Desktop Protocol Implementation |
3 | | * Surface Commands |
4 | | * |
5 | | * Copyright 2011 Vic Lee |
6 | | * |
7 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
8 | | * you may not use this file except in compliance with the License. |
9 | | * You may obtain a copy of the License at |
10 | | * |
11 | | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | | * |
13 | | * Unless required by applicable law or agreed to in writing, software |
14 | | * distributed under the License is distributed on an "AS IS" BASIS, |
15 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | | * See the License for the specific language governing permissions and |
17 | | * limitations under the License. |
18 | | */ |
19 | | |
20 | | #include <freerdp/config.h> |
21 | | |
22 | | #include "settings.h" |
23 | | |
24 | | #include <winpr/assert.h> |
25 | | #include <winpr/cast.h> |
26 | | |
27 | | #include <freerdp/utils/pcap.h> |
28 | | #include <freerdp/log.h> |
29 | | |
30 | | #include "../cache/cache.h" |
31 | | #include "surface.h" |
32 | | |
33 | | #define TAG FREERDP_TAG("core.surface") |
34 | | |
35 | | static BOOL update_recv_surfcmd_bitmap_header_ex(wStream* s, TS_COMPRESSED_BITMAP_HEADER_EX* header) |
36 | 2.31k | { |
37 | 2.31k | if (!s || !header) |
38 | 0 | return FALSE; |
39 | | |
40 | 2.31k | if (!Stream_CheckAndLogRequiredLength(TAG, s, 24)) |
41 | 111 | return FALSE; |
42 | | |
43 | 2.20k | Stream_Read_UINT32(s, header->highUniqueId); |
44 | 2.20k | Stream_Read_UINT32(s, header->lowUniqueId); |
45 | 2.20k | Stream_Read_UINT64(s, header->tmMilliseconds); |
46 | 2.20k | Stream_Read_UINT64(s, header->tmSeconds); |
47 | 2.20k | return TRUE; |
48 | 2.31k | } |
49 | | |
50 | | static BOOL update_recv_surfcmd_bitmap_ex(wStream* s, TS_BITMAP_DATA_EX* bmp) |
51 | 4.38k | { |
52 | 4.38k | if (!s || !bmp) |
53 | 0 | return FALSE; |
54 | | |
55 | 4.38k | if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) |
56 | 241 | return FALSE; |
57 | | |
58 | 4.14k | Stream_Read_UINT8(s, bmp->bpp); |
59 | 4.14k | Stream_Read_UINT8(s, bmp->flags); |
60 | 4.14k | Stream_Seek(s, 1); /* reserved */ |
61 | 4.14k | Stream_Read_UINT8(s, bmp->codecID); |
62 | 4.14k | Stream_Read_UINT16(s, bmp->width); |
63 | 4.14k | Stream_Read_UINT16(s, bmp->height); |
64 | 4.14k | Stream_Read_UINT32(s, bmp->bitmapDataLength); |
65 | | |
66 | 4.14k | if ((bmp->width == 0) || (bmp->height == 0)) |
67 | 161 | { |
68 | 161 | WLog_ERR(TAG, "invalid size value width=%" PRIu16 ", height=%" PRIu16, bmp->width, |
69 | 161 | bmp->height); |
70 | 161 | return FALSE; |
71 | 161 | } |
72 | | |
73 | 3.98k | if ((bmp->bpp < 1) || (bmp->bpp > 32)) |
74 | 767 | { |
75 | 767 | WLog_ERR(TAG, "invalid bpp value %" PRIu32 "", bmp->bpp); |
76 | 767 | return FALSE; |
77 | 767 | } |
78 | | |
79 | 3.21k | if (bmp->flags & EX_COMPRESSED_BITMAP_HEADER_PRESENT) |
80 | 2.31k | { |
81 | 2.31k | if (!update_recv_surfcmd_bitmap_header_ex(s, &bmp->exBitmapDataHeader)) |
82 | 111 | return FALSE; |
83 | 2.31k | } |
84 | | |
85 | 3.10k | bmp->bitmapData = Stream_Pointer(s); |
86 | 3.10k | if (!Stream_SafeSeek(s, bmp->bitmapDataLength)) |
87 | 1.21k | { |
88 | 1.21k | WLog_ERR(TAG, "expected bitmapDataLength %" PRIu32 ", not enough data", |
89 | 1.21k | bmp->bitmapDataLength); |
90 | 1.21k | return FALSE; |
91 | 1.21k | } |
92 | 1.89k | return TRUE; |
93 | 3.10k | } |
94 | | |
95 | | static BOOL update_recv_surfcmd_is_rect_valid(const rdpContext* context, |
96 | | const SURFACE_BITS_COMMAND* cmd) |
97 | 6.71k | { |
98 | 6.71k | WINPR_ASSERT(context); |
99 | 6.71k | WINPR_ASSERT(context->settings); |
100 | 6.71k | WINPR_ASSERT(cmd); |
101 | | |
102 | | /* We need a rectangle with left/top being smaller than right/bottom. |
103 | | * Also do not allow empty rectangles. */ |
104 | 6.71k | if ((cmd->destTop >= cmd->destBottom) || (cmd->destLeft >= cmd->destRight)) |
105 | 1.35k | { |
106 | 1.35k | WLog_WARN(TAG, |
107 | 1.35k | "Empty surface bits command rectangle: %" PRIu16 "x%" PRIu16 "-%" PRIu16 |
108 | 1.35k | "x%" PRIu16, |
109 | 1.35k | cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom); |
110 | 1.35k | return FALSE; |
111 | 1.35k | } |
112 | | |
113 | | /* The rectangle needs to fit into our session size */ |
114 | 5.36k | const DWORD DesktopWidth = freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopWidth); |
115 | 5.36k | const DWORD DesktopHeight = |
116 | 5.36k | freerdp_settings_get_uint32(context->settings, FreeRDP_DesktopHeight); |
117 | 5.36k | if ((cmd->destRight > DesktopWidth) || (cmd->destBottom > DesktopHeight)) |
118 | 975 | { |
119 | 975 | WLog_WARN(TAG, |
120 | 975 | "Invalid surface bits command rectangle: %" PRIu16 "x%" PRIu16 "-%" PRIu16 |
121 | 975 | "x%" PRIu16 " does not fit %" PRIu32 "x%" PRIu32, |
122 | 975 | cmd->destLeft, cmd->destTop, cmd->destRight, cmd->destBottom, DesktopWidth, |
123 | 975 | DesktopHeight); |
124 | 975 | return FALSE; |
125 | 975 | } |
126 | | |
127 | 4.38k | return TRUE; |
128 | 5.36k | } |
129 | | |
130 | | static BOOL update_recv_surfcmd_surface_bits(rdpUpdate* update, wStream* s, UINT16 cmdType) |
131 | 7.24k | { |
132 | 7.24k | rdp_update_internal* up = update_cast(update); |
133 | 7.24k | BOOL rc = FALSE; |
134 | 7.24k | SURFACE_BITS_COMMAND cmd = WINPR_C_ARRAY_INIT; |
135 | | |
136 | 7.24k | WINPR_ASSERT(up); |
137 | | |
138 | 7.24k | if (!Stream_CheckAndLogRequiredLength(TAG, s, 8)) |
139 | 527 | goto fail; |
140 | | |
141 | 6.71k | cmd.cmdType = cmdType; |
142 | 6.71k | Stream_Read_UINT16(s, cmd.destLeft); |
143 | 6.71k | Stream_Read_UINT16(s, cmd.destTop); |
144 | 6.71k | Stream_Read_UINT16(s, cmd.destRight); |
145 | 6.71k | Stream_Read_UINT16(s, cmd.destBottom); |
146 | | |
147 | 6.71k | if (!update_recv_surfcmd_is_rect_valid(update->context, &cmd)) |
148 | 2.33k | goto fail; |
149 | | |
150 | 4.38k | if (!update_recv_surfcmd_bitmap_ex(s, &cmd.bmp)) |
151 | 2.49k | goto fail; |
152 | | |
153 | 1.89k | up->stats.base[RDP_STATS_SURFACE_BITS]++; |
154 | 1.89k | switch (cmd.bmp.codecID) |
155 | 1.89k | { |
156 | 23 | case RDP_CODEC_ID_REMOTEFX: |
157 | 23 | up->stats.base[RDP_STATS_SURFACE_BITS_RFX]++; |
158 | 23 | break; |
159 | 20 | case RDP_CODEC_ID_IMAGE_REMOTEFX: |
160 | 20 | up->stats.base[RDP_STATS_SURFACE_BITS_RFX_IMAGE]++; |
161 | 20 | break; |
162 | 11 | case RDP_CODEC_ID_NSCODEC: |
163 | 11 | up->stats.base[RDP_STATS_SURFACE_BITS_NSC]++; |
164 | 11 | break; |
165 | 31 | case RDP_CODEC_ID_NONE: |
166 | 31 | up->stats.base[RDP_STATS_SURFACE_BITS_NONE]++; |
167 | 31 | break; |
168 | 1.80k | default: |
169 | 1.80k | up->stats.base[RDP_STATS_SURFACE_BITS_UNKNOWN]++; |
170 | 1.80k | break; |
171 | 1.89k | } |
172 | | |
173 | 1.89k | if (!IFCALLRESULT(TRUE, update->SurfaceBits, update->context, &cmd)) |
174 | 1.22k | { |
175 | 1.22k | WLog_DBG(TAG, "update->SurfaceBits implementation failed"); |
176 | 1.22k | goto fail; |
177 | 1.22k | } |
178 | | |
179 | 669 | rc = TRUE; |
180 | 7.24k | fail: |
181 | 7.24k | return rc; |
182 | 669 | } |
183 | | |
184 | | static BOOL update_recv_surfcmd_frame_marker(rdpUpdate* update, wStream* s) |
185 | 1.64k | { |
186 | 1.64k | SURFACE_FRAME_MARKER marker = WINPR_C_ARRAY_INIT; |
187 | 1.64k | rdp_update_internal* up = update_cast(update); |
188 | | |
189 | 1.64k | WINPR_ASSERT(s); |
190 | | |
191 | 1.64k | if (!Stream_CheckAndLogRequiredLength(TAG, s, 2)) |
192 | 149 | return FALSE; |
193 | | |
194 | 1.49k | Stream_Read_UINT16(s, marker.frameAction); |
195 | 1.49k | if (!Stream_CheckAndLogRequiredLength(TAG, s, 4)) |
196 | 759 | WLog_WARN(TAG, |
197 | 1.49k | "[SERVER-BUG]: got %" PRIuz ", expected %u" |
198 | 1.49k | " bytes. [MS-RDPBCGR] 2.2.9.2.3 Frame Marker Command (TS_FRAME_MARKER) is " |
199 | 1.49k | "missing frameId, ignoring", |
200 | 1.49k | Stream_GetRemainingLength(s), 4u); |
201 | 735 | else |
202 | 735 | Stream_Read_UINT32(s, marker.frameId); |
203 | 1.49k | WLog_Print(up->log, WLOG_DEBUG, "SurfaceFrameMarker: action: %s (%" PRIu32 ") id: %" PRIu32 "", |
204 | 1.49k | (!marker.frameAction) ? "Begin" : "End", marker.frameAction, marker.frameId); |
205 | | |
206 | 1.49k | if (!update->SurfaceFrameMarker) |
207 | 335 | { |
208 | 335 | WINPR_ASSERT(update->context); |
209 | 335 | if (freerdp_settings_get_bool(update->context->settings, FreeRDP_DeactivateClientDecoding)) |
210 | 0 | return TRUE; |
211 | 335 | WLog_ERR(TAG, "Missing callback update->SurfaceFrameMarker"); |
212 | 335 | return FALSE; |
213 | 335 | } |
214 | | |
215 | 1.15k | if (!update->SurfaceFrameMarker(update->context, &marker)) |
216 | 1.15k | { |
217 | 1.15k | WLog_DBG(TAG, "update->SurfaceFrameMarker implementation failed"); |
218 | 1.15k | return FALSE; |
219 | 1.15k | } |
220 | | |
221 | 0 | return TRUE; |
222 | 1.15k | } |
223 | | |
224 | | BOOL update_recv_surfcmds(rdpUpdate* update, wStream* s) |
225 | 30.6k | { |
226 | 30.6k | UINT16 cmdType = 0; |
227 | 30.6k | rdp_update_internal* up = update_cast(update); |
228 | | |
229 | 30.6k | WINPR_ASSERT(s); |
230 | | |
231 | 31.2k | while (Stream_GetRemainingLength(s) >= 2) |
232 | 18.4k | { |
233 | 18.4k | const size_t start = Stream_GetPosition(s); |
234 | 18.4k | const BYTE* mark = Stream_ConstPointer(s); |
235 | | |
236 | 18.4k | Stream_Read_UINT16(s, cmdType); |
237 | | |
238 | 18.4k | switch (cmdType) |
239 | 18.4k | { |
240 | 6.18k | case CMDTYPE_SET_SURFACE_BITS: |
241 | 7.24k | case CMDTYPE_STREAM_SURFACE_BITS: |
242 | 7.24k | if (!update_recv_surfcmd_surface_bits(update, s, cmdType)) |
243 | 6.57k | return FALSE; |
244 | 669 | break; |
245 | | |
246 | 1.64k | case CMDTYPE_FRAME_MARKER: |
247 | 1.64k | up->stats.base[RDP_STATS_SURFACE_FRAME_MARKER]++; |
248 | 1.64k | if (!update_recv_surfcmd_frame_marker(update, s)) |
249 | 1.64k | return FALSE; |
250 | | |
251 | 0 | break; |
252 | | |
253 | 9.51k | default: |
254 | 9.51k | WLog_ERR(TAG, "unknown cmdType 0x%04" PRIX16 "", cmdType); |
255 | 9.51k | return FALSE; |
256 | 18.4k | } |
257 | | |
258 | 669 | if (up->dump_rfx) |
259 | 0 | { |
260 | 0 | const size_t size = Stream_GetPosition(s) - start; |
261 | | /* TODO: treat return values */ |
262 | 0 | if (!pcap_add_record(up->pcap_rfx, mark, size)) |
263 | 0 | return FALSE; |
264 | 0 | pcap_flush(up->pcap_rfx); |
265 | 0 | } |
266 | 669 | } |
267 | | |
268 | 12.8k | return TRUE; |
269 | 30.6k | } |
270 | | |
271 | | static BOOL update_write_surfcmd_bitmap_header_ex(wStream* s, |
272 | | const TS_COMPRESSED_BITMAP_HEADER_EX* header) |
273 | 986 | { |
274 | 986 | if (!s || !header) |
275 | 0 | return FALSE; |
276 | | |
277 | 986 | if (!Stream_EnsureRemainingCapacity(s, 24)) |
278 | 0 | return FALSE; |
279 | | |
280 | 986 | Stream_Write_UINT32(s, header->highUniqueId); |
281 | 986 | Stream_Write_UINT32(s, header->lowUniqueId); |
282 | 986 | Stream_Write_UINT64(s, header->tmMilliseconds); |
283 | 986 | Stream_Write_UINT64(s, header->tmSeconds); |
284 | 986 | return TRUE; |
285 | 986 | } |
286 | | |
287 | | static BOOL update_write_surfcmd_bitmap_ex(wStream* s, const TS_BITMAP_DATA_EX* bmp) |
288 | 1.22k | { |
289 | 1.22k | if (!s || !bmp) |
290 | 0 | return FALSE; |
291 | | |
292 | 1.22k | if (!Stream_EnsureRemainingCapacity(s, 12)) |
293 | 0 | return FALSE; |
294 | | |
295 | 1.22k | if (bmp->codecID > UINT8_MAX) |
296 | 0 | { |
297 | 0 | WLog_ERR(TAG, "Invalid TS_BITMAP_DATA_EX::codecID=0x%04" PRIx16 "", bmp->codecID); |
298 | 0 | return FALSE; |
299 | 0 | } |
300 | 1.22k | Stream_Write_UINT8(s, bmp->bpp); |
301 | 1.22k | Stream_Write_UINT8(s, bmp->flags); |
302 | 1.22k | Stream_Write_UINT8(s, 0); /* reserved1, reserved2 */ |
303 | 1.22k | Stream_Write_UINT8(s, (UINT8)bmp->codecID); |
304 | 1.22k | Stream_Write_UINT16(s, bmp->width); |
305 | 1.22k | Stream_Write_UINT16(s, bmp->height); |
306 | 1.22k | Stream_Write_UINT32(s, bmp->bitmapDataLength); |
307 | | |
308 | 1.22k | if (bmp->flags & EX_COMPRESSED_BITMAP_HEADER_PRESENT) |
309 | 986 | { |
310 | 986 | if (!update_write_surfcmd_bitmap_header_ex(s, &bmp->exBitmapDataHeader)) |
311 | 0 | return FALSE; |
312 | 986 | } |
313 | | |
314 | 1.22k | if (!Stream_EnsureRemainingCapacity(s, bmp->bitmapDataLength)) |
315 | 0 | return FALSE; |
316 | | |
317 | 1.22k | Stream_Write(s, bmp->bitmapData, bmp->bitmapDataLength); |
318 | 1.22k | return TRUE; |
319 | 1.22k | } |
320 | | |
321 | | BOOL update_write_surfcmd_surface_bits(wStream* s, const SURFACE_BITS_COMMAND* cmd) |
322 | 1.22k | { |
323 | 1.22k | if (!Stream_EnsureRemainingCapacity(s, SURFCMD_SURFACE_BITS_HEADER_LENGTH)) |
324 | 0 | return FALSE; |
325 | | |
326 | 1.22k | WINPR_ASSERT(cmd->cmdType <= UINT16_MAX); |
327 | 1.22k | UINT16 cmdType = (UINT16)cmd->cmdType; |
328 | 1.22k | switch (cmdType) |
329 | 1.22k | { |
330 | 1.06k | case CMDTYPE_SET_SURFACE_BITS: |
331 | 1.22k | case CMDTYPE_STREAM_SURFACE_BITS: |
332 | 1.22k | break; |
333 | 0 | default: |
334 | 0 | { |
335 | 0 | const UINT16 defaultCmdType = CMDTYPE_STREAM_SURFACE_BITS; |
336 | 0 | WLog_WARN(TAG, |
337 | 0 | "SURFACE_BITS_COMMAND->cmdType 0x%04" PRIx16 |
338 | 0 | " not allowed, correcting to 0x%04" PRIx16, |
339 | 0 | cmdType, defaultCmdType); |
340 | 0 | cmdType = defaultCmdType; |
341 | 0 | } |
342 | 0 | break; |
343 | 1.22k | } |
344 | | |
345 | 1.22k | Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, cmdType)); |
346 | 1.22k | Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, cmd->destLeft)); |
347 | 1.22k | Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, cmd->destTop)); |
348 | 1.22k | Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, cmd->destRight)); |
349 | 1.22k | Stream_Write_UINT16(s, WINPR_ASSERTING_INT_CAST(uint16_t, cmd->destBottom)); |
350 | 1.22k | return update_write_surfcmd_bitmap_ex(s, &cmd->bmp); |
351 | 1.22k | } |
352 | | |
353 | | BOOL update_write_surfcmd_frame_marker(wStream* s, UINT16 frameAction, UINT32 frameId) |
354 | 1.14k | { |
355 | 1.14k | if (!Stream_EnsureRemainingCapacity(s, SURFCMD_FRAME_MARKER_LENGTH)) |
356 | 0 | return FALSE; |
357 | | |
358 | 1.14k | Stream_Write_UINT16(s, CMDTYPE_FRAME_MARKER); |
359 | 1.14k | Stream_Write_UINT16(s, frameAction); |
360 | 1.14k | Stream_Write_UINT32(s, frameId); |
361 | 1.14k | return TRUE; |
362 | 1.14k | } |