diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-09-19 04:14:33 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-09-19 04:14:33 +0000 |
commit | 9f153fbfec0fb9c9ce38e749a7c6f4a5e115d4e9 (patch) | |
tree | 2784370cda9bbf2da9114d70f05399c0b229d28c /capture/capture_sync.c | |
parent | Adding debian version 4.2.6-1. (diff) | |
download | wireshark-9f153fbfec0fb9c9ce38e749a7c6f4a5e115d4e9.tar.xz wireshark-9f153fbfec0fb9c9ce38e749a7c6f4a5e115d4e9.zip |
Merging upstream version 4.4.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'capture/capture_sync.c')
-rw-r--r-- | capture/capture_sync.c | 805 |
1 files changed, 483 insertions, 322 deletions
diff --git a/capture/capture_sync.c b/capture/capture_sync.c index 1af14187..2a5db8bc 100644 --- a/capture/capture_sync.c +++ b/capture/capture_sync.c @@ -21,6 +21,8 @@ #include <signal.h> +#include <ws_exit_codes.h> + #include <wsutil/strtoi.h> #include <wsutil/ws_assert.h> @@ -101,6 +103,11 @@ static char *dummy_control_id; static const char *sync_pipe_signame(int); #endif +/* We use this pipe buffer size for both the sync message pipe and the + * data pipe. Ensure that it's large enough for the indicator and header + * plus maximum message size. + */ +#define PIPE_BUF_SIZE (SP_MAX_MSG_LEN+4) static gboolean sync_pipe_input_cb(GIOChannel *pipe_io, capture_session *cap_session); static int sync_pipe_wait_for_child(ws_process_id fork_child, char **msgp); @@ -108,15 +115,7 @@ static void pipe_convert_header(const unsigned char *header, int header_len, cha static ssize_t pipe_read_block(GIOChannel *pipe_io, char *indicator, int len, char *msg, char **err_msg); -static void (*fetch_dumpcap_pid)(ws_process_id) = NULL; - -static void free_argv(char** argv, int argc) -{ - int i; - for (i = 0; i < argc; i++) - g_free(argv[i]); - g_free(argv); -} +static void (*fetch_dumpcap_pid)(ws_process_id); void capture_session_init(capture_session *cap_session, capture_file *cf, @@ -156,7 +155,7 @@ void capture_process_finished(capture_session *cap_session) unsigned i; if (!extcap_session_stop(cap_session)) { - /* Atleast one extcap process did not fully finish yet, wait for it */ + /* At least one extcap process did not fully finish yet, wait for it */ return; } @@ -189,7 +188,7 @@ void capture_process_finished(capture_session *cap_session) } cap_session->closed(cap_session, message->str); - g_string_free(message, true); + g_string_free(message, TRUE); g_free(capture_opts->closed_msg); capture_opts->closed_msg = NULL; capture_opts->stop_after_extcaps = false; @@ -197,6 +196,8 @@ void capture_process_finished(capture_session *cap_session) /* Append an arg (realloc) to an argc/argv array */ /* (add a string pointer to a NULL-terminated array of string pointers) */ +/* XXX: For glib >= 2.68 we could use a GStrvBuilder. + */ static char ** sync_pipe_add_arg(char **args, int *argc, const char *arg) { @@ -219,6 +220,23 @@ sync_pipe_add_arg(char **args, int *argc, const char *arg) return args; } +/* Take a buffer from an SP_LOG_MSG from dumpcap and send it to our + * current logger. Keep this in sync with the format used in + * dumpcap_log_writer. (We might want to do more proper serialization + * of more than just the log level.) + */ +static void +sync_pipe_handle_log_msg(const char *buffer) { + const char *log_msg = NULL; + const char* end; + uint32_t level = 0; + + if (ws_strtou32(buffer, &end, &level) && end[0] == ':') { + log_msg = end + 1; + } + ws_log(LOG_DOMAIN_CAPCHILD, level, "%s", log_msg); +} + /* Initialize an argument list and add dumpcap to it. */ static char ** init_pipe_args(int *argc) { @@ -240,6 +258,18 @@ init_pipe_args(int *argc) { /* Make that the first argument in the argument list (argv[0]). */ argv = sync_pipe_add_arg(argv, argc, exename); + /* Tell dumpcap to log at the lowest level its domain (Capchild) is + * set to log in the main program. (It might be in the special noisy + * or debug filter, so we can't just check the overall level.) + */ + for (enum ws_log_level level = LOG_LEVEL_NOISY; level != _LOG_LEVEL_LAST; level++) { + if (ws_log_msg_is_active(LOG_DOMAIN_CAPCHILD, level)) { + argv = sync_pipe_add_arg(argv, argc, "--log-level"); + argv = sync_pipe_add_arg(argv, argc, ws_log_level_to_string(level)); + break; + } + } + /* sync_pipe_add_arg strdupes exename, so we should free our copy */ g_free(exename); @@ -269,17 +299,15 @@ pipe_io_cb(GIOChannel *pipe_io, GIOCondition condition _U_, void * user_data) * On failure, *msg points to an error message for the failure, and -1 is * returned, in which case *msg must be freed with g_free(). */ -/* XXX - assumes PIPE_BUF_SIZE > SP_MAX_MSG_LEN */ #define ARGV_NUMBER_LEN 24 -#define PIPE_BUF_SIZE 5120 static int #ifdef _WIN32 -sync_pipe_open_command(char* const argv[], int *data_read_fd, +sync_pipe_open_command(char **argv, int *data_read_fd, GIOChannel **message_read_io, int *signal_write_fd, ws_process_id *fork_child, GArray *ifaces, char **msg, void(*update_cb)(void)) #else -sync_pipe_open_command(char* const argv[], int *data_read_fd, +sync_pipe_open_command(char **argv, int *data_read_fd, GIOChannel **message_read_io, int *signal_write_fd _U_, ws_process_id *fork_child, GArray *ifaces _U_, char **msg, void(*update_cb)(void)) @@ -287,6 +315,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, { enum PIPES { PIPE_READ, PIPE_WRITE }; /* Constants 0 and 1 for PIPE_READ and PIPE_WRITE */ int message_read_fd = -1; + char sync_id[ARGV_NUMBER_LEN]; #ifdef _WIN32 HANDLE sync_pipe[2]; /* pipe used to send messages from child to parent */ HANDLE data_pipe[2]; /* pipe used to send data from child to parent */ @@ -317,8 +346,9 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, if (!msg) { /* We can't return anything */ + g_strfreev(argv); #ifdef _WIN32 - g_string_free(args, true); + g_string_free(args, TRUE); #endif return -1; } @@ -335,6 +365,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, /* Couldn't create the message pipe between parent and child. */ *msg = ws_strdup_printf("Couldn't create sync pipe: %s", win32strerror(GetLastError())); + g_strfreev(argv); return -1; } @@ -348,6 +379,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, message_read_fd = _open_osfhandle( (intptr_t) sync_pipe[PIPE_READ], _O_BINARY); if (message_read_fd == -1) { *msg = ws_strdup_printf("Couldn't get C file handle for message read pipe: %s", g_strerror(errno)); + g_strfreev(argv); CloseHandle(sync_pipe[PIPE_READ]); CloseHandle(sync_pipe[PIPE_WRITE]); return -1; @@ -360,6 +392,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, /* Couldn't create the message pipe between parent and child. */ *msg = ws_strdup_printf("Couldn't create data pipe: %s", win32strerror(GetLastError())); + g_strfreev(argv); ws_close(message_read_fd); /* Should close sync_pipe[PIPE_READ] */ CloseHandle(sync_pipe[PIPE_WRITE]); return -1; @@ -375,6 +408,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, *data_read_fd = _open_osfhandle( (intptr_t) data_pipe[PIPE_READ], _O_BINARY); if (*data_read_fd == -1) { *msg = ws_strdup_printf("Couldn't get C file handle for data read pipe: %s", g_strerror(errno)); + g_strfreev(argv); CloseHandle(data_pipe[PIPE_READ]); CloseHandle(data_pipe[PIPE_WRITE]); ws_close(message_read_fd); /* Should close sync_pipe[PIPE_READ] */ @@ -395,6 +429,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, /* Couldn't create the signal pipe between parent and child. */ *msg = ws_strdup_printf("Couldn't create signal pipe: %s", win32strerror(GetLastError())); + g_strfreev(argv); ws_close(message_read_fd); /* Should close sync_pipe[PIPE_READ] */ CloseHandle(sync_pipe[PIPE_WRITE]); return -1; @@ -411,6 +446,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, if (signal_pipe_write_fd == -1) { /* Couldn't create the pipe between parent and child. */ *msg = ws_strdup_printf("Couldn't get C file handle for sync pipe: %s", g_strerror(errno)); + g_strfreev(argv); ws_close(message_read_fd); /* Should close sync_pipe[PIPE_READ] */ CloseHandle(sync_pipe[PIPE_WRITE]); CloseHandle(signal_pipe); @@ -436,7 +472,25 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, si.hStdInput = NULL; /* handle for named pipe*/ si.hStdOutput = data_pipe[PIPE_WRITE]; } - si.hStdError = sync_pipe[PIPE_WRITE]; + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + /* On Windows, "[a]n inherited handle refers to the same object in the child + * process as it does in the parent process. It also has the same value." + * https://learn.microsoft.com/en-us/windows/win32/procthread/inheritance + * When converted to a file descriptor (via _open_osfhandle), the fd + * value is not necessarily the same in the two processes, but the handle + * value can be shared. + * A HANDLE is a void* though "64-bit versions of Windows use 32-bit handles + * for interoperability... only the lower 32 bits are significant, so it is + * safe to truncate the handle... or sign-extend the handle" + * https://learn.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication + * So it should be fine to call PtrToLong instead of casting to intptr_t. + * https://learn.microsoft.com/en-us/windows/win32/WinProg64/rules-for-using-pointers + */ + int argc = g_strv_length(argv); + argv = sync_pipe_add_arg(argv, &argc, "-Z"); + snprintf(sync_id, ARGV_NUMBER_LEN, "%ld", PtrToLong(sync_pipe[PIPE_WRITE])); + argv = sync_pipe_add_arg(argv, &argc, sync_id); #endif if (ifaces) { @@ -455,7 +509,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, if (si.hStdOutput && (si.hStdOutput != si.hStdInput)) { handles[i_handles++] = si.hStdOutput; } - handles[i_handles++] = si.hStdError; + handles[i_handles++] = sync_pipe[PIPE_WRITE]; if (ifaces) { for (j = 0; j < ifaces->len; j++) { interface_opts = &g_array_index(ifaces, interface_options, j); @@ -488,14 +542,16 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, } ws_close(message_read_fd); /* Should close sync_pipe[PIPE_READ] */ CloseHandle(sync_pipe[PIPE_WRITE]); - g_string_free(args, true); + g_strfreev(argv); + g_string_free(args, TRUE); g_free(handles); return -1; } *fork_child = pi.hProcess; /* We may need to store this and close it later */ CloseHandle(pi.hThread); - g_string_free(args, true); + g_strfreev(argv); + g_string_free(args, TRUE); g_free(handles); if (signal_write_fd != NULL) { @@ -506,6 +562,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, if (pipe(sync_pipe) < 0) { /* Couldn't create the message pipe between parent and child. */ *msg = ws_strdup_printf("Couldn't create sync pipe: %s", g_strerror(errno)); + g_strfreev(argv); return -1; } @@ -514,6 +571,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, if (pipe(data_pipe) < 0) { /* Couldn't create the data pipe between parent and child. */ *msg = ws_strdup_printf("Couldn't create data pipe: %s", g_strerror(errno)); + g_strfreev(argv); ws_close(sync_pipe[PIPE_READ]); ws_close(sync_pipe[PIPE_WRITE]); return -1; @@ -530,11 +588,16 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, ws_close(data_pipe[PIPE_READ]); ws_close(data_pipe[PIPE_WRITE]); } - dup2(sync_pipe[PIPE_WRITE], 2); ws_close(sync_pipe[PIPE_READ]); - ws_close(sync_pipe[PIPE_WRITE]); + /* dumpcap should be running in capture child mode (hidden feature) */ +#ifndef DEBUG_CHILD + int argc = g_strv_length(argv); + argv = sync_pipe_add_arg(argv, &argc, "-Z"); + snprintf(sync_id, ARGV_NUMBER_LEN, "%d", sync_pipe[PIPE_WRITE]); + argv = sync_pipe_add_arg(argv, &argc, sync_id); +#endif execv(argv[0], argv); - sync_pipe_write_int_msg(2, SP_EXEC_FAILED, errno); + sync_pipe_write_int_msg(sync_pipe[PIPE_WRITE], SP_EXEC_FAILED, errno); /* Exit with "_exit()", so that we don't close the connection to the X server (and cause stuff buffered up by our parent but @@ -546,6 +609,8 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, _exit(1); } + g_strfreev(argv); + if (fetch_dumpcap_pid && *fork_child > 0) fetch_dumpcap_pid(*fork_child); @@ -553,6 +618,7 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, *data_read_fd = data_pipe[PIPE_READ]; } message_read_fd = sync_pipe[PIPE_READ]; + #endif /* Parent process - read messages from the child process over the @@ -643,10 +709,11 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, if (capture_opts->ifaces->len > 1) argv = sync_pipe_add_arg(argv, &argc, "-t"); + argv = sync_pipe_add_arg(argv, &argc, "-F"); if (capture_opts->use_pcapng) - argv = sync_pipe_add_arg(argv, &argc, "-n"); + argv = sync_pipe_add_arg(argv, &argc, "pcapng"); else - argv = sync_pipe_add_arg(argv, &argc, "-P"); + argv = sync_pipe_add_arg(argv, &argc, "pcap"); if (capture_comments != NULL) { for (j = 0; j < capture_comments->len; j++) { @@ -696,6 +763,13 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, argv = sync_pipe_add_arg(argv, &argc, sring_num_files); } + if (capture_opts->print_file_names) { + char *print_name = g_strdup_printf("printname:%s", capture_opts->print_name_to); + argv = sync_pipe_add_arg(argv, &argc, "-b"); + argv = sync_pipe_add_arg(argv, &argc, print_name); + g_free(print_name); + } + if (capture_opts->has_nametimenum) { char nametimenum[ARGV_NUMBER_LEN]; argv = sync_pipe_add_arg(argv, &argc, "-b"); @@ -767,16 +841,18 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, /* Add a name for the interface, to put into an IDB. */ argv = sync_pipe_add_arg(argv, &argc, "--ifname"); argv = sync_pipe_add_arg(argv, &argc, interface_opts->name); - if (interface_opts->descr != NULL) - { - /* Add a description for the interface, to put into an IDB. */ - argv = sync_pipe_add_arg(argv, &argc, "--ifdescr"); - argv = sync_pipe_add_arg(argv, &argc, interface_opts->descr); - } } else argv = sync_pipe_add_arg(argv, &argc, interface_opts->name); + if (interface_opts->descr != NULL) + { + /* Add a description for the interface to put into an IDB and + * use for the temporary filename. */ + argv = sync_pipe_add_arg(argv, &argc, "--ifdescr"); + argv = sync_pipe_add_arg(argv, &argc, interface_opts->descr); + } + if (interface_opts->cfilter != NULL && strlen(interface_opts->cfilter) != 0) { argv = sync_pipe_add_arg(argv, &argc, "-f"); argv = sync_pipe_add_arg(argv, &argc, interface_opts->cfilter); @@ -853,14 +929,12 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, } } - /* dumpcap should be running in capture child mode (hidden feature) */ #ifndef DEBUG_CHILD - argv = sync_pipe_add_arg(argv, &argc, "-Z"); #ifdef _WIN32 + /* pass process id to dumpcap for named signal pipe */ + argv = sync_pipe_add_arg(argv, &argc, "--signal-pipe"); snprintf(control_id, ARGV_NUMBER_LEN, "%ld", GetCurrentProcessId()); argv = sync_pipe_add_arg(argv, &argc, control_id); -#else - argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE); #endif #endif @@ -889,13 +963,11 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, if (ret == -1) { report_failure("%s", msg); g_free(msg); - free_argv(argv, argc); return false; } /* Parent process - read messages from the child process over the sync pipe. */ - free_argv(argv, argc); cap_session->fork_child_status = 0; cap_session->cap_data_info = cap_data; @@ -953,10 +1025,8 @@ sync_pipe_close_command(int *data_read_fd, GIOChannel *message_read_io, * NULL, and -1 is returned; *primary_msg, and *secondary_msg if not NULL, * must be freed with g_free(). */ -/* XXX - assumes PIPE_BUF_SIZE > SP_MAX_MSG_LEN */ -#define PIPE_BUF_SIZE 5120 static int -sync_pipe_run_command_actual(char* const argv[], char **data, char **primary_msg, +sync_pipe_run_command_actual(char **argv, char **data, char **primary_msg, char **secondary_msg, void(*update_cb)(void)) { char *msg; @@ -964,7 +1034,7 @@ sync_pipe_run_command_actual(char* const argv[], char **data, char **primary_msg GIOChannel *sync_pipe_read_io; ws_process_id fork_child; char *wait_msg; - char buffer[PIPE_BUF_SIZE+1] = {0}; + char *buffer = g_malloc(PIPE_BUF_SIZE + 1); ssize_t nread; char indicator; int32_t exec_errno = 0; @@ -982,6 +1052,7 @@ sync_pipe_run_command_actual(char* const argv[], char **data, char **primary_msg *primary_msg = msg; *secondary_msg = NULL; *data = NULL; + g_free(buffer); return -1; } @@ -990,177 +1061,190 @@ sync_pipe_run_command_actual(char* const argv[], char **data, char **primary_msg * * First, wait for an SP_ERROR_MSG message or SP_SUCCESS message. */ - nread = pipe_read_block(sync_pipe_read_io, &indicator, SP_MAX_MSG_LEN, - buffer, primary_msg); - if(nread <= 0) { - /* We got a read error from the sync pipe, or we got no data at - all from the sync pipe, so we're not going to be getting any - data or error message from the child process. Pick up its - exit status, and complain. - - We don't have to worry about killing the child, if the sync pipe - returned an error. Usually this error is caused as the child killed - itself while going down. Even in the rare cases that this isn't the - case, the child will get an error when writing to the broken pipe - the next time, cleaning itself up then. */ - ret = sync_pipe_wait_for_child(fork_child, &wait_msg); - if(nread == 0) { - /* We got an EOF from the sync pipe. That means that it exited - before giving us any data to read. If ret is -1, we report - that as a bad exit (e.g., exiting due to a signal); otherwise, - we report it as a premature exit. */ - if (ret == -1) - *primary_msg = wait_msg; - else - *primary_msg = g_strdup("Child dumpcap closed sync pipe prematurely"); - } else { - /* We got an error from the sync pipe. If ret is -1, report - both the sync pipe I/O error and the wait error. */ - if (ret == -1) { - combined_msg = ws_strdup_printf("%s\n\n%s", *primary_msg, wait_msg); - g_free(*primary_msg); - g_free(wait_msg); - *primary_msg = combined_msg; + do { + nread = pipe_read_block(sync_pipe_read_io, &indicator, SP_MAX_MSG_LEN, + buffer, primary_msg); + if(nread <= 0) { + /* We got a read error from the sync pipe, or we got no data at + all from the sync pipe, so we're not going to be getting any + data or error message from the child process. Pick up its + exit status, and complain. + + We don't have to worry about killing the child, if the sync pipe + returned an error. Usually this error is caused as the child killed + itself while going down. Even in the rare cases that this isn't the + case, the child will get an error when writing to the broken pipe + the next time, cleaning itself up then. */ + g_io_channel_unref(sync_pipe_read_io); + ret = sync_pipe_wait_for_child(fork_child, &wait_msg); + if(nread == 0) { + /* We got an EOF from the sync pipe. That means that it exited + before giving us any data to read. If ret is -1, we report + that as a bad exit (e.g., exiting due to a signal); otherwise, + we report it as a premature exit. */ + if (ret == -1) + *primary_msg = wait_msg; + else + *primary_msg = g_strdup("Child dumpcap closed sync pipe prematurely"); + } else { + /* We got an error from the sync pipe. If ret is -1, report + both the sync pipe I/O error and the wait error. */ + if (ret == -1) { + combined_msg = ws_strdup_printf("%s\n\n%s", *primary_msg, wait_msg); + g_free(*primary_msg); + g_free(wait_msg); + *primary_msg = combined_msg; + } } - } - *secondary_msg = NULL; - *data = NULL; - - return -1; - } - - /* we got a valid message block from the child, process it */ - switch(indicator) { + *secondary_msg = NULL; + *data = NULL; + g_free(buffer); - case SP_EXEC_FAILED: - /* - * Exec of dumpcap failed. Get the errno for the failure. - */ - if (!ws_strtoi32(buffer, NULL, &exec_errno)) { - ws_warning("Invalid errno: %s", buffer); + return -1; } - /* - * Pick up the child status. - */ - ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, - &fork_child, &msg); - if (ret == -1) { - /* - * Child process failed unexpectedly, or wait failed; msg is the - * error message. - */ - *primary_msg = msg; - *secondary_msg = NULL; - } else { + /* we got a valid message block from the child, process it */ + switch(indicator) { + + case SP_EXEC_FAILED: /* - * Child process failed, but returned the expected exit status. - * Return the messages it gave us, and indicate failure. + * Exec of dumpcap failed. Get the errno for the failure. */ - *primary_msg = ws_strdup_printf("Couldn't run dumpcap in child process: %s", - g_strerror(exec_errno)); - *secondary_msg = NULL; - ret = -1; - } - *data = NULL; - break; - - case SP_ERROR_MSG: - /* - * Error from dumpcap; there will be a primary message and a - * secondary message. - */ - - /* convert primary message */ - pipe_convert_header((unsigned char*)buffer, 4, &indicator, &primary_msg_len); - primary_msg_text = buffer+4; - /* convert secondary message */ - pipe_convert_header((unsigned char*)primary_msg_text + primary_msg_len, 4, &indicator, - &secondary_msg_len); - secondary_msg_text = primary_msg_text + primary_msg_len + 4; - /* the capture child will close the sync_pipe, nothing to do */ + if (!ws_strtoi32(buffer, NULL, &exec_errno)) { + ws_warning("Invalid errno: %s", buffer); + } - /* - * Pick up the child status. - */ - ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, - &fork_child, &msg); - if (ret == -1) { /* - * Child process failed unexpectedly, or wait failed; msg is the - * error message. + * Pick up the child status. */ - *primary_msg = msg; - *secondary_msg = NULL; - } else { + ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, + &fork_child, &msg); + if (ret == -1) { + /* + * Child process failed unexpectedly, or wait failed; msg is the + * error message. + */ + *primary_msg = msg; + *secondary_msg = NULL; + } else { + /* + * Child process failed, but returned the expected exit status. + * Return the messages it gave us, and indicate failure. + */ + *primary_msg = ws_strdup_printf("Couldn't run dumpcap in child process: %s", + g_strerror(exec_errno)); + *secondary_msg = NULL; + ret = -1; + } + *data = NULL; + break; + + case SP_ERROR_MSG: /* - * Child process failed, but returned the expected exit status. - * Return the messages it gave us, and indicate failure. + * Error from dumpcap; there will be a primary message and a + * secondary message. */ - *primary_msg = g_strdup(primary_msg_text); - *secondary_msg = g_strdup(secondary_msg_text); - ret = -1; - } - *data = NULL; - break; - case SP_SUCCESS: - /* read the output from the command */ - data_buf = g_string_new(""); - while ((count = ws_read(data_pipe_read_fd, buffer, PIPE_BUF_SIZE)) > 0) { - buffer[count] = '\0'; - g_string_append(data_buf, buffer); - } + /* convert primary message */ + pipe_convert_header((unsigned char*)buffer, 4, &indicator, &primary_msg_len); + primary_msg_text = buffer+4; + /* convert secondary message */ + pipe_convert_header((unsigned char*)primary_msg_text + primary_msg_len, 4, &indicator, + &secondary_msg_len); + secondary_msg_text = primary_msg_text + primary_msg_len + 4; + /* the capture child will close the sync_pipe, nothing to do */ - /* - * Pick up the child status. - */ - ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, - &fork_child, &msg); - if (ret == -1) { /* - * Child process failed unexpectedly, or wait failed; msg is the - * error message. + * Pick up the child status. */ - *primary_msg = msg; - *secondary_msg = NULL; - g_string_free(data_buf, true); + ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, + &fork_child, &msg); + if (ret == -1) { + /* + * Child process failed unexpectedly, or wait failed; msg is the + * error message. + */ + *primary_msg = msg; + *secondary_msg = NULL; + } else { + /* + * Child process failed, but returned the expected exit status. + * Return the messages it gave us, and indicate failure. + */ + *primary_msg = g_strdup(primary_msg_text); + *secondary_msg = g_strdup(secondary_msg_text); + ret = -1; + } *data = NULL; - } else { + break; + + case SP_LOG_MSG: /* - * Child process succeeded. + * Log from dumpcap; pass to our log */ - *primary_msg = NULL; - *secondary_msg = NULL; - *data = g_string_free(data_buf, false); - } - break; + sync_pipe_handle_log_msg(buffer); + break; + + case SP_SUCCESS: + /* read the output from the command */ + data_buf = g_string_new(""); + while ((count = ws_read(data_pipe_read_fd, buffer, PIPE_BUF_SIZE)) > 0) { + buffer[count] = '\0'; + g_string_append(data_buf, buffer); + } - default: - /* - * Pick up the child status. - */ - ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, - &fork_child, &msg); - if (ret == -1) { /* - * Child process failed unexpectedly, or wait failed; msg is the - * error message. + * Pick up the child status. */ - *primary_msg = msg; - *secondary_msg = NULL; - } else { + ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, + &fork_child, &msg); + if (ret == -1) { + /* + * Child process failed unexpectedly, or wait failed; msg is the + * error message. + */ + *primary_msg = msg; + *secondary_msg = NULL; + g_string_free(data_buf, TRUE); + *data = NULL; + } else { + /* + * Child process succeeded. + */ + *primary_msg = NULL; + *secondary_msg = NULL; + *data = g_string_free(data_buf, FALSE); + } + break; + + default: /* - * Child process returned an unknown status. + * Pick up the child status. */ - *primary_msg = ws_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x", - indicator); - *secondary_msg = NULL; - ret = -1; + ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, + &fork_child, &msg); + if (ret == -1) { + /* + * Child process failed unexpectedly, or wait failed; msg is the + * error message. + */ + *primary_msg = msg; + *secondary_msg = NULL; + } else { + /* + * Child process returned an unknown status. + */ + *primary_msg = ws_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x", + indicator); + *secondary_msg = NULL; + ret = -1; + } + *data = NULL; + break; } - *data = NULL; - break; - } + } while (indicator != SP_SUCCESS && ret != -1); + + g_free(buffer); return ret; } @@ -1168,7 +1252,7 @@ sync_pipe_run_command_actual(char* const argv[], char **data, char **primary_msg * redirects to sync_pipe_run_command_actual() */ static int -sync_pipe_run_command(char* const argv[], char **data, char **primary_msg, +sync_pipe_run_command(char **argv, char **data, char **primary_msg, char **secondary_msg, void (*update_cb)(void)) { int ret, i; @@ -1233,22 +1317,14 @@ sync_interface_set_80211_chan(const char *iface, const char *freq, const char *t *primary_msg = g_strdup("Out of mem."); *secondary_msg = NULL; *data = NULL; - free_argv(argv, argc); return -1; } argv = sync_pipe_add_arg(argv, &argc, "-k"); argv = sync_pipe_add_arg(argv, &argc, opt); -#ifndef DEBUG_CHILD - /* Run dumpcap in capture child mode */ - argv = sync_pipe_add_arg(argv, &argc, "-Z"); - argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE); -#endif - ret = sync_pipe_run_command(argv, data, primary_msg, secondary_msg, update_cb); g_free(opt); - free_argv(argv, argc); return ret; } @@ -1286,13 +1362,7 @@ sync_interface_list_open(char **data, char **primary_msg, /* Ask for the interface list */ argv = sync_pipe_add_arg(argv, &argc, "-D"); -#ifndef DEBUG_CHILD - /* Run dumpcap in capture child mode */ - argv = sync_pipe_add_arg(argv, &argc, "-Z"); - argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE); -#endif ret = sync_pipe_run_command(argv, data, primary_msg, secondary_msg, update_cb); - free_argv(argv, argc); return ret; } @@ -1340,13 +1410,51 @@ sync_if_capabilities_open(const char *ifname, bool monitor_mode, const char* aut argv = sync_pipe_add_arg(argv, &argc, auth); } -#ifndef DEBUG_CHILD - /* Run dumpcap in capture child mode */ - argv = sync_pipe_add_arg(argv, &argc, "-Z"); - argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE); -#endif ret = sync_pipe_run_command(argv, data, primary_msg, secondary_msg, update_cb); - free_argv(argv, argc); + return ret; +} + +int +sync_if_list_capabilities_open(GList *if_queries, + char **data, char **primary_msg, + char **secondary_msg, void (*update_cb)(void)) +{ + int argc; + char **argv; + int ret; + if_cap_query_t *if_cap_query; + + ws_debug("sync_if_list_capabilities_open"); + + argv = init_pipe_args(&argc); + + if (!argv) { + *primary_msg = g_strdup("We don't know where to find dumpcap."); + *secondary_msg = NULL; + *data = NULL; + return -1; + } + + for (GList *li = if_queries; li != NULL; li = g_list_next(li)) { + if_cap_query = (if_cap_query_t*)li->data; + /* Ask for the interface capabilities */ + argv = sync_pipe_add_arg(argv, &argc, "-i"); + argv = sync_pipe_add_arg(argv, &argc, if_cap_query->name); + if (if_cap_query->monitor_mode) + argv = sync_pipe_add_arg(argv, &argc, "-I"); + if (if_cap_query->auth_username && if_cap_query->auth_password) { + char sauth[256]; + argv = sync_pipe_add_arg(argv, &argc, "-A"); + snprintf(sauth, sizeof(sauth), "%s:%s", + if_cap_query->auth_username, + if_cap_query->auth_password); + argv = sync_pipe_add_arg(argv, &argc, sauth); + } + } + argv = sync_pipe_add_arg(argv, &argc, "-L"); + argv = sync_pipe_add_arg(argv, &argc, "--list-time-stamp-types"); + + ret = sync_pipe_run_command(argv, data, primary_msg, secondary_msg, update_cb); return ret; } @@ -1355,16 +1463,18 @@ sync_if_capabilities_open(const char *ifname, bool monitor_mode, const char* aut * contains the file descriptor for the pipe's stdout, *msg is unchanged, * and zero is returned. On failure, *msg will point to an error message * that must be g_free()d, and -1 will be returned. + * If data is not NULL, then it will also be set to point to a JSON + * serialization of the list of local interfaces and their capabilities. */ int -sync_interface_stats_open(int *data_read_fd, ws_process_id *fork_child, char **msg, void (*update_cb)(void)) +sync_interface_stats_open(int *data_read_fd, ws_process_id *fork_child, char **data, char **msg, void (*update_cb)(void)) { int argc; char **argv; int ret; GIOChannel *message_read_io; char *wait_msg; - char buffer[PIPE_BUF_SIZE+1] = {0}; + char *buffer = g_malloc(PIPE_BUF_SIZE + 1); ssize_t nread; char indicator; int32_t exec_errno = 0; @@ -1380,28 +1490,34 @@ sync_interface_stats_open(int *data_read_fd, ws_process_id *fork_child, char **m if (!argv) { *msg = g_strdup("We don't know where to find dumpcap."); + g_free(buffer); return -1; } /* Ask for the interface statistics */ argv = sync_pipe_add_arg(argv, &argc, "-S"); + /* If requested, ask for the interface list and capabilities. */ + if (data) { + argv = sync_pipe_add_arg(argv, &argc, "-D"); + argv = sync_pipe_add_arg(argv, &argc, "-L"); + } + #ifndef DEBUG_CHILD - argv = sync_pipe_add_arg(argv, &argc, "-Z"); #ifdef _WIN32 + argv = sync_pipe_add_arg(argv, &argc, "--signal-pipe"); ret = create_dummy_signal_pipe(msg); if (ret == -1) { + g_free(buffer); return -1; } argv = sync_pipe_add_arg(argv, &argc, dummy_control_id); -#else - argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE); #endif #endif ret = sync_pipe_open_command(argv, data_read_fd, &message_read_io, NULL, fork_child, NULL, msg, update_cb); - free_argv(argv, argc); if (ret == -1) { + g_free(buffer); return -1; } @@ -1410,126 +1526,163 @@ sync_interface_stats_open(int *data_read_fd, ws_process_id *fork_child, char **m * * First, wait for an SP_ERROR_MSG message or SP_SUCCESS message. */ - nread = pipe_read_block(message_read_io, &indicator, SP_MAX_MSG_LEN, - buffer, msg); - if(nread <= 0) { - /* We got a read error from the sync pipe, or we got no data at - all from the sync pipe, so we're not going to be getting any - data or error message from the child process. Pick up its - exit status, and complain. - - We don't have to worry about killing the child, if the sync pipe - returned an error. Usually this error is caused as the child killed - itself while going down. Even in the rare cases that this isn't the - case, the child will get an error when writing to the broken pipe - the next time, cleaning itself up then. */ - ret = sync_pipe_wait_for_child(*fork_child, &wait_msg); - g_io_channel_unref(message_read_io); - ws_close(*data_read_fd); - if(nread == 0) { - /* We got an EOF from the sync pipe. That means that it exited - before giving us any data to read. If ret is -1, we report - that as a bad exit (e.g., exiting due to a signal); otherwise, - we report it as a premature exit. */ - if (ret == -1) - *msg = wait_msg; - else - *msg = g_strdup("Child dumpcap closed sync pipe prematurely"); - } else { - /* We got an error from the sync pipe. If ret is -1, report - both the sync pipe I/O error and the wait error. */ - if (ret == -1) { - combined_msg = ws_strdup_printf("%s\n\n%s", *msg, wait_msg); - g_free(*msg); - g_free(wait_msg); - *msg = combined_msg; + do { + nread = pipe_read_block(message_read_io, &indicator, SP_MAX_MSG_LEN, + buffer, msg); + if(nread <= 0) { + /* We got a read error from the sync pipe, or we got no data at + all from the sync pipe, so we're not going to be getting any + data or error message from the child process. Pick up its + exit status, and complain. + + We don't have to worry about killing the child, if the sync pipe + returned an error. Usually this error is caused as the child killed + itself while going down. Even in the rare cases that this isn't the + case, the child will get an error when writing to the broken pipe + the next time, cleaning itself up then. */ + g_io_channel_unref(message_read_io); + ws_close(*data_read_fd); + ret = sync_pipe_wait_for_child(*fork_child, &wait_msg); + if(nread == 0) { + /* We got an EOF from the sync pipe. That means that it exited + before giving us any data to read. If ret is -1, we report + that as a bad exit (e.g., exiting due to a signal); otherwise, + we report it as a premature exit. */ + if (ret == -1) + *msg = wait_msg; + else + *msg = g_strdup("Child dumpcap closed sync pipe prematurely"); + } else { + /* We got an error from the sync pipe. If ret is -1, report + both the sync pipe I/O error and the wait error. */ + if (ret == -1) { + combined_msg = ws_strdup_printf("%s\n\n%s", *msg, wait_msg); + g_free(*msg); + g_free(wait_msg); + *msg = combined_msg; + } } + g_free(buffer); + return -1; } - return -1; - } - /* we got a valid message block from the child, process it */ - switch(indicator) { + /* we got a valid message block from the child, process it */ + switch(indicator) { - case SP_EXEC_FAILED: - /* - * Exec of dumpcap failed. Get the errno for the failure. - */ - if (!ws_strtoi32(buffer, NULL, &exec_errno)) { - ws_warning("Invalid errno: %s", buffer); - } - *msg = ws_strdup_printf("Couldn't run dumpcap in child process: %s", - g_strerror(exec_errno)); + case SP_EXEC_FAILED: + /* + * Exec of dumpcap failed. Get the errno for the failure. + */ + if (!ws_strtoi32(buffer, NULL, &exec_errno)) { + ws_warning("Invalid errno: %s", buffer); + } + *msg = ws_strdup_printf("Couldn't run dumpcap in child process: %s", + g_strerror(exec_errno)); - /* - * Pick up the child status. - */ - sync_pipe_close_command(data_read_fd, message_read_io, - fork_child, msg); - ret = -1; - break; + /* + * Pick up the child status. + */ + char *close_msg = NULL; + sync_pipe_close_command(data_read_fd, message_read_io, + fork_child, &close_msg); + /* + * Ignore the error from sync_pipe_close_command, presumably the one + * returned by the child is more pertinent to what went wrong. + */ + g_free(close_msg); + ret = -1; + break; - case SP_ERROR_MSG: - /* - * Error from dumpcap; there will be a primary message and a - * secondary message. - */ + case SP_ERROR_MSG: + /* + * Error from dumpcap; there will be a primary message and a + * secondary message. + */ - /* convert primary message */ - pipe_convert_header((unsigned char*)buffer, 4, &indicator, &primary_msg_len); - primary_msg_text = buffer+4; - /* convert secondary message */ - pipe_convert_header((unsigned char*)primary_msg_text + primary_msg_len, 4, &indicator, - &secondary_msg_len); - /*secondary_msg_text = primary_msg_text + primary_msg_len + 4;*/ - /* the capture child will close the sync_pipe, nothing to do */ + /* convert primary message */ + pipe_convert_header((unsigned char*)buffer, 4, &indicator, &primary_msg_len); + primary_msg_text = buffer+4; + /* convert secondary message */ + pipe_convert_header((unsigned char*)primary_msg_text + primary_msg_len, 4, &indicator, + &secondary_msg_len); + /*secondary_msg_text = primary_msg_text + primary_msg_len + 4;*/ + /* the capture child will close the sync_pipe, nothing to do */ - /* - * Pick up the child status. - */ - ret = sync_pipe_close_command(data_read_fd, message_read_io, - fork_child, msg); - if (ret == -1) { /* - * Child process failed unexpectedly, or wait failed; msg is the - * error message. + * Pick up the child status. */ - } else { + ret = sync_pipe_close_command(data_read_fd, message_read_io, + fork_child, msg); + if (ret == -1) { + /* + * Child process failed unexpectedly, or wait failed; msg is the + * error message. + */ + } else if (ret == WS_EXIT_NO_INTERFACES) { + /* + * No interfaces were found. If that's not the + * result of an error when fetching the local + * interfaces, let the user know. + */ + *msg = g_strdup(primary_msg_text); + } else { + /* + * Child process failed, but returned the expected exit status. + * Return the messages it gave us, and indicate failure. + */ + *msg = g_strdup(primary_msg_text); + ret = -1; + } + g_free(buffer); + return ret; + + case SP_LOG_MSG: /* - * Child process failed, but returned the expected exit status. - * Return the messages it gave us, and indicate failure. + * Log from dumpcap; pass to our log */ - *msg = g_strdup(primary_msg_text); - ret = -1; - } - break; - - case SP_SUCCESS: - /* Close the message pipe. */ - g_io_channel_unref(message_read_io); - break; + sync_pipe_handle_log_msg(buffer); + break; - default: - /* - * Pick up the child status. - */ - ret = sync_pipe_close_command(data_read_fd, message_read_io, - fork_child, msg); - if (ret == -1) { + case SP_IFACE_LIST: /* - * Child process failed unexpectedly, or wait failed; msg is the - * error message. + * Dumpcap giving us the interface list */ - } else { + + /* convert primary message */ + if (data) { + *data = g_strdup(buffer); + } + break; + + case SP_SUCCESS: + /* Close the message pipe. */ + g_io_channel_unref(message_read_io); + break; + + default: /* - * Child process returned an unknown status. + * Pick up the child status. */ - *msg = ws_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x", - indicator); - ret = -1; + ret = sync_pipe_close_command(data_read_fd, message_read_io, + fork_child, msg); + if (ret == -1) { + /* + * Child process failed unexpectedly, or wait failed; msg is the + * error message. + */ + } else { + /* + * Child process returned an unknown status. + */ + *msg = ws_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x", + indicator); + ret = -1; + } + break; } - break; - } + } while (indicator != SP_SUCCESS && ret != -1); + + g_free(buffer); return ret; } @@ -1680,14 +1833,13 @@ pipe_read_block(GIOChannel *pipe_io, char *indicator, int len, char *msg, header[0], header[1], header[2], header[3]); /* we have a problem here, try to read some more bytes from the pipe to debug where the problem really is */ - memcpy(msg, header, sizeof(header)); - g_io_channel_read_chars(pipe_io, &msg[sizeof(header)], len-sizeof(header), &bytes_read, &err); + g_io_channel_read_chars(pipe_io, msg, len, &bytes_read, &err); if (err != NULL) { /* error */ ws_debug("read from pipe %p: error(%u): %s", pipe_io, err->code, err->message); g_clear_error(&err); } - *err_msg = ws_strdup_printf("Unknown message from dumpcap reading header, try to show it as a string: %s", - msg); + *err_msg = ws_strdup_printf("Message %c from dumpcap with length %d > buffer size %d! Partial message: %s", + *indicator, required, len, msg); return -1; } len = required; @@ -1716,7 +1868,7 @@ static gboolean sync_pipe_input_cb(GIOChannel *pipe_io, capture_session *cap_session) { int ret; - char buffer[SP_MAX_MSG_LEN+1] = {0}; + char *buffer = g_malloc(SP_MAX_MSG_LEN + 1); ssize_t nread; char indicator; int32_t exec_errno = 0; @@ -1775,6 +1927,7 @@ sync_pipe_input_cb(GIOChannel *pipe_io, capture_session *cap_session) } else { extcap_request_stop(cap_session); } + g_free(buffer); return false; } @@ -1801,6 +1954,7 @@ sync_pipe_input_cb(GIOChannel *pipe_io, capture_session *cap_session) "standard output", as the capture file. */ sync_pipe_stop(cap_session); cap_session->closed(cap_session, NULL); + g_free(buffer); return false; } break; @@ -1837,6 +1991,12 @@ sync_pipe_input_cb(GIOChannel *pipe_io, capture_session *cap_session) /* the capture child will close the sync_pipe, nothing to do for now */ /* (an error message doesn't mean we have to stop capturing) */ break; + case SP_LOG_MSG: + /* + * Log from dumpcap; pass to our log + */ + sync_pipe_handle_log_msg(buffer); + break; case SP_BAD_FILTER: { const char *message=NULL; uint32_t indx = 0; @@ -1870,6 +2030,7 @@ sync_pipe_input_cb(GIOChannel *pipe_io, capture_session *cap_session) break; } + g_free(buffer); return true; } |