Coverage Report

Created: 2026-01-02 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/wiretap/pcapng-netflix-custom.c
Line
Count
Source
1
/** @file
2
 *
3
 * Wireshark - Network traffic analyzer
4
 * By Gerald Combs <gerald@wireshark.org>
5
 * Copyright 2001 Gerald Combs
6
 *
7
 * SPDX-License-Identifier: GPL-2.0-or-later
8
 */
9
10
#include "config.h"
11
#include "wtap_module.h"
12
#include "wtap_opttypes.h"
13
#include "pcapng.h"
14
#include "pcapng_module.h"
15
#include "pcapng-netflix-custom.h"
16
17
/*
18
 * Per-section information managed and used for Netflix BBLog blocks
19
 * and options.
20
 */
21
typedef struct {
22
    uint32_t bblog_version;        /**< BBLog: version used */
23
    uint64_t bblog_offset_tv_sec;  /**< BBLog: UTC offset */
24
    uint64_t bblog_offset_tv_usec;
25
} pcapng_nflx_per_section_t;
26
27
typedef struct pcapng_nflx_custom_block_s {
28
    uint32_t nflx_type;
29
} pcapng_nflx_custom_block_t;
30
31
static void *
32
new_nflx_custom_block_data(void)
33
0
{
34
0
    return g_new0(pcapng_nflx_per_section_t, 1);
35
0
}
36
37
static const section_info_funcs_t nflx_custom_block_data_funcs = {
38
  new_nflx_custom_block_data,
39
  g_free
40
};
41
42
static pcapng_nflx_per_section_t *
43
get_nflx_custom_blocK_data(section_info_t *section_info)
44
0
{
45
0
    return pcapng_get_cb_section_info_data(section_info, PEN_NFLX,
46
0
                                           &nflx_custom_block_data_funcs);
47
0
}
48
49
/*
50
 * Minimum length of the payload (custom block data plus options) of a
51
 * Netflix custom bock.
52
 */
53
0
#define MIN_NFLX_CB_SIZE ((uint32_t)sizeof(pcapng_nflx_custom_block_t))
54
55
static bool
56
pcapng_read_nflx_custom_block(FILE_T fh, section_info_t *section_info,
57
                              wtapng_block_t *wblock,
58
                              int *err, char **err_info)
