/src/wireshark/epan/dissectors/packet-moq-file.c
Line | Count | Source |
1 | | /* packet-moq-file.c |
2 | | * Dissector for MoQ Object File records produced by wiretap/moq.c. |
3 | | * SPDX-License-Identifier: GPL-2.0-or-later |
4 | | */ |
5 | | #include "config.h" |
6 | | |
7 | | #include <epan/packet.h> |
8 | | #include <wsutil/wsjson.h> |
9 | | #include <inttypes.h> |
10 | | #include <stdlib.h> |
11 | | #include <string.h> |
12 | | |
13 | | void proto_register_moq_file(void); |
14 | | |
15 | | static int proto_moq_file; |
16 | | static int hf_moq_metadata_length; |
17 | | static int hf_moq_metadata; |
18 | | static int hf_moq_track_namespace; |
19 | | static int hf_moq_track_name; |
20 | | static int hf_moq_object_id; |
21 | | static int hf_moq_group_id; |
22 | | static int hf_moq_subgroup_id; |
23 | | static int hf_moq_forwarding_pref; |
24 | | static int hf_moq_object_status; |
25 | | static int hf_moq_publisher_priority; |
26 | | static int hf_moq_receive_time; |
27 | | static int hf_moq_data_file; |
28 | | static int hf_moq_data_offset; |
29 | | static int hf_moq_data_length; |
30 | | static int hf_moq_object_data; |
31 | | static gint ett_moq_file; |
32 | | static gint ett_moq_metadata; |
33 | | |
34 | | static bool |
35 | | moq_add_int_field(proto_tree *tree, int hf, tvbuff_t *metadata_tvb, |
36 | | char *json, jsmntok_t *object, const char *key) |
37 | 0 | { |
38 | 0 | int64_t value; |
39 | 0 | if (!json_get_int(json, object, key, &value)) return false; |
40 | 0 | proto_tree_add_int64(tree, hf, metadata_tvb, 0, |
41 | 0 | tvb_captured_length(metadata_tvb), value); |
42 | 0 | return true; |
43 | 0 | } |
44 | | |
45 | | static bool |
46 | | moq_add_string_field(proto_tree *tree, int hf, tvbuff_t *metadata_tvb, |
47 | | char *json, jsmntok_t *object, const char *key) |
48 | 0 | { |
49 | 0 | char *value = json_get_string(json, object, key); |
50 | 0 | if (value == NULL) return false; |
51 | 0 | proto_tree_add_string(tree, hf, metadata_tvb, 0, |
52 | 0 | tvb_captured_length(metadata_tvb), value); |
53 | | /* json_get_string() returns a pointer into json; do not free it. */ |
54 | 0 | return true; |
55 | 0 | } |
56 | | |
57 | | static int |
58 | | dissect_moq_file(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
59 | 0 | { |
60 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "MoQ File"); |
61 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
62 | |
|
63 | 0 | proto_item *root_item = proto_tree_add_item(tree, proto_moq_file, tvb, 0, -1, ENC_NA); |
64 | 0 | proto_tree *root_tree = proto_item_add_subtree(root_item, ett_moq_file); |
65 | 0 | if (tvb_captured_length(tvb) < 4) { |
66 | 0 | return tvb_captured_length(tvb); |
67 | 0 | } |
68 | | |
69 | 0 | uint32_t metadata_len = tvb_get_ntohl(tvb, 0); |
70 | 0 | proto_tree_add_uint(root_tree, hf_moq_metadata_length, tvb, 0, 4, metadata_len); |
71 | 0 | gint metadata_remaining = tvb_captured_length_remaining(tvb, 4); |
72 | 0 | if (metadata_remaining < 0 || metadata_len > (uint32_t)metadata_remaining) { |
73 | 0 | proto_item_set_len(root_item, 4); |
74 | 0 | return tvb_captured_length(tvb); |
75 | 0 | } |
76 | | |
77 | 0 | tvbuff_t *metadata_tvb = tvb_new_subset_length(tvb, 4, metadata_len); |
78 | 0 | char *json = (char *)tvb_get_string_enc(pinfo->pool, metadata_tvb, 0, metadata_len, ENC_UTF_8 | ENC_NA); |
79 | 0 | int num_tokens = json_parse_len(json, metadata_len, NULL, 0); |
80 | 0 | jsmntok_t *tokens = NULL; |
81 | 0 | char *track_name = NULL; |
82 | 0 | int64_t object_id = 0, group_id = 0, subgroup_id = 0; |
83 | 0 | bool has_object_id = false, has_group_id = false, has_subgroup_id = false; |
84 | 0 | proto_tree *meta_tree = proto_tree_add_subtree(root_tree, tvb, 4, metadata_len, ett_moq_metadata, NULL, "MoQ object metadata"); |
85 | 0 | proto_tree_add_string(meta_tree, hf_moq_metadata, tvb, 4, metadata_len, json); |
86 | |
|
87 | 0 | if (num_tokens > 0) { |
88 | 0 | tokens = g_new0(jsmntok_t, num_tokens); |
89 | 0 | if (json_parse_len(json, metadata_len, tokens, (unsigned int)num_tokens) > 0 && tokens[0].type == JSMN_OBJECT) { |
90 | 0 | track_name = json_get_string(json, &tokens[0], "trackName"); |
91 | 0 | has_object_id = json_get_int(json, &tokens[0], "objectID", &object_id); |
92 | 0 | has_group_id = json_get_int(json, &tokens[0], "groupID", &group_id); |
93 | 0 | has_subgroup_id = json_get_int(json, &tokens[0], "subgroupID", &subgroup_id); |
94 | 0 | if (!has_subgroup_id) |
95 | 0 | has_subgroup_id = json_get_int(json, &tokens[0], "subGroup", &subgroup_id); |
96 | 0 | if (track_name) |
97 | 0 | proto_tree_add_string(meta_tree, hf_moq_track_name, metadata_tvb, 0, tvb_captured_length(metadata_tvb), track_name); |
98 | 0 | moq_add_string_field(meta_tree, hf_moq_forwarding_pref, metadata_tvb, json, &tokens[0], "forwardingPref"); |
99 | 0 | moq_add_string_field(meta_tree, hf_moq_data_file, metadata_tvb, json, &tokens[0], "dataFile"); |
100 | 0 | moq_add_int_field(meta_tree, hf_moq_object_id, metadata_tvb, json, &tokens[0], "objectID"); |
101 | 0 | moq_add_int_field(meta_tree, hf_moq_group_id, metadata_tvb, json, &tokens[0], "groupID"); |
102 | 0 | if (!moq_add_int_field(meta_tree, hf_moq_subgroup_id, metadata_tvb, json, &tokens[0], "subgroupID")) |
103 | 0 | moq_add_int_field(meta_tree, hf_moq_subgroup_id, metadata_tvb, json, &tokens[0], "subGroup"); |
104 | 0 | moq_add_int_field(meta_tree, hf_moq_object_status, metadata_tvb, json, &tokens[0], "objectStatus"); |
105 | 0 | moq_add_int_field(meta_tree, hf_moq_publisher_priority, metadata_tvb, json, &tokens[0], "publisherPriority"); |
106 | 0 | moq_add_int_field(meta_tree, hf_moq_receive_time, metadata_tvb, json, &tokens[0], "receiveTime"); |
107 | 0 | moq_add_int_field(meta_tree, hf_moq_data_offset, metadata_tvb, json, &tokens[0], "dataOffset"); |
108 | 0 | moq_add_int_field(meta_tree, hf_moq_data_length, metadata_tvb, json, &tokens[0], "dataLength"); |
109 | 0 | jsmntok_t *ns = json_get_array(json, &tokens[0], "trackNamespace"); |
110 | 0 | if (ns == NULL) ns = json_get_array(json, &tokens[0], "namesSpace"); |
111 | 0 | if (ns != NULL) { |
112 | | /* json_get_*() NUL-terminate values in place, so json + ns->start |
113 | | * is not terminated at the end of the array; copy the span. */ |
114 | 0 | proto_tree_add_string(meta_tree, hf_moq_track_namespace, tvb, 4, metadata_len, |
115 | 0 | wmem_strndup(pinfo->pool, json + ns->start, (size_t)(ns->end - ns->start))); |
116 | 0 | } |
117 | 0 | } |
118 | 0 | g_free(tokens); |
119 | 0 | } |
120 | |
|
121 | 0 | gint object_offset = 4 + (gint)metadata_len; |
122 | 0 | gint object_length = tvb_captured_length_remaining(tvb, object_offset); |
123 | 0 | if (object_length > 0) { |
124 | 0 | proto_tree_add_item(root_tree, hf_moq_object_data, tvb, object_offset, object_length, ENC_NA); |
125 | 0 | if (track_name != NULL && has_group_id && has_object_id && has_subgroup_id) { |
126 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, |
127 | 0 | "Track %s G=%" PRId64 " S=%" PRId64 " O=%" PRId64 ", %u bytes", |
128 | 0 | track_name, group_id, subgroup_id, object_id, object_length); |
129 | 0 | } else if (track_name != NULL && has_group_id && has_object_id) { |
130 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, |
131 | 0 | "Track %s G=%" PRId64 " O=%" PRId64 ", %u bytes", |
132 | 0 | track_name, group_id, object_id, object_length); |
133 | 0 | } else if (track_name != NULL && has_object_id) { |
134 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, "Track %s O=%" PRId64 ", %u bytes", |
135 | 0 | track_name, object_id, object_length); |
136 | 0 | } else { |
137 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, "Object data: %u bytes", object_length); |
138 | 0 | } |
139 | 0 | } |
140 | 0 | return tvb_captured_length(tvb); |
141 | 0 | } |
142 | | |
143 | | void |
144 | | proto_register_moq_file(void) |
145 | 16 | { |
146 | 16 | static hf_register_info hf[] = { |
147 | 16 | { &hf_moq_metadata_length, { "Metadata length", "moq_file.metadata_length", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
148 | 16 | { &hf_moq_metadata, { "Metadata JSON", "moq_file.metadata", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
149 | 16 | { &hf_moq_track_namespace, { "Track namespace (Base64URL JSON array)", "moq_file.track_namespace", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
150 | 16 | { &hf_moq_track_name, { "Track name (Base64URL)", "moq_file.track_name", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
151 | 16 | { &hf_moq_object_id, { "Object ID", "moq_file.object_id", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
152 | 16 | { &hf_moq_group_id, { "Group ID", "moq_file.group_id", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
153 | 16 | { &hf_moq_subgroup_id, { "Subgroup ID", "moq_file.subgroup_id", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
154 | 16 | { &hf_moq_forwarding_pref, { "Forwarding preference", "moq_file.forwarding_pref", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
155 | 16 | { &hf_moq_object_status, { "Object status", "moq_file.object_status", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
156 | 16 | { &hf_moq_publisher_priority, { "Publisher priority", "moq_file.publisher_priority", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
157 | 16 | { &hf_moq_receive_time, { "Receive time (Unix milliseconds)", "moq_file.receive_time", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
158 | 16 | { &hf_moq_data_file, { "Data file", "moq_file.data_file", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
159 | 16 | { &hf_moq_data_offset, { "Data offset", "moq_file.data_offset", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
160 | 16 | { &hf_moq_data_length, { "Data length", "moq_file.data_length", FT_INT64, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
161 | 16 | { &hf_moq_object_data, { "MoQT object bytes (header and payload)", "moq_file.object_data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
162 | 16 | }; |
163 | 16 | static gint *ett[] = { &ett_moq_file, &ett_moq_metadata }; |
164 | | |
165 | 16 | proto_moq_file = proto_register_protocol("MoQ Object File", "MoQ File", "moq_file"); |
166 | 16 | proto_register_field_array(proto_moq_file, hf, array_length(hf)); |
167 | 16 | proto_register_subtree_array(ett, array_length(ett)); |
168 | 16 | register_dissector("moq_file", dissect_moq_file, proto_moq_file); |
169 | 16 | } |