PageRenderTime 58ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/ui/cli/tap-follow.c

https://gitlab.com/crondaemon/wireshark-legacy
C | 539 lines | 429 code | 75 blank | 35 comment | 86 complexity | 60d0141c1c18cdc3150d14e928edacc4 MD5 | raw file
  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. /* This module provides udp and tcp follow stream capabilities to tshark.
  12. * It is only used by tshark and not wireshark.
  13. */
  14. #include "config.h"
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <glib.h>
  18. #include <epan/addr_resolv.h>
  19. #include <epan/charsets.h>
  20. #include <epan/follow.h>
  21. #include <epan/stat_tap_ui.h>
  22. #include <epan/tap.h>
  23. void register_tap_listener_follow(void);
  24. typedef struct _cli_follow_info {
  25. show_type_t show_type;
  26. register_follow_t* follower;
  27. /* range */
  28. guint32 chunkMin;
  29. guint32 chunkMax;
  30. /* filter */
  31. int stream_index;
  32. int sub_stream_index;
  33. int port[2];
  34. address addr[2];
  35. union {
  36. guint32 addrBuf_v4;
  37. ws_in6_addr addrBuf_v6;
  38. } addrBuf[2];
  39. } cli_follow_info_t;
  40. #define STR_FOLLOW "follow,"
  41. #define STR_HEX ",hex"
  42. #define STR_ASCII ",ascii"
  43. #define STR_EBCDIC ",ebcdic"
  44. #define STR_RAW ",raw"
  45. WS_NORETURN static void follow_exit(const char *strp)
  46. {
  47. fprintf(stderr, "tshark: follow - %s\n", strp);
  48. exit(1);
  49. }
  50. static const char * follow_str_type(cli_follow_info_t* cli_follow_info)
  51. {
  52. switch (cli_follow_info->show_type)
  53. {
  54. case SHOW_HEXDUMP: return "hex";
  55. case SHOW_ASCII: return "ascii";
  56. case SHOW_EBCDIC: return "ebcdic";
  57. case SHOW_RAW: return "raw";
  58. default:
  59. g_assert_not_reached();
  60. break;
  61. }
  62. g_assert_not_reached();
  63. return "<unknown-mode>";
  64. }
  65. static void
  66. follow_free(follow_info_t *follow_info)
  67. {
  68. cli_follow_info_t* cli_follow_info = (cli_follow_info_t*)follow_info->gui_data;
  69. g_free(cli_follow_info);
  70. follow_info_free(follow_info);
  71. }
  72. #define BYTES_PER_LINE 16
  73. #define OFFSET_LEN 8
  74. #define OFFSET_SPACE 2
  75. #define HEX_START (OFFSET_LEN + OFFSET_SPACE)
  76. #define HEX_LEN (BYTES_PER_LINE * 3) /* extra space at column 8 */
  77. #define HEX_SPACE 2
  78. #define ASCII_START (HEX_START + HEX_LEN + HEX_SPACE)
  79. #define ASCII_LEN (BYTES_PER_LINE + 1) /* extra space at column 8 */
  80. #define LINE_LEN (ASCII_START + ASCII_LEN)
  81. static const char bin2hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
  82. '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
  83. static void follow_print_hex(const char *prefixp, guint32 offset, void *datap, int len)
  84. {
  85. int ii;
  86. int jj;
  87. int kk;
  88. guint8 val;
  89. char line[LINE_LEN + 1];
  90. for (ii = 0, jj = 0, kk = 0; ii < len; )
  91. {
  92. if ((ii % BYTES_PER_LINE) == 0)
  93. {
  94. /* new line */
  95. g_snprintf(line, LINE_LEN + 1, "%0*X", OFFSET_LEN, offset);
  96. memset(line + HEX_START - OFFSET_SPACE, ' ',
  97. HEX_LEN + OFFSET_SPACE + HEX_SPACE);
  98. /* offset of hex */
  99. jj = HEX_START;
  100. /* offset of ascii */
  101. kk = ASCII_START;
  102. }
  103. val = ((guint8 *)datap)[ii];
  104. line[jj++] = bin2hex[val >> 4];
  105. line[jj++] = bin2hex[val & 0xf];
  106. jj++;
  107. line[kk++] = val >= ' ' && val < 0x7f ? val : '.';
  108. /* extra space at column 8 */
  109. if (++ii % BYTES_PER_LINE == BYTES_PER_LINE/2)
  110. {
  111. line[jj++] = ' ';
  112. line[kk++] = ' ';
  113. }
  114. if ((ii % BYTES_PER_LINE) == 0 || ii == len)
  115. {
  116. /* end of line or buffer */
  117. if (line[kk - 1] == ' ')
  118. {
  119. kk--;
  120. }
  121. line[kk] = 0;
  122. printf("%s%s\n", prefixp, line);
  123. offset += BYTES_PER_LINE;
  124. }
  125. }
  126. }
  127. static void follow_draw(void *contextp)
  128. {
  129. static const char separator[] =
  130. "===================================================================\n";
  131. follow_info_t *follow_info = (follow_info_t*)contextp;
  132. cli_follow_info_t* cli_follow_info = (cli_follow_info_t*)follow_info->gui_data;
  133. gchar buf[WS_INET6_ADDRSTRLEN];
  134. guint32 global_client_pos = 0, global_server_pos = 0;
  135. guint32 *global_pos;
  136. guint32 ii, jj;
  137. char *buffer;
  138. GList *cur;
  139. follow_record_t *follow_record;
  140. guint chunk;
  141. printf("\n%s", separator);
  142. printf("Follow: %s,%s\n", proto_get_protocol_filter_name(get_follow_proto_id(cli_follow_info->follower)), follow_str_type(cli_follow_info));
  143. printf("Filter: %s\n", follow_info->filter_out_filter);
  144. address_to_str_buf(&follow_info->client_ip, buf, sizeof buf);
  145. if (follow_info->client_ip.type == AT_IPv6)
  146. printf("Node 0: [%s]:%u\n", buf, follow_info->client_port);
  147. else
  148. printf("Node 0: %s:%u\n", buf, follow_info->client_port);
  149. address_to_str_buf(&follow_info->server_ip, buf, sizeof buf);
  150. if (follow_info->client_ip.type == AT_IPv6)
  151. printf("Node 1: [%s]:%u\n", buf, follow_info->server_port);
  152. else
  153. printf("Node 1: %s:%u\n", buf, follow_info->server_port);
  154. for (cur = g_list_last(follow_info->payload), chunk = 1;
  155. cur != NULL;
  156. cur = g_list_previous(cur), chunk++)
  157. {
  158. follow_record = (follow_record_t *)cur->data;
  159. if (!follow_record->is_server) {
  160. global_pos = &global_client_pos;
  161. } else {
  162. global_pos = &global_server_pos;
  163. }
  164. /* ignore chunks not in range */
  165. if ((chunk < cli_follow_info->chunkMin) || (chunk > cli_follow_info->chunkMax)) {
  166. (*global_pos) += follow_record->data->len;
  167. continue;
  168. }
  169. switch (cli_follow_info->show_type)
  170. {
  171. case SHOW_HEXDUMP:
  172. break;
  173. case SHOW_ASCII:
  174. case SHOW_EBCDIC:
  175. printf("%s%u\n", follow_record->is_server ? "\t" : "", follow_record->data->len);
  176. break;
  177. case SHOW_RAW:
  178. if (follow_record->is_server)
  179. {
  180. putchar('\t');
  181. }
  182. break;
  183. default:
  184. g_assert_not_reached();
  185. }
  186. switch (cli_follow_info->show_type)
  187. {
  188. case SHOW_HEXDUMP:
  189. follow_print_hex(follow_record->is_server ? "\t" : "", *global_pos, follow_record->data->data, follow_record->data->len);
  190. (*global_pos) += follow_record->data->len;
  191. break;
  192. case SHOW_ASCII:
  193. case SHOW_EBCDIC:
  194. buffer = (char *)g_malloc(follow_record->data->len+2);
  195. for (ii = 0; ii < follow_record->data->len; ii++)
  196. {
  197. switch (follow_record->data->data[ii])
  198. {
  199. case '\r':
  200. case '\n':
  201. buffer[ii] = follow_record->data->data[ii];
  202. break;
  203. default:
  204. buffer[ii] = g_ascii_isprint(follow_record->data->data[ii]) ? follow_record->data->data[ii] : '.';
  205. break;
  206. }
  207. }
  208. buffer[ii++] = '\n';
  209. buffer[ii] = 0;
  210. if (cli_follow_info->show_type == SHOW_EBCDIC) {
  211. EBCDIC_to_ASCII(buffer, ii);
  212. }
  213. printf("%s", buffer);
  214. g_free(buffer);
  215. break;
  216. case SHOW_RAW:
  217. buffer = (char *)g_malloc((follow_record->data->len*2)+2);
  218. for (ii = 0, jj = 0; ii < follow_record->data->len; ii++)
  219. {
  220. buffer[jj++] = bin2hex[follow_record->data->data[ii] >> 4];
  221. buffer[jj++] = bin2hex[follow_record->data->data[ii] & 0xf];
  222. }
  223. buffer[jj++] = '\n';
  224. buffer[jj] = 0;
  225. printf("%s", buffer);
  226. g_free(buffer);
  227. break;
  228. default:
  229. g_assert_not_reached();
  230. }
  231. }
  232. printf("%s", separator);
  233. }
  234. static gboolean follow_arg_strncmp(const char **opt_argp, const char *strp)
  235. {
  236. size_t len = strlen(strp);
  237. if (strncmp(*opt_argp, strp, len) == 0)
  238. {
  239. *opt_argp += len;
  240. return TRUE;
  241. }
  242. return FALSE;
  243. }
  244. static void
  245. follow_arg_mode(const char **opt_argp, follow_info_t *follow_info)
  246. {
  247. cli_follow_info_t* cli_follow_info = (cli_follow_info_t*)follow_info->gui_data;
  248. if (follow_arg_strncmp(opt_argp, STR_HEX))
  249. {
  250. cli_follow_info->show_type = SHOW_HEXDUMP;
  251. }
  252. else if (follow_arg_strncmp(opt_argp, STR_ASCII))
  253. {
  254. cli_follow_info->show_type = SHOW_ASCII;
  255. }
  256. else if (follow_arg_strncmp(opt_argp, STR_EBCDIC))
  257. {
  258. cli_follow_info->show_type = SHOW_EBCDIC;
  259. }
  260. else if (follow_arg_strncmp(opt_argp, STR_RAW))
  261. {
  262. cli_follow_info->show_type = SHOW_RAW;
  263. }
  264. else
  265. {
  266. follow_exit("Invalid display mode.");
  267. }
  268. }
  269. #define _STRING(s) # s
  270. #define STRING(s) _STRING(s)
  271. #define ADDR_CHARS 80
  272. #define ADDR_LEN (ADDR_CHARS + 1)
  273. #define ADDRv6_FMT ",[%" STRING(ADDR_CHARS) "[^]]]:%d%n"
  274. #define ADDRv4_FMT ",%" STRING(ADDR_CHARS) "[^:]:%d%n"
  275. static void
  276. follow_arg_filter(const char **opt_argp, follow_info_t *follow_info)
  277. {
  278. int len;
  279. unsigned int ii;
  280. char addr[ADDR_LEN];
  281. cli_follow_info_t* cli_follow_info = (cli_follow_info_t*)follow_info->gui_data;
  282. gboolean is_ipv6;
  283. if (sscanf(*opt_argp, ",%d%n", &cli_follow_info->stream_index, &len) == 1 &&
  284. ((*opt_argp)[len] == 0 || (*opt_argp)[len] == ','))
  285. {
  286. *opt_argp += len;
  287. /* if it's HTTP2 or QUIC protocol we should read substream id otherwise it's a range parameter from follow_arg_range */
  288. if (cli_follow_info->sub_stream_index == -1 && sscanf(*opt_argp, ",%d%n", &cli_follow_info->sub_stream_index, &len) == 1 &&
  289. ((*opt_argp)[len] == 0 || (*opt_argp)[len] == ','))
  290. {
  291. *opt_argp += len;
  292. }
  293. }
  294. else
  295. {
  296. for (ii = 0; ii < sizeof cli_follow_info->addr/sizeof *cli_follow_info->addr; ii++)
  297. {
  298. if (sscanf(*opt_argp, ADDRv6_FMT, addr, &cli_follow_info->port[ii], &len) == 2)
  299. {
  300. is_ipv6 = TRUE;
  301. }
  302. else if (sscanf(*opt_argp, ADDRv4_FMT, addr, &cli_follow_info->port[ii], &len) == 2)
  303. {
  304. is_ipv6 = FALSE;
  305. }
  306. else
  307. {
  308. follow_exit("Invalid address.");
  309. }
  310. if (cli_follow_info->port[ii] <= 0 || cli_follow_info->port[ii] > G_MAXUINT16)
  311. {
  312. follow_exit("Invalid port.");
  313. }
  314. if (is_ipv6)
  315. {
  316. if (!get_host_ipaddr6(addr, &cli_follow_info->addrBuf[ii].addrBuf_v6))
  317. {
  318. follow_exit("Can't get IPv6 address");
  319. }
  320. set_address(&cli_follow_info->addr[ii], AT_IPv6, 16, (void *)&cli_follow_info->addrBuf[ii].addrBuf_v6);
  321. }
  322. else
  323. {
  324. if (!get_host_ipaddr(addr, &cli_follow_info->addrBuf[ii].addrBuf_v4))
  325. {
  326. follow_exit("Can't get IPv4 address");
  327. }
  328. set_address(&cli_follow_info->addr[ii], AT_IPv4, 4, (void *)&cli_follow_info->addrBuf[ii].addrBuf_v4);
  329. }
  330. *opt_argp += len;
  331. }
  332. if (cli_follow_info->addr[0].type != cli_follow_info->addr[1].type)
  333. {
  334. follow_exit("Mismatched IP address types.");
  335. }
  336. cli_follow_info->stream_index = -1;
  337. }
  338. }
  339. static void follow_arg_range(const char **opt_argp, cli_follow_info_t* cli_follow_info)
  340. {
  341. int len;
  342. if (**opt_argp == 0)
  343. {
  344. cli_follow_info->chunkMin = 1;
  345. cli_follow_info->chunkMax = G_MAXUINT32;
  346. }
  347. else
  348. {
  349. if (sscanf(*opt_argp, ",%u-%u%n", &cli_follow_info->chunkMin, &cli_follow_info->chunkMax, &len) == 2)
  350. {
  351. *opt_argp += len;
  352. }
  353. else if (sscanf(*opt_argp, ",%u%n", &cli_follow_info->chunkMin, &len) == 1)
  354. {
  355. cli_follow_info->chunkMax = cli_follow_info->chunkMin;
  356. *opt_argp += len;
  357. }
  358. else
  359. {
  360. follow_exit("Invalid range.");
  361. }
  362. if (cli_follow_info->chunkMin < 1 || cli_follow_info->chunkMin > cli_follow_info->chunkMax)
  363. {
  364. follow_exit("Invalid range value.");
  365. }
  366. }
  367. }
  368. static void
  369. follow_arg_done(const char *opt_argp)
  370. {
  371. if (*opt_argp != 0)
  372. {
  373. follow_exit("Invalid parameter.");
  374. }
  375. }
  376. static void follow_stream(const char *opt_argp, void *userdata)
  377. {
  378. follow_info_t *follow_info;
  379. cli_follow_info_t* cli_follow_info;
  380. GString *errp;
  381. register_follow_t* follower = (register_follow_t*)userdata;
  382. follow_index_filter_func index_filter;
  383. follow_address_filter_func address_filter;
  384. int proto_id = get_follow_proto_id(follower);
  385. const char* proto_filter_name = proto_get_protocol_filter_name(proto_id);
  386. opt_argp += strlen(STR_FOLLOW);
  387. opt_argp += strlen(proto_filter_name);
  388. cli_follow_info = g_new0(cli_follow_info_t, 1);
  389. cli_follow_info->stream_index = -1;
  390. /* use second parameter only for HTTP2 or QUIC substream */
  391. if (g_str_equal(proto_filter_name, "http2") ||
  392. g_str_equal(proto_filter_name, "quic")) {
  393. cli_follow_info->sub_stream_index = -1;
  394. } else {
  395. cli_follow_info->sub_stream_index = 0;
  396. }
  397. follow_info = g_new0(follow_info_t, 1);
  398. follow_info->gui_data = cli_follow_info;
  399. cli_follow_info->follower = follower;
  400. follow_arg_mode(&opt_argp, follow_info);
  401. follow_arg_filter(&opt_argp, follow_info);
  402. follow_arg_range(&opt_argp, cli_follow_info);
  403. follow_arg_done(opt_argp);
  404. if (cli_follow_info->stream_index >= 0)
  405. {
  406. index_filter = get_follow_index_func(follower);
  407. follow_info->filter_out_filter = index_filter(cli_follow_info->stream_index, cli_follow_info->sub_stream_index);
  408. if (follow_info->filter_out_filter == NULL || cli_follow_info->sub_stream_index < 0)
  409. {
  410. follow_exit("Error creating filter for this stream.");
  411. }
  412. }
  413. else
  414. {
  415. address_filter = get_follow_address_func(follower);
  416. 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]);
  417. if (follow_info->filter_out_filter == NULL)
  418. {
  419. follow_exit("Error creating filter for this address/port pair.\n");
  420. }
  421. }
  422. errp = register_tap_listener(get_follow_tap_string(follower), follow_info, follow_info->filter_out_filter, 0,
  423. NULL, get_follow_tap_handler(follower), follow_draw, (tap_finish_cb)follow_free);
  424. if (errp != NULL)
  425. {
  426. follow_free(follow_info);
  427. g_string_free(errp, TRUE);
  428. follow_exit("Error registering tap listener.");
  429. }
  430. }
  431. static gboolean
  432. follow_register(const void *key _U_, void *value, void *userdata _U_)
  433. {
  434. register_follow_t *follower = (register_follow_t*)value;
  435. stat_tap_ui follow_ui;
  436. gchar *cli_string;
  437. cli_string = follow_get_stat_tap_string(follower);
  438. follow_ui.group = REGISTER_STAT_GROUP_GENERIC;
  439. follow_ui.title = NULL; /* construct this from the protocol info? */
  440. follow_ui.cli_string = cli_string;
  441. follow_ui.tap_init_cb = follow_stream;
  442. follow_ui.nparams = 0;
  443. follow_ui.params = NULL;
  444. register_stat_tap_ui(&follow_ui, follower);
  445. g_free(cli_string);
  446. return FALSE;
  447. }
  448. void
  449. register_tap_listener_follow(void)
  450. {
  451. follow_iterate_followers(follow_register, NULL);
  452. }
  453. /*
  454. * Editor modelines - https://www.wireshark.org/tools/modelines.html
  455. *
  456. * Local Variables:
  457. * c-basic-offset: 2
  458. * tab-width: 8
  459. * indent-tabs-mode: nil
  460. * End:
  461. *
  462. * ex: set shiftwidth=2 tabstop=8 expandtab:
  463. * :indentSize=2:tabSize=8:noTabs=true:
  464. */