File: | ui/cli/tap-follow.c |
Warning: | line 586, column 12 Potential leak of memory pointed to by 'follow_info' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* tap-follow.c | |||
2 | * | |||
3 | * Copyright 2011-2013, QA Cafe <[email protected]> | |||
4 | * | |||
5 | * Wireshark - Network traffic analyzer | |||
6 | * By Gerald Combs <[email protected]> | |||
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(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)) { | |||
| ||||
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))); | |||
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
| |||
586 | return false0; | |||
| ||||
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 | */ |