### Bug Summary

File: | ui/cli/tap-follow.c
Warning: | line 586, column 12 Potential leak of memory pointed to by 'follow_info'

### Annotated Source Code

Press '?' to see keyboard shortcuts

Show analyzer invocation
clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name tap-follow.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-21/lib/clang/21 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_DEBUG -D WS_DEBUG_UTF_8 -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -I /builds/wireshark/wireshark/wiretap -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-nonliteral -std=gnu11 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2025-12-02-100328-3593-1 -x c /builds/wireshark/wireshark/ui/cli/tap-follow.c

Keyboard shortcuts:

- Use 'j/k' keys for keyboard navigation
- Use 'Shift+S' to show/hide relevant lines
- Use '?' to toggle this window

Close
Show only relevant lines Show control flow arrows

1 | /* tap-follow.c
2 | *
3 | * Copyright 2011-2013, QA Cafe <info@qacafe.com>
4 | *
5 | * Wireshark - Network traffic analyzer
6 | * By Gerald Combs <gerald@wireshark.org>
7 | * Copyright 1998 Gerald Combs
8 | *
9 | * SPDX-License-Identifier: GPL-2.0-or-later
10 | */
11 |
12 | /* This module provides udp and tcp follow stream capabilities to tshark.
13 | * It is only used by tshark and not wireshark.
14 | */
15 |
16 | #include "config.h"
17 |
18 | #include <stdio.h>
19 | #include <stdlib.h>
20 |
21 | #include <glib.h>
22 | #include <epan/addr_resolv.h>
23 | #include <wsutil/str_util.h>
24 | #include <wsutil/unicode-utils.h>
25 | #include <epan/follow.h>
26 | #include <epan/stat_tap_ui.h>
27 | #include <epan/tap.h>
28 | #include <wsutil/ws_assert.h>
29 | #include <wsutil/cmdarg_err.h>
30 |
31 | void register_tap_listener_follow(void);
32 |
33 | /* Show Type */
34 | typedef enum {
35 | SHOW_ASCII,
36 | SHOW_CARRAY,
37 | SHOW_EBCDIC,
38 | SHOW_HEXDUMP,
39 | SHOW_RAW,
40 | SHOW_CODEC, // Ordered to match UTF-8 combobox index
41 | SHOW_YAML
42 | } show_type_t;
43 |
44 | typedef struct _cli_follow_info {
45 | show_type_t show_type;
46 | register_follow_t* follower;
47 |
48 | /* range */
49 | uint32_t chunkMin;
50 | uint32_t chunkMax;
51 |
52 | /* filter */
53 | int stream_index;
54 | int sub_stream_index;
55 | int port[2];
56 | address addr[2];
57 | union {
58 | uint32_t addrBuf_v4;
59 | ws_in6_addr addrBuf_v6;
60 | } addrBuf[2];
61 | } cli_follow_info_t;
62 |
63 |
64 | #define STR_FOLLOW"follow," "follow,"
65 |
66 | #define STR_HEX",hex" ",hex"
67 | #define STR_ASCII",ascii" ",ascii"
68 | #define STR_EBCDIC",ebcdic" ",ebcdic"
69 | #define STR_RAW",raw" ",raw"
70 | #define STR_CODEC",utf-8" ",utf-8"
71 | #define STR_YAML",yaml" ",yaml"
72 |
73 | static const char * follow_str_type(cli_follow_info_t* cli_follow_info)
74 | {
75 | switch (cli_follow_info->show_type)
76 | {
77 | case SHOW_HEXDUMP: return "hex";
78 | case SHOW_ASCII: return "ascii";
79 | case SHOW_EBCDIC: return "ebcdic";
80 | case SHOW_RAW: return "raw";
81 | case SHOW_CODEC: return "utf-8";
82 | case SHOW_YAML: return "yaml";
83 | default:
84 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "ui/cli/tap-follow.c", 84, __func__, "assertion \"not reached\" failed");
85 | break;
86 | }
87 |
88 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "ui/cli/tap-follow.c", 88, __func__, "assertion \"not reached\" failed");
89 |
90 | return "<unknown-mode>";
91 | }
92 |
93 | static void
94 | follow_free(follow_info_t *follow_info)
95 | {
96 | cli_follow_info_t* cli_follow_info = (cli_follow_info_t*)follow_info->gui_data;
97 |
98 | g_free(cli_follow_info);
99 | follow_info_free(follow_info);
100 | }
101 |
102 | #define BYTES_PER_LINE16 16
103 | #define OFFSET_LEN8 8
104 | #define OFFSET_SPACE2 2
105 | #define HEX_START(8 + 2) (OFFSET_LEN8 + OFFSET_SPACE2)
106 | #define HEX_LEN(16 * 3) (BYTES_PER_LINE16 * 3) /* extra space at column 8 */
107 | #define HEX_SPACE2 2
108 | #define ASCII_START((8 + 2) + (16 * 3) + 2) (HEX_START(8 + 2) + HEX_LEN(16 * 3) + HEX_SPACE2)
109 | #define ASCII_LEN(16 + 1) (BYTES_PER_LINE16 + 1) /* extra space at column 8 */
110 | #define LINE_LEN(((8 + 2) + (16 * 3) + 2) + (16 + 1)) (ASCII_START((8 + 2) + (16 * 3) + 2) + ASCII_LEN(16 + 1))
111 |
112 | static const char bin2hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
113 | '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
114 |
115 | static void follow_print_hex(const char *prefixp, uint32_t offset, void *datap, int len)
116 | {
117 | int ii;
118 | int jj;
119 | int kk;
120 | uint8_t val;
121 | char line[LINE_LEN(((8 + 2) + (16 * 3) + 2) + (16 + 1)) + 1];
122 |
123 | for (ii = 0, jj = 0, kk = 0; ii < len; )
124 | {
125 | if ((ii % BYTES_PER_LINE16) == 0)
126 | {
127 | /* new line */
128 | snprintf(line, LINE_LEN(((8 + 2) + (16 * 3) + 2) + (16 + 1)) + 1, "%0*X", OFFSET_LEN8, offset);
129 | memset(line + HEX_START(8 + 2) - OFFSET_SPACE2, ' ',
130 | HEX_LEN(16 * 3) + OFFSET_SPACE2 + HEX_SPACE2);
131 |
132 | /* offset of hex */
133 | jj = HEX_START(8 + 2);
134 |
135 | /* offset of ascii */
136 | kk = ASCII_START((8 + 2) + (16 * 3) + 2);
137 | }
138 |
139 | val = ((uint8_t *)datap)[ii];
140 |
141 | line[jj++] = bin2hex[val >> 4];
142 | line[jj++] = bin2hex[val & 0xf];
143 | jj++;
144 |
145 | line[kk++] = val >= ' ' && val < 0x7f ? val : '.';
146 |
147 | /* extra space at column 8 */
148 | if (++ii % BYTES_PER_LINE16 == BYTES_PER_LINE16/2)
149 | {
150 | line[jj++] = ' ';
151 | line[kk++] = ' ';
152 | }
153 |
154 | if ((ii % BYTES_PER_LINE16) == 0 || ii == len)
155 | {
156 | /* end of line or buffer */
157 | if (line[kk - 1] == ' ')
158 | {
159 | kk--;
160 | }
161 | line[kk] = 0;
162 | printf("%s%s\n", prefixp, line);
163 | offset += BYTES_PER_LINE16;
164 | }
165 | }
166 | }
167 |
168 | static void follow_draw(void *contextp)
169 | {
170 | static const char separator[] =
171 | "===================================================================\n";
172 |
173 | follow_info_t *follow_info = (follow_info_t*)contextp;
174 | cli_follow_info_t* cli_follow_info = (cli_follow_info_t*)follow_info->gui_data;
175 | char buf[WS_INET6_ADDRSTRLEN46];
176 | uint32_t global_client_pos = 0, global_server_pos = 0;
177 | uint32_t *global_pos;
178 | uint32_t ii, jj;
179 | char *buffer;
180 | wmem_strbuf_t *strbuf;
181 | GList *cur;
182 | follow_record_t *follow_record;
183 | unsigned chunk;
184 | char *b64encoded;
185 | const uint32_t base64_raw_len = 57; /* Encodes to 76 bytes, common in RFCs */
186 |
187 | /* Print header */
188 | switch (cli_follow_info->show_type)
189 | {
190 | case SHOW_YAML:
191 | printf("peers:\n");
192 | printf(" - peer: 0\n");
193 | address_to_str_buf(&follow_info->client_ip, buf, sizeof buf);
194 | printf(" host: %s\n", buf);
195 | printf(" port: %d\n", follow_info->client_port);
196 | printf(" - peer: 1\n");
197 | address_to_str_buf(&follow_info->server_ip, buf, sizeof buf);
198 | printf(" host: %s\n", buf);
199 | printf(" port: %d\n", follow_info->server_port);
200 | printf("packets:\n");
201 | break;
202 |
203 | default:
204 | printf("\n%s", separator);
205 | printf("Follow: %s,%s\n", proto_get_protocol_filter_name(get_follow_proto_id(cli_follow_info->follower)), follow_str_type(cli_follow_info));
206 | printf("Filter: %s\n", follow_info->filter_out_filter);
207 |
208 | address_to_str_buf(&follow_info->client_ip, buf, sizeof buf);
209 | if (follow_info->client_ip.type == AT_IPv6)
210 | printf("Node 0: [%s]:%u\n", buf, follow_info->client_port);
211 | else
212 | printf("Node 0: %s:%u\n", buf, follow_info->client_port);
213 |
214 | address_to_str_buf(&follow_info->server_ip, buf, sizeof buf);
215 | if (follow_info->server_ip.type == AT_IPv6)
216 | printf("Node 1: [%s]:%u\n", buf, follow_info->server_port);
217 | else
218 | printf("Node 1: %s:%u\n", buf, follow_info->server_port);
219 | break;
220 | }
221 |
222 | for (cur = g_list_last(follow_info->payload), chunk = 1;
223 | cur != NULL((void*)0);
224 | cur = g_list_previous(cur)((cur) ? (((GList *)(cur))->prev) : ((void*)0)), chunk++)
225 | {
226 | follow_record = (follow_record_t *)cur->data;
227 | if (!follow_record->is_server) {
228 | global_pos = &global_client_pos;
229 | } else {
230 | global_pos = &global_server_pos;
231 | }
232 |
233 | /* ignore chunks not in range */
234 | if ((chunk < cli_follow_info->chunkMin) || (chunk > cli_follow_info->chunkMax)) {
235 | (*global_pos) += follow_record->data->len;
236 | continue;
237 | }
238 |
239 | /* Print start of line */
240 | switch (cli_follow_info->show_type)
241 | {
242 | case SHOW_HEXDUMP:
243 | case SHOW_YAML:
244 | case SHOW_CODEC: /* The transformation to UTF-8 can change the length */
245 | break;
246 |
247 | case SHOW_ASCII:
248 | case SHOW_EBCDIC:
249 | printf("%s%u\n", follow_record->is_server ? "\t" : "", follow_record->data->len);
250 | break;
251 |
252 | case SHOW_RAW:
253 | if (follow_record->is_server)
254 | {
255 | putchar('\t');
256 | }
257 | break;
258 |
259 | default:
260 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "ui/cli/tap-follow.c", 260, __func__, "assertion \"not reached\" failed");
261 | }
262 |
263 | /* Print data */
264 | switch (cli_follow_info->show_type)
265 | {
266 | case SHOW_HEXDUMP:
267 | follow_print_hex(follow_record->is_server ? "\t" : "", *global_pos, follow_record->data->data, follow_record->data->len);
268 | (*global_pos) += follow_record->data->len;
269 | break;
270 |
271 | case SHOW_ASCII:
272 | case SHOW_EBCDIC:
273 | buffer = (char *)g_malloc(follow_record->data->len+2);
274 |
275 | for (ii = 0; ii < follow_record->data->len; ii++)
276 | {
277 | switch (follow_record->data->data[ii])
278 | {
279 | // XXX: qt/follow_stream_dialog.c sanitize_buffer() also passes
280 | // tabs ('\t') through. Should we do that here too?
281 | // The Qt code has automatic universal new line handling for reading
282 | // so, e.g., \r\n in HTML becomes just \n, but we don't do that here.
283 | // (The Qt version doesn't write the file as Text, so all files use
284 | // Unix line endings, including on Windows.)
285 | case '\r':
286 | case '\n':
287 | buffer[ii] = follow_record->data->data[ii];
288 | break;
289 | default:
290 | buffer[ii] = g_ascii_isprint(follow_record->data->data[ii])((g_ascii_table[(guchar) (follow_record->data->data[ii] )] & G_ASCII_PRINT) != 0) ? follow_record->data->data[ii] : '.';
291 | break;
292 | }
293 | }
294 |
295 | buffer[ii++] = '\n';
296 | buffer[ii] = 0;
297 | if (cli_follow_info->show_type == SHOW_EBCDIC) {
298 | EBCDIC_to_ASCII((uint8_t*)buffer, ii);
299 | }
300 | printf("%s", buffer);
301 | g_free(buffer);
302 | break;
303 |
304 | case SHOW_CODEC:
305 | // This does the same as the Show As UTF-8 code in the Qt version
306 | // (passing through all legal UTF-8, including control codes and
307 | // internal NULs, substituting illegal UTF-8 sequences with
308 | // REPLACEMENT CHARACTER, and not handling valid UTF-8 sequences
309 | // which are split between unreassembled frames), except for the
310 | // end of line terminator issue as above.
311 | strbuf = ws_utf8_make_valid_strbuf(NULL((void*)0), follow_record->data->data, follow_record->data->len);
312 | printf("%s%zu\n", follow_record->is_server ? "\t" : "", wmem_strbuf_get_len(strbuf));
313 | fwrite(wmem_strbuf_get_str(strbuf), 1, wmem_strbuf_get_len(strbuf), stdoutstdout);
314 | wmem_strbuf_destroy(strbuf);
315 | putchar('\n');
316 | break;
317 |
318 | case SHOW_RAW:
319 | buffer = (char *)g_malloc((follow_record->data->len*2)+2);
320 |
321 | for (ii = 0, jj = 0; ii < follow_record->data->len; ii++)
322 | {
323 | buffer[jj++] = bin2hex[follow_record->data->data[ii] >> 4];
324 | buffer[jj++] = bin2hex[follow_record->data->data[ii] & 0xf];
325 | }
326 |
327 | buffer[jj++] = '\n';
328 | buffer[jj] = 0;
329 | printf("%s", buffer);
330 | g_free(buffer);
331 | break;
332 |
333 | case SHOW_YAML:
334 | printf(" - packet: %d\n", follow_record->packet_num);
335 | printf(" peer: %d\n", follow_record->is_server ? 1 : 0);
336 | printf(" timestamp: %.9f\n", nstime_to_sec(&follow_record->abs_ts));
337 | printf(" data: !!binary |\n");
338 | ii = 0;
339 | while (ii < follow_record->data->len) {
340 | uint32_t len = ii + base64_raw_len < follow_record->data->len
341 | ? base64_raw_len
342 | : follow_record->data->len - ii;
343 | b64encoded = g_base64_encode(&follow_record->data->data[ii], len);
344 | printf(" %s\n", b64encoded);
345 | g_free(b64encoded);
346 | ii += len;
347 | }
348 | break;
349 |
350 | default:
351 | ws_assert_not_reached()ws_log_fatal_full("", LOG_LEVEL_ERROR, "ui/cli/tap-follow.c", 351, __func__, "assertion \"not reached\" failed");
352 | }
353 | }
354 |
355 | /* Print footer */
356 | switch (cli_follow_info->show_type)
357 | {
358 | case SHOW_YAML:
359 | break;
360 |
361 | default:
362 | printf("%s", separator);
363 | break;
364 | }
365 | }
366 |
367 | static bool_Bool follow_arg_strncmp(const char **opt_argp, const char *strp)
368 | {
369 | size_t len = strlen(strp);
370 |
371 | if (strncmp(*opt_argp, strp, len) == 0)
372 | {
373 | *opt_argp += len;
374 | return true1;
375 | }
376 | return false0;
377 | }
378 |
379 | static bool_Bool
380 | follow_arg_mode(const char **opt_argp, follow_info_t *follow_info)
381 | {
382 | cli_follow_info_t* cli_follow_info = (cli_follow_info_t*)follow_info->gui_data;
383 |
384 | if (follow_arg_strncmp(opt_argp, STR_HEX",hex"))
385 | {
386 | cli_follow_info->show_type = SHOW_HEXDUMP;
387 | }
388 | else if (follow_arg_strncmp(opt_argp, STR_ASCII",ascii"))
389 | {
390 | cli_follow_info->show_type = SHOW_ASCII;
391 | }
392 | else if (follow_arg_strncmp(opt_argp, STR_EBCDIC",ebcdic"))
393 | {
394 | cli_follow_info->show_type = SHOW_EBCDIC;
395 | }
396 | else if (follow_arg_strncmp(opt_argp, STR_RAW",raw"))
397 | {
398 | cli_follow_info->show_type = SHOW_RAW;
399 | }
400 | else if (follow_arg_strncmp(opt_argp, STR_CODEC",utf-8"))
401 | {
402 | cli_follow_info->show_type = SHOW_CODEC;
403 | }
404 | else if (follow_arg_strncmp(opt_argp, STR_YAML",yaml"))
405 | {
406 | cli_follow_info->show_type = SHOW_YAML;
407 | }
408 | else
409 | {
410 | cmdarg_err("Invalid display mode.");
411 | return false0;
412 | }
413 |
414 | return true1;
415 | }
416 |
417 | #define _STRING(s)"s" # s
418 | #define STRING(s)"s" _STRING(s)"s"
419 |
420 | #define ADDR_CHARS80 80
421 | #define ADDR_LEN(80 + 1) (ADDR_CHARS80 + 1)
422 | #define ADDRv6_FMT",[%" "80" "[^]]]:%d%n" ",[%" STRING(ADDR_CHARS)"80" "[^]]]:%d%n"
423 | #define ADDRv4_FMT",%" "80" "[^:]:%d%n" ",%" STRING(ADDR_CHARS)"80" "[^:]:%d%n"
424 |
425 | static bool_Bool
426 | follow_arg_filter(const char **opt_argp, follow_info_t *follow_info)
427 | {
428 | int len;
429 | unsigned int ii;
430 | char addr[ADDR_LEN(80 + 1)];
431 | cli_follow_info_t* cli_follow_info = (cli_follow_info_t*)follow_info->gui_data;
432 | bool_Bool is_ipv6;
433 |
434 | if (sscanf(*opt_argp, ",%d%n", &cli_follow_info->stream_index, &len) == 1 &&
435 | ((*opt_argp)[len] == 0 || (*opt_argp)[len] == ','))
436 | {
437 | *opt_argp += len;
438 |
439 | /* if it's HTTP2 or QUIC protocol we should read substream id otherwise it's a range parameter from follow_arg_range */
440 | if (cli_follow_info->sub_stream_index == -1 && sscanf(*opt_argp, ",%d%n", &cli_follow_info->sub_stream_index, &len) == 1 &&
441 | ((*opt_argp)[len] == 0 || (*opt_argp)[len] == ','))
442 | {
443 | *opt_argp += len;
444 | follow_info->substream_id = cli_follow_info->sub_stream_index;
445 | }
446 | }
447 | else
448 | {
449 | for (ii = 0; ii < array_length(cli_follow_info->addr)(sizeof (cli_follow_info->addr) / sizeof (cli_follow_info-> addr)[0]); ii++)
450 | {
451 | if (sscanf(*opt_argp, ADDRv6_FMT",[%" "80" "[^]]]:%d%n", addr, &cli_follow_info->port[ii], &len) == 2)
452 | {
453 | is_ipv6 = true1;
454 | }
455 | else if (sscanf(*opt_argp, ADDRv4_FMT",%" "80" "[^:]:%d%n", addr, &cli_follow_info->port[ii], &len) == 2)
456 | {
457 | is_ipv6 = false0;
458 | }
459 | else
460 | {
461 | cmdarg_err("Invalid address.");
462 | return false0;
463 | }
464 |
465 | if (cli_follow_info->port[ii] <= 0 || cli_follow_info->port[ii] > UINT16_MAX(65535))
466 | {
467 | cmdarg_err("Invalid port.");
468 | return false0;
469 | }
470 |
471 | if (is_ipv6)
472 | {
473 | if (!get_host_ipaddr6(addr, &cli_follow_info->addrBuf[ii].addrBuf_v6))
474 | {
475 | cmdarg_err("Can't get IPv6 address");
476 | return false0;
477 | }
478 | set_address(&cli_follow_info->addr[ii], AT_IPv6, 16, (void *)&cli_follow_info->addrBuf[ii].addrBuf_v6);
479 | }
480 | else
481 | {
482 | if (!get_host_ipaddr(addr, &cli_follow_info->addrBuf[ii].addrBuf_v4))
483 | {
484 | cmdarg_err("Can't get IPv4 address");
485 | return false0;
486 | }
487 | set_address(&cli_follow_info->addr[ii], AT_IPv4, 4, (void *)&cli_follow_info->addrBuf[ii].addrBuf_v4);
488 | }
489 |
490 | *opt_argp += len;
491 | }
492 |
493 | if (cli_follow_info->addr[0].type != cli_follow_info->addr[1].type)
494 | {
495 | cmdarg_err("Mismatched IP address types.");
496 | return false0;
497 | }
498 | cli_follow_info->stream_index = -1;
499 | }
500 |
501 | return true1;
502 | }
503 |
504 | static bool_Bool follow_arg_range(const char **opt_argp, cli_follow_info_t* cli_follow_info)
505 | {
506 | int len;
507 |
508 | if (**opt_argp == 0)
509 | {
510 | cli_follow_info->chunkMin = 1;
511 | cli_follow_info->chunkMax = UINT32_MAX(4294967295U);
512 | }
513 | else
514 | {
515 | if (sscanf(*opt_argp, ",%u-%u%n", &cli_follow_info->chunkMin, &cli_follow_info->chunkMax, &len) == 2)
516 | {
517 | *opt_argp += len;
518 | }
519 | else if (sscanf(*opt_argp, ",%u%n", &cli_follow_info->chunkMin, &len) == 1)
520 | {
521 | cli_follow_info->chunkMax = cli_follow_info->chunkMin;
522 | *opt_argp += len;
523 | }
524 | else
525 | {
526 | cmdarg_err("Invalid range.");
527 | return false0;
528 | }
529 |
530 | if (cli_follow_info->chunkMin < 1 || cli_follow_info->chunkMin > cli_follow_info->chunkMax)
531 | {
532 | cmdarg_err("Invalid range value.");
533 | return false0;
534 | }
535 | }
536 |
537 | return true1;
538 | }
539 |
540 | static bool_Bool
541 | follow_arg_done(const char *opt_argp)
542 | {
543 | if (*opt_argp != 0)
544 | {
545 | cmdarg_err("Invalid parameter.");
546 | return false0;
547 | }
548 |
549 | return true1;
550 | }
551 |
552 | static bool_Bool follow_stream(const char *opt_argp, void *userdata)
553 | {
554 | follow_info_t *follow_info;
555 | cli_follow_info_t* cli_follow_info;
556 | GString *errp;
557 | register_follow_t* follower = (register_follow_t*)userdata;
558 | follow_index_filter_func index_filter;
559 | follow_address_filter_func address_filter;
560 | int proto_id = get_follow_proto_id(follower);
561 | const char* proto_filter_name = proto_get_protocol_filter_name(proto_id);
562 | bool_Bool arg_success = true1;
563 |
564 | opt_argp += strlen(STR_FOLLOW"follow,");
565 | opt_argp += strlen(proto_filter_name);
566 |
567 | cli_follow_info = g_new0(cli_follow_info_t, 1)((cli_follow_info_t *) g_malloc0_n ((1), sizeof (cli_follow_info_t )));
568 | cli_follow_info->stream_index = -1;
569 | /* use second parameter only for followers that have sub streams
570 | * (currently HTTP2 or QUIC) */
571 | if (get_follow_sub_stream_id_func(follower)) {
1 | Assuming the condition is false | →
2 | ← | Taking false branch | →
572 | cli_follow_info->sub_stream_index = -1;
573 | } else {
574 | cli_follow_info->sub_stream_index = 0;
575 | }
576 | follow_info = g_new0(follow_info_t, 1)((follow_info_t *) g_malloc0_n ((1), sizeof (follow_info_t)));
3 | ← | Memory is allocated | →
577 | follow_info->gui_data = cli_follow_info;
578 | follow_info->substream_id = SUBSTREAM_UNUSED0xFFFFFFFFFFFFFFFFUL;
579 | cli_follow_info->follower = follower;
580 |
581 | arg_success &= follow_arg_mode(&opt_argp, follow_info);
582 | arg_success &= follow_arg_filter(&opt_argp, follow_info);
583 | arg_success &= follow_arg_range(&opt_argp, cli_follow_info);
584 | arg_success &= follow_arg_done(opt_argp);
585 | if (!arg_success
3.1 | 'arg_success' is false
)
4 | ← | Taking true branch | →
586 | return false0;
5 | ← | Potential leak of memory pointed to by 'follow_info'
587 |
588 | if (cli_follow_info->stream_index >= 0)
589 | {
590 | index_filter = get_follow_index_func(follower);
591 | follow_info->filter_out_filter = index_filter(cli_follow_info->stream_index, cli_follow_info->sub_stream_index);
592 | if (follow_info->filter_out_filter == NULL((void*)0) || cli_follow_info->sub_stream_index < 0)
593 | {
594 | cmdarg_err("Error creating filter for this stream.");
595 | return false0;
596 | }
597 | }
598 | else
599 | {
600 | address_filter = get_follow_address_func(follower);
601 | follow_info->filter_out_filter = address_filter(&cli_follow_info->addr[0], &cli_follow_info->addr[1], cli_follow_info->port[0], cli_follow_info->port[1]);
602 | if (follow_info->filter_out_filter == NULL((void*)0))
603 | {
604 | cmdarg_err("Error creating filter for this address/port pair.\n");
605 | return false0;
606 | }
607 | }
608 |
609 | errp = register_tap_listener(get_follow_tap_string(follower), follow_info, follow_info->filter_out_filter, 0,
610 | NULL((void*)0), get_follow_tap_handler(follower), follow_draw, (tap_finish_cb)follow_free);
611 |
612 | if (errp != NULL((void*)0))
613 | {
614 | follow_free(follow_info);
615 | g_string_free(errp, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) ( (errp), ((!(0)))) : g_string_free_and_steal (errp)) : (g_string_free ) ((errp), ((!(0)))));
616 | cmdarg_err("Error registering tap listener.");
617 | return false0;
618 | }
619 | return true1;
620 | }
621 |
622 | static bool_Bool
623 | follow_register(const void *key _U___attribute__((unused)), void *value, void *userdata _U___attribute__((unused)))
624 | {
625 | register_follow_t *follower = (register_follow_t*)value;
626 | stat_tap_ui follow_ui;
627 | char *cli_string;
628 |
629 | cli_string = follow_get_stat_tap_string(follower);
630 | follow_ui.group = REGISTER_STAT_GROUP_GENERIC;
631 | follow_ui.title = NULL((void*)0); /* construct this from the protocol info? */
632 | follow_ui.cli_string = cli_string;
633 | follow_ui.tap_init_cb = follow_stream;
634 | follow_ui.nparams = 0;
635 | follow_ui.params = NULL((void*)0);
636 | register_stat_tap_ui(&follow_ui, follower);
637 | g_free(cli_string);
638 | return false0;
639 | }
640 |
641 | void
642 | register_tap_listener_follow(void)
643 | {
644 | follow_iterate_followers(follow_register, NULL((void*)0));
645 | }
646 |
647 | /*
648 | * Editor modelines - https://www.wireshark.org/tools/modelines.html
649 | *
650 | * Local Variables:
651 | * c-basic-offset: 2
652 | * tab-width: 8
653 | * indent-tabs-mode: nil
654 | * End:
655 | *
656 | * ex: set shiftwidth=2 tabstop=8 expandtab:
657 | * :indentSize=2:tabSize=8:noTabs=true:
658 | */