1 /* Packet-rdp_egfx.c
2 * Routines for the EGFX RDP channel
3 * Copyright 2021, David Fort <contact@hardening-consulting.com>
4 *
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12 /*
13 * See: "[MS-RDPEGFX] "
14 */
15
16 #include "config.h"
17
18 #include <epan/packet.h>
19 #include <epan/prefs.h>
20 #include <epan/conversation.h>
21 #include <epan/expert.h>
22 #include <epan/tvbuff_rdp.h>
23
24 #include "packet-rdp.h"
25 #include "packet-rdpudp.h"
26
27 void proto_register_rdp_egfx(void);
28 void proto_reg_handoff_rdp_egfx(void);
29
30 static int proto_rdp_egfx = -1;
31
32 static int hf_egfx_cmdId = -1;
33 static int hf_egfx_flags = -1;
34 static int hf_egfx_pduLength = -1;
35
36 static int hf_egfx_caps_capsSetCount = -1;
37 static int hf_egfx_cap_version = -1;
38 static int hf_egfx_cap_length = -1;
39
40 static int hf_egfx_reset_width = -1;
41 static int hf_egfx_reset_height = -1;
42 static int hf_egfx_reset_monitorCount = -1;
43 static int hf_egfx_reset_monitorDefLeft = -1;
44 static int hf_egfx_reset_monitorDefTop = -1;
45 static int hf_egfx_reset_monitorDefRight = -1;
46 static int hf_egfx_reset_monitorDefBottom = -1;
47 static int hf_egfx_reset_monitorDefFlags = -1;
48
49
50 static int hf_egfx_ack_queue_depth = -1;
51 static int hf_egfx_ack_frame_id = -1;
52 static int hf_egfx_ack_total_decoded = -1;
53
54 static int hf_egfx_ackqoe_frame_id = -1;
55 static int hf_egfx_ackqoe_timestamp = -1;
56 static int hf_egfx_ackqoe_timediffse = -1;
57 static int hf_egfx_ackqoe_timediffedr = -1;
58
59 static int hf_egfx_start_timestamp = -1;
60 static int hf_egfx_start_frameid = -1;
61 static int hf_egfx_end_frameid = -1;
62
63
64 static int ett_rdp_egfx = -1;
65 static int ett_egfx_caps = -1;
66 static int ett_egfx_capsconfirm = -1;
67 static int ett_egfx_cap = -1;
68 static int ett_egfx_ack = -1;
69 static int ett_egfx_ackqoe = -1;
70 static int ett_egfx_reset = -1;
71 static int ett_egfx_monitors = -1;
72 static int ett_egfx_monitordef = -1;
73
74
75 static expert_field ei_egfx_pdulen_invalid = EI_INIT;
76 static expert_field ei_egfx_invalid_compression = EI_INIT;
77
78
79 #define PNAME "RDP Graphic pipeline channel Protocol"
80 #define PSNAME "EGFX"
81 #define PFNAME "rdp_egfx"
82
83 enum {
84 RDPGFX_CMDID_WIRETOSURFACE_1 = 0x0001,
85 RDPGFX_CMDID_WIRETOSURFACE_2 = 0x0002,
86 RDPGFX_CMDID_DELETEENCODINGCONTEXT = 0x0003,
87 RDPGFX_CMDID_SOLIDFILL = 0x0004,
88 RDPGFX_CMDID_SURFACETOSURFACE = 0x0005,
89 RDPGFX_CMDID_SURFACETOCACHE = 0x0006,
90 RDPGFX_CMDID_CACHETOSURFACE = 0x0007,
91 RDPGFX_CMDID_EVICTCACHEENTRY = 0x0008,
92 RDPGFX_CMDID_CREATESURFACE = 0x0009,
93 RDPGFX_CMDID_DELETESURFACE = 0x000a,
94 RDPGFX_CMDID_STARTFRAME = 0x000b,
95 RDPGFX_CMDID_ENDFRAME = 0x000c,
96 RDPGFX_CMDID_FRAMEACKNOWLEDGE = 0x000d,
97 RDPGFX_CMDID_RESETGRAPHICS = 0x000e,
98 RDPGFX_CMDID_MAPSURFACETOOUTPUT = 0x000f,
99 RDPGFX_CMDID_CACHEIMPORTOFFER = 0x0010,
100 RDPGFX_CMDID_CACHEIMPORTREPLY = 0x0011,
101 RDPGFX_CMDID_CAPSADVERTISE = 0x0012,
102 RDPGFX_CMDID_CAPSCONFIRM = 0x0013,
103 RDPGFX_CMDID_MAPSURFACETOWINDOW = 0x0015,
104 RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE = 0x0016,
105 RDPGFX_CMDID_MAPSURFACETOSCALEDOUTPUT = 0x0017,
106 RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW = 0x0018,
107 };
108
109 enum {
110 RDPGFX_CAPVERSION_8 = 0x00080004,
111 RDPGFX_CAPVERSION_81 = 0x00080105,
112 RDPGFX_CAPVERSION_101 = 0x000A0100,
113 RDPGFX_CAPVERSION_102 = 0x000A0200,
114 RDPGFX_CAPVERSION_103 = 0x000A0301,
115 RDPGFX_CAPVERSION_104 = 0x000A0400,
116 RDPGFX_CAPVERSION_105 = 0x000A0502,
117 RDPGFX_CAPVERSION_106 = 0x000A0600
118 };
119
120 static const value_string rdp_egfx_cmd_vals[] = {
121 { RDPGFX_CMDID_WIRETOSURFACE_1, "Wire to surface 1" },
122 { RDPGFX_CMDID_WIRETOSURFACE_2, "Wire to surface 2" },
123 { RDPGFX_CMDID_DELETEENCODINGCONTEXT, "delete encoding context" },
124 { RDPGFX_CMDID_SOLIDFILL, "Solid fill" },
125 { RDPGFX_CMDID_SURFACETOSURFACE, "Surface to surface" },
126 { RDPGFX_CMDID_SURFACETOCACHE, "Surface to cache" },
127 { RDPGFX_CMDID_CACHETOSURFACE, "Cache to surface" },
128 { RDPGFX_CMDID_EVICTCACHEENTRY, "Evict cache entry" },
129 { RDPGFX_CMDID_CREATESURFACE, "Create surface" },
130 { RDPGFX_CMDID_DELETESURFACE, "Delete surface" },
131 { RDPGFX_CMDID_STARTFRAME, "Start frame" },
132 { RDPGFX_CMDID_ENDFRAME, "End frame" },
133 { RDPGFX_CMDID_FRAMEACKNOWLEDGE, "Frame acknowlegde" },
134 { RDPGFX_CMDID_RESETGRAPHICS, "Reset graphics" },
135 { RDPGFX_CMDID_MAPSURFACETOOUTPUT, "Map Surface to output" },
136 { RDPGFX_CMDID_CACHEIMPORTOFFER, "Cache import offer" },
137 { RDPGFX_CMDID_CACHEIMPORTREPLY, "Cache import reply" },
138 { RDPGFX_CMDID_CAPSADVERTISE, "Caps advertise" },
139 { RDPGFX_CMDID_CAPSCONFIRM, "Caps confirm" },
140 { RDPGFX_CMDID_MAPSURFACETOWINDOW, "Map surface to window" },
141 { RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE, "Qoe frame acknowlegde" },
142 { RDPGFX_CMDID_MAPSURFACETOSCALEDOUTPUT, "Map surface to scaled output" },
143 { RDPGFX_CMDID_MAPSURFACETOSCALEDWINDOW, "Map surface to scaled window" },
144 { 0x0, NULL },
145 };
146
147 static const value_string rdp_egfx_caps_version_vals[] = {
148 { RDPGFX_CAPVERSION_8, "8" },
149 { RDPGFX_CAPVERSION_81, "8.1" } ,
150 { RDPGFX_CAPVERSION_101, "10.1" },
151 { RDPGFX_CAPVERSION_102, "10.2" },
152 { RDPGFX_CAPVERSION_103, "10.3" },
153 { RDPGFX_CAPVERSION_104, "10.4" },
154 { RDPGFX_CAPVERSION_105, "10.5" },
155 { RDPGFX_CAPVERSION_106, "10.6" },
156 { 0x0, NULL },
157 };
158
159 static const value_string rdp_egfx_monitor_flags_vals[] = {
160 { 0x00000000, "is secondary" },
161 { 0x00000001, "is primary" },
162 { 0x0, NULL },
163 };
164
165
166 typedef struct {
167 zgfx_context_t *zgfx;
168 } egfx_conv_info_t;
169
170
171 static egfx_conv_info_t *
egfx_get_conversation_data(packet_info * pinfo)172 egfx_get_conversation_data(packet_info *pinfo)
173 {
174 conversation_t *conversation, *conversation_tcp;
175 egfx_conv_info_t *info;
176
177 conversation = find_or_create_conversation(pinfo);
178
179 info = (egfx_conv_info_t *)conversation_get_proto_data(conversation, proto_rdp_egfx);
180 if (!info) {
181 conversation_tcp = rdp_find_tcp_conversation_from_udp(conversation);
182 if (conversation_tcp)
183 info = (egfx_conv_info_t *)conversation_get_proto_data(conversation_tcp, proto_rdp_egfx);
184 }
185
186 if (info == NULL) {
187 info = wmem_new0(wmem_file_scope(), egfx_conv_info_t);
188 info->zgfx = zgfx_context_new(wmem_file_scope());
189 conversation_add_proto_data(conversation, proto_rdp_egfx, info);
190 }
191
192 return info;
193 }
194
195
196 static int
dissect_rdp_egfx_payload(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,void * data _U_)197 dissect_rdp_egfx_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data _U_)
198 {
199 proto_item *item;
200 proto_tree *tree;
201 proto_tree *subtree;
202 gint offset = 0;
203 guint16 cmdId = 0;
204 guint32 pduLength;
205 guint32 i;
206
207
208 parent_tree = proto_tree_get_root(parent_tree);
209 col_set_str(pinfo->cinfo, COL_PROTOCOL, "EGFX");
210 col_clear(pinfo->cinfo, COL_INFO);
211
212 while (tvb_captured_length_remaining(tvb, offset) > 8) {
213 pduLength = tvb_get_guint32(tvb, offset + 4, ENC_LITTLE_ENDIAN);
214
215 item = proto_tree_add_item(parent_tree, proto_rdp_egfx, tvb, offset, pduLength, ENC_NA);
216 tree = proto_item_add_subtree(item, ett_rdp_egfx);
217
218 cmdId = tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN);
219 proto_tree_add_item(tree, hf_egfx_cmdId, tvb, offset, 2, ENC_LITTLE_ENDIAN);
220 offset += 2;
221
222 proto_tree_add_item(tree, hf_egfx_flags, tvb, offset, 2, ENC_LITTLE_ENDIAN);
223 offset += 2;
224
225 proto_tree_add_item(tree, hf_egfx_pduLength, tvb, offset, 4, ENC_LITTLE_ENDIAN);
226 offset += 4;
227
228 if (pduLength < 8) {
229 expert_add_info_format(pinfo, item, &ei_egfx_pdulen_invalid, "pduLength is %u, not < 8", pduLength);
230 return offset;
231 }
232
233 switch (cmdId) {
234 case RDPGFX_CMDID_CAPSADVERTISE: {
235 guint16 capsSetCount = tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN);
236
237 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "Caps advertise");
238 proto_tree_add_item(tree, hf_egfx_caps_capsSetCount, tvb, offset, 2, ENC_LITTLE_ENDIAN);
239
240 subtree = proto_tree_add_subtree(tree, tvb, offset, pduLength-8, ett_egfx_caps, NULL, "Caps");
241 offset += 2;
242
243 for (i = 0; i < capsSetCount; i++) {
244 guint32 capsDataLength;
245
246 proto_tree_add_item(subtree, hf_egfx_cap_version, tvb, offset, 4, ENC_LITTLE_ENDIAN);
247 offset += 4;
248
249 proto_tree_add_item(subtree, hf_egfx_cap_length, tvb, offset, 4, ENC_LITTLE_ENDIAN);
250 capsDataLength = tvb_get_guint32(tvb, offset, ENC_LITTLE_ENDIAN);
251 offset += 4;
252
253 offset += capsDataLength;
254 }
255 break;
256 }
257
258 case RDPGFX_CMDID_CAPSCONFIRM: {
259 guint32 capsDataLength;
260
261 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "Caps confirm");
262
263 subtree = proto_tree_add_subtree(tree, tvb, offset, pduLength-8, ett_egfx_capsconfirm, NULL, "Caps confirm");
264 proto_tree_add_item(subtree, hf_egfx_cap_version, tvb, offset, 4, ENC_LITTLE_ENDIAN);
265 offset += 4;
266
267 proto_tree_add_item_ret_uint(subtree, hf_egfx_cap_length, tvb, offset, 4, ENC_LITTLE_ENDIAN, &capsDataLength);
268 offset += 4 + capsDataLength;
269 break;
270 }
271
272 case RDPGFX_CMDID_RESETGRAPHICS: {
273 guint32 nmonitor;
274 proto_tree *monitors_tree;
275 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "Reset graphics");
276
277 subtree = proto_tree_add_subtree(tree, tvb, offset, pduLength-4, ett_egfx_reset, NULL, "Reset graphics");
278 proto_tree_add_item(subtree, hf_egfx_reset_width, tvb, offset, 4, ENC_LITTLE_ENDIAN);
279 offset += 4;
280
281 proto_tree_add_item(subtree, hf_egfx_reset_height, tvb, offset, 4, ENC_LITTLE_ENDIAN);
282 offset += 4;
283
284 proto_tree_add_item_ret_uint(subtree, hf_egfx_reset_monitorCount, tvb, offset, 4, ENC_LITTLE_ENDIAN, &nmonitor);
285 offset += 4;
286
287 monitors_tree = proto_tree_add_subtree(subtree, tvb, offset, nmonitor * 20, ett_egfx_monitors, NULL, "Monitors");
288 for (i = 0; i < nmonitor; i++) {
289 proto_item *monitor_tree;
290 guint32 left, top, right, bottom;
291 left = tvb_get_guint32(tvb, offset, ENC_LITTLE_ENDIAN);
292 top = tvb_get_guint32(tvb, offset+4, ENC_LITTLE_ENDIAN);
293 right = tvb_get_guint32(tvb, offset+8, ENC_LITTLE_ENDIAN);
294 bottom = tvb_get_guint32(tvb, offset+12, ENC_LITTLE_ENDIAN);
295
296 monitor_tree = proto_tree_add_subtree_format(monitors_tree, tvb, offset, 20, ett_egfx_monitordef, NULL,
297 "(%d,%d) - (%d,%d)", left, top, right, bottom);
298
299 proto_tree_add_item(monitor_tree, hf_egfx_reset_monitorDefLeft, tvb, offset, 4, ENC_LITTLE_ENDIAN);
300 offset += 4;
301
302 proto_tree_add_item(monitor_tree, hf_egfx_reset_monitorDefTop, tvb, offset, 4, ENC_LITTLE_ENDIAN);
303 offset += 4;
304
305 proto_tree_add_item(monitor_tree, hf_egfx_reset_monitorDefRight, tvb, offset, 4, ENC_LITTLE_ENDIAN);
306 offset += 4;
307
308 proto_tree_add_item(monitor_tree, hf_egfx_reset_monitorDefBottom, tvb, offset, 4, ENC_LITTLE_ENDIAN);
309 offset += 4;
310
311 proto_tree_add_item(monitor_tree, hf_egfx_reset_monitorDefFlags, tvb, offset, 4, ENC_LITTLE_ENDIAN);
312 offset += 4;
313 }
314
315 offset += (pduLength - 8);
316 break;
317 }
318
319 case RDPGFX_CMDID_STARTFRAME: {
320 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "Start frame");
321 proto_tree_add_item(tree, hf_egfx_start_timestamp, tvb, offset, 4, ENC_LITTLE_ENDIAN);
322 // TODO: dissect timestamp
323 offset += 4;
324
325 proto_tree_add_item(tree, hf_egfx_start_frameid, tvb, offset, 4, ENC_LITTLE_ENDIAN);
326 offset += 4;
327 break;
328 }
329
330 case RDPGFX_CMDID_ENDFRAME:
331 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "End frame");
332 proto_tree_add_item(tree, hf_egfx_end_frameid, tvb, offset, 4, ENC_LITTLE_ENDIAN);
333 offset += 4;
334 break;
335
336 case RDPGFX_CMDID_FRAMEACKNOWLEDGE:
337 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "Frame acknowledge");
338 subtree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_egfx_ack, NULL, "Frame acknowledge");
339 proto_tree_add_item(subtree, hf_egfx_ack_queue_depth, tvb, offset, 4, ENC_LITTLE_ENDIAN);
340 offset += 4;
341
342 proto_tree_add_item(subtree, hf_egfx_ack_frame_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
343 offset += 4;
344
345 proto_tree_add_item(subtree, hf_egfx_ack_total_decoded, tvb, offset, 4, ENC_LITTLE_ENDIAN);
346 offset += 4;
347 break;
348
349 case RDPGFX_CMDID_QOEFRAMEACKNOWLEDGE:
350 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "Frame acknowledge QoE");
351 subtree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_egfx_ackqoe, NULL, "Frame acknowledge QoE");
352 proto_tree_add_item(subtree, hf_egfx_ackqoe_frame_id, tvb, offset, 4, ENC_LITTLE_ENDIAN);
353 offset += 4;
354
355 proto_tree_add_item(subtree, hf_egfx_ackqoe_timestamp, tvb, offset, 4, ENC_LITTLE_ENDIAN);
356 offset += 4;
357
358 proto_tree_add_item(subtree, hf_egfx_ackqoe_timediffse, tvb, offset, 2, ENC_LITTLE_ENDIAN);
359 offset += 2;
360
361 proto_tree_add_item(subtree, hf_egfx_ackqoe_timediffedr, tvb, offset, 2, ENC_LITTLE_ENDIAN);
362 offset += 2;
363 break;
364
365 default:
366 offset += (pduLength - 8);
367 break;
368 }
369 }
370 return offset;
371 }
372
373 static int
dissect_rdp_egfx(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,void * data)374 dissect_rdp_egfx(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data)
375 {
376 tvbuff_t *work_tvb = tvb;
377
378 col_set_str(pinfo->cinfo, COL_PROTOCOL, "EGFX");
379 col_clear(pinfo->cinfo, COL_INFO);
380
381 parent_tree = proto_tree_get_root(parent_tree);
382
383 if (!rdp_isServerAddressTarget(pinfo)) {
384 egfx_conv_info_t *infos = egfx_get_conversation_data(pinfo);
385 work_tvb = rdp8_decompress(infos->zgfx, wmem_packet_scope(), tvb, 0);
386 if (!work_tvb && parent_tree) {
387 expert_add_info_format(pinfo, parent_tree->last_child, &ei_egfx_invalid_compression, "invalid compression");
388 return 0;
389 }
390 add_new_data_source(pinfo, work_tvb, "Uncompressed GFX");
391 }
392
393 dissect_rdp_egfx_payload(work_tvb, pinfo, parent_tree, data);
394 return tvb_reported_length(tvb);
395 }
396
397
proto_register_rdp_egfx(void)398 void proto_register_rdp_egfx(void) {
399 static hf_register_info hf[] = {
400 { &hf_egfx_cmdId,
401 { "CmdId", "rdp_egfx.cmdid",
402 FT_UINT16, BASE_HEX, VALS(rdp_egfx_cmd_vals), 0x0,
403 NULL, HFILL }
404 },
405 { &hf_egfx_flags,
406 { "flags", "rdp_egfx.flags",
407 FT_UINT16, BASE_HEX, NULL, 0x0,
408 NULL, HFILL }
409 },
410 { &hf_egfx_pduLength,
411 { "pduLength", "rdp_egfx.pdulength",
412 FT_UINT32, BASE_DEC, NULL, 0x0,
413 NULL, HFILL }
414 },
415 { &hf_egfx_caps_capsSetCount,
416 { "capsSetCount", "rdp_egfx.caps.setcount",
417 FT_UINT16, BASE_DEC, NULL, 0x0,
418 NULL, HFILL }
419 },
420 { &hf_egfx_cap_version,
421 { "Version", "rdp_egfx.cap.version",
422 FT_UINT32, BASE_HEX, VALS(rdp_egfx_caps_version_vals), 0x0,
423 NULL, HFILL }
424 },
425 { &hf_egfx_cap_length,
426 { "capsDataLength", "rdp_egfx.cap.length",
427 FT_UINT32, BASE_DEC, NULL, 0x0,
428 NULL, HFILL }
429 },
430 { &hf_egfx_ack_queue_depth,
431 { "queueDepth", "rdp_egfx.ack.queuedepth",
432 FT_UINT32, BASE_DEC, NULL, 0x0,
433 NULL, HFILL }
434 },
435 { &hf_egfx_ack_frame_id,
436 { "frameId", "rdp_egfx.ack.frameid",
437 FT_UINT32, BASE_HEX, NULL, 0x0,
438 NULL, HFILL }
439 },
440 { &hf_egfx_ack_total_decoded,
441 { "Total frames decoded", "rdp_egfx.ack.totalframesdecoded",
442 FT_UINT32, BASE_DEC, NULL, 0x0,
443 NULL, HFILL }
444 },
445 { &hf_egfx_ackqoe_frame_id,
446 { "frameId", "rdp_egfx.ackqoe.frameid",
447 FT_UINT32, BASE_HEX, NULL, 0x0,
448 NULL, HFILL }
449 },
450 { &hf_egfx_ackqoe_timestamp,
451 { "Timestamp", "rdp_egfx.ackqoe.timestamp",
452 FT_UINT32, BASE_DEC, NULL, 0x0,
453 NULL, HFILL }
454 },
455 { &hf_egfx_ackqoe_timediffse,
456 { "TimeDiffSE", "rdp_egfx.ackqoe.timediffse",
457 FT_UINT16, BASE_DEC, NULL, 0x0,
458 NULL, HFILL }
459 },
460 { &hf_egfx_ackqoe_timediffedr,
461 { "TimeDiffEDR", "rdp_egfx.ackqoe.timediffedr",
462 FT_UINT16, BASE_DEC, NULL, 0x0,
463 NULL, HFILL }
464 },
465 { &hf_egfx_reset_width,
466 { "Width", "rdp_egfx.reset.width",
467 FT_UINT32, BASE_DEC, NULL, 0x0,
468 NULL, HFILL }
469 },
470 { &hf_egfx_reset_height,
471 { "Height", "rdp_egfx.reset.height",
472 FT_UINT32, BASE_DEC, NULL, 0x0,
473 NULL, HFILL }
474 },
475 { &hf_egfx_reset_monitorCount,
476 { "Monitor count", "rdp_egfx.reset.monitorcount",
477 FT_UINT32, BASE_DEC, NULL, 0x0,
478 NULL, HFILL }
479 },
480 { &hf_egfx_reset_monitorDefLeft,
481 { "Left", "rdp_egfx.monitor.left",
482 FT_UINT32, BASE_DEC, NULL, 0x0,
483 NULL, HFILL }
484 },
485 { &hf_egfx_reset_monitorDefTop,
486 { "Top", "rdp_egfx.monitor.top",
487 FT_UINT32, BASE_DEC, NULL, 0x0,
488 NULL, HFILL }
489 },
490 { &hf_egfx_reset_monitorDefRight,
491 { "Right", "rdp_egfx.monitor.right",
492 FT_UINT32, BASE_DEC, NULL, 0x0,
493 NULL, HFILL }
494 },
495 { &hf_egfx_reset_monitorDefBottom,
496 { "Bottom", "rdp_egfx.monitor.bottom",
497 FT_UINT32, BASE_DEC, NULL, 0x0,
498 NULL, HFILL }
499 },
500 { &hf_egfx_reset_monitorDefFlags,
501 { "Flags", "rdp_egfx.monitor.flags",
502 FT_UINT32, BASE_DEC, VALS(rdp_egfx_monitor_flags_vals), 0x0,
503 NULL, HFILL }
504 },
505 { &hf_egfx_start_timestamp,
506 { "Timestamp", "rdp_egfx.startframe.timestamp",
507 FT_UINT32, BASE_DEC, NULL, 0x0,
508 NULL, HFILL }
509 },
510 { &hf_egfx_start_frameid,
511 { "Frame id", "rdp_egfx.startframe.frameid",
512 FT_UINT32, BASE_HEX, NULL, 0x0,
513 NULL, HFILL }
514 },
515 { &hf_egfx_end_frameid,
516 { "Frame id", "rdp_egfx.endframe.frameid",
517 FT_UINT32, BASE_HEX, NULL, 0x0,
518 NULL, HFILL }
519 },
520 };
521
522 static gint *ett[] = {
523 &ett_rdp_egfx,
524 &ett_egfx_caps,
525 &ett_egfx_cap,
526 &ett_egfx_ack,
527 &ett_egfx_ackqoe,
528 &ett_egfx_reset,
529 &ett_egfx_capsconfirm,
530 &ett_egfx_monitors,
531 &ett_egfx_monitordef,
532 };
533
534 static ei_register_info ei[] = {
535 { &ei_egfx_pdulen_invalid, { "rdp_egfx.pdulength.invalid", PI_PROTOCOL, PI_ERROR, "Invalid length", EXPFILL }},
536 { &ei_egfx_invalid_compression, { "rdp_egfx.compression.invalid", PI_PROTOCOL, PI_ERROR, "Invalid compression", EXPFILL }},
537 };
538 expert_module_t* expert_egfx;
539
540
541 proto_rdp_egfx = proto_register_protocol(PNAME, PSNAME, PFNAME);
542 /* Register fields and subtrees */
543 proto_register_field_array(proto_rdp_egfx, hf, array_length(hf));
544 proto_register_subtree_array(ett, array_length(ett));
545 expert_egfx = expert_register_protocol(proto_rdp_egfx);
546 expert_register_field_array(expert_egfx, ei, array_length(ei));
547
548 register_dissector("rdp_egfx", dissect_rdp_egfx, proto_rdp_egfx);
549 }
550
proto_reg_handoff_rdp_egfx(void)551 void proto_reg_handoff_rdp_egfx(void) {
552 }
553