diff options
Diffstat (limited to 'capture/capture_sync.c')
-rw-r--r-- | capture/capture_sync.c | 1665 |
1 files changed, 957 insertions, 708 deletions
diff --git a/capture/capture_sync.c b/capture/capture_sync.c index 5ce1d479da..4734d0dcd3 100644 --- a/capture/capture_sync.c +++ b/capture/capture_sync.c @@ -11,23 +11,27 @@ #include "config.h" #define WS_LOG_DOMAIN LOG_DOMAIN_CAPTURE +#include <wireshark.h> + #ifdef HAVE_LIBPCAP #include <glib.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> #include <signal.h> +#include <ws_exit_codes.h> + #include <wsutil/strtoi.h> -#include <wsutil/wslog.h> #include <wsutil/ws_assert.h> #ifdef _WIN32 #include <wsutil/unicode-utils.h> #include <wsutil/win32-utils.h> #include <wsutil/ws_pipe.h> +#else +#include <glib-unix.h> #endif #ifdef HAVE_SYS_WAIT_H @@ -92,30 +96,27 @@ #include <wsutil/ws_pipe.h> #ifdef _WIN32 -static void create_dummy_signal_pipe(); +static int create_dummy_signal_pipe(char **msg); static HANDLE dummy_signal_pipe; /* Dummy named pipe which lets the child check for a dropped connection */ -static gchar *dummy_control_id; +static char *dummy_control_id; #else 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(gint source, gpointer user_data); -static int sync_pipe_wait_for_child(ws_process_id fork_child, gchar **msgp); -static void pipe_convert_header(const guchar *header, int header_len, char *indicator, int *block_len); -static ssize_t pipe_read_block(int pipe_fd, char *indicator, int len, char *msg, +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); +static void pipe_convert_header(const unsigned char *header, int header_len, char *indicator, int *block_len); +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); -} - void capture_session_init(capture_session *cap_session, capture_file *cf, new_file_fn new_file, new_packets_fn new_packets, @@ -124,6 +125,7 @@ capture_session_init(capture_session *cap_session, capture_file *cf, { cap_session->cf = cf; cap_session->fork_child = WS_INVALID_PID; /* invalid process handle */ + cap_session->pipe_input_id = 0; #ifdef _WIN32 cap_session->signal_pipe_write_fd = -1; #endif @@ -133,7 +135,8 @@ capture_session_init(capture_session *cap_session, capture_file *cf, cap_session->group = getgid(); #endif cap_session->count = 0; - cap_session->session_will_restart = FALSE; + cap_session->count_pending = 0; + cap_session->session_will_restart = false; cap_session->new_file = new_file; cap_session->new_packets = new_packets; @@ -141,10 +144,60 @@ capture_session_init(capture_session *cap_session, capture_file *cf, cap_session->error = error; cap_session->cfilter_error = cfilter_error; cap_session->closed = closed; + cap_session->frame_cksum = NULL; +} + +void capture_process_finished(capture_session *cap_session) +{ + capture_options *capture_opts = cap_session->capture_opts; + interface_options *interface_opts; + GString *message; + unsigned i; + + if (!extcap_session_stop(cap_session)) { + /* Atleast one extcap process did not fully finish yet, wait for it */ + return; + } + + if (cap_session->fork_child != WS_INVALID_PID) { + if (capture_opts->stop_after_extcaps) { + /* User has requested capture stop and all extcaps are gone now */ + capture_opts->stop_after_extcaps = false; + sync_pipe_stop(cap_session); + } + /* Wait for child process to end, session is not closed yet */ + return; + } + + /* Construct message and close session */ + message = g_string_new(capture_opts->closed_msg); + for (i = 0; i < capture_opts->ifaces->len; i++) { + interface_opts = &g_array_index(capture_opts->ifaces, interface_options, i); + if (interface_opts->if_type != IF_EXTCAP) { + continue; + } + + if ((interface_opts->extcap_stderr != NULL) && + (interface_opts->extcap_stderr->len > 0)) { + if (message->len > 0) { + g_string_append(message, "\n"); + } + g_string_append(message, "Error from extcap pipe: "); + g_string_append(message, interface_opts->extcap_stderr->str); + } + } + + cap_session->closed(cap_session, message->str); + g_string_free(message, true); + g_free(capture_opts->closed_msg); + capture_opts->closed_msg = NULL; + capture_opts->stop_after_extcaps = false; } /* 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) { @@ -152,7 +205,7 @@ sync_pipe_add_arg(char **args, int *argc, const char *arg) pointers, *not* counting the NULL pointer at the end, so we have to add 2 in order to get the new size of the array, including the new pointer and the terminating NULL pointer. */ - args = (char **)g_realloc( (gpointer) args, (*argc + 2) * sizeof (char *)); + args = (char **)g_realloc( (void *) args, (*argc + 2) * sizeof (char *)); /* Stuff the pointer into the penultimate element of the array, which is the one at the index specified by "*argc". */ @@ -167,16 +220,33 @@ 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) { - char **argv; - const char *progfile_dir; char *exename; + char **argv; - progfile_dir = get_progfile_dir(); - if (progfile_dir == NULL) { - return NULL; + /* Find the absolute path of the dumpcap executable. */ + exename = get_executable_path("dumpcap"); + if (exename == NULL) { + return NULL; } /* Allocate the string pointer array with enough space for the @@ -185,70 +255,455 @@ init_pipe_args(int *argc) { argv = (char **)g_malloc(sizeof (char *)); *argv = NULL; - /* take Wireshark's absolute program path and replace "Wireshark" with "dumpcap" */ -#ifdef _WIN32 - exename = g_strdup_printf("%s\\dumpcap.exe", progfile_dir); -#else - exename = g_strdup_printf("%s/dumpcap", progfile_dir); -#endif - /* 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); return argv; } +static gboolean +pipe_io_cb(GIOChannel *pipe_io, GIOCondition condition _U_, void * user_data) +{ + capture_session *cap_session = (capture_session *)user_data; + if (!sync_pipe_input_cb(pipe_io, cap_session)) { + cap_session->pipe_input_id = 0; + return G_SOURCE_REMOVE; + } + return G_SOURCE_CONTINUE; +} + +/* + * Open two pipes to dumpcap with the supplied arguments, one for its + * standard output and one for its standard error. + * + * On success, *msg is unchanged and 0 is returned; data_read_fd, + * message_read_fd, and fork_child point to the standard output pipe's + * file descriptor, the standard error pipe's file descriptor, and + * the child's PID/handle, respectively. + * + * 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(). + */ #define ARGV_NUMBER_LEN 24 -/* a new capture run: start a new dumpcap task and hand over parameters through command line */ -gboolean -sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, - capture_session *cap_session, info_data_t* cap_data, - void (*update_cb)(void)) +static int +#ifdef _WIN32 +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 **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)) +#endif { + 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_read; /* pipe used to send messages from child to parent */ - HANDLE sync_pipe_write; /* pipe used to send messages from child to parent */ - int signal_pipe_write_fd; + 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 */ + int signal_pipe_write_fd = -1; HANDLE signal_pipe; /* named pipe used to send messages from parent to child (currently only stop) */ + char control_id[ARGV_NUMBER_LEN]; + char *signal_pipe_name; + size_t i_handles = 0; + HANDLE *handles; GString *args = g_string_sized_new(200); - gchar *quoted_arg; + char *quoted_arg; SECURITY_ATTRIBUTES sa; STARTUPINFO si; PROCESS_INFORMATION pi; - char control_id[ARGV_NUMBER_LEN]; - gchar *signal_pipe_name; + int i; + unsigned j; + interface_options *interface_opts; #else - char errmsg[1024+1]; int sync_pipe[2]; /* pipe used to send messages from child to parent */ - enum PIPES { PIPE_READ, PIPE_WRITE }; /* Constants 0 and 1 for PIPE_READ and PIPE_WRITE */ + int data_pipe[2]; /* pipe used to send data from child to parent */ +#endif + *fork_child = WS_INVALID_PID; + if (data_read_fd != NULL) { + *data_read_fd = -1; + } + *message_read_io = NULL; + ws_debug("sync_pipe_open_command"); + + if (!msg) { + /* We can't return anything */ + g_strfreev(argv); +#ifdef _WIN32 + g_string_free(args, true); +#endif + return -1; + } + +#ifdef _WIN32 + /* init SECURITY_ATTRIBUTES */ + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = false; + sa.lpSecurityDescriptor = NULL; + + /* Create a pipe for the child process to send us messages */ + /* (increase this value if you have trouble while fast capture file switches) */ + if (! CreatePipe(&sync_pipe[PIPE_READ], &sync_pipe[PIPE_WRITE], &sa, PIPE_BUF_SIZE)) { + /* 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; + } + + /* + * Associate a C run-time file handle with the Windows HANDLE for the + * read side of the message pipe. + * + * (See http://www.flounder.com/handles.htm for information on various + * types of file handle in C/C++ on Windows.) + */ + 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; + } + + if (data_read_fd != NULL) { + /* Create a pipe for the child process to send us data */ + /* (increase this value if you have trouble while fast capture file switches) */ + if (! CreatePipe(&data_pipe[PIPE_READ], &data_pipe[PIPE_WRITE], &sa, PIPE_BUF_SIZE)) { + /* 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; + } + + /* + * Associate a C run-time file handle with the Windows HANDLE for the + * read side of the data pipe. + * + * (See http://www.flounder.com/handles.htm for information on various + * types of file handle in C/C++ on Windows.) + */ + *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] */ + CloseHandle(sync_pipe[PIPE_WRITE]); + return -1; + } + } + + if (signal_write_fd != NULL) { + /* Create the signal pipe */ + snprintf(control_id, ARGV_NUMBER_LEN, "%ld", GetCurrentProcessId()); + signal_pipe_name = ws_strdup_printf(SIGNAL_PIPE_FORMAT, control_id); + signal_pipe = CreateNamedPipe(utf_8to16(signal_pipe_name), + PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE, 1, 65535, 65535, 0, NULL); + g_free(signal_pipe_name); + + if (signal_pipe == INVALID_HANDLE_VALUE) { + /* 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; + } + + /* + * Associate a C run-time file handle with the Windows HANDLE for the + * read side of the message pipe. + * + * (See http://www.flounder.com/handles.htm for information on various + * types of file handle in C/C++ on Windows.) + */ + signal_pipe_write_fd = _open_osfhandle( (intptr_t) signal_pipe, _O_BINARY); + 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); + return -1; + } + } + + /* init STARTUPINFO & PROCESS_INFORMATION */ + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + memset(&pi, 0, sizeof(pi)); +#ifdef DEBUG_CHILD + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOW; +#else + si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; /* this hides the console window */ + + if (data_read_fd == NULL) { + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + } else { + si.hStdInput = NULL; /* handle for named pipe*/ + si.hStdOutput = data_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) { + for (j = 0; j < ifaces->len; j++) { + interface_opts = &g_array_index(ifaces, interface_options, j); + if (interface_opts->extcap_fifo != NULL) { + i_handles++; + } + } + } + handles = g_new(HANDLE, 3 + i_handles); + i_handles = 0; + if (si.hStdInput) { + handles[i_handles++] = si.hStdInput; + } + if (si.hStdOutput && (si.hStdOutput != si.hStdInput)) { + handles[i_handles++] = si.hStdOutput; + } + 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); + if (interface_opts->extcap_fifo != NULL) { + handles[i_handles++] = interface_opts->extcap_pipe_h; + } + } + } + + /* convert args array into a single string */ + /* XXX - could change sync_pipe_add_arg() instead */ + /* there is a drawback here: the length is internally limited to 1024 bytes */ + for(i=0; argv[i] != 0; i++) { + if(i != 0) g_string_append_c(args, ' '); /* don't prepend a space before the path!!! */ + quoted_arg = protect_arg(argv[i]); + g_string_append(args, quoted_arg); + g_free(quoted_arg); + } + + /* call dumpcap */ + if(!win32_create_process(argv[0], args->str, NULL, NULL, i_handles, handles, + CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { + *msg = ws_strdup_printf("Couldn't run %s in child process: %s", + args->str, win32strerror(GetLastError())); + if (data_read_fd) { + ws_close(*data_read_fd); /* Should close data_pipe[PIPE_READ] */ + CloseHandle(data_pipe[PIPE_WRITE]); + } else { + ws_close(signal_pipe_write_fd); + } + ws_close(message_read_fd); /* Should close sync_pipe[PIPE_READ] */ + CloseHandle(sync_pipe[PIPE_WRITE]); + 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_strfreev(argv); + g_string_free(args, true); + g_free(handles); + + if (signal_write_fd != NULL) { + *signal_write_fd = signal_pipe_write_fd; + } +#else /* _WIN32 */ + /* Create a pipe for the child process to send us messages */ + 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; + } + + if (data_read_fd != NULL) { + /* Create a pipe for the child process to send us data */ + 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; + } + } + + if ((*fork_child = fork()) == 0) { + /* + * Child process - run dumpcap with the right arguments to make + * it just capture with the specified capture parameters + */ + if (data_read_fd != NULL) { + dup2(data_pipe[PIPE_WRITE], 1); + ws_close(data_pipe[PIPE_READ]); + ws_close(data_pipe[PIPE_WRITE]); + } + ws_close(sync_pipe[PIPE_READ]); + /* 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(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 + not yet sent to be sent, as that stuff should only be sent by + our parent). We've sent an error message to the parent, so + we exit with an exit status of 1 (any exit status other than + 0 or 1 will cause an additional message to report that exit + status, over and above the error message we sent to the parent). */ + _exit(1); + } + + g_strfreev(argv); + + if (fetch_dumpcap_pid && *fork_child > 0) + fetch_dumpcap_pid(*fork_child); + + if (data_read_fd != NULL) { + *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 + sync pipe. */ + + /* Close the write sides of the pipes, so that only the child has them + open, and thus they completely close, and thus return to us + an EOF indication, if the child closes them (either deliberately + or by exiting abnormally). */ +#ifdef _WIN32 + if (data_read_fd != NULL) { + CloseHandle(data_pipe[PIPE_WRITE]); + } + CloseHandle(sync_pipe[PIPE_WRITE]); +#else + if (data_read_fd != NULL) { + ws_close(data_pipe[PIPE_WRITE]); + } + ws_close(sync_pipe[PIPE_WRITE]); #endif - int sync_pipe_read_fd; + + if (*fork_child == WS_INVALID_PID) { + /* We couldn't even create the child process. */ + *msg = ws_strdup_printf("Couldn't create child process: %s", g_strerror(errno)); + if (data_read_fd != NULL) { + ws_close(*data_read_fd); + } +#ifdef _WIN32 + if (signal_write_fd != NULL) { + ws_close(signal_pipe_write_fd); + } +#endif + ws_close(message_read_fd); + return -1; + } + +#ifdef _WIN32 + *message_read_io = g_io_channel_win32_new_fd(message_read_fd); +#else + *message_read_io = g_io_channel_unix_new(message_read_fd); +#endif + g_io_channel_set_encoding(*message_read_io, NULL, NULL); + g_io_channel_set_buffered(*message_read_io, false); + g_io_channel_set_close_on_unref(*message_read_io, true); + + /* we might wait for a moment till child is ready, so update screen now */ + if (update_cb) update_cb(); + return 0; +} + +/* a new capture run: start a new dumpcap task and hand over parameters through command line */ +bool +sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, + capture_session *cap_session, info_data_t* cap_data, + void (*update_cb)(void)) +{ +#ifdef _WIN32 + size_t i_handles = 0; + char control_id[ARGV_NUMBER_LEN]; +#endif + GIOChannel *sync_pipe_read_io; int argc; char **argv; int i; - guint j; + unsigned j; interface_options *interface_opts; if (capture_opts->ifaces->len > 1) - capture_opts->use_pcapng = TRUE; + capture_opts->use_pcapng = true; ws_debug("sync_pipe_start"); capture_opts_log(LOG_DOMAIN_CAPTURE, LOG_LEVEL_DEBUG, capture_opts); cap_session->fork_child = WS_INVALID_PID; + cap_session->capture_opts = capture_opts; - if (!extcap_init_interfaces(capture_opts)) { + if (!extcap_init_interfaces(cap_session)) { report_failure("Unable to init extcaps. (tmp fifo already exists?)"); - return FALSE; + return false; } argv = init_pipe_args(&argc); if (!argv) { /* We don't know where to find dumpcap. */ report_failure("We don't know where to find dumpcap."); - return FALSE; + return false; } if (capture_opts->ifaces->len > 1) @@ -266,60 +721,72 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, } } + if (capture_opts->temp_dir) { + argv = sync_pipe_add_arg(argv, &argc, "--temp-dir"); + argv = sync_pipe_add_arg(argv, &argc, capture_opts->temp_dir); + } + if (capture_opts->multi_files_on) { if (capture_opts->has_autostop_filesize) { char sfilesize[ARGV_NUMBER_LEN]; argv = sync_pipe_add_arg(argv, &argc, "-b"); - g_snprintf(sfilesize, ARGV_NUMBER_LEN, "filesize:%u",capture_opts->autostop_filesize); + snprintf(sfilesize, ARGV_NUMBER_LEN, "filesize:%u",capture_opts->autostop_filesize); argv = sync_pipe_add_arg(argv, &argc, sfilesize); } if (capture_opts->has_file_duration) { char sfile_duration[ARGV_NUMBER_LEN]; argv = sync_pipe_add_arg(argv, &argc, "-b"); - g_snprintf(sfile_duration, ARGV_NUMBER_LEN, "duration:%f",capture_opts->file_duration); + snprintf(sfile_duration, ARGV_NUMBER_LEN, "duration:%f",capture_opts->file_duration); argv = sync_pipe_add_arg(argv, &argc, sfile_duration); } if (capture_opts->has_file_interval) { char sfile_interval[ARGV_NUMBER_LEN]; argv = sync_pipe_add_arg(argv, &argc, "-b"); - g_snprintf(sfile_interval, ARGV_NUMBER_LEN, "interval:%d",capture_opts->file_interval); + snprintf(sfile_interval, ARGV_NUMBER_LEN, "interval:%d",capture_opts->file_interval); argv = sync_pipe_add_arg(argv, &argc, sfile_interval); } if (capture_opts->has_file_packets) { char sfile_packets[ARGV_NUMBER_LEN]; argv = sync_pipe_add_arg(argv, &argc, "-b"); - g_snprintf(sfile_packets, ARGV_NUMBER_LEN, "packets:%d",capture_opts->file_packets); + snprintf(sfile_packets, ARGV_NUMBER_LEN, "packets:%d",capture_opts->file_packets); argv = sync_pipe_add_arg(argv, &argc, sfile_packets); } if (capture_opts->has_ring_num_files) { char sring_num_files[ARGV_NUMBER_LEN]; argv = sync_pipe_add_arg(argv, &argc, "-b"); - g_snprintf(sring_num_files, ARGV_NUMBER_LEN, "files:%d",capture_opts->ring_num_files); + snprintf(sring_num_files, ARGV_NUMBER_LEN, "files:%d",capture_opts->ring_num_files); 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"); - g_snprintf(nametimenum, ARGV_NUMBER_LEN, "nametimenum:2"); + snprintf(nametimenum, ARGV_NUMBER_LEN, "nametimenum:2"); argv = sync_pipe_add_arg(argv, &argc, nametimenum); } if (capture_opts->has_autostop_files) { char sautostop_files[ARGV_NUMBER_LEN]; argv = sync_pipe_add_arg(argv, &argc, "-a"); - g_snprintf(sautostop_files, ARGV_NUMBER_LEN, "files:%d",capture_opts->autostop_files); + snprintf(sautostop_files, ARGV_NUMBER_LEN, "files:%d",capture_opts->autostop_files); argv = sync_pipe_add_arg(argv, &argc, sautostop_files); } } else { if (capture_opts->has_autostop_filesize) { char sautostop_filesize[ARGV_NUMBER_LEN]; argv = sync_pipe_add_arg(argv, &argc, "-a"); - g_snprintf(sautostop_filesize, ARGV_NUMBER_LEN, "filesize:%u",capture_opts->autostop_filesize); + snprintf(sautostop_filesize, ARGV_NUMBER_LEN, "filesize:%u",capture_opts->autostop_filesize); argv = sync_pipe_add_arg(argv, &argc, sautostop_filesize); } } @@ -327,21 +794,35 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, if (capture_opts->has_autostop_packets) { char scount[ARGV_NUMBER_LEN]; argv = sync_pipe_add_arg(argv, &argc, "-c"); - g_snprintf(scount, ARGV_NUMBER_LEN, "%d",capture_opts->autostop_packets); + snprintf(scount, ARGV_NUMBER_LEN, "%d",capture_opts->autostop_packets); argv = sync_pipe_add_arg(argv, &argc, scount); } if (capture_opts->has_autostop_duration) { char sautostop_duration[ARGV_NUMBER_LEN]; argv = sync_pipe_add_arg(argv, &argc, "-a"); - g_snprintf(sautostop_duration, ARGV_NUMBER_LEN, "duration:%f",capture_opts->autostop_duration); + snprintf(sautostop_duration, ARGV_NUMBER_LEN, "duration:%f",capture_opts->autostop_duration); argv = sync_pipe_add_arg(argv, &argc, sautostop_duration); } + if (capture_opts->has_autostop_written_packets) { + char scount[ARGV_NUMBER_LEN]; + argv = sync_pipe_add_arg(argv, &argc, "-a"); + snprintf(scount, ARGV_NUMBER_LEN, "packets:%d",capture_opts->autostop_written_packets); + argv = sync_pipe_add_arg(argv, &argc, scount); + } + if (capture_opts->group_read_access) { argv = sync_pipe_add_arg(argv, &argc, "-g"); } + if (capture_opts->update_interval != DEFAULT_UPDATE_INTERVAL) { + char scount[ARGV_NUMBER_LEN]; + argv = sync_pipe_add_arg(argv, &argc, "--update-interval"); + snprintf(scount, ARGV_NUMBER_LEN, "%d", capture_opts->update_interval); + argv = sync_pipe_add_arg(argv, &argc, scount); + } + for (j = 0; j < capture_opts->ifaces->len; j++) { interface_opts = &g_array_index(capture_opts->ifaces, interface_options, j); @@ -349,9 +830,10 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, if (interface_opts->extcap_fifo != NULL) { #ifdef _WIN32 - char *pipe = g_strdup_printf("%s%" G_GUINTPTR_FORMAT, EXTCAP_PIPE_PREFIX, interface_opts->extcap_pipe_h); + char *pipe = ws_strdup_printf("%s%" PRIuMAX, EXTCAP_PIPE_PREFIX, (uintmax_t)interface_opts->extcap_pipe_h); argv = sync_pipe_add_arg(argv, &argc, pipe); g_free(pipe); + i_handles++; #else argv = sync_pipe_add_arg(argv, &argc, interface_opts->extcap_fifo); #endif @@ -375,7 +857,7 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, if (interface_opts->has_snaplen) { char ssnap[ARGV_NUMBER_LEN]; argv = sync_pipe_add_arg(argv, &argc, "-s"); - g_snprintf(ssnap, ARGV_NUMBER_LEN, "%d", interface_opts->snaplen); + snprintf(ssnap, ARGV_NUMBER_LEN, "%d", interface_opts->snaplen); argv = sync_pipe_add_arg(argv, &argc, ssnap); } @@ -398,7 +880,7 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, argv = sync_pipe_add_arg(argv, &argc, "-B"); if(interface_opts->buffer_size == 0x00) interface_opts->buffer_size = DEFAULT_CAPTURE_BUFFER_SIZE; - g_snprintf(buffer_size, ARGV_NUMBER_LEN, "%d", interface_opts->buffer_size); + snprintf(buffer_size, ARGV_NUMBER_LEN, "%d", interface_opts->buffer_size); argv = sync_pipe_add_arg(argv, &argc, buffer_size); } #endif @@ -419,7 +901,7 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, if (interface_opts->auth_type == CAPTURE_AUTH_PWD) { char sauth[256]; argv = sync_pipe_add_arg(argv, &argc, "-A"); - g_snprintf(sauth, sizeof(sauth), "%s:%s", + snprintf(sauth, sizeof(sauth), "%s:%s", interface_opts->auth_username, interface_opts->auth_password); argv = sync_pipe_add_arg(argv, &argc, sauth); @@ -430,7 +912,7 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, if (interface_opts->sampling_method != CAPTURE_SAMP_NONE) { char ssampling[ARGV_NUMBER_LEN]; argv = sync_pipe_add_arg(argv, &argc, "-m"); - g_snprintf(ssampling, ARGV_NUMBER_LEN, "%s:%d", + snprintf(ssampling, ARGV_NUMBER_LEN, "%s:%d", interface_opts->sampling_method == CAPTURE_SAMP_BY_COUNT ? "count" : interface_opts->sampling_method == CAPTURE_SAMP_BY_TIMER ? "timer" : "undef", @@ -444,14 +926,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 - g_snprintf(control_id, ARGV_NUMBER_LEN, "%d", GetCurrentProcessId()); + /* 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 @@ -467,420 +947,43 @@ sync_pipe_start(capture_options *capture_opts, GPtrArray *capture_comments, argv = sync_pipe_add_arg(argv, &argc, capture_opts->compress_type); } + int ret; + char* msg; #ifdef _WIN32 - /* init SECURITY_ATTRIBUTES */ - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.bInheritHandle = TRUE; - sa.lpSecurityDescriptor = NULL; - - /* Create a pipe for the child process */ - /* (increase this value if you have trouble while fast capture file switches) */ - if (! CreatePipe(&sync_pipe_read, &sync_pipe_write, &sa, 5120)) { - /* Couldn't create the pipe between parent and child. */ - report_failure("Couldn't create sync pipe: %s", - win32strerror(GetLastError())); - free_argv(argv, argc); - return FALSE; - } - - /* - * Associate a C run-time file handle with the Windows HANDLE for the - * read side of the message pipe. - * - * (See http://www.flounder.com/handles.htm for information on various - * types of file handle in C/C++ on Windows.) - */ - sync_pipe_read_fd = _open_osfhandle( (intptr_t) sync_pipe_read, _O_BINARY); - if (sync_pipe_read_fd == -1) { - /* Couldn't create the pipe between parent and child. */ - report_failure("Couldn't get C file handle for sync pipe: %s", g_strerror(errno)); - CloseHandle(sync_pipe_read); - CloseHandle(sync_pipe_write); - free_argv(argv, argc); - return FALSE; - } - - /* Create the signal pipe */ - signal_pipe_name = g_strdup_printf(SIGNAL_PIPE_FORMAT, control_id); - signal_pipe = CreateNamedPipe(utf_8to16(signal_pipe_name), - PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE, 1, 65535, 65535, 0, NULL); - g_free(signal_pipe_name); - - if (signal_pipe == INVALID_HANDLE_VALUE) { - /* Couldn't create the signal pipe between parent and child. */ - report_failure("Couldn't create signal pipe: %s", - win32strerror(GetLastError())); - ws_close(sync_pipe_read_fd); /* Should close sync_pipe_read */ - CloseHandle(sync_pipe_write); - free_argv(argv, argc); - return FALSE; - } - - /* - * Associate a C run-time file handle with the Windows HANDLE for the - * read side of the message pipe. - * - * (See http://www.flounder.com/handles.htm for information on various - * types of file handle in C/C++ on Windows.) - */ - signal_pipe_write_fd = _open_osfhandle( (intptr_t) signal_pipe, _O_BINARY); - if (sync_pipe_read_fd == -1) { - /* Couldn't create the pipe between parent and child. */ - report_failure("Couldn't get C file handle for sync pipe: %s", g_strerror(errno)); - ws_close(sync_pipe_read_fd); /* Should close sync_pipe_read */ - CloseHandle(sync_pipe_write); - CloseHandle(signal_pipe); - free_argv(argv, argc); - return FALSE; - } - - /* init STARTUPINFO & PROCESS_INFORMATION */ - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - memset(&pi, 0, sizeof(pi)); -#ifdef DEBUG_CHILD - si.dwFlags = STARTF_USESHOWWINDOW; - si.wShowWindow = SW_SHOW; + ret = sync_pipe_open_command(argv, NULL, &sync_pipe_read_io, &cap_session->signal_pipe_write_fd, + &cap_session->fork_child, capture_opts->ifaces, &msg, update_cb); #else - si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; - si.wShowWindow = SW_HIDE; /* this hides the console window */ - si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); - si.hStdError = sync_pipe_write; - /*si.hStdError = (HANDLE) _get_osfhandle(2);*/ + ret = sync_pipe_open_command(argv, NULL, &sync_pipe_read_io, NULL, + &cap_session->fork_child, NULL, &msg, update_cb); #endif - /* convert args array into a single string */ - /* XXX - could change sync_pipe_add_arg() instead */ - /* there is a drawback here: the length is internally limited to 1024 bytes */ - for(i=0; argv[i] != 0; i++) { - if(i != 0) g_string_append_c(args, ' '); /* don't prepend a space before the path!!! */ - quoted_arg = protect_arg(argv[i]); - g_string_append(args, quoted_arg); - g_free(quoted_arg); - } - - /* call dumpcap */ - if(!win32_create_process(argv[0], args->str, NULL, NULL, TRUE, - CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { - report_failure("Couldn't run %s in child process: %s", - args->str, win32strerror(GetLastError())); - ws_close(sync_pipe_read_fd); /* Should close sync_pipe_read */ - CloseHandle(sync_pipe_write); - CloseHandle(signal_pipe); - free_argv(argv, argc); - g_string_free(args, TRUE); - return FALSE; - } - cap_session->fork_child = pi.hProcess; - /* We may need to store this and close it later */ - CloseHandle(pi.hThread); - g_string_free(args, TRUE); - - cap_session->signal_pipe_write_fd = signal_pipe_write_fd; - -#else /* _WIN32 */ - if (pipe(sync_pipe) < 0) { - /* Couldn't create the pipe between parent and child. */ - report_failure("Couldn't create sync pipe: %s", g_strerror(errno)); - free_argv(argv, argc); - return FALSE; - } - - if ((cap_session->fork_child = fork()) == 0) { - /* - * Child process - run dumpcap with the right arguments to make - * it just capture with the specified capture parameters - */ - dup2(sync_pipe[PIPE_WRITE], 2); - ws_close(sync_pipe[PIPE_READ]); - execv(argv[0], argv); - g_snprintf(errmsg, sizeof errmsg, "Couldn't run %s in child process: %s", - argv[0], g_strerror(errno)); - sync_pipe_errmsg_to_parent(2, errmsg, ""); - - /* Exit with "_exit()", so that we don't close the connection - to the X server (and cause stuff buffered up by our parent but - not yet sent to be sent, as that stuff should only be sent by - our parent). We've sent an error message to the parent, so - we exit with an exit status of 1 (any exit status other than - 0 or 1 will cause an additional message to report that exit - status, over and above the error message we sent to the parent). */ - _exit(1); + if (ret == -1) { + report_failure("%s", msg); + g_free(msg); + return false; } - if (fetch_dumpcap_pid && cap_session->fork_child > 0) - fetch_dumpcap_pid(cap_session->fork_child); - - sync_pipe_read_fd = sync_pipe[PIPE_READ]; -#endif - /* Parent process - read messages from the child process over the sync pipe. */ - free_argv(argv, argc); - - /* Close the write side of the pipe, so that only the child has it - open, and thus it completely closes, and thus returns to us - an EOF indication, if the child closes it (either deliberately - or by exiting abnormally). */ -#ifdef _WIN32 - CloseHandle(sync_pipe_write); -#else - ws_close(sync_pipe[PIPE_WRITE]); -#endif - - if (cap_session->fork_child == WS_INVALID_PID) { - /* We couldn't even create the child process. */ - report_failure("Couldn't create child process: %s", g_strerror(errno)); - ws_close(sync_pipe_read_fd); -#ifdef _WIN32 - ws_close(cap_session->signal_pipe_write_fd); -#endif - return FALSE; - } cap_session->fork_child_status = 0; - cap_session->capture_opts = capture_opts; cap_session->cap_data_info = cap_data; - /* we might wait for a moment till child is ready, so update screen now */ - if (update_cb) update_cb(); - /* We were able to set up to read the capture file; arrange that our callback be called whenever it's possible to read from the sync pipe, so that it's called when the child process wants to tell us something. */ /* we have a running capture, now wait for the real capture filename */ - pipe_input_set_handler(sync_pipe_read_fd, (gpointer) cap_session, - &cap_session->fork_child, sync_pipe_input_cb); - - return TRUE; -} - -/* - * Open two pipes to dumpcap with the supplied arguments, one for its - * standard output and one for its standard error. - * - * On success, *msg is unchanged and 0 is returned; data_read_fd, - * message_read_fd, and fork_child point to the standard output pipe's - * file descriptor, the standard error pipe's file descriptor, and - * the child's PID/handle, respectively. - * - * 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 - This duplicates a lot of code in sync_pipe_start() */ -/* XXX - assumes PIPE_BUF_SIZE > SP_MAX_MSG_LEN */ -#define PIPE_BUF_SIZE 5120 -static int -sync_pipe_open_command(char* const argv[], int *data_read_fd, - int *message_read_fd, ws_process_id *fork_child, gchar **msg, void(*update_cb)(void)) -{ - enum PIPES { PIPE_READ, PIPE_WRITE }; /* Constants 0 and 1 for PIPE_READ and PIPE_WRITE */ -#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 */ - GString *args = g_string_sized_new(200); - gchar *quoted_arg; - SECURITY_ATTRIBUTES sa; - STARTUPINFO si; - PROCESS_INFORMATION pi; - int i; -#else - char errmsg[1024+1]; - int sync_pipe[2]; /* pipe used to send messages from child to parent */ - int data_pipe[2]; /* pipe used to send data from child to parent */ -#endif - *fork_child = WS_INVALID_PID; - *data_read_fd = -1; - *message_read_fd = -1; - ws_debug("sync_pipe_open_command"); - - if (!msg) { - /* We can't return anything */ -#ifdef _WIN32 - g_string_free(args, TRUE); -#endif - return -1; - } - -#ifdef _WIN32 - /* init SECURITY_ATTRIBUTES */ - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.bInheritHandle = TRUE; - sa.lpSecurityDescriptor = NULL; - - /* Create a pipe for the child process to send us messages */ - /* (increase this value if you have trouble while fast capture file switches) */ - if (! CreatePipe(&sync_pipe[PIPE_READ], &sync_pipe[PIPE_WRITE], &sa, 5120)) { - /* Couldn't create the message pipe between parent and child. */ - *msg = g_strdup_printf("Couldn't create sync pipe: %s", - win32strerror(GetLastError())); - return -1; - } - - /* - * Associate a C run-time file handle with the Windows HANDLE for the - * read side of the message pipe. - * - * (See http://www.flounder.com/handles.htm for information on various - * types of file handle in C/C++ on Windows.) - */ - *message_read_fd = _open_osfhandle( (intptr_t) sync_pipe[PIPE_READ], _O_BINARY); - if (*message_read_fd == -1) { - *msg = g_strdup_printf("Couldn't get C file handle for message read pipe: %s", g_strerror(errno)); - CloseHandle(sync_pipe[PIPE_READ]); - CloseHandle(sync_pipe[PIPE_WRITE]); - return -1; - } - - /* Create a pipe for the child process to send us data */ - /* (increase this value if you have trouble while fast capture file switches) */ - if (! CreatePipe(&data_pipe[PIPE_READ], &data_pipe[PIPE_WRITE], &sa, 5120)) { - /* Couldn't create the message pipe between parent and child. */ - *msg = g_strdup_printf("Couldn't create data pipe: %s", - win32strerror(GetLastError())); - ws_close(*message_read_fd); /* Should close sync_pipe[PIPE_READ] */ - CloseHandle(sync_pipe[PIPE_WRITE]); - return -1; - } - - /* - * Associate a C run-time file handle with the Windows HANDLE for the - * read side of the data pipe. - * - * (See http://www.flounder.com/handles.htm for information on various - * types of file handle in C/C++ on Windows.) - */ - *data_read_fd = _open_osfhandle( (intptr_t) data_pipe[PIPE_READ], _O_BINARY); - if (*data_read_fd == -1) { - *msg = g_strdup_printf("Couldn't get C file handle for data read pipe: %s", g_strerror(errno)); - CloseHandle(data_pipe[PIPE_READ]); - CloseHandle(data_pipe[PIPE_WRITE]); - ws_close(*message_read_fd); /* Should close sync_pipe[PIPE_READ] */ - CloseHandle(sync_pipe[PIPE_WRITE]); - return -1; - } - - /* init STARTUPINFO & PROCESS_INFORMATION */ - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - memset(&pi, 0, sizeof(pi)); -#ifdef DEBUG_CHILD - si.dwFlags = STARTF_USESHOWWINDOW; - si.wShowWindow = SW_SHOW; -#else - si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; - si.wShowWindow = SW_HIDE; /* this hides the console window */ - si.hStdInput = NULL; /* handle for named pipe*/ - - si.hStdOutput = data_pipe[PIPE_WRITE]; - si.hStdError = sync_pipe[PIPE_WRITE]; -#endif - - /* convert args array into a single string */ - /* XXX - could change sync_pipe_add_arg() instead */ - /* there is a drawback here: the length is internally limited to 1024 bytes */ - for(i=0; argv[i] != 0; i++) { - if(i != 0) g_string_append_c(args, ' '); /* don't prepend a space before the path!!! */ - quoted_arg = protect_arg(argv[i]); - g_string_append(args, quoted_arg); - g_free(quoted_arg); - } - - /* call dumpcap */ - if(!win32_create_process(argv[0], args->str, NULL, NULL, TRUE, - CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { - *msg = g_strdup_printf("Couldn't run %s in child process: %s", - args->str, win32strerror(GetLastError())); - ws_close(*data_read_fd); /* Should close data_pipe[PIPE_READ] */ - CloseHandle(data_pipe[PIPE_WRITE]); - ws_close(*message_read_fd); /* Should close sync_pipe[PIPE_READ] */ - CloseHandle(sync_pipe[PIPE_WRITE]); - g_string_free(args, TRUE); - return -1; - } - *fork_child = pi.hProcess; - /* We may need to store this and close it later */ - CloseHandle(pi.hThread); - g_string_free(args, TRUE); -#else /* _WIN32 */ - /* Create a pipe for the child process to send us messages */ - if (pipe(sync_pipe) < 0) { - /* Couldn't create the message pipe between parent and child. */ - *msg = g_strdup_printf("Couldn't create sync pipe: %s", g_strerror(errno)); - return -1; - } - - /* Create a pipe for the child process to send us data */ - if (pipe(data_pipe) < 0) { - /* Couldn't create the data pipe between parent and child. */ - *msg = g_strdup_printf("Couldn't create data pipe: %s", g_strerror(errno)); - ws_close(sync_pipe[PIPE_READ]); - ws_close(sync_pipe[PIPE_WRITE]); - return -1; - } - - if ((*fork_child = fork()) == 0) { - /* - * Child process - run dumpcap with the right arguments to make - * it just capture with the specified capture parameters - */ - dup2(data_pipe[PIPE_WRITE], 1); - 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]); - execv(argv[0], argv); - g_snprintf(errmsg, sizeof errmsg, "Couldn't run %s in child process: %s", - argv[0], g_strerror(errno)); - sync_pipe_errmsg_to_parent(2, errmsg, ""); - - /* Exit with "_exit()", so that we don't close the connection - to the X server (and cause stuff buffered up by our parent but - not yet sent to be sent, as that stuff should only be sent by - our parent). We've sent an error message to the parent, so - we exit with an exit status of 1 (any exit status other than - 0 or 1 will cause an additional message to report that exit - status, over and above the error message we sent to the parent). */ - _exit(1); + if (cap_session->pipe_input_id) { + g_source_remove(cap_session->pipe_input_id); + cap_session->pipe_input_id = 0; } + cap_session->pipe_input_id = g_io_add_watch(sync_pipe_read_io, G_IO_IN | G_IO_HUP, pipe_io_cb, cap_session); + /* Pipe will be closed when watch is removed */ + g_io_channel_unref(sync_pipe_read_io); - if (fetch_dumpcap_pid && *fork_child > 0) - fetch_dumpcap_pid(*fork_child); - - *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 - sync pipe. */ - - /* Close the write sides of the pipes, so that only the child has them - open, and thus they completely close, and thus return to us - an EOF indication, if the child closes them (either deliberately - or by exiting abnormally). */ -#ifdef _WIN32 - CloseHandle(data_pipe[PIPE_WRITE]); - CloseHandle(sync_pipe[PIPE_WRITE]); -#else - ws_close(data_pipe[PIPE_WRITE]); - ws_close(sync_pipe[PIPE_WRITE]); -#endif - - if (*fork_child == WS_INVALID_PID) { - /* We couldn't even create the child process. */ - *msg = g_strdup_printf("Couldn't create child process: %s", g_strerror(errno)); - ws_close(*data_read_fd); - ws_close(*message_read_fd); - return -1; - } - - /* we might wait for a moment till child is ready, so update screen now */ - if (update_cb) update_cb(); - return 0; + return true; } /* @@ -892,12 +995,12 @@ sync_pipe_open_command(char* const argv[], int *data_read_fd, * latter case, *msgp must be freed with g_free(). */ static int -sync_pipe_close_command(int *data_read_fd, int *message_read_fd, - ws_process_id *fork_child, gchar **msgp) +sync_pipe_close_command(int *data_read_fd, GIOChannel *message_read_io, + ws_process_id *fork_child, char **msgp) { ws_close(*data_read_fd); - if (message_read_fd != NULL) - ws_close(*message_read_fd); + if (message_read_io != NULL) + g_io_channel_unref(message_read_io); #ifdef _WIN32 /* XXX - Should we signal the child somehow? */ @@ -919,20 +1022,19 @@ sync_pipe_close_command(int *data_read_fd, int *message_read_fd, * NULL, and -1 is returned; *primary_msg, and *secondary_msg if not NULL, * must be freed with g_free(). */ -/* XXX - This duplicates a lot of code in sync_pipe_start() */ -/* XXX - assumes PIPE_BUF_SIZE > SP_MAX_MSG_LEN */ -#define PIPE_BUF_SIZE 5120 static int -sync_pipe_run_command_actual(char* const argv[], gchar **data, gchar **primary_msg, - gchar **secondary_msg, void(*update_cb)(void)) +sync_pipe_run_command_actual(char **argv, char **data, char **primary_msg, + char **secondary_msg, void(*update_cb)(void)) { - gchar *msg; - int data_pipe_read_fd, sync_pipe_read_fd, ret; + char *msg; + int data_pipe_read_fd, ret; + GIOChannel *sync_pipe_read_io; ws_process_id fork_child; char *wait_msg; - gchar buffer[PIPE_BUF_SIZE+1] = {0}; + char buffer[PIPE_BUF_SIZE+1] = {0}; ssize_t nread; char indicator; + int32_t exec_errno = 0; int primary_msg_len; char *primary_msg_text; int secondary_msg_len; @@ -941,8 +1043,8 @@ sync_pipe_run_command_actual(char* const argv[], gchar **data, gchar **primary_m GString *data_buf = NULL; ssize_t count; - ret = sync_pipe_open_command(argv, &data_pipe_read_fd, &sync_pipe_read_fd, - &fork_child, &msg, update_cb); + ret = sync_pipe_open_command(argv, &data_pipe_read_fd, &sync_pipe_read_io, NULL, + &fork_child, NULL, &msg, update_cb); if (ret == -1) { *primary_msg = msg; *secondary_msg = NULL; @@ -955,7 +1057,7 @@ sync_pipe_run_command_actual(char* const argv[], gchar **data, gchar **primary_m * * First, wait for an SP_ERROR_MSG message or SP_SUCCESS message. */ - nread = pipe_read_block(sync_pipe_read_fd, &indicator, SP_MAX_MSG_LEN, + 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 @@ -982,7 +1084,7 @@ sync_pipe_run_command_actual(char* const argv[], gchar **data, gchar **primary_m /* 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 = g_strdup_printf("%s\n\n%s", *primary_msg, wait_msg); + 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; @@ -997,6 +1099,39 @@ sync_pipe_run_command_actual(char* const argv[], gchar **data, gchar **primary_m /* 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); + } + + /* + * 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 { + /* + * 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: /* * Error from dumpcap; there will be a primary message and a @@ -1004,10 +1139,10 @@ sync_pipe_run_command_actual(char* const argv[], gchar **data, gchar **primary_m */ /* convert primary message */ - pipe_convert_header((guchar*)buffer, 4, &indicator, &primary_msg_len); + pipe_convert_header((unsigned char*)buffer, 4, &indicator, &primary_msg_len); primary_msg_text = buffer+4; /* convert secondary message */ - pipe_convert_header((guchar*)primary_msg_text + primary_msg_len, 4, &indicator, + 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 */ @@ -1015,7 +1150,7 @@ sync_pipe_run_command_actual(char* const argv[], gchar **data, gchar **primary_m /* * Pick up the child status. */ - ret = sync_pipe_close_command(&data_pipe_read_fd, &sync_pipe_read_fd, + ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, &fork_child, &msg); if (ret == -1) { /* @@ -1036,6 +1171,13 @@ sync_pipe_run_command_actual(char* const argv[], gchar **data, gchar **primary_m *data = NULL; break; + case SP_LOG_MSG: + /* + * Log from dumpcap; pass to our log + */ + sync_pipe_handle_log_msg(buffer); + break; + case SP_SUCCESS: /* read the output from the command */ data_buf = g_string_new(""); @@ -1047,7 +1189,7 @@ sync_pipe_run_command_actual(char* const argv[], gchar **data, gchar **primary_m /* * Pick up the child status. */ - ret = sync_pipe_close_command(&data_pipe_read_fd, &sync_pipe_read_fd, + ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, &fork_child, &msg); if (ret == -1) { /* @@ -1056,7 +1198,7 @@ sync_pipe_run_command_actual(char* const argv[], gchar **data, gchar **primary_m */ *primary_msg = msg; *secondary_msg = NULL; - g_string_free(data_buf, TRUE); + g_string_free(data_buf, true); *data = NULL; } else { /* @@ -1064,7 +1206,7 @@ sync_pipe_run_command_actual(char* const argv[], gchar **data, gchar **primary_m */ *primary_msg = NULL; *secondary_msg = NULL; - *data = g_string_free(data_buf, FALSE); + *data = g_string_free(data_buf, false); } break; @@ -1072,7 +1214,7 @@ sync_pipe_run_command_actual(char* const argv[], gchar **data, gchar **primary_m /* * Pick up the child status. */ - ret = sync_pipe_close_command(&data_pipe_read_fd, &sync_pipe_read_fd, + ret = sync_pipe_close_command(&data_pipe_read_fd, sync_pipe_read_io, &fork_child, &msg); if (ret == -1) { /* @@ -1085,7 +1227,7 @@ sync_pipe_run_command_actual(char* const argv[], gchar **data, gchar **primary_m /* * Child process returned an unknown status. */ - *primary_msg = g_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x", + *primary_msg = ws_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x", indicator); *secondary_msg = NULL; ret = -1; @@ -1100,11 +1242,11 @@ sync_pipe_run_command_actual(char* const argv[], gchar **data, gchar **primary_m * redirects to sync_pipe_run_command_actual() */ static int -sync_pipe_run_command(char* const argv[], gchar **data, gchar **primary_msg, - gchar **secondary_msg, void (*update_cb)(void)) +sync_pipe_run_command(char **argv, char **data, char **primary_msg, + char **secondary_msg, void (*update_cb)(void)) { int ret, i; - gint64 start_time; + int64_t start_time; double elapsed; int logging_enabled; @@ -1112,9 +1254,9 @@ sync_pipe_run_command(char* const argv[], gchar **data, gchar **primary_msg, logging_enabled = ws_log_msg_is_active(WS_LOG_DOMAIN, LOG_LEVEL_INFO); if (logging_enabled) { start_time = g_get_monotonic_time(); - ws_info("sync_pipe_run_command() starts"); + ws_debug("sync_pipe_run_command() starts"); for (i=0; argv[i] != 0; i++) { - ws_debug(" argv[%d]: %s", i, argv[i]); + ws_noisy(" argv[%d]: %s", i, argv[i]); } } /* do the actual sync pipe run command */ @@ -1123,7 +1265,7 @@ sync_pipe_run_command(char* const argv[], gchar **data, gchar **primary_msg, if (logging_enabled) { elapsed = (g_get_monotonic_time() - start_time) / 1e6; - ws_info("sync_pipe_run_command() ends, taking %.3fs, result=%d", elapsed, ret); + ws_debug("sync_pipe_run_command() ends, taking %.3fs, result=%d", elapsed, ret); } return ret; @@ -1131,14 +1273,14 @@ sync_pipe_run_command(char* const argv[], gchar **data, gchar **primary_msg, int -sync_interface_set_80211_chan(const gchar *iface, const char *freq, const gchar *type, - const gchar *center_freq1, const gchar *center_freq2, - gchar **data, gchar **primary_msg, - gchar **secondary_msg, void (*update_cb)(void)) +sync_interface_set_80211_chan(const char *iface, const char *freq, const char *type, + const char *center_freq1, const char *center_freq2, + char **data, char **primary_msg, + char **secondary_msg, void (*update_cb)(void)) { int argc, ret; char **argv; - gchar *opt; + char *opt; argv = init_pipe_args(&argc); @@ -1153,11 +1295,11 @@ sync_interface_set_80211_chan(const gchar *iface, const char *freq, const gchar argv = sync_pipe_add_arg(argv, &argc, iface); if (center_freq2) - opt = g_strdup_printf("%s,%s,%s,%s", freq, type, center_freq1, center_freq2); + opt = ws_strdup_printf("%s,%s,%s,%s", freq, type, center_freq1, center_freq2); else if (center_freq1) - opt = g_strdup_printf("%s,%s,%s", freq, type, center_freq1); + opt = ws_strdup_printf("%s,%s,%s", freq, type, center_freq1); else if (type) - opt = g_strdup_printf("%s,%s", freq, type); + opt = ws_strdup_printf("%s,%s", freq, type); else opt = g_strdup(freq); @@ -1165,22 +1307,14 @@ sync_interface_set_80211_chan(const gchar *iface, const char *freq, const gchar *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; } @@ -1197,8 +1331,8 @@ sync_interface_set_80211_chan(const gchar *iface, const char *freq, const gchar * must be freed with g_free(). */ int -sync_interface_list_open(gchar **data, gchar **primary_msg, - gchar **secondary_msg, void (*update_cb)(void)) +sync_interface_list_open(char **data, char **primary_msg, + char **secondary_msg, void (*update_cb)(void)) { int argc; char **argv; @@ -1218,13 +1352,7 @@ sync_interface_list_open(gchar **data, gchar **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; } @@ -1241,9 +1369,9 @@ sync_interface_list_open(gchar **data, gchar **primary_msg, * must be freed with g_free(). */ int -sync_if_capabilities_open(const gchar *ifname, gboolean monitor_mode, const gchar* auth, - gchar **data, gchar **primary_msg, - gchar **secondary_msg, void (*update_cb)(void)) +sync_if_capabilities_open(const char *ifname, bool monitor_mode, const char* auth, + char **data, char **primary_msg, + char **secondary_msg, void (*update_cb)(void)) { int argc; char **argv; @@ -1272,13 +1400,51 @@ sync_if_capabilities_open(const gchar *ifname, gboolean monitor_mode, const gcha 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; } @@ -1287,17 +1453,21 @@ sync_if_capabilities_open(const gchar *ifname, gboolean monitor_mode, const gcha * 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, gchar **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 message_read_fd, ret; + int ret; + GIOChannel *message_read_io; char *wait_msg; - gchar buffer[PIPE_BUF_SIZE+1] = {0}; + char buffer[PIPE_BUF_SIZE+1] = {0}; ssize_t nread; char indicator; + int32_t exec_errno = 0; int primary_msg_len; char *primary_msg_text; int secondary_msg_len; @@ -1316,18 +1486,24 @@ sync_interface_stats_open(int *data_read_fd, ws_process_id *fork_child, gchar ** /* 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 - create_dummy_signal_pipe(); + argv = sync_pipe_add_arg(argv, &argc, "--signal-pipe"); + ret = create_dummy_signal_pipe(msg); + if (ret == -1) { + 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_fd, - fork_child, msg, update_cb); - free_argv(argv, argc); + ret = sync_pipe_open_command(argv, data_read_fd, &message_read_io, NULL, + fork_child, NULL, msg, update_cb); if (ret == -1) { return -1; } @@ -1337,116 +1513,169 @@ sync_interface_stats_open(int *data_read_fd, ws_process_id *fork_child, gchar ** * * First, wait for an SP_ERROR_MSG message or SP_SUCCESS message. */ - nread = pipe_read_block(message_read_fd, &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); - ws_close(message_read_fd); - 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 = g_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. */ + 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; + } } + 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_ERROR_MSG: - /* - * Error from dumpcap; there will be a primary message and a - * secondary message. - */ - - /* convert primary message */ - pipe_convert_header((guchar*)buffer, 4, &indicator, &primary_msg_len); - primary_msg_text = buffer+4; - /* convert secondary message */ - pipe_convert_header((guchar*)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 */ + 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. - */ - ret = sync_pipe_close_command(data_read_fd, &message_read_fd, - fork_child, msg); - if (ret == -1) { /* - * Child process failed unexpectedly, or wait failed; msg is the - * error message. + * Pick up the child status. */ - } else { + char *close_msg = NULL; + sync_pipe_close_command(data_read_fd, message_read_io, + fork_child, &close_msg); /* - * Child process failed, but returned the expected exit status. - * Return the messages it gave us, and indicate failure. + * Ignore the error from sync_pipe_close_command, presumably the one + * returned by the child is more pertinent to what went wrong. */ - *msg = g_strdup(primary_msg_text); + g_free(close_msg); ret = -1; - } - break; + break; - case SP_SUCCESS: - /* Close the message pipe. */ - ws_close(message_read_fd); - 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 */ - default: - /* - * Pick up the child status. - */ - ret = sync_pipe_close_command(data_read_fd, &message_read_fd, - 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; + } + return ret; + + case SP_LOG_MSG: /* - * Child process returned an unknown status. + * Log from dumpcap; pass to our log */ - *msg = g_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x", - indicator); - ret = -1; + sync_pipe_handle_log_msg(buffer); + break; + + case SP_IFACE_LIST: + /* + * Dumpcap giving us the interface list + */ + + /* convert primary message */ + *data = g_strdup(buffer); + break; + + case SP_SUCCESS: + /* Close the message pipe. */ + g_io_channel_unref(message_read_io); + break; + + default: + /* + * 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. + */ + } 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); + return ret; } /* Close down the stats process */ int -sync_interface_stats_close(int *read_fd, ws_process_id *fork_child, gchar **msg) +sync_interface_stats_close(int *read_fd, ws_process_id *fork_child, char **msg) { -#ifndef _WIN32 +#ifdef _WIN32 + CloseHandle(dummy_signal_pipe); + dummy_signal_pipe = NULL; +#else /* * Don't bother waiting for the child. sync_pipe_close_command * does this for us on Windows. @@ -1459,30 +1688,28 @@ sync_interface_stats_close(int *read_fd, ws_process_id *fork_child, gchar **msg) /* read a number of bytes from a pipe */ /* (blocks until enough bytes read or an error occurs) */ static ssize_t -pipe_read_bytes(int pipe_fd, char *bytes, int required, char **msg) +pipe_read_bytes(GIOChannel *pipe_io, char *bytes, size_t required, char **msg) { - ssize_t newly; - ssize_t offset = 0; - int error; + GError *err = NULL; + size_t newly; + size_t offset = 0; while(required) { - newly = ws_read(pipe_fd, &bytes[offset], required); + g_io_channel_read_chars(pipe_io, &bytes[offset], required, &newly, &err); + if (err != NULL) { + ws_debug("read from pipe %p: error(%u): %s", pipe_io, err->code, err->message); + *msg = ws_strdup_printf("Error reading from sync pipe: %s", err->message); + g_clear_error(&err); + return -1; + } if (newly == 0) { /* EOF */ - ws_debug("read from pipe %d: EOF (capture closed?)", pipe_fd); + ws_debug("read from pipe %p: EOF (capture closed?)", pipe_io); *msg = 0; return offset; } - if (newly < 0) { - /* error */ - error = errno; - ws_debug("read from pipe %d: error(%u): %s", pipe_fd, error, g_strerror(error)); - *msg = g_strdup_printf("Error reading from sync pipe: %s", - g_strerror(error)); - return newly; - } - required -= (int)newly; + required -= newly; offset += newly; } @@ -1527,7 +1754,7 @@ sync_pipe_gets_nonblock(int pipe_fd, char *bytes, int max) { /* convert header values (indicator and 3-byte length) */ static void -pipe_convert_header(const guchar *header, int header_len _U_, char *indicator, int *block_len) { +pipe_convert_header(const unsigned char *header, int header_len _U_, char *indicator, int *block_len) { ws_assert(header_len == 4); @@ -1540,15 +1767,15 @@ pipe_convert_header(const guchar *header, int header_len _U_, char *indicator, i (1-byte message indicator, 3-byte message length (excluding length and indicator field), and the rest is the message) */ static ssize_t -pipe_read_block(int pipe_fd, char *indicator, int len, char *msg, +pipe_read_block(GIOChannel *pipe_io, char *indicator, int len, char *msg, char **err_msg) { int required; ssize_t newly; - gchar header[4]; + char header[4]; /* read header (indicator and 3-byte length) */ - newly = pipe_read_bytes(pipe_fd, header, 4, err_msg); + newly = pipe_read_bytes(pipe_io, header, 4, err_msg); if(newly != 4) { if (newly == 0) { /* @@ -1556,59 +1783,62 @@ pipe_read_block(int pipe_fd, char *indicator, int len, char *msg, * is an "I'm done" indication, so don't report it as an * error. */ - ws_debug("read %d got an EOF", pipe_fd); + ws_debug("read %p got an EOF", pipe_io); return 0; } - ws_debug("read %d failed to read header: %lu", pipe_fd, (long)newly); + ws_debug("read %p failed to read header: %lu", pipe_io, (long)newly); if (newly != -1) { /* * Short read, but not an immediate EOF. */ - *err_msg = g_strdup_printf("Premature EOF reading from sync pipe: got only %ld bytes", + *err_msg = ws_strdup_printf("Premature EOF reading from sync pipe: got only %ld bytes", (long)newly); } return -1; } /* convert header values */ - pipe_convert_header((guchar*)header, 4, indicator, &required); + pipe_convert_header((unsigned char*)header, 4, indicator, &required); /* only indicator with no value? */ if(required == 0) { - ws_debug("read %d indicator: %c empty value", pipe_fd, *indicator); + ws_debug("read %p indicator: %c empty value", pipe_io, *indicator); return 4; } /* does the data fit into the given buffer? */ if(required > len) { - ws_debug("read %d length error, required %d > len %d, header: 0x%02x 0x%02x 0x%02x 0x%02x", - pipe_fd, required, len, + size_t bytes_read; + GError *err = NULL; + ws_debug("read %p length error, required %d > len %d, header: 0x%02x 0x%02x 0x%02x 0x%02x", + pipe_io, required, len, 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)); - newly = ws_read(pipe_fd, &msg[sizeof(header)], len-sizeof(header)); - if (newly < 0) { /* error */ - ws_debug("read from pipe %d: error(%u): %s", pipe_fd, errno, g_strerror(errno)); + g_io_channel_read_chars(pipe_io, &msg[sizeof(header)], len-sizeof(header), &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 = g_strdup_printf("Unknown message from dumpcap reading header, try to show it as a string: %s", + *err_msg = ws_strdup_printf("Unknown message from dumpcap reading header, try to show it as a string: %s", msg); return -1; } len = required; /* read the actual block data */ - newly = pipe_read_bytes(pipe_fd, msg, required, err_msg); + newly = pipe_read_bytes(pipe_io, msg, required, err_msg); if(newly != required) { if (newly != -1) { - *err_msg = g_strdup_printf("Unknown message from dumpcap reading data, try to show it as a string: %s", + *err_msg = ws_strdup_printf("Unknown message from dumpcap reading data, try to show it as a string: %s", msg); } return -1; } /* XXX If message is "2part", the msg probably won't be sent to debug log correctly */ - ws_debug("read %d ok indicator: %c len: %u msg: %s", pipe_fd, *indicator, len, msg); + ws_debug("read %p ok indicator: %c len: %u msg: %s", pipe_io, *indicator, len, msg); *err_msg = NULL; return newly + 4; } @@ -1618,21 +1848,21 @@ pipe_read_block(int pipe_fd, char *indicator, int len, char *msg, us a message, or the sync pipe has closed, meaning the child has closed it (perhaps because it exited). */ static gboolean -sync_pipe_input_cb(gint source, gpointer user_data) +sync_pipe_input_cb(GIOChannel *pipe_io, capture_session *cap_session) { - capture_session *cap_session = (capture_session *)user_data; int ret; char buffer[SP_MAX_MSG_LEN+1] = {0}; ssize_t nread; char indicator; + int32_t exec_errno = 0; int primary_len; char *primary_msg; int secondary_len; char *secondary_msg; char *wait_msg, *combined_msg; - guint32 npackets = 0; + uint32_t npackets = 0; - nread = pipe_read_block(source, &indicator, SP_MAX_MSG_LEN, buffer, + nread = pipe_read_block(pipe_io, &indicator, SP_MAX_MSG_LEN, buffer, &primary_msg); if(nread <= 0) { /* We got a read error, or a bad message, or an EOF, from the sync pipe. @@ -1660,7 +1890,7 @@ sync_pipe_input_cb(gint source, gpointer user_data) /* 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 = g_strdup_printf("%s\n\n%s", primary_msg, wait_msg); + 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; @@ -1674,11 +1904,13 @@ sync_pipe_input_cb(gint source, gpointer user_data) #ifdef _WIN32 ws_close(cap_session->signal_pipe_write_fd); #endif - ws_debug("cleaning extcap pipe"); - extcap_if_cleanup(cap_session->capture_opts, &primary_msg); - cap_session->closed(cap_session, primary_msg); - g_free(primary_msg); - return FALSE; + cap_session->capture_opts->closed_msg = primary_msg; + if (extcap_session_stop(cap_session)) { + capture_process_finished(cap_session); + } else { + extcap_request_stop(cap_session); + } + return false; } /* we got a valid message block from the child, process it */ @@ -1688,8 +1920,7 @@ sync_pipe_input_cb(gint source, gpointer user_data) ws_debug("file failed, closing capture"); /* We weren't able to open the new capture file; user has been - alerted. Close the sync pipe. */ - ws_close(source); + alerted. The sync pipe will close after we return false. */ /* The child has sent us a filename which we couldn't open. @@ -1705,7 +1936,7 @@ sync_pipe_input_cb(gint source, gpointer user_data) "standard output", as the capture file. */ sync_pipe_stop(cap_session); cap_session->closed(cap_session, NULL); - return FALSE; + return false; } break; case SP_PACKET_COUNT: @@ -1716,22 +1947,41 @@ sync_pipe_input_cb(gint source, gpointer user_data) cap_session->count += npackets; cap_session->new_packets(cap_session, npackets); break; + 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); + } + primary_msg = ws_strdup_printf("Couldn't run dumpcap in child process: %s", + g_strerror(exec_errno)); + cap_session->error(cap_session, primary_msg, NULL); + /* 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_ERROR_MSG: /* convert primary message */ - pipe_convert_header((guchar*)buffer, 4, &indicator, &primary_len); + pipe_convert_header((unsigned char*)buffer, 4, &indicator, &primary_len); primary_msg = buffer+4; /* convert secondary message */ - pipe_convert_header((guchar*)primary_msg + primary_len, 4, &indicator, &secondary_len); + pipe_convert_header((unsigned char*)primary_msg + primary_len, 4, &indicator, &secondary_len); secondary_msg = primary_msg + primary_len + 4; /* message output */ cap_session->error(cap_session, primary_msg, secondary_msg); /* 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; - guint32 indx = 0; - const gchar* end; + uint32_t indx = 0; + const char* end; if (ws_strtou32(buffer, &end, &indx) && end[0] == ':') { message = end + 1; @@ -1743,8 +1993,8 @@ sync_pipe_input_cb(gint source, gpointer user_data) } case SP_DROPS: { const char *name = NULL; - const gchar* end; - guint32 num = 0; + const char* end; + uint32_t num = 0; if (ws_strtou32(buffer, &end, &num) && end[0] == ':') { name = end + 1; @@ -1754,10 +2004,14 @@ sync_pipe_input_cb(gint source, gpointer user_data) break; } default: - ws_assert_not_reached(); + if (g_ascii_isprint(indicator)) + ws_warning("Unknown indicator '%c'", indicator); + else + ws_warning("Unknown indicator '\\x%02x", indicator); + break; } - return TRUE; + return true; } @@ -1771,14 +2025,14 @@ sync_pipe_input_cb(gint source, gpointer user_data) * must be freed with g_free(). */ static int -sync_pipe_wait_for_child(ws_process_id fork_child, gchar **msgp) +sync_pipe_wait_for_child(ws_process_id fork_child, char **msgp) { int fork_child_status; #ifndef _WIN32 int retry_waitpid = 3; #endif int ret = -1; - gint64 start_time; + int64_t start_time; double elapsed; start_time = g_get_monotonic_time(); @@ -1789,7 +2043,7 @@ sync_pipe_wait_for_child(ws_process_id fork_child, gchar **msgp) *msgp = NULL; /* assume no error */ #ifdef _WIN32 if (_cwait(&fork_child_status, (intptr_t) fork_child, _WAIT_CHILD) == -1) { - *msgp = g_strdup_printf("Error from cwait(): %s", g_strerror(errno)); + *msgp = ws_strdup_printf("Error from cwait(): %s", g_strerror(errno)); ret = -1; } else { /* @@ -1799,7 +2053,7 @@ sync_pipe_wait_for_child(ws_process_id fork_child, gchar **msgp) ret = fork_child_status; if ((fork_child_status & 0xC0000000) == ERROR_SEVERITY_ERROR) { /* Probably an exception code */ - *msgp = g_strdup_printf("Child dumpcap process died: %s", + *msgp = ws_strdup_printf("Child dumpcap process died: %s", win32strexception(fork_child_status)); ret = -1; } @@ -1816,19 +2070,19 @@ sync_pipe_wait_for_child(ws_process_id fork_child, gchar **msgp) ret = WEXITSTATUS(fork_child_status); } else if (WIFSTOPPED(fork_child_status)) { /* It stopped, rather than exiting. "Should not happen." */ - *msgp = g_strdup_printf("Child dumpcap process stopped: %s", + *msgp = ws_strdup_printf("Child dumpcap process stopped: %s", sync_pipe_signame(WSTOPSIG(fork_child_status))); ret = -1; } else if (WIFSIGNALED(fork_child_status)) { /* It died with a signal. */ - *msgp = g_strdup_printf("Child dumpcap process died: %s%s", + *msgp = ws_strdup_printf("Child dumpcap process died: %s%s", sync_pipe_signame(WTERMSIG(fork_child_status)), WCOREDUMP(fork_child_status) ? " - core dumped" : ""); ret = -1; } else { /* What? It had to either have exited, or stopped, or died with a signal; what happened here? */ - *msgp = g_strdup_printf("Bad status from waitpid(): %#o", + *msgp = ws_strdup_printf("Bad status from waitpid(): %#o", fork_child_status); ret = -1; } @@ -1863,7 +2117,7 @@ sync_pipe_wait_for_child(ws_process_id fork_child, gchar **msgp) ret = fetch_dumpcap_pid ? 0 : -1; } else { /* Unknown error. */ - *msgp = g_strdup_printf("Error from waitpid(): %s", g_strerror(errno)); + *msgp = ws_strdup_printf("Error from waitpid(): %s", g_strerror(errno)); ret = -1; } } @@ -1965,7 +2219,7 @@ sync_pipe_signame(int sig) default: /* Returning a static buffer is ok in the context we use it here */ - g_snprintf(sigmsg_buf, sizeof sigmsg_buf, "Signal %d", sig); + snprintf(sigmsg_buf, sizeof sigmsg_buf, "Signal %d", sig); sigmsg = sigmsg_buf; break; } @@ -1976,20 +2230,26 @@ sync_pipe_signame(int sig) #ifdef _WIN32 -static void create_dummy_signal_pipe() { - gchar *dummy_signal_pipe_name; +static int create_dummy_signal_pipe(char **msg) { + char *dummy_signal_pipe_name; - if (dummy_signal_pipe != NULL) return; + if (dummy_signal_pipe != NULL) return 0; if (!dummy_control_id) { - dummy_control_id = g_strdup_printf("%d.dummy", GetCurrentProcessId()); + dummy_control_id = ws_strdup_printf("%ld.dummy", GetCurrentProcessId()); } /* Create the signal pipe */ - dummy_signal_pipe_name = g_strdup_printf(SIGNAL_PIPE_FORMAT, dummy_control_id); + dummy_signal_pipe_name = ws_strdup_printf(SIGNAL_PIPE_FORMAT, dummy_control_id); dummy_signal_pipe = CreateNamedPipe(utf_8to16(dummy_signal_pipe_name), PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE, 1, 65535, 65535, 0, NULL); g_free(dummy_signal_pipe_name); + if (dummy_signal_pipe == INVALID_HANDLE_VALUE) { + *msg = ws_strdup_printf("Couldn't create signal pipe: %s", + win32strerror(GetLastError())); + return -1; + } + return 0; } /* tell the child through the signal pipe that we want to quit the capture */ @@ -2003,10 +2263,10 @@ signal_pipe_capquit_to_child(capture_session *cap_session) /* it doesn't matter *what* we send here, the first byte will stop the capture */ /* simply sending a "QUIT" string */ - /*pipe_write_block(cap_session->signal_pipe_write_fd, SP_QUIT, quit_msg);*/ + /*sync_pipe_write_string_msg(cap_session->signal_pipe_write_fd, SP_QUIT, quit_msg);*/ ret = ws_write(cap_session->signal_pipe_write_fd, quit_msg, sizeof quit_msg); if(ret == -1) { - ws_warning("%d header: error %s", cap_session->signal_pipe_write_fd, g_strerror(errno)); + ws_warning("%d header: error %s", cap_session->signal_pipe_write_fd, win32strerror(GetLastError())); } } #endif @@ -2016,11 +2276,6 @@ signal_pipe_capquit_to_child(capture_session *cap_session) void sync_pipe_stop(capture_session *cap_session) { -#ifdef _WIN32 - int count; - DWORD childstatus; - gboolean terminate = TRUE; -#endif if (cap_session->fork_child != WS_INVALID_PID) { #ifndef _WIN32 /* send the SIGINT signal to close the capture child gracefully. */ @@ -2030,24 +2285,18 @@ sync_pipe_stop(capture_session *cap_session) } #else #define STOP_SLEEP_TIME 500 /* ms */ -#define STOP_CHECK_TIME 50 + DWORD status; + /* First, use the special signal pipe to try to close the capture child * gracefully. */ signal_pipe_capquit_to_child(cap_session); /* Next, wait for the process to exit on its own */ - for (count = 0; count < STOP_SLEEP_TIME / STOP_CHECK_TIME; count++) { - if (GetExitCodeProcess((HANDLE) cap_session->fork_child, &childstatus) && - childstatus != STILL_ACTIVE) { - terminate = FALSE; - break; - } - Sleep(STOP_CHECK_TIME); - } + status = WaitForSingleObject((HANDLE) cap_session->fork_child, STOP_SLEEP_TIME); /* Force the issue. */ - if (terminate) { + if (status != WAIT_OBJECT_0) { ws_warning("sync_pipe_stop: forcing child to exit"); sync_pipe_kill(cap_session->fork_child); } |