diff --git a/capture/capture_ifinfo.c b/capture/capture_ifinfo.c index 95d06424c3..783d0a7a4c 100644 --- a/capture/capture_ifinfo.c +++ b/capture/capture_ifinfo.c @@ -26,6 +26,7 @@ #include #include +#include #ifdef HAVE_PCAP_REMOTE static GList *remote_interface_list = NULL; @@ -199,8 +200,8 @@ capture_get_if_capabilities(const char *ifname, bool monitor_mode, if_capabilities_t *caps; GList *linktype_list = NULL, *timestamp_list = NULL; int err, i; - char *data, *primary_msg, *secondary_msg; - char **raw_list; + char *data, *primary_msg, *secondary_msg, *val_s; + jsmntok_t *tokens, *inf_tok, *array_tok, *cur_tok; /* see if the interface is from extcap */ caps = extcap_get_if_dlts(ifname, err_primary_msg); @@ -228,23 +229,81 @@ capture_get_if_capabilities(const char *ifname, bool monitor_mode, return NULL; } - /* Split our lines */ -#ifdef _WIN32 - raw_list = g_strsplit(data, "\r\n", 0); -#else - raw_list = g_strsplit(data, "\n", 0); -#endif - g_free(data); + int num_tokens = json_parse(data, NULL, 0); + if (num_tokens <= 0) { + ws_warning("Capture Interface Capabilities failed with invalid JSON."); + g_free(data); + return NULL; + } - /* - * First line is 0 if monitor mode isn't supported, 1 if it is. - */ - if (raw_list[0] == NULL || *raw_list[0] == '\0') { + tokens = wmem_alloc_array(NULL, jsmntok_t, num_tokens); + if (json_parse(data, tokens, num_tokens) <= 0) { ws_info("Capture Interface Capabilities returned no information."); if (err_primary_msg) { *err_primary_msg = g_strdup("Dumpcap returned no interface capability information"); } - g_strfreev(raw_list); + wmem_free(NULL, tokens); + g_free(data); + return NULL; + } + + cur_tok = json_get_array_index(tokens, 0); + if (cur_tok == NULL) { + ws_warning("Capture Interface Capabilities failed with invalid JSON."); + wmem_free(NULL, tokens); + g_free(data); + return NULL; + } + + inf_tok = json_get_object(data, cur_tok, ifname); + if (inf_tok == NULL) { + ws_warning("Capture Interface Capabilities failed with invalid JSON."); + wmem_free(NULL, tokens); + g_free(data); + return NULL; + } + + double val_d; + if (!json_get_double(data, inf_tok, "status", &val_d)) { + ws_warning("Capture Interface Capabilities failed with invalid JSON."); + wmem_free(NULL, tokens); + g_free(data); + return NULL; + } + err = (int)val_d; + if (err != 0) { + primary_msg = json_get_string(data, inf_tok, "primary_msg"); + if (primary_msg) { + primary_msg = g_strdup(primary_msg); + } + secondary_msg = json_get_string(data, inf_tok, "secondary_msg"); + if (secondary_msg) { + secondary_msg = g_strdup(secondary_msg); + } + ws_info("Capture Interface Capabilities failed. Error %d, %s", + err, primary_msg ? primary_msg : "no message"); + if (err_primary_msg) + *err_primary_msg = primary_msg; + else + g_free(primary_msg); + if (err_secondary_msg) + *err_secondary_msg = secondary_msg; + else + g_free(secondary_msg); + wmem_free(NULL, tokens); + g_free(data); + return NULL; + } + + bool rfmon; + if (!json_get_boolean(data, inf_tok, "rfmon", &rfmon)) { + ws_message("Capture Interface Capabilities returned bad information."); + ws_message("Didn't return monitor-mode cap"); + if (err_primary_msg) { + *err_primary_msg = ws_strdup_printf("Dumpcap didn't return monitor-mode capability"); + } + wmem_free(NULL, tokens); + g_free(data); return NULL; } @@ -252,75 +311,67 @@ capture_get_if_capabilities(const char *ifname, bool monitor_mode, * Allocate the interface capabilities structure. */ caps = (if_capabilities_t *)g_malloc(sizeof *caps); - switch (*raw_list[0]) { - - case '0': - caps->can_set_rfmon = false; - break; - - case '1': - caps->can_set_rfmon = true; - break; - - default: - ws_info("Capture Interface Capabilities returned bad information."); - if (err_primary_msg) { - *err_primary_msg = ws_strdup_printf("Dumpcap returned \"%s\" for monitor-mode capability", - raw_list[0]); - } - g_free(caps); - g_strfreev(raw_list); - return NULL; - } + caps->can_set_rfmon = rfmon; /* * The following are link-layer types. */ - for (i = 1; raw_list[i] != NULL && *raw_list[i] != '\0'; i++) { - data_link_info_t *data_link_info; - /* ...and what if the interface name has a tab in it, Mr. Clever Programmer? */ - char **lt_parts = g_strsplit(raw_list[i], "\t", 3); - if (lt_parts[0] == NULL || lt_parts[1] == NULL || lt_parts[2] == NULL) { - g_strfreev(lt_parts); + array_tok = json_get_array(data, inf_tok, "data_link_types"); + if (!array_tok) { + ws_message("Capture Interface Capabilities returned bad data_link information."); + if (err_primary_msg) { + *err_primary_msg = ws_strdup_printf("Dumpcap didn't return data link types capability"); + } + wmem_free(NULL, tokens); + g_free(data); + g_free(caps); + return NULL; + } + for (i = 0; i < json_get_array_len(array_tok); i++) { + cur_tok = json_get_array_index(array_tok, i); + + if (!json_get_double(data, cur_tok, "dlt", &val_d)) { continue; } + data_link_info_t *data_link_info; data_link_info = g_new(data_link_info_t,1); - data_link_info->dlt = (int) strtol(lt_parts[0], NULL, 10); - data_link_info->name = g_strdup(lt_parts[1]); - if (strcmp(lt_parts[2], "(not supported)") != 0) - data_link_info->description = g_strdup(lt_parts[2]); - else - data_link_info->description = NULL; - g_strfreev(lt_parts); + data_link_info->dlt = (int)val_d; + val_s = json_get_string(data, cur_tok, "name"); + data_link_info->name = val_s ? g_strdup(val_s) : NULL; + val_s = json_get_string(data, cur_tok, "description"); + if (!val_s || strcmp(val_s, "(not supported)") == 0) { + data_link_info->description = NULL; + } else { + data_link_info->description = g_strdup(val_s); + } linktype_list = g_list_append(linktype_list, data_link_info); } - if (raw_list[i]) { /* Oh, timestamp types! */ - for (i++; raw_list[i] != NULL && *raw_list[i] != '\0'; i++) { - timestamp_info_t *timestamp_info; - char **tt_parts = g_strsplit(raw_list[i], "\t", 2); - if (tt_parts[0] == NULL || tt_parts[1] == NULL) { - g_strfreev(tt_parts); - continue; - } + array_tok = json_get_array(data, inf_tok, "timestamp_types"); + if (array_tok) { + for (i = 0; i < json_get_array_len(array_tok); i++) { + cur_tok = json_get_array_index(array_tok, i); + timestamp_info_t *timestamp_info; timestamp_info = g_new(timestamp_info_t,1); - timestamp_info->name = g_strdup(tt_parts[0]); - timestamp_info->description = g_strdup(tt_parts[1]); - g_strfreev(tt_parts); + val_s = json_get_string(data, cur_tok, "name"); + timestamp_info->name = val_s ? g_strdup(val_s) : NULL; + val_s = json_get_string(data, cur_tok, "description"); + timestamp_info->description = val_s ? g_strdup(val_s) : NULL; timestamp_list = g_list_append(timestamp_list, timestamp_info); } } - g_strfreev(raw_list); - caps->data_link_types = linktype_list; /* Might be NULL. Not all systems report timestamp types */ caps->timestamp_types = timestamp_list; + wmem_free(NULL, tokens); + g_free(data); + return caps; } diff --git a/dumpcap.c b/dumpcap.c index 703ca98288..0b0c8cafb8 100644 --- a/dumpcap.c +++ b/dumpcap.c @@ -78,6 +78,7 @@ #include "wsutil/time_util.h" #include "wsutil/please_report_bug.h" #include "wsutil/glib-compat.h" +#include #include #include "capture/ws80211_utils.h" @@ -1089,21 +1090,17 @@ print_machine_readable_interfaces(GList *if_list) * you MUST update capture_ifinfo.c:capture_get_if_capabilities() accordingly! */ static void -print_machine_readable_if_capabilities(if_capabilities_t *caps, int queries) +print_machine_readable_if_capabilities(json_dumper *dumper, if_capabilities_t *caps, int queries) { GList *lt_entry, *ts_entry; const gchar *desc_str; - if (capture_child) { - /* Let our parent know we succeeded. */ - sync_pipe_write_string_msg(2, SP_SUCCESS, NULL); - } - if (queries & CAPS_QUERY_LINK_TYPES) { - if (caps->can_set_rfmon) - printf("1\n"); - else - printf("0\n"); + json_dumper_set_member_name(dumper, "rfmon"); + // XXX: wsjson.h doesn't have a function to read booleans + json_dumper_value_anyf(dumper, "%s", caps->can_set_rfmon ? "true" : "false"); + json_dumper_set_member_name(dumper, "data_link_types"); + json_dumper_begin_array(dumper); for (lt_entry = caps->data_link_types; lt_entry != NULL; lt_entry = g_list_next(lt_entry)) { data_link_info_t *data_link_info = (data_link_info_t *)lt_entry->data; @@ -1111,12 +1108,20 @@ print_machine_readable_if_capabilities(if_capabilities_t *caps, int queries) desc_str = data_link_info->description; else desc_str = "(not supported)"; - printf("%d\t%s\t%s\n", data_link_info->dlt, data_link_info->name, - desc_str); + json_dumper_begin_object(dumper); + json_dumper_set_member_name(dumper, "dlt"); + json_dumper_value_anyf(dumper, "%d", data_link_info->dlt); + json_dumper_set_member_name(dumper, "name"); + json_dumper_value_string(dumper, data_link_info->name); + json_dumper_set_member_name(dumper, "description"); + json_dumper_value_string(dumper, desc_str); + json_dumper_end_object(dumper); } + json_dumper_end_array(dumper); } - printf("\n"); if (queries & CAPS_QUERY_TIMESTAMP_TYPES) { + json_dumper_set_member_name(dumper, "timestamp_types"); + json_dumper_begin_array(dumper); for (ts_entry = caps->timestamp_types; ts_entry != NULL; ts_entry = g_list_next(ts_entry)) { timestamp_info_t *timestamp = (timestamp_info_t *)ts_entry->data; @@ -1124,8 +1129,14 @@ print_machine_readable_if_capabilities(if_capabilities_t *caps, int queries) desc_str = timestamp->description; else desc_str = "(none)"; - printf("%s\t%s\n", timestamp->name, desc_str); + json_dumper_begin_object(dumper); + json_dumper_set_member_name(dumper, "name"); + json_dumper_value_string(dumper, timestamp->name); + json_dumper_set_member_name(dumper, "description"); + json_dumper_value_string(dumper, desc_str); + json_dumper_end_object(dumper); } + json_dumper_end_array(dumper); } } @@ -5870,43 +5881,89 @@ main(int argc, char *argv[]) gchar *open_status_str; guint ii; - for (ii = 0; ii < global_capture_opts.ifaces->len; ii++) { - interface_options *interface_opts; + if (machine_readable) { + status = 0; + json_dumper dumper = { + .output_file = stdout, + .flags = JSON_DUMPER_FLAGS_NO_DEBUG, + // Don't abort on failure + }; + json_dumper_begin_array(&dumper); + for (ii = 0; ii < global_capture_opts.ifaces->len; ii++) { + interface_options *interface_opts; - interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, ii); + interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, ii); - caps = get_if_capabilities(interface_opts, &open_status, &open_status_str); - if (caps == NULL) { - if (capture_child) { - char *error_msg = ws_strdup_printf("The capabilities of the capture device " - "\"%s\" could not be obtained (%s)", - interface_opts->name, open_status_str); - sync_pipe_write_errmsgs_to_parent(2, error_msg, - get_pcap_failure_secondary_error_message(open_status, open_status_str)); - g_free(error_msg); + json_dumper_begin_object(&dumper); + json_dumper_set_member_name(&dumper, interface_opts->name); + + json_dumper_begin_object(&dumper); + + open_status = CAP_DEVICE_OPEN_NO_ERR; + caps = get_if_capabilities(interface_opts, &open_status, &open_status_str); + json_dumper_set_member_name(&dumper, "status"); + json_dumper_value_anyf(&dumper, "%i", open_status); + if (caps == NULL) { + json_dumper_set_member_name(&dumper, "primary_msg"); + json_dumper_value_string(&dumper, open_status_str); + json_dumper_set_member_name(&dumper, "secondary_msg"); + json_dumper_value_string(&dumper, get_pcap_failure_secondary_error_message(open_status, open_status_str)); + g_free(open_status_str); + } else { + /* XXX: We need to change the format and adapt consumers */ + print_machine_readable_if_capabilities(&dumper, caps, caps_queries); + free_if_capabilities(caps); } - else { - cmdarg_err("The capabilities of the capture device " - "\"%s\" could not be obtained (%s).\n%s", - interface_opts->name, open_status_str, - get_pcap_failure_secondary_error_message(open_status, open_status_str)); - } - g_free(open_status_str); - exit_main(2); + json_dumper_end_object(&dumper); + json_dumper_end_object(&dumper); } - - if (machine_readable) { /* tab-separated values to stdout */ - /* XXX: We need to change the format and adapt consumers */ - print_machine_readable_if_capabilities(caps, caps_queries); + json_dumper_end_array(&dumper); + if (json_dumper_finish(&dumper)) { status = 0; - } else + if (capture_child) { + /* Let our parent know we succeeded. */ + sync_pipe_write_string_msg(2, SP_SUCCESS, NULL); + } + } else { + status = 2; + if (capture_child) { + sync_pipe_write_errmsgs_to_parent(2, "Unexpected JSON error", ""); + } + } + } else { + for (ii = 0; ii < global_capture_opts.ifaces->len; ii++) { + interface_options *interface_opts; + + interface_opts = &g_array_index(global_capture_opts.ifaces, interface_options, ii); + + caps = get_if_capabilities(interface_opts, &open_status, &open_status_str); + if (caps == NULL) { + if (capture_child) { + char *error_msg = ws_strdup_printf("The capabilities of the capture device " + "\"%s\" could not be obtained (%s)", + interface_opts->name, open_status_str); + sync_pipe_write_errmsgs_to_parent(2, error_msg, + get_pcap_failure_secondary_error_message(open_status, open_status_str)); + g_free(error_msg); + } + else { + cmdarg_err("The capabilities of the capture device " + "\"%s\" could not be obtained (%s).\n%s", + interface_opts->name, open_status_str, + get_pcap_failure_secondary_error_message(open_status, open_status_str)); + } + g_free(open_status_str); + exit_main(2); + } + /* XXX: We might want to print also the interface name */ status = capture_opts_print_if_capabilities(caps, interface_opts, caps_queries); - free_if_capabilities(caps); - if (status != 0) - break; + free_if_capabilities(caps); + if (status != 0) + break; + } } exit_main(status); }