diff options
author | Guy Harris <guy@alum.mit.edu> | 2014-06-23 16:48:41 -0700 |
---|---|---|
committer | Guy Harris <guy@alum.mit.edu> | 2014-06-23 23:58:29 +0000 |
commit | 213189ef9e7bbd851e145e1cfb1067c7b2b72de8 (patch) | |
tree | 67bc7b5ce4a1e36fd16203d12fd184a0aee38965 /capchild | |
parent | e22d3c9b744af8fa49806f1cde2100be85cc58cc (diff) |
Move the routines to talk to dumpcap into a static libcapchild.
This pulls some stuff out of the top-level directory, and means we don't
have to build them once for every program using them.
Change-Id: I37b31fed20f2d5c3563ecd2bae9fd86af70afff5
Reviewed-on: https://code.wireshark.org/review/2591
Reviewed-by: Guy Harris <guy@alum.mit.edu>
Diffstat (limited to 'capchild')
-rw-r--r-- | capchild/CMakeLists.txt | 46 | ||||
-rw-r--r-- | capchild/Makefile.am | 80 | ||||
-rw-r--r-- | capchild/Makefile.common | 30 | ||||
-rw-r--r-- | capchild/Makefile.nmake | 77 | ||||
-rw-r--r-- | capchild/capture_ifinfo.c | 342 | ||||
-rw-r--r-- | capchild/capture_ifinfo.h | 121 | ||||
-rw-r--r-- | capchild/capture_sync.c | 2127 | ||||
-rw-r--r-- | capchild/capture_sync.h | 138 |
8 files changed, 2961 insertions, 0 deletions
diff --git a/capchild/CMakeLists.txt b/capchild/CMakeLists.txt new file mode 100644 index 0000000000..af4a1cbc58 --- /dev/null +++ b/capchild/CMakeLists.txt @@ -0,0 +1,46 @@ +# CMakeLists.txt +# +# Wireshark - Network traffic analyzer +# By Gerald Combs <gerald@wireshark.org> +# Copyright 1998 Gerald Combs +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + + +set(CAPCHILD_SRC + capture_ifinfo.c + capture_sync.c +) + +set(CLEAN_FILES + ${CAPCHILD_SRC} +) + +if (WERROR) + set_source_files_properties( + ${CLEAN_FILES} + PROPERTIES + COMPILE_FLAGS -Werror + ) +endif() + + +add_library(capchild STATIC + ${CAPCHILD_SRC} +) + +set_target_properties(capchild PROPERTIES LINK_FLAGS "${WS_LINK_FLAGS}") +set_target_properties(capchild PROPERTIES FOLDER "CAPCHILD") diff --git a/capchild/Makefile.am b/capchild/Makefile.am new file mode 100644 index 0000000000..ed14eeb5f2 --- /dev/null +++ b/capchild/Makefile.am @@ -0,0 +1,80 @@ +# Makefile.am +# Automake file for the "talking to dumpcap" routines for Wireshark +# +# Wireshark - Network traffic analyzer +# By Gerald Combs <gerald@wireshark.org> +# Copyright 1998 Gerald Combs +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +include Makefile.common +include ../Makefile.am.inc + +if HAVE_WARNINGS_AS_ERRORS +AM_CLEAN_CFLAGS = -Werror +endif + +noinst_LIBRARIES = libcapchild.a + +CLEANFILES = \ + doxygen-capchild.tag \ + libcapchild.a \ + *~ + +MAINTAINERCLEANFILES = \ + $(GENERATED_FILES) \ + Makefile.in + +# All sources that should be put in the source distribution tarball +libcapchild_a_SOURCES = \ + $(CAPCHILD_SRC) \ + $(noinst_HEADERS) + +libcapchild_a_CFLAGS = $(AM_CLEAN_CFLAGS) + +libcapchild_a_DEPENDENCIES = + +# Common headers +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/wiretap $(LIBGCRYPT_CFLAGS) $(LIBGNUTLS_CFLAGS) $(PORTAUDIO_INCLUDES) + + +doxygen: +if HAVE_DOXYGEN + $(DOXYGEN) doxygen.cfg +endif # HAVE_DOXYGEN + +wsar_html: doxygen.cfg ../doxygen_global.cfg +if HAVE_DOXYGEN + (umask 022 ; $(DOXYGEN) doxygen.cfg) +endif + +checkapi: checkapi-base checkapi-todo + +checkapi-base: + $(PERL) $(top_srcdir)/tools/checkAPIs.pl -g deprecated-gtk -build \ + -sourcedir=$(srcdir) \ + $(CAPCHILD_SRC) + +checkapi-todo: + $(PERL) $(top_srcdir)/tools/checkAPIs.pl -M -g deprecated-gtk-todo -build \ + -sourcedir=$(srcdir) \ + $(CAPCHILD_SRC) + +EXTRA_DIST = \ + $(GENERATOR_FILES) \ + CMakeLists.txt \ + doxygen.cfg.in \ + Makefile.common \ + Makefile.nmake diff --git a/capchild/Makefile.common b/capchild/Makefile.common new file mode 100644 index 0000000000..80a8b9adaa --- /dev/null +++ b/capchild/Makefile.common @@ -0,0 +1,30 @@ +# Makefile.common +# Contains the stuff from Makefile.am and Makefile.nmake that is +# a) common to both files and +# b) portable between both files +# +# Wireshark - Network traffic analyzer +# By Gerald Combs <gerald@wireshark.org> +# Copyright 1998 Gerald Combs +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +CAPCHILD_SRC = \ + capture_ifinfo.c \ + capture_sync.c + +noinst_HEADERS = \ + capture_ifinfo.h \ + capture_sync.h diff --git a/capchild/Makefile.nmake b/capchild/Makefile.nmake new file mode 100644 index 0000000000..2e55521e66 --- /dev/null +++ b/capchild/Makefile.nmake @@ -0,0 +1,77 @@ +## Makefile for building wireshark.exe with Microsoft C and nmake +## Use: $(MAKE) /$(MAKEFLAGS) -f makefile.nmake +# + +include ..\config.nmake +include ..\Makefile.nmake.inc + +############### no need to modify below this line ######### + +# We use GENERATED_CFLAGS to get around flex's non-LLP64-compliant output +GENERATED_CFLAGS=\ + $(STANDARD_CFLAGS) \ + /Zm800 \ + /I.. /I../wiretap $(GLIB_CFLAGS) $(GNUTLS_CFLAGS) \ + /I$(PCAP_DIR)\WPCAP\LIBPCAP /I$(PCAP_DIR)\WPCAP\LIBPCAP\bpf \ + /I$(PCAP_DIR)\WPCAP\LIBPCAP\lbl \ + /I$(PCAP_DIR)\include $(AIRPCAP_CFLAGS) \ + $(PORTAUDIO_CFLAGS) $(GEOIP_CFLAGS) $(WINSPARKLE_CFLAGS) \ + $(HHC_CFLAGS) + +CFLAGS=$(WARNINGS_ARE_ERRORS) $(GENERATED_CFLAGS) + +.c.obj:: + $(CC) $(CFLAGS) $(WSUG_CFLAGS) -Fd.\ -c $< + +include Makefile.common + + +# if you add files here, be sure to include them also in Makefile.am EXTRA_DIST +CAPCHILD_OBJECTS = \ + $(CAPCHILD_SRC:.c=.obj) + +RUNLEX=..\tools\runlex.sh + +libcapchild.lib : ..\config.h $(CAPCHILD_OBJECTS) + link /lib /out:libcapchild.lib $(CAPCHILD_OBJECTS) + +clean: + rm -f $(CAPCHILD_OBJECTS) $(WIRESHARK_TAP_OBJECTS) libcapchild.lib *.pdb *.sbr \ + doxygen.cfg html/*.* wireshark-tap-register-cache.pkl + if exist html rmdir html + +distclean: clean + +maintainer-clean: distclean + rm -f $(GENERATED_FILES) + +# convert doxygen.cfg.in to doxygen.cfg with stamped version info +doxygen.cfg: ..\config.nmake doxygen.cfg.in +!IFDEF DOXYGEN + sed -e s/@VERSION@/$(VERSION)/ \ + < doxygen.cfg.in > $@ +!ENDIF + +doxygen-run: +!IFDEF DOXYGEN + $(DOXYGEN) doxygen.cfg +!ENDIF + +# MS html help compiler hhc returns 1 on success, but as nmake expects 0 it would stop here. +# the prepended -1 will raise the accepted error levels of nmake, so it will continue +doxygen.chm: +!IFDEF HHC + -1 $(HHC) html\index.hhp +!ENDIF + +doxygen: doxygen.cfg doxygen-run doxygen.chm + +checkapi: checkapi-base checkapi-todo + +checkapi-base: + $(PERL) ../tools/checkAPIs.pl -g deprecated-gtk -build \ + $(CAPCHILD_SRC) + +checkapi-todo: + $(PERL) ../tools/checkAPIs.pl -M -g deprecated-gtk-todo -build \ + $(CAPCHILD_SRC) diff --git a/capchild/capture_ifinfo.c b/capchild/capture_ifinfo.c new file mode 100644 index 0000000000..039ef53473 --- /dev/null +++ b/capchild/capture_ifinfo.c @@ -0,0 +1,342 @@ +/* capture_ifinfo.c + * Routines for getting interface information from dumpcap + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#ifdef HAVE_LIBPCAP + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> /* needed to define AF_ values on UNIX */ +#endif + +#ifdef HAVE_WINSOCK2_H +#include <winsock2.h> /* needed to define AF_ values on Windows */ +#endif + +#ifdef NEED_INET_V6DEFS_H +# include "wsutil/inet_v6defs.h" +#endif + +#include <glib.h> + +#include "capture_opts.h" +#include "capture_session.h" +#include "capture_sync.h" +#include "log.h" + +#include "capture_ifinfo.h" + +#ifdef HAVE_PCAP_REMOTE +static GList *remote_interface_list = NULL; + +static void append_remote_list(GList *iflist) +{ + GSList *list; + GList *rlist; + if_addr_t *if_addr, *temp_addr; + if_info_t *if_info, *temp; + + for (rlist = g_list_nth(remote_interface_list, 0); rlist != NULL; rlist = g_list_next(rlist)) { + if_info = (if_info_t *)rlist->data; + temp = g_malloc0(sizeof(if_info_t)); + temp->name = g_strdup(if_info->name); + temp->friendly_name = g_strdup(if_info->friendly_name); + temp->vendor_description = g_strdup(if_info->vendor_description); + for (list = g_slist_nth(if_info->addrs, 0); list != NULL; list = g_slist_next(list)) { + temp_addr = g_malloc0(sizeof(if_addr_t)); + if_addr = (if_addr_t *)list->data; + if (if_addr) { + temp_addr->ifat_type = if_addr->ifat_type; + if (temp_addr->ifat_type == IF_AT_IPv4) { + temp_addr->addr.ip4_addr = if_addr->addr.ip4_addr; + } else { + memcpy(temp_addr->addr.ip6_addr, if_addr->addr.ip6_addr, sizeof(if_addr->addr)); + } + } else { + g_free(temp_addr); + temp_addr = NULL; + } + if (temp_addr) { + temp->addrs = g_slist_append(temp->addrs, temp_addr); + } + } + temp->loopback = if_info->loopback; + iflist = g_list_append(iflist, temp); + } +} +#endif + +/** + * Fetch the interface list from a child process (dumpcap). + * + * @return A GList containing if_info_t structs if successful, NULL (with err and possibly err_str set) otherwise. + * + */ + +/* XXX - We parse simple text output to get our interface list. Should + * we use "real" data serialization instead, e.g. via XML? */ +GList * +capture_interface_list(int *err, char **err_str, void (*update_cb)(void)) +{ + int ret; + GList *if_list = NULL; + int i, j; + gchar *data, *primary_msg, *secondary_msg; + gchar **raw_list, **if_parts, **addr_parts; + gchar *name; + if_info_t *if_info; + if_addr_t *if_addr; + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List ..."); + + /* Try to get our interface list */ + ret = sync_interface_list_open(&data, &primary_msg, &secondary_msg, update_cb); + if (ret != 0) { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List failed!"); + if (err_str) { + *err_str = primary_msg; + } else { + g_free(primary_msg); + } + g_free(secondary_msg); + *err = CANT_GET_INTERFACE_LIST; + return NULL; + } + + /* Split our lines */ +#ifdef _WIN32 + raw_list = g_strsplit(data, "\r\n", 0); +#else + raw_list = g_strsplit(data, "\n", 0); +#endif + g_free(data); + + for (i = 0; raw_list[i] != NULL; i++) { + if_parts = g_strsplit(raw_list[i], "\t", 6); + if (if_parts[0] == NULL || if_parts[1] == NULL || if_parts[2] == NULL || + if_parts[3] == NULL || if_parts[4] == NULL || if_parts[5] == NULL) { + g_strfreev(if_parts); + continue; + } + + /* Number followed by the name, e.g "1. eth0" */ + name = strchr(if_parts[0], ' '); + if (name) { + name++; + } else { + g_strfreev(if_parts); + continue; + } + + if_info = g_new0(if_info_t,1); + if_info->name = g_strdup(name); + if (strlen(if_parts[1]) > 0) + if_info->vendor_description = g_strdup(if_parts[1]); + if (strlen(if_parts[2]) > 0) + if_info->friendly_name = g_strdup(if_parts[2]); + if_info->type = (interface_type)(int)strtol(if_parts[3], NULL, 10); + addr_parts = g_strsplit(if_parts[4], ",", 0); + for (j = 0; addr_parts[j] != NULL; j++) { + if_addr = g_new0(if_addr_t,1); + if (inet_pton(AF_INET, addr_parts[j], &if_addr->addr.ip4_addr) > 0) { + if_addr->ifat_type = IF_AT_IPv4; + } else if (inet_pton(AF_INET6, addr_parts[j], + &if_addr->addr.ip6_addr) > 0) { + if_addr->ifat_type = IF_AT_IPv6; + } else { + g_free(if_addr); + if_addr = NULL; + } + if (if_addr) { + if_info->addrs = g_slist_append(if_info->addrs, if_addr); + } + } + if (strcmp(if_parts[5], "loopback") == 0) + if_info->loopback = TRUE; + g_strfreev(if_parts); + g_strfreev(addr_parts); + if_list = g_list_append(if_list, if_info); + } + g_strfreev(raw_list); + + /* Check to see if we built a list */ + if (if_list == NULL) { + *err = NO_INTERFACES_FOUND; + if (err_str) + *err_str = g_strdup("No interfaces found"); + } +#ifdef HAVE_PCAP_REMOTE + if (remote_interface_list && g_list_length(remote_interface_list) > 0) { + append_remote_list(if_list); + } +#endif + return if_list; +} + +/* XXX - We parse simple text output to get our interface list. Should + * we use "real" data serialization instead, e.g. via XML? */ +if_capabilities_t * +capture_get_if_capabilities(const gchar *ifname, gboolean monitor_mode, + char **err_str, void (*update_cb)(void)) +{ + if_capabilities_t *caps; + GList *linktype_list = NULL; + int err, i; + gchar *data, *primary_msg, *secondary_msg; + gchar **raw_list, **lt_parts; + data_link_info_t *data_link_info; + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface Capabilities ..."); + + /* Try to get our interface list */ + err = sync_if_capabilities_open(ifname, monitor_mode, &data, + &primary_msg, &secondary_msg, update_cb); + if (err != 0) { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface Capabilities failed!"); + if (err_str) { + *err_str = primary_msg; + } else { + g_free(primary_msg); + } + g_free(secondary_msg); + return NULL; + } + + /* Split our lines */ +#ifdef _WIN32 + raw_list = g_strsplit(data, "\r\n", 0); +#else + raw_list = g_strsplit(data, "\n", 0); +#endif + g_free(data); + + /* + * First line is 0 if monitor mode isn't supported, 1 if it is. + */ + if (raw_list[0] == NULL || *raw_list[0] == '\0') { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface Capabilities returned no information!"); + if (err_str) { + *err_str = g_strdup("Dumpcap returned no interface capability information"); + } + return NULL; + } + + /* + * Allocate the interface capabilities structure. + */ + caps = (if_capabilities_t *)g_malloc(sizeof *caps); + switch (*raw_list[0]) { + + case '0': + caps->can_set_rfmon = FALSE; + break; + + case '1': + caps->can_set_rfmon = TRUE; + break; + + default: + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface Capabilities returned bad information!"); + if (err_str) { + *err_str = g_strdup_printf("Dumpcap returned \"%s\" for monitor-mode capability", + raw_list[0]); + } + g_free(caps); + return NULL; + } + + /* + * The rest are link-layer types. + */ + for (i = 1; raw_list[i] != NULL; i++) { + /* ...and what if the interface name has a tab in it, Mr. Clever Programmer? */ + lt_parts = g_strsplit(raw_list[i], "\t", 3); + if (lt_parts[0] == NULL || lt_parts[1] == NULL || lt_parts[2] == NULL) { + g_strfreev(lt_parts); + continue; + } + + data_link_info = g_new(data_link_info_t,1); + data_link_info->dlt = (int) strtol(lt_parts[0], NULL, 10); + data_link_info->name = g_strdup(lt_parts[1]); + if (strcmp(lt_parts[2], "(not supported)") != 0) + data_link_info->description = g_strdup(lt_parts[2]); + else + data_link_info->description = NULL; + + linktype_list = g_list_append(linktype_list, data_link_info); + } + g_strfreev(raw_list); + + /* Check to see if we built a list */ + if (linktype_list == NULL) { + /* No. */ + if (err_str) + *err_str = g_strdup("Dumpcap returned no link-layer types"); + g_free(caps); + return NULL; + } + caps->data_link_types = linktype_list; + return caps; +} + +#ifdef HAVE_PCAP_REMOTE +void add_interface_to_remote_list(if_info_t *if_info) +{ + GSList *list; + if_addr_t *if_addr, *temp_addr; + + if_info_t *temp = g_malloc0(sizeof(if_info_t)); + temp->name = g_strdup(if_info->name); + temp->friendly_name = g_strdup(if_info->friendly_name); + temp->vendor_description = g_strdup(if_info->vendor_description); + for (list = g_slist_nth(if_info->addrs, 0); list != NULL; list = g_slist_next(list)) { + temp_addr = g_malloc0(sizeof(if_addr_t)); + if_addr = (if_addr_t *)list->data; + if (if_addr) { + temp_addr->ifat_type = if_addr->ifat_type; + if (temp_addr->ifat_type == IF_AT_IPv4) { + temp_addr->addr.ip4_addr = if_addr->addr.ip4_addr; + } else { + memcpy(temp_addr->addr.ip6_addr, if_addr->addr.ip6_addr, sizeof(if_addr->addr)); + } + } else { + g_free(temp_addr); + temp_addr = NULL; + } + if (temp_addr) { + temp->addrs = g_slist_append(temp->addrs, temp_addr); + } + } + temp->loopback = if_info->loopback; + remote_interface_list = g_list_append(remote_interface_list, temp); +} +#endif +#endif /* HAVE_LIBPCAP */ diff --git a/capchild/capture_ifinfo.h b/capchild/capture_ifinfo.h new file mode 100644 index 0000000000..f70bdf4ba5 --- /dev/null +++ b/capchild/capture_ifinfo.h @@ -0,0 +1,121 @@ +/* capture_ifinfo.h + * Definitions for routines to get information about capture interfaces + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __CAPTURE_IFINFO_H__ +#define __CAPTURE_IFINFO_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef enum { + IF_WIRED, + IF_AIRPCAP, + IF_PIPE, + IF_STDIN, + IF_BLUETOOTH, + IF_WIRELESS, + IF_DIALUP, + IF_USB, + IF_VIRTUAL +} interface_type; + +/* + * The list of interfaces returned by "get_interface_list()" is + * a list of these structures. + */ +typedef struct { + char *name; /* e.g. "eth0" */ + char *friendly_name; /* from OS, e.g. "Local Area Connection", or + NULL if not available */ + char *vendor_description; + /* vendor description from pcap_findalldevs(), + e.g. "Realtek PCIe GBE Family Controller", + or NULL if not available */ + GSList *addrs; /* containing address values of if_addr_t */ + interface_type type; /* type of interface */ + gboolean loopback; /* TRUE if loopback, FALSE otherwise */ +} if_info_t; + +/* + * An address in the "addrs" list. + */ +typedef enum { + IF_AT_IPv4, + IF_AT_IPv6 +} if_address_type; + +typedef struct { + if_address_type ifat_type; + union { + guint32 ip4_addr; /* 4 byte IP V4 address, or */ + guint8 ip6_addr[16];/* 16 byte IP V6 address */ + } addr; +} if_addr_t; + +/** + * Fetch the interface list from a child process. + */ +extern GList *capture_interface_list(int *err, char **err_str, void (*update_cb)(void)); + +/* Error values from "get_interface_list()/capture_interface_list()". */ +#define CANT_GET_INTERFACE_LIST 1 /* error getting list */ +#define NO_INTERFACES_FOUND 2 /* list is empty */ +#define DONT_HAVE_PCAP 3 /* couldn't load WinPcap */ + +void free_interface_list(GList *if_list); + +/* + * "get_if_capabilities()" and "capture_if_capabilities()" return a pointer + * to an allocated instance of this structure. "free_if_capabilities()" + * frees the returned instance. + */ +typedef struct { + gboolean can_set_rfmon; /* TRUE if can be put into monitor mode */ + GList *data_link_types; /* GList of data_link_info_t's */ +} if_capabilities_t; + +/* + * Information about data link types. + */ +typedef struct { + int dlt; /* e.g. DLT_EN10MB (which is 1) */ + char *name; /* e.g. "EN10MB" or "DLT 1" */ + char *description; /* descriptive name from wiretap e.g. "Ethernet", NULL if unknown */ +} data_link_info_t; + +/** + * Fetch the linktype list for the specified interface from a child process. + */ +extern if_capabilities_t * +capture_get_if_capabilities(const char *devname, gboolean monitor_mode, + char **err_str, void (*update_cb)(void)); + +void free_if_capabilities(if_capabilities_t *caps); + +void add_interface_to_remote_list(if_info_t *if_info); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __CAPTURE_IFINFO_H__ */ diff --git a/capchild/capture_sync.c b/capchild/capture_sync.c new file mode 100644 index 0000000000..2f9d2cc62e --- /dev/null +++ b/capchild/capture_sync.c @@ -0,0 +1,2127 @@ +/* capture_sync.c + * Synchronisation between Wireshark capture parent and child instances + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#ifdef HAVE_LIBPCAP + +#include <glib.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#include <signal.h> + +#ifdef _WIN32 +#include <wsutil/unicode-utils.h> +#endif + +#ifdef HAVE_SYS_WAIT_H +# include <sys/wait.h> +#endif + +#include "capture-pcap-util.h" + +#ifndef _WIN32 +/* + * Define various POSIX macros (and, in the case of WCOREDUMP, non-POSIX + * macros) on UNIX systems that don't have them. + */ +#ifndef WIFEXITED +# define WIFEXITED(status) (((status) & 0177) == 0) +#endif +#ifndef WIFSTOPPED +# define WIFSTOPPED(status) (((status) & 0177) == 0177) +#endif +#ifndef WIFSIGNALED +# define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status)) +#endif +#ifndef WEXITSTATUS +# define WEXITSTATUS(status) ((status) >> 8) +#endif +#ifndef WTERMSIG +# define WTERMSIG(status) ((status) & 0177) +#endif +#ifndef WCOREDUMP +# define WCOREDUMP(status) ((status) & 0200) +#endif +#ifndef WSTOPSIG +# define WSTOPSIG(status) ((status) >> 8) +#endif +#endif /* _WIN32 */ + +#include <epan/packet.h> +#include <epan/prefs.h> + +#include "globals.h" +#include "file.h" + +#include "capture.h" +#include "capture_sync.h" + +#include "sync_pipe.h" + +#ifdef _WIN32 +#include "capture-wpcap.h" +#endif + +#include "ui/ui_util.h" + +#include <wsutil/filesystem.h> +#include <wsutil/file_util.h> +#include <wsutil/report_err.h> +#include "log.h" + +#ifdef _WIN32 +#include <process.h> /* For spawning child process */ +#endif + + + +#ifdef _WIN32 +static void create_dummy_signal_pipe(); +static HANDLE dummy_signal_pipe; /* Dummy named pipe which lets the child check for a dropped connection */ +static gchar *dummy_control_id; +#else +static const char *sync_pipe_signame(int); +#endif + + +static gboolean sync_pipe_input_cb(gint source, gpointer user_data); +static int sync_pipe_wait_for_child(int 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, + char **err_msg); + +static void (*fetch_dumpcap_pid)(int) = NULL; + + +void +capture_session_init(capture_session *cap_session, void *cf) +{ + cap_session->cf = cf; + cap_session->fork_child = -1; /* invalid process handle */ +#ifdef _WIN32 + cap_session->signal_pipe_write_fd = -1; +#endif + cap_session->state = CAPTURE_STOPPED; +#ifndef _WIN32 + cap_session->owner = getuid(); + cap_session->group = getgid(); +#endif + cap_session->session_started = FALSE; +} + +/* Append an arg (realloc) to an argc/argv array */ +/* (add a string pointer to a NULL-terminated array of string pointers) */ +static char ** +sync_pipe_add_arg(char **args, int *argc, const char *arg) +{ + /* Grow the array; "*argc" currently contains the number of string + 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 *)); + + /* Stuff the pointer into the penultimate element of the array, which + is the one at the index specified by "*argc". */ + args[*argc] = g_strdup(arg); + /* Now bump the count. */ + (*argc)++; + + /* We overwrite the NULL pointer; put it back right after the + element we added. */ + args[*argc] = NULL; + + return args; +} + + + +#ifdef _WIN32 +/* Quote the argument element if necessary, so that it will get + * reconstructed correctly in the C runtime startup code. Note that + * the unquoting algorithm in the C runtime is really weird, and + * rather different than what Unix shells do. See stdargv.c in the C + * runtime sources (in the Platform SDK, in src/crt). + * + * Stolen from GLib's protect_argv(), an internal routine that quotes + * string in an argument list so that they arguments will be handled + * correctly in the command-line string passed to CreateProcess() + * if that string is constructed by gluing those strings together. + */ +static gchar * +protect_arg (const gchar *argv) +{ + gchar *new_arg; + const gchar *p = argv; + gchar *q; + gint len = 0; + gboolean need_dblquotes = FALSE; + + while (*p) { + if (*p == ' ' || *p == '\t') + need_dblquotes = TRUE; + else if (*p == '"') + len++; + else if (*p == '\\') { + const gchar *pp = p; + + while (*pp && *pp == '\\') + pp++; + if (*pp == '"') + len++; + } + len++; + p++; + } + + q = new_arg = g_malloc (len + need_dblquotes*2 + 1); + p = argv; + + if (need_dblquotes) + *q++ = '"'; + + while (*p) { + if (*p == '"') + *q++ = '\\'; + else if (*p == '\\') { + const gchar *pp = p; + + while (*pp && *pp == '\\') + pp++; + if (*pp == '"') + *q++ = '\\'; + } + *q++ = *p; + p++; + } + + if (need_dblquotes) + *q++ = '"'; + *q++ = '\0'; + + return new_arg; +} + +/* + * Generate a string for a Win32 error. + */ +#define ERRBUF_SIZE 1024 +static const char * +win32strerror(DWORD error) +{ + static char errbuf[ERRBUF_SIZE+1]; + size_t errlen; + char *p; + + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, error, 0, errbuf, ERRBUF_SIZE, NULL); + + /* + * "FormatMessage()" "helpfully" sticks CR/LF at the end of the + * message. Get rid of it. + */ + errlen = strlen(errbuf); + if (errlen >= 2) { + errbuf[errlen - 1] = '\0'; + errbuf[errlen - 2] = '\0'; + } + p = strchr(errbuf, '\0'); + g_snprintf(p, (gulong)(sizeof errbuf - (p-errbuf)), " (%lu)", error); + return errbuf; +} + +/* + * Generate a string for a Win32 exception code. + */ +static const char * +win32strexception(DWORD exception) +{ + static char errbuf[ERRBUF_SIZE+1]; + static const struct exception_msg { + int code; + char *msg; + } exceptions[] = { + { EXCEPTION_ACCESS_VIOLATION, "Access violation" }, + { EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "Array bounds exceeded" }, + { EXCEPTION_BREAKPOINT, "Breakpoint" }, + { EXCEPTION_DATATYPE_MISALIGNMENT, "Data type misalignment" }, + { EXCEPTION_FLT_DENORMAL_OPERAND, "Denormal floating-point operand" }, + { EXCEPTION_FLT_DIVIDE_BY_ZERO, "Floating-point divide by zero" }, + { EXCEPTION_FLT_INEXACT_RESULT, "Floating-point inexact result" }, + { EXCEPTION_FLT_INVALID_OPERATION, "Invalid floating-point operation" }, + { EXCEPTION_FLT_OVERFLOW, "Floating-point overflow" }, + { EXCEPTION_FLT_STACK_CHECK, "Floating-point stack check" }, + { EXCEPTION_FLT_UNDERFLOW, "Floating-point underflow" }, + { EXCEPTION_GUARD_PAGE, "Guard page violation" }, + { EXCEPTION_ILLEGAL_INSTRUCTION, "Illegal instruction" }, + { EXCEPTION_IN_PAGE_ERROR, "Page-in error" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Integer divide by zero" }, + { EXCEPTION_INT_OVERFLOW, "Integer overflow" }, + { EXCEPTION_INVALID_DISPOSITION, "Invalid disposition" }, + { EXCEPTION_INVALID_HANDLE, "Invalid handle" }, + { EXCEPTION_NONCONTINUABLE_EXCEPTION, "Non-continuable exception" }, + { EXCEPTION_PRIV_INSTRUCTION, "Privileged instruction" }, + { EXCEPTION_SINGLE_STEP, "Single-step complete" }, + { EXCEPTION_STACK_OVERFLOW, "Stack overflow" }, + { 0, NULL } + }; +#define N_EXCEPTIONS (sizeof exceptions / sizeof exceptions[0]) + int i; + + for (i = 0; i < N_EXCEPTIONS; i++) { + if (exceptions[i].code == exception) + return exceptions[i].msg; + } + g_snprintf(errbuf, (gulong)sizeof errbuf, "Exception 0x%08x", exception); + return errbuf; +} +#endif + +/* Initialize an argument list and add dumpcap to it. */ +static char ** +init_pipe_args(int *argc) { + char **argv; + const char *progfile_dir; + char *exename; + + progfile_dir = get_progfile_dir(); + if (progfile_dir == NULL) { + return NULL; + } + + /* Allocate the string pointer array with enough space for the + terminating NULL pointer. */ + *argc = 0; + argv = (char **)g_malloc(sizeof (char *)); + *argv = NULL; + + /* take Wireshark's absolute program path and replace "Wireshark" with "dumpcap" */ + exename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "dumpcap", progfile_dir); + + /* Make that the first argument in the argument list (argv[0]). */ + argv = sync_pipe_add_arg(argv, argc, exename); + + /* sync_pipe_add_arg strdupes exename, so we should free our copy */ + g_free(exename); + + return argv; +} + +#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, capture_session *cap_session, void (*update_cb)(void)) +{ + char ssnap[ARGV_NUMBER_LEN]; + char scount[ARGV_NUMBER_LEN]; + char sfilesize[ARGV_NUMBER_LEN]; + char sfile_duration[ARGV_NUMBER_LEN]; + char sring_num_files[ARGV_NUMBER_LEN]; + char sautostop_files[ARGV_NUMBER_LEN]; + char sautostop_filesize[ARGV_NUMBER_LEN]; + char sautostop_duration[ARGV_NUMBER_LEN]; +#ifdef HAVE_PCAP_REMOTE + char sauth[256]; +#endif +#ifdef HAVE_PCAP_SETSAMPLING + char ssampling[ARGV_NUMBER_LEN]; +#endif + +#if defined(_WIN32) || defined(HAVE_PCAP_CREATE) + char buffer_size[ARGV_NUMBER_LEN]; +#endif + +#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 */ + HANDLE signal_pipe; /* named pipe used to send messages from parent to child (currently only stop) */ + GString *args = g_string_sized_new(200); + gchar *quoted_arg; + SECURITY_ATTRIBUTES sa; + STARTUPINFO si; + PROCESS_INFORMATION pi; + char control_id[ARGV_NUMBER_LEN]; + gchar *signal_pipe_name; +#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 */ +#endif + int sync_pipe_read_fd; + int argc; + char **argv; + int i; + guint j; + interface_options interface_opts; + + if (capture_opts->ifaces->len > 1) + capture_opts->use_pcapng = TRUE; + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_start"); + capture_opts_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, capture_opts); + + cap_session->fork_child = -1; + + 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; + } + + if (capture_opts->ifaces->len > 1) + argv = sync_pipe_add_arg(argv, &argc, "-t"); + + if (capture_opts->use_pcapng) + argv = sync_pipe_add_arg(argv, &argc, "-n"); + else + argv = sync_pipe_add_arg(argv, &argc, "-P"); + + if (capture_opts->capture_comment) { + argv = sync_pipe_add_arg(argv, &argc, "--capture-comment"); + argv = sync_pipe_add_arg(argv, &argc, capture_opts->capture_comment); + } + + if (capture_opts->multi_files_on) { + if (capture_opts->has_autostop_filesize) { + argv = sync_pipe_add_arg(argv, &argc, "-b"); + g_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) { + argv = sync_pipe_add_arg(argv, &argc, "-b"); + g_snprintf(sfile_duration, ARGV_NUMBER_LEN, "duration:%d",capture_opts->file_duration); + argv = sync_pipe_add_arg(argv, &argc, sfile_duration); + } + + if (capture_opts->has_ring_num_files) { + argv = sync_pipe_add_arg(argv, &argc, "-b"); + g_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->has_autostop_files) { + argv = sync_pipe_add_arg(argv, &argc, "-a"); + g_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) { + argv = sync_pipe_add_arg(argv, &argc, "-a"); + g_snprintf(sautostop_filesize, ARGV_NUMBER_LEN, "filesize:%u",capture_opts->autostop_filesize); + argv = sync_pipe_add_arg(argv, &argc, sautostop_filesize); + } + } + + if (capture_opts->has_autostop_packets) { + argv = sync_pipe_add_arg(argv, &argc, "-c"); + g_snprintf(scount, ARGV_NUMBER_LEN, "%d",capture_opts->autostop_packets); + argv = sync_pipe_add_arg(argv, &argc, scount); + } + + if (capture_opts->has_autostop_duration) { + argv = sync_pipe_add_arg(argv, &argc, "-a"); + g_snprintf(sautostop_duration, ARGV_NUMBER_LEN, "duration:%d",capture_opts->autostop_duration); + argv = sync_pipe_add_arg(argv, &argc, sautostop_duration); + } + + if (capture_opts->group_read_access) { + argv = sync_pipe_add_arg(argv, &argc, "-g"); + } + + for (j = 0; j < capture_opts->ifaces->len; j++) { + interface_opts = g_array_index(capture_opts->ifaces, interface_options, j); + + argv = sync_pipe_add_arg(argv, &argc, "-i"); + argv = sync_pipe_add_arg(argv, &argc, interface_opts.name); + + if (interface_opts.cfilter != NULL && strlen(interface_opts.cfilter) != 0) { + argv = sync_pipe_add_arg(argv, &argc, "-f"); + argv = sync_pipe_add_arg(argv, &argc, interface_opts.cfilter); + } + if (interface_opts.snaplen != WTAP_MAX_PACKET_SIZE) { + argv = sync_pipe_add_arg(argv, &argc, "-s"); + g_snprintf(ssnap, ARGV_NUMBER_LEN, "%d", interface_opts.snaplen); + argv = sync_pipe_add_arg(argv, &argc, ssnap); + } + + if (interface_opts.linktype != -1) { + argv = sync_pipe_add_arg(argv, &argc, "-y"); + argv = sync_pipe_add_arg(argv, &argc, linktype_val_to_name(interface_opts.linktype)); + } + + if (!interface_opts.promisc_mode) { + argv = sync_pipe_add_arg(argv, &argc, "-p"); + } + +#if defined(_WIN32) || defined(HAVE_PCAP_CREATE) + if (interface_opts.buffer_size != DEFAULT_CAPTURE_BUFFER_SIZE) { + argv = sync_pipe_add_arg(argv, &argc, "-B"); + g_snprintf(buffer_size, ARGV_NUMBER_LEN, "%d", interface_opts.buffer_size); + argv = sync_pipe_add_arg(argv, &argc, buffer_size); + } +#endif + +#ifdef HAVE_PCAP_CREATE + if (interface_opts.monitor_mode) { + argv = sync_pipe_add_arg(argv, &argc, "-I"); + } +#endif + +#ifdef HAVE_PCAP_REMOTE + if (interface_opts.datatx_udp) + argv = sync_pipe_add_arg(argv, &argc, "-u"); + + if (!interface_opts.nocap_rpcap) + argv = sync_pipe_add_arg(argv, &argc, "-r"); + + if (interface_opts.auth_type == CAPTURE_AUTH_PWD) { + argv = sync_pipe_add_arg(argv, &argc, "-A"); + g_snprintf(sauth, sizeof(sauth), "%s:%s", + interface_opts.auth_username, + interface_opts.auth_password); + argv = sync_pipe_add_arg(argv, &argc, sauth); + } +#endif + +#ifdef HAVE_PCAP_SETSAMPLING + if (interface_opts.sampling_method != CAPTURE_SAMP_NONE) { + argv = sync_pipe_add_arg(argv, &argc, "-m"); + g_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", + interface_opts.sampling_param); + argv = sync_pipe_add_arg(argv, &argc, ssampling); + } +#endif + } + + /* 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()); + argv = sync_pipe_add_arg(argv, &argc, control_id); +#else + argv = sync_pipe_add_arg(argv, &argc, SIGNAL_PIPE_CTRL_ID_NONE); +#endif +#endif + + if (capture_opts->save_file) { + argv = sync_pipe_add_arg(argv, &argc, "-w"); + argv = sync_pipe_add_arg(argv, &argc, capture_opts->save_file); + } + for (i = 0; i < argc; i++) { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "argv[%d]: %s", i, argv[i]); + } + +#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())); + for (i = 0; i < argc; i++) { + g_free( (gpointer) argv[i]); + } + g_free( (gpointer) argv); + 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())); + for (i = 0; i < argc; i++) { + g_free( (gpointer) argv[i]); + } + g_free( (gpointer) argv); + return FALSE; + } + + /* init STARTUPINFO */ + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); +#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 = GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = sync_pipe_write; + /*si.hStdError = (HANDLE) _get_osfhandle(2);*/ +#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(!CreateProcess(NULL, utf_8to16(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())); + CloseHandle(sync_pipe_read); + CloseHandle(sync_pipe_write); + for (i = 0; i < argc; i++) { + g_free( (gpointer) argv[i]); + } + g_free( (gpointer) argv); + return FALSE; + } + cap_session->fork_child = (int) pi.hProcess; + g_string_free(args, TRUE); + + /* associate the operating system filehandle to a C run-time file handle */ + /* (good file handle infos at: http://www.flounder.com/handles.htm) */ + sync_pipe_read_fd = _open_osfhandle( (long) sync_pipe_read, _O_BINARY); + + /* associate the operating system filehandle to a C run-time file handle */ + cap_session->signal_pipe_write_fd = _open_osfhandle( (long) signal_pipe, _O_BINARY); + +#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)); + for (i = 0; i < argc; i++) { + g_free( (gpointer) argv[i]); + } + g_free(argv); + 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 (fetch_dumpcap_pid && cap_session->fork_child > 0) + fetch_dumpcap_pid(cap_session->fork_child); + + sync_pipe_read_fd = sync_pipe[PIPE_READ]; +#endif + + for (i = 0; i < argc; i++) { + g_free( (gpointer) argv[i]); + } + + /* Parent process - read messages from the child process over the + sync pipe. */ + g_free( (gpointer) argv); /* free up arg array */ + + /* 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 == -1) { + /* 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; + + /* 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, + * messsage_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** argv, int *data_read_fd, + int *message_read_fd, int *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; +#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 + int i; + *fork_child = -1; + *data_read_fd = -1; + *message_read_fd = -1; + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_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())); + for (i = 0; argv[i] != NULL; i++) { + g_free( (gpointer) argv[i]); + } + g_free( (gpointer) argv); + 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())); + CloseHandle(sync_pipe[PIPE_READ]); + CloseHandle(sync_pipe[PIPE_WRITE]); + for (i = 0; argv[i] != NULL; i++) { + g_free( (gpointer) argv[i]); + } + g_free( (gpointer) argv); + return -1; + } + + /* init STARTUPINFO */ + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); +#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; + 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(!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]); + for (i = 0; argv[i] != NULL; i++) { + g_free( (gpointer) argv[i]); + } + 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", g_strerror(errno)); + for (i = 0; argv[i] != NULL; i++) { + g_free( (gpointer) argv[i]); + } + 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", g_strerror(errno)); + ws_close(sync_pipe[PIPE_READ]); + ws_close(sync_pipe[PIPE_WRITE]); + for (i = 0; argv[i] != NULL; i++) { + g_free( (gpointer) argv[i]); + } + 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], 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 (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 + + for (i = 0; argv[i] != NULL; i++) { + g_free( (gpointer) argv[i]); + } + + /* 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", 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; +} + +/* + * Close the pipes we're using to read from dumpcap, and wait for it + * to exit. On success, *msgp is unchanged, and the exit status of + * dumpcap is returned. On failure (which includes "dumpcap exited + * due to being killed by a signal or an exception"), *msgp points + * to an error message for the failure, and -1 is returned. In the + * latter case, *msgp must be freed with g_free(). + */ +static int +sync_pipe_close_command(int *data_read_fd, int *message_read_fd, + int *fork_child, gchar **msgp) +{ + 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, msgp); +} + +/* + * 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_actual(char** argv, gchar **data, gchar **primary_msg, + gchar **secondary_msg, void(*update_cb)(void)) +{ + gchar *msg; + int data_pipe_read_fd, sync_pipe_read_fd, fork_child, ret; + char *wait_msg; + gchar buffer[PIPE_BUF_SIZE+1] = {0}; + ssize_t 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; + ssize_t count; + + ret = sync_pipe_open_command(argv, &data_pipe_read_fd, &sync_pipe_read_fd, + &fork_child, &msg, update_cb); + 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((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 */ + + /* + * 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; +} + +/* centralised logging and timing for sync_pipe_run_command_actual(), +* redirects to sync_pipe_run_command_actual() +*/ +static int +sync_pipe_run_command(char** argv, gchar **data, gchar **primary_msg, + gchar **secondary_msg, void (*update_cb)(void)) +{ + int ret, i; + GTimeVal start_time; + GTimeVal end_time; + float elapsed; + int logging_enabled; + + /* check if logging is actually enabled, otherwise don't expend the CPU generating logging */ + logging_enabled=( (G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_INFO) & G_LOG_LEVEL_MASK & prefs.console_log_level); + if(logging_enabled){ + g_get_current_time(&start_time); + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_INFO, "sync_pipe_run_command() starts"); + for(i=0; argv[i] != 0; i++) { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, " argv[%d]: %s", i, argv[i]); + } + } + /* do the actual sync pipe run command */ + ret=sync_pipe_run_command_actual(argv, data, primary_msg, secondary_msg, update_cb); + + if(logging_enabled){ + g_get_current_time(&end_time); + elapsed = (float) ((end_time.tv_sec - start_time.tv_sec) + + ((end_time.tv_usec - start_time.tv_usec) / 1e6)); + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_INFO, "sync_pipe_run_command() ends, taking %.3fs, result=%d", elapsed, ret); + + } + return ret; +} + + +int +sync_interface_set_80211_chan(const gchar *iface, const char *freq, const gchar *type, + gchar **data, gchar **primary_msg, + gchar **secondary_msg, void (*update_cb)(void)) +{ + int argc, ret; + char **argv; + gchar *opt; + + 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; + } + + argv = sync_pipe_add_arg(argv, &argc, "-i"); + argv = sync_pipe_add_arg(argv, &argc, iface); + + if (type) + opt = g_strdup_printf("%s,%s", freq, type); + else + opt = g_strdup_printf("%s", freq); + + if (!opt) { + *primary_msg = g_strdup("Out of mem."); + *secondary_msg = NULL; + *data = NULL; + 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); + 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, void (*update_cb)(void)) +{ + int argc; + 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, update_cb); +} + +/* + * 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, void (*update_cb)(void)) +{ + int argc; + char **argv; + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_if_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; + } + + /* 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, update_cb); +} + +/* + * 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, void (*update_cb)(void)) +{ + int argc; + char **argv; + int message_read_fd, ret; + char *wait_msg; + gchar buffer[PIPE_BUF_SIZE+1] = {0}; + ssize_t 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"); +#ifdef _WIN32 + create_dummy_signal_pipe(); + 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); + 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((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 */ + + /* + * 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) +{ +#ifndef _WIN32 + /* + * Don't bother waiting for the child. sync_pipe_close_command + * does this for us on Windows. + */ + sync_pipe_kill(*fork_child); +#endif + 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 ssize_t +pipe_read_bytes(int pipe_fd, char *bytes, int required, char **msg) +{ + ssize_t newly; + ssize_t 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, + g_strerror(error)); + *msg = g_strdup_printf("Error reading from sync pipe: %s", + g_strerror(error)); + return newly; + } + + required -= (int)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) { + ssize_t 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 == -1) { + /* error */ + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read from pipe %d: error(%u): %s", pipe_fd, errno, g_strerror(errno)); + return -1; + } else if (bytes[offset] == '\n') { + break; + } + } + + if (offset >= 0) + bytes[offset] = '\0'; + + return offset; +} + + +/* convert header values (indicator and 3-byte length) */ +static void +pipe_convert_header(const guchar *header, int header_len, char *indicator, int *block_len) { + + g_assert(header_len == 4); + + /* convert header values */ + *indicator = header[0]; + *block_len = (header[1]&0xFF)<<16 | (header[2]&0xFF)<<8 | (header[3]&0xFF); +} + +/* read a message from the sending pipe in the standard format + (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, + char **err_msg) +{ + int required; + ssize_t newly; + gchar header[4]; + + /* read header (indicator and 3-byte length) */ + newly = pipe_read_bytes(pipe_fd, header, 4, err_msg); + if(newly != 4) { + if (newly == 0) { + /* + * Immediate EOF; if the capture child exits normally, this + * is an "I'm done" indication, so don't report it as an + * error. + */ + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read %d got an EOF", pipe_fd); + return 0; + } + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read %d failed to read header: %lu", pipe_fd, (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", + (long)newly); + } + return -1; + } + + /* convert header values */ + pipe_convert_header((guchar*)header, 4, indicator, &required); + + /* only indicator with no value? */ + if(required == 0) { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read %d indicator: %c empty value", pipe_fd, *indicator); + return 4; + } + + /* does the data fit into the given buffer? */ + if(required > len) { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read %d length error, required %d > len %d, header: 0x%02x 0x%02x 0x%02x 0x%02x", + pipe_fd, 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 = read(pipe_fd, &msg[sizeof(header)], len-sizeof(header)); + if (newly < 0) { /* error */ + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read from pipe %d: error(%u): %s", pipe_fd, errno, g_strerror(errno)); + } + *err_msg = g_strdup_printf("Unknown message from dumpcap, 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); + if(newly != required) { + if (newly != -1) { + *err_msg = g_strdup_printf("Unknown message from dumpcap, 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 */ + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "read %d ok indicator: %c len: %u msg: %s", pipe_fd, *indicator, + len, msg); + *err_msg = NULL; + return newly + 4; +} + + +/* There's stuff to read from the sync pipe, meaning the child has sent + 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) +{ + capture_session *cap_session = (capture_session *)user_data; + int ret; + char buffer[SP_MAX_MSG_LEN+1] = {0}; + ssize_t nread; + char indicator; + int primary_len; + char *primary_msg; + int secondary_len; + char *secondary_msg; + char *wait_msg, *combined_msg; + int npackets; + + nread = pipe_read_block(source, &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. + + If we got a read error or a bad message, nread is -1 and + primary_msg is set to point to an error message. We don't + have to worry about killing the child; 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. + + If we got an EOF, nread is 0 and primary_msg isn't set. This + is an indication that the capture is finished. */ + ret = sync_pipe_wait_for_child(cap_session->fork_child, &wait_msg); + if(nread == 0) { + /* We got an EOF from the sync pipe. That means that the capture + child exited, and not in the middle of a message; we treat + that as an indication that it's done, and only report an + error if ret is -1, in which case wait_msg is the error + message. */ + if (ret == -1) + primary_msg = wait_msg; + } 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; + } + } + + /* No more child process. */ + cap_session->fork_child = -1; + cap_session->fork_child_status = ret; + +#ifdef _WIN32 + ws_close(cap_session->signal_pipe_write_fd); +#endif + capture_input_closed(cap_session, primary_msg); + g_free(primary_msg); + return FALSE; + } + + /* we got a valid message block from the child, process it */ + switch(indicator) { + case SP_FILE: + if(!capture_input_new_file(cap_session, buffer)) { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_input_cb: 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); + + /* The child has sent us a filename which we couldn't open. + + This could mean that the child is creating and deleting files + (ring buffer mode) faster than we can handle it. + + That should only be the case for very fast file switches; + We can't do much more than telling the child to stop. + (This is the "emergency brake" if the user e.g. wants to + switch files every second). + + This can also happen if the user specified "-", meaning + "standard output", as the capture file. */ + sync_pipe_stop(cap_session); + capture_input_closed(cap_session, NULL); + return FALSE; + } + break; + case SP_PACKET_COUNT: + npackets = atoi(buffer); + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_input_cb: new packets %u", npackets); + capture_input_new_packets(cap_session, npackets); + break; + case SP_ERROR_MSG: + /* convert primary message */ + pipe_convert_header((guchar*)buffer, 4, &indicator, &primary_len); + primary_msg = buffer+4; + /* convert secondary message */ + pipe_convert_header((guchar*)primary_msg + primary_len, 4, &indicator, &secondary_len); + secondary_msg = primary_msg + primary_len + 4; + /* message output */ + capture_input_error_message(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_BAD_FILTER: { + char *ch=NULL; + int indx=0; + + ch = strtok(buffer, ":"); + if (ch) { + indx = (int)strtol(ch, NULL, 10); + ch = strtok(NULL, ":"); + } + capture_input_cfilter_error_message(cap_session, indx, ch); + /* the capture child will close the sync_pipe, nothing to do for now */ + break; + } + case SP_DROPS: + capture_input_drops(cap_session, (guint32)strtoul(buffer, NULL, 10)); + break; + default: + g_assert_not_reached(); + } + + return TRUE; +} + + + +/* + * dumpcap is exiting; wait for it to exit. On success, *msgp is + * unchanged, and the exit status of dumpcap is returned. On + * failure (which includes "dumpcap exited due to being killed by + * a signal or an exception"), *msgp points to an error message + * for the failure, and -1 is returned. In the latter case, *msgp + * must be freed with g_free(). + */ +static int +sync_pipe_wait_for_child(int fork_child, gchar **msgp) +{ + int fork_child_status; + int ret; + GTimeVal start_time; + GTimeVal end_time; + float elapsed; + + /* + * GLIB_CHECK_VERSION(2,28,0) adds g_get_real_time which could minimize or + * replace this + */ + g_get_current_time(&start_time); + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_wait_for_child: wait till child closed"); + g_assert(fork_child != -1); + + *msgp = NULL; /* assume no error */ +#ifdef _WIN32 + if (_cwait(&fork_child_status, fork_child, _WAIT_CHILD) == -1) { + *msgp = g_strdup_printf("Error from cwait(): %s", g_strerror(errno)); + ret = -1; + } else { + /* + * The child exited; return its exit status. Do not treat this as + * an error. + */ + 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", + win32strexception(fork_child_status)); + ret = -1; + } + } +#else + if (waitpid(fork_child, &fork_child_status, 0) != -1) { + if (WIFEXITED(fork_child_status)) { + /* + * The child exited; return its exit status. Do not treat this as + * an error. + */ + 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", + 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", + 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", + fork_child_status); + ret = -1; + } + } else if (errno != ECHILD) { + *msgp = g_strdup_printf("Error from waitpid(): %s", g_strerror(errno)); + ret = -1; + } else { + /* errno == ECHILD ; echld might have already reaped the child */ + ret = fetch_dumpcap_pid ? 0 : -1; + } +#endif + + g_get_current_time(&end_time); + elapsed = (float) ((end_time.tv_sec - start_time.tv_sec) + + ((end_time.tv_usec - start_time.tv_usec) / 1e6)); + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_wait_for_child: capture child closed after %.3fs", elapsed); + return ret; +} + + +#ifndef _WIN32 +/* convert signal to corresponding name */ +static const char * +sync_pipe_signame(int sig) +{ + const char *sigmsg; + static char sigmsg_buf[6+1+3+1]; + + switch (sig) { + + case SIGHUP: + sigmsg = "Hangup"; + break; + + case SIGINT: + sigmsg = "Interrupted"; + break; + + case SIGQUIT: + sigmsg = "Quit"; + break; + + case SIGILL: + sigmsg = "Illegal instruction"; + break; + + case SIGTRAP: + sigmsg = "Trace trap"; + break; + + case SIGABRT: + sigmsg = "Abort"; + break; + + case SIGFPE: + sigmsg = "Arithmetic exception"; + break; + + case SIGKILL: + sigmsg = "Killed"; + break; + + case SIGBUS: + sigmsg = "Bus error"; + break; + + case SIGSEGV: + sigmsg = "Segmentation violation"; + break; + + /* http://metalab.unc.edu/pub/Linux/docs/HOWTO/GCC-HOWTO + Linux is POSIX compliant. These are not POSIX-defined signals --- + ISO/IEC 9945-1:1990 (IEEE Std 1003.1-1990), paragraph B.3.3.1.1 sez: + + ``The signals SIGBUS, SIGEMT, SIGIOT, SIGTRAP, and SIGSYS + were omitted from POSIX.1 because their behavior is + implementation dependent and could not be adequately catego- + rized. Conforming implementations may deliver these sig- + nals, but must document the circumstances under which they + are delivered and note any restrictions concerning their + delivery.'' + + So we only check for SIGSYS on those systems that happen to + implement them (a system can be POSIX-compliant and implement + them, it's just that POSIX doesn't *require* a POSIX-compliant + system to implement them). + */ + +#ifdef SIGSYS + case SIGSYS: + sigmsg = "Bad system call"; + break; +#endif + + case SIGPIPE: + sigmsg = "Broken pipe"; + break; + + case SIGALRM: + sigmsg = "Alarm clock"; + break; + + case SIGTERM: + sigmsg = "Terminated"; + break; + + default: + /* Returning a static buffer is ok in the context we use it here */ + g_snprintf(sigmsg_buf, sizeof sigmsg_buf, "Signal %d", sig); + sigmsg = sigmsg_buf; + break; + } + return sigmsg; +} +#endif + + +#ifdef _WIN32 + +static void create_dummy_signal_pipe() { + gchar *dummy_signal_pipe_name; + + if (dummy_signal_pipe != NULL) return; + + if (!dummy_control_id) { + dummy_control_id = g_strdup_printf("%d.dummy", GetCurrentProcessId()); + } + + /* Create the signal pipe */ + dummy_signal_pipe_name = g_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); +} + +/* tell the child through the signal pipe that we want to quit the capture */ +static void +signal_pipe_capquit_to_child(capture_session *cap_session) +{ + const char quit_msg[] = "QUIT"; + int ret; + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "signal_pipe_capquit_to_child"); + + /* 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);*/ + ret = write(cap_session->signal_pipe_write_fd, quit_msg, sizeof quit_msg); + if(ret == -1) { + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING, + "signal_pipe_capquit_to_child: %d header: error %s", cap_session->signal_pipe_write_fd, g_strerror(errno)); + } +} +#endif + + +/* user wants to stop the capture run */ +void +sync_pipe_stop(capture_session *cap_session) +{ +#ifdef _WIN32 + int count; + DWORD childstatus; + gboolean terminate = TRUE; +#endif + + if (cap_session->fork_child != -1) { +#ifndef _WIN32 + /* send the SIGINT signal to close the capture child gracefully. */ + int sts = kill(cap_session->fork_child, SIGINT); + if (sts != 0) { + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING, + "Sending SIGINT to child failed: %s\n", g_strerror(errno)); + } +#else +#define STOP_SLEEP_TIME 500 /* ms */ +#define STOP_CHECK_TIME 50 + /* 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); + } + + /* Force the issue. */ + if (terminate) { + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING, + "sync_pipe_stop: forcing child to exit"); + sync_pipe_kill(cap_session->fork_child); + } +#endif + } +} + + +/* Wireshark has to exit, force the capture child to close */ +void +sync_pipe_kill(int fork_child) +{ + if (fork_child != -1) { +#ifndef _WIN32 + int sts = kill(fork_child, SIGTERM); /* SIGTERM so it can clean up if necessary */ + if (sts != 0) { + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_WARNING, + "Sending SIGTERM to child failed: %s\n", g_strerror(errno)); + } +#else + /* Remark: This is not the preferred method of closing a process! + * the clean way would be getting the process id of the child process, + * then getting window handle hWnd of that process (using EnumChildWindows), + * and then do a SendMessage(hWnd, WM_CLOSE, 0, 0) + * + * Unfortunately, I don't know how to get the process id from the + * handle. OpenProcess will get an handle (not a window handle) + * from the process ID; it will not get a window handle from the + * process ID. (How could it? A process can have more than one + * window. For that matter, a process might have *no* windows, + * as a process running dumpcap, the normal child process program, + * probably does.) + * + * Hint: GenerateConsoleCtrlEvent() will only work if both processes are + * running in the same console; that's not necessarily the case for + * us, as we might not be running in a console. + * And this also will require to have the process id. + */ + TerminateProcess((HANDLE) (fork_child), 0); +#endif + } +} + +void capture_sync_set_fetch_dumpcap_pid_cb(void(*cb)(int pid)) { + fetch_dumpcap_pid = cb; +} + +#endif /* HAVE_LIBPCAP */ diff --git a/capchild/capture_sync.h b/capchild/capture_sync.h new file mode 100644 index 0000000000..2c4bb639af --- /dev/null +++ b/capchild/capture_sync.h @@ -0,0 +1,138 @@ +/* capture_sync.h + * Synchronisation between Wireshark capture parent and child instances + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +/** @file + * + * Sync mode capture (internal interface). + * + * Will start a new Wireshark child instance which will do the actual capture + * work. + */ + +#ifndef __CAPTURE_SYNC_H__ +#define __CAPTURE_SYNC_H__ + +/** + * Start a new capture session. + * Create a capture child which is doing the real capture work. + * The various capture_input_... functions will be called, if something had + * happened. + * + * Most of the parameters are passed through the global capture_opts. + * + * @param capture_opts the options + * @param cap_session a handle for the capture session + * @param update_cb update screen + * @return TRUE if a capture could be started, FALSE if not + */ +extern gboolean +sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, void(*update_cb)(void)); + +/** User wants to stop capturing, gracefully close the capture child */ +extern void +sync_pipe_stop(capture_session *cap_session); + +/** User wants to stop the program, just kill the child as soon as possible */ +extern void +sync_pipe_kill(int fork_child); + +/** Set wireless channel using dumpcap */ +extern int +sync_interface_set_80211_chan(const gchar *iface, const char *freq, const gchar *type, + gchar **data, gchar **primary_msg, + gchar **secondary_msg, void (*update_cb)(void)); + +/** Get an interface list using dumpcap */ +extern int +sync_interface_list_open(gchar **data, gchar **primary_msg, + gchar **secondary_msg, void (*update_cb)(void)); + +/** Get interface capabilities using dumpcap */ +extern int +sync_if_capabilities_open(const gchar *ifname, gboolean monitor_mode, + gchar **data, gchar **primary_msg, + gchar **secondary_msg, void (*update_cb)(void)); + +/** Start getting interface statistics using dumpcap. */ +extern int +sync_interface_stats_open(int *read_fd, int *fork_child, gchar **msg, void (*update_cb)(void)); + +/** Stop gathering statistics. */ +extern int +sync_interface_stats_close(int *read_fd, int *fork_child, gchar **msg); + +/** Read a line from a pipe, similar to fgets. Non-blocking. */ +extern int +sync_pipe_gets_nonblock(int pipe_fd, char *bytes, int max); + +/* + * Routines supplied by our caller; we call them back to notify them + * of various events. + * + * XXX - this is *really* ugly. We should do this better. + */ + +/** + * Capture child told us we have a new (or the first) capture file. + */ +extern gboolean +capture_input_new_file(capture_session *cap_session, gchar *new_file); + +/** + * Capture child told us we have new packets to read. + */ +extern void +capture_input_new_packets(capture_session *cap_session, int to_read); + +/** + * Capture child told us how many dropped packets it counted. + */ +extern void +capture_input_drops(capture_session *cap_session, guint32 dropped); + +/** + * Capture child told us that an error has occurred while starting the capture. + */ +extern void +capture_input_error_message(capture_session *cap_session, char *error_message, + char *secondary_error_msg); + +/** + * Capture child told us that an error has occurred while parsing a + * capture filter when starting/running the capture. + */ +extern void +capture_input_cfilter_error_message(capture_session *cap_session, guint i, + char *error_message); + +/** + * Capture child closed its side of the pipe, report any error and + * do the required cleanup. + */ +extern void +capture_input_closed(capture_session *cap_session, gchar *msg); + +/* set a callback to be called after fork with the pid of the forked child */ +extern void capture_sync_set_fetch_dumpcap_pid_cb(void(*cb)(int pid)); + +#endif /* capture_sync.h */ |