59
0
{
60
0
    pcapng_nflx_custom_block_t nflx_cb;
61
0
    unsigned opt_cont_buf_len;
62
0
    uint32_t type, skipped;
63
0
    wtapng_nflx_custom_mandatory_t *mandatory_data;
64
65
    /*
66
     * Set the record type name for this particular type of custom
67
     * block.
68
     */
69
0
    wblock->rec->rec_type_name = "Black Box Log Block";
70
0
    if (wblock->rec->rec_header.custom_block_header.length < MIN_NFLX_CB_SIZE) {
71
0
        *err = WTAP_ERR_REC_MALFORMED;
72
0
        *err_info = ws_strdup_printf("pcapng: payload length %u of a Netflix CB is too small (< %u)",
73
0
                                     wblock->rec->rec_header.custom_block_header.length,
74
0
                                     MIN_NFLX_CB_SIZE);
75
0
        return false;
76
0
    }
77
78
    /* "NFLX Custom Block" read fixed part */
79
0
    if (!wtap_read_bytes(fh, &nflx_cb, sizeof nflx_cb, err, err_info)) {
80
0
        ws_debug("Failed to read nflx type");
81
0
        return false;
82
0
    }
83
84
    /*
85
     * Allocate mandatory data.
86
     */
87
0
    wblock->block->mandatory_data = g_new0(wtapng_nflx_custom_mandatory_t, 1);
88
0
    mandatory_data = (wtapng_nflx_custom_mandatory_t *)wblock->block->mandatory_data;
89
0
    type = GUINT32_FROM_LE(nflx_cb.nflx_type);
90
0
    mandatory_data->type = type;
91
0
    ws_debug("BBLog type: %u", type);
92
0
    switch (type) {
93
0
        case NFLX_BLOCK_TYPE_EVENT:
94
            /*
95
             * The fixed-length portion is MIN_NFLX_CB_SIZE bytes.
96
             * We already know we have that much data in the block.
97
             */
98
0
            opt_cont_buf_len = wblock->rec->rec_header.custom_block_header.length - MIN_NFLX_CB_SIZE;
99
0
            ws_debug("event");
100
0
            break;
101
0
        case NFLX_BLOCK_TYPE_SKIP:
102
            /*
103
             * The fixed-length portion is MIN_NFLX_CB_SIZE bytes plus a
104
             * 32-bit value.
105
             *
106
             * Make sure we have that much data in the block.
107
             */
108
0
            if (wblock->rec->rec_header.custom_block_header.length < MIN_NFLX_CB_SIZE + (uint32_t)sizeof(uint32_t)) {
109
0
                *err = WTAP_ERR_REC_MALFORMED;
110
0
                *err_info = ws_strdup_printf("pcapng: payload length %u of a Netflix skip CB is too small (< %u)",
111
0
                                             wblock->rec->rec_header.custom_block_header.length,
112
0
                                             MIN_NFLX_CB_SIZE + (uint32_t)sizeof(uint32_t));
113
0
                return false;
114
0
            }
115
0
            if (!wtap_read_bytes(fh, &skipped, sizeof(uint32_t), err, err_info)) {
116
0
                ws_debug("Failed to read skipped");
117
0
                return false;
118
0
            }
119
0
            opt_cont_buf_len = wblock->rec->rec_header.custom_block_header.length - MIN_NFLX_CB_SIZE - sizeof(uint32_t);
120
0
            wblock->rec->presence_flags = 0;
121
0
            wblock->rec->rec_header.custom_block_header.length = 4;
122
0
            mandatory_data->skipped = GUINT32_FROM_LE(skipped);
123
0
            wblock->internal = false;
124
0
            ws_debug("skipped: %u", mandatory_data->skipped);
125
0
            break;
126
0
        default:
127
0
            ws_debug("Unknown type %u", type);
128
0
            *err = WTAP_ERR_UNSUPPORTED;
129
0
            *err_info = g_strdup_printf("pcapng Netflix BBLog block: unknown type %u", type);
130
0
            return false;
131
0
    }
132
133
    /*
134
     * Options.
135
     *
136
     * This block type supports only comments and custom options,
137
     * so it doesn't need a callback.
138
     */
139
0
    if (!pcapng_process_options(fh, wblock, section_info, opt_cont_buf_len,
140
0
                                NULL, OPT_LITTLE_ENDIAN, err, err_info))
141
0
        return false;
142
143
0
    return true;
144
0
}
145
146
/*
147
 * Everything in this is little-endian, regardless of the byte order
148
 * of the host that wrote the file.
149
 */
150
static bool
151
pcapng_process_nflx_custom_option(wtapng_block_t *wblock,
152
                                  section_info_t *section_info,
153
                                  uint16_t option_code,
154
                                  const uint8_t *value, uint16_t length)
