Coverage Report

Created: 2026-05-14 06:28

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
            mandatory_data->skipped = GUINT32_FROM_LE(skipped);
122
0
            wblock->internal = false;
123
0
            ws_debug("skipped: %u", mandatory_data->skipped);
124
0
            break;
125
0
        default:
126
0
            ws_debug("Unknown type %u", type);
127
0
            *err = WTAP_ERR_UNSUPPORTED;
128
0
            *err_info = g_strdup_printf("pcapng Netflix BBLog block: unknown type %u", type);
129
0
            return false;
130
0
    }
131
132
    /* This is used to set cap_len and pkt_len in the frame_data struct.
133
     * The former especially should match ws_buffer_length(&wblock->rec->data).
134
     * The only data written to the buffer, and that the BBLog dissector expects,
135
     * is the NFLX_OPT_TYPE_TCPINFO.  */
136
0
    wblock->rec->rec_header.custom_block_header.length = 0;
137
138
    /*
139
     * Options.
140
     *
141
     * This block type supports only comments and custom options,
142
     * so it doesn't need a callback.
143
     */
144
0
    if (!pcapng_process_options(fh, wblock, section_info, opt_cont_buf_len,
145
0
                                NULL, OPT_LITTLE_ENDIAN, err, err_info))
146
0
        return false;
147
148
0
    return true;
149
0
}
150
151
/*
152
 * Everything in this is little-endian, regardless of the byte order
153
 * of the host that wrote the file.
154
 */
155
static bool
156
pcapng_process_nflx_custom_option(wtapng_block_t *wblock,
157
                                  section_info_t *section_info,
158
                                  uint16_t option_code,
159
                                  const uint8_t *value, uint16_t length)
160
0
{
161
0
    struct nflx_dumpinfo dumpinfo;
162
0
    uint32_t type, version;
163
0
    int64_t dumptime, temp;
164
0
    pcapng_nflx_per_section_t *nflx_per_section_info;
165
166
0
    if (length < 4) {
167
0
        ws_debug("Length = %u too small", length);
168
0
        return false;
169
0
    }
170
0
    if (wtap_block_add_custom_binary_option_from_data(wblock->block, option_code, PEN_NFLX, value, length) != WTAP_OPTTYPE_SUCCESS)
171
0
        return false;
172
0
    memcpy(&type, value, sizeof(uint32_t));
173
0
    type = GUINT32_FROM_LE(type);
174
0
    value += 4;
175
0
    length -= 4;
176
0
    ws_debug("Handling type = %u, payload of length = %u", type, length);
177
    /* XXX - Can any of these options occur more than once? If not, should
178
     * there be an error or warning if they do? */
179
0
    switch (type) {
180
0
    case NFLX_OPT_TYPE_VERSION:
181
0
        if (length == sizeof(uint32_t)) {
182
0
            memcpy(&version, value, sizeof(uint32_t));
183
0
            version = GUINT32_FROM_LE(version);
184
0
            ws_debug("BBLog version: %u", version);
185
0
            nflx_per_section_info = get_nflx_custom_blocK_data(section_info);
186
0
            nflx_per_section_info->bblog_version = version;
187
0
        } else {
188
0
            ws_debug("BBLog version parameter has strange length: %u", length);
189
0
        }
190
0
        break;
191
0
    case NFLX_OPT_TYPE_TCPINFO:
192
0
        ws_debug("BBLog tcpinfo of length: %u", length);
193
0
        if (wblock->type == BLOCK_TYPE_CB_COPY) {
194
            /*
195
             * This is in a BBlog custom block; we append the option's
196
             * value to the data of the block, and use times from
197
             * the option to set the time stamp.
198
             */
199
0
            wblock->rec->rec_header.custom_block_header.length += length;
200
0
            ws_buffer_append(&wblock->rec->data, value, length);
201
0
            memcpy(&temp, value, sizeof(uint64_t));
202
0
            temp = GUINT64_FROM_LE(temp);
203
0
            nflx_per_section_info = get_nflx_custom_blocK_data(section_info);
204
0
            wblock->rec->ts.secs = nflx_per_section_info->bblog_offset_tv_sec + temp;
205
0
            memcpy(&temp, value + sizeof(uint64_t), sizeof(uint64_t));
206
0
            temp = GUINT64_FROM_LE(temp);
207
0
            wblock->rec->ts.nsecs = (uint32_t)(nflx_per_section_info->bblog_offset_tv_usec + temp) * 1000;
208
0
            if (wblock->rec->ts.nsecs >= 1000000000) {
209
0
                wblock->rec->ts.secs += 1;
210
0
                wblock->rec->ts.nsecs -= 1000000000;
211
0
            }
212
0
            wblock->rec->presence_flags = WTAP_HAS_TS;
213
0
            wblock->internal = false;
214
0
        }
215
0
        break;
216
0
    case NFLX_OPT_TYPE_DUMPINFO:
217
0
        if (length == sizeof(struct nflx_dumpinfo)) {
218
0
            memcpy(&dumpinfo, value, sizeof(struct nflx_dumpinfo));
219
0
            nflx_per_section_info = get_nflx_custom_blocK_data(section_info);
220
0
            nflx_per_section_info->bblog_offset_tv_sec = GUINT64_FROM_LE(dumpinfo.tlh_offset_tv_sec);
221
0
            nflx_per_section_info->bblog_offset_tv_usec = GUINT64_FROM_LE(dumpinfo.tlh_offset_tv_usec);
222
0
            ws_debug("BBLog dumpinfo time offset: %" PRIu64, nflx_per_section_info->bblog_offset_tv_sec);
223
0
        } else {
224
0
            ws_debug("BBLog dumpinfo parameter has strange length: %u", length);
225
0
        }
226
0
        break;
227
0
    case NFLX_OPT_TYPE_DUMPTIME:
228
0
        if (length == sizeof(int64_t)) {
229
0
            memcpy(&dumptime, value, sizeof(int64_t));
230
0
            dumptime = GINT64_FROM_LE(dumptime);
231
0
            ws_debug("BBLog dumpinfo time offset: %" PRIu64, dumptime);
232
0
        } else {
233
0
            ws_debug("BBLog dumptime parameter has strange length: %u", length);
234
0
        }
235
0
        break;
236
0
    case NFLX_OPT_TYPE_STACKNAME:
237
0
        if (length >= 2) {
238
0
            ws_debug("BBLog stack name: %.*s(%u)", length - 1, value + 1, *(uint8_t *)value);
239
0
        } else {
240
0
            ws_debug("BBLog stack name has strange length: %u)", length);
241
0
        }
242
0
        break;
243
0
    default:
244
0
        ws_debug("Unknown type: %u, length: %u", type, length);
245
0
        break;
246
0
    }
247
0
    return true;
248
0
}
249
250
static bool
251
pcapng_write_nflx_custom_block(wtap_dumper *wdh, const wtap_rec *rec, int *err,
252
                               char **err_info)
253
0
{
254
0
    pcapng_block_header_t bh;
255
0
    uint32_t options_size = 0;
256
0
    uint32_t pen, skipped, type;
257
0
    wtapng_nflx_custom_mandatory_t *mandatory_data;
258
259
    /*
260
     * Compute size of all the options.
261
     *
262
     * Only the universal options - comments and custom options -
263
     * are supported, so we need no option-processing routine.
264
     */
265
0
    options_size = pcapng_compute_options_size(rec->block, NULL);
266
267
0
    mandatory_data = (wtapng_nflx_custom_mandatory_t *)rec->block->mandatory_data;
268
269
    /* write block header */
270
0
    bh.block_type = BLOCK_TYPE_CB_COPY;
271
0
    bh.block_total_length = (uint32_t)(sizeof(bh) + sizeof(uint32_t) + sizeof(uint32_t) + options_size + 4);
272
0
    if (mandatory_data->type == NFLX_BLOCK_TYPE_SKIP) {
273
0
        bh.block_total_length += (uint32_t)sizeof(uint32_t);
274
0
    }
275
0
    ws_debug("writing %u bytes, type %u",
276
0
             bh.block_total_length, mandatory_data->type);
277
0
    if (!wtap_dump_file_write(wdh, &bh, sizeof(bh), err)) {
278
0
        return false;
279
0
    }
280
281
    /* write PEN */
282
0
    pen = PEN_NFLX;
283
0
    if (!wtap_dump_file_write(wdh, &pen, sizeof(uint32_t), err)) {
284
0
        return false;
285
0
    }
286
0
    ws_debug("wrote PEN = %u", pen);
287
288
    /* write type */
289
0
    type = GUINT32_TO_LE(mandatory_data->type);
290
0
    if (!wtap_dump_file_write(wdh, &type, sizeof(uint32_t), err)) {
291
0
        return false;
292
0
    }
293
0
    ws_debug("wrote type = %u", mandatory_data->type);
294
295
0
    if (mandatory_data->type == NFLX_BLOCK_TYPE_SKIP) {
296
0
        skipped = GUINT32_TO_LE(mandatory_data->skipped);
297
0
        if (!wtap_dump_file_write(wdh, &skipped, sizeof(uint32_t), err)) {
298
0
            return false;
299
0
        }
300
0
        ws_debug("wrote skipped = %u", mandatory_data->skipped);
301
0
    }
302
303
    /* Write options, if we have any */
304
0
    if (options_size != 0) {
305
        /*
306
         * This block type supports only comments and custom options,
307
         * so it doesn't need a callback.
308
         */
309
0
        if (!pcapng_write_options(wdh, OPT_LITTLE_ENDIAN, rec->block, NULL,
310
0
                                  err, err_info))
311
0
            return false;
312
0
    }
313
314
    /* write block footer */
315
0
    if (!wtap_dump_file_write(wdh, &bh.block_total_length,
316
0
                              sizeof bh.block_total_length, err)) {
317
0
        return false;
318
0
    }
319
320
0
    return true;
321
0
}
322
323
void register_nflx_custom(void)
324
15
{
325
15
    static const pcapng_custom_block_enterprise_handler_t enterprise_netflix =
326
15
    {
327
15
        pcapng_read_nflx_custom_block,
328
15
        pcapng_process_nflx_custom_option,
329
15
        pcapng_write_nflx_custom_block
330
15
    };
331
332
15
    register_pcapng_custom_block_enterprise_handler(PEN_NFLX, &enterprise_netflix);
333
15
}