diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a4824a601..406b34e388 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1577,7 +1577,6 @@ set(SHARK_COMMON_SRC version_info.c extcap.c extcap_parser.c - extcap_spawn.c ) set(TSHARK_TAP_SRC diff --git a/Makefile.am b/Makefile.am index f93cad4c15..7d9ca8de47 100644 --- a/Makefile.am +++ b/Makefile.am @@ -380,8 +380,7 @@ SHARK_COMMON_SRC = \ frame_tvbuff.c \ sync_pipe_write.c \ extcap.c \ - extcap_parser.c \ - extcap_spawn.c + extcap_parser.c # wireshark specifics WIRESHARK_COMMON_SRC = \ @@ -722,7 +721,6 @@ noinst_HEADERS = \ conditions.h \ extcap.h \ extcap_parser.h \ - extcap_spawn.h \ fileset.h \ frame_tvbuff.h \ ringbuffer.h \ diff --git a/capture_opts.c b/capture_opts.c index 5d6e84d641..a8bddee36f 100644 --- a/capture_opts.c +++ b/capture_opts.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "caputils/capture_ifinfo.h" #include "caputils/capture-pcap-util.h" @@ -53,7 +54,7 @@ capture_opts_init(capture_options *capture_opts) capture_opts->default_options.extcap = NULL; capture_opts->default_options.extcap_fifo = NULL; capture_opts->default_options.extcap_args = NULL; - capture_opts->default_options.extcap_userdata = NULL; + capture_opts->default_options.extcap_pipedata = NULL; capture_opts->default_options.extcap_pid = INVALID_EXTCAP_PID; #ifdef _WIN32 capture_opts->default_options.extcap_pipe_h = INVALID_HANDLE_VALUE; @@ -684,7 +685,7 @@ capture_opts_add_iface_opt(capture_options *capture_opts, const char *optarg_str interface_opts.extcap_fifo = g_strdup(capture_opts->default_options.extcap_fifo); interface_opts.extcap_args = NULL; interface_opts.extcap_pid = INVALID_EXTCAP_PID; - interface_opts.extcap_userdata = NULL; + interface_opts.extcap_pipedata = NULL; #ifdef _WIN32 interface_opts.extcap_pipe_h = INVALID_HANDLE_VALUE; interface_opts.extcap_control_in_h = INVALID_HANDLE_VALUE; @@ -1127,7 +1128,7 @@ capture_opts_del_iface(capture_options *capture_opts, guint if_index) g_hash_table_unref(interface_opts->extcap_args); if (interface_opts->extcap_pid != INVALID_EXTCAP_PID) g_spawn_close_pid(interface_opts->extcap_pid); - g_free(interface_opts->extcap_userdata); + g_free(interface_opts->extcap_pipedata); g_free(interface_opts->extcap_control_in); g_free(interface_opts->extcap_control_out); #ifdef HAVE_PCAP_REMOTE @@ -1174,12 +1175,12 @@ collect_ifaces(capture_options *capture_opts) interface_opts.if_type = device->if_info.type; interface_opts.extcap = g_strdup(device->if_info.extcap); interface_opts.extcap_fifo = NULL; - interface_opts.extcap_userdata = NULL; + interface_opts.extcap_pipedata = NULL; interface_opts.extcap_args = device->external_cap_args_settings; interface_opts.extcap_pid = INVALID_EXTCAP_PID; if (interface_opts.extcap_args) g_hash_table_ref(interface_opts.extcap_args); - interface_opts.extcap_userdata = NULL; + interface_opts.extcap_pipedata = NULL; #ifdef _WIN32 interface_opts.extcap_pipe_h = INVALID_HANDLE_VALUE; interface_opts.extcap_control_in_h = INVALID_HANDLE_VALUE; diff --git a/capture_opts.h b/capture_opts.h index 715a3bee90..7c344e31a7 100644 --- a/capture_opts.h +++ b/capture_opts.h @@ -198,12 +198,6 @@ typedef struct link_row_tag { gint dlt; } link_row; -#ifdef _WIN32 -#define INVALID_EXTCAP_PID INVALID_HANDLE_VALUE -#else -#define INVALID_EXTCAP_PID (GPid)-1 -#endif - typedef struct interface_options_tag { gchar *name; /* the name of the interface provided to winpcap/libpcap to specify the interface */ gchar *descr; @@ -218,7 +212,7 @@ typedef struct interface_options_tag { gchar *extcap_fifo; GHashTable *extcap_args; GPid extcap_pid; /* pid of running process or INVALID_EXTCAP_PID */ - gpointer extcap_userdata; + gpointer extcap_pipedata; guint extcap_child_watch; #ifdef _WIN32 HANDLE extcap_pipe_h; diff --git a/extcap.c b/extcap.c index 25932affd7..d83fd9c27e 100644 --- a/extcap.c +++ b/extcap.c @@ -49,7 +49,6 @@ #include "extcap.h" #include "extcap_parser.h" -#include "extcap_spawn.h" #ifdef _WIN32 static HANDLE pipe_h = INVALID_HANDLE_VALUE; @@ -319,7 +318,7 @@ static gboolean extcap_foreach(gint argc, gchar **args, continue; } - if (extcap_spawn_sync((gchar *) dirname, extcap_path->str, argc, args, &command_output)) + if (ws_pipe_spawn_sync((gchar *) dirname, extcap_path->str, argc, args, &command_output)) { cb_info.output = command_output; cb_info.extcap = extcap_path->str; @@ -957,7 +956,7 @@ extcap_has_toolbar(const char *ifname) void extcap_if_cleanup(capture_options *capture_opts, gchar **errormsg) { interface_options *interface_opts; - extcap_userdata *userdata; + ws_pipe_t *pipedata; guint icnt = 0; gboolean overwrite_exitcode; gchar *buffer; @@ -1031,17 +1030,17 @@ void extcap_if_cleanup(capture_options *capture_opts, gchar **errormsg) "Extcap [%s] - Closing spawned PID: %d", interface_opts->name, interface_opts->extcap_pid); - userdata = (extcap_userdata *) interface_opts->extcap_userdata; - if (userdata) + pipedata = (ws_pipe_t *) interface_opts->extcap_pipedata; + if (pipedata) { - if (userdata->extcap_stderr_rd > 0 && ws_pipe_data_available(userdata->extcap_stderr_rd)) + if (pipedata->stderr_fd > 0 && ws_pipe_data_available(pipedata->stderr_fd)) { buffer = (gchar *)g_malloc0(STDERR_BUFFER_SIZE + 1); - ws_read_string_from_pipe(ws_get_pipe_handle(userdata->extcap_stderr_rd), buffer, STDERR_BUFFER_SIZE + 1); + ws_read_string_from_pipe(ws_get_pipe_handle(pipedata->stderr_fd), buffer, STDERR_BUFFER_SIZE + 1); if (strlen(buffer) > 0) { - userdata->extcap_stderr = g_strdup_printf("%s", buffer); - userdata->exitcode = 1; + pipedata->stderr_msg = g_strdup_printf("%s", buffer); + pipedata->exitcode = 1; } g_free(buffer); } @@ -1050,37 +1049,37 @@ void extcap_if_cleanup(capture_options *capture_opts, gchar **errormsg) /* Final child watch may not have been called */ if (interface_opts->extcap_child_watch != 0) { - extcap_child_watch_cb(userdata->pid, 0, capture_opts); + extcap_child_watch_cb(pipedata->pid, 0, capture_opts); /* it will have changed in extcap_child_watch_cb */ interface_opts = &g_array_index(capture_opts->ifaces, interface_options, icnt); } #endif - if (userdata->extcap_stderr != NULL) + if (pipedata->stderr_msg != NULL) { overwrite_exitcode = TRUE; } - if (overwrite_exitcode || userdata->exitcode != 0) + if (overwrite_exitcode || pipedata->exitcode != 0) { - if (userdata->extcap_stderr != 0) + if (pipedata->stderr_msg != 0) { if (*errormsg == NULL) { - *errormsg = g_strdup_printf("Error by extcap pipe: %s", userdata->extcap_stderr); + *errormsg = g_strdup_printf("Error by extcap pipe: %s", pipedata->stderr_msg); } else { - gchar *temp = g_strconcat(*errormsg, "\nError by extcap pipe: " , userdata->extcap_stderr, NULL); + gchar *temp = g_strconcat(*errormsg, "\nError by extcap pipe: " , pipedata->stderr_msg, NULL); g_free(*errormsg); *errormsg = temp; } - g_free(userdata->extcap_stderr); + g_free(pipedata->stderr_msg); } - userdata->extcap_stderr = NULL; - userdata->exitcode = 0; + pipedata->stderr_msg = NULL; + pipedata->exitcode = 0; } } @@ -1098,8 +1097,8 @@ void extcap_if_cleanup(capture_options *capture_opts, gchar **errormsg) g_spawn_close_pid(interface_opts->extcap_pid); interface_opts->extcap_pid = INVALID_EXTCAP_PID; - g_free(interface_opts->extcap_userdata); - interface_opts->extcap_userdata = NULL; + g_free(interface_opts->extcap_pipedata); + interface_opts->extcap_pipedata = NULL; } } } @@ -1128,7 +1127,7 @@ void extcap_child_watch_cb(GPid pid, gint status, gpointer user_data) { guint i; interface_options *interface_opts; - extcap_userdata *userdata = NULL; + ws_pipe_t *pipedata = NULL; capture_options *capture_opts = (capture_options *)(user_data); if (capture_opts == NULL || capture_opts->ifaces == NULL || capture_opts->ifaces->len == 0) @@ -1145,32 +1144,32 @@ void extcap_child_watch_cb(GPid pid, gint status, gpointer user_data) interface_opts = &g_array_index(capture_opts->ifaces, interface_options, i); if (interface_opts->extcap_pid == pid) { - userdata = (extcap_userdata *)interface_opts->extcap_userdata; - if (userdata != NULL) + pipedata = (ws_pipe_t *)interface_opts->extcap_pipedata; + if (pipedata != NULL) { interface_opts->extcap_pid = INVALID_EXTCAP_PID; - userdata->exitcode = 0; + pipedata->exitcode = 0; #ifndef _WIN32 if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) { - userdata->exitcode = WEXITSTATUS(status); + pipedata->exitcode = WEXITSTATUS(status); } } else { - userdata->exitcode = G_SPAWN_ERROR_FAILED; + pipedata->exitcode = G_SPAWN_ERROR_FAILED; } #else if (status != 0) { - userdata->exitcode = status; + pipedata->exitcode = status; } #endif - if (status == 0 && userdata->extcap_stderr != NULL) + if (status == 0 && pipedata->stderr_msg != NULL) { - userdata->exitcode = 1; + pipedata->exitcode = 1; } } g_source_remove(interface_opts->extcap_child_watch); @@ -1288,7 +1287,7 @@ extcap_init_interfaces(capture_options *capture_opts) { guint i; interface_options *interface_opts; - extcap_userdata *userdata; + ws_pipe_t *pipedata; for (i = 0; i < capture_opts->ifaces->len; i++) { @@ -1331,16 +1330,16 @@ extcap_init_interfaces(capture_options *capture_opts) /* Create extcap call */ args = extcap_prepare_arguments(interface_opts); - userdata = g_new0(extcap_userdata, 1); + pipedata = g_new0(ws_pipe_t, 1); - pid = extcap_spawn_async(userdata, args); + pid = ws_pipe_spawn_async(pipedata, args); g_ptr_array_foreach(args, (GFunc)g_free, NULL); g_ptr_array_free(args, TRUE); if (pid == INVALID_EXTCAP_PID) { - g_free(userdata); + g_free(pipedata); continue; } @@ -1356,8 +1355,6 @@ extcap_init_interfaces(capture_options *capture_opts) * connect to named pipe (including user interaction). * Wait on multiple object in case of extcap termination * without opening pipe. - * - * Minimum supported version of Windows: XP / Server 2003. */ if (pid != INVALID_EXTCAP_PID) { @@ -1372,11 +1369,11 @@ extcap_init_interfaces(capture_options *capture_opts) num_pipe_handles += 2; } - extcap_wait_for_pipe(pipe_handles, num_pipe_handles, pid); + ws_pipe_wait_for_pipe(pipe_handles, num_pipe_handles, pid); } #endif - interface_opts->extcap_userdata = (gpointer) userdata; + interface_opts->extcap_pipedata = (gpointer) pipedata; } return TRUE; diff --git a/extcap_spawn.c b/extcap_spawn.c deleted file mode 100644 index 816e0e3a5f..0000000000 --- a/extcap_spawn.c +++ /dev/null @@ -1,381 +0,0 @@ -/* extcap_spawn.c - * - * Routines to spawn extcap external capture programs - * Copyright 2016, Roland Knall - * - * Wireshark - Network traffic analyzer - * By Gerald Combs - * Copyright 1998 Gerald Combs - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#include - -#include -#include -#include - -#include -#include -#include -#ifdef _WIN32 -#include -#endif - -#include - -#include "extcap.h" -#include "extcap_spawn.h" - -gboolean extcap_spawn_sync(gchar *dirname, gchar *command, gint argc, gchar **args, gchar **command_output) -{ - gboolean status = FALSE; - gboolean result = FALSE; - gchar **argv = NULL; - gint cnt = 0; - gchar *local_output = NULL; -#ifdef _WIN32 - -#define BUFFER_SIZE 16384 - - GString *winargs = g_string_sized_new(200); - gchar *quoted_arg; - gunichar2 *wcommandline; - - STARTUPINFO info; - PROCESS_INFORMATION processInfo; - - SECURITY_ATTRIBUTES sa; - HANDLE child_stdout_rd = NULL; - HANDLE child_stdout_wr = NULL; - HANDLE child_stderr_rd = NULL; - HANDLE child_stderr_wr = NULL; - - const gchar *oldpath = g_getenv("PATH"); - gchar *newpath = NULL; -#else - gint exit_status = 0; -#endif - - argv = (gchar **) g_malloc0(sizeof(gchar *) * (argc + 2)); - -#ifdef _WIN32 - newpath = g_strdup_printf("%s;%s", g_strescape(get_progfile_dir(), NULL), oldpath); - g_setenv("PATH", newpath, TRUE); - - argv[0] = g_strescape(command, NULL); -#else - argv[0] = g_strdup(command); -#endif - - for (cnt = 0; cnt < argc; cnt++) - argv[cnt + 1] = args[cnt]; - argv[argc + 1] = NULL; - -#ifdef _WIN32 - - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.bInheritHandle = TRUE; - sa.lpSecurityDescriptor = NULL; - - if (!CreatePipe(&child_stdout_rd, &child_stdout_wr, &sa, 0)) - { - g_free(argv[0]); - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stdout handle"); - return FALSE; - } - - if (!CreatePipe(&child_stderr_rd, &child_stderr_wr, &sa, 0)) - { - CloseHandle(child_stdout_rd); - CloseHandle(child_stdout_wr); - g_free(argv[0]); - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stderr handle"); - return FALSE; - } - - /* 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 (cnt = 0; argv[cnt] != 0; cnt++) { - if (cnt != 0) g_string_append_c(winargs, ' '); /* don't prepend a space before the path!!! */ - quoted_arg = protect_arg(argv[cnt]); - g_string_append(winargs, quoted_arg); - g_free(quoted_arg); - } - - wcommandline = g_utf8_to_utf16(winargs->str, (glong)winargs->len, NULL, NULL, NULL); - - memset(&processInfo, 0, sizeof(PROCESS_INFORMATION)); - memset(&info, 0, sizeof(STARTUPINFO)); - - info.cb = sizeof(STARTUPINFO); - info.hStdError = child_stderr_wr; - info.hStdOutput = child_stdout_wr; - info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - info.wShowWindow = SW_HIDE; - - if (CreateProcess(NULL, wcommandline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo)) - { - gchar* buffer; - - WaitForSingleObject(processInfo.hProcess, INFINITE); - buffer = (gchar*)g_malloc(BUFFER_SIZE); - status = ws_read_string_from_pipe(child_stdout_rd, buffer, BUFFER_SIZE); - if (status) - { - local_output = g_strdup_printf("%s", buffer); - } - g_free(buffer); - - CloseHandle(child_stdout_rd); - CloseHandle(child_stdout_wr); - CloseHandle(child_stderr_rd); - CloseHandle(child_stderr_wr); - - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); - } - else - status = FALSE; - - g_setenv("PATH", oldpath, TRUE); -#else - - status = g_spawn_sync(dirname, argv, NULL, - (GSpawnFlags) 0, NULL, NULL, &local_output, NULL, &exit_status, NULL); - - if (status && exit_status != 0) - status = FALSE; -#endif - - if (status) - { - if (command_output != NULL && local_output != NULL) - *command_output = g_strdup(local_output); - - result = TRUE; - } - - g_free(local_output); - g_free(argv[0]); - g_free(argv); - - return result; -} - -GPid extcap_spawn_async(extcap_userdata *userdata, GPtrArray *args) -{ - GPid pid = INVALID_EXTCAP_PID; - -#ifdef _WIN32 - gint cnt = 0; - gchar **tmp = NULL; - - GString *winargs = g_string_sized_new(200); - gchar *quoted_arg; - gunichar2 *wcommandline; - - STARTUPINFO info; - PROCESS_INFORMATION processInfo; - - SECURITY_ATTRIBUTES sa; - HANDLE child_stdout_rd = NULL; - HANDLE child_stdout_wr = NULL; - HANDLE child_stderr_rd = NULL; - HANDLE child_stderr_wr = NULL; - - const gchar *oldpath = g_getenv("PATH"); - gchar *newpath = NULL; - - newpath = g_strdup_printf("%s;%s", g_strescape(get_progfile_dir(), NULL), oldpath); - g_setenv("PATH", newpath, TRUE); - - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.bInheritHandle = TRUE; - sa.lpSecurityDescriptor = NULL; - - if (!CreatePipe(&child_stdout_rd, &child_stdout_wr, &sa, 0)) - { - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stdout handle"); - return FALSE; - } - - if (!CreatePipe(&child_stderr_rd, &child_stderr_wr, &sa, 0)) - { - CloseHandle(child_stdout_rd); - CloseHandle(child_stdout_wr); - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stderr handle"); - return FALSE; - } - - /* 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 (tmp = (gchar **)args->pdata, cnt = 0; *tmp && **tmp; ++cnt, ++tmp) { - if (cnt != 0) g_string_append_c(winargs, ' '); /* don't prepend a space before the path!!! */ - quoted_arg = protect_arg(*tmp); - g_string_append(winargs, quoted_arg); - g_free(quoted_arg); - } - - wcommandline = g_utf8_to_utf16(winargs->str, (glong)winargs->len, NULL, NULL, NULL); - - memset(&processInfo, 0, sizeof(PROCESS_INFORMATION)); - memset(&info, 0, sizeof(STARTUPINFO)); - - info.cb = sizeof(STARTUPINFO); - info.hStdError = child_stderr_wr; - info.hStdOutput = child_stdout_wr; - info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - info.wShowWindow = SW_HIDE; - - if (CreateProcess(NULL, wcommandline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo)) - { - userdata->extcap_stderr_rd = _open_osfhandle((intptr_t)(child_stderr_rd), _O_BINARY); - userdata->extcap_stdout_rd = _open_osfhandle((intptr_t)(child_stdout_rd), _O_BINARY); - userdata->threadId = processInfo.hThread; - pid = processInfo.hProcess; - } - - g_setenv("PATH", oldpath, TRUE); -#else - g_spawn_async_with_pipes(NULL, (gchar **)args->pdata, NULL, - (GSpawnFlags) G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, - &pid, NULL, &userdata->extcap_stdout_rd, &userdata->extcap_stderr_rd, NULL); -#endif - - userdata->pid = pid; - - return pid; -} - -#ifdef _WIN32 - -typedef struct -{ - HANDLE pipeHandle; - OVERLAPPED ol; - BOOL pendingIO; -} PIPEINTS; - -gboolean -extcap_wait_for_pipe(HANDLE * pipe_handles, int num_pipe_handles, HANDLE pid) -{ - PIPEINTS pipeinsts[3]; - DWORD dw, cbRet; - HANDLE handles[4]; - int error_code; - int num_waiting_to_connect = 0; - int num_handles = num_pipe_handles + 1; // PID handle is also added to list of handles. - - if (num_pipe_handles == 0 || num_pipe_handles > 3) - { - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Invalid number of pipes given as argument."); - return FALSE; - } - - for (int i = 0; i < num_pipe_handles; ++i) - { - pipeinsts[i].pipeHandle = pipe_handles[i]; - pipeinsts[i].ol.Pointer = 0; - pipeinsts[i].ol.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - pipeinsts[i].pendingIO = FALSE; - handles[i] = pipeinsts[i].ol.hEvent; - BOOL connected = ConnectNamedPipe(pipeinsts[i].pipeHandle, &pipeinsts[i].ol); - if (connected) - { - error_code = GetLastError(); - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "ConnectNamedPipe failed with %d \n.", error_code); - return FALSE; - } - - switch (GetLastError()) - { - case ERROR_IO_PENDING: - num_waiting_to_connect++; - pipeinsts[i].pendingIO = TRUE; - break; - - case ERROR_PIPE_CONNECTED: - if (SetEvent(pipeinsts[i].ol.hEvent)) - { - break; - } // Fallthrough if this fails. - - default: - error_code = GetLastError(); - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "ConnectNamedPipe failed with %d \n.", error_code); - return FALSE; - } - } - - // Store pid of extcap process so it can be monitored in case it fails before the pipes has connceted. - handles[num_pipe_handles] = pid; - - while(num_waiting_to_connect > 0) - { - dw = WaitForMultipleObjects(num_handles, handles, FALSE, 30000); - int idx = dw - WAIT_OBJECT_0; - if (dw == WAIT_TIMEOUT) - { - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap didn't connect to pipe within 30 seconds."); - return FALSE; - } - // If index points to our handles array - else if (idx >= 0 && idx < num_handles) - { - if (idx < num_pipe_handles) // Index of pipe handle - { - if (pipeinsts[idx].pendingIO) - { - BOOL success = GetOverlappedResult( - pipeinsts[idx].pipeHandle, // handle to pipe - &pipeinsts[idx].ol, // OVERLAPPED structure - &cbRet, // bytes transferred - FALSE); // do not wait - - if (!success) - { - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Error %d \n.", GetLastError()); - return FALSE; - } - else - { - pipeinsts[idx].pendingIO = FALSE; - CloseHandle(pipeinsts[idx].ol.hEvent); - num_waiting_to_connect--; - } - } - } - else // Index of PID - { - // Fail since index of 'pid' indicates that the pid of the extcap process has terminated. - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap terminated without connecting to pipe."); - return FALSE; - } - } - else - { - g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "WaitForMultipleObjects returned 0x%08X. Error %d", dw, GetLastError()); - return FALSE; - } - } - - return TRUE; -} -#endif - -/* - * Editor modelines - http://www.wireshark.org/tools/modelines.html - * - * Local variables: - * c-basic-offset: 4 - * tab-width: 8 - * indent-tabs-mode: nil - * End: - * - * vi: set shiftwidth=4 tabstop=8 expandtab: - * :indentSize=4:tabSize=8:noTabs=true: - */ diff --git a/extcap_spawn.h b/extcap_spawn.h deleted file mode 100644 index 0557a6f352..0000000000 --- a/extcap_spawn.h +++ /dev/null @@ -1,54 +0,0 @@ -/* extcap_spawn.h - * Helper routines for executing extcap utilities - * - * Copyright 2016, Roland Knall - * - * Wireshark - Network traffic analyzer - * By Gerald Combs - * Copyright 1998 Gerald Combs - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef __EXTCAP_SPAWN_H__ -#define __EXTCAP_SPAWN_H__ - -#include - -#include - -#include - -typedef struct _extcap_userdata { - GPid pid; - gchar * extcap_stderr; - gint exitcode; - gint extcap_stderr_rd; - gint extcap_stdout_rd; -#ifdef _WIN32 - HANDLE threadId; -#endif -} extcap_userdata; - -gboolean extcap_spawn_sync ( gchar * dirname, gchar * command, gint argc, gchar ** argv, gchar ** command_output ); - -GPid extcap_spawn_async ( extcap_userdata * userdata, GPtrArray * args ); - -#ifdef _WIN32 -gboolean extcap_wait_for_pipe(HANDLE * pipe_handles, int num_pipe_handles, HANDLE pid); -#endif - -#endif - -/* - * Editor modelines - http://www.wireshark.org/tools/modelines.html - * - * Local variables: - * c-basic-offset: 4 - * tab-width: 8 - * indent-tabs-mode: nil - * End: - * - * vi: set shiftwidth=4 tabstop=8 expandtab: - * :indentSize=4:tabSize=8:noTabs=true: - */ diff --git a/wsutil/ws_pipe.c b/wsutil/ws_pipe.c index 2c0f28b89e..3649276864 100644 --- a/wsutil/ws_pipe.c +++ b/wsutil/ws_pipe.c @@ -18,6 +18,8 @@ #ifdef _WIN32 #include #include +#include /* for _O_BINARY */ +#include #else #include #ifdef HAVE_SYS_SELECT_H @@ -28,8 +30,358 @@ #include #include +#include #include "wsutil/ws_pipe.h" +gboolean ws_pipe_spawn_sync(gchar *dirname, gchar *command, gint argc, gchar **args, gchar **command_output) +{ + gboolean status = FALSE; + gboolean result = FALSE; + gchar **argv = NULL; + gint cnt = 0; + gchar *local_output = NULL; +#ifdef _WIN32 + +#define BUFFER_SIZE 16384 + + GString *winargs = g_string_sized_new(200); + gchar *quoted_arg; + gunichar2 *wcommandline; + + STARTUPINFO info; + PROCESS_INFORMATION processInfo; + + SECURITY_ATTRIBUTES sa; + HANDLE child_stdout_rd = NULL; + HANDLE child_stdout_wr = NULL; + HANDLE child_stderr_rd = NULL; + HANDLE child_stderr_wr = NULL; + + const gchar *oldpath = g_getenv("PATH"); + gchar *newpath = NULL; +#else + gint exit_status = 0; +#endif + + argv = (gchar **) g_malloc0(sizeof(gchar *) * (argc + 2)); + +#ifdef _WIN32 + newpath = g_strdup_printf("%s;%s", g_strescape(get_progfile_dir(), NULL), oldpath); + g_setenv("PATH", newpath, TRUE); + + argv[0] = g_strescape(command, NULL); +#else + argv[0] = g_strdup(command); +#endif + + for (cnt = 0; cnt < argc; cnt++) + argv[cnt + 1] = args[cnt]; + argv[argc + 1] = NULL; + +#ifdef _WIN32 + + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&child_stdout_rd, &child_stdout_wr, &sa, 0)) + { + g_free(argv[0]); + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stdout handle"); + return FALSE; + } + + if (!CreatePipe(&child_stderr_rd, &child_stderr_wr, &sa, 0)) + { + CloseHandle(child_stdout_rd); + CloseHandle(child_stdout_wr); + g_free(argv[0]); + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stderr handle"); + return FALSE; + } + + /* 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 (cnt = 0; argv[cnt] != 0; cnt++) { + if (cnt != 0) g_string_append_c(winargs, ' '); /* don't prepend a space before the path!!! */ + quoted_arg = protect_arg(argv[cnt]); + g_string_append(winargs, quoted_arg); + g_free(quoted_arg); + } + + wcommandline = g_utf8_to_utf16(winargs->str, (glong)winargs->len, NULL, NULL, NULL); + + memset(&processInfo, 0, sizeof(PROCESS_INFORMATION)); + memset(&info, 0, sizeof(STARTUPINFO)); + + info.cb = sizeof(STARTUPINFO); + info.hStdError = child_stderr_wr; + info.hStdOutput = child_stdout_wr; + info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + info.wShowWindow = SW_HIDE; + + if (CreateProcess(NULL, wcommandline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo)) + { + gchar* buffer; + + WaitForSingleObject(processInfo.hProcess, INFINITE); + buffer = (gchar*)g_malloc(BUFFER_SIZE); + status = ws_read_string_from_pipe(child_stdout_rd, buffer, BUFFER_SIZE); + if (status) + { + local_output = g_strdup_printf("%s", buffer); + } + g_free(buffer); + + CloseHandle(child_stdout_rd); + CloseHandle(child_stdout_wr); + CloseHandle(child_stderr_rd); + CloseHandle(child_stderr_wr); + + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + } + else + status = FALSE; + + g_setenv("PATH", oldpath, TRUE); +#else + + status = g_spawn_sync(dirname, argv, NULL, + (GSpawnFlags) 0, NULL, NULL, &local_output, NULL, &exit_status, NULL); + + if (status && exit_status != 0) + status = FALSE; +#endif + + if (status) + { + if (command_output != NULL && local_output != NULL) + *command_output = g_strdup(local_output); + + result = TRUE; + } + + g_free(local_output); + g_free(argv[0]); + g_free(argv); + + return result; +} + +GPid ws_pipe_spawn_async(ws_pipe_t *ws_pipe, GPtrArray *args) +{ + GPid pid = INVALID_EXTCAP_PID; + +#ifdef _WIN32 + gint cnt = 0; + gchar **tmp = NULL; + + GString *winargs = g_string_sized_new(200); + gchar *quoted_arg; + gunichar2 *wcommandline; + + STARTUPINFO info; + PROCESS_INFORMATION processInfo; + + SECURITY_ATTRIBUTES sa; + HANDLE child_stdin_rd = NULL; + HANDLE child_stdin_wr = NULL; + HANDLE child_stdout_rd = NULL; + HANDLE child_stdout_wr = NULL; + HANDLE child_stderr_rd = NULL; + HANDLE child_stderr_wr = NULL; + + const gchar *oldpath = g_getenv("PATH"); + gchar *newpath = NULL; + + newpath = g_strdup_printf("%s;%s", g_strescape(get_progfile_dir(), NULL), oldpath); + g_setenv("PATH", newpath, TRUE); + + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&child_stdin_rd, &child_stdin_wr, &sa, 0)) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stdin handle"); + return FALSE; + } + + if (!CreatePipe(&child_stdout_rd, &child_stdout_wr, &sa, 0)) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stdout handle"); + return FALSE; + } + + if (!CreatePipe(&child_stderr_rd, &child_stderr_wr, &sa, 0)) + { + CloseHandle(child_stdout_rd); + CloseHandle(child_stdout_wr); + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Could not create stderr handle"); + return FALSE; + } + + /* 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 (tmp = (gchar **)args->pdata, cnt = 0; *tmp && **tmp; ++cnt, ++tmp) { + if (cnt != 0) g_string_append_c(winargs, ' '); /* don't prepend a space before the path!!! */ + quoted_arg = protect_arg(*tmp); + g_string_append(winargs, quoted_arg); + g_free(quoted_arg); + } + + wcommandline = g_utf8_to_utf16(winargs->str, (glong)winargs->len, NULL, NULL, NULL); + + memset(&processInfo, 0, sizeof(PROCESS_INFORMATION)); + memset(&info, 0, sizeof(STARTUPINFO)); + + info.cb = sizeof(STARTUPINFO); + info.hStdInput = child_stdin_rd; + info.hStdError = child_stderr_wr; + info.hStdOutput = child_stdout_wr; + info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + info.wShowWindow = SW_HIDE; + + if (CreateProcess(NULL, wcommandline, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &info, &processInfo)) + { + ws_pipe->stdin_fd = _open_osfhandle((intptr_t)(child_stdin_wr), _O_BINARY); + ws_pipe->stdout_fd = _open_osfhandle((intptr_t)(child_stdout_rd), _O_BINARY); + ws_pipe->stderr_fd = _open_osfhandle((intptr_t)(child_stderr_rd), _O_BINARY); + ws_pipe->threadId = processInfo.hThread; + pid = processInfo.hProcess; + } + + g_setenv("PATH", oldpath, TRUE); +#else + g_spawn_async_with_pipes(NULL, (gchar **)args->pdata, NULL, + (GSpawnFlags) G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, + &pid, &ws_pipe->stdin_fd, &ws_pipe->stdout_fd, &ws_pipe->stderr_fd, NULL); +#endif + + ws_pipe->pid = pid; + + return pid; +} + +#ifdef _WIN32 + +typedef struct +{ + HANDLE pipeHandle; + OVERLAPPED ol; + BOOL pendingIO; +} PIPEINTS; + +gboolean +ws_pipe_wait_for_pipe(HANDLE * pipe_handles, int num_pipe_handles, HANDLE pid) +{ + PIPEINTS pipeinsts[3]; + DWORD dw, cbRet; + HANDLE handles[4]; + int error_code; + int num_waiting_to_connect = 0; + int num_handles = num_pipe_handles + 1; // PID handle is also added to list of handles. + + if (num_pipe_handles == 0 || num_pipe_handles > 3) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Invalid number of pipes given as argument."); + return FALSE; + } + + for (int i = 0; i < num_pipe_handles; ++i) + { + pipeinsts[i].pipeHandle = pipe_handles[i]; + pipeinsts[i].ol.Pointer = 0; + pipeinsts[i].ol.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + pipeinsts[i].pendingIO = FALSE; + handles[i] = pipeinsts[i].ol.hEvent; + BOOL connected = ConnectNamedPipe(pipeinsts[i].pipeHandle, &pipeinsts[i].ol); + if (connected) + { + error_code = GetLastError(); + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "ConnectNamedPipe failed with %d \n.", error_code); + return FALSE; + } + + switch (GetLastError()) + { + case ERROR_IO_PENDING: + num_waiting_to_connect++; + pipeinsts[i].pendingIO = TRUE; + break; + + case ERROR_PIPE_CONNECTED: + if (SetEvent(pipeinsts[i].ol.hEvent)) + { + break; + } // Fallthrough if this fails. + + default: + error_code = GetLastError(); + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "ConnectNamedPipe failed with %d \n.", error_code); + return FALSE; + } + } + + // Store pid of extcap process so it can be monitored in case it fails before the pipes has connceted. + handles[num_pipe_handles] = pid; + + while(num_waiting_to_connect > 0) + { + dw = WaitForMultipleObjects(num_handles, handles, FALSE, 30000); + int idx = dw - WAIT_OBJECT_0; + if (dw == WAIT_TIMEOUT) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap didn't connect to pipe within 30 seconds."); + return FALSE; + } + // If index points to our handles array + else if (idx >= 0 && idx < num_handles) + { + if (idx < num_pipe_handles) // Index of pipe handle + { + if (pipeinsts[idx].pendingIO) + { + BOOL success = GetOverlappedResult( + pipeinsts[idx].pipeHandle, // handle to pipe + &pipeinsts[idx].ol, // OVERLAPPED structure + &cbRet, // bytes transferred + FALSE); // do not wait + + if (!success) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Error %d \n.", GetLastError()); + return FALSE; + } + else + { + pipeinsts[idx].pendingIO = FALSE; + CloseHandle(pipeinsts[idx].ol.hEvent); + num_waiting_to_connect--; + } + } + } + else // Index of PID + { + // Fail since index of 'pid' indicates that the pid of the extcap process has terminated. + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "extcap terminated without connecting to pipe."); + return FALSE; + } + } + else + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "WaitForMultipleObjects returned 0x%08X. Error %d", dw, GetLastError()); + return FALSE; + } + } + + return TRUE; +} +#endif + gboolean ws_pipe_data_available(int pipe_fd) { diff --git a/wsutil/ws_pipe.h b/wsutil/ws_pipe.h index 8a4870f2cc..e69873c449 100644 --- a/wsutil/ws_pipe.h +++ b/wsutil/ws_pipe.h @@ -16,6 +16,12 @@ #include +#ifdef _WIN32 +#define INVALID_EXTCAP_PID INVALID_HANDLE_VALUE +#else +#define INVALID_EXTCAP_PID (GPid)-1 +#endif + #ifdef _WIN32 #include #include @@ -26,6 +32,26 @@ #define ws_get_pipe_handle(pipe_fd) (pipe_fd) #endif +typedef struct _ws_pipe_t { + GPid pid; + gchar *stderr_msg; + gint exitcode; + gint stdin_fd; + gint stdout_fd; + gint stderr_fd; +#ifdef _WIN32 + HANDLE threadId; +#endif +} ws_pipe_t; + +WS_DLL_PUBLIC gboolean ws_pipe_spawn_sync ( gchar * dirname, gchar * command, gint argc, gchar ** argv, gchar ** command_output ); + +WS_DLL_PUBLIC GPid ws_pipe_spawn_async (ws_pipe_t * ws_pipe, GPtrArray * args ); + +#ifdef _WIN32 +WS_DLL_PUBLIC gboolean ws_pipe_wait_for_pipe(HANDLE * pipe_handles, int num_pipe_handles, HANDLE pid); +#endif + WS_DLL_PUBLIC gboolean ws_pipe_data_available(int pipe_fd); WS_DLL_PUBLIC gboolean ws_read_string_from_pipe(ws_pipe_handle read_pipe,