### Bug Summary

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