2 * Routines for MS-DO (Microsoft Delivery Optimization) dissection
3 * Copyright 2023, Benjamin Levine (binyamin.l@sygnia.co, levbinyamin@gmail.com)
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
13 * Microsoft Delivery Optimization is an internal Windows protocol for exchanging updates between Windows peers.
15 * As of today there are no Microsoft official docs specifying the protocol itself,
16 * this dissector was written as part of my research into MS-DO.
17 * For a detailed explanation see our blog posts:
23 #include <epan/packet.h>
24 #include <epan/conversation.h>
25 #include <epan/expert.h>
27 void proto_reg_handoff_do(void);
28 void proto_register_do(void);
32 static int hf_do_handshake_message
;
33 static int hf_do_keepalive_message
;
34 static int hf_do_choke_message
;
35 static int hf_do_unchoke_message
;
36 static int hf_do_interested_message
;
37 static int hf_do_notinterested_message
;
38 static int hf_do_have_message
;
39 static int hf_do_bitfield_message
;
40 static int hf_do_request_message
;
41 static int hf_do_piece_message
;
42 static int hf_do_cancel_message
;
43 static int hf_do_heap_spraying_message
;
44 static int hf_do_unknown_message
;
46 static int hf_do_protocol_name
;
47 static int hf_do_size
;
48 static int hf_do_swarm_hash
;
49 static int hf_do_peer_id
;
50 static int hf_do_peer_id_suffix
;
52 static int hf_do_message_size
;
53 static int hf_do_message_id
;
55 static int hf_do_bitfield
;
56 static int hf_do_bitfield_piece
;
57 static int hf_do_has_piece
;
59 static int hf_do_piece_index
;
60 static int hf_do_piece_start_offset
;
61 static int hf_do_piece_size
;
62 static int hf_do_piece_buffer
;
63 static int hf_do_piece_response_size
;
65 static int hf_do_heap_spraying
;
68 static int ett_do_handshake
;
69 static int ett_do_message
;
70 static int ett_do_bitfield
;
71 static int ett_do_bitfield_single
;
73 static expert_field ei_do_invalid_message_id
;
74 static expert_field ei_do_invalid_message_length
;
76 static dissector_handle_t do_handle
;
78 #define DO_PORT (7680)
80 #define DO_CHOKE_ID (0)
81 #define DO_UNCHOKE_ID (1)
82 #define DO_INTERESTED_ID (2)
83 #define DO_NOTINTERESTED_ID (3)
84 #define DO_HAVE_ID (4)
85 #define DO_BITFIELD_ID (5)
86 #define DO_REQUEST_ID (6)
87 #define DO_PIECE_ID (7)
88 #define DO_CANCEL_ID (8)
89 #define DO_HEAPSPRAYING_ID (20)
91 static const value_string message_types
[] = {
92 { DO_CHOKE_ID
, "Choke Message" },
93 { DO_UNCHOKE_ID
, "Unchoke Message" },
94 { DO_INTERESTED_ID
, "Interested Message" },
95 { DO_NOTINTERESTED_ID
, "NotInterested Message" },
96 { DO_HAVE_ID
, "Have Message" },
97 { DO_BITFIELD_ID
, "BitField Message" },
98 { DO_REQUEST_ID
, "Request Message" },
99 { DO_PIECE_ID
, "Piece Message" },
100 { DO_CANCEL_ID
, "Cancel Message" },
101 { DO_HEAPSPRAYING_ID
, "HeapSpraying Message" },
105 static value_string_ext message_types_ext
= VALUE_STRING_EXT_INIT(message_types
);
107 typedef bool (*do_dissect_callback_t
)(tvbuff_t
*, packet_info
*, proto_tree
*, uint32_t, uint8_t, unsigned*);
110 do_get_direction_str(packet_info
* pinfo
)
112 if (pinfo
->match_uint
== pinfo
->destport
)
116 else if (pinfo
->match_uint
== pinfo
->srcport
)
120 // This shouldn't happen.
123 DISSECTOR_ASSERT_NOT_REACHED();
128 * Function attempts to identify a handshake and if so, parses it.
129 * If not returns false.
132 dissect_do_handshake(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* do_tree
, unsigned* offset_ptr
)
134 uint8_t protocol_name_length
= 0;
135 uint32_t calculated_length
= 0;
136 proto_item
* handshame_item
= NULL
;
137 proto_tree
* handshake_tree
= NULL
;
139 /* Identify handshake heuristically.
140 It's hard to detect a handshake as it has a different format to messages.
141 We could search for the "\x0eSwarm protocol" string at the beginning,
142 but the Microsoft code supports other options that may be relevant.
143 Perhaps in the future use conversations, they would have to handle
144 cases where the sniff begins in the middle without any handhsakes.
145 As anyway that approach requires heuristically verifying for handshakes in the middle
146 let's just do that every time without preserving a complex context. */
147 protocol_name_length
= tvb_get_uint8(tvb
, *offset_ptr
);
148 calculated_length
= 1 + protocol_name_length
+ 8 + 32 + 16 + 4;
149 if (calculated_length
!= tvb_reported_length(tvb
))
154 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Handshake Message (%s)", do_get_direction_str(pinfo
));
156 handshame_item
= proto_tree_add_item(do_tree
, hf_do_handshake_message
, tvb
, 0, -1, ENC_NA
);
157 handshake_tree
= proto_item_add_subtree(handshame_item
, ett_do_handshake
);
159 proto_tree_add_item(handshake_tree
, hf_do_protocol_name
, tvb
, *offset_ptr
, 1, ENC_ASCII
| ENC_BIG_ENDIAN
);
160 *offset_ptr
+= sizeof(protocol_name_length
) + protocol_name_length
;
162 proto_tree_add_item(handshake_tree
, hf_do_size
, tvb
, *offset_ptr
, 8, ENC_BIG_ENDIAN
);
165 proto_tree_add_item(handshake_tree
, hf_do_swarm_hash
, tvb
, *offset_ptr
, 32, ENC_NA
);
168 proto_tree_add_item(handshake_tree
, hf_do_peer_id
, tvb
, *offset_ptr
, 16, ENC_BIG_ENDIAN
);
171 proto_tree_add_item(handshake_tree
, hf_do_peer_id_suffix
, tvb
, *offset_ptr
, 4, ENC_BIG_ENDIAN
);
178 do_message_id_to_str(uint8_t message_id
)
180 return val_to_str_ext_const(message_id
, &message_types_ext
, "Unknown Message");
184 do_message_id_to_hfindex(uint8_t message_id
)
189 return hf_do_choke_message
;
191 return hf_do_unchoke_message
;
192 case DO_INTERESTED_ID
:
193 return hf_do_interested_message
;
194 case DO_NOTINTERESTED_ID
:
195 return hf_do_notinterested_message
;
197 return hf_do_have_message
;
199 return hf_do_bitfield_message
;
201 return hf_do_request_message
;
203 return hf_do_piece_message
;
205 return hf_do_cancel_message
;
206 case DO_HEAPSPRAYING_ID
:
207 return hf_do_heap_spraying_message
;
209 return hf_do_unknown_message
;
214 * Add a subtree for a single message and add the message header into it.
217 do_add_message_tree(tvbuff_t
* tvb
, proto_tree
* tree
, uint8_t message_id
, uint32_t message_full_size
, unsigned* offset_ptr
)
219 proto_item
* message_item
= NULL
;
220 proto_tree
* message_tree
= NULL
;
222 message_item
= proto_tree_add_item(tree
, do_message_id_to_hfindex(message_id
), tvb
, *offset_ptr
, message_full_size
, ENC_NA
);
223 message_tree
= proto_item_add_subtree(message_item
, ett_do_message
);
225 proto_tree_add_item(message_tree
, hf_do_message_size
, tvb
, *offset_ptr
, 4, ENC_BIG_ENDIAN
);
227 proto_tree_add_item(message_tree
, hf_do_message_id
, tvb
, *offset_ptr
, 1, ENC_NA
);
234 * Add KeepAlive message into tree.
237 dissect_do_keepalive(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset_ptr
)
239 proto_item
* message_item
= NULL
;
240 proto_tree
* message_tree
= NULL
;
242 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, NULL
, "KeepAlive Message");
244 message_item
= proto_tree_add_item(tree
, hf_do_keepalive_message
, tvb
, *offset_ptr
, 4, ENC_NA
);
245 message_tree
= proto_item_add_subtree(message_item
, ett_do_message
);
246 proto_tree_add_item(message_tree
, hf_do_message_size
, tvb
, *offset_ptr
, 4, ENC_BIG_ENDIAN
);
251 * Callback to add a message without variables, this is used for multiple simple messages.
254 dissect_do_empty_message(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* message_tree
, uint32_t message_size
, uint8_t message_id
, unsigned* offset_ptr
)
256 if (1 != message_size
)
258 proto_tree_add_expert_format(message_tree
, pinfo
, &ei_do_invalid_message_length
, tvb
, *offset_ptr
, message_size
- 1,
259 "Invalid message size: %u instead of %u", message_size
, 1);
260 *offset_ptr
+= message_size
- 1;
264 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, NULL
, do_message_id_to_str(message_id
));
270 * Callback to add a Have message.
273 dissect_do_have(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* message_tree
, uint32_t message_size
, uint8_t message_id
, unsigned* offset_ptr
)
275 uint32_t piece_index
= -1;
277 if (5 != message_size
)
279 proto_tree_add_expert_format(message_tree
, pinfo
, &ei_do_invalid_message_length
, tvb
, *offset_ptr
, message_size
- 1,
280 "Invalid message size: %u instead of %u", message_size
, 5);
281 *offset_ptr
+= message_size
- 1;
284 proto_tree_add_item_ret_uint(message_tree
, hf_do_piece_index
, tvb
, *offset_ptr
, 4, ENC_BIG_ENDIAN
, &piece_index
);
287 col_append_sep_fstr(pinfo
->cinfo
, COL_INFO
, NULL
, "%s (piece %u)", do_message_id_to_str(message_id
), piece_index
);
293 * Callback to add a BitField message.
296 dissect_do_bitfield(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* message_tree
, uint32_t message_size
, _U_
uint8_t message_id
, unsigned* offset_ptr
)
298 proto_item
* bitfield_item
= NULL
;
299 proto_tree
* bitfield_tree
= NULL
;
300 proto_item
* piece_item
= NULL
;
301 proto_tree
* piece_tree
= NULL
;
302 uint32_t total_has
= 0;
303 uint32_t bitfield_size
= 0;
304 uint32_t byte_index
= 0;
305 uint32_t bit_index
= 0;
306 uint8_t current_byte
= 0;
307 bool has_piece
= false;
308 uint32_t piece_index
= 0;
310 bitfield_size
= message_size
- 1;
312 bitfield_item
= proto_tree_add_item(message_tree
, hf_do_bitfield
, tvb
, *offset_ptr
, bitfield_size
, ENC_NA
);
313 bitfield_tree
= proto_item_add_subtree(bitfield_item
, ett_do_bitfield
);
315 for (byte_index
= 0; byte_index
< bitfield_size
; byte_index
++)
317 current_byte
= tvb_get_uint8(tvb
, *offset_ptr
);
318 for (bit_index
= 0; bit_index
< 8; bit_index
++)
321 // Simplified from dosvc.dll!CBitField::_IsSet
322 if (current_byte
& (1 << (7 - bit_index
)))
327 piece_index
= 8 * byte_index
+ bit_index
;
328 piece_item
= proto_tree_add_string_format_value(bitfield_tree
, hf_do_bitfield_piece
, tvb
, *offset_ptr
, 1,
329 NULL
, "Index: %u, has: %s", piece_index
, has_piece
? "true" : "false");
330 piece_tree
= proto_item_add_subtree(piece_item
, ett_do_bitfield_single
);
331 proto_tree_add_uint(piece_tree
, hf_do_piece_index
, tvb
, *offset_ptr
, 1, piece_index
);
332 proto_tree_add_boolean(piece_tree
, hf_do_has_piece
, tvb
, *offset_ptr
, 1, has_piece
);
338 col_append_sep_fstr(pinfo
->cinfo
, COL_INFO
, NULL
, "%s (has %u of %u pieces)",
339 do_message_id_to_str(message_id
), total_has
, bitfield_size
* 8);
345 * Callback to add a Request or Cancel message.
348 dissect_do_request_cancel(tvbuff_t
* tvb
, _U_ packet_info
* pinfo
, proto_tree
* message_tree
, uint32_t message_size
, uint8_t message_id
, unsigned* offset_ptr
)
350 uint32_t piece_index
= 0;
351 uint32_t piece_start_offset
= 0;
352 uint32_t piece_size
= 0;
354 if (13 != message_size
)
356 proto_tree_add_expert_format(message_tree
, pinfo
, &ei_do_invalid_message_length
, tvb
, *offset_ptr
, message_size
- 1,
357 "Invalid message size: %u instead of %u", message_size
, 13);
358 *offset_ptr
+= message_size
- 1;
362 proto_tree_add_item_ret_uint(message_tree
, hf_do_piece_index
, tvb
, *offset_ptr
, 4, ENC_BIG_ENDIAN
, &piece_index
);
364 proto_tree_add_item_ret_uint(message_tree
, hf_do_piece_start_offset
, tvb
, *offset_ptr
, 4, ENC_BIG_ENDIAN
, &piece_start_offset
);
366 proto_tree_add_item_ret_uint(message_tree
, hf_do_piece_size
, tvb
, *offset_ptr
, 4, ENC_BIG_ENDIAN
, &piece_size
);
369 col_append_sep_fstr(pinfo
->cinfo
, COL_INFO
, NULL
, "%s (piece %u; offset 0x%x; size 0x%x)",
370 do_message_id_to_str(message_id
), piece_index
, piece_start_offset
, piece_size
);
376 * Callback to add a Piece message.
379 dissect_do_piece(tvbuff_t
* tvb
, _U_ packet_info
* pinfo
, proto_tree
* message_tree
, uint32_t message_size
, uint8_t message_id
, unsigned* offset_ptr
)
381 proto_item
* pi
= NULL
;
382 uint32_t piece_index
= 0;
383 uint32_t piece_start_offset
= 0;
384 uint32_t piece_size
= 0;
386 piece_size
= message_size
- 9;
388 if (message_size
<= 9)
390 proto_tree_add_expert_format(message_tree
, pinfo
, &ei_do_invalid_message_length
, tvb
, *offset_ptr
, message_size
- 1,
391 "Invalid message size: message size %u must be larger than 9", message_size
);
392 *offset_ptr
+= message_size
- 1;
395 proto_tree_add_item_ret_uint(message_tree
, hf_do_piece_index
, tvb
, *offset_ptr
, 4, ENC_BIG_ENDIAN
, &piece_index
);
397 proto_tree_add_item_ret_uint(message_tree
, hf_do_piece_start_offset
, tvb
, *offset_ptr
, 4, ENC_BIG_ENDIAN
, &piece_start_offset
);
399 proto_tree_add_item(message_tree
, hf_do_piece_buffer
, tvb
, *offset_ptr
, message_size
- 9, ENC_NA
);
400 *offset_ptr
+= message_size
- 9;
401 pi
= proto_tree_add_uint(message_tree
, hf_do_piece_response_size
, NULL
, 0, 0, piece_size
);
402 proto_item_set_generated(pi
);
404 col_append_sep_fstr(pinfo
->cinfo
, COL_INFO
, NULL
, "%s (piece %u; offset 0x%x; size 0x%x)",
405 do_message_id_to_str(message_id
), piece_index
, piece_start_offset
, piece_size
);
411 * Callback to add a "HeapSpraying" message.
414 dissect_do_heap_spraying(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* message_tree
, uint32_t message_size
, _U_
uint8_t message_id
, unsigned* offset_ptr
)
416 proto_tree_add_item(message_tree
, hf_do_heap_spraying
, tvb
, *offset_ptr
, message_size
- 1, ENC_NA
);
417 *offset_ptr
+= message_size
- 1;
419 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, NULL
, do_message_id_to_str(message_id
));
425 * Callback to handle an unknown message
426 * Just adds its size (which we have from the message header) to the offset.
429 dissect_do_unkown_message(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* message_tree
, uint32_t message_size
, uint8_t message_id
, unsigned* offset_ptr
)
431 proto_tree_add_expert_format(message_tree
, pinfo
, &ei_do_invalid_message_id
, tvb
, *offset_ptr
, message_size
- 1,
432 "Unknown message ID: %u", message_id
);
434 *offset_ptr
+= message_size
- 1;
439 static do_dissect_callback_t
440 message_id_to_callback(uint8_t message_id
)
446 case DO_INTERESTED_ID
:
447 case DO_NOTINTERESTED_ID
:
448 return &dissect_do_empty_message
;
450 return &dissect_do_have
;
452 return &dissect_do_bitfield
;
455 return dissect_do_request_cancel
;
457 return &dissect_do_piece
;
458 case DO_HEAPSPRAYING_ID
:
459 return &dissect_do_heap_spraying
;
461 return &dissect_do_unkown_message
;
466 * Parse a single message, on success return true.
467 * If true is returned but *desegment_len_ptr is non-zero, the message wasn't parsed as more bytes are required.
470 dissect_do_message(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* do_tree
, unsigned* offset_ptr
, unsigned* desegment_len_ptr
)
472 unsigned buffer_size
= 0;
473 uint32_t message_size
= 0;
474 uint32_t message_full_size
= 0;
475 uint8_t message_id
= -1;
476 proto_tree
* message_tree
= NULL
;
478 buffer_size
= tvb_reported_length_remaining(tvb
, *offset_ptr
);
480 // Request more bytes if necessary
483 *desegment_len_ptr
= 4 - buffer_size
;
487 message_size
= tvb_get_uint32(tvb
, *offset_ptr
, ENC_BIG_ENDIAN
);
490 if (0 == message_size
)
492 dissect_do_keepalive(tvb
, pinfo
, do_tree
, offset_ptr
);
496 if (buffer_size
< message_size
+ 4)
498 // Message size (4 bytes) + required size - current size
499 *desegment_len_ptr
= 4 + message_size
- buffer_size
;
503 message_id
= tvb_get_uint8(tvb
, *offset_ptr
+ 4);
504 message_full_size
= sizeof(message_size
) + message_size
;
506 message_tree
= do_add_message_tree(tvb
, do_tree
, message_id
, message_full_size
, offset_ptr
);
508 if (!(*message_id_to_callback(message_id
))(tvb
, pinfo
, message_tree
, message_size
, message_id
, offset_ptr
))
517 dissect_do(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, _U_
void* data
)
519 proto_item
* ti
= NULL
;
520 proto_tree
* do_tree
= NULL
;
522 // Using GLib types instead of standard interface to match existing API.
524 uint32_t desegment_len
= 0;
526 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "MS-DO");
527 col_clear(pinfo
->cinfo
, COL_INFO
);
529 if (0 == tvb_captured_length(tvb
))
531 return tvb_captured_length(tvb
);
534 ti
= proto_tree_add_item(tree
, proto_do
, tvb
, 0, -1, ENC_NA
);
535 do_tree
= proto_item_add_subtree(ti
, ett_do
);
537 // Multiple messages can be concatted, parse them one at a time.
538 while (offset
< tvb_reported_length(tvb
))
540 // Check if this is a handshake message, if not parse it as a message
541 if (dissect_do_handshake(tvb
, pinfo
, do_tree
, &offset
))
547 if (dissect_do_message(tvb
, pinfo
, do_tree
, &offset
, &desegment_len
))
551 // Wait for more data.
552 pinfo
->desegment_offset
= offset
;
553 pinfo
->desegment_len
= desegment_len
;
554 return tvb_reported_length(tvb
);
559 // Parsing error, stop parsing
560 return tvb_captured_length(tvb
);
568 proto_register_do(void)
570 static hf_register_info hf
[] = {
571 { &hf_do_handshake_message
,
572 { "Handshake Message", "msdo.Handshake",
573 FT_NONE
, BASE_NONE
, NULL
, 0x0,
576 { &hf_do_keepalive_message
,
577 { "KeepAlive Message", "msdo.KeepAlive",
578 FT_NONE
, BASE_NONE
, NULL
, 0x0,
581 { &hf_do_choke_message
,
582 { "Choke Message", "msdo.Choke",
583 FT_NONE
, BASE_NONE
, NULL
, 0x0,
586 { &hf_do_unchoke_message
,
587 { "UnChoke Message", "msdo.UnChoke",
588 FT_NONE
, BASE_NONE
, NULL
, 0x0,
591 { &hf_do_interested_message
,
592 { "Interested Message", "msdo.Interested",
593 FT_NONE
, BASE_NONE
, NULL
, 0x0,
596 { &hf_do_notinterested_message
,
597 { "NotInterested Message", "msdo.NotInterested",
598 FT_NONE
, BASE_NONE
, NULL
, 0x0,
601 { &hf_do_have_message
,
602 { "Have Message", "msdo.Have",
603 FT_NONE
, BASE_NONE
, NULL
, 0x0,
606 { &hf_do_bitfield_message
,
607 { "BitField Message", "msdo.BitField",
608 FT_NONE
, BASE_NONE
, NULL
, 0x0,
611 { &hf_do_request_message
,
612 { "Request Message", "msdo.Request",
613 FT_NONE
, BASE_NONE
, NULL
, 0x0,
616 { &hf_do_piece_message
,
617 { "Piece Message", "msdo.Piece",
618 FT_NONE
, BASE_NONE
, NULL
, 0x0,
621 { &hf_do_cancel_message
,
622 { "Cancel Message", "msdo.Cancel",
623 FT_NONE
, BASE_NONE
, NULL
, 0x0,
626 { &hf_do_heap_spraying_message
,
627 { "HeapSpraying Message", "msdo.HeapSpraying",
628 FT_NONE
, BASE_NONE
, NULL
, 0x0,
631 { &hf_do_unknown_message
,
632 { "Unknown Message, this shouldn't happen", "msdo.UnknownMessage",
633 FT_NONE
, BASE_NONE
, NULL
, 0x0,
637 { &hf_do_protocol_name
,
638 { "Protocol Name", "msdo.Handshake.ProtocolName",
639 FT_UINT_STRING
, BASE_NONE
, NULL
, 0x0,
643 { "Size", "msdo.Handshake.Size",
644 FT_UINT64
, BASE_HEX
, NULL
, 0x0,
648 { "Swarm Hash", "msdo.Handshake.SwarmHash",
649 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
653 { "Peer Id", "msdo.Handshake.PeerId",
654 FT_GUID
, BASE_NONE
, NULL
, 0x0,
657 { &hf_do_peer_id_suffix
,
658 { "Peer Id Suffix", "msdo.Handshake.PeerIdSuffix",
659 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
663 { &hf_do_message_size
,
664 { "Message Size", "msdo.MessageSize",
665 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
669 { "Message Id", "msdo.MessageId",
670 FT_UINT32
, BASE_DEC
| BASE_EXT_STRING
, &message_types_ext
, 0x0,
675 { "Bit Field", "msdo.BitField.BitField",
676 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
679 { &hf_do_bitfield_piece
,
680 { "Bit Field", "msdo.BitField.Piece",
681 FT_STRINGZ
, BASE_NONE
, NULL
, 0x0,
685 { "Has Piece", "msdo.BitField.HasPiece",
686 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
690 { &hf_do_piece_index
,
691 { "Piece Index", "msdo.PieceIndex",
692 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
695 { &hf_do_piece_start_offset
,
696 { "Piece Start Offset", "msdo.PieceStartOffset",
697 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
701 { "Requested Piece Size", "msdo.PieceSize",
702 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
705 { &hf_do_piece_buffer
,
706 { "Piece Buffer", "msdo.PieceBuffer",
707 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
710 { &hf_do_piece_response_size
,
711 { "Response Piece Buffer Size", "msdo.PieceSize",
712 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
715 { &hf_do_heap_spraying
,
716 { "Heap Spraying Buffer", "msdo.HeapSpraying.HeapSpraying",
717 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
722 static int* ett
[] = {
727 &ett_do_bitfield_single
730 static ei_register_info ei
[] = {
731 { &ei_do_invalid_message_id
, { "msdo.invalid_message_id", PI_MALFORMED
, PI_WARN
, "Unknown message ID", EXPFILL
}},
732 { &ei_do_invalid_message_length
, { "msdo.invalid_message_length", PI_MALFORMED
, PI_ERROR
, "Invalid message size", EXPFILL
}}
735 expert_module_t
* expert_do
= NULL
;
737 proto_do
= proto_register_protocol("Microsoft Delivery Optimization", "MS-DO", "msdo");
739 proto_register_field_array(proto_do
, hf
, array_length(hf
));
740 proto_register_subtree_array(ett
, array_length(ett
));
741 expert_do
= expert_register_protocol(proto_do
);
742 expert_register_field_array(expert_do
, ei
, array_length(ei
));
744 do_handle
= register_dissector("msdo", dissect_do
, proto_do
);
748 proto_reg_handoff_do(void)
750 static bool initialized
= false;
754 dissector_add_uint("tcp.port", DO_PORT
, do_handle
);