3 * Routines for Ruby Marshal Object
5 * Copyright 2018, Dario Lombardo (lomato@gmail.com)
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * SPDX-License-Identifier: GPL-2.0-or-later
15 * Example for creating a Ruby marshal file:
16 * o = <whatever ruby object>
17 * f = File.open("marshal.dat", 'wb')
18 * f.write(Marshal.dump(o))
24 #include <epan/expert.h>
25 #include <epan/packet.h>
26 #include <wsutil/array.h>
28 #include <wiretap/ruby_marshal.h>
32 static int hf_rbm_version
;
33 static int hf_rbm_type
;
34 static int hf_rbm_integer
;
35 static int hf_rbm_length
;
36 static int hf_rbm_string
;
37 static int hf_rbm_link
;
38 static int hf_rbm_double
;
39 static int hf_rbm_struct
;
40 static int hf_rbm_regex_param
;
44 static int ett_array_obj
;
46 static int ett_hash_obj
;
47 static int ett_variable
;
49 static expert_field ei_rbm_invalid
;
50 static expert_field ei_rbm_version_unsupported
;
53 static const value_string rbm_types
[] = {
60 { 'I', "Instance variable" },
70 { 'e', "Extended_object" },
71 { ';', "Symbol link" },
72 { '@', "Object link" },
73 { 'u', "DRb::DRbObject" },
74 { ',', "DRb address" },
78 void proto_register_rbm(void);
79 void proto_reg_handoff_rbm(void);
81 static dissector_handle_t rbm_file_handle
;
83 #define BETWEEN(v, b1, b2) (((v) >= (b1)) && ((v) <= (b2)))
85 static void dissect_rbm_object(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, char** type
, char** value
);
87 static void rbm_set_info(packet_info
* pinfo
, const char* str
)
89 const char* col_str
= col_get_text(pinfo
->cinfo
, COL_INFO
);
90 if (!col_str
|| !strlen(col_str
))
91 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "Ruby Marshal Object: %s", str
);
94 void get_rbm_integer(tvbuff_t
* tvb
, unsigned offset
, int32_t* value
, int* len
)
97 c
= (tvb_get_int8(tvb
, offset
) ^ 128) - 128;
108 if (BETWEEN(c
, 1, 3)) {
112 for (i
= 0; i
< c
; i
++) {
113 byte
= tvb_get_uint8(tvb
, offset
+ 1 + i
);
114 *value
|= (byte
<< (8 * i
));
124 if (BETWEEN(c
, -5, -1)) {
130 for (i
= 0; i
< -c
; i
++) {
131 byte
= tvb_get_uint8(tvb
, offset
+ 1 + i
);
132 a
= ~(0xff << (8*i
));
134 *value
= ((*value
& a
) | b
);
141 static void dissect_rbm_integer(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, char** value_str
)
145 rbm_set_info(pinfo
, "integer");
146 get_rbm_integer(tvb
, *offset
, &value
, &len
);
147 proto_tree_add_int_format_value(tree
, hf_rbm_integer
, tvb
, *offset
, len
, value
, "%d", value
);
150 *value_str
= wmem_strdup_printf(pinfo
->pool
, "%d", value
);
153 static void dissect_rbm_basic(tvbuff_t
* tvb _U_
, packet_info
* pinfo
, proto_tree
* tree _U_
, unsigned* offset _U_
, const uint8_t subtype
,
154 char** type
, char** value_str
)
166 *value_str
= "false";
169 DISSECTOR_ASSERT_NOT_REACHED();
171 rbm_set_info(pinfo
, *type
);
174 static void dissect_rbm_string_data_trailer(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, const char* label
,
175 const char* prefix
, const char* trailer
, char** value_str
)
181 rbm_set_info(pinfo
, label
);
183 get_rbm_integer(tvb
, *offset
, &value
, &len
);
184 proto_tree_add_int_format_value(tree
, hf_rbm_length
, tvb
, *offset
, len
, value
, "%d", value
);
186 s
= (const char*)tvb_get_string_enc(pinfo
->pool
, tvb
, *offset
, value
, ENC_NA
);
187 proto_tree_add_string_format_value(tree
, hf_rbm_string
, tvb
, *offset
, value
, s
, "%s%s%s", prefix
, s
, trailer
);
189 *value_str
= wmem_strdup_printf(pinfo
->pool
, "%s%s%s", prefix
, s
, trailer
);
192 static void dissect_rbm_string_data(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, const char* label
,
193 const char* prefix
, char** value_str
)
195 dissect_rbm_string_data_trailer(tvb
, pinfo
, tree
, offset
, label
, prefix
, "", value_str
);
198 // NOLINTNEXTLINE(misc-no-recursion)
199 static void dissect_rbm_array(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, char** value_str
)
204 proto_tree
* array_tree
= NULL
;
205 proto_tree
* array_obj_tree
= NULL
;
206 int offset_start
= *offset
;
208 rbm_set_info(pinfo
, "Array");
209 get_rbm_integer(tvb
, *offset
, &value
, &len
);
210 proto_tree_add_int_format_value(tree
, hf_rbm_length
, tvb
, *offset
, len
, value
, "%d", value
);
211 array_tree
= proto_tree_add_subtree(tree
, tvb
, *offset
, 0, ett_array
, NULL
, "Array");
214 for (i
= 0; i
< value
; i
++) {
215 array_obj_tree
= proto_tree_add_subtree(array_tree
, tvb
, *offset
, 0, ett_array_obj
, NULL
, "Object");
216 dissect_rbm_object(tvb
, pinfo
, array_obj_tree
, offset
, NULL
, NULL
);
218 proto_item_append_text(array_tree
, " (%d)", value
);
219 proto_item_set_len(array_tree
, *offset
- offset_start
);
222 *value_str
= wmem_strdup_printf(pinfo
->pool
, "%d", value
);
225 // NOLINTNEXTLINE(misc-no-recursion)
226 static void dissect_rbm_hash(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, char** value_str
)
231 proto_tree
* hash_tree
= NULL
;
232 proto_tree
* hash_obj_tree
= NULL
;
233 proto_tree
* hash_key_tree
= NULL
;
234 proto_tree
* hash_value_tree
= NULL
;
237 int offset_start
= *offset
;
239 rbm_set_info(pinfo
, "Hash");
240 get_rbm_integer(tvb
, *offset
, &value
, &len
);
241 proto_tree_add_int_format_value(tree
, hf_rbm_length
, tvb
, *offset
, len
, value
, "%d", value
);
242 hash_tree
= proto_tree_add_subtree(tree
, tvb
, *offset
, 0, ett_hash
, NULL
, "Hash");
245 for (i
= 0; i
< value
; i
++) {
246 hash_obj_tree
= proto_tree_add_subtree(hash_tree
, tvb
, *offset
, 0, ett_hash_obj
, NULL
, "Entry");
247 hash_key_tree
= proto_tree_add_subtree(hash_obj_tree
, tvb
, *offset
, 0, ett_hash_obj
, NULL
, "Key");
248 dissect_rbm_object(tvb
, pinfo
, hash_key_tree
, offset
, NULL
, &hkey
);
249 hash_value_tree
= proto_tree_add_subtree(hash_obj_tree
, tvb
, *offset
, 0, ett_hash_obj
, NULL
, "Value");
250 dissect_rbm_object(tvb
, pinfo
, hash_value_tree
, offset
, NULL
, &hval
);
251 proto_item_append_text(hash_obj_tree
, " %s => %s", hkey
, hval
);
253 proto_item_append_text(hash_tree
, " (%d)", value
);
254 proto_item_set_len(hash_tree
, *offset
- offset_start
);
257 *value_str
= wmem_strdup_printf(pinfo
->pool
, "%d", value
);
260 static void dissect_rbm_link(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, uint8_t subtype
,
261 char** type
, char** value_str
)
275 DISSECTOR_ASSERT_NOT_REACHED();
278 rbm_set_info(pinfo
, wmem_strdup_printf(pinfo
->pool
, "%s Link", label
));
279 get_rbm_integer(tvb
, *offset
, &value
, &len
);
280 proto_tree_add_int_format_value(tree
, hf_rbm_link
, tvb
, *offset
, len
, value
, "%d", value
);
285 *value_str
= wmem_strdup_printf(pinfo
->pool
, "%d", value
);
288 static void dissect_rbm_double(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, char** value_str
)
295 rbm_set_info(pinfo
, "Double");
297 get_rbm_integer(tvb
, *offset
, &value
, &len
);
298 proto_tree_add_int_format_value(tree
, hf_rbm_length
, tvb
, *offset
, len
, value
, "%d", value
);
300 s
= (const char*)tvb_get_string_enc(pinfo
->pool
, tvb
, *offset
, value
, ENC_NA
);
301 valued
= g_ascii_strtod(s
, NULL
);
302 proto_tree_add_double(tree
, hf_rbm_double
, tvb
, *offset
, value
, valued
);
305 *value_str
= wmem_strdup_printf(pinfo
->pool
, "%f", valued
);
308 static void dissect_rbm_struct_data(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, char** value_str
)
313 if (tvb_get_uint8(tvb
, *offset
) != ':')
317 rbm_set_info(pinfo
, "Struct");
318 get_rbm_integer(tvb
, *offset
, &value
, &len
);
319 proto_tree_add_item(tree
, hf_rbm_struct
, tvb
, *offset
+ 1, value
, ENC_ASCII
);
320 *offset
+= 1 + value
;
322 *value_str
= wmem_strdup_printf(pinfo
->pool
, "%d", value
);
325 // NOLINTNEXTLINE(misc-no-recursion)
326 static void dissect_rbm_string(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, char** value
)
328 dissect_rbm_string_data(tvb
, pinfo
, tree
, offset
, "String", "", value
);
329 dissect_rbm_integer(tvb
, pinfo
, tree
, offset
, NULL
);
330 dissect_rbm_object(tvb
, pinfo
, tree
, offset
, NULL
, NULL
);
331 dissect_rbm_object(tvb
, pinfo
, tree
, offset
, NULL
, NULL
);
334 // NOLINTNEXTLINE(misc-no-recursion)
335 static void dissect_rbm_regex(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, char** value
)
337 dissect_rbm_string_data_trailer(tvb
, pinfo
, tree
, offset
, "Regexp", "/", "/", value
);
338 proto_tree_add_item(tree
, hf_rbm_regex_param
, tvb
, *offset
, 1, ENC_NA
);
340 dissect_rbm_integer(tvb
, pinfo
, tree
, offset
, NULL
);
341 dissect_rbm_object(tvb
, pinfo
, tree
, offset
, NULL
, NULL
);
342 dissect_rbm_object(tvb
, pinfo
, tree
, offset
, NULL
, NULL
);
345 static void dissect_rbm_class(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, char** value_str
)
347 dissect_rbm_string_data(tvb
, pinfo
, tree
, offset
, "Class", "", value_str
);
350 // NOLINTNEXTLINE(misc-no-recursion)
351 static void dissect_rbm_userclass(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, char** value
)
353 rbm_set_info(pinfo
, "UserClass");
354 dissect_rbm_object(tvb
, pinfo
, tree
, offset
, NULL
, value
);
357 static void dissect_rbm_symbol(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, char** value_str
)
359 dissect_rbm_string_data(tvb
, pinfo
, tree
, offset
, "Symbol", ":", value_str
);
362 // NOLINTNEXTLINE(misc-no-recursion)
363 static void dissect_rbm_variable(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, char** value_str
)
365 int offset_start
= *offset
;
366 proto_tree
* variable_tree
= proto_tree_add_subtree(tree
, tvb
, *offset
, 0, ett_variable
, NULL
, "Variable");
367 dissect_rbm_object(tvb
, pinfo
, variable_tree
, offset
, NULL
, value_str
);
368 proto_item_set_len(variable_tree
, *offset
- offset_start
);
371 static void dissect_rbm_module(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, char** value_str
)
373 dissect_rbm_string_data(tvb
, pinfo
, tree
, offset
, "Module", "", value_str
);
376 // NOLINTNEXTLINE(misc-no-recursion)
377 static void dissect_rbm_struct(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, char** value
)
379 dissect_rbm_struct_data(tvb
, pinfo
, tree
, offset
, value
);
380 dissect_rbm_hash(tvb
, pinfo
, tree
, offset
, NULL
);
383 // NOLINTNEXTLINE(misc-no-recursion)
384 static void dissect_rbm_drb(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
)
386 int offset_start
= *offset
;
387 proto_tree
* drb_tree
= proto_tree_add_subtree(tree
, tvb
, *offset
, 0, ett_variable
, NULL
, "Objects");
388 dissect_rbm_object(tvb
, pinfo
, drb_tree
, offset
, NULL
, NULL
);
389 dissect_rbm_object(tvb
, pinfo
, drb_tree
, offset
, NULL
, NULL
);
390 proto_item_set_len(drb_tree
, *offset
- offset_start
);
393 // NOLINTNEXTLINE(misc-no-recursion)
394 static void dissect_rbm_rubyobject(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
)
396 int offset_start
= *offset
;
397 proto_tree
* obj_tree
= proto_tree_add_subtree(tree
, tvb
, *offset
, 0, ett_variable
, NULL
, "Ruby Object");
399 rbm_set_info(pinfo
, "Ruby Object");
401 dissect_rbm_object(tvb
, pinfo
, obj_tree
, offset
, NULL
, NULL
);
402 dissect_rbm_hash(tvb
, pinfo
, obj_tree
, offset
, NULL
);
404 while (tvb_captured_length_remaining(tvb
, *offset
)) {
405 dissect_rbm_object(tvb
, pinfo
, obj_tree
, offset
, NULL
, NULL
);
408 proto_item_set_len(obj_tree
, *offset
- offset_start
);
411 // NOLINTNEXTLINE(misc-no-recursion)
412 static void dissect_rbm_extended(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
)
414 int offset_start
= *offset
;
415 proto_tree
* ext_tree
= proto_tree_add_subtree(tree
, tvb
, *offset
, 0, ett_variable
, NULL
, "Extended");
417 rbm_set_info(pinfo
, "Extended");
418 dissect_rbm_object(tvb
, pinfo
, ext_tree
, offset
, NULL
, NULL
);
419 proto_item_set_len(ext_tree
, *offset
- offset_start
);
422 // NOLINTNEXTLINE(misc-no-recursion)
423 static void dissect_rbm_object(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* ptree
, unsigned* offset
, char** type
, char** value
)
425 uint8_t subtype
= tvb_get_uint8(tvb
, *offset
);
427 char* type_local
= "Unknown";
428 char* value_local
= "Unknown";
429 int offset_start
= *offset
;
431 tree
= proto_tree_add_subtree(ptree
, tvb
, *offset
, 0, ett_variable
, NULL
, "");
433 proto_tree_add_item(tree
, hf_rbm_type
, tvb
, *offset
, 1, ENC_NA
);
436 increment_dissection_depth(pinfo
);
442 dissect_rbm_basic(tvb
, pinfo
, tree
, offset
, subtype
, &type_local
, &value_local
);
445 type_local
= "Integer";
446 dissect_rbm_integer(tvb
, pinfo
, tree
, offset
, &value_local
);
449 type_local
= "Symbol";
450 dissect_rbm_symbol(tvb
, pinfo
, tree
, offset
, &value_local
);
453 type_local
= "String";
454 dissect_rbm_string(tvb
, pinfo
, tree
, offset
, &value_local
);
457 type_local
= "Instance Variable";
458 dissect_rbm_variable(tvb
, pinfo
, tree
, offset
, &value_local
);
461 type_local
= "Array";
462 dissect_rbm_array(tvb
, pinfo
, tree
, offset
, &value_local
);
466 dissect_rbm_hash(tvb
, pinfo
, tree
, offset
, &value_local
);
470 dissect_rbm_link(tvb
, pinfo
, tree
, offset
, subtype
, &type_local
, &value_local
);
473 type_local
= "Double";
474 dissect_rbm_double(tvb
, pinfo
, tree
, offset
, &value_local
);
477 type_local
= "Class";
478 dissect_rbm_class(tvb
, pinfo
, tree
, offset
, &value_local
);
481 type_local
= "Module";
482 dissect_rbm_module(tvb
, pinfo
, tree
, offset
, &value_local
);
485 type_local
= "Struct";
486 dissect_rbm_struct(tvb
, pinfo
, tree
, offset
, &value_local
);
489 type_local
= "Regex";
490 dissect_rbm_regex(tvb
, pinfo
, tree
, offset
, &value_local
);
493 type_local
= "DRb::DRbObject";
494 dissect_rbm_drb(tvb
, pinfo
, tree
, offset
);
497 dissect_rbm_inline(tvb
, pinfo
, tree
, offset
, &type_local
, &value_local
);
500 dissect_rbm_rubyobject(tvb
, pinfo
, tree
, offset
);
501 type_local
= "Ruby Object";
504 type_local
= "UserClass";
505 dissect_rbm_userclass(tvb
, pinfo
, tree
, offset
, &value_local
);
508 type_local
= "Extended Object";
509 dissect_rbm_extended(tvb
, pinfo
, tree
, offset
);
512 expert_add_info_format(pinfo
, tree
, &ei_rbm_invalid
,
513 "Object type 0x%x is invalid", subtype
);
514 *offset
+= tvb_reported_length_remaining(tvb
, *offset
);
517 proto_item_set_len(tree
, *offset
- offset_start
);
519 proto_item_append_text(tree
, "Type: %s", type_local
);
520 if (value_local
&& strlen(value_local
))
521 proto_item_append_text(tree
, ", Value: %s", value_local
);
526 *value
= value_local
;
528 decrement_dissection_depth(pinfo
);
531 static bool dissect_rbm_header(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
)
537 major
= tvb_get_uint8(tvb
, *offset
);
538 minor
= tvb_get_uint8(tvb
, *offset
+ 1);
540 version
= wmem_strdup_printf(pinfo
->pool
, "%u.%u", major
, minor
);
541 proto_tree_add_string_format(tree
, hf_rbm_version
, tvb
, *offset
, 2, version
, "Version: %s", version
);
544 if (major
!= RUBY_MARSHAL_MAJOR
|| minor
!= RUBY_MARSHAL_MINOR
) {
545 expert_add_info_format(pinfo
, tree
, &ei_rbm_version_unsupported
, "Version %u.%u is not supported (only %u.%u)",
546 major
, minor
, RUBY_MARSHAL_MAJOR
, RUBY_MARSHAL_MINOR
);
552 // NOLINTNEXTLINE(misc-no-recursion)
553 void dissect_rbm_inline(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, unsigned* offset
, char** type
, char** value
)
555 if (!dissect_rbm_header(tvb
, pinfo
, tree
, offset
))
557 dissect_rbm_object(tvb
, pinfo
, tree
, offset
, type
, value
);
560 static int dissect_rbm(tvbuff_t
* tvb
, packet_info
* pinfo
, proto_tree
* tree
, void* data _U_
)
564 proto_tree
* rbm_tree
;
566 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "Rbm");
567 col_clear(pinfo
->cinfo
, COL_INFO
);
569 ti
= proto_tree_add_item(tree
, proto_rbm
, tvb
, 0, -1, ENC_NA
);
570 rbm_tree
= proto_item_add_subtree(ti
, ett_rbm
);
572 dissect_rbm_inline(tvb
, pinfo
, rbm_tree
, &offset
, NULL
, NULL
);
576 void proto_register_rbm(void)
578 expert_module_t
* expert_rbm
;
580 static hf_register_info hf
[] = {
582 { "Version", "rbm.version", FT_STRING
, BASE_NONE
, NULL
, 0x00, NULL
, HFILL
}
585 { "Type", "rbm.type", FT_UINT8
, BASE_HEX
, VALS(rbm_types
), 0x00, NULL
, HFILL
}
588 { "Integer", "rbm.int", FT_INT32
, BASE_DEC
, NULL
, 0x00, NULL
, HFILL
}
591 { "Length", "rbm.length", FT_INT32
, BASE_DEC
, NULL
, 0x00, NULL
, HFILL
}
594 { "Value", "rbm.string", FT_STRING
, BASE_NONE
, NULL
, 0x00, NULL
, HFILL
}
597 { "Link to object", "rbm.link", FT_INT32
, BASE_DEC
, NULL
, 0x00, NULL
, HFILL
}
600 { "Value", "rbm.double", FT_DOUBLE
, BASE_NONE
, NULL
, 0x00, NULL
, HFILL
}
603 { "Struct", "rbm.struct", FT_STRING
, BASE_NONE
, NULL
, 0x00, NULL
, HFILL
}
605 { &hf_rbm_regex_param
,
606 { "Regexp parameter", "rbm.regex.param", FT_UINT8
, BASE_DEC
, NULL
, 0x00, NULL
, HFILL
}
610 static ei_register_info ei
[] = {
611 { &ei_rbm_invalid
, { "rbm.invalid", PI_UNDECODED
, PI_WARN
, "Invalid type", EXPFILL
}},
612 { &ei_rbm_version_unsupported
, { "rbm.version.unsupported", PI_UNDECODED
, PI_WARN
, "Unsupported version", EXPFILL
}}
615 /* Setup protocol subtree array */
616 static int* ett
[] = {
625 proto_rbm
= proto_register_protocol("Ruby Marshal Object", "Rbm", "rbm");
627 expert_rbm
= expert_register_protocol(proto_rbm
);
628 expert_register_field_array(expert_rbm
, ei
, array_length(ei
));
630 proto_register_field_array(proto_rbm
, hf
, array_length(hf
));
631 proto_register_subtree_array(ett
, array_length(ett
));
633 rbm_file_handle
= register_dissector("rbm", dissect_rbm
, proto_rbm
);
636 void proto_reg_handoff_rbm(void)
638 dissector_add_uint("wtap_encap", WTAP_ENCAP_RUBY_MARSHAL
, rbm_file_handle
);
642 * Editor modelines - https://www.wireshark.org/tools/modelines.html
647 * indent-tabs-mode: t
650 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
651 * :indentSize=8:tabSize=8:noTabs=false: