/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 | } |