155
0
{
156
0
    struct nflx_dumpinfo dumpinfo;
157
0
    uint32_t type, version;
158
0
    int64_t dumptime, temp;
159
0
    pcapng_nflx_per_section_t *nflx_per_section_info;
160
161
0
    if (length < 4) {
162
0
        ws_debug("Length = %u too small", length);
163
0
        return false;
164
0
    }
165
0
    if (wtap_block_add_custom_binary_option_from_data(wblock->block, option_code, PEN_NFLX, value, length) != WTAP_OPTTYPE_SUCCESS)
166
0
        return false;
167
0
    memcpy(&type, value, sizeof(uint32_t));
168
0
    type = GUINT32_FROM_LE(type);
169
0
    value += 4;
170
0
    length -= 4;
171
0
    ws_debug("Handling type = %u, payload of length = %u", type, length);
172
0
    switch (type) {
173
0
    case NFLX_OPT_TYPE_VERSION:
174
0
        if (length == sizeof(uint32_t)) {
175
0
            memcpy(&version, value, sizeof(uint32_t));
176
0
            version = GUINT32_FROM_LE(version);
177
0
            ws_debug("BBLog version: %u", version);
178
0
            nflx_per_section_info = get_nflx_custom_blocK_data(section_info);
179
0
            nflx_per_section_info->bblog_version = version;
180
0
        } else {
181
0
            ws_debug("BBLog version parameter has strange length: %u", length);
182
0
        }
183
0
        break;
184
0
    case NFLX_OPT_TYPE_TCPINFO:
185
0
        ws_debug("BBLog tcpinfo of length: %u", length);
186
0
        if (wblock->type == BLOCK_TYPE_CB_COPY) {
187
            /*
188
             * This is in a BBlog custom block; we append the option's
189
             * value to the data of the block, and use times from
190
             * the option to set the time stamp.
191
             */
192
0
            ws_buffer_assure_space(&wblock->rec->data, length);
193
0
            wblock->rec->rec_header.custom_block_header.length = length + 4;
194
0
            memcpy(ws_buffer_start_ptr(&wblock->rec->data), value, length);
195
0
            memcpy(&temp, value, sizeof(uint64_t));
196
0
            temp = GUINT64_FROM_LE(temp);
197
0
            nflx_per_section_info = get_nflx_custom_blocK_data(section_info);
198
0
            wblock->rec->ts.secs = nflx_per_section_info->bblog_offset_tv_sec + temp;
199
0
            memcpy(&temp, value + sizeof(uint64_t), sizeof(uint64_t));
200
0
            temp = GUINT64_FROM_LE(temp);
201
0
            wblock->rec->ts.nsecs = (uint32_t)(nflx_per_section_info->bblog_offset_tv_usec + temp) * 1000;
202
0
            if (wblock->rec->ts.nsecs >= 1000000000) {
203
0
                wblock->rec->ts.secs += 1;
204
0
                wblock->rec->ts.nsecs -= 1000000000;
205
0
            }
206
0
            wblock->rec->presence_flags = WTAP_HAS_TS;
207
0
            wblock->internal = false;
208
0
        }
209
0
        break;
210
0
    case NFLX_OPT_TYPE_DUMPINFO:
211
0
        if (length == sizeof(struct nflx_dumpinfo)) {
212
0
            memcpy(&dumpinfo, value, sizeof(struct nflx_dumpinfo));
213
0
            nflx_per_section_info = get_nflx_custom_blocK_data(section_info);
214
0
            nflx_per_section_info->bblog_offset_tv_sec = GUINT64_FROM_LE(dumpinfo.tlh_offset_tv_sec);
215
0
            nflx_per_section_info->bblog_offset_tv_usec = GUINT64_FROM_LE(dumpinfo.tlh_offset_tv_usec);
216
0
            ws_debug("BBLog dumpinfo time offset: %" PRIu64, nflx_per_section_info->bblog_offset_tv_sec);
217
0
        } else {
218
0
            ws_debug("BBLog dumpinfo parameter has strange length: %u", length);
219
0
        }
220
0
        break;
221
0
    case NFLX_OPT_TYPE_DUMPTIME:
222
0
        if (length == sizeof(int64_t)) {
223
0
            memcpy(&dumptime, value, sizeof(int64_t));
224
0
            dumptime = GINT64_FROM_LE(dumptime);
225
0
            ws_debug("BBLog dumpinfo time offset: %" PRIu64, dumptime);
226
0
        } else {
227
0
            ws_debug("BBLog dumptime parameter has strange length: %u", length);
228
0
        }
229
0
        break;
230
0
    case NFLX_OPT_TYPE_STACKNAME:
231
0
        if (length >= 2) {
232
0
            ws_debug("BBLog stack name: %.*s(%u)", length - 1, value + 1, *(uint8_t *)value);
233
0
        } else {
234
0
            ws_debug("BBLog stack name has strange length: %u)", length);
235
0
        }
236
0
        break;
237
0
    default:
238
0
        ws_debug("Unknown type: %u, length: %u", type, length);
239
0
        break;
240
0
    }
241
0
    return true;
242
0
}
243
244
static bool
245
pcapng_write_nflx_custom_block(wtap_dumper *wdh, const wtap_rec *rec, int *err,
246
                               char **err_info)
