+ /* call dumpcap */
+ if(!CreateProcess(NULL, utf_8to16(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()));
+ CloseHandle(data_pipe[PIPE_READ]);
+ CloseHandle(data_pipe[PIPE_WRITE]);
+ CloseHandle(sync_pipe[PIPE_READ]);
+ CloseHandle(sync_pipe[PIPE_WRITE]);
+ g_free( (gpointer) argv[0]);
+ g_free( (gpointer) argv);
+ return -1;
+ }
+ *fork_child = (int) pi.hProcess;
+ g_string_free(args, TRUE);
+
+ /* associate the operating system filehandles to C run-time file handles */
+ /* (good file handle infos at: http://www.flounder.com/handles.htm) */
+ *data_read_fd = _open_osfhandle( (long) data_pipe[PIPE_READ], _O_BINARY);
+ *message_read_fd = _open_osfhandle( (long) sync_pipe[PIPE_READ], _O_BINARY);
+#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", strerror(errno));
+ g_free( (gpointer) argv[0]);
+ g_free(argv);
+ 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", strerror(errno));
+ ws_close(sync_pipe[PIPE_READ]);
+ ws_close(sync_pipe[PIPE_WRITE]);
+ g_free( (gpointer) argv[0]);
+ g_free(argv);
+ 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], (gpointer)argv);
+ g_snprintf(errmsg, sizeof errmsg, "Couldn't run %s in child process: %s",
+ argv[0], 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);
+ }
+
+ *data_read_fd = data_pipe[PIPE_READ];
+ *message_read_fd = sync_pipe[PIPE_READ];
+#endif
+
+ g_free( (gpointer) argv[0]); /* exename */
+
+ /* Parent process - read messages from the child process over the
+ sync pipe. */
+ g_free( (gpointer) argv); /* free up arg array */
+
+ /* 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 == -1) {
+ /* We couldn't even create the child process. */
+ *msg = g_strdup_printf("Couldn't create child process: %s", 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 */
+ main_window_update();
+ return 0;
+}
+
+/*
+ * Wait for dumpcap to finish. On success, *msg is unchanged, and 0 is
+ * returned. On failure, *msg points to an error message for the
+ * failure, and -1 is returned. In the latter case, *msg must be
+ * freed with g_free().
+ */
+static int
+sync_pipe_close_command(int *data_read_fd, int *message_read_fd,
+ int *fork_child, gchar **msg)
+{
+ ws_close(*data_read_fd);
+ if (message_read_fd != NULL)
+ ws_close(*message_read_fd);
+
+#ifdef _WIN32
+ /* XXX - Should we signal the child somehow? */
+ sync_pipe_kill(*fork_child);
+#endif
+
+ return sync_pipe_wait_for_child(*fork_child, msg);
+}
+
+/*
+ * Run dumpcap with the supplied arguments.
+ *
+ * On success, *data points to a buffer containing the dumpcap output,
+ * *primary_msg and *secondary_message are NULL, and 0 is returned; *data
+ * must be freed with g_free().
+ *
+ * On failure, *data is NULL, *primary_msg points to an error message,
+ * *secondary_msg either points to an additional error message or is
+ * 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(const char** argv, gchar **data, gchar **primary_msg,
+ gchar **secondary_msg)
+{
+ gchar *msg;
+ int data_pipe_read_fd, sync_pipe_read_fd, fork_child, ret;
+ char *wait_msg;
+ gchar buffer[PIPE_BUF_SIZE+1];
+ int nread;
+ char indicator;
+ int primary_msg_len;
+ char *primary_msg_text;
+ int secondary_msg_len;
+ char *secondary_msg_text;
+ char *combined_msg;
+ GString *data_buf = NULL;
+ int count;
+
+ ret = sync_pipe_open_command(argv, &data_pipe_read_fd, &sync_pipe_read_fd,
+ &fork_child, &msg);
+ if (ret == -1) {
+ *primary_msg = msg;
+ *secondary_msg = NULL;
+ *data = NULL;
+ return -1;
+ }
+
+ /*
+ * We were able to set up to read dumpcap's output. Do so.
+ *
+ * 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,
+ buffer, primary_msg);
+ if(nread <= 0) {
+ /* We got a read error from the sync pipe, or we got no data at
+ all from the sync pipe, so we're not going to be getting any
+ data or error message from the child process. Pick up its
+ exit status, and complain.
+
+ We don't have to worry about killing the child, if the sync pipe
+ returned an error. Usually this error is caused as the child killed
+ itself while going down. Even in the rare cases that this isn't the
+ case, the child will get an error when writing to the broken pipe
+ the next time, cleaning itself up then. */
+ ret = sync_pipe_wait_for_child(fork_child, &wait_msg);
+ if(nread == 0) {
+ /* We got an EOF from the sync pipe. That means that it exited
+ before giving us any data to read. If ret is -1, we report
+ that as a bad exit (e.g., exiting due to a signal); otherwise,
+ we report it as a premature exit. */
+ if (ret == -1)
+ *primary_msg = wait_msg;
+ else
+ *primary_msg = g_strdup("Child dumpcap closed sync pipe prematurely");
+ } else {
+ /* We got an error from the sync pipe. If ret is -1, report
+ both the sync pipe I/O error and the wait error. */
+ if (ret == -1) {
+ combined_msg = g_strdup_printf("%s\n\n%s", *primary_msg, wait_msg);
+ g_free(*primary_msg);
+ g_free(wait_msg);
+ *primary_msg = combined_msg;
+ }
+ }
+ *secondary_msg = NULL;
+
+ return -1;
+ }
+
+ /* 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(buffer, 4, &indicator, &primary_msg_len);
+ primary_msg_text = buffer+4;
+ /* convert secondary message */
+ pipe_convert_header(primary_msg_text + primary_msg_len, 4, &indicator,
+ &secondary_msg_len);
+ secondary_msg_text = primary_msg_text + primary_msg_len + 4;
+ /* the capture child will close the sync_pipe, nothing to do */
+
+ /*
+ * Pick up the child status.
+ */
+ ret = sync_pipe_close_command(&data_pipe_read_fd, &sync_pipe_read_fd,
+ &fork_child, &msg);
+ if (ret == -1) {
+ /*
+ * Child process failed unexpectedly, or wait failed; msg is the
+ * error message.
+ */
+ *primary_msg = msg;
+ *secondary_msg = NULL;
+ } else {
+ /*
+ * Child process failed, but returned the expected exit status.
+ * Return the messages it gave us, and indicate failure.
+ */
+ *primary_msg = g_strdup(primary_msg_text);
+ *secondary_msg = g_strdup(secondary_msg_text);
+ ret = -1;
+ }
+ *data = NULL;
+ break;
+
+ case SP_SUCCESS:
+ /* read the output from the command */
+ data_buf = g_string_new("");
+ while ((count = ws_read(data_pipe_read_fd, buffer, PIPE_BUF_SIZE)) > 0) {
+ buffer[count] = '\0';
+ g_string_append(data_buf, buffer);
+ }
+
+ /*
+ * Pick up the child status.
+ */
+ ret = sync_pipe_close_command(&data_pipe_read_fd, &sync_pipe_read_fd,
+ &fork_child, &msg);
+ if (ret == -1) {
+ /*
+ * Child process failed unexpectedly, or wait failed; msg is the
+ * error message.
+ */
+ *primary_msg = msg;
+ *secondary_msg = NULL;
+ g_string_free(data_buf, TRUE);
+ *data = NULL;
+ } else {
+ /*
+ * Child process succeeded.
+ */
+ *primary_msg = NULL;
+ *secondary_msg = NULL;
+ *data = data_buf->str;
+ g_string_free(data_buf, FALSE);
+ }
+ break;
+
+ default:
+ /*
+ * Pick up the child status.
+ */
+ ret = sync_pipe_close_command(&data_pipe_read_fd, &sync_pipe_read_fd,
+ &fork_child, &msg);
+ if (ret == -1) {
+ /*
+ * Child process failed unexpectedly, or wait failed; msg is the
+ * error message.
+ */
+ *primary_msg = msg;
+ *secondary_msg = NULL;
+ } else {
+ /*
+ * Child process returned an unknown status.
+ */
+ *primary_msg = g_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x",
+ indicator);
+ *secondary_msg = NULL;
+ ret = -1;
+ }
+ *data = NULL;
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Get the list of interfaces using dumpcap.
+ *
+ * On success, *data points to a buffer containing the dumpcap output,
+ * *primary_msg and *secondary_msg are NULL, and 0 is returned. *data
+ * must be freed with g_free().
+ *
+ * On failure, *data is NULL, *primary_msg points to an error message,
+ * *secondary_msg either points to an additional error message or is
+ * NULL, and -1 is returned; *primary_msg, and *secondary_msg if not NULL,
+ * must be freed with g_free().
+ */
+int
+sync_interface_list_open(gchar **data, gchar **primary_msg,
+ gchar **secondary_msg)
+{
+ int argc;
+ const char **argv;
+
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_interface_list_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;
+ }
+
+ /* 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
+ return sync_pipe_run_command(argv, data, primary_msg, secondary_msg);
+}
+
+/*
+ * Get the capabilities of an interface using dumpcap.
+ *
+ * On success, *data points to a buffer containing the dumpcap output,
+ * *primary_msg and *secondary_msg are NULL, and 0 is returned. *data
+ * must be freed with g_free().
+ *
+ * On failure, *data is NULL, *primary_msg points to an error message,
+ * *secondary_msg either points to an additional error message or is
+ * NULL, and -1 is returned; *primary_msg, and *secondary_msg if not NULL,
+ * must be freed with g_free().
+ */
+int
+sync_if_capabilities_open(const gchar *ifname, gboolean monitor_mode,
+ gchar **data, gchar **primary_msg,
+ gchar **secondary_msg)
+{
+ int argc;
+ const char **argv;
+
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_linktype_list_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;
+ }
+
+ /* Ask for the interface capabilities */
+ argv = sync_pipe_add_arg(argv, &argc, "-i");
+ argv = sync_pipe_add_arg(argv, &argc, ifname);
+ argv = sync_pipe_add_arg(argv, &argc, "-L");
+ if (monitor_mode)
+ argv = sync_pipe_add_arg(argv, &argc, "-I");
+
+#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
+ return sync_pipe_run_command(argv, data, primary_msg, secondary_msg);
+}
+
+/*
+ * Start getting interface statistics using dumpcap. On success, read_fd
+ * 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.
+ */
+int
+sync_interface_stats_open(int *data_read_fd, int *fork_child, gchar **msg)
+{
+ int argc;
+ const char **argv;
+ int message_read_fd, ret;
+ char *wait_msg;
+ gchar buffer[PIPE_BUF_SIZE+1];
+ int nread;
+ char indicator;
+ int primary_msg_len;
+ char *primary_msg_text;
+ int secondary_msg_len;
+ char *secondary_msg_text;
+ char *combined_msg;
+
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_interface_stats_open");
+
+ argv = init_pipe_args(&argc);
+
+ if (!argv) {
+ *msg = g_strdup("We don't know where to find dumpcap.");
+ return -1;
+ }
+
+ /* Ask for the interface statistics */
+ argv = sync_pipe_add_arg(argv, &argc, "-S");
+
+#ifndef DEBUG_CHILD
+ argv = sync_pipe_add_arg(argv, &argc, "-Z");
+ argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE);
+#endif
+ ret = sync_pipe_open_command(argv, data_read_fd, &message_read_fd,
+ fork_child, msg);
+ if (ret == -1)
+ return -1;
+
+ /*
+ * We were able to set up to read dumpcap's output. Do so.
+ *
+ * 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);
+ 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;
+ }
+ }
+
+ return -1;
+ }
+
+ /* 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(buffer, 4, &indicator, &primary_msg_len);
+ primary_msg_text = buffer+4;
+ /* convert secondary message */
+ pipe_convert_header(primary_msg_text + primary_msg_len, 4, &indicator,
+ &secondary_msg_len);
+ secondary_msg_text = primary_msg_text + primary_msg_len + 4;
+ /* the capture child will close the sync_pipe, nothing to do */
+
+ /*
+ * Pick up the child status.
+ */
+ ret = sync_pipe_close_command(data_read_fd, &message_read_fd,
+ fork_child, msg);
+ if (ret == -1) {
+ /*
+ * Child process failed unexpectedly, or wait failed; msg is the
+ * error message.
+ */
+ } 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;
+ }
+ break;
+
+ case SP_SUCCESS:
+ /* Close the message pipe. */
+ ws_close(message_read_fd);
+ break;
+
+ 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.
+ */
+ } else {
+ /*
+ * Child process returned an unknown status.
+ */
+ *msg = g_strdup_printf("dumpcap process gave an unexpected message type: 0x%02x",
+ indicator);
+ ret = -1;
+ }
+ break;
+ }
+ return ret;
+}
+
+/* Close down the stats process */
+int
+sync_interface_stats_close(int *read_fd, int *fork_child, gchar **msg)
+{
+ return sync_pipe_close_command(read_fd, NULL, fork_child, msg);
+}
+
+/* read a number of bytes from a pipe */
+/* (blocks until enough bytes read or an error occurs) */
+static int
+pipe_read_bytes(int pipe_fd, char *bytes, int required, char **msg)
+{
+ int newly;
+ int offset = 0;
+ int error;
+
+ while(required) {
+ newly = read(pipe_fd, &bytes[offset], required);
+ if (newly == 0) {
+ /* EOF */
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+ "read from pipe %d: EOF (capture closed?)", pipe_fd);
+ *msg = 0;
+ return offset;
+ }
+ if (newly < 0) {
+ /* error */
+ error = errno;
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+ "read from pipe %d: error(%u): %s", pipe_fd, error,
+ strerror(error));
+ *msg = g_strdup_printf("Error reading from sync pipe: %s",
+ strerror(error));
+ return newly;
+ }
+
+ required -= newly;
+ offset += newly;
+ }
+
+ *msg = NULL;
+ return offset;
+}
+
+static gboolean pipe_data_available(int pipe_fd) {
+#ifdef _WIN32 /* PeekNamedPipe */
+ HANDLE hPipe = (HANDLE) _get_osfhandle(pipe_fd);
+ DWORD bytes_avail;
+
+ if (hPipe == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ if (! PeekNamedPipe(hPipe, NULL, 0, NULL, &bytes_avail, NULL))
+ return FALSE;
+
+ if (bytes_avail > 0)
+ return TRUE;
+ return FALSE;
+#else /* select */
+ fd_set rfds;
+ struct timeval timeout;
+
+ FD_ZERO(&rfds);
+ FD_SET(pipe_fd, &rfds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ if (select(pipe_fd+1, &rfds, NULL, NULL, &timeout) > 0)
+ return TRUE;
+
+ return FALSE;
+#endif
+}
+
+/* Read a line from a pipe, similar to fgets */
+int
+sync_pipe_gets_nonblock(int pipe_fd, char *bytes, int max) {
+ int newly;
+ int offset = -1;
+
+ while(offset < max - 1) {
+ offset++;
+ if (! pipe_data_available(pipe_fd))
+ break;
+ newly = read(pipe_fd, &bytes[offset], 1);
+ if (newly == 0) {
+ /* EOF - not necessarily an error */
+ break;
+ } else if (newly < 0) {
+ /* error */
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+ "read from pipe %d: error(%u): %s", pipe_fd, errno, strerror(errno));
+ return newly;
+ } else if (bytes[offset] == '\n') {
+ break;
+ }
+ }
+
+ if (offset >= 0)
+ bytes[offset] = '\0';
+
+ return offset;
+}
+
+
+/* convert header values (indicator and 3-byte length) */