Coverage Report

Created: 2026-11-12 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}