Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-rdpudp.c
blobcf20f2b676825806f49587ea9cad080d51a642e8
1 /* packet-rdpudp.c
2 * Routines for UDP RDP packet dissection
3 * Copyright 2021, David Fort
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include "config.h"
14 #include <epan/packet.h>
15 #include <epan/prefs.h>
16 #include <epan/expert.h>
17 #include <epan/conversation.h>
18 #include <epan/proto_data.h>
20 #include "packet-rdp.h"
21 #include "packet-rdpudp.h"
23 #define PNAME "UDP Remote Desktop Protocol"
24 #define PSNAME "RDPUDP"
25 #define PFNAME "rdpudp"
27 void proto_register_rdpudp(void);
28 void proto_reg_handoff_rdpudp(void);
30 static dissector_handle_t rdpudp_handle;
31 int proto_rdpudp;
33 static int ett_rdpudp;
34 static int ett_rdpudp_flags;
35 static int ett_rdpudp_synex;
36 static int ett_rdpudp_ack;
37 static int ett_rdpudp_fec;
38 static int ett_rdpudp_data;
39 static int ett_rdpudp2_packetType;
40 static int ett_rdpudp2_flags;
41 static int ett_rdpudp2_ack;
42 static int ett_rdpudp2_overhead;
43 static int ett_rdpudp2_delayack;
44 static int ett_rdpudp2_aoa;
45 static int ett_rdpudp2_data;
46 static int ett_rdpudp2_ackvec;
47 static int ett_rdpudp2_ackvec_vecs;
48 static int ett_rdpudp2_ackvec_vec;
51 static int hf_rdpudp_snSourceAck;
52 static int hf_rdpudp_ReceiveWindowSize;
53 static int hf_rdpudp_flags;
54 static int hf_rdpudp_flag_syn;
55 static int hf_rdpudp_flag_fin;
56 static int hf_rdpudp_flag_ack;
57 static int hf_rdpudp_flag_data;
58 static int hf_rdpudp_flag_fec;
59 static int hf_rdpudp_flag_cn;
60 static int hf_rdpudp_flag_cwr;
61 static int hf_rdpudp_flag_aoa;
62 static int hf_rdpudp_flag_synlossy;
63 static int hf_rdpudp_flag_ackdelayed;
64 static int hf_rdpudp_flag_correlationId;
65 static int hf_rdpudp_flag_synex;
66 static int hf_rdpudp_snInitialSequenceNumber;
67 static int hf_rdpudp_upstreamMtu;
68 static int hf_rdpudp_downstreamMtu;
69 static int hf_rdpudp_correlationId;
70 static int hf_rdpudp_synex_flags;
71 static int hf_rdpudp_synex_flag_version;
72 static int hf_rdpudp_synex_version;
73 static int hf_rdpudp_synex_cookiehash;
74 static int hf_rdpudp_ack_vectorsize;
75 static int hf_rdpudp_ack_item;
76 static int hf_rdpudp_ack_item_state;
77 static int hf_rdpudp_ack_item_rle;
78 static int hf_rdpudp_fec_coded;
79 static int hf_rdpudp_fec_sourcestart;
80 static int hf_rdpudp_fec_range;
81 static int hf_rdpudp_fec_fecindex;
82 static int hf_rdpudp_resetseqenum;
83 static int hf_rdpudp_source_sncoded;
84 static int hf_rdpudp_source_snSourceStart;
85 static int hf_rdpudp_data;
87 static int * const rdpudp_flags[] = {
88 &hf_rdpudp_flag_syn,
89 &hf_rdpudp_flag_fin,
90 &hf_rdpudp_flag_ack,
91 &hf_rdpudp_flag_data,
92 &hf_rdpudp_flag_fec,
93 &hf_rdpudp_flag_cn,
94 &hf_rdpudp_flag_cwr,
95 &hf_rdpudp_flag_aoa,
96 &hf_rdpudp_flag_synlossy,
97 &hf_rdpudp_flag_ackdelayed,
98 &hf_rdpudp_flag_correlationId,
99 &hf_rdpudp_flag_synex,
100 NULL
103 static int hf_rdpudp2_PacketPrefixByte;
104 static int hf_rdpudp2_packetType;
105 static int hf_rdpudp2_shortPacketLength;
106 static int hf_rdpudp2_flags;
107 static int hf_rdpudp2_flag_ack;
108 static int hf_rdpudp2_flag_data;
109 static int hf_rdpudp2_flag_ackvec;
110 static int hf_rdpudp2_flag_aoa;
111 static int hf_rdpudp2_flag_overhead;
112 static int hf_rdpudp2_flag_delayackinfo;
113 static int hf_rdpudp2_logWindow;
114 static int hf_rdpudp2_AckSeq;
115 static int hf_rdpudp2_AckTs;
116 static int hf_rdpudp2_AckSendTimeGap;
117 static int hf_rdpudp2_ndelayedAcks;
118 static int hf_rdpudp2_delayedTimeScale;
119 static int hf_rdpudp2_delayedAcks;
120 static int hf_rdpudp2_delayedAck;
121 static int hf_rdpudp2_OverHeadSize;
122 static int hf_rdpudp2_DelayAckMax;
123 static int hf_rdpudp2_DelayAckTimeout;
124 static int hf_rdpudp2_AckOfAcksSeqNum;
125 static int hf_rdpudp2_DataSeqNumber;
126 static int hf_rdpudp2_DataFullSeqNumber;
127 static int hf_rdpudp2_DataChannelSeqNumber;
128 static int hf_rdpudp2_DataChannelFullSeqNumber;
129 static int hf_rdpudp2_Data;
130 static int hf_rdpudp2_AckvecBaseSeq;
131 static int hf_rdpudp2_AckvecCodecAckVecSize;
132 static int hf_rdpudp2_AckvecHaveTs;
133 static int hf_rdpudp2_AckvecTimeStamp;
134 static int hf_rdpudp2_SendAckTimeGapInMs;
135 static int hf_rdpudp2_AckvecCodedAck;
136 static int hf_rdpudp2_AckvecCodedAckMode;
137 static int hf_rdpudp2_AckvecCodedAckRleState;
138 static int hf_rdpudp2_AckvecCodedAckRleLen;
140 static int * const rdpudp2_flags[] = {
141 &hf_rdpudp2_flag_ack,
142 &hf_rdpudp2_flag_data,
143 &hf_rdpudp2_flag_ackvec,
144 &hf_rdpudp2_flag_aoa,
145 &hf_rdpudp2_flag_overhead,
146 &hf_rdpudp2_flag_delayackinfo,
147 &hf_rdpudp2_logWindow,
148 NULL
151 static dissector_handle_t tls_handle;
152 static dissector_handle_t dtls_handle;
154 enum {
155 RDPUDP_FULL_DATA_SEQ_KEY = 1,
156 RDPUDP_FULL_CHANNEL_SEQ_KEY = 2
159 enum {
160 RDPUDP_SYN = 0x0001,
161 RDPUDP_FIN = 0x0002,
162 RDPUDP_ACK = 0x0004,
163 RDPUDP_DATA = 0x0008,
164 RDPUDP_FEC = 0x0010,
165 RDPUDP_CN = 0x0020,
166 RDPUDP_CWR = 0x0040,
167 RDPUDP_AOA = 0x0100,
168 RDPUDP_SYNLOSSY = 0x0200,
169 RDPUDP_ACKDELAYED = 0x0400,
170 RDPUDP_CORRELATIONID = 0x0800,
171 RDPUDP_SYNEX = 0x1000
174 #define RDPUDP_VERSION_INFO_VALID 0x0001
176 enum {
177 RDPUDP2_ACK = 0x0001,
178 RDPUDP2_DATA = 0x0004,
179 RDPUDP2_ACKVEC = 0x0008,
180 RDPUDP2_AOA = 0x0010,
181 RDPUDP2_OVERHEAD = 0x0040,
182 RDPUDP2_DELAYACK = 0x0100
185 static const value_string rdpudp_version_vals[] = {
186 { 0x0001, "UDPv1-1" },
187 { 0x0002, "UDPv1-2" },
188 { 0x0101, "UDPv2" },
189 { 0x0, NULL}
192 static const value_string rdpudp_ack_states_vals[] = {
193 { 0, "Received" },
194 { 1, "Reserved 1" },
195 { 2, "Reserved 2" },
196 { 3, "Pending" },
197 { 0x0, NULL }
200 static const value_string rdpudp2_packetType_vals[] = {
201 { 0, "Data" },
202 { 8, "Dummy"},
203 { 0x0, NULL }
206 static const value_string rdpudp2_ackvec_mode_vals[] = {
207 { 0x00, "Bitmap"},
208 { 0x01, "Run length"},
209 { 0x00, NULL }
212 static const value_string rdpudp2_ackvec_rlestates_vals[] = {
213 { 0x00, "lost" },
214 { 0x01, "received" },
215 { 0x0, NULL }
218 static bool
219 rdpudp_chunk_free_cb(const void *key _U_, void *value, void *userdata _U_)
221 tvbuff_t *tvb = (tvbuff_t*)value;
223 tvb_free(tvb);
224 return false;
227 static bool
228 rdpudp_info_free_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event _U_,
229 void *user_data)
231 rdpudp_conv_info_t *rdpudp_info = (rdpudp_conv_info_t*)user_data;
233 wmem_tree_foreach(rdpudp_info->client_chunks, rdpudp_chunk_free_cb, NULL);
234 wmem_tree_foreach(rdpudp_info->server_chunks, rdpudp_chunk_free_cb, NULL);
236 return false;
239 bool
240 rdp_isServerAddressTarget(packet_info *pinfo)
242 conversation_t *conv;
243 rdp_conv_info_t *rdp_info;
244 rdpudp_conv_info_t *rdpudp_info;
246 conv = find_conversation_pinfo(pinfo, 0);
247 if (!conv)
248 return false;
250 rdp_info = (rdp_conv_info_t *)conversation_get_proto_data(conv, proto_rdp);
251 if (rdp_info) {
252 rdp_server_address_t *server = &rdp_info->serverAddr;
253 return addresses_equal(&server->addr, &pinfo->dst) && (pinfo->destport == server->port);
256 rdpudp_info = (rdpudp_conv_info_t *)conversation_get_proto_data(conv, proto_rdpudp);
257 if (!rdpudp_info)
258 return false;
260 return addresses_equal(&rdpudp_info->server_addr, &pinfo->dst) && (rdpudp_info->server_port == pinfo->destport);
263 bool
264 rdpudp_is_reliable_transport(packet_info *pinfo)
266 conversation_t *conv;
267 rdpudp_conv_info_t *rdpudp_info;
269 conv = find_conversation_pinfo(pinfo, 0);
270 if (!conv)
271 return false;
273 rdpudp_info = (rdpudp_conv_info_t *)conversation_get_proto_data(conv, proto_rdpudp);
274 if (!rdpudp_info)
275 return false;
277 return !rdpudp_info->is_lossy;
280 static int
281 dissect_rdpudp_v1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, rdpudp_conv_info_t *conv)
283 int offset = 0;
284 uint16_t flags;
286 col_set_str(pinfo->cinfo, COL_PROTOCOL, "RDPUDP");
287 col_clear(pinfo->cinfo, COL_INFO);
289 proto_tree_add_item(tree, hf_rdpudp_snSourceAck, tvb, offset, 4, ENC_BIG_ENDIAN);
290 offset += 4;
292 proto_tree_add_item(tree, hf_rdpudp_ReceiveWindowSize, tvb, offset, 2, ENC_BIG_ENDIAN);
293 offset += 2;
295 proto_tree_add_bitmask(tree, tvb, offset, hf_rdpudp_flags, ett_rdpudp_flags, rdpudp_flags, ENC_BIG_ENDIAN);
296 flags = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN);
297 offset += 2;
299 if (flags & RDPUDP_SYN) {
300 conv->is_lossy = (flags & RDPUDP_SYNLOSSY);
301 if (!(flags & RDPUDP_ACK)) {
302 /* set the server address only on the first SYN packet */
303 copy_address_wmem(wmem_file_scope(), &conv->server_addr, &pinfo->dst);
304 conv->server_port = pinfo->destport;
306 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "SYN");
309 if (flags & RDPUDP_SYN) {
310 proto_tree_add_item(tree, hf_rdpudp_snInitialSequenceNumber, tvb, offset, 4, ENC_BIG_ENDIAN);
311 offset += 4;
312 proto_tree_add_item(tree, hf_rdpudp_upstreamMtu, tvb, offset, 2, ENC_BIG_ENDIAN);
313 offset += 2;
314 proto_tree_add_item(tree, hf_rdpudp_downstreamMtu, tvb, offset, 2, ENC_BIG_ENDIAN);
315 offset += 2;
318 if (flags & RDPUDP_CORRELATIONID) {
319 proto_tree_add_item(tree, hf_rdpudp_correlationId, tvb, offset, 16, ENC_NA);
320 offset += 32;
321 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "CORRELATIONID");
324 if (flags & RDPUDP_SYNEX) {
325 uint16_t synex_flags;
326 proto_tree *synex_tree;
327 unsigned synex_sz = 2;
328 uint16_t version_val;
330 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "SYNEX");
332 synex_flags = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN);
333 if (synex_flags & RDPUDP_VERSION_INFO_VALID) {
334 synex_sz += 2; /* version */
336 version_val = tvb_get_uint16(tvb, offset+2, ENC_BIG_ENDIAN);
338 if (version_val == 0x101)
339 synex_sz += 32; /* cookie hash */
342 synex_tree = proto_tree_add_subtree(tree, tvb, offset, synex_sz, ett_rdpudp_synex, NULL, "SynEx");
343 proto_tree_add_item(synex_tree, hf_rdpudp_synex_flags, tvb, offset, 2, ENC_BIG_ENDIAN);
344 proto_tree_add_item(synex_tree, hf_rdpudp_synex_flag_version, tvb, offset, 2, ENC_BIG_ENDIAN);
346 offset += 2;
348 if (synex_flags & RDPUDP_VERSION_INFO_VALID) {
349 proto_tree_add_item(synex_tree, hf_rdpudp_synex_version, tvb, offset, 2, ENC_BIG_ENDIAN);
351 offset += 2;
352 if (version_val == 0x101) {
353 proto_tree_add_item(synex_tree, hf_rdpudp_synex_cookiehash, tvb, offset, 32, ENC_NA);
354 offset += 32;
356 if (flags & RDPUDP_ACK)
357 conv->start_v2_at = pinfo->num + 1;
362 if ((flags & RDPUDP_ACK) && !(flags & RDPUDP_SYN)) {
363 proto_tree *ack_tree;
364 uint16_t uAckVectorSize = tvb_get_uint16(tvb, offset, ENC_BIG_ENDIAN);
366 ack_tree = proto_tree_add_subtree(tree, tvb, offset, 2 + uAckVectorSize, ett_rdpudp_ack, NULL, "Ack");
367 offset += 2;
368 for ( ; uAckVectorSize; uAckVectorSize--, offset++) {
369 proto_tree_add_item(ack_tree, hf_rdpudp_ack_item, tvb, offset, 1, ENC_BIG_ENDIAN);
370 proto_tree_add_item(ack_tree, hf_rdpudp_ack_item_rle, tvb, offset, 1, ENC_BIG_ENDIAN);
372 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "ACK");
375 if (flags & RDPUDP_FEC) {
376 proto_tree *fec_tree = proto_tree_add_subtree(tree, tvb, offset, 4 * 3, ett_rdpudp_fec, NULL, "FEC");
378 proto_tree_add_item(fec_tree, hf_rdpudp_fec_coded, tvb, offset, 4, ENC_BIG_ENDIAN);
379 offset += 4;
380 proto_tree_add_item(fec_tree, hf_rdpudp_fec_sourcestart, tvb, offset, 4, ENC_BIG_ENDIAN);
381 offset += 4;
382 proto_tree_add_item(fec_tree, hf_rdpudp_fec_range, tvb, offset, 1, ENC_BIG_ENDIAN);
383 offset += 1;
384 proto_tree_add_item(fec_tree, hf_rdpudp_fec_fecindex, tvb, offset, 1, ENC_BIG_ENDIAN);
385 offset += 1;
387 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "FEC");
390 if (flags & RDPUDP_AOA) {
391 proto_tree_add_item(tree, hf_rdpudp_resetseqenum, tvb, offset, 4, ENC_BIG_ENDIAN);
392 offset += 4;
393 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "AOA");
396 if (flags & RDPUDP_DATA)
397 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "DATA");
399 if (flags & RDPUDP_DATA) {
400 proto_tree *data_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_rdpudp_data, NULL, "Data");
401 dissector_handle_t target_dissector;
403 proto_tree_add_item(data_tree, hf_rdpudp_source_sncoded, tvb, offset, 4, ENC_BIG_ENDIAN);
404 offset += 4;
406 proto_tree_add_item(data_tree, hf_rdpudp_source_snSourceStart, tvb, offset, 4, ENC_BIG_ENDIAN);
407 offset += 4;
409 target_dissector = conv->is_lossy ? dtls_handle : tls_handle;
411 call_dissector(target_dissector, tvb_new_subset_remaining(tvb, offset), pinfo, data_tree);
413 offset = tvb_reported_length(tvb);
416 return offset;
419 static tvbuff_t *
420 unwrap_udp_v2(tvbuff_t *tvb, packet_info *pinfo)
422 int len = tvb_captured_length_remaining(tvb, 0);
423 unsigned char *buffer = (unsigned char*)wmem_alloc(pinfo->pool, len);
425 /* copy and do the swap of byte 0 and 7*/
426 tvb_memcpy(tvb, buffer, 0, len);
427 buffer[0] = tvb_get_uint8(tvb, 7);
428 buffer[7] = tvb_get_uint8(tvb, 0);
430 return tvb_new_child_real_data(tvb, buffer, len, len);
433 static uint64_t
434 computeAndUpdateSeqContext(rdpudp_seq_context_t *context, uint16_t seq)
436 uint16_t diff = (context->last_received > seq) ? (context->last_received - seq) : (seq - context->last_received);
439 if (diff < 8000) {
440 /* not too much difference between last and seq, so we keep the same base
441 * seq seq
442 * | |
443 * [0 ...................... 0xffff]
445 * last
447 if (seq > context->last_received)
448 context->last_received = seq;
449 return (context->current_base + seq);
452 /* when diff is bigger than 8000 that means that either we've just switched
453 * the base, or that it is a sequence number from the previous base
455 if (seq < context->last_received) {
456 /* in this case we have
457 * [0 ...................... 0xffff]
458 * | |
459 * seq last
461 * so the new sequence number is in fact after last_received: we've just
462 * switched the base
464 context->last_received = seq;
465 context->current_base += 0x10000;
466 return (context->current_base + seq);
469 /* this is a sequence number from the previous base
471 * [0 ........................ 0xffff]
472 * | |
473 * last seq
475 return (context->current_base + seq - 0x10000);
478 static int
479 dissect_rdpudp_v2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, rdpudp_conv_info_t *rdpudp)
481 proto_item *item;
482 proto_tree *subtree, *data_tree = NULL;
483 uint16_t flags;
484 uint8_t packet_type;
485 tvbuff_t *subtvb;
486 int offset = 0;
487 tvbuff_t *tvb2 = unwrap_udp_v2(tvb, pinfo);
489 col_set_str(pinfo->cinfo, COL_PROTOCOL, "RDPUDP2");
490 col_clear(pinfo->cinfo, COL_INFO);
492 add_new_data_source(pinfo, tvb2, "Unwrapped RDPUDP2 packet");
494 packet_type = (tvb_get_uint8(tvb2, 0) >> 1) & 0xf;
495 item = proto_tree_add_item(tree, hf_rdpudp2_PacketPrefixByte, tvb2, offset, 1, ENC_LITTLE_ENDIAN);
496 subtree = proto_item_add_subtree(item, ett_rdpudp2_packetType);
497 proto_tree_add_item(subtree, hf_rdpudp2_packetType, tvb2, offset, 1, ENC_LITTLE_ENDIAN);
498 proto_tree_add_item(subtree, hf_rdpudp2_shortPacketLength, tvb2, offset, 1, ENC_LITTLE_ENDIAN);
499 offset++;
501 proto_tree_add_bitmask(tree, tvb2, offset, hf_rdpudp2_flags, ett_rdpudp2_flags, rdpudp2_flags, ENC_LITTLE_ENDIAN);
503 flags = tvb_get_uint16(tvb2, offset, ENC_LITTLE_ENDIAN);
504 offset += 2;
506 if (flags & RDPUDP2_ACK) {
507 uint8_t nacks = tvb_get_uint8(tvb, offset + 6) & 0xf;
508 subtree = proto_tree_add_subtree(tree, tvb2, offset, 7 + nacks, ett_rdpudp2_ack, NULL, "Ack");
509 proto_tree_add_item(subtree, hf_rdpudp2_AckSeq, tvb2, offset, 2, ENC_LITTLE_ENDIAN); offset += 2;
510 proto_tree_add_item(subtree, hf_rdpudp2_AckTs, tvb2, offset, 3, ENC_LITTLE_ENDIAN); offset += 3;
511 proto_tree_add_item(subtree, hf_rdpudp2_AckSendTimeGap, tvb2, offset, 1, ENC_LITTLE_ENDIAN); offset++;
513 proto_tree_add_item(subtree, hf_rdpudp2_ndelayedAcks, tvb2, offset, 1, ENC_LITTLE_ENDIAN);
514 proto_tree_add_item(subtree, hf_rdpudp2_delayedTimeScale, tvb2, offset, 1, ENC_LITTLE_ENDIAN);
515 offset++;
517 offset += nacks;
518 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "ACK");
521 if (flags & RDPUDP2_OVERHEAD) {
522 subtree = proto_tree_add_subtree(tree, tvb2, offset, 1, ett_rdpudp2_overhead, NULL, "Overhead");
523 proto_tree_add_item(subtree, hf_rdpudp2_OverHeadSize, tvb2, offset, 1, ENC_LITTLE_ENDIAN);
524 offset++;
525 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "OVERHEAD");
529 if (flags & RDPUDP2_DELAYACK) {
530 subtree = proto_tree_add_subtree(tree, tvb2, offset, 3, ett_rdpudp2_delayack, NULL, "DelayAck");
531 proto_tree_add_item(subtree, hf_rdpudp2_DelayAckMax, tvb2, offset, 1, ENC_LITTLE_ENDIAN);
532 offset++;
533 proto_tree_add_item(subtree, hf_rdpudp2_DelayAckTimeout, tvb2, offset, 2, ENC_LITTLE_ENDIAN);
534 offset += 2;
536 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "DELAYACK");
539 if (flags & RDPUDP2_AOA) {
540 subtree = proto_tree_add_subtree(tree, tvb2, offset, 1, ett_rdpudp2_aoa, NULL, "Ack of Acks");
541 proto_tree_add_item(subtree, hf_rdpudp2_AckOfAcksSeqNum, tvb2, offset, 2, ENC_LITTLE_ENDIAN);
542 offset += 2;
543 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "AOA");
546 if (flags & RDPUDP2_DATA) {
547 uint32_t rawSeq;
548 uint64_t *seqPtr;
549 bool is_server_target = rdp_isServerAddressTarget(pinfo);
550 rdpudp_seq_context_t *target_seq_context = is_server_target ? &rdpudp->client_data_seq : &rdpudp->server_data_seq;
552 bool isDummy = !!(packet_type == 0x8);
553 data_tree = proto_tree_add_subtree(tree, tvb2, offset, 1, ett_rdpudp2_data, NULL, isDummy ? "Dummy data" : "Data");
554 proto_tree_add_item_ret_uint(data_tree, hf_rdpudp2_DataSeqNumber, tvb2, offset, 2, ENC_LITTLE_ENDIAN, &rawSeq);
556 if (!PINFO_FD_VISITED(pinfo)) {
557 seqPtr = wmem_alloc(wmem_file_scope(), sizeof(*seqPtr));
558 *seqPtr = computeAndUpdateSeqContext(target_seq_context, rawSeq);
560 p_set_proto_data(wmem_file_scope(), pinfo, proto_rdpudp, RDPUDP_FULL_DATA_SEQ_KEY, seqPtr);
561 } else {
562 seqPtr = (uint64_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_rdpudp, RDPUDP_FULL_DATA_SEQ_KEY);
564 proto_item_set_generated(
565 proto_tree_add_uint(data_tree, hf_rdpudp2_DataFullSeqNumber, tvb2, offset, 2, (uint32_t)*seqPtr)
568 offset += 2;
570 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", isDummy ? "DUMMY" : "DATA");
573 if (flags & RDPUDP2_ACKVEC) {
574 proto_tree *acks_tree;
575 uint8_t i;
576 uint32_t base_seq;
577 int ackvecSz = 3;
578 uint8_t codedAckVecSizeA = tvb_get_uint8(tvb2, offset + 2);
579 uint8_t codedAckVecSize = codedAckVecSizeA & 0x7f;
580 bool haveTs = !!(codedAckVecSizeA & 0x80);
582 ackvecSz += codedAckVecSize;
583 if (haveTs)
584 ackvecSz += 3;
586 subtree = proto_tree_add_subtree(tree, tvb2, offset, ackvecSz, ett_rdpudp2_ackvec, NULL, "AckVec");
587 proto_tree_add_item_ret_uint(subtree, hf_rdpudp2_AckvecBaseSeq, tvb2, offset, 2, ENC_LITTLE_ENDIAN, &base_seq);
588 offset += 2;
590 proto_tree_add_item(subtree, hf_rdpudp2_AckvecCodecAckVecSize, tvb2, offset, 1, ENC_LITTLE_ENDIAN);
591 proto_tree_add_item(subtree, hf_rdpudp2_AckvecHaveTs, tvb2, offset, 1, ENC_LITTLE_ENDIAN);
592 offset++;
594 if (haveTs) {
595 proto_tree_add_item(subtree, hf_rdpudp2_AckvecTimeStamp, tvb2, offset, 3, ENC_LITTLE_ENDIAN);
596 offset += 3;
598 proto_tree_add_item(subtree, hf_rdpudp2_SendAckTimeGapInMs, tvb2, offset, 1, ENC_LITTLE_ENDIAN);
599 offset += 1;
602 acks_tree = proto_tree_add_subtree(subtree, tvb2, offset, codedAckVecSize, ett_rdpudp2_ackvec_vecs, NULL, "Acks");
603 for (i = 0; i < codedAckVecSize; i++) {
604 proto_tree *ack_tree;
606 uint8_t b = tvb_get_uint8(tvb2, offset + i);
608 if (b & 0x80) {
609 /* run length mode */
610 uint8_t rle_len = (b & 0x3f);
611 ack_tree = proto_tree_add_subtree_format(acks_tree, tvb2, offset + i, 1, ett_rdpudp2_ackvec_vec, NULL,
612 "RLE %s %04x -> %04x", (b & 0x40) ? "received" : "lost",
613 base_seq, base_seq + rle_len);
615 base_seq += rle_len;
616 } else {
617 /* bitmap mode */
618 ack_tree = proto_tree_add_subtree_format(acks_tree, tvb2, offset + i, 1, ett_rdpudp2_ackvec_vec, NULL,
619 "bitmap %s%04x %s%04x %s%04x %s%04x %s%04x %s%04x %s%04x",
620 (b & 0x01) ? "" : "!", base_seq,
621 (b & 0x02) ? "" : "!", base_seq + 1,
622 (b & 0x04) ? "" : "!", base_seq + 2,
623 (b & 0x08) ? "" : "!", base_seq + 3,
624 (b & 0x10) ? "" : "!", base_seq + 4,
625 (b & 0x20) ? "" : "!", base_seq + 5,
626 (b & 0x40) ? "" : "!", base_seq + 6
628 base_seq += 7;
631 proto_tree_add_item(ack_tree, hf_rdpudp2_AckvecCodedAckMode, tvb2, offset + i, 1, ENC_LITTLE_ENDIAN);
632 if (b & 0x80) {
633 proto_tree_add_item(ack_tree, hf_rdpudp2_AckvecCodedAckRleState, tvb2, offset + i, 1, ENC_LITTLE_ENDIAN);
634 proto_tree_add_item(ack_tree, hf_rdpudp2_AckvecCodedAckRleLen, tvb2, offset + i, 1, ENC_LITTLE_ENDIAN);
638 offset += codedAckVecSize;
639 col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "ACKVEC");
642 if ((flags & RDPUDP2_DATA) && (packet_type != 0x8)) {
643 tvbuff_t *data_tvb;
644 tvbuff_t *chunk;
645 uint32_t rawSeq;
646 uint64_t *seqPtr;
647 bool is_server_target = rdp_isServerAddressTarget(pinfo);
648 wmem_tree_t *targetTree = is_server_target ? rdpudp->client_chunks : rdpudp->server_chunks;
649 rdpudp_seq_context_t *target_seq_context = is_server_target ? &rdpudp->client_channel_seq : &rdpudp->server_channel_seq;
651 proto_tree_add_item_ret_uint(data_tree, hf_rdpudp2_DataChannelSeqNumber, tvb2, offset, 2, ENC_LITTLE_ENDIAN, &rawSeq);
652 if (!PINFO_FD_VISITED(pinfo)) {
653 seqPtr = wmem_alloc(wmem_file_scope(), sizeof(*seqPtr));
654 *seqPtr = computeAndUpdateSeqContext(target_seq_context, rawSeq);
656 p_set_proto_data(wmem_file_scope(), pinfo, proto_rdpudp, RDPUDP_FULL_CHANNEL_SEQ_KEY, seqPtr);
657 } else {
658 seqPtr = (uint64_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_rdpudp, RDPUDP_FULL_CHANNEL_SEQ_KEY);
660 proto_item_set_generated(
661 proto_tree_add_uint(data_tree, hf_rdpudp2_DataChannelFullSeqNumber, tvb2, offset, 2, (uint32_t)*seqPtr)
663 offset += 2;
665 chunk = wmem_tree_lookup32(targetTree, (uint32_t)*seqPtr);
666 data_tvb = tvb_new_composite();
668 if (chunk)
669 tvb_composite_prepend(data_tvb, chunk);
671 subtvb = tvb_new_subset_length(tvb2, offset, tvb_captured_length_remaining(tvb2, offset));
672 tvb_composite_append(data_tvb, subtvb);
673 tvb_composite_finalize(data_tvb);
675 add_new_data_source(pinfo, data_tvb, "SSL fragment");
676 pinfo->can_desegment = 2;
678 call_dissector(tls_handle, data_tvb, pinfo, data_tree);
680 if (!PINFO_FD_VISITED(pinfo) && pinfo->desegment_len) {
681 int remaining = tvb_captured_length_remaining(subtvb, pinfo->desegment_offset);
682 /* Something went wrong if seqPtr didn't advance.
683 * XXX: Should we ignore this or free the old chunk and
684 * use the new one?
686 chunk = (tvbuff_t*)wmem_tree_lookup32(targetTree, (uint32_t)(*seqPtr + 1));
687 if (chunk) {
688 tvb_free(chunk);
690 chunk = tvb_clone_offset_len(data_tvb, pinfo->desegment_offset, remaining);
691 wmem_tree_insert32(targetTree, (uint32_t)(*seqPtr + 1), chunk);
694 offset = tvb_captured_length(tvb2);
697 return offset;
700 static int
701 dissect_rdpudp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data _U_)
703 proto_item *item;
704 proto_tree *tree;
705 conversation_t *conversation;
706 rdpudp_conv_info_t *rdpudp_info;
708 conversation = find_or_create_conversation(pinfo);
710 rdpudp_info = (rdpudp_conv_info_t *)conversation_get_proto_data(conversation, proto_rdpudp);
711 if (rdpudp_info == NULL) {
712 rdpudp_info = wmem_new0(wmem_file_scope(), rdpudp_conv_info_t);
713 rdpudp_info->start_v2_at = UINT32_MAX;
714 rdpudp_info->is_lossy = false;
715 rdpudp_info->client_chunks = wmem_tree_new(wmem_file_scope());
716 rdpudp_info->server_chunks = wmem_tree_new(wmem_file_scope());
717 wmem_register_callback(wmem_file_scope(), rdpudp_info_free_cb, rdpudp_info);
719 conversation_add_proto_data(conversation, proto_rdpudp, rdpudp_info);
722 item = proto_tree_add_item(parent_tree, proto_rdpudp, tvb, 0, -1, ENC_NA);
723 tree = proto_item_add_subtree(item, ett_rdpudp);
725 if (rdpudp_info->start_v2_at > pinfo->num)
726 return dissect_rdpudp_v1(tvb, pinfo, tree, rdpudp_info);
727 else
728 return dissect_rdpudp_v2(tvb, pinfo, tree, rdpudp_info);
731 /*--- proto_register_rdpudp -------------------------------------------*/
732 void
733 proto_register_rdpudp(void) {
734 /* List of fields */
735 static hf_register_info hf[] = {
736 { &hf_rdpudp_snSourceAck,
737 {"snSourceAck", "rdpudp.snsourceack", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL},
739 { &hf_rdpudp_ReceiveWindowSize,
740 {"ReceiveWindowSize", "rdpudp.receivewindowsize", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL}
742 { &hf_rdpudp_flags,
743 {"Flags", "rdpudp.flags", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL}
745 { &hf_rdpudp_flag_syn,
746 {"Syn", "rdpudp.flags.syn", FT_BOOLEAN, 16, NULL, RDPUDP_SYN, NULL, HFILL}
748 { &hf_rdpudp_flag_fin,
749 {"Fin", "rdpudp.flags.fin", FT_BOOLEAN, 16, NULL, RDPUDP_FIN, NULL, HFILL}
751 { &hf_rdpudp_flag_ack,
752 {"Ack", "rdpudp.flags.ack", FT_BOOLEAN, 16, NULL, RDPUDP_ACK, NULL, HFILL}
754 { &hf_rdpudp_flag_data,
755 {"Data", "rdpudp.flags.data", FT_BOOLEAN, 16, NULL, RDPUDP_DATA, NULL, HFILL}
757 { &hf_rdpudp_flag_fec,
758 {"FECData", "rdpudp.flags.fec", FT_BOOLEAN, 16, NULL, RDPUDP_FEC, NULL, HFILL}
760 { &hf_rdpudp_flag_cn,
761 {"CN", "rdpudp.flags.cn", FT_BOOLEAN, 16, NULL, RDPUDP_CN, NULL, HFILL}
763 { &hf_rdpudp_flag_cwr,
764 {"CWR", "rdpudp.flags.cwr", FT_BOOLEAN, 16, NULL, RDPUDP_CWR, NULL, HFILL}
766 { &hf_rdpudp_flag_aoa,
767 {"Ack of Acks", "rdpudp.flags.aoa", FT_BOOLEAN, 16, NULL, RDPUDP_AOA, NULL, HFILL}
769 { &hf_rdpudp_flag_synlossy,
770 {"Syn lossy", "rdpudp.flags.synlossy", FT_BOOLEAN, 16, NULL, RDPUDP_SYNLOSSY, NULL, HFILL}
772 { &hf_rdpudp_flag_ackdelayed,
773 {"Ack delayed", "rdpudp.flags.ackdelayed", FT_BOOLEAN, 16, NULL, RDPUDP_ACKDELAYED, NULL, HFILL}
775 { &hf_rdpudp_flag_correlationId,
776 {"Correlation id", "rdpudp.flags.correlationid", FT_BOOLEAN, 16, NULL, RDPUDP_CORRELATIONID, NULL, HFILL}
778 { &hf_rdpudp_flag_synex,
779 {"SynEx","rdpudp.flags.synex",FT_BOOLEAN,16,NULL,RDPUDP_SYNEX,NULL,HFILL}
781 { &hf_rdpudp_snInitialSequenceNumber,
782 {"Initial SequenceNumber","rdpudp.initialsequencenumber", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}
784 { &hf_rdpudp_upstreamMtu,
785 {"Upstream MTU", "rdpudp.upstreammtu", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL}
787 { &hf_rdpudp_downstreamMtu,
788 {"DownStream MTU", "rdpudp.downstreammtu", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL}
790 { &hf_rdpudp_correlationId,
791 {"Correlation Id", "rdpudp.correlationid", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL}
793 { &hf_rdpudp_synex_flags,
794 {"Flags", "rdpudp.synex.flags", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL}
796 { &hf_rdpudp_synex_flag_version,
797 {"Version info", "rdpudp.synex.flags.versioninfo", FT_BOOLEAN, 16, NULL, 0x0001, NULL, HFILL}
799 { &hf_rdpudp_synex_version,
800 {"Version", "rdpudp.synex.version", FT_UINT16, BASE_HEX, VALS(rdpudp_version_vals), 0, NULL, HFILL}
802 {&hf_rdpudp_synex_cookiehash,
803 {"Cookie Hash", "rdpudp.synex.cookiehash", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL}
805 { &hf_rdpudp_ack_vectorsize,
806 {"uAckVectorSize", "rdpudp.ack.vectorsize", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL}
808 { &hf_rdpudp_ack_item,
809 {"Ack item", "rdpudp.ack.item", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}
811 { &hf_rdpudp_ack_item_state,
812 {"VECTOR_ELEMENT_STATE", "rdpudp.ack.item.state", FT_UINT8, BASE_HEX, VALS(rdpudp_ack_states_vals), 0xc0, NULL, HFILL}
814 { &hf_rdpudp_ack_item_rle,
815 {"Run length", "rdpudp.ack.item.rle", FT_UINT8, BASE_DEC, NULL, 0x3f, NULL, HFILL}
817 { &hf_rdpudp_fec_coded,
818 {"snCoded", "rdpudp.fec.coded", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}
820 { &hf_rdpudp_fec_sourcestart,
821 {"snSourceStart", "rdpudp.fec.sourcestart", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}
823 { &hf_rdpudp_fec_range,
824 {"Range", "rdpudp.fec.range", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}
826 { &hf_rdpudp_fec_fecindex,
827 {"Fec index", "rdpudp.fec.fecindex", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}
829 { &hf_rdpudp_resetseqenum,
830 {"snResetSeqNum", "rdpudp.resetSeqNum", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}
832 { &hf_rdpudp_source_sncoded,
833 {"snCoded", "rdpudp.data.sncoded", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}
835 { &hf_rdpudp_source_snSourceStart,
836 {"snSourceStart", "rdpudp.data.sourceStart", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}
838 { &hf_rdpudp_data,
839 {"Data", "rdpudp.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL}
843 { &hf_rdpudp2_PacketPrefixByte,
844 {"PacketPrefixByte", "rdpudp.prefixbyte", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}
846 { &hf_rdpudp2_packetType,
847 {"PacketType", "rdpudp.packetType", FT_UINT8, BASE_HEX, VALS(rdpudp2_packetType_vals), 0x1e, NULL, HFILL}
849 { &hf_rdpudp2_shortPacketLength,
850 {"Short packet length", "rdpudp.shortpacketlen", FT_UINT8, BASE_DEC, NULL, 0x7, NULL, HFILL}
852 { &hf_rdpudp2_flags,
853 {"Flags", "rdpudp.flags", FT_UINT16, BASE_HEX, NULL, 0x0fff, NULL, HFILL}
855 { &hf_rdpudp2_flag_ack,
856 {"Ack", "rdpudp.flags.ack", FT_BOOLEAN, 16, NULL, RDPUDP2_ACK, NULL, HFILL}
858 { &hf_rdpudp2_flag_data,
859 {"Data", "rdpudp.flags.data", FT_BOOLEAN, 16, NULL, RDPUDP2_DATA, NULL, HFILL}
861 { &hf_rdpudp2_flag_ackvec,
862 {"AckVec", "rdpudp.flags.ackvec", FT_UINT16, BASE_HEX, NULL, RDPUDP2_ACKVEC, NULL, HFILL}
864 { &hf_rdpudp2_flag_aoa,
865 {"AckOfAcks", "rdpudp.flags.ackofacks", FT_UINT16, BASE_HEX, NULL, RDPUDP2_AOA, NULL, HFILL}
867 { &hf_rdpudp2_flag_overhead,
868 {"OverheadSize", "rdpudp.flags.overheadsize", FT_UINT16, BASE_HEX, NULL, RDPUDP2_OVERHEAD, NULL, HFILL}
870 { &hf_rdpudp2_flag_delayackinfo,
871 {"DelayedAckInfo", "rdpudp.flags.delayackinfo", FT_UINT16, BASE_HEX, NULL, RDPUDP2_DELAYACK, NULL, HFILL}
873 { &hf_rdpudp2_logWindow,
874 {"LogWindow", "rdpudp.logWindow", FT_UINT16, BASE_DEC, NULL, 0xf000, NULL, HFILL}
876 { &hf_rdpudp2_AckSeq,
877 {"Base Seq", "rdpudp.ack.seqnum", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL}
879 { &hf_rdpudp2_AckTs,
880 {"receivedTS", "rdpudp.ack.ts", FT_UINT24, BASE_DEC, NULL, 0, NULL, HFILL}
882 { &hf_rdpudp2_AckSendTimeGap,
883 {"sendTimeGap", "rdpudp.ack.sendTimeGap", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}
885 { &hf_rdpudp2_ndelayedAcks,
886 {"NumDelayedAcks", "rdpudp.ack.numDelayedAcks", FT_UINT8, BASE_DEC, NULL, 0x0F, NULL, HFILL}
888 { &hf_rdpudp2_delayedTimeScale,
889 {"delayedTimeScale", "rdpudp.ack.delayedTimeScale", FT_UINT8, BASE_DEC, NULL, 0xF0, NULL, HFILL}
891 { &hf_rdpudp2_delayedAcks,
892 {"Delayed acks", "rdpudp.ack.delayedAcks", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL}
894 { &hf_rdpudp2_delayedAck,
895 {"Delayed ack", "rdpudp.ack.delayedAck", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}
897 { &hf_rdpudp2_OverHeadSize,
898 {"Overhead size", "rdpudp.overheadsize", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}
900 { &hf_rdpudp2_DelayAckMax,
901 {"MaxDelayedAcks", "rdpudp.delayackinfo.max", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL}
903 { &hf_rdpudp2_DelayAckTimeout,
904 {"DelayedAckTimeoutInMs", "rdpudp.delayackinfo.timeout", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL}
906 { &hf_rdpudp2_AckOfAcksSeqNum,
907 {"Sequence number", "rdpudp.ackofacksseqnum", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL}
909 { &hf_rdpudp2_DataSeqNumber,
910 {"Sequence number", "rdpudp.data.seqnum", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL}
912 { &hf_rdpudp2_DataFullSeqNumber,
913 {"Full sequence number", "rdpudp.data.fullseqnum", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}
915 { &hf_rdpudp2_DataChannelSeqNumber,
916 {"Channel sequence number", "rdpudp.data.channelseqnumber", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL}
918 { &hf_rdpudp2_DataChannelFullSeqNumber,
919 {"Channel full sequence number", "rdpudp.data.channelfullseqnumber", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}
921 { &hf_rdpudp2_Data,
922 {"Data", "rdpudp.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL}
924 { &hf_rdpudp2_AckvecBaseSeq,
925 {"Base sequence number", "rdpudp.ackvec.baseseqnum", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL}
927 { &hf_rdpudp2_AckvecCodecAckVecSize,
928 {"Coded ackvec size","rdpudp.ackvec.codedackvecsize", FT_UINT16, BASE_DEC, NULL, 0x7f, NULL, HFILL}
930 { &hf_rdpudp2_AckvecHaveTs,
931 {"Have timestamp", "rdpudp.ackvec.havets", FT_BOOLEAN, 8, NULL, 0x80, NULL, HFILL}
933 { &hf_rdpudp2_AckvecTimeStamp,
934 {"Timestamp", "rdpudp.ackvec.timestamp", FT_UINT24, BASE_HEX, NULL, 0, NULL, HFILL}
936 { &hf_rdpudp2_SendAckTimeGapInMs,
937 {"SendAckTimeGap", "rdpudp.ackvec.sendacktimegap", FT_UINT8, BASE_DEC, NULL, 0x00, NULL, HFILL}
939 { &hf_rdpudp2_AckvecCodedAck,
940 {"Coded Ack", "rdpudp.ackvec.codedAck", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL}
942 { &hf_rdpudp2_AckvecCodedAckMode,
943 {"Mode", "rdpudp.ackvec.codecAckMode", FT_UINT8, BASE_HEX, VALS(rdpudp2_ackvec_mode_vals), 0x80, NULL, HFILL}
945 { &hf_rdpudp2_AckvecCodedAckRleState,
946 {"State", "rdpudp.ackvec.codecAckRleState", FT_UINT8, BASE_DEC, VALS(rdpudp2_ackvec_rlestates_vals), 0x40, NULL, HFILL}
948 { &hf_rdpudp2_AckvecCodedAckRleLen,
949 {"Length", "rdpudp.ackvec.codecAckRleLen", FT_UINT8, BASE_DEC, NULL, 0x3f, NULL, HFILL}
953 /* List of subtrees */
954 static int *ett[] = {
955 &ett_rdpudp,
956 &ett_rdpudp_flags,
957 &ett_rdpudp_synex,
958 &ett_rdpudp_ack,
959 &ett_rdpudp_fec,
960 &ett_rdpudp_data,
961 &ett_rdpudp2_packetType,
962 &ett_rdpudp2_flags,
963 &ett_rdpudp2_ack,
964 &ett_rdpudp2_overhead,
965 &ett_rdpudp2_delayack,
966 &ett_rdpudp2_aoa,
967 &ett_rdpudp2_data,
968 &ett_rdpudp2_ackvec,
969 &ett_rdpudp2_ackvec_vecs,
970 &ett_rdpudp2_ackvec_vec,
973 /* Register protocol */
974 proto_rdpudp = proto_register_protocol(PNAME, PSNAME, PFNAME);
975 /* Register fields and subtrees */
976 proto_register_field_array(proto_rdpudp, hf, array_length(hf));
977 proto_register_subtree_array(ett, array_length(ett));
979 rdpudp_handle = register_dissector("rdpudp", dissect_rdpudp, proto_rdpudp);
982 void
983 proto_reg_handoff_rdpudp(void)
985 tls_handle = find_dissector("tls");
986 dtls_handle = find_dissector("dtls");
987 dissector_add_uint("udp.port", 3389, rdpudp_handle);
991 * Editor modelines - https://www.wireshark.org/tools/modelines.html
993 * Local Variables:
994 * c-basic-offset: 2
995 * tab-width: 8
996 * indent-tabs-mode: nil
997 * End:
999 * ex: set shiftwidth=2 tabstop=8 expandtab:
1000 * :indentSize=2:tabSize=8:noTabs=true: