diff options
33 files changed, 3589 insertions, 18 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index a928f69f91..07a9863783 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -792,6 +792,11 @@ foreach(PLUGIN_DIR ${PLUGIN_SRC_DIRS}) add_subdirectory( ${PLUGIN_DIR} ) endforeach() +if(ENABLE_EXTCAP) + set(HAVE_EXTCAP 1) + set(EXTCAP_DIR "${DATAFILE_DIR}/extcap/") +endif() + add_subdirectory( asn1 EXCLUDE_FROM_ALL ) add_subdirectory( capchild ) add_subdirectory( caputils ) @@ -945,6 +950,15 @@ set(SHARK_COMMON_SRC version_info.c ) +# sources for external capture interfaces +if(ENABLE_EXTCAP) + set(SHARK_COMMON_SRC + ${SHARK_COMMON_SRC} + extcap.c + extcap_parser.c + ) +endif() + set(TSHARK_TAP_SRC ui/cli/tap-afpstat.c ui/cli/tap-ansi_astat.c diff --git a/CMakeOptions.txt b/CMakeOptions.txt index ce0b944719..75e61658aa 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -44,6 +44,7 @@ endif() option(ENABLE_STATIC "Build a static version of Wireshark (not yet working)" OFF) option(ENABLE_ECHLD "Enable echld support" OFF) option(ENABLE_PLUGINS "Build with plugins" ON) +option(ENABLE_EXTCAP "Build with extcap hooks" ON) option(ENABLE_GUIDES "Build Guides" OFF) option(ENABLE_PCAP_NG_DEFAULT "Enable pcap-ng as default file format" ON) diff --git a/Makefile.common b/Makefile.common index 688b6e56b9..a6f501a764 100644 --- a/Makefile.common +++ b/Makefile.common @@ -44,12 +44,16 @@ SHARK_COMMON_SRC = \ cfile.c \ frame_tvbuff.c \ sync_pipe_write.c \ - version_info.c + version_info.c \ + extcap.c \ + extcap_parser.c # corresponding headers SHARK_COMMON_INCLUDES = \ cfile.h \ color.h \ + extcap.h \ + extcap_parser.h \ file.h \ fileset.h \ frame_tvbuff.h \ diff --git a/Makefile.nmake b/Makefile.nmake index 5eb24dcafc..47af7e7028 100644 --- a/Makefile.nmake +++ b/Makefile.nmake @@ -1371,6 +1371,8 @@ install-all: install-generated-files xcopy $(GTK_DIR)\bin\libgobject-2.0-0.dll $(INSTALL_DIR) /d xcopy $(GTK_DIR)\bin\libgthread-2.0-0.dll $(INSTALL_DIR) /d xcopy $(GTK_DIR)\bin\$(INTL_DLL) $(INSTALL_DIR) /d + xcopy $(GTK_DIR)\bin\gspawn-$(WIRESHARK_TARGET_PLATFORM)-helper.exe $(INSTALL_DIR) /d + xcopy $(GTK_DIR)\bin\gspawn-$(WIRESHARK_TARGET_PLATFORM)-helper-console.exe $(INSTALL_DIR) /d !IFDEF ZLIB_DIR xcopy $(ZLIB_DLL) $(INSTALL_DIR) /d !ENDIF diff --git a/capchild/capture_ifinfo.c b/capchild/capture_ifinfo.c index 54e4d4c4fc..0ae807ec53 100644 --- a/capchild/capture_ifinfo.c +++ b/capchild/capture_ifinfo.c @@ -49,6 +49,9 @@ #include "capture_opts.h" #include <capchild/capture_session.h> #include <capchild/capture_sync.h> +#ifdef HAVE_EXTCAP +#include "extcap.h" +#endif #include "log.h" #include <caputils/capture_ifinfo.h> @@ -117,6 +120,10 @@ capture_interface_list(int *err, char **err_str, void (*update_cb)(void)) g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List ..."); *err = 0; +#ifdef HAVE_EXTCAP + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Loading External Capture Interface List ..."); + if_list = extcap_interface_list(err_str); +#endif /* Try to get our interface list */ ret = sync_interface_list_open(&data, &primary_msg, &secondary_msg, update_cb); @@ -143,12 +150,22 @@ capture_interface_list(int *err, char **err_str, void (*update_cb)(void)) g_free(data); for (i = 0; raw_list[i] != NULL; i++) { +#ifdef HAVE_EXTCAP + if_parts = g_strsplit(raw_list[i], "\t", 7); + 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 || + if_parts[6] == NULL) { + g_strfreev(if_parts); + continue; + } +#else 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; } +#endif /* Number followed by the name, e.g "1. eth0" */ name = strchr(if_parts[0], ' '); @@ -184,6 +201,9 @@ capture_interface_list(int *err, char **err_str, void (*update_cb)(void)) } if (strcmp(if_parts[5], "loopback") == 0) if_info->loopback = TRUE; +#ifdef HAVE_EXTCAP + if_info->extcap = g_strdup(if_parts[6]); +#endif g_strfreev(if_parts); g_strfreev(addr_parts); if_list = g_list_append(if_list, if_info); @@ -213,6 +233,17 @@ capture_get_if_capabilities(const gchar *ifname, gboolean monitor_mode, g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface Capabilities ..."); +#ifdef HAVE_EXTCAP + /* see if the interface is from extcap */ + caps = extcap_get_if_dlts(ifname, err_str); + if (caps != NULL) + return caps; + + /* return if the extcap interface generated an error */ + if (err_str != NULL && *err_str != NULL) + return NULL; +#endif /* HAVE_EXTCAP */ + /* Try to get our interface list */ err = sync_if_capabilities_open(ifname, monitor_mode, &data, &primary_msg, &secondary_msg, update_cb); diff --git a/capchild/capture_sync.c b/capchild/capture_sync.c index f744b1a92d..d49a5c8080 100644 --- a/capchild/capture_sync.c +++ b/capchild/capture_sync.c @@ -98,6 +98,9 @@ #include <wsutil/filesystem.h> #include <wsutil/file_util.h> #include <wsutil/report_err.h> +#ifdef HAVE_EXTCAP +#include "extcap.h" +#endif #include "log.h" #ifdef _WIN32 @@ -391,6 +394,14 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, voi cap_session->fork_child = -1; +#ifdef HAVE_EXTCAP + if (!extcaps_init_initerfaces(capture_opts)) { + report_failure("Unable to init extcaps. (tmp fifo already exists?)"); + return FALSE; + } + +#endif + argv = init_pipe_args(&argc); if (!argv) { /* We don't know where to find dumpcap. */ @@ -463,7 +474,12 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, voi 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); +#ifdef HAVE_EXTCAP + if (interface_opts.extcap_fifo != NULL) + argv = sync_pipe_add_arg(argv, &argc, interface_opts.extcap_fifo); + else +#endif + 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"); @@ -476,8 +492,12 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, voi } 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)); + const char *linktype = linktype_val_to_name(interface_opts.linktype); + if ( linktype != NULL ) + { + argv = sync_pipe_add_arg(argv, &argc, "-y"); + argv = sync_pipe_add_arg(argv, &argc, linktype); + } } if (!interface_opts.promisc_mode) { @@ -487,6 +507,8 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, voi #if defined(_WIN32) || defined(HAVE_PCAP_CREATE) if (interface_opts.buffer_size != DEFAULT_CAPTURE_BUFFER_SIZE) { argv = sync_pipe_add_arg(argv, &argc, "-B"); + if(interface_opts.buffer_size == 0x00) + interface_opts.buffer_size = DEFAULT_CAPTURE_BUFFER_SIZE; g_snprintf(buffer_size, ARGV_NUMBER_LEN, "%d", interface_opts.buffer_size); argv = sync_pipe_add_arg(argv, &argc, buffer_size); } @@ -591,7 +613,20 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, voi #else si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; /* this hides the console window */ - si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); +#if defined(_WIN32) + /* needs first a check if NULL * + * otherwise wouldnt work with non extcap interfaces */ + if(interface_opts.extcap_fifo != NULL) + { + if(strncmp(interface_opts.extcap_fifo,"\\\\.\\pipe\\",9)== 0) + { + si.hStdInput = extcap_get_win32_handle(); + } + } + else +#endif + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); si.hStdError = sync_pipe_write; /*si.hStdError = (HANDLE) _get_osfhandle(2);*/ @@ -805,7 +840,8 @@ sync_pipe_open_command(char** argv, int *data_read_fd, #else si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE; /* this hides the console window */ - si.hStdInput = NULL; + si.hStdInput = NULL; /* handle for named pipe*/ + si.hStdOutput = data_pipe[PIPE_WRITE]; si.hStdError = sync_pipe[PIPE_WRITE]; #endif @@ -1741,6 +1777,10 @@ sync_pipe_input_cb(gint source, gpointer user_data) #ifdef _WIN32 ws_close(cap_session->signal_pipe_write_fd); #endif +#ifdef HAVE_EXTCAP + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_input_cb: cleaning extcap pipe"); + extcap_cleanup(cap_session->capture_opts); +#endif capture_input_closed(cap_session, primary_msg); g_free(primary_msg); return FALSE; @@ -2047,7 +2087,6 @@ sync_pipe_stop(capture_session *cap_session) DWORD childstatus; gboolean terminate = TRUE; #endif - if (cap_session->fork_child != -1) { #ifndef _WIN32 /* send the SIGINT signal to close the capture child gracefully. */ @@ -2116,6 +2155,7 @@ sync_pipe_kill(int fork_child) * And this also will require to have the process id. */ TerminateProcess((HANDLE) (fork_child), 0); + #endif } } diff --git a/capture_opts.c b/capture_opts.c index 313471f1b9..9d5620c0f2 100644 --- a/capture_opts.c +++ b/capture_opts.c @@ -66,6 +66,12 @@ capture_opts_init(capture_options *capture_opts) capture_opts->default_options.linktype = -1; /* use interface default */ capture_opts->default_options.promisc_mode = TRUE; capture_opts->default_options.if_type = IF_WIRED; +#ifdef HAVE_EXTCAP + capture_opts->default_options.extcap = NULL; + capture_opts->default_options.extcap_fifo = NULL; + capture_opts->default_options.extcap_args = NULL; + capture_opts->default_options.extcap_pid = (GPid)-1; +#endif #if defined(_WIN32) || defined(HAVE_PCAP_CREATE) capture_opts->default_options.buffer_size = DEFAULT_CAPTURE_BUFFER_SIZE; #endif @@ -138,6 +144,11 @@ capture_opts_log(const char *log_domain, GLogLevelFlags log_level, capture_optio g_log(log_domain, log_level, "Snap length[%02d] (%u) : %d", i, interface_opts.has_snaplen, interface_opts.snaplen); g_log(log_domain, log_level, "Link Type[%02d] : %d", i, interface_opts.linktype); g_log(log_domain, log_level, "Promiscuous Mode[%02d]: %s", i, interface_opts.promisc_mode?"TRUE":"FALSE"); +#ifdef HAVE_EXTCAP + g_log(log_domain, log_level, "Extcap[%02d] : %s", i, interface_opts.extcap ? interface_opts.extcap : "(unspecified)"); + g_log(log_domain, log_level, "Extcap FIFO[%02d] : %s", i, interface_opts.extcap_fifo ? interface_opts.extcap_fifo : "(unspecified)"); + g_log(log_domain, log_level, "Extcap PID[%02d] : %d", i, interface_opts.extcap_pid); +#endif #if defined(_WIN32) || defined(HAVE_PCAP_CREATE) g_log(log_domain, log_level, "Buffer size[%02d] : %d (MB)", i, interface_opts.buffer_size); #endif @@ -174,6 +185,10 @@ capture_opts_log(const char *log_domain, GLogLevelFlags log_level, capture_optio g_log(log_domain, log_level, "Snap length[df] (%u) : %d", capture_opts->default_options.has_snaplen, capture_opts->default_options.snaplen); g_log(log_domain, log_level, "Link Type[df] : %d", capture_opts->default_options.linktype); g_log(log_domain, log_level, "Promiscuous Mode[df]: %s", capture_opts->default_options.promisc_mode?"TRUE":"FALSE"); +#ifdef HAVE_EXTCAP + g_log(log_domain, log_level, "Extcap[df] : %s", capture_opts->default_options.extcap ? capture_opts->default_options.extcap : "(unspecified)"); + g_log(log_domain, log_level, "Extcap FIFO[df] : %s", capture_opts->default_options.extcap_fifo ? capture_opts->default_options.extcap_fifo : "(unspecified)"); +#endif #if defined(_WIN32) || defined(HAVE_PCAP_CREATE) g_log(log_domain, log_level, "Buffer size[df] : %d (MB)", capture_opts->default_options.buffer_size); #endif @@ -591,6 +606,12 @@ capture_opts_add_iface_opt(capture_options *capture_opts, const char *optarg_str interface_opts.linktype = capture_opts->default_options.linktype; interface_opts.promisc_mode = capture_opts->default_options.promisc_mode; interface_opts.if_type = capture_opts->default_options.if_type; +#ifdef HAVE_EXTCAP + interface_opts.extcap = g_strdup(capture_opts->default_options.extcap); + interface_opts.extcap_fifo = g_strdup(capture_opts->default_options.extcap_fifo); + interface_opts.extcap_args = NULL; + interface_opts.extcap_pid = (GPid)-1; +#endif #if defined(_WIN32) || defined(HAVE_PCAP_CREATE) interface_opts.buffer_size = capture_opts->default_options.buffer_size; #endif @@ -1020,6 +1041,14 @@ capture_opts_del_iface(capture_options *capture_opts, guint if_index) if (interface_opts.console_display_name != NULL) g_free(interface_opts.console_display_name); g_free(interface_opts.cfilter); +#ifdef HAVE_EXTCAP + g_free(interface_opts.extcap); + g_free(interface_opts.extcap_fifo); + if (interface_opts.extcap_args) + g_hash_table_unref(interface_opts.extcap_args); + if (interface_opts.extcap_pid > 0) + g_spawn_close_pid(interface_opts.extcap_pid); +#endif #ifdef HAVE_PCAP_REMOTE if (interface_opts.src_type == CAPTURE_IFREMOTE) { g_free(interface_opts.remote_host); @@ -1061,6 +1090,13 @@ collect_ifaces(capture_options *capture_opts) interface_opts.has_snaplen = device.has_snaplen; interface_opts.promisc_mode = device.pmode; interface_opts.if_type = device.if_info.type; +#ifdef HAVE_EXTCAP + interface_opts.extcap = g_strdup(device.if_info.extcap); + interface_opts.extcap_fifo = NULL; + interface_opts.extcap_args = device.external_cap_args_settings; + interface_opts.extcap_pid = (GPid)-1; + g_hash_table_ref(interface_opts.extcap_args); +#endif #if defined(_WIN32) || defined(HAVE_PCAP_CREATE) interface_opts.buffer_size = device.buffer; #endif diff --git a/capture_opts.h b/capture_opts.h index 2097e2ab4d..9410db280c 100644 --- a/capture_opts.h +++ b/capture_opts.h @@ -195,6 +195,10 @@ typedef struct interface_tag { gboolean selected; gboolean hidden; gboolean locked; +#ifdef HAVE_EXTCAP + /* External capture cached data */ + GHashTable *external_cap_args_settings; +#endif } interface_t; typedef struct link_row_tag { @@ -212,6 +216,12 @@ typedef struct interface_options_tag { int linktype; gboolean promisc_mode; interface_type if_type; +#ifdef HAVE_EXTCAP + gchar *extcap; + gchar *extcap_fifo; + GHashTable *extcap_args; + GPid extcap_pid; +#endif #if defined(_WIN32) || defined(HAVE_PCAP_CREATE) int buffer_size; #endif diff --git a/caputils/capture-pcap-util.c b/caputils/capture-pcap-util.c index 98054c4a0f..8755fcefaf 100644 --- a/caputils/capture-pcap-util.c +++ b/caputils/capture-pcap-util.c @@ -246,6 +246,9 @@ if_info_new(const char *name, const char *description, gboolean loopback) if_info->friendly_name = NULL; /* default - unknown */ if_info->vendor_description = NULL; if_info->type = IF_WIRED; /* default */ +#ifdef HAVE_EXTCAP + if_info->extcap = g_strdup(""); +#endif #ifdef _WIN32 /* * Get the interface type. @@ -506,6 +509,9 @@ free_if_cb(gpointer data, gpointer user_data _U_) g_free(if_info->name); g_free(if_info->friendly_name); g_free(if_info->vendor_description); +#ifdef HAVE_EXTCAP + g_free(if_info->extcap); +#endif g_slist_foreach(if_info->addrs, free_if_info_addr_cb, NULL); g_slist_free(if_info->addrs); diff --git a/caputils/capture_ifinfo.h b/caputils/capture_ifinfo.h index 2353f06fed..4293b816b1 100644 --- a/caputils/capture_ifinfo.h +++ b/caputils/capture_ifinfo.h @@ -38,6 +38,9 @@ typedef enum { IF_WIRELESS, IF_DIALUP, IF_USB, +#ifdef HAVE_EXTCAP + IF_EXTCAP, +#endif IF_VIRTUAL } interface_type; @@ -56,6 +59,9 @@ typedef struct { GSList *addrs; /* containing address values of if_addr_t */ interface_type type; /* type of interface */ gboolean loopback; /* TRUE if loopback, FALSE otherwise */ +#ifdef HAVE_EXTCAP + char *extcap; /* extcap arguments, which present the data to call the extcap interface */ +#endif } if_info_t; /* diff --git a/cmakeconfig.h.in b/cmakeconfig.h.in index 6c4df84143..7d0f7dc252 100644 --- a/cmakeconfig.h.in +++ b/cmakeconfig.h.in @@ -18,6 +18,12 @@ /* Build wsutil with SIMD optimization */ #cmakedefine HAVE_SSE4_2 1 +/* Directory where extcap hooks reside */ +#define EXTCAP_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/${CPACK_PACKAGE_NAME}/extcap/" + +/* Define to 1 if we want to enable extcap */ +#cmakedefine HAVE_EXTCAP 1 + /* Define to 1 if we want to enable plugins */ #cmakedefine HAVE_PLUGINS 1 diff --git a/config.h.win32 b/config.h.win32 index b19b7dc7b6..a5cdf7435d 100644 --- a/config.h.win32 +++ b/config.h.win32 @@ -39,6 +39,7 @@ #define YYTEXT_POINTER 1 #define HAVE_PLUGINS 1 +#define HAVE_EXTCAP 1 /* #undef HAVE_SA_LEN */ diff --git a/configure.ac b/configure.ac index 044728202e..ff9c4f7fc5 100644 --- a/configure.ac +++ b/configure.ac @@ -2699,6 +2699,32 @@ CPPFLAGS="$CPPFLAGS '-DPLUGIN_INSTALL_DIR=\"\$(plugindir)\"'" PLUGIN_LIBS="" AC_SUBST(PLUGIN_LIBS) + +dnl Use extcap by default +extcapdir='${datadir}/wireshark/extcap/' +AC_ARG_WITH(extcap, + AC_HELP_STRING( [--with-extcap@<:@=DIR@:>@], + [use extcap for external capture sources (installed in DIR, if supplied) @<:@default=yes, if possible@:>@]), +[ + if test "x$withval" = "xno"; then + have_extcap=no + elif test "x$withval" = "xyes"; then + have_extcap=yes + elif test "x$withval" != "xyes"; then + have_extcap=yes + extcapdir ="$withval" + fi +],[ + have_extcap=yes +]) +AM_CONDITIONAL(HAVE_EXTCAP, test "x$have_extcap" = "xyes") +if test "x$have_extcap" = "xyes" +then + AC_DEFINE(HAVE_EXTCAP, 1, [Define if external capture sources should be enabled]) + AC_DEFINE_UNQUOTED(EXTCAP_DIR,"$extcapdir", [Directory for extcap plugins]) +fi +AC_SUBST(extcapdir) + # # Check if (emem) memory allocations must be 8-byte aligned. # I haven't been able to write C code that reliably makes that determination @@ -3079,6 +3105,7 @@ echo " Install dumpcap with capabilities : $setcap_message" echo " Install dumpcap setuid : $setuid_message" echo " Use dumpcap group : $dumpcap_group_message" echo " Use plugins : $have_plugins" +echo " Use external capture sources : $have_extcap" echo " Use Lua library : $lua_message" echo " Build rtp_player : $portaudio_message" echo " Build profile binaries : $enable_profile_build" diff --git a/doc/extcap.pod b/doc/extcap.pod new file mode 100644 index 0000000000..c6cc9bb1bc --- /dev/null +++ b/doc/extcap.pod @@ -0,0 +1,78 @@ + +=head1 NAME + +extcap - Extcap grammar elements + +=head1 SYNOPSIS + +Suggested config grammar elements: +arg (options) argument for CLI calling +number Reference # of argument for other values, display order +call Literal argument to call (--call=...) +display Displayed name +default Default value, in proper form for type +range Range of valid values for UI checking (min,max) in proper form +type Argument type for UI filtering for raw, or UI type for selector: + integer + unsigned + long (may include scientific / special notation) +float +menu (display popup menu in UI) +selector (display selector table, all values as strings) +boolean (display checkbox) +radio (display group of radio buttons with provided values, all values as strings) + +value (options) Values for argument selection + arg Argument # this value applies to +value Passed value +display Displayed value +default Boolean (true if default, all others ignored, ie default=true) + +flag (options) external-capture level flags + dedicated Bypass dumpcap & mux for high speed + failure Failure message + + +Possible grammar example: + +arg {number=0}{call=channel}{display=Wi-Fi Channel}{type=integer} +arg {number=1}{call=chanflags}{display=Channel Flags}{type=radio} +arg {number=2}{call=interface}{display=Interface}{type=selector} +value {arg=0}{range=1,11} +value {arg=1}{value=ht40p}{display=HT40+} +value {arg=1}{value=ht40m}{display=HT40-} +value {arg=1}{value=ht20}{display=HT20} +value {arg=2}{value=wlan0}{display=wlan0} + +Example 2 +arg {number=0}{call=usbdevice}{USB Device}{type=selector} +value {arg=0}{call=/dev/sysfs/usb/foo/123}{display=Ubertooth One sn 1234} +value {arg=0}{call=”/dev/sysfs/usb/foo/456}{display=Ubertooth One sn 8901} + +Example 3 +arg {number=0}{call=usbdevice}{USB Device}{type=selector} +flag {failure=Permission denied opening Ubertooth device} + + +Security awareness: + +- Users running wireshark as root, we can’t save you +- Dumpcap retains suid/setgid and group+x permissions to allow users in wireshark group only +- Third-party capture programs run w/ whatever privs they’re installed with +- If an attacker can write to a system binary directory, we’re game over anyhow +- Don’t let wireshark be told to look for capture binaries somewhere else? + +Notes: +- daemonized dumpcap? +- multiuser? +- sync_pipe.h commands +- expand pipe commands to have status notifications, etc? +- Wireshark->dumpcap options for channel control, etc? + +TODO +define grammar +write grammar to HTML mockup +sketch interface with dumpcap +launch external-pcap from wireshark, bypass dumpcap +launch external-pcap from wireshark, hand fd to dumpcap +extract netif capture as first cap source
\ No newline at end of file diff --git a/doc/extcap_example.py b/doc/extcap_example.py new file mode 100755 index 0000000000..02524bc9e4 --- /dev/null +++ b/doc/extcap_example.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python + +""" +This is a generic example, which produces pcap packages every n seconds, and +is configurable via extcap options. + +@note +{ +To use this script on Windows, please generate an extcap_example.bat inside +the extcap folder, with the following content: + +------- +@echo off +<Path to python interpreter> <Path to script file> $* +------- + +Windows is not able to execute Python scripts directly, which also goes for all +other script-based formates beside VBScript +} + +""" + +import os +import sys +import signal +import re +import argparse +import time +import struct +import binascii +from threading import Thread + +ERROR_USAGE = 0 +ERROR_ARG = 1 +ERROR_INTERFACE = 2 +ERROR_FIFO = 3 + +doExit = False +globalinterface = 0 + +def signalHandler(signal, frame): + global doExit + doExit = True + +#### EXTCAP FUNCTIONALITY + +"""@brief Extcap configuration +This method prints the extcap configuration, which will be picked up by the +interface in Wireshark to present a interface specific configuration for +this extcap plugin +""" +def extcap_config(interface): + args = [] + values = [] + + args.append ( (0, '--delay', 'Time delay', 'Time delay between packages', 'integer', '{range=1,15}') ) + args.append ( (1, '--message', 'Message', 'Package message content', 'string', '') ) + args.append ( (2, '--verify', 'Verify', 'Verify package content', 'boolflag', '') ) + args.append ( (3, '--remote', 'Remote Channel', 'Remote Channel Selector', 'selector', '')) + + values.append ( (3, "if1", "Remote1", "true" ) ) + values.append ( (3, "if2", "Remote2", "false" ) ) + + for arg in args: + print ("arg {number=%d}{call=%s}{display=%s}{tooltip=%s}{type=%s}%s" % arg) + + for value in values: + print ("value {arg=%d}{value=%s}{display=%s}{default=%s}" % value) + + +def extcap_interfaces(): + print ("interface {value=example1}{display=Example interface usage for extcap}") + +def extcap_dlts(interface): + if ( interface == 'example1' ): + print ("dlt {number=147}{name=USER0}{display=Demo Implementation for Extcap}") + +""" + +### FAKE DATA GENERATOR + +Extcap capture routine + This routine simulates a capture by any kind of user defined device. The parameters + are user specified and must be handled by the extcap. + + The data captured inside this routine is fake, so change this routine to present + your own input data, or call your own capture program via Popen for example. See + + for more details. + +""" +def unsigned(n): + return int(n) & 0xFFFFFFFF + +def append_bytes(ba, blist): + for c in range(0, len(blist)): + ba.append(blist[c]) + return ba + +def pcap_fake_header(): + + header = bytearray() + header = append_bytes(header, struct.pack('<L', int ('a1b2c3d4', 16) )) + header = append_bytes(header, struct.pack('<H', unsigned(2)) ) # Pcap Major Version + header = append_bytes(header, struct.pack('<H', unsigned(4)) ) # Pcap Minor Version + header = append_bytes(header, struct.pack('<I', int(0))) # Timezone + header = append_bytes(header, struct.pack('<I', int(0))) # Accurancy of timestamps + header = append_bytes(header, struct.pack('<L', int ('0000ffff', 16) )) # Max Length of capture frame + header = append_bytes(header, struct.pack('<L', unsigned(1))) # Ethernet + return header + +# Calculates and returns the IP checksum based on the given IP Header +def ip_checksum(iph): + #split into bytes + words = splitN(''.join(iph.split()),4) + csum = 0; + for word in words: + csum += int(word, base=16) + csum += (csum >> 16) + csum = csum & 0xFFFF ^ 0xFFFF + return csum + +def pcap_fake_package ( message ): + + pcap = bytearray() + #length = 14 bytes [ eth ] + 20 bytes [ ip ] + messagelength + + caplength = len(message) + 14 + 20 + timestamp = int(time.time()) + + pcap = append_bytes(pcap, struct.pack('<L', unsigned(timestamp) ) ) # timestamp seconds + pcap = append_bytes(pcap, struct.pack('<L', 0x00 ) ) # timestamp nanoseconds + pcap = append_bytes(pcap, struct.pack('<L', unsigned(caplength) ) ) # length captured + pcap = append_bytes(pcap, struct.pack('<L', unsigned(caplength) ) ) # length in frame + +# ETH + pcap = append_bytes(pcap, struct.pack('h', 0 )) # source mac + pcap = append_bytes(pcap, struct.pack('h', 0 )) # source mac + pcap = append_bytes(pcap, struct.pack('h', 0 )) # source mac + pcap = append_bytes(pcap, struct.pack('h', 0 )) # dest mac + pcap = append_bytes(pcap, struct.pack('h', 0 )) # dest mac + pcap = append_bytes(pcap, struct.pack('h', 0 )) # dest mac + pcap = append_bytes(pcap, struct.pack('<h', unsigned(8) )) # protocol (ip) + +# IP + pcap = append_bytes(pcap, struct.pack('b', int ( '45', 16) )) # IP version + pcap = append_bytes(pcap, struct.pack('b', int ( '0', 16) )) # + pcap = append_bytes(pcap, struct.pack('>H', unsigned(len(message)+20) )) # length of data + payload + pcap = append_bytes(pcap, struct.pack('<H', int ( '0', 16) )) # Identification + pcap = append_bytes(pcap, struct.pack('b', int ( '40', 16) )) # Don't fragment + pcap = append_bytes(pcap, struct.pack('b', int ( '0', 16) )) # Fragment Offset + pcap = append_bytes(pcap, struct.pack('b', int ( '40', 16) )) + pcap = append_bytes(pcap, struct.pack('B', 0xFE )) # Protocol (2 = unspecified) + pcap = append_bytes(pcap, struct.pack('<H', int ( '0000', 16) )) # Checksum + pcap = append_bytes(pcap, struct.pack('>L', int ( '7F000001', 16) )) # Source IP + pcap = append_bytes(pcap, struct.pack('>L', int ( '7F000001', 16) )) # Dest IP + + pcap = append_bytes(pcap, message) + return pcap + +def extcap_capture(interface, fifo, delay, verify, message, remote): + global doExit + + signal.signal(signal.SIGINT, signalHandler) + signal.signal(signal.SIGTERM , signalHandler) + + tdelay = delay if delay != 0 else 5 + + try: + os.stat(fifo) + except OSError: + doExit = True + print ( "Fifo does not exist, exiting!" ) + + fh = open(fifo, 'w+b', 0 ) + fh.write (pcap_fake_header()) + + while doExit == False: + out = str( "%s|%04X%s|%s" % ( remote.strip(), len(message), message, verify ) ) + try: + fh.write (pcap_fake_package(out)) + time.sleep(tdelay) + except IOError: + doExit = True + + fh.close() + +#### + +def usage(): + print ( "Usage: %s <--extcap-interfaces | --extcap-dlts | --extcap-interface | --extcap-config | --capture | --fifo>" % sys.argv[0] ) + +if __name__ == '__main__': + interface = "" + + # Capture options + delay = 0 + message = "" + + parser = argparse.ArgumentParser( + prog="Extcap Example", + description="Extcap example program for python" + ) + + # Extcap Arguments + parser.add_argument("--capture", help="Start the capture routine", action="store_true" ) + parser.add_argument("--extcap-interfaces", help="Provide a list of interfaces to capture from", action="store_true") + parser.add_argument("--extcap-interface", help="Provide the interface to capture from") + parser.add_argument("--extcap-dlts", help="Provide a list of dlts for the given interface", action="store_true") + parser.add_argument("--extcap-config", help="Provide a list of configurations for the given interface", action="store_true") + parser.add_argument("--fifo", help="Use together with capture to provide the fifo to dump data to") + + # Interface Arguments + parser.add_argument("--verify", help="Demonstrates a verification bool flag", action="store_true" ) + parser.add_argument("--delay", help="Demonstrates an integer variable", type=int, default=0, choices=[0, 1, 2, 3, 4, 5] ) + parser.add_argument("--remote", help="Demonstrates a selector choice", default="if1", choices=["if1", "if2"] ) + parser.add_argument("--message", help="Demonstrates string variable", nargs='?', default="" ) + + args = parser.parse_args() + if ( len(sys.argv) <= 1 ): + parser.exit("No arguments given!") + + if ( args.extcap_interfaces == False and args.extcap_interface == None ): + parser.exit("An interface must be provided or the selection must be displayed") + + if ( args.extcap_interfaces == True or args.extcap_interface == None ): + extcap_interfaces() + sys.exit(0) + + m = re.match ( 'example(\d+)', args.extcap_interface ) + if not m: + sys.exit(ERROR_INTERFACE) + interface = m.group(1) + + message = args.message + if ( args.message == None or len(args.message) == 0 ): + message = "Extcap Test" + + if args.extcap_config: + extcap_config(interface) + elif args.extcap_dlts: + extcap_dlts(interface) + elif args.capture: + if args.fifo is None: + sys.exit(ERROR_FIFO) + extcap_capture(interface, args.fifo, args.delay, args.verify, message, args.remote) + else: + usage() + sys.exit(ERROR_USAGE) @@ -193,7 +193,7 @@ enable_kernel_bpf_jit_compiler(void) ssize_t written _U_; static const char file[] = "/proc/sys/net/core/bpf_jit_enable"; - fd = open(file, O_WRONLY); + fd = ws_open(file, O_WRONLY); if (fd < 0) return; @@ -1373,7 +1373,9 @@ print_machine_readable_interfaces(GList *if_list) printf("\tloopback"); else printf("\tnetwork"); - +#ifdef HAVE_EXTCAP + printf("\t%s", if_info->extcap); +#endif printf("\n"); } } @@ -1864,14 +1866,14 @@ cap_open_socket(char *pipename, pcap_options *pcap_opts, char *errmsg, int errms goto fail_invalid; } - strncpy(buf, sockname, len); + g_snprintf ( buf,(gulong)len + 1, "%s", sockname ); buf[len] = '\0'; if (inet_pton(AF_INET, buf, &sa.sin_addr) <= 0) { goto fail_invalid; } sa.sin_family = AF_INET; - sa.sin_port = htons((u_short)port); + sa.sin_port = g_htons((u_short)port); if (((fd = (int)socket(AF_INET, SOCK_STREAM, 0)) < 0) || (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)) { @@ -1893,7 +1895,7 @@ cap_open_socket(char *pipename, pcap_options *pcap_opts, char *errmsg, int errms if (errorText) LocalFree(errorText); #else - " %d: %s", errno, strerror(errno)); + " %d: %s", errno, g_strerror(errno)); #endif pcap_opts->cap_pipe_err = PIPERR; @@ -1947,12 +1949,12 @@ cap_pipe_open_live(char *pipename, #else /* _WIN32 */ char *pncopy, *pos; wchar_t *err_str; + interface_options interface_opts; #endif ssize_t b; int fd = -1, sel_ret; size_t bytes_read; guint32 magic = 0; - pcap_opts->cap_pipe_fd = -1; #ifdef _WIN32 pcap_opts->cap_pipe_h = INVALID_HANDLE_VALUE; @@ -2083,10 +2085,16 @@ cap_pipe_open_live(char *pipename, return; } + interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, 0); + /* Wait for the pipe to appear */ while (1) { - pcap_opts->cap_pipe_h = CreateFile(utf_8to16(pipename), GENERIC_READ, 0, NULL, - OPEN_EXISTING, 0, NULL); + + if(strncmp(interface_opts.name,"\\\\.\\pipe\\",9)== 0) + pcap_opts->cap_pipe_h = GetStdHandle(STD_INPUT_HANDLE); + else + pcap_opts->cap_pipe_h = CreateFile(utf_8to16(pipename), GENERIC_READ, 0, NULL, + OPEN_EXISTING, 0, NULL); if (pcap_opts->cap_pipe_h != INVALID_HANDLE_VALUE) break; @@ -2105,7 +2113,7 @@ cap_pipe_open_live(char *pipename, if (!WaitNamedPipe(utf_8to16(pipename), 30 * 1000)) { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL); + NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL); g_snprintf(errmsg, errmsgl, "The capture session on \"%s\" timed out during " "pipe open: %s (error %d)", @@ -4526,7 +4534,6 @@ main(int argc, char *argv[]) /* Set the initial values in the capture options. This might be overwritten by the command line parameters. */ capture_opts_init(&global_capture_opts); - /* We always save to a file - if no file was specified, we save to a temporary file. */ global_capture_opts.saving_to_file = TRUE; @@ -4857,6 +4864,7 @@ main(int argc, char *argv[]) interface_options interface_opts; interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, ii); + caps = get_if_capabilities(interface_opts.name, interface_opts.monitor_mode, &err_str); if (caps == NULL) { @@ -4900,7 +4908,6 @@ main(int argc, char *argv[]) fflush(stderr); /* Now start the capture. */ - if (capture_loop_start(&global_capture_opts, &stats_known, &stats) == TRUE) { /* capture ok */ exit_main(0); diff --git a/extcap.c b/extcap.c new file mode 100644 index 0000000000..a676fc7233 --- /dev/null +++ b/extcap.c @@ -0,0 +1,654 @@ +/* extcap.h + * + * Routines for extcap external capture + * Copyright 2013, Mike Ryan <mikeryan@lacklustre.net> + * + * 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" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef WIN32 +#include <windows.h> +#include <process.h> +#include <time.h> +#else +/* Include for unlink */ +#include <unistd.h> +#endif + +#include <glib.h> +#include <log.h> + +#include <wsutil/file_util.h> +#include <wsutil/filesystem.h> +#include <wsutil/tempfile.h> + +#include "capture_opts.h" + +#ifdef HAVE_EXTCAP + +#include "extcap.h" +#include "extcap_parser.h" + +#ifdef _WIN32 +static HANDLE pipe_h = NULL; +#endif + +/* internal container, for all the extcap interfaces that have been found. + * will be resetted by every call to extcap_interface_list() and is being + * used in extcap_get_if_* as well as extcaps_init_initerfaces to ensure, + * that only extcap interfaces are being given to underlying extcap programs + */ +static GHashTable *ifaces = NULL; + +/* Prefix for the pipe interfaces */ +#define EXTCAP_PIPE_PREFIX "wireshark_extcap" + +/* Callback definition for extcap_foreach */ +typedef gboolean (*extcap_cb_t)(const gchar *extcap, gchar *output, void *data, + gchar **err_str); + +/* #define ARG_DEBUG */ +#if ARG_DEBUG +static void extcap_debug_arguments ( extcap_arg *arg_iter ); +#endif + +static gboolean +extcap_if_exists(const char *ifname) +{ + if ( ifname != NULL ) + { + if ( ifaces != NULL ) + { + if ( g_hash_table_size(ifaces) > 0 ) + { + if ( g_hash_table_lookup(ifaces, (const gchar *)ifname) != NULL ) + { + return TRUE; + } + } + } + } + return FALSE; +} + +static gboolean +extcap_if_exists_for_extcap(const char *ifname, const char *extcap) +{ + gchar * entry = NULL; + + if ( extcap_if_exists(ifname) ) + { + if ( ( entry = (gchar *)g_hash_table_lookup(ifaces, (const gchar *)ifname) ) != NULL ) + { + if ( strcmp(entry, extcap) == 0 ) + return TRUE; + } + } + + return FALSE; +} + +static gchar * +extcap_if_executable(const char *ifname) +{ + if ( extcap_if_exists(ifname) ) + return (gchar *)g_hash_table_lookup(ifaces, (const gchar *)ifname); + + return (gchar *)NULL; +} + +static void +extcap_if_cleanup(void) +{ + if ( ifaces == NULL ) + ifaces = g_hash_table_new(g_str_hash, g_str_equal); + + g_hash_table_remove_all(ifaces); +} + +static void +extcap_if_add(gchar *ifname, gchar *extcap) +{ + if ( !g_hash_table_contains(ifaces, ifname) ) + g_hash_table_insert(ifaces, ifname, extcap); +} + +static void extcap_foreach(gint argc, gchar **args, extcap_cb_t cb, + void *cb_data, char **err_str, const char * ifname _U_) { + const char *dirname = get_extcap_dir(); + GDir *dir; + const gchar *file; + gboolean keep_going; + gchar **argv; + + keep_going = TRUE; + + argv = (gchar **) g_malloc0(sizeof(gchar *) * (argc + 2)); + + if ((dir = g_dir_open(dirname, 0, NULL)) != NULL) { +#ifdef WIN32 + dirname = g_strescape(dirname,NULL); +#endif + while (keep_going && (file = g_dir_read_name(dir)) != NULL ) { + GString *extcap_string = NULL; + gchar *extcap = NULL; + gchar *command_output = NULL; + gboolean status = FALSE; + gint i; + gint exit_status = 0; + GError *error = NULL; + + /* full path to extcap binary */ + extcap_string = g_string_new(""); +#ifdef WIN32 + g_string_printf(extcap_string, "%s\\\\%s",dirname,file); + extcap = g_string_free(extcap_string, FALSE); +#else + g_string_printf(extcap_string, "%s/%s", dirname, file); + extcap = g_string_free(extcap_string, FALSE); +#endif + if ( extcap_if_exists(ifname) && !extcap_if_exists_for_extcap(ifname, extcap ) ) + continue; + + argv[0] = extcap; + for (i = 0; i < argc; ++i) + argv[i+1] = args[i]; + argv[argc+1] = NULL; + + status = g_spawn_sync(dirname, argv, NULL, + (GSpawnFlags) 0, NULL, NULL, + &command_output, NULL, &exit_status, &error); + + if (status && exit_status == 0) + keep_going = cb(extcap, command_output, cb_data, err_str); + + g_free(extcap); + g_free(command_output); + } + + g_dir_close(dir); + } + + g_free(argv); +} + +static gboolean dlt_cb(const gchar *extcap _U_, gchar *output, void *data, + char **err_str) { + extcap_token_sentence *tokens; + extcap_dlt *dlts, *dlt_iter, *next; + if_capabilities_t *caps; + GList *linktype_list = NULL; + data_link_info_t *data_link_info; + + tokens = extcap_tokenize_sentences(output); + extcap_parse_dlts(tokens, &dlts); + + extcap_free_tokenized_sentence_list(tokens); + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Extcap pipe %s ", extcap); + + /* + * Allocate the interface capabilities structure. + */ + caps = (if_capabilities_t *) g_malloc(sizeof *caps); + caps->can_set_rfmon = FALSE; + + dlt_iter = dlts; + while (dlt_iter != NULL ) { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + " DLT %d name=\"%s\" display=\"%s\" ", dlt_iter->number, + dlt_iter->name, dlt_iter->display); + + data_link_info = g_new(data_link_info_t, 1); + data_link_info->dlt = dlt_iter->number; + data_link_info->name = g_strdup(dlt_iter->name); + data_link_info->description = g_strdup(dlt_iter->display); + linktype_list = g_list_append(linktype_list, data_link_info); + dlt_iter = dlt_iter->next_dlt; + } + + /* Check to see if we built a list */ + if (linktype_list != NULL && data != NULL) { + caps->data_link_types = linktype_list; + *(if_capabilities_t **) data = caps; + } else { + if (err_str) { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, " returned no DLTs"); + *err_str = g_strdup("Extcap returned no DLTs"); + } + g_free(caps); + } + + dlt_iter = dlts; + while (dlt_iter != NULL ) { + next = dlt_iter->next_dlt; + extcap_free_dlt(dlt_iter); + dlt_iter = next; + } + + return FALSE; +} + +if_capabilities_t * +extcap_get_if_dlts(const gchar *ifname, char **err_str) { + gchar *argv[3]; + gint i; + if_capabilities_t *caps = NULL; + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, " returned no DLTs"); + + if (ifname != NULL && err_str != NULL) + *err_str = NULL; + + if ( extcap_if_exists(ifname) ) + { + argv[0] = g_strdup(EXTCAP_ARGUMENT_LIST_DLTS); + argv[1] = g_strdup(EXTCAP_ARGUMENT_INTERFACE); + argv[2] = g_strdup(ifname); + + if (err_str) + *err_str = NULL; + extcap_foreach(3, argv, dlt_cb, &caps, err_str, ifname); + + for (i = 0; i < 3; ++i) + g_free(argv[i]); + } + + return caps; +} + +static gboolean interfaces_cb(const gchar *extcap, gchar *output, void *data, + char **err_str _U_) { + GList **il = (GList **) data; + extcap_token_sentence *tokens; + extcap_interface *interfaces, *int_iter; /*, *next; */ + if_info_t *if_info; + + tokens = extcap_tokenize_sentences(output); + extcap_parse_interfaces(tokens, &interfaces); + + extcap_free_tokenized_sentence_list(tokens); + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Extcap pipe %s ", extcap); + + int_iter = interfaces; + while (int_iter != NULL ) { + if ( extcap_if_exists(int_iter->call) ) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_WARNING, "Extcap interface \"%s\" is already provided by \"%s\" ", + int_iter->call, (gchar *)extcap_if_executable(int_iter->call) ); + int_iter = int_iter->next_interface; + continue; + } + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, " Interface [%s] \"%s\" ", + int_iter->call, int_iter->display); + + if_info = g_new0(if_info_t, 1); + if_info->name = g_strdup(int_iter->call); + if_info->friendly_name = g_strdup(int_iter->display); + + if_info->type = IF_EXTCAP; + + if_info->extcap = g_strdup(extcap); + *il = g_list_append(*il, if_info); + + extcap_if_add(g_strdup(int_iter->call), g_strdup(extcap) ); + int_iter = int_iter->next_interface; + } + + return TRUE; +} + +GList * +extcap_interface_list(char **err_str) { + gchar *argv; + /* gint i; */ + GList *ret = NULL; + + if (err_str != NULL) + *err_str = NULL; + + extcap_if_cleanup(); + + argv = g_strdup(EXTCAP_ARGUMENT_LIST_INTERFACES); + + if (err_str) + *err_str = NULL; + extcap_foreach(1, &argv, interfaces_cb, &ret, err_str, NULL); + + g_free(argv); + + return ret; +} + +static gboolean search_cb(const gchar *extcap _U_, gchar *output, void *data, + char **err_str _U_) { + extcap_token_sentence *tokens = NULL; + GList *arguments = NULL; + GList **il = (GList **) data; + + tokens = extcap_tokenize_sentences(output); + arguments = extcap_parse_args(tokens); + + extcap_free_tokenized_sentence_list(tokens); + +#if ARG_DEBUG + extcap_debug_arguments ( arguments ); +#endif + + *il = g_list_append(*il, arguments); + + /* By returning false, extcap_foreach will break on first found */ + return TRUE; +} + +GList * +extcap_get_if_configuration(const char * ifname) { + gchar *argv[4]; + GList *ret = NULL; + gchar **err_str = NULL; + + if ( extcap_if_exists(ifname) ) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Extcap path %s", + get_extcap_dir()); + + if (err_str != NULL) + *err_str = NULL; + + argv[0] = g_strdup(EXTCAP_ARGUMENT_CONFIG); + argv[1] = g_strdup(EXTCAP_ARGUMENT_INTERFACE); + argv[2] = g_strdup(ifname); + argv[3] = NULL; + + extcap_foreach(4, argv, search_cb, &ret, err_str, ifname); + } + + return ret; +} + +void extcap_cleanup(capture_options * capture_opts) { + interface_options interface_opts; + guint icnt = 0; + + for (icnt = 0; icnt < capture_opts->ifaces->len; icnt++) { + interface_opts = g_array_index(capture_opts->ifaces, interface_options, + icnt); + + /* skip native interfaces */ + if (interface_opts.if_type != IF_EXTCAP) + continue; + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "Extcap [%s] - Cleaning up fifo: %s; PID: %d", interface_opts.name, + interface_opts.extcap_fifo, interface_opts.extcap_pid); +#ifdef WIN32 + if (pipe_h) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "Extcap [%s] - Closing pipe", interface_opts.name); + FlushFileBuffers(pipe_h); + DisconnectNamedPipe(pipe_h); + CloseHandle(pipe_h); + } +#else + if (interface_opts.extcap_fifo != NULL && file_exists(interface_opts.extcap_fifo)) + { + /* the fifo will not be freed here, but with the other capture_opts in capture_sync */ + ws_unlink(interface_opts.extcap_fifo); + interface_opts.extcap_fifo = NULL; + } +#endif + /* Maybe the client closed and removed fifo, but ws should check if + * pid should be closed */ + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "Extcap [%s] - Closing spawned PID: %d", interface_opts.name, + interface_opts.extcap_pid); + + if (interface_opts.extcap_pid != (GPid)-1 ) + { + g_spawn_close_pid(interface_opts.extcap_pid); + interface_opts.extcap_pid = (GPid)-1; + } + } +} + +static void +extcap_arg_cb(gpointer key, gpointer value, gpointer data) { + GPtrArray *args = (GPtrArray *)data; + + if ( key != NULL ) + { + g_ptr_array_add(args, key); + + if ( value != NULL ) + g_ptr_array_add(args, value); + } +} + +/* call mkfifo for each extcap, + * returns FALSE if there's an error creating a FIFO */ +gboolean +extcaps_init_initerfaces(capture_options *capture_opts) +{ + guint i; + interface_options interface_opts; + + for (i = 0; i < capture_opts->ifaces->len; i++) + { + GPtrArray *args = NULL; + GPid pid = 0; + + interface_opts = g_array_index(capture_opts->ifaces, interface_options, i); + + /* skip native interfaces */ + if (interface_opts.if_type != IF_EXTCAP ) + continue; + + /* create pipe for fifo */ + if ( ! extcap_create_pipe ( &interface_opts.extcap_fifo ) ) + return FALSE; + + /* Create extcap call */ + args = g_ptr_array_new_with_free_func(g_free); +#define add_arg(X) g_ptr_array_add(args, g_strdup(X)) + + add_arg(interface_opts.extcap); + add_arg(EXTCAP_ARGUMENT_RUN_CAPTURE); + add_arg(EXTCAP_ARGUMENT_INTERFACE); + add_arg(interface_opts.name); + add_arg(EXTCAP_ARGUMENT_RUN_PIPE); + add_arg(interface_opts.extcap_fifo); + if (interface_opts.extcap_args != NULL) + g_hash_table_foreach(interface_opts.extcap_args, extcap_arg_cb, args); + add_arg(NULL); +#undef add_arg + + /* Wireshark for windows crashes here sometimes * + * Access violation reading location 0x... */ + g_spawn_async(NULL, (gchar **)args->pdata, NULL, + (GSpawnFlags) 0, NULL, NULL, + &pid,NULL); + + interface_opts.extcap_pid = pid; + capture_opts->ifaces = g_array_remove_index(capture_opts->ifaces, i); + g_array_insert_val(capture_opts->ifaces, i, interface_opts); + } + + return TRUE; +} + +#ifdef WIN32 +/* called by capture_sync to get the CreatNamedPipe handle*/ +HANDLE +extcap_get_win32_handle() +{ + return pipe_h; +} +#endif + +gboolean extcap_create_pipe(char ** fifo) +{ +#ifdef WIN32 + gchar timestr[ 14+1 ]; + time_t current_time; + + gchar *pipename = NULL; + + LPSECURITY_ATTRIBUTES security = NULL; + /* create pipename */ + current_time = time(NULL); + strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S", localtime(¤t_time)); + pipename = g_strconcat ( "\\\\.\\pipe\\", EXTCAP_PIPE_PREFIX, "_", timestr, NULL ); + + /* Security struct to enable Inheritable HANDLE */ + security = (LPSECURITY_ATTRIBUTES)g_malloc0(sizeof(LPSECURITY_ATTRIBUTES)); + security->nLength = sizeof(LPSECURITY_ATTRIBUTES); + security->bInheritHandle = TRUE; + security->lpSecurityDescriptor = NULL; + + /* create a namedPipe*/ + pipe_h = CreateNamedPipe( + utf_8to16(pipename), + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE| PIPE_READMODE_MESSAGE | PIPE_WAIT, + 5, 65536, 65536, + 300, + security); + + if (pipe_h == INVALID_HANDLE_VALUE) + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,"\nError creating pipe => (%d)", GetLastError()); + return FALSE; + } + else + { + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,"\nWireshark Created pipe =>(%s)",pipename); + *fifo = g_strdup(pipename); + } +#else + gchar *temp_name = NULL; + int fd = 0; + + if ( ( fd = create_tempfile ( &temp_name, EXTCAP_PIPE_PREFIX ) ) == 0 ) + return FALSE; + + ws_close(fd); + + g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, + "Extcap - Creating fifo: %s", temp_name); + + if ( file_exists(temp_name) ) + ws_unlink(temp_name); + + if (mkfifo(temp_name, 0600) == 0) + *fifo = g_strdup(temp_name); +#endif + + return TRUE; +} + +#if ARG_DEBUG +void extcap_debug_arguments ( extcap_arg *arg_iter ) +{ + extcap_value *v = NULL; + GList *walker = NULL; + + printf("debug - parser dump\n"); + while (arg_iter != NULL) { + printf("ARG %d call=%s display=\"%s\" type=", arg_iter->arg_num, arg_iter->call, arg_iter->display); + + switch (arg_iter->arg_type) { + case EXTCAP_ARG_INTEGER: + printf("int\n"); + break; + case EXTCAP_ARG_UNSIGNED: + printf("unsigned\n"); + break; + case EXTCAP_ARG_LONG: + printf("long\n"); + break; + case EXTCAP_ARG_DOUBLE: + printf("double\n"); + break; + case EXTCAP_ARG_BOOLEAN: + printf("boolean\n"); + break; + case EXTCAP_ARG_MENU: + printf("menu\n"); + break; + case EXTCAP_ARG_RADIO: + printf("radio\n"); + break; + case EXTCAP_ARG_SELECTOR: + printf("selctor\n"); + break; + case EXTCAP_ARG_STRING: + printf ( "string\n" ); + break; + case EXTCAP_ARG_MULTICHECK: + printf ( "unknown\n" ); + break; + case EXTCAP_ARG_UNKNOWN: + printf ( "unknown\n" ); + break; + } + + if (arg_iter->range_start != NULL && arg_iter->range_end != NULL) { + printf("\tRange: "); + extcap_printf_complex(arg_iter->range_start); + printf(" - "); + extcap_printf_complex(arg_iter->range_end); + printf("\n"); + } + + for ( walker = g_list_first ( arg_iter->value_list ); walker; walker = walker->next ) + { + v = (extcap_value *)walker->data; + if (v->is_default == TRUE) + printf("*"); + printf("\tcall=\"%p\" display=\"%p\"\n", v->call, v->display); + printf("\tcall=\"%s\" display=\"%s\"\n", v->call, v->display); + } + + arg_iter = arg_iter->next_arg; + } +} +#endif +#endif + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=4 tabstop=4 noexpandtab: + * :indentSize=4:tabSize=4:noTabs=false: + */ diff --git a/extcap.h b/extcap.h new file mode 100644 index 0000000000..64ea855045 --- /dev/null +++ b/extcap.h @@ -0,0 +1,90 @@ +/* extcap.h + * Definitions for extcap external capture + * Copyright 2013, Mike Ryan <mikeryan@lacklustre.net> + * + * 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 __EXTCAP_H__ +#define __EXTCAP_H__ + +#include "config.h" + +#include <glib.h> + +#ifdef _WIN32 +#include <windows.h> +#include <wsutil/unicode-utils.h> +#endif + +#include <ui/capture_ui_utils.h> + +#ifdef HAVE_EXTCAP + +#define EXTCAP_ARGUMENT_CONFIG "--extcap-config" +#define EXTCAP_ARGUMENT_LIST_INTERFACES "--extcap-interfaces" +#define EXTCAP_ARGUMENT_INTERFACE "--extcap-interface" +#define EXTCAP_ARGUMENT_LIST_DLTS "--extcap-dlts" + +#define EXTCAP_ARGUMENT_RUN_CAPTURE "--capture" +#define EXTCAP_ARGUMENT_RUN_PIPE "--fifo" + +/* try to get if capabilities from extcap */ +if_capabilities_t * +extcap_get_if_dlts(const gchar * ifname, char ** err_str); + +/* get a list of all capture interfaces */ +GList * +extcap_interface_list(char **err_str); + +/* returns the configuration for the given interface name, or an + * empty list, if no configuration has been found */ +GList * +extcap_get_if_configuration(const char * ifname); + +#ifdef WIN32 +HANDLE +extcap_get_win32_handle(); +#endif + +gboolean +extcaps_init_initerfaces(capture_options * capture_opts); + +gboolean +extcap_create_pipe(char ** fifo); + +void +extcap_cleanup(capture_options * capture_opts _U_); + +#endif + +#endif + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=4 tabstop=4 noexpandtab: + * :indentSize=4:tabSize=4:noTabs=false: + */ diff --git a/extcap_parser.c b/extcap_parser.c new file mode 100644 index 0000000000..274428f981 --- /dev/null +++ b/extcap_parser.c @@ -0,0 +1,881 @@ +/* extcap_parser.c + * + * Routines for extcap external capture + * Copyright 2013, Mike Ryan <mikeryan@lacklustre.net> + * + * 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" + +#include <stdio.h> +#include <glib.h> +#include <string.h> + +#include "extcap_parser.h" + +void extcap_printf_complex(extcap_complex *comp) { + gchar *ret = extcap_get_complex_as_string(comp); + printf("%s", ret); + g_free(ret); +} + +gchar *extcap_get_complex_as_string(extcap_complex *comp) { + /* Pick an arbitrary size that should be big enough */ + gchar *ret = g_new(gchar, 32); + + if (comp == NULL) { + g_snprintf(ret, 32, "(null)"); + return ret; + } + + switch (comp->complex_type) { + case EXTCAP_ARG_INTEGER: + g_snprintf(ret, 32, "%d", comp->complex_value.int_value); + break; + case EXTCAP_ARG_UNSIGNED: + g_snprintf(ret, 32, "%u", comp->complex_value.uint_value); + break; + case EXTCAP_ARG_LONG: + g_snprintf(ret, 32, "%ld", comp->complex_value.long_value); + break; + case EXTCAP_ARG_DOUBLE: + g_snprintf(ret, 32, "%f", comp->complex_value.double_value); + break; + case EXTCAP_ARG_BOOLEAN: + g_snprintf(ret, 32, "%s", + comp->complex_value.bool_value ? "TRUE" : "FALSE"); + break; + case EXTCAP_ARG_STRING: + case EXTCAP_ARG_FILESELECT: + g_free(ret); + ret = g_strdup(comp->complex_value.string_value); + break; + default: + /* Nulling out the return string */ + g_snprintf(ret, 32, " "); + break; + } + + return ret; +} + +extcap_complex *extcap_parse_complex(extcap_arg_type complex_type, + const gchar *data) { + extcap_complex *rc = g_new(extcap_complex, 1); + gboolean success = FALSE; + long double exp_f; + + switch (complex_type) { + case EXTCAP_ARG_INTEGER: + if (sscanf(data, "%Lf", &exp_f) == 1) { + rc->complex_value.int_value = (int) exp_f; + success = TRUE; + break; + } + break; + case EXTCAP_ARG_UNSIGNED: + if (sscanf(data, "%Lf", &exp_f) == 1) { + rc->complex_value.uint_value = (unsigned int) exp_f; + success = TRUE; + break; + } + break; + case EXTCAP_ARG_LONG: + if (sscanf(data, "%Lf", &exp_f) == 1) { + rc->complex_value.long_value = (long) exp_f; + success = TRUE; + break; + } + break; + case EXTCAP_ARG_DOUBLE: + if (sscanf(data, "%Lf", &exp_f) == 1) { + rc->complex_value.double_value = (double) exp_f; + success = TRUE; + break; + } + break; + case EXTCAP_ARG_BOOLEAN: + case EXTCAP_ARG_BOOLFLAG: + if (data[0] == 't' || data[0] == 'T' || data[0] == '1') { + rc->complex_value.bool_value = 1; + } else { + rc->complex_value.bool_value = 0; + } + success = TRUE; + break; + case EXTCAP_ARG_STRING: + case EXTCAP_ARG_FILESELECT: + rc->complex_value.string_value = g_strdup(data); + success = TRUE; + break; + default: + break; + } + + if (!success) { + g_free(rc); + return NULL ; + } + + rc->complex_type = complex_type; + rc->value_filled = TRUE; + + return rc; +} + +gboolean extcap_compare_is_default(extcap_arg *element, extcap_complex *test) { + gboolean result = FALSE; + + if (element->default_complex == NULL) + return result; + + switch (element->arg_type) { + case EXTCAP_ARG_INTEGER: + if (extcap_complex_get_int(test) + == extcap_complex_get_int(element->default_complex)) + result = TRUE; + break; + case EXTCAP_ARG_UNSIGNED: + if (extcap_complex_get_uint(test) + == extcap_complex_get_uint(element->default_complex)) + result = TRUE; + break; + case EXTCAP_ARG_LONG: + if (extcap_complex_get_long(test) + == extcap_complex_get_long(element->default_complex)) + result = TRUE; + break; + case EXTCAP_ARG_DOUBLE: + if (extcap_complex_get_double(test) + == extcap_complex_get_double(element->default_complex)) + result = TRUE; + break; + case EXTCAP_ARG_BOOLEAN: + case EXTCAP_ARG_BOOLFLAG: + if (extcap_complex_get_bool(test) + == extcap_complex_get_bool(element->default_complex)) + result = TRUE; + break; + case EXTCAP_ARG_STRING: + if (strcmp(extcap_complex_get_string(test), + extcap_complex_get_string(element->default_complex)) == 0) + result = TRUE; + break; + + default: + break; + } + + return result; +} + +void extcap_free_complex(extcap_complex *comp) { + if (comp->complex_type == EXTCAP_ARG_STRING + || comp->complex_type == EXTCAP_ARG_FILESELECT) + g_free(comp->complex_value.string_value); + + g_free(comp); +} + +int extcap_complex_get_int(extcap_complex *comp) { + if ( comp == NULL ) + return (int)0; + return comp->complex_value.int_value; +} + +unsigned int extcap_complex_get_uint(extcap_complex *comp) { + if ( comp == NULL ) + return (unsigned int)0; + return comp->complex_value.uint_value; +} + +long extcap_complex_get_long(extcap_complex *comp) { + if ( comp == NULL ) + return (long)0; + return comp->complex_value.long_value; +} + +double extcap_complex_get_double(extcap_complex *comp) { + if ( comp == NULL ) + return (double)0; + return comp->complex_value.double_value; +} + +gboolean extcap_complex_get_bool(extcap_complex *comp) { + if ( comp == NULL ) + return FALSE; + return comp->complex_value.bool_value; +} + +gchar *extcap_complex_get_string(extcap_complex *comp) { + return comp->complex_value.string_value; +} + +void extcap_free_tokenized_param(extcap_token_param *v) { + if (v == NULL) + return; + + if (v->arg != NULL) + g_free(v->arg); + + if (v->value != NULL) + g_free(v->value); + + g_free(v); +} + +void extcap_free_tokenized_sentence(extcap_token_sentence *s) { + extcap_token_param *tv; + + if (s == NULL) + return; + + if (s->sentence != NULL) + g_free(s->sentence); + + while (s->param_list != NULL ) { + tv = s->param_list; + s->param_list = tv->next_token; + + extcap_free_tokenized_param(tv); + } +} + +void extcap_free_tokenized_sentence_list(extcap_token_sentence *f) { + extcap_token_sentence *t; + + while (f != NULL ) { + t = f->next_sentence; + extcap_free_tokenized_sentence(f); + f = t; + } +} + +extcap_token_sentence *extcap_tokenize_sentence(const gchar *s) { + gchar *b, *e, *eq; + + extcap_token_param *tv = NULL; + + extcap_token_sentence *rs = g_new(extcap_token_sentence, 1); + + rs->sentence = NULL; + rs->next_sentence = NULL; + rs->param_list = NULL; + + if ((b = g_strstr_len(s, -1, " ")) == NULL) { + extcap_free_tokenized_sentence(rs); + return NULL ; + } + + rs->sentence = g_strndup(s, b - s); + + if ((b = g_strstr_len(s, -1, "{")) == NULL) { + /* printf("debug - tokenizer - sentence with no values\n"); */ + extcap_free_tokenized_sentence(rs); + return NULL ; + } + + while (b != NULL ) { + if ((e = g_strstr_len(b, -1, "}")) == NULL) { + /* printf("debug - tokenizer - invalid, missing }\n"); */ + extcap_free_tokenized_sentence(rs); + return NULL ; + } + + if ((eq = g_strstr_len(b, -1, "=")) == NULL) { + /* printf("debug - tokenizer - invalid, missing =\n"); */ + extcap_free_tokenized_sentence(rs); + return NULL ; + } + + b++; + e--; + + if (b >= eq || e <= eq) { + /* printf("debug - tokenizer - invalid, missing arg or value in {}\n"); */ + extcap_free_tokenized_sentence(rs); + return NULL ; + } + + tv = g_new(extcap_token_param, 1); + tv->arg = g_strndup(b, eq - b); + tv->value = g_strndup(eq + 1, e - eq); + + if (g_ascii_strcasecmp(tv->arg, "number") == 0) { + tv->param_type = EXTCAP_PARAM_ARGNUM; + } else if (g_ascii_strcasecmp(tv->arg, "call") == 0) { + tv->param_type = EXTCAP_PARAM_CALL; + } else if (g_ascii_strcasecmp(tv->arg, "display") == 0) { + tv->param_type = EXTCAP_PARAM_DISPLAY; + } else if (g_ascii_strcasecmp(tv->arg, "type") == 0) { + tv->param_type = EXTCAP_PARAM_TYPE; + } else if (g_ascii_strcasecmp(tv->arg, "arg") == 0) { + tv->param_type = EXTCAP_PARAM_ARG; + } else if (g_ascii_strcasecmp(tv->arg, "default") == 0) { + tv->param_type = EXTCAP_PARAM_DEFAULT; + } else if (g_ascii_strcasecmp(tv->arg, "value") == 0) { + tv->param_type = EXTCAP_PARAM_VALUE; + } else if (g_ascii_strcasecmp(tv->arg, "range") == 0) { + tv->param_type = EXTCAP_PARAM_RANGE; + } else if (g_ascii_strcasecmp(tv->arg, "tooltip") == 0) { + tv->param_type = EXTCAP_PARAM_TOOLTIP; + } else if (g_ascii_strcasecmp(tv->arg, "mustexist") == 0) { + tv->param_type = EXTCAP_PARAM_FILE_MUSTEXIST; + } else if (g_ascii_strcasecmp(tv->arg, "name") == 0) { + tv->param_type = EXTCAP_PARAM_NAME; + } else if (g_ascii_strcasecmp(tv->arg, "enabled") == 0) { + tv->param_type = EXTCAP_PARAM_ENABLED; + } else { + tv->param_type = EXTCAP_PARAM_UNKNOWN; + } + + tv->next_token = rs->param_list; + rs->param_list = tv; + + /* printf("debug - tokenizer - got '%s' = '%s'\n", tv->arg, tv->value); */ + + b = e + 1; + if ((size_t) (b - s) > strlen(s)) + break; + + b = g_strstr_len(b, -1, "{"); + } + + return rs; +} + +extcap_token_sentence *extcap_tokenize_sentences(const gchar *s) { + extcap_token_sentence *first = NULL, *cur = NULL, *last = NULL; + + gchar **list, **list_iter; + + list_iter = list = g_strsplit(s, "\n", 0); + + while (*list_iter != NULL ) { + cur = extcap_tokenize_sentence(*list_iter); + + if (cur != NULL) { + if (first == NULL) { + first = cur; + last = cur; + } else { + last->next_sentence = cur; + last = cur; + } + } + + list_iter++; + } + + g_strfreev(list); + + return first; +} + +extcap_token_param *extcap_find_param_by_type(extcap_token_param *first, + extcap_param_type t) { + while (first != NULL ) { + if (first->param_type == t) { + return first; + } + + first = first->next_token; + } + + return NULL ; +} + +void extcap_free_value(extcap_value *v) { + if (v == NULL) + return; + + if (v->call != NULL) + g_free(v->call); + + if (v->display != NULL) + g_free(v->display); + + g_free(v); +} + +extcap_interface *extcap_new_interface(void) { + extcap_interface *r = g_new(extcap_interface, 1); + + r->call = r->display = NULL; + r->next_interface = NULL; + + return r; +} + +void extcap_free_interface(extcap_interface *i) { + if (i == NULL) + return; + + if (i->call != NULL) + g_free(i->call); + + if (i->display != NULL) + g_free(i->display); +} + +extcap_dlt *extcap_new_dlt(void) { + extcap_dlt *r = g_new(extcap_dlt, 1); + + r->number = -1; + r->name = r->display = NULL; + r->next_dlt = NULL; + + return r; +} + +void extcap_free_dlt(extcap_dlt *d) { + if (d == NULL) + return; + + if (d->name != NULL) + g_free(d->name); + + if (d->display != NULL) + g_free(d->display); +} + +extcap_arg *extcap_new_arg(void) { + extcap_arg *r = g_new(extcap_arg, 1); + + r->call = NULL; + r->display = NULL; + r->tooltip = NULL; + r->arg_type = EXTCAP_ARG_UNKNOWN; + r->range_start = NULL; + r->range_end = NULL; + r->default_complex = NULL; + r->fileexists = FALSE; + + r->values = NULL; + /*r->next_arg = NULL; */ + + return r; +} + +static void extcap_free_valuelist(gpointer data, gpointer user_data _U_) { + extcap_free_value((extcap_value *) data); +} + +void extcap_free_arg(extcap_arg *a) { + + if (a == NULL) + return; + + if (a->call != NULL) + g_free(a->call); + + if (a->display != NULL) + g_free(a->display); + + if (a->tooltip != NULL) + g_free(a->tooltip); + + if (a->range_start != NULL) + extcap_free_complex(a->range_start); + + if (a->range_end != NULL) + extcap_free_complex(a->range_end); + + if (a->default_complex != NULL) + extcap_free_complex(a->default_complex); + + g_list_foreach(a->values, (GFunc) extcap_free_valuelist, NULL); +} + +static void extcap_free_arg_list_cb(gpointer listentry) { + if (listentry != NULL) + extcap_free_arg((extcap_arg *) listentry); +} + +void extcap_free_arg_list(GList *a) { + g_list_free_full(a, extcap_free_arg_list_cb); +} + +static gint glist_find_numbered_arg(gconstpointer listelem, gconstpointer needle) { + if (((const extcap_arg *) listelem)->arg_num == *((const int*) needle)) + return 0; + return 1; +} + +extcap_arg *extcap_parse_arg_sentence(GList * args, extcap_token_sentence *s) { + extcap_token_param *v = NULL; + extcap_arg *target_arg = NULL; + extcap_value *value = NULL; + GList * entry = NULL; + int tint; + extcap_sentence_type sent = EXTCAP_SENTENCE_UNKNOWN; + + if (s == NULL) + return target_arg; + + if (g_ascii_strcasecmp(s->sentence, "arg") == 0) { + sent = EXTCAP_SENTENCE_ARG; + /* printf("ARG sentence\n"); */ + } else if (g_ascii_strcasecmp(s->sentence, "value") == 0) { + sent = EXTCAP_SENTENCE_VALUE; + /* printf("VALUE sentence\n"); */ + } + + if (sent == EXTCAP_SENTENCE_ARG) { + target_arg = extcap_new_arg(); + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_ARGNUM)) + == NULL) { + extcap_free_arg(target_arg); + return NULL ; + } + + if (sscanf(v->value, "%d", &(target_arg->arg_num)) != 1) { + extcap_free_arg(target_arg); + return NULL ; + } + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_CALL)) + == NULL) { + extcap_free_arg(target_arg); + return NULL ; + } + target_arg->call = g_strdup(v->value); + + /* No value only parameters allowed */ + if (strlen(target_arg->call) == 0) { + extcap_free_arg(target_arg); + return NULL ; + } + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_DISPLAY)) + == NULL) { + extcap_free_arg(target_arg); + return NULL ; + } + target_arg->display = g_strdup(v->value); + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_TOOLTIP)) + != NULL) { + target_arg->tooltip = g_strdup(v->value); + } + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_FILE_MUSTEXIST)) + != NULL) { + target_arg->fileexists = (v->value[0] == 't' || v->value[0] == 'T'); + } + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_TYPE)) + == NULL) { + /* printf("no type in ARG sentence\n"); */ + extcap_free_arg(target_arg); + return NULL ; + } + + if (g_ascii_strcasecmp(v->value, "integer") == 0) { + target_arg->arg_type = EXTCAP_ARG_INTEGER; + } else if (g_ascii_strcasecmp(v->value, "unsigned") == 0) { + target_arg->arg_type = EXTCAP_ARG_UNSIGNED; + } else if (g_ascii_strcasecmp(v->value, "long") == 0) { + target_arg->arg_type = EXTCAP_ARG_LONG; + } else if (g_ascii_strcasecmp(v->value, "double") == 0) { + target_arg->arg_type = EXTCAP_ARG_DOUBLE; + } else if (g_ascii_strcasecmp(v->value, "boolean") == 0) { + target_arg->arg_type = EXTCAP_ARG_BOOLEAN; + } else if (g_ascii_strcasecmp(v->value, "boolflag") == 0) { + target_arg->arg_type = EXTCAP_ARG_BOOLFLAG; + } else if (g_ascii_strcasecmp(v->value, "menu") == 0) { + target_arg->arg_type = EXTCAP_ARG_MENU; + } else if (g_ascii_strcasecmp(v->value, "selector") == 0) { + target_arg->arg_type = EXTCAP_ARG_SELECTOR; + } else if (g_ascii_strcasecmp(v->value, "radio") == 0) { + target_arg->arg_type = EXTCAP_ARG_RADIO; + } else if (g_ascii_strcasecmp(v->value, "string") == 0) { + target_arg->arg_type = EXTCAP_ARG_STRING; + } else if (g_ascii_strcasecmp(v->value, "fileselect") == 0) { + target_arg->arg_type = EXTCAP_ARG_FILESELECT; + } else if (g_ascii_strcasecmp(v->value, "multicheck") == 0) { + target_arg->arg_type = EXTCAP_ARG_MULTICHECK; + } else { + printf("invalid type %s in ARG sentence\n", v->value); + extcap_free_arg(target_arg); + return NULL ; + } + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_RANGE)) + != NULL) { + gchar *cp = g_strstr_len(v->value, -1, ","); + + if (cp == NULL) { + printf("invalid range, expected value,value got %s\n", + v->value); + extcap_free_arg(target_arg); + return NULL ; + } + + if ((target_arg->range_start = extcap_parse_complex( + target_arg->arg_type, v->value)) == NULL) { + printf("invalid range, expected value,value got %s\n", + v->value); + extcap_free_arg(target_arg); + return NULL ; + } + + if ((target_arg->range_end = extcap_parse_complex( + target_arg->arg_type, cp + 1)) == NULL) { + printf("invalid range, expected value,value got %s\n", + v->value); + extcap_free_arg(target_arg); + return NULL ; + } + } + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_DEFAULT)) + != NULL) { + if ((target_arg->default_complex = extcap_parse_complex( + target_arg->arg_type, v->value)) == NULL) { + printf("invalid default, couldn't parse %s\n", v->value); + } + } + + } else if (sent == EXTCAP_SENTENCE_VALUE) { + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_ARG)) + == NULL) { + printf("no arg in VALUE sentence\n"); + return NULL ; + } + + if (sscanf(v->value, "%d", &tint) != 1) { + printf("invalid arg in VALUE sentence\n"); + return NULL ; + } + + ; + if ((entry = g_list_find_custom(args, &tint, glist_find_numbered_arg)) + == NULL) { + printf("couldn't find arg %d in list for VALUE sentence\n", tint); + return NULL ; + } + + value = g_new(extcap_value, 1); + value->display = NULL; + value->call = NULL; + value->enabled = FALSE; + value->is_default = FALSE; + value->arg_num = tint; + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_VALUE)) + == NULL) { + /* printf("no value in VALUE sentence\n"); */ + extcap_free_value(value); + return NULL ; + } + value->call = g_strdup(v->value); + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_DISPLAY)) + == NULL) { + /* printf("no display in VALUE sentence\n"); */ + extcap_free_value(value); + return NULL ; + } + value->display = g_strdup(v->value); + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_DEFAULT)) + != NULL) { + /* printf("found default value\n"); */ + value->is_default = (v->value[0] == 't' || v->value[0] == 'T'); + } + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_ENABLED)) + != NULL) { + value->enabled = (v->value[0] == 't' || v->value[0] == 'T'); + } + + ((extcap_arg*) entry->data)->values = g_list_append( + ((extcap_arg*) entry->data)->values, value); + + return NULL ; + } + + return target_arg; +} + +GList * extcap_parse_args(extcap_token_sentence *first_s) { + GList * args = NULL; + + while (first_s) { + extcap_arg *ra = NULL; + + if ((ra = extcap_parse_arg_sentence(args, first_s)) != NULL) + args = g_list_append(args, (gpointer) ra); + + first_s = first_s->next_sentence; + } + + return args; +} + +int extcap_parse_interface_sentence(extcap_token_sentence *s, + extcap_interface **ri) { + extcap_token_param *v = NULL; + extcap_sentence_type sent = EXTCAP_SENTENCE_UNKNOWN; + + *ri = NULL; + + if (s == NULL) + return -1; + + if (g_ascii_strcasecmp(s->sentence, "interface") == 0) { + sent = EXTCAP_SENTENCE_INTERFACE; + /* printf("INTERFACE sentence\n"); */ + } + + if (sent == EXTCAP_SENTENCE_UNKNOWN) + return -1; + + *ri = extcap_new_interface(); + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_VALUE)) + == NULL) { + printf("No value in INTERFACE sentence\n"); + extcap_free_interface(*ri); + return -1; + } + (*ri)->call = g_strdup(v->value); + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_DISPLAY)) + == NULL) { + printf("No display in INTERFACE sentence\n"); + extcap_free_interface(*ri); + return -1; + } + (*ri)->display = g_strdup(v->value); + + return 1; +} + +int extcap_parse_interfaces(extcap_token_sentence *first_s, + extcap_interface **first_int) { + extcap_interface *first_i = NULL, *last_i = NULL; + + while (first_s) { + extcap_interface *ri; + + if (extcap_parse_interface_sentence(first_s, &ri) >= 0 && ri != NULL) { + if (first_i == NULL) { + first_i = last_i = ri; + } else { + last_i->next_interface = ri; + last_i = ri; + } + } + + first_s = first_s->next_sentence; + } + + *first_int = first_i; + + return 1; +} + +int extcap_parse_dlt_sentence(extcap_token_sentence *s, extcap_dlt **rd) { + extcap_token_param *v = NULL; + extcap_sentence_type sent = EXTCAP_SENTENCE_UNKNOWN; + + *rd = NULL; + + if (s == NULL) + return -1; + + if (g_ascii_strcasecmp(s->sentence, "dlt") == 0) { + sent = EXTCAP_SENTENCE_DLT; + } + + if (sent == EXTCAP_SENTENCE_UNKNOWN) + return -1; + + *rd = extcap_new_dlt(); + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_ARGNUM)) + == NULL) { + printf("No number in DLT sentence\n"); + extcap_free_dlt(*rd); + return -1; + } + if (sscanf(v->value, "%d", &((*rd)->number)) != 1) { + printf("Invalid number in DLT sentence\n"); + extcap_free_dlt(*rd); + return -1; + } + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_NAME)) + == NULL) { + printf("No name in DLT sentence\n"); + extcap_free_dlt(*rd); + return -1; + } + (*rd)->name = g_strdup(v->value); + + if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_DISPLAY)) + == NULL) { + printf("No display in DLT sentence\n"); + extcap_free_dlt(*rd); + return -1; + } + (*rd)->display = g_strdup(v->value); + + return 1; +} + +int extcap_parse_dlts(extcap_token_sentence *first_s, extcap_dlt **first_dlt) { + extcap_dlt *first_d = NULL, *last_d = NULL; + + while (first_s) { + extcap_dlt *rd; + + if (extcap_parse_dlt_sentence(first_s, &rd) >= 0 && rd != NULL) { + if (first_d == NULL) { + first_d = last_d = rd; + } else { + last_d->next_dlt = rd; + last_d = rd; + } + } + + first_s = first_s->next_sentence; + } + + *first_dlt = first_d; + + return 1; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=4 tabstop=4 noexpandtab: + * :indentSize=4:tabSize=4:noTabs=false: + */ diff --git a/extcap_parser.h b/extcap_parser.h new file mode 100644 index 0000000000..0058597535 --- /dev/null +++ b/extcap_parser.h @@ -0,0 +1,253 @@ +/* extcap_parser.h + * + * 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 __EXTCAP_PARSER_H__ +#define __EXTCAP_PARSER_H__ + +#include <stdio.h> +#include <glib.h> +#include <string.h> + +typedef enum { + EXTCAP_SENTENCE_UNKNOWN, + EXTCAP_SENTENCE_ARG, + EXTCAP_SENTENCE_VALUE, + EXTCAP_SENTENCE_FLAG, + EXTCAP_SENTENCE_INTERFACE, + EXTCAP_SENTENCE_DLT +} extcap_sentence_type; + +typedef enum { + /* Simple types */ + EXTCAP_ARG_UNKNOWN, + EXTCAP_ARG_INTEGER, + EXTCAP_ARG_UNSIGNED, + EXTCAP_ARG_LONG, + EXTCAP_ARG_DOUBLE, + EXTCAP_ARG_BOOLEAN, + EXTCAP_ARG_BOOLFLAG, + EXTCAP_ARG_STRING, + /* Complex GUI types which are populated with value sentences */ + EXTCAP_ARG_MENU, + EXTCAP_ARG_SELECTOR, + EXTCAP_ARG_RADIO, + EXTCAP_ARG_MULTICHECK, + EXTCAP_ARG_FILESELECT +} extcap_arg_type; + +typedef enum { + /* value types */ + EXTCAP_PARAM_UNKNOWN, + EXTCAP_PARAM_ARGNUM, + EXTCAP_PARAM_CALL, + EXTCAP_PARAM_DISPLAY, + EXTCAP_PARAM_TYPE, + EXTCAP_PARAM_ARG, + EXTCAP_PARAM_DEFAULT, + EXTCAP_PARAM_VALUE, + EXTCAP_PARAM_RANGE, + EXTCAP_PARAM_TOOLTIP, + EXTCAP_PARAM_NAME, + EXTCAP_PARAM_ENABLED, + EXTCAP_PARAM_FILE_MUSTEXIST +} extcap_param_type; + +/* Values for a given sentence; values are all stored as a call + * and a value string, or a valid range, so we only need to store + * those and repeat them */ +typedef struct _extcap_value { + int arg_num; + + gchar *call; + gchar *display; + gboolean enabled; + gboolean is_default; +} extcap_value; + +/* Complex-ish struct for storing complex values */ +typedef struct _extcap_complex { + extcap_arg_type complex_type; + union { + int int_value; + unsigned int uint_value; + long long_value; + double double_value; + gboolean bool_value; + gchar *string_value; + } complex_value; + gboolean value_filled; +} extcap_complex; + +/* An argument sentence and accompanying options */ +typedef struct _extcap_arg { + int arg_num; + + gchar *call; + gchar *display; + gchar *tooltip; + gboolean fileexists; + + extcap_arg_type arg_type; + + extcap_complex *range_start; + extcap_complex *range_end; + extcap_complex *default_complex; + + GList * values; +} extcap_arg; + +typedef struct _extcap_if { + gchar * extcap_path; + GList * interfaces; +} extcap_if; + +typedef struct _extcap_interface { + gchar *call; + gchar *display; + + struct _extcap_interface *next_interface; +} extcap_interface; + +extcap_interface *extcap_new_interface(void); +void extcap_free_interface(extcap_interface *interface); + +typedef struct _extcap_dlt { + gint number; + gchar *name; + gchar *display; + + struct _extcap_dlt *next_dlt; +} extcap_dlt; + +extcap_dlt *extcap_new_dlt(void); +void extcap_free_dlt(extcap_dlt *dlt); + +/* Parser internals */ +typedef struct _extcap_token_param { + gchar *arg; + gchar *value; + + extcap_param_type param_type; + + struct _extcap_token_param *next_token; +} extcap_token_param; + +typedef struct _extcap_token_sentence { + gchar *sentence; + + extcap_token_param *param_list; + + struct _extcap_token_sentence *next_sentence; +} extcap_token_sentence; + +/* Parse a string into a complex type */ +extcap_complex *extcap_parse_complex(extcap_arg_type complex_type, + const gchar *data); + +/* Free a complex */ +void extcap_free_complex(extcap_complex *comp); + +/* Print a complex value out for debug */ +void extcap_printf_complex(extcap_complex *comp); + +/* + * Return a string representation of a complex type + * Caller is responsible for calling g_free on the returned string + */ +gchar *extcap_get_complex_as_string(extcap_complex *comp); + +int extcap_complex_get_int(extcap_complex *comp); +unsigned int extcap_complex_get_uint(extcap_complex *comp); +long extcap_complex_get_long(extcap_complex *comp); +double extcap_complex_get_double(extcap_complex *comp); +gboolean extcap_complex_get_bool(extcap_complex *comp); +gchar *extcap_complex_get_string(extcap_complex *comp); + +/* compares the default value of an element with a given parameter */ +gboolean extcap_compare_is_default(extcap_arg *element, extcap_complex *test); + +void extcap_free_tokenized_param(extcap_token_param *v); +void extcap_free_tokenized_sentence(extcap_token_sentence *s); +void extcap_free_tokenized_sentence_list(extcap_token_sentence *f); + +/* Turn a sentence into logical tokens, don't validate beyond basic syntax */ +extcap_token_sentence *extcap_tokenize_sentence(const gchar *s); + +/* Tokenize a set of sentences (such as the output of a g_spawn_sync) */ +extcap_token_sentence *extcap_tokenize_sentences(const gchar *s); + +/* Find an argument in the extcap_arg list which matches the given arg=X number */ +extcap_arg *extcap_find_numbered_arg(extcap_arg *first, int number); + +/* Find the first occurrence in a parameter list of a parameter of the given type */ +extcap_token_param *extcap_find_param_by_type(extcap_token_param *first, + extcap_param_type t); + +void extcap_free_value(extcap_value *v); + +extcap_arg *extcap_new_arg(void); + +/* Free a single argument */ +void extcap_free_arg(extcap_arg *a); + +/* Free an entire arg list */ +void extcap_free_arg_list(GList *a); + +/* + * Parse a tokenized sentence and validate. If a new sentence is created, the result + * is returned in 'ra'. On error, < 0 is returned. Not all sentences will create a + * new returned sentence (VALUE sentences, for example) + */ +extcap_arg * extcap_parse_arg_sentence(GList * args, extcap_token_sentence *s); + +/* Parse all sentences for args and values */ +GList * extcap_parse_args(extcap_token_sentence *first_s); + +/* + * Parse a tokenized set of sentences and validate, looking for interface definitions. + */ +int extcap_parse_interface_sentence(extcap_token_sentence *s, + extcap_interface **ri); + +/* Parse all sentences for interfaces */ +int extcap_parse_interfaces(extcap_token_sentence *first_s, + extcap_interface **first_int); + +/* Parse a tokenized set of sentences and validate, looking for DLT definitions */ +int extcap_parse_dlt_sentence(extcap_token_sentence *s, extcap_dlt **ri); + +/* Parse all sentences for DLTs */ +int extcap_parse_dlts(extcap_token_sentence *first_s, extcap_dlt **first_dlt); + +#endif + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=4 tabstop=4 noexpandtab: + * :indentSize=4:tabSize=4:noTabs=false: + */ diff --git a/packaging/nsis/wireshark.nsi b/packaging/nsis/wireshark.nsi index d87db3e00f..881a02eaee 100644 --- a/packaging/nsis/wireshark.nsi +++ b/packaging/nsis/wireshark.nsi @@ -886,6 +886,8 @@ SetOutPath $INSTDIR File "${STAGING_DIR}\${PROGRAM_NAME_PATH_GTK}" File "${STAGING_DIR}\${GDK_DLL}" File "${STAGING_DIR}\libgdk_pixbuf-2.0-0.dll" +File "${STAGING_DIR}\gspawn-${WIRESHARK_TARGET_PLATFORM}-helper.exe" +File "${STAGING_DIR}\gspawn-${WIRESHARK_TARGET_PLATFORM}-helper-console.exe" File "${STAGING_DIR}\${GTK_DLL}" File "${STAGING_DIR}\libatk-1.0-0.dll" File "${STAGING_DIR}\libpango-1.0-0.dll" diff --git a/ui/gtk/CMakeLists.txt b/ui/gtk/CMakeLists.txt index a97a72e893..545781eebc 100644 --- a/ui/gtk/CMakeLists.txt +++ b/ui/gtk/CMakeLists.txt @@ -119,6 +119,13 @@ set(WIRESHARK_GTK_SRC webbrowser.c ) +if(HAVE_EXTCAP) + set(WIRESHARK_GTK_SRC + ${WIRESHARK_GTK_SRC} + extcap_gtk.c + ) +endif() + set(WIRESHARK_DIRTY_TAP_SRC ) diff --git a/ui/gtk/Makefile.common b/ui/gtk/Makefile.common index 405ae13c9a..c2a0a726e1 100644 --- a/ui/gtk/Makefile.common +++ b/ui/gtk/Makefile.common @@ -135,6 +135,7 @@ WIRESHARK_GTK_SRC = \ uat_gui.c \ voip_calls.c \ webbrowser.c \ + extcap_gtk.c \ $(WIRESHARK_CUSTOM_GTK_SRC) about_dlg.c main_welcome.c: wssplash.h remote_icons.h @@ -317,4 +318,5 @@ noinst_HEADERS = \ wsiconcap.h \ wsicon.h \ wssplash.h \ + extcap_gtk.h \ $(WIRESHARK_CUSTOM_HDRS) diff --git a/ui/gtk/about_dlg.c b/ui/gtk/about_dlg.c index a7b251defc..151a691feb 100644 --- a/ui/gtk/about_dlg.c +++ b/ui/gtk/about_dlg.c @@ -484,6 +484,18 @@ about_folders_page_new(void) g_free(path); #endif +#ifdef HAVE_EXTCAP + /* extcap */ + constpath = get_extcap_dir(); + + resultArray = g_strsplit(constpath, G_SEARCHPATH_SEPARATOR_S, 10); + + for(i = 0; resultArray[i]; i++) + about_folders_row(table, "Extcap path", g_strstrip(resultArray[i]), + "Extcap Plugins search path"); + g_strfreev(resultArray); +#endif + gtk_container_add(GTK_CONTAINER(scrolledwindow), table); return scrolledwindow; diff --git a/ui/gtk/capture_dlg.c b/ui/gtk/capture_dlg.c index 4a628c022b..7a299d047c 100644 --- a/ui/gtk/capture_dlg.c +++ b/ui/gtk/capture_dlg.c @@ -80,6 +80,11 @@ #include "airpcap_dlg.h" #endif +#ifdef HAVE_EXTCAP +#include "extcap.h" +#include "ui/gtk/extcap_gtk.h" +#endif + /* * Symbolic names for column indices. */ @@ -165,6 +170,10 @@ enum #define E_CAP_T_RESOLVE_KEY "cap_t_resolve" #define E_CAP_E_RESOLVE_KEY "cap_e_resolve" +#ifdef HAVE_EXTCAP +#define E_CAP_EXTCAP_KEY "cap_extcap_vbox" +#endif + #define E_CAP_IFTYPE_CBX_KEY "cap_iftype_cbx" #ifdef HAVE_PCAP_REMOTE #define E_CAP_IF_LIST_KEY "cap_if_list" @@ -2382,6 +2391,9 @@ save_options_cb(GtkWidget *win _U_, gpointer user_data _U_) #if defined(_WIN32) || defined(HAVE_PCAP_CREATE) GtkWidget *buffer_size_sb; #endif +#ifdef HAVE_EXTCAP + GtkWidget *extcap_vbox; +#endif interface_t device; gpointer ptr = NULL; @@ -2403,6 +2415,10 @@ save_options_cb(GtkWidget *win _U_, gpointer user_data _U_) linktype_combo_box = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_LT_CBX_KEY); +#ifdef HAVE_EXTCAP + extcap_vbox = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_EXTCAP_KEY); +#endif + if (device.links != NULL) { if (ws_combo_box_get_active_pointer(GTK_COMBO_BOX(linktype_combo_box), &ptr)) { /* Even though device.links != NULL, we might not have an active pointer @@ -2436,6 +2452,19 @@ save_options_cb(GtkWidget *win _U_, gpointer user_data _U_) g_free(device.cfilter); g_assert(filter_text != NULL); device.cfilter = filter_text; + +#ifdef HAVE_EXTCAP + if (device.external_cap_args_settings != NULL) + g_hash_table_unref(device.external_cap_args_settings); + + device.external_cap_args_settings = extcap_gtk_get_state(extcap_vbox); + + /* Destroy the args data linked in the gtk widget */ +#if 0 + extcap_gtk_free_args(extcap_vbox); +#endif +#endif + #ifdef HAVE_PCAP_CREATE /* if dumpcap reported that the interface does not support monitor mode, we disable monitor mode even if the user explicitly selected it */ @@ -2472,6 +2501,36 @@ adjust_snap_sensitivity(GtkWidget *tb _U_, gpointer parent_w _U_) g_array_insert_val(global_capture_opts.all_ifaces, marked_interface, device); } +#ifdef HAVE_EXTCAP +void +extcap_free_arglist(gpointer data, gpointer user_data _U_) +{ + extcap_free_arg ( (extcap_arg *) data ); +} + +static GtkWidget *build_extcap_options(const gchar *name, GHashTable *hash) { + GtkWidget *ret_box = NULL; + GList *arglist = NULL; + GList *elem = NULL; + + arglist = extcap_get_if_configuration( name ); + for ( elem = g_list_first(arglist); elem; elem = elem->next ) + { + GSList *widget_list; + +#if GTK_CHECK_VERSION(3, 0, 0) + ret_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 3); +#else + ret_box = gtk_vbox_new(FALSE, 3); +#endif + widget_list = extcap_populate_gtk_vbox((GList *) elem->data, ret_box, hash); + g_object_set_data(G_OBJECT(ret_box), EXTCAP_GTK_DATA_KEY_WIDGETLIST, widget_list); + } + + return ret_box; +} +#endif + void options_interface_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column _U_, gpointer userdata) { GtkWidget *caller, *window, *swindow = NULL, *if_view, @@ -2494,7 +2553,11 @@ void options_interface_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColum *compile_bt, #endif *bbox, *ok_but, *cancel_bt, +#ifdef HAVE_EXTCAP + *extcap_vbox, +#endif *help_bt; + GList *cf_entry, *list, *cfilter_list; GtkAdjustment *snap_adj; #if defined(_WIN32) || defined(HAVE_PCAP_CREATE) @@ -2518,6 +2581,9 @@ void options_interface_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColum GtkCellRenderer *renderer; GtkListStore *store; const gchar *new_cfilter; +#ifdef HAVE_EXTCAP + GHashTable *extcap_hash; +#endif window = (GtkWidget *)userdata; caller = gtk_widget_get_toplevel(GTK_WIDGET(window)); @@ -2544,6 +2610,9 @@ void options_interface_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColum #if defined(_WIN32) || defined(HAVE_PCAP_CREATE) device.buffer = DEFAULT_CAPTURE_BUFFER_SIZE; #endif +#ifdef HAVE_EXTCAP + device.external_cap_args_settings = NULL; +#endif model = gtk_tree_view_get_model(view); gtk_tree_model_get_iter (model, &iter, path); @@ -2913,6 +2982,14 @@ void options_interface_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColum } #endif +#ifdef HAVE_EXTCAP + extcap_hash = device.external_cap_args_settings; + extcap_vbox = build_extcap_options(device.name, extcap_hash); + gtk_box_pack_start(GTK_BOX(capture_vb), extcap_vbox, FALSE, FALSE, 5); + gtk_widget_show(extcap_vbox); + g_object_set_data(G_OBJECT(opt_edit_w), E_CAP_EXTCAP_KEY, extcap_vbox); +#endif + /* Button row: "Start", "Cancel" and "Help" buttons */ bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL); gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5); diff --git a/ui/gtk/capture_if_dlg.c b/ui/gtk/capture_if_dlg.c index 9eb002cc27..d792af83eb 100644 --- a/ui/gtk/capture_if_dlg.c +++ b/ui/gtk/capture_if_dlg.c @@ -426,6 +426,9 @@ GtkWidget * capture_get_if_icon(interface_t *device) return pixbuf_to_widget(network_wired_pb_data); case IF_PIPE: case IF_STDIN: +#ifdef HAVE_EXTCAP + case IF_EXTCAP: +#endif return pixbuf_to_widget(pipe_pb_data); default: printf("unknown device type\n"); diff --git a/ui/gtk/extcap_gtk.c b/ui/gtk/extcap_gtk.c new file mode 100644 index 0000000000..69f3908626 --- /dev/null +++ b/ui/gtk/extcap_gtk.c @@ -0,0 +1,806 @@ +/* extcap_gtk.c + * + * 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" + +#include <stdio.h> +#include <string.h> + +#include <glib.h> +#include <gtk/gtk.h> + +#include <ui/gtk/gui_utils.h> +#include <wsutil/filesystem.h> + +#include <extcap_parser.h> +#include "extcap_gtk.h" + +GHashTable *extcap_gtk_get_state(GtkWidget *widget) { + GSList *widget_list, *widget_iter; + GSList *radio_list = NULL, *radio_iter = NULL; + + GtkWidget *list_widget, *radio_widget, *tree_widget, *entry_widget; + + extcap_arg *arg = NULL; + extcap_value *value = NULL; + extcap_complex *parsed_complex = NULL; + + GtkTreeSelection *treeselection; + GtkTreeModel *treemodel; + GtkTreeIter treeiter; + + GHashTable *ret_hash; + + gchar *call_string = NULL; + + gchar **multi_list = NULL; + int multi_num = 0; + gboolean multi_valid, multi_enabled; + + widget_list = (GSList *) g_object_get_data(G_OBJECT(widget), + EXTCAP_GTK_DATA_KEY_WIDGETLIST); + + if (widget_list == NULL) + return NULL ; + + /* String hash */ + ret_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + + for (widget_iter = widget_list; widget_iter; widget_iter = + widget_iter->next) { + list_widget = (GtkWidget *) widget_iter->data; + + if ((arg = (extcap_arg *) g_object_get_data(G_OBJECT(list_widget), + EXTCAP_GTK_DATA_KEY_ARGPTR)) == NULL) { + continue; + } + + switch (arg->arg_type) { + case EXTCAP_ARG_INTEGER: + case EXTCAP_ARG_UNSIGNED: + case EXTCAP_ARG_LONG: + case EXTCAP_ARG_DOUBLE: + case EXTCAP_ARG_STRING: + parsed_complex = extcap_parse_complex(arg->arg_type, + gtk_entry_get_text(GTK_ENTRY(list_widget))); + if (parsed_complex == NULL) { + continue; + } + break; + case EXTCAP_ARG_BOOLEAN: + case EXTCAP_ARG_BOOLFLAG: + parsed_complex = extcap_parse_complex(arg->arg_type, + gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(list_widget)) ? "true" : "false"); + break; + case EXTCAP_ARG_FILESELECT: + if ((entry_widget = + (GtkWidget *) g_object_get_data(G_OBJECT(list_widget), + EXTCAP_GTK_DATA_KEY_FILENAME)) == NULL) { + continue; + } + parsed_complex = extcap_parse_complex(arg->arg_type, + gtk_entry_get_text(GTK_ENTRY(entry_widget))); + if (parsed_complex == NULL) { + continue; + } + break; + case EXTCAP_ARG_MENU: + break; + case EXTCAP_ARG_RADIO: + if ((radio_widget = (GtkWidget *) g_object_get_data( + G_OBJECT(list_widget), + EXTCAP_GTK_DATA_KEY_FIRSTRADIO)) == NULL) { + continue; + } + + if ((radio_list = gtk_radio_button_get_group( + GTK_RADIO_BUTTON(radio_widget))) == NULL) { + continue; + } + + for (radio_iter = radio_list; radio_iter; + radio_iter = radio_iter->next) { + GtkWidget *cur_radio = (GtkWidget *) radio_iter->data; + + if (gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(cur_radio))) { + if ((value = (extcap_value *) g_object_get_data( + G_OBJECT(cur_radio), + EXTCAP_GTK_DATA_KEY_VALPTR)) == NULL) { + continue; + } + + if (value->is_default) + continue; + + call_string = g_strdup(value->call); + break; + } + } + + break; + case EXTCAP_ARG_SELECTOR: + if ((tree_widget = (GtkWidget *) g_object_get_data( + G_OBJECT(list_widget), + EXTCAP_GTK_DATA_KEY_TREEVIEW)) == NULL) { + continue; + } + + treeselection = gtk_tree_view_get_selection( + GTK_TREE_VIEW(tree_widget)); + treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(tree_widget)); + if (gtk_tree_selection_get_selected(treeselection, &treemodel, + &treeiter)) { + gtk_tree_model_get(treemodel, &treeiter, EXTCAP_GTK_COL_VALUE, + &value, -1); + + if (value->is_default) + continue; + + call_string = g_strdup(value->call); + } + + break; + case EXTCAP_ARG_MULTICHECK: + if ((tree_widget = (GtkWidget *) g_object_get_data( + G_OBJECT(list_widget), + EXTCAP_GTK_DATA_KEY_TREEVIEW)) == NULL) { + continue; + } + + treeselection = gtk_tree_view_get_selection( + GTK_TREE_VIEW(tree_widget)); + treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(tree_widget)); + + multi_num = 0; + + /* Count the # of items enabled */ + multi_valid = gtk_tree_model_get_iter_first(treemodel, &treeiter); + while (multi_valid) { + gtk_tree_model_get(treemodel, &treeiter, + EXTCAP_GTK_MULTI_COL_CHECK, &multi_enabled, -1); + + if (multi_enabled) + multi_num++; + + multi_valid = gtk_tree_model_iter_next(treemodel, &treeiter); + } + + multi_list = g_new(gchar *, multi_num + 1); + + multi_num = 0; + + /* Count the # of items enabled */ + multi_valid = gtk_tree_model_get_iter_first(treemodel, &treeiter); + while (multi_valid) { + gtk_tree_model_get(treemodel, &treeiter, + EXTCAP_GTK_MULTI_COL_CHECK, &multi_enabled, + EXTCAP_GTK_MULTI_COL_VALUE, &value, -1); + + if (multi_enabled) { + multi_list[multi_num] = g_strdup(value->call); + multi_num++; + } + + multi_valid = gtk_tree_model_iter_next(treemodel, &treeiter); + } + multi_list[multi_num] = NULL; + + call_string = g_strjoinv(",", multi_list); + + g_strfreev(multi_list); + + break; + default: + break; + } + + if (parsed_complex == NULL && call_string == NULL) + continue; + + /* Comparing if the user has changed the value at all, and ignoring it if so */ + if (extcap_compare_is_default(arg, parsed_complex)) + continue; + + /* Flags are set as is, and have not true/false switch */ + if ((arg->arg_type == EXTCAP_ARG_BOOLFLAG) + && (extcap_complex_get_bool(parsed_complex) == TRUE)) { + call_string = g_strdup(" "); + } + + if (parsed_complex != NULL && call_string == NULL) + call_string = extcap_get_complex_as_string(parsed_complex); + + g_hash_table_insert(ret_hash, g_strdup(arg->call), + g_strdup(call_string)); + + g_free(call_string); + call_string = NULL; + + g_free(parsed_complex); + parsed_complex = NULL; + } + + return ret_hash; +} + +static void extcap_gtk_treeview_vscroll_map_handler(GtkTreeView *treeView, + gpointer data) { + GtkWidget *padBox = (GtkWidget*) data; + gint x, y; + + g_assert(GTK_IS_BOX(padBox)); + + /* Set the padding above the scrollbar to the height of the tree header window */ + gtk_tree_view_convert_bin_window_to_widget_coords(GTK_TREE_VIEW(treeView), + 0, 0, &x, &y); + gtk_widget_set_size_request(padBox, -1, y); +} + +static GtkWidget *extcap_gtk_wrap_scroll_treeview(GtkWidget *view) { + GtkWidget *vscroll, *padbox, *hbox, *vbox; + GtkAdjustment *padj; + +#if GTK_CHECK_VERSION(3, 0, 0) + padj = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(view)); +#if GTK_CHECK_VERSION(3, 2, 0) + vscroll = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, padj); +#else + vscroll = gtk_vscrollbar_new(padj); +#endif +#else + padj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(view)); + vscroll = gtk_vscrollbar_new(padj); +#endif + + hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0, FALSE); + + /* First insert the tree view */ + gtk_box_pack_start(GTK_BOX(hbox), view, TRUE, TRUE, 0); + gtk_widget_show(view); + + /* Pack to the right a vbox containing a box for padding at top and scrollbar */ + vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE); + gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0); + gtk_widget_show(vbox); + + padbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0, FALSE); + gtk_box_pack_start(GTK_BOX(vbox), padbox, FALSE, FALSE, 0); + gtk_widget_show(padbox); + + gtk_box_pack_start(GTK_BOX(vbox), vscroll, TRUE, TRUE, 0); + gtk_widget_show(vscroll); + + g_object_set_data(G_OBJECT(hbox), EXTCAP_GTK_DATA_KEY_TREEVIEW, view); + + g_signal_connect(view, "map", + G_CALLBACK(extcap_gtk_treeview_vscroll_map_handler), padbox); + + return hbox; +} + +GtkWidget *extcap_create_gtk_listwidget(extcap_arg *argument, + GHashTable *prev_map) { + GtkCellRenderer *renderer; + GtkTreeModel *model; + GtkWidget *view, *retview; + GtkListStore *store; + GtkTreeIter iter; + GtkTreeSelection *selection; + extcap_value *v = NULL; + GList * walker = NULL; + gchar *prev_item = NULL; + + if (g_list_length(argument->values) == 0) + return NULL ; + + view = gtk_tree_view_new(); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); + + store = gtk_list_store_new(EXTCAP_GTK_NUM_COLS, G_TYPE_STRING, + G_TYPE_POINTER); + + model = GTK_TREE_MODEL(store); + gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); + + if (prev_map != NULL) + prev_item = (gchar *) g_hash_table_lookup(prev_map, argument->call); + + for (walker = g_list_first(argument->values); walker != NULL ; walker = + walker->next) { + v = (extcap_value *) walker->data; + if (v->display == NULL) + break; + + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, EXTCAP_GTK_COL_DISPLAY, v->display, + EXTCAP_GTK_COL_VALUE, v, -1); + + if (prev_item != NULL) { + if (g_ascii_strcasecmp(prev_item, v->call) == 0) { + gtk_tree_selection_select_iter(selection, &iter); + } + } else if (v->is_default) { + gtk_tree_selection_select_iter(selection, &iter); + } + } + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "Name", + renderer, "text", EXTCAP_GTK_COL_DISPLAY, NULL); + + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); + + retview = extcap_gtk_wrap_scroll_treeview(view); + + if (gtk_tree_model_iter_n_children(model, NULL) > 3) + gtk_widget_set_size_request(retview, 0, 100); + + /* Tree view has own reference */ + g_object_unref(model); + + return retview; +} + +GtkWidget *extcap_create_gtk_radiowidget(extcap_arg *argument, + GHashTable *prev_map) { + GtkWidget *radiobox = NULL, *last_radio = NULL; + extcap_value *v = NULL; + GList * walker = NULL; + gchar *prev_item = NULL; + + if (g_list_length(argument->values) == 0) + return NULL ; + + if (prev_map != NULL) + prev_item = (gchar *) g_hash_table_lookup(prev_map, argument->call); + + radiobox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 1, FALSE); + + for (walker = g_list_first(argument->values); walker != NULL ; walker = + walker->next) { + v = (extcap_value *) walker->data; + + if (last_radio == NULL) { + last_radio = gtk_radio_button_new_with_label(NULL, v->display); + /* Set a pointer to the first radio button */ + g_object_set_data(G_OBJECT(radiobox), + EXTCAP_GTK_DATA_KEY_FIRSTRADIO, last_radio); + } else { + last_radio = gtk_radio_button_new_with_label_from_widget( + GTK_RADIO_BUTTON(last_radio), v->display); + } + + /* Set a pointer to the value used in this radio */ + g_object_set_data(G_OBJECT(last_radio), EXTCAP_GTK_DATA_KEY_VALPTR, v); + + if (prev_item != NULL) { + if (g_ascii_strcasecmp(prev_item, v->call) == 0) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(last_radio), + TRUE); + } + } else if (v->is_default) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(last_radio), TRUE); + } + + gtk_box_pack_start(GTK_BOX(radiobox), last_radio, TRUE, TRUE, 0); + gtk_widget_show(last_radio); + } + + return radiobox; +} + +static void extcap_gtk_multicheck_toggled(GtkCellRendererToggle *cell _U_, + gchar *path_str, gpointer data) { + GtkTreeModel *model = (GtkTreeModel *) data; + GtkTreeIter iter; + GtkTreePath *path = gtk_tree_path_new_from_string(path_str); + gboolean enabled; + + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, EXTCAP_GTK_MULTI_COL_CHECK, &enabled, -1); + + enabled ^= 1; + + gtk_list_store_set(GTK_LIST_STORE(model), &iter, EXTCAP_GTK_MULTI_COL_CHECK, + enabled, -1); + + gtk_tree_path_free(path); +} + +GtkWidget *extcap_create_gtk_rangewidget(extcap_arg *argument, + GHashTable *prev_map _U_) { + GtkWidget *spinButton; + GtkAdjustment *adjustment; + + gfloat def = 0.0, min = 0.0, max = 0.0; + + switch (argument->arg_type) { + case EXTCAP_ARG_INTEGER: + def = (gfloat) extcap_complex_get_int(argument->default_complex); + min = (gfloat) extcap_complex_get_int(argument->range_start); + max = (gfloat) extcap_complex_get_int(argument->range_end); + break; + case EXTCAP_ARG_UNSIGNED: + def = (gfloat) extcap_complex_get_uint(argument->default_complex); + min = (gfloat) extcap_complex_get_uint(argument->range_start); + max = (gfloat) extcap_complex_get_uint(argument->range_end); + break; + case EXTCAP_ARG_LONG: + def = (gfloat) extcap_complex_get_long(argument->default_complex); + min = (gfloat) extcap_complex_get_long(argument->range_start); + max = (gfloat) extcap_complex_get_long(argument->range_end); + break; + case EXTCAP_ARG_DOUBLE: + def = (gfloat) extcap_complex_get_double(argument->default_complex); + min = (gfloat) extcap_complex_get_double(argument->range_start); + max = (gfloat) extcap_complex_get_double(argument->range_end); + break; + default: + return NULL ; + break; + } + + adjustment = (GtkAdjustment *)gtk_adjustment_new(def, min, max, 1.0, 10.0, 0.0); + + spinButton = gtk_spin_button_new(adjustment, 0, 0); + gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spinButton), TRUE); + gtk_widget_set_size_request(spinButton, 80, -1); + + return spinButton; +} + +static void extcap_file_selectiondialog( GtkWidget *widget _U_, gpointer data ) +{ + GtkWidget * filechooser = NULL; + GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE; + gchar *filename = NULL; + gint res = 0; + extcap_arg *argument = NULL; + + if ( GTK_ENTRY(data) == NULL ) + return; + + argument = (extcap_arg *)g_object_get_data(G_OBJECT(data), EXTCAP_GTK_DATA_KEY_ARGUMENT); + if ( argument != NULL && argument->fileexists == TRUE ) + action = GTK_FILE_CHOOSER_ACTION_OPEN; + + filechooser = gtk_file_chooser_dialog_new("Select file path", NULL, action, + "_Cancel", GTK_RESPONSE_CANCEL, "_Open", GTK_RESPONSE_ACCEPT, NULL); + + res = gtk_dialog_run (GTK_DIALOG (filechooser)); + if (res == GTK_RESPONSE_ACCEPT) + { + GtkFileChooser *chooser = GTK_FILE_CHOOSER (filechooser); + filename = gtk_file_chooser_get_filename (chooser); + + /* this check might not be necessary, but just to be on the safe side */ + if ( action == GTK_FILE_CHOOSER_ACTION_OPEN && ! file_exists ( filename ) ) + filename = g_strdup ( " " ); + + gtk_entry_set_text(GTK_ENTRY(data), filename); + } + + gtk_widget_destroy (filechooser); +} + +static GtkWidget *extcap_create_gtk_fileselect(extcap_arg *argument, + GHashTable *prev_map _U_, gchar * file _U_) { + GtkWidget * entry = NULL; + GtkWidget * button = NULL; + GtkWidget * ret_box = NULL; + + button = gtk_button_new_with_label ("..."); + entry = gtk_entry_new(); + if (file != NULL) + gtk_entry_set_text(GTK_ENTRY(entry), file); + gtk_editable_set_editable (GTK_EDITABLE (entry), FALSE); + g_object_set_data(G_OBJECT(entry), EXTCAP_GTK_DATA_KEY_ARGUMENT, argument); + +#if GTK_CHECK_VERSION(3, 0, 0) + ret_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3); +#else + ret_box = gtk_hbox_new(FALSE, 3); +#endif + + gtk_box_pack_start ( GTK_BOX(ret_box), entry, TRUE, TRUE, 5 ); + gtk_widget_show(entry); + gtk_box_pack_end ( GTK_BOX(ret_box), button, FALSE, FALSE, 5 ); + gtk_widget_show(button); + + g_signal_connect (button, "clicked", + G_CALLBACK (extcap_file_selectiondialog), (gpointer) entry); + + g_object_set_data(G_OBJECT(ret_box), EXTCAP_GTK_DATA_KEY_FILENAME, entry); + + return ret_box; +} + +GtkWidget *extcap_create_gtk_multicheckwidget(extcap_arg *argument, + GHashTable *prev_map) { + GtkCellRenderer *renderer, *togglerenderer; + GtkTreeModel *model; + GtkWidget *view, *retview; + GtkListStore *store; + GtkTreeIter iter; + GtkTreeSelection *selection; + extcap_value *v = NULL; + GList * walker = NULL; + gchar *prev_item = NULL; + gchar **prev_list = NULL, **prev_iter = NULL; + gboolean prev_value, prev_matched; + + if (g_list_length(argument->values) == 0) + return NULL ; + + view = gtk_tree_view_new(); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); + + store = gtk_list_store_new(EXTCAP_GTK_MULTI_NUM_COLS, G_TYPE_BOOLEAN, + G_TYPE_STRING, G_TYPE_POINTER); + + model = GTK_TREE_MODEL(store); + gtk_tree_view_set_model(GTK_TREE_VIEW(view), model); + gtk_tree_selection_set_mode(selection, GTK_SELECTION_NONE); + + if (prev_map != NULL) + prev_item = (gchar *) g_hash_table_lookup(prev_map, argument->call); + + if (prev_item != NULL) + prev_list = g_strsplit(prev_item, ",", 0); + + for (walker = g_list_first(argument->values); walker != NULL ; walker = + walker->next) { + v = (extcap_value *) walker->data; + if (v->display == NULL) + break; + + prev_value = FALSE; + prev_matched = FALSE; + gtk_list_store_append(store, &iter); + + if (prev_list != NULL) { + prev_matched = FALSE; + prev_iter = prev_list; + + while (*prev_iter != NULL ) { + if (g_strcmp0(*prev_iter, v->call) == 0) { + prev_matched = TRUE; + prev_value = TRUE; + break; + } + + prev_iter++; + } + } + + if (prev_matched == FALSE) + prev_value = v->enabled; + + gtk_list_store_set(store, &iter, EXTCAP_GTK_MULTI_COL_CHECK, prev_value, + EXTCAP_GTK_MULTI_COL_DISPLAY, v->display, + EXTCAP_GTK_MULTI_COL_VALUE, v, -1); + } + + if (prev_list != NULL) + g_strfreev(prev_list); + + renderer = gtk_cell_renderer_text_new(); + togglerenderer = gtk_cell_renderer_toggle_new(); + g_signal_connect(togglerenderer, "toggled", + G_CALLBACK(extcap_gtk_multicheck_toggled), model); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, + "Enabled", togglerenderer, "active", EXTCAP_GTK_MULTI_COL_CHECK, + NULL); + gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "Name", + renderer, "text", EXTCAP_GTK_MULTI_COL_DISPLAY, + NULL); + + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); + + retview = extcap_gtk_wrap_scroll_treeview(view); + + if (gtk_tree_model_iter_n_children(model, NULL) > 3) + gtk_widget_set_size_request(retview, 0, 100); + + /* Tree view has own reference */ + g_object_unref(model); + + return retview; +} + +void extcap_gtk_free_args(GtkWidget *vbox) { + GList *arguments = (GList *) g_object_get_data(G_OBJECT(vbox), + EXTCAP_GTK_DATA_KEY_ARGPTR); + extcap_free_arg_list(arguments); + g_object_set_data(G_OBJECT(vbox), EXTCAP_GTK_DATA_KEY_ARGPTR, NULL); +} + +GSList *extcap_populate_gtk_vbox(GList *arguments, GtkWidget *vbox, + GHashTable *prev_map) { + GSList *widget_toplist = NULL; + + extcap_arg *arg_iter = NULL; + + extcap_complex *prev_complex = NULL; + gchar *prev_call, *default_str; + + GList * arg_list = g_list_first(arguments); + if ( arg_list == NULL ) + return NULL; + arg_iter = (extcap_arg*) (arg_list->data); + + g_object_set_data(G_OBJECT(vbox), EXTCAP_GTK_DATA_KEY_ARGPTR, arguments); + + while (arg_list != NULL ) { + GtkWidget *hbox = NULL, *label = NULL, *item = NULL; + + arg_iter = (extcap_arg*) (arg_list->data); + + /* A new storage box for label + element */ + + hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5, FALSE); + + if (prev_map != NULL + && (prev_call = (gchar *) g_hash_table_lookup(prev_map, + arg_iter->call)) != NULL) { + prev_complex = extcap_parse_complex(arg_iter->arg_type, prev_call); + } else { + prev_complex = NULL; + } + + switch (arg_iter->arg_type) { + case EXTCAP_ARG_INTEGER: + case EXTCAP_ARG_UNSIGNED: + case EXTCAP_ARG_LONG: + case EXTCAP_ARG_DOUBLE: + label = gtk_label_new(arg_iter->display); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.1f); + item = extcap_create_gtk_rangewidget(arg_iter, prev_map); + if (item == NULL) { + item = gtk_entry_new(); + + if (prev_complex != NULL) { + default_str = extcap_get_complex_as_string(prev_complex); + gtk_entry_set_text(GTK_ENTRY(item), default_str); + g_free(default_str); + } else if (arg_iter->default_complex != NULL) { + default_str = extcap_get_complex_as_string( + arg_iter->default_complex); + gtk_entry_set_text(GTK_ENTRY(item), default_str); + g_free(default_str); + } + } + break; + case EXTCAP_ARG_STRING: + label = gtk_label_new(arg_iter->display); + + item = gtk_entry_new(); + default_str = NULL; + + if (prev_complex != NULL) + default_str = extcap_get_complex_as_string(prev_complex); + else if (arg_iter->default_complex != NULL) + default_str = extcap_get_complex_as_string( + arg_iter->default_complex); + + if (default_str != NULL) { + gtk_entry_set_text(GTK_ENTRY(item), default_str); + g_free(default_str); + } + + break; + case EXTCAP_ARG_FILESELECT: + label = gtk_label_new(arg_iter->display); + default_str = NULL; + + if (prev_complex != NULL) + default_str = extcap_get_complex_as_string(prev_complex); + else if (arg_iter->default_complex != NULL) + default_str = extcap_get_complex_as_string( + arg_iter->default_complex); + + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.1f); + item = extcap_create_gtk_fileselect(arg_iter, prev_map, default_str); + if (default_str != NULL) + g_free(default_str); + break; + case EXTCAP_ARG_BOOLEAN: + case EXTCAP_ARG_BOOLFLAG: + item = gtk_check_button_new_with_label(arg_iter->display); + + if (prev_complex != NULL) { + if (extcap_complex_get_bool(prev_complex)) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item), TRUE); + } else if (arg_iter->default_complex != NULL + && extcap_complex_get_bool(arg_iter->default_complex)) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item), TRUE); + } + + break; + case EXTCAP_ARG_MENU: + break; + case EXTCAP_ARG_RADIO: + label = gtk_label_new(arg_iter->display); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.1f); + item = extcap_create_gtk_radiowidget(arg_iter, prev_map); + break; + case EXTCAP_ARG_SELECTOR: + label = gtk_label_new(arg_iter->display); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.1f); + item = extcap_create_gtk_listwidget(arg_iter, prev_map); + break; + case EXTCAP_ARG_MULTICHECK: + label = gtk_label_new(arg_iter->display); + gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.1f); + item = extcap_create_gtk_multicheckwidget(arg_iter, prev_map); + break; + default: + break; + } + + if (prev_complex != NULL) + extcap_free_complex(prev_complex); + + if (label != NULL) { + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5); + gtk_widget_show(label); + } + + if (item != NULL) { + gtk_box_pack_start(GTK_BOX(hbox), item, TRUE, TRUE, 0); + gtk_widget_show(item); + g_object_set_data(G_OBJECT(item), EXTCAP_GTK_DATA_KEY_ARGPTR, + arg_iter); + + if (arg_iter->tooltip != NULL) { + gtk_widget_set_tooltip_text(item, arg_iter->tooltip); + } + + widget_toplist = g_slist_append(widget_toplist, item); + } + + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 1); + + gtk_widget_show(hbox); + + arg_list = arg_list->next; + } + + return widget_toplist; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=4 tabstop=4 noexpandtab: + * :indentSize=4:tabSize=4:noTabs=false: + */ diff --git a/ui/gtk/extcap_gtk.h b/ui/gtk/extcap_gtk.h new file mode 100644 index 0000000000..07a8f21e7b --- /dev/null +++ b/ui/gtk/extcap_gtk.h @@ -0,0 +1,122 @@ +/* extcap_gtk.c + * + * 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 __EXTCAP_GTK_H__ +#define __EXTCAP_GTK_H__ + +#include <stdio.h> +#include <string.h> + +#include <glib.h> +#include <gtk/gtk.h> + +#include <extcap_parser.h> + +/* + * GObject data keys for linking argument records to the gtk + * UI + */ +#define EXTCAP_GTK_DATA_KEY_ARGPTR "EXTCAP_ARGPTR" +#define EXTCAP_GTK_DATA_KEY_VALPTR "EXTCAP_VALPTR" +#define EXTCAP_GTK_DATA_KEY_FIRSTRADIO "EXTCAP_FIRSTRADIO" +#define EXTCAP_GTK_DATA_KEY_WIDGETLIST "EXTCAP_WIDGETLIST" +#define EXTCAP_GTK_DATA_KEY_TREEVIEW "EXTCAP_TREEVIEW" +#define EXTCAP_GTK_DATA_KEY_FILENAME "EXTCAP_FILENAME" +#define EXTCAP_GTK_DATA_KEY_ARGUMENT "EXTCAP_ARGUMENT" + +/* + * GTK UI / EXTCAP Linkage: + * + * Packed vbox of widgets + * Contains EXTCAP_WIDGETLIST pointing to enclosed widget list + * + * GSList gtk_ui_widgets + * Linked list of drawable widgets in the UI + * + * GtkWidget contained in GSList + * Drawn GTK UI element. If UI element is directly linked + * to argument, will contain EXTCAP_ARGPTR. + * + * Top-level GTK widgets will include text boxes, sliders + * (if supported), and checkboxes. + * + * If the top level widget contains radio buttons, it will + * contain an EXTCAP_ARGPTR *and* an EXTCAP_FIRSTRADIO + * + * Radio buttons + * Each radio button will contain an EXTCAP_VALPTR reference + * to the extcap_value * value being used. + * + * Selectors + * Each selector row contains a pointer to the value, in the + * column COL_VALUE + * + */ + +enum extcap_gtk_col_types { + EXTCAP_GTK_COL_DISPLAY = 0, EXTCAP_GTK_COL_VALUE = 1, EXTCAP_GTK_NUM_COLS +}; + +enum extcap_gtk_multi_col_types { + EXTCAP_GTK_MULTI_COL_CHECK = 0, + EXTCAP_GTK_MULTI_COL_DISPLAY = 1, + EXTCAP_GTK_MULTI_COL_VALUE = 2, + EXTCAP_GTK_MULTI_NUM_COLS +}; + +/* Get a hash map of calls and values from the top widget */ +GHashTable *extcap_gtk_get_state(GtkWidget *widget); + +GtkWidget *extcap_create_gtk_rangewidget(extcap_arg *argument, + GHashTable *prev_map); +GtkWidget *extcap_create_gtk_listwidget(extcap_arg *argument, + GHashTable *prev_map); +GtkWidget *extcap_create_gtk_radiowidget(extcap_arg *argument, + GHashTable *prev_map); +GtkWidget *extcap_create_gtk_multicheckwidget(extcap_arg *argument, + GHashTable *prev_map); + +/* + * Populate a (pre-created) container widget based on an arguments record. + * For secondary repopulations, a saved state can be passed to populate + * with known values. This should occur when setting interface options + * repeatedly, for example + */ +GSList *extcap_populate_gtk_vbox(GList *arguments, GtkWidget *vbox, + GHashTable *prev_map); + +/* Free args associated with a GTK item */ +void extcap_gtk_free_args(GtkWidget *vbox); + +#endif + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=4 tabstop=4 noexpandtab: + * :indentSize=4:tabSize=4:noTabs=false: + */ diff --git a/ui/iface_lists.c b/ui/iface_lists.c index a9dd10c72b..1cf3dfeffb 100644 --- a/ui/iface_lists.c +++ b/ui/iface_lists.c @@ -136,6 +136,9 @@ scan_local_interfaces(void (*update_cb)(void)) temp->vendor_description = g_strdup(if_info->vendor_description); temp->loopback = if_info->loopback; temp->type = if_info->type; +#ifdef HAVE_EXTCAP + temp->extcap = g_strdup(if_info->extcap); +#endif /* Is this interface hidden and, if so, should we include it anyway? */ /* Do we have a user-supplied description? */ @@ -318,6 +321,11 @@ scan_local_interfaces(void (*update_cb)(void)) } } } + +#ifdef HAVE_EXTCAP + /* Extcap devices start with no cached args */ + device.external_cap_args_settings = NULL; +#endif if (global_capture_opts.all_ifaces->len <= count) { g_array_append_val(global_capture_opts.all_ifaces, device); count = global_capture_opts.all_ifaces->len; @@ -374,6 +382,9 @@ scan_local_interfaces(void (*update_cb)(void)) device.if_info.vendor_description = g_strdup(interface_opts.descr); device.if_info.addrs = NULL; device.if_info.loopback = FALSE; +#ifdef HAVE_EXTCAP + device.if_info.extcap = g_strdup(interface_opts.extcap); +#endif g_array_append_val(global_capture_opts.all_ifaces, device); global_capture_opts.num_selected++; diff --git a/ui/qt/QtShark.pro b/ui/qt/QtShark.pro index f50f94e0ae..291ee8512f 100644 --- a/ui/qt/QtShark.pro +++ b/ui/qt/QtShark.pro @@ -192,6 +192,8 @@ SOURCES_WS_C = \ ../../capture_opts.c \ ../../cfile.c \ ../../color_filters.c \ + ../../extcap.c \ + ../../extcap_parser.c \ ../../file.c \ ../../fileset.c \ ../../frame_tvbuff.c \ diff --git a/ui/qt/about_dialog.cpp b/ui/qt/about_dialog.cpp index 231645ee4f..1d54f6690b 100644 --- a/ui/qt/about_dialog.cpp +++ b/ui/qt/about_dialog.cpp @@ -239,6 +239,19 @@ AboutDialog::AboutDialog(QWidget *parent) : g_free(path); #endif +#ifdef HAVE_EXTCAP + /* Extcap */ + constpath = get_extcap_dir(); + + resultArray = g_strsplit(constpath, G_SEARCHPATH_SEPARATOR_S, 10); + + for(i = 0; resultArray[i]; i++) { + message += about_folders_row("Extcap path", g_strstrip(resultArray[i]), + "Extcap Plugins search path"); + } + g_strfreev(resultArray); +#endif + message += "</table>"; ui->label_folders->setText(message); diff --git a/wsutil/filesystem.c b/wsutil/filesystem.c index 6125236bd7..99b3e5340e 100644 --- a/wsutil/filesystem.c +++ b/wsutil/filesystem.c @@ -1062,6 +1062,117 @@ get_plugin_dir(void) #endif } +#if defined(HAVE_EXTCAP) +/* + * Find the directory where the extcap hooks are stored. + * + * On Windows, we use the "extcap" subdirectory of the datafile directory. + * + * On UN*X, we use the EXTCAP_DIR value supplied by the configure + * script, unless we think we're being run from the build directory, + * in which case we use the "extcap" subdirectory of the datafile directory. + * + * In both cases, we then use the subdirectory of that directory whose + * name is the version number. + * + * XXX - if we think we're being run from the build directory, perhaps we + * should have the extcap code not look in the version subdirectory + * of the extcap directory, but look in all of the subdirectories + * of the extcap directory, so it can just fetch the extcap hooks built + * as part of the build process. + */ +static const char *extcap_dir = NULL; + +static void init_extcap_dir(void) { +#ifdef _WIN32 + /* + * On Windows, the data file directory is the installation + * directory; the extcap hooks are stored under it. + * + * Assume we're running the installed version of Wireshark; + * on Windows, the data file directory is the directory + * in which the Wireshark binary resides. + */ + extcap_dir = g_strdup_printf("%s\\extcap\\%s", get_datafile_dir(), + VERSION); + + /* + * Make sure that pathname refers to a directory. + */ + if (test_for_directory(extcap_dir) != EISDIR) { + /* + * Either it doesn't refer to a directory or it + * refers to something that doesn't exist. + * + * Assume that means we're running a version of + * Wireshark we've built in a build directory, + * in which case {datafile dir}\plugins is the + * top-level extcap hooks source directory, and use + * that directory and set the "we're running in + * a build directory" flag, so the plugin + * scanner will check all subdirectories of that + * directory for extcap hooks. + */ + g_free( (gpointer) extcap_dir); + extcap_dir = g_strdup_printf("%s\\extcap", get_datafile_dir()); + running_in_build_directory_flag = TRUE; + } +#else + if (running_in_build_directory_flag) { + /* + * We're (probably) being run from the build directory and + * weren't started with special privileges, so we'll use + * the "extcap hooks" subdirectory of the directory where the program + * we're running is (that's the build directory). + */ + extcap_dir = g_strdup_printf("%s/extcap", get_progfile_dir()); + } else { + if (getenv("WIRESHARK_EXTCAP_DIR") && !started_with_special_privs()) { + /* + * The user specified a different directory for extcap hooks + * and we aren't running with special privileges. + */ + extcap_dir = g_strdup(getenv("WIRESHARK_EXTCAP_DIR")); + } +#ifdef __APPLE__ + /* + * If we're running from an app bundle and weren't started + * with special privileges, use the Contents/Resources/lib/wireshark/extcap + * subdirectory of the app bundle. + * + * (appbundle_dir is not set to a non-null value if we're + * started with special privileges, so we need only check + * it; we don't need to call started_with_special_privs().) + */ + else if (appbundle_dir != NULL) { + extcap_dir = g_strdup_printf("%s/Contents/Resources/lib/wireshark/extcap", + appbundle_dir); + } +#endif + else { + extcap_dir = EXTCAP_DIR; + } + } +#endif +} +#endif /* HAVE_EXTCAP */ + +/* + * Get the directory in which the extcap hooks are stored. + * + * XXX - A fix instead of HAVE_EXTCAP must be found + */ +const char * +get_extcap_dir(void) { +#if defined(HAVE_EXTCAP) + if (!extcap_dir) + init_extcap_dir(); + return extcap_dir; +#else + return NULL; +#endif +} + /* * Get the flag indicating whether we're running from a build * directory. diff --git a/wsutil/filesystem.h b/wsutil/filesystem.h index 6a5bfe54d0..bb066d37e3 100644 --- a/wsutil/filesystem.h +++ b/wsutil/filesystem.h @@ -55,6 +55,13 @@ WS_DLL_PUBLIC const char *get_progfile_dir(void); WS_DLL_PUBLIC const char *get_plugin_dir(void); /* + * Get the directory in which extcap hooks are stored; this must not be called + * before init_progfile_dir() is called, as they might be stored in a + * subdirectory of the program file directory. + */ +WS_DLL_PUBLIC const char *get_extcap_dir(void); + +/* * Get the flag indicating whether we're running from a build * directory. */ |