247
0
{
248
0
    pcapng_block_header_t bh;
249
0
    uint32_t options_size = 0;
250
0
    uint32_t pen, skipped, type;
251
0
    wtapng_nflx_custom_mandatory_t *mandatory_data;
252
253
    /*
254
     * Compute size of all the options.
255
     *
256
     * Only the universal options - comments and custom options -
257
     * are supported, so we need no option-processing routine.
258
     */
259
0
    options_size = pcapng_compute_options_size(rec->block, NULL);
260
261
0
    mandatory_data = (wtapng_nflx_custom_mandatory_t *)rec->block->mandatory_data;
262
263
    /* write block header */
264
0
    bh.block_type = BLOCK_TYPE_CB_COPY;
265
0
    bh.block_total_length = (uint32_t)(sizeof(bh) + sizeof(uint32_t) + sizeof(uint32_t) + options_size + 4);
266
0
    if (mandatory_data->type == NFLX_BLOCK_TYPE_SKIP) {
267
0
        bh.block_total_length += (uint32_t)sizeof(uint32_t);
268
0
    }
269
0
    ws_debug("writing %u bytes, type %u",
270
0
             bh.block_total_length, mandatory_data->type);
271
0
    if (!wtap_dump_file_write(wdh, &bh, sizeof(bh), err)) {
272
0
        return false;
273
0
    }
274
275
    /* write PEN */
276
0
    pen = PEN_NFLX;
277
0
    if (!wtap_dump_file_write(wdh, &pen, sizeof(uint32_t), err)) {
278
0
        return false;
279
0
    }
280
0
    ws_debug("wrote PEN = %u", pen);
281
282
    /* write type */
283
0
    type = GUINT32_TO_LE(mandatory_data->type);
284
0
    if (!wtap_dump_file_write(wdh, &type, sizeof(uint32_t), err)) {
285
0
        return false;
286
0
    }
287
0
    ws_debug("wrote type = %u", mandatory_data->type);
288
289
0
    if (mandatory_data->type == NFLX_BLOCK_TYPE_SKIP) {
290
0
        skipped = GUINT32_TO_LE(mandatory_data->skipped);
291
0
        if (!wtap_dump_file_write(wdh, &skipped, sizeof(uint32_t), err)) {
292
0
            return false;
293
0
        }
294
0
        ws_debug("wrote skipped = %u", mandatory_data->skipped);
295
0
    }
296
297
    /* Write options, if we have any */
298
0
    if (options_size != 0) {
299
        /*
300
         * This block type supports only comments and custom options,
301
         * so it doesn't need a callback.
302
         */
303
0
        if (!pcapng_write_options(wdh, OPT_LITTLE_ENDIAN, rec->block, NULL,
304
0
                                  err, err_info))
305
0
            return false;
306
0
    }
307
308
    /* write block footer */
309
0
    if (!wtap_dump_file_write(wdh, &bh.block_total_length,
310
0
                              sizeof bh.block_total_length, err)) {
311
0
        return false;
312
0
    }
313
314
0
    return true;
315
0
}
316
317
void register_nflx_custom(void)
318
14
{
319
14
    static const pcapng_custom_block_enterprise_handler_t enterprise_netflix =
320
14
    {
321
14
        pcapng_read_nflx_custom_block,
322
14
        pcapng_process_nflx_custom_option,
323
14
        pcapng_write_nflx_custom_block
324
14
    };
325
326
14
    register_pcapng_custom_block_enterprise_handler(PEN_NFLX, &enterprise_netflix);
327
14
}