3 * Routines for extcap external capture
4 * Copyright 2013, Mike Ryan <mikeryan@lacklustre.net>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
14 #define WS_LOG_DOMAIN LOG_DOMAIN_EXTCAP
25 /* Include for unlink */
29 #include <sys/types.h>
33 #include <epan/prefs.h>
34 #include <epan/prefs-int.h>
36 #include "ui/iface_toolbar.h"
38 #include <wsutil/file_util.h>
39 #include <wsutil/filesystem.h>
40 #include <wsutil/ws_pipe.h>
41 #include <wsutil/tempfile.h>
42 #include <wsutil/wslog.h>
43 #include <wsutil/ws_assert.h>
44 #include <wsutil/version_info.h>
46 #include "capture/capture_session.h"
47 #include "capture_opts.h"
50 #include "extcap_parser.h"
52 /* Number of seconds to wait for extcap process to exit after cleanup.
53 * If extcap does not exit before the timeout, it is forcefully terminated.
56 /* Extcap interface does not specify SIGTERM replacement on Windows yet */
57 #define EXTCAP_CLEANUP_TIMEOUT 0
59 #define EXTCAP_CLEANUP_TIMEOUT 30
62 /* internal container, for all the extcap executables that have been found.
63 * Will be reset if extcap_clear_interfaces() is being explicitly called
64 * and is being used for printing information about all extcap interfaces found,
65 * as well as storing all sub-interfaces
67 static GHashTable
* _loaded_interfaces
;
69 /* Internal container, which maps each ifname to the tool providing it, for faster
70 * lookup. The key and string value are owned by this table.
72 static GHashTable
* _tool_for_ifname
;
74 /* internal container, for all the extcap executables that have been found
75 * and that provides a toolbar with controls to be added to a Interface Toolbar
77 static GHashTable
*_toolbars
;
79 /* internal container, to map preference names to pointers that hold preference
80 * values. These ensure that preferences can survive extcap if garbage
81 * collection, and does not lead to dangling pointers in the prefs subsystem.
83 static GHashTable
*_extcap_prefs_dynamic_vals
;
85 typedef struct _extcap_callback_info_t
92 } extcap_callback_info_t
;
94 /* Callback definition for extcap_run_one.
95 * N.B.: extcap_run_one does not use the return value, which is
96 * vestigial from extcap_foreach, which no longer exists.
97 * Now extcap operations are run in parallel in multiple threads.
99 typedef bool(*extcap_cb_t
)(extcap_callback_info_t info_structure
);
101 /** GThreadPool does not support pushing new work from a thread while waiting
102 * for the thread pool to finish. This data structure tracks ongoing work.
103 * See https://gitlab.gnome.org/GNOME/glib/issues/1598 */
104 typedef struct thread_pool
{
106 int count
; /**< Number of tasks that have not finished. */
112 * Callback definition for extcap_run_all, invoked with a thread pool (to
113 * schedule new tasks), an opaque data parameter, and the output from last task
114 * (or NULL if it failed). The output must be freed by the callback function.
115 * The implementation MUST be thread-safe.
117 typedef void (*extcap_run_cb_t
)(thread_pool_t
*pool
, void *data
, char *output
);
119 typedef struct extcap_run_task
{
120 const char *extcap_path
;
121 char **argv
; /**< NULL-terminated arguments list, freed when the task is completed. */
122 extcap_run_cb_t output_cb
;
123 void *data
; /** Parameter to be passed to output_cb. */
126 typedef struct extcap_iface_info
{
127 char *ifname
; /**< Interface name. */
128 char *output
; /**< Output of --extcap-config. */
129 } extcap_iface_info_t
;
131 typedef struct extcap_run_extcaps_info
{
132 char *extcap_path
; /**< Extcap program path, MUST be the first member. */
133 char *output
; /**< Output of --extcap-interfaces. */
134 unsigned num_interfaces
; /**< Number of discovered interfaces. */
135 extcap_iface_info_t
*iface_infos
; /**< Per-interface information. */
136 } extcap_run_extcaps_info_t
;
139 static void extcap_load_interface_list(void);
141 /* Used for lazily loading our interfaces. */
142 static void extcap_ensure_all_interfaces_loaded(void) {
143 if ( !_loaded_interfaces
|| g_hash_table_size(_loaded_interfaces
) == 0 )
144 extcap_load_interface_list();
148 thread_pool_push(thread_pool_t
*pool
, void *data
, GError
**error
)
150 g_mutex_lock(&pool
->data_mutex
);
152 g_mutex_unlock(&pool
->data_mutex
);
153 return g_thread_pool_push(pool
->pool
, data
, error
);
157 thread_pool_wait(thread_pool_t
*pool
)
159 g_mutex_lock(&pool
->data_mutex
);
160 while (pool
->count
!= 0) {
161 g_cond_wait(&pool
->cond
, &pool
->data_mutex
);
163 g_mutex_unlock(&pool
->data_mutex
);
167 extcap_loaded_interfaces(void)
169 if (prefs
.capture_no_extcap
)
172 extcap_ensure_all_interfaces_loaded();
174 return _loaded_interfaces
;
178 extcap_clear_interfaces(void)
180 if ( _loaded_interfaces
)
181 g_hash_table_destroy(_loaded_interfaces
);
182 _loaded_interfaces
= NULL
;
184 if ( _tool_for_ifname
)
185 g_hash_table_destroy(_tool_for_ifname
);
186 _tool_for_ifname
= NULL
;
190 compare_tools(const void *a
, const void *b
)
192 return g_strcmp0((*(extcap_info
*const *)a
)->basename
, (*(extcap_info
*const *)b
)->basename
);
196 extcap_get_descriptions(extcap_plugin_description_callback callback
, void *callback_data
)
198 extcap_ensure_all_interfaces_loaded();
200 GHashTable
* tools
= extcap_loaded_interfaces();
201 GPtrArray
*tools_array
= g_ptr_array_new();
203 if (tools
&& g_hash_table_size(tools
) > 0) {
204 GList
* keys
= g_hash_table_get_keys(tools
);
205 GList
* walker
= g_list_first(keys
);
206 while (walker
&& walker
->data
) {
207 extcap_info
* tool
= (extcap_info
*)g_hash_table_lookup(tools
, walker
->data
);
209 g_ptr_array_add(tools_array
, tool
);
211 walker
= g_list_next(walker
);
216 g_ptr_array_sort(tools_array
, compare_tools
);
218 for (unsigned i
= 0; i
< tools_array
->len
; i
++) {
219 extcap_info
*tool
= (extcap_info
*)tools_array
->pdata
[i
];
220 callback(tool
->basename
, tool
->version
, "extcap", tool
->full_path
, callback_data
);
223 g_ptr_array_free(tools_array
, true);
227 print_extcap_description(const char *basename
, const char *version
,
228 const char *description
, const char *filename
,
231 printf("%-16s\t%s\t%s\t%s\n", basename
, version
, description
, filename
);
235 extcap_dump_all(void)
237 extcap_get_descriptions(print_extcap_description
, NULL
);
241 extcap_get_extcap_paths_from_dir(GSList
* list
, const char * dirname
)
246 GSList
* paths
= list
;
248 if ((dir
= g_dir_open(dirname
, 0, NULL
)) != NULL
) {
249 while ((file
= g_dir_read_name(dir
)) != NULL
) {
250 /* full path to extcap binary */
251 char *extcap_path
= ws_strdup_printf("%s" G_DIR_SEPARATOR_S
"%s", dirname
, file
);
252 /* treat anything executable as an extcap binary */
253 if (g_file_test(extcap_path
, G_FILE_TEST_IS_REGULAR
) &&
254 g_file_test(extcap_path
, G_FILE_TEST_IS_EXECUTABLE
)) {
255 paths
= g_slist_append(paths
, extcap_path
);
268 * Obtains a list of extcap program paths. Use g_slist_free_full(paths, g_free)
269 * to destroy the list.
272 extcap_get_extcap_paths(void)
274 GSList
*paths
= NULL
;
276 paths
= extcap_get_extcap_paths_from_dir(paths
, get_extcap_pers_dir());
277 paths
= extcap_get_extcap_paths_from_dir(paths
, get_extcap_dir());
282 static extcap_interface
*
283 extcap_find_interface_for_ifname(const char *ifname
)
285 extcap_interface
* result
= NULL
;
287 if ( !ifname
|| ! _tool_for_ifname
|| ! _loaded_interfaces
)
290 char * extcap_util
= (char *)g_hash_table_lookup(_tool_for_ifname
, ifname
);
294 extcap_info
* element
= (extcap_info
*)g_hash_table_lookup(_loaded_interfaces
, extcap_util
);
298 GList
* walker
= element
->interfaces
;
299 while ( walker
&& walker
->data
&& ! result
)
301 extcap_interface
* interface
= (extcap_interface
*)walker
->data
;
302 if ( g_strcmp0(interface
->call
, ifname
) == 0 )
308 walker
= g_list_next ( walker
);
315 extcap_free_toolbar(void *data
)
322 iface_toolbar
*toolbar
= (iface_toolbar
*)data
;
324 g_free(toolbar
->menu_title
);
325 g_free(toolbar
->help
);
326 g_list_free_full(toolbar
->ifnames
, g_free
);
327 g_list_free_full(toolbar
->controls
, (GDestroyNotify
)extcap_free_toolbar_control
);
332 extcap_if_executable(const char *ifname
)
334 extcap_interface
*interface
= extcap_find_interface_for_ifname(ifname
);
335 return interface
!= NULL
? interface
->extcap_path
: NULL
;
339 extcap_iface_toolbar_add(const char *extcap
, iface_toolbar
*toolbar_entry
)
344 if (!extcap
|| !toolbar_entry
)
349 toolname
= g_path_get_basename(extcap
);
351 if (!g_hash_table_lookup(_toolbars
, toolname
))
353 g_hash_table_insert(_toolbars
, g_strdup(toolname
), toolbar_entry
);
362 extcap_convert_arguments_to_array(GList
* arguments
)
364 char ** result
= NULL
;
367 GList
* walker
= g_list_first(arguments
);
370 result
= (char **) g_malloc0(sizeof(char *) * (g_list_length(arguments
)));
374 result
[cnt
] = g_strdup((const char *)walker
->data
);
375 walker
= g_list_next(walker
);
382 static void extcap_free_array(char ** args
, int argc
)
386 for ( cnt
= 0; cnt
< argc
; cnt
++ )
392 extcap_free_extcaps_info_array(extcap_run_extcaps_info_t
*infos
, unsigned count
)
394 for (unsigned i
= 0; i
< count
; i
++) {
395 g_free(infos
[i
].extcap_path
);
396 g_free(infos
[i
].output
);
397 for (unsigned j
= 0; j
< infos
[i
].num_interfaces
; j
++) {
398 extcap_iface_info_t
*iface_info
= &infos
[i
].iface_infos
[j
];
399 g_free(iface_info
->ifname
);
400 g_free(iface_info
->output
);
402 g_free(infos
[i
].iface_infos
);
408 extcap_run_one(const extcap_interface
*interface
, GList
*arguments
, extcap_cb_t cb
, void *user_data
, char **err_str
)
410 const char *dirname
= get_extcap_dir();
411 char **args
= extcap_convert_arguments_to_array(arguments
);
412 int cnt
= g_list_length(arguments
);
413 char *command_output
;
414 if (ws_pipe_spawn_sync(dirname
, interface
->extcap_path
, cnt
, args
, &command_output
)) {
415 extcap_callback_info_t cb_info
= {
416 .ifname
= interface
->call
,
417 .extcap
= interface
->extcap_path
,
418 .output
= command_output
,
423 g_free(command_output
);
425 extcap_free_array(args
, cnt
);
428 /** Thread callback to run an extcap program and pass its output. */
430 extcap_thread_callback(void *data
, void *user_data
)
432 extcap_run_task_t
*task
= (extcap_run_task_t
*)data
;
433 thread_pool_t
*pool
= (thread_pool_t
*)user_data
;
434 const char *dirname
= get_extcap_dir();
436 char *command_output
;
437 if (ws_pipe_spawn_sync(dirname
, task
->extcap_path
, g_strv_length(task
->argv
), task
->argv
, &command_output
)) {
438 task
->output_cb(pool
, task
->data
, command_output
);
440 task
->output_cb(pool
, task
->data
, NULL
);
442 g_strfreev(task
->argv
);
445 // Notify when all tasks are completed and no new subtasks were created.
446 g_mutex_lock(&pool
->data_mutex
);
447 if (--pool
->count
== 0) {
448 g_cond_signal(&pool
->cond
);
450 g_mutex_unlock(&pool
->data_mutex
);
454 * Run all extcap programs with the given arguments list, invoke the callback to
455 * do some processing and return the results.
457 * @param [IN] argv NULL-terminated arguments list.
458 * @param [IN] output_cb Thread callback function that receives the output.
459 * @param [IN] data_size Size of the per-program information that will be returned.
460 * @param [OUT] count Size of the returned array.
461 * @return Array of information or NULL if there are none. The first member of
462 * each element (char *extcap_path) must be freed.
465 extcap_run_all(const char *argv
[], extcap_run_cb_t output_cb
, size_t data_size
, unsigned *count
)
467 /* Need enough space for at least 'extcap_path'. */
468 ws_assert(data_size
>= sizeof(char *));
470 GSList
*paths
= extcap_get_extcap_paths();
472 int max_threads
= (int)g_get_num_processors();
479 uint64_t start_time
= g_get_monotonic_time();
480 unsigned paths_count
= g_slist_length(paths
);
481 /* GSList is not thread-safe, so pre-allocate an array instead. */
482 void *infos
= g_malloc0_n(paths_count
, data_size
);
485 pool
.pool
= g_thread_pool_new(extcap_thread_callback
, &pool
, max_threads
, false, NULL
);
487 g_cond_init(&pool
.cond
);
488 g_mutex_init(&pool
.data_mutex
);
490 for (GSList
*path
= paths
; path
; path
= g_slist_next(path
), i
++) {
491 extcap_run_task_t
*task
= g_new0(extcap_run_task_t
, 1);
493 task
->extcap_path
= (char *)path
->data
;
494 task
->argv
= g_strdupv((char **)argv
);
495 task
->output_cb
= output_cb
;
496 task
->data
= ((char *)infos
) + (i
* data_size
);
497 *((char **)task
->data
) = (char *)path
->data
;
499 thread_pool_push(&pool
, task
, NULL
);
501 g_slist_free(paths
); /* Note: the contents are transferred to 'infos'. */
503 /* Wait for all (sub)tasks to complete. */
504 thread_pool_wait(&pool
);
506 g_mutex_clear(&pool
.data_mutex
);
507 g_cond_clear(&pool
.cond
);
508 g_thread_pool_free(pool
.pool
, false, true);
510 ws_debug("extcap: completed discovery of %d tools in %.3fms",
511 paths_count
, (g_get_monotonic_time() - start_time
) / 1000.0);
512 *count
= paths_count
;
516 static void extcap_free_dlt(void *d
, void *user_data _U_
)
523 g_free(((extcap_dlt
*)d
)->name
);
524 g_free(((extcap_dlt
*)d
)->display
);
528 static void extcap_free_dlts(GList
*dlts
)
530 g_list_foreach(dlts
, extcap_free_dlt
, NULL
);
534 static bool cb_dlt(extcap_callback_info_t cb_info
)
536 GList
*dlts
= NULL
, *temp
= NULL
;
538 if_capabilities_t
*caps
;
539 GList
*linktype_list
= NULL
;
540 data_link_info_t
*data_link_info
;
541 extcap_dlt
*dlt_item
;
543 dlts
= extcap_parse_dlts(cb_info
.output
);
546 ws_debug("Extcap pipe %s ", cb_info
.extcap
);
549 * Allocate the interface capabilities structure.
551 caps
= (if_capabilities_t
*) g_malloc0(sizeof * caps
);
552 caps
->can_set_rfmon
= false;
553 caps
->timestamp_types
= NULL
;
557 dlt_item
= (extcap_dlt
*)dlts
->data
;
560 ws_debug(" DLT %d name=\"%s\" display=\"%s\" ", dlt_item
->number
,
561 dlt_item
->name
, dlt_item
->display
);
563 data_link_info
= g_new(data_link_info_t
, 1);
564 data_link_info
->dlt
= dlt_item
->number
;
565 data_link_info
->name
= g_strdup(dlt_item
->name
);
566 data_link_info
->description
= g_strdup(dlt_item
->display
);
567 linktype_list
= g_list_append(linktype_list
, data_link_info
);
570 dlts
= g_list_next(dlts
);
573 /* Check to see if we built a list */
574 if (linktype_list
!= NULL
)
576 caps
->data_link_types
= linktype_list
;
580 caps
->primary_msg
= g_strdup("Extcap returned no DLTs");
583 ws_debug(" returned no DLTs");
584 *(cb_info
.err_str
) = g_strdup(caps
->primary_msg
);
587 if (cb_info
.data
!= NULL
)
589 *(if_capabilities_t
**) cb_info
.data
= caps
;
593 free_if_capabilities(caps
);
595 /* TODO: free_if_capabilities is in capture-pcap-util.c and doesn't
596 * get defined unless HAVE_LIBPCAP is set.
602 extcap_free_dlts(temp
);
608 extcap_get_if_dlts(const char *ifname
, char **err_str
)
610 GList
* arguments
= NULL
;
611 if_capabilities_t
*caps
= NULL
;
618 /* Update the extcap interfaces and get a list of their if_infos */
619 extcap_ensure_all_interfaces_loaded();
621 extcap_interface
*interface
= extcap_find_interface_for_ifname(ifname
);
624 arguments
= g_list_append(arguments
, g_strdup(EXTCAP_ARGUMENT_LIST_DLTS
));
625 arguments
= g_list_append(arguments
, g_strdup(EXTCAP_ARGUMENT_INTERFACE
));
626 arguments
= g_list_append(arguments
, g_strdup(ifname
));
628 extcap_run_one(interface
, arguments
, cb_dlt
, &caps
, err_str
);
630 g_list_free_full(arguments
, g_free
);
636 static void extcap_free_interface(void *i
)
639 extcap_interface
*interface
= (extcap_interface
*)i
;
646 g_free(interface
->call
);
647 g_free(interface
->display
);
648 g_free(interface
->version
);
649 g_free(interface
->help
);
650 g_free(interface
->extcap_path
);
654 static void extcap_free_interfaces(GList
*interfaces
)
656 if (interfaces
== NULL
)
661 g_list_free_full(interfaces
, extcap_free_interface
);
665 if_info_compare(const void *a
, const void *b
)
668 const if_info_t
*if_a
= (const if_info_t
*)a
;
669 const if_info_t
*if_b
= (const if_info_t
*)b
;
671 if ((comp
= g_strcmp0(if_a
->name
, if_b
->name
)) == 0)
673 return g_strcmp0(if_a
->friendly_name
, if_b
->friendly_name
);
680 extcap_get_help_for_ifname(const char *ifname
)
682 extcap_ensure_all_interfaces_loaded();
684 extcap_interface
*interface
= extcap_find_interface_for_ifname(ifname
);
685 return interface
!= NULL
? interface
->help
: NULL
;
689 append_extcap_interface_list(GList
*list
)
691 GList
*interface_list
= NULL
;
692 extcap_interface
*data
= NULL
;
693 GList
*ifutilkeys_head
= NULL
, *ifutilkeys
= NULL
;
695 if (prefs
.capture_no_extcap
)
698 /* Update the extcap interfaces and get a list of their if_infos */
699 extcap_ensure_all_interfaces_loaded();
701 ifutilkeys_head
= g_hash_table_get_keys(_loaded_interfaces
);
702 ifutilkeys
= ifutilkeys_head
;
703 while ( ifutilkeys
&& ifutilkeys
->data
)
705 extcap_info
* extinfo
=
706 (extcap_info
*) g_hash_table_lookup(_loaded_interfaces
, (char *)ifutilkeys
->data
);
707 GList
* walker
= extinfo
->interfaces
;
708 while ( walker
&& walker
->data
)
710 interface_list
= g_list_append(interface_list
, walker
->data
);
711 walker
= g_list_next(walker
);
714 ifutilkeys
= g_list_next(ifutilkeys
);
716 g_list_free(ifutilkeys_head
);
719 interface_list
= g_list_sort(interface_list
, if_info_compare
);
721 /* Append the interfaces in that list to the list we're handed. */
722 while (interface_list
!= NULL
)
724 GList
*entry
= g_list_first(interface_list
);
725 data
= (extcap_interface
*)entry
->data
;
726 interface_list
= g_list_delete_link(interface_list
, entry
);
728 if_info_t
* if_info
= g_new0(if_info_t
, 1);
729 if_info
->name
= g_strdup(data
->call
);
730 if_info
->friendly_name
= g_strdup(data
->display
);
732 if_info
->type
= IF_EXTCAP
;
734 if_info
->extcap
= g_strdup(data
->extcap_path
);
736 list
= g_list_append(list
, if_info
);
742 void extcap_register_preferences(void)
744 /* Unconditionally register the extcap configuration file, so that
745 * it is copied if we copy the profile even if we're not going to
746 * read it because extcaps are disabled.
748 profile_register_persconffile("extcap.cfg");
750 if (prefs
.capture_no_extcap
)
753 module_t
*dev_module
= prefs_find_module("extcap");
760 // Will load information about extcaps and their supported config.
761 extcap_ensure_all_interfaces_loaded();
765 * Releases the dynamic preference value pointers. Must not be called before
766 * prefs_cleanup since these pointers could still be in use.
768 void extcap_cleanup(void)
770 if (_extcap_prefs_dynamic_vals
)
771 g_hash_table_destroy(_extcap_prefs_dynamic_vals
);
773 if (_loaded_interfaces
)
774 g_hash_table_destroy(_loaded_interfaces
);
776 if (_tool_for_ifname
)
777 g_hash_table_destroy(_tool_for_ifname
);
781 * Obtains a pointer which can store a value for the given preference name.
782 * The preference name that can be passed to the prefs API is stored into
785 * Extcap interfaces (and their preferences) are dynamic, they can be created
786 * and destroyed at will. Thus their data structures are insufficient to pass to
787 * the preferences APIs which require pointers which are valid until the
788 * preferences are removed (at exit).
790 static char **extcap_prefs_dynamic_valptr(const char *name
, char **pref_name
)
793 if (!_extcap_prefs_dynamic_vals
)
795 /* Initialize table only as needed, most preferences are not dynamic */
796 _extcap_prefs_dynamic_vals
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
799 if (!g_hash_table_lookup_extended(_extcap_prefs_dynamic_vals
, name
,
800 (void * *)pref_name
, (void * *)&valp
))
802 /* New dynamic pref, allocate, initialize and store. */
803 valp
= g_new0(char *, 1);
804 *pref_name
= g_strdup(name
);
805 g_hash_table_insert(_extcap_prefs_dynamic_vals
, *pref_name
, valp
);
810 void extcap_free_if_configuration(GList
*list
, bool free_args
)
814 for (elem
= g_list_first(list
); elem
; elem
= elem
->next
)
816 if (elem
->data
!= NULL
)
818 sl
= g_list_first((GList
*)elem
->data
);
821 extcap_free_arg_list(sl
);
833 extcap_pref_for_argument(const char *ifname
, struct _extcap_arg
*arg
)
835 struct preference
*pref
= NULL
;
837 extcap_ensure_all_interfaces_loaded();
839 GRegex
*regex_name
= g_regex_new("[-]+", G_REGEX_RAW
, (GRegexMatchFlags
) 0, NULL
);
840 GRegex
*regex_ifname
= g_regex_new("(?![a-zA-Z0-9_]).", G_REGEX_RAW
, (GRegexMatchFlags
) 0, NULL
);
841 if (regex_name
&& regex_ifname
)
843 if (prefs_find_module("extcap"))
845 char *pref_name
= g_regex_replace(regex_name
, arg
->call
, strlen(arg
->call
), 0, "", (GRegexMatchFlags
) 0, NULL
);
846 char *ifname_underscore
= g_regex_replace(regex_ifname
, ifname
, strlen(ifname
), 0, "_", (GRegexMatchFlags
) 0, NULL
);
847 char *ifname_lowercase
= g_ascii_strdown(ifname_underscore
, -1);
848 char *pref_ifname
= g_strconcat(ifname_lowercase
, ".", pref_name
, NULL
);
850 pref
= prefs_find_preference(prefs_find_module("extcap"), pref_ifname
);
853 g_free(ifname_underscore
);
854 g_free(ifname_lowercase
);
860 g_regex_unref(regex_name
);
864 g_regex_unref(regex_ifname
);
870 static bool cb_preference(extcap_callback_info_t cb_info
)
872 bool new_pref
= false;
873 GList
*arguments
= NULL
;
874 GList
**il
= (GList
**) cb_info
.data
;
875 module_t
*dev_module
= NULL
;
877 arguments
= extcap_parse_args(cb_info
.output
);
879 dev_module
= prefs_find_module("extcap");
883 GList
*walker
= arguments
;
885 GRegex
*regex_name
= g_regex_new("[-]+", G_REGEX_RAW
, (GRegexMatchFlags
) 0, NULL
);
886 GRegex
*regex_ifname
= g_regex_new("(?![a-zA-Z0-9_]).", G_REGEX_RAW
, (GRegexMatchFlags
) 0, NULL
);
887 if (regex_name
&& regex_ifname
)
889 while (walker
!= NULL
)
891 extcap_arg
*arg
= (extcap_arg
*)walker
->data
;
892 arg
->device_name
= g_strdup(cb_info
.ifname
);
896 char *pref_name
= g_regex_replace(regex_name
, arg
->call
, strlen(arg
->call
), 0, "", (GRegexMatchFlags
) 0, NULL
);
897 char *ifname_underscore
= g_regex_replace(regex_ifname
, cb_info
.ifname
, strlen(cb_info
.ifname
), 0, "_", (GRegexMatchFlags
) 0, NULL
);
898 char *ifname_lowercase
= g_ascii_strdown(ifname_underscore
, -1);
899 char *pref_ifname
= g_strconcat(ifname_lowercase
, ".", pref_name
, NULL
);
901 if (prefs_find_preference(dev_module
, pref_ifname
) == NULL
)
903 char *pref_name_for_prefs
;
904 char *pref_title
= wmem_strdup(wmem_epan_scope(), arg
->display
);
906 arg
->pref_valptr
= extcap_prefs_dynamic_valptr(pref_ifname
, &pref_name_for_prefs
);
907 /* Set an initial value if any (the string will be copied at registration) */
908 if (arg
->default_complex
)
910 *arg
->pref_valptr
= arg
->default_complex
->_val
;
913 if (arg
->arg_type
== EXTCAP_ARG_PASSWORD
)
915 prefs_register_password_preference(dev_module
, pref_name_for_prefs
,
916 pref_title
, pref_title
, (const char **)arg
->pref_valptr
);
918 prefs_register_string_preference(dev_module
, pref_name_for_prefs
,
919 pref_title
, pref_title
, (const char **)arg
->pref_valptr
);
924 /* Been here before, restore stored value */
925 if (arg
->pref_valptr
== NULL
)
927 arg
->pref_valptr
= (char**)g_hash_table_lookup(_extcap_prefs_dynamic_vals
, pref_ifname
);
932 g_free(ifname_underscore
);
933 g_free(ifname_lowercase
);
937 walker
= g_list_next(walker
);
942 g_regex_unref(regex_name
);
946 g_regex_unref(regex_ifname
);
951 *il
= g_list_append(*il
, arguments
);
953 extcap_free_arg_list(arguments
);
960 extcap_get_if_configuration(const char *ifname
)
962 GList
* arguments
= NULL
;
965 extcap_ensure_all_interfaces_loaded();
967 extcap_interface
*interface
= extcap_find_interface_for_ifname(ifname
);
970 ws_debug("Extcap path %s", get_extcap_dir());
972 arguments
= g_list_append(arguments
, g_strdup(EXTCAP_ARGUMENT_CONFIG
));
973 arguments
= g_list_append(arguments
, g_strdup(EXTCAP_ARGUMENT_INTERFACE
));
974 arguments
= g_list_append(arguments
, g_strdup(ifname
));
976 extcap_run_one(interface
, arguments
, cb_preference
, &ret
, NULL
);
978 g_list_free_full(arguments
, g_free
);
984 static bool cb_reload_preference(extcap_callback_info_t cb_info
)
986 GList
*arguments
= NULL
, * walker
= NULL
;
987 GList
**il
= (GList
**) cb_info
.data
;
989 arguments
= extcap_parse_values(cb_info
.output
);
991 walker
= g_list_first(arguments
);
992 while (walker
!= NULL
)
994 extcap_value
* val
= (extcap_value
*)walker
->data
;
995 *il
= g_list_append(*il
, val
);
996 walker
= g_list_next(walker
);
998 g_list_free(arguments
);
1004 extcap_get_if_configuration_values(const char * ifname
, const char * argname
, GHashTable
*arguments
)
1006 GList
* args
= NULL
;
1009 extcap_ensure_all_interfaces_loaded();
1011 extcap_interface
*interface
= extcap_find_interface_for_ifname(ifname
);
1014 ws_debug("Extcap path %s", get_extcap_dir());
1016 args
= g_list_append(args
, g_strdup(EXTCAP_ARGUMENT_CONFIG
));
1017 args
= g_list_append(args
, g_strdup(EXTCAP_ARGUMENT_INTERFACE
));
1018 args
= g_list_append(args
, g_strdup(ifname
));
1019 args
= g_list_append(args
, g_strdup(EXTCAP_ARGUMENT_RELOAD_OPTION
));
1020 args
= g_list_append(args
, g_strdup(argname
));
1024 GList
* keys
= g_hash_table_get_keys(arguments
);
1025 GList
* walker
= g_list_first(keys
);
1028 const char * key_data
= (const char *)walker
->data
;
1029 args
= g_list_append(args
, g_strdup(key_data
));
1030 args
= g_list_append(args
, g_strdup((const char *)g_hash_table_lookup(arguments
, key_data
)));
1031 walker
= g_list_next(walker
);
1036 extcap_run_one(interface
, args
, cb_reload_preference
, &ret
, NULL
);
1038 g_list_free_full(args
, g_free
);
1045 _extcap_requires_configuration_int(const char *ifname
, bool check_required
)
1047 GList
*arguments
= 0;
1048 GList
*walker
= 0, * item
= 0;
1051 extcap_ensure_all_interfaces_loaded();
1053 arguments
= extcap_get_if_configuration(ifname
);
1054 walker
= g_list_first(arguments
);
1056 while (walker
!= NULL
&& !found
)
1058 item
= g_list_first((GList
*)(walker
->data
));
1059 while (item
!= NULL
&& !found
)
1061 if ((extcap_arg
*)(item
->data
) != NULL
)
1063 extcap_arg
*arg
= (extcap_arg
*)(item
->data
);
1064 /* Should required options be present, or any kind of options */
1065 if (!check_required
)
1069 /* Following branch is executed when check of required items is requested */
1070 else if (arg
->is_required
)
1072 const char *stored
= NULL
;
1073 const char *defval
= NULL
;
1075 if (arg
->pref_valptr
!= NULL
)
1077 stored
= *arg
->pref_valptr
;
1080 if (arg
->default_complex
!= NULL
&& arg
->default_complex
->_val
!= NULL
)
1082 defval
= arg
->default_complex
->_val
;
1085 if (arg
->is_required
)
1087 if (!defval
&& (!stored
|| !*stored
))
1093 if (arg
->arg_type
== EXTCAP_ARG_FILESELECT
)
1095 if (arg
->fileexists
&& !(file_exists(defval
) || file_exists(stored
)))
1105 walker
= walker
->next
;
1107 extcap_free_if_configuration(arguments
, true);
1113 extcap_has_configuration(const char *ifname
)
1115 return _extcap_requires_configuration_int(ifname
, false);
1119 extcap_requires_configuration(const char *ifname
)
1121 return _extcap_requires_configuration_int(ifname
, true);
1124 static bool cb_verify_filter(extcap_callback_info_t cb_info
)
1126 extcap_filter_status
*status
= (extcap_filter_status
*)cb_info
.data
;
1127 size_t output_size
, i
;
1129 output_size
= strlen(cb_info
.output
);
1130 if (output_size
== 0) {
1131 *status
= EXTCAP_FILTER_VALID
;
1133 *status
= EXTCAP_FILTER_INVALID
;
1134 for (i
= 0; i
< output_size
; i
++) {
1135 if (cb_info
.output
[i
] == '\n' || cb_info
.output
[i
] == '\r') {
1136 cb_info
.output
[i
] = '\0';
1140 *cb_info
.err_str
= g_strdup(cb_info
.output
);
1146 extcap_filter_status
1147 extcap_verify_capture_filter(const char *ifname
, const char *filter
, char **err_str
)
1149 GList
* arguments
= NULL
;
1150 extcap_filter_status status
= EXTCAP_FILTER_UNKNOWN
;
1152 extcap_ensure_all_interfaces_loaded();
1154 extcap_interface
*interface
= extcap_find_interface_for_ifname(ifname
);
1157 ws_debug("Extcap path %s", get_extcap_dir());
1159 arguments
= g_list_append(arguments
, g_strdup(EXTCAP_ARGUMENT_CAPTURE_FILTER
));
1160 arguments
= g_list_append(arguments
, g_strdup(filter
));
1161 arguments
= g_list_append(arguments
, g_strdup(EXTCAP_ARGUMENT_INTERFACE
));
1162 arguments
= g_list_append(arguments
, g_strdup(ifname
));
1164 extcap_run_one(interface
, arguments
, cb_verify_filter
, &status
, err_str
);
1165 g_list_free_full(arguments
, g_free
);
1172 extcap_has_toolbar(const char *ifname
)
1174 if (!iface_toolbar_use())
1179 extcap_ensure_all_interfaces_loaded();
1181 GList
*toolbar_list
= g_hash_table_get_values (_toolbars
);
1182 for (GList
*walker
= toolbar_list
; walker
; walker
= walker
->next
)
1184 iface_toolbar
*toolbar
= (iface_toolbar
*) walker
->data
;
1185 if (g_list_find_custom(toolbar
->ifnames
, ifname
, (GCompareFunc
) g_strcmp0
))
1187 g_list_free(toolbar_list
);
1192 g_list_free(toolbar_list
);
1197 static gboolean
extcap_terminate_cb(void *user_data
)
1199 capture_session
*cap_session
= (capture_session
*)user_data
;
1200 capture_options
*capture_opts
= cap_session
->capture_opts
;
1201 interface_options
*interface_opts
;
1203 bool all_finished
= true;
1205 for (icnt
= 0; icnt
< capture_opts
->ifaces
->len
; icnt
++)
1207 interface_opts
= &g_array_index(capture_opts
->ifaces
, interface_options
,
1210 /* skip native interfaces */
1211 if (interface_opts
->if_type
!= IF_EXTCAP
)
1216 if (interface_opts
->extcap_pid
!= WS_INVALID_PID
)
1219 TerminateProcess(interface_opts
->extcap_pid
, 0);
1221 kill(interface_opts
->extcap_pid
, SIGKILL
);
1223 all_finished
= false;
1226 /* Do not care about stdout/stderr anymore */
1227 if (interface_opts
->extcap_stdout_watch
> 0)
1229 g_source_remove(interface_opts
->extcap_stdout_watch
);
1230 interface_opts
->extcap_stdout_watch
= 0;
1233 if (interface_opts
->extcap_stderr_watch
> 0)
1235 g_source_remove(interface_opts
->extcap_stderr_watch
);
1236 interface_opts
->extcap_stderr_watch
= 0;
1240 capture_opts
->wait_for_extcap_cbs
= true;
1241 capture_opts
->extcap_terminate_id
= 0;
1244 capture_process_finished(cap_session
);
1247 return G_SOURCE_REMOVE
;
1250 void extcap_request_stop(capture_session
*cap_session
)
1252 capture_options
*capture_opts
= cap_session
->capture_opts
;
1253 interface_options
*interface_opts
;
1256 if (capture_opts
->extcap_terminate_id
> 0)
1258 /* Already requested, do not extend timeout */
1262 if (capture_opts
->wait_for_extcap_cbs
)
1264 /* Terminate callback was called, waiting for child callbacks */
1268 if (extcap_session_stop(cap_session
))
1270 /* Nothing left to do, all extcaps have fully finished */
1274 for (icnt
= 0; icnt
< capture_opts
->ifaces
->len
; icnt
++)
1276 interface_opts
= &g_array_index(capture_opts
->ifaces
, interface_options
,
1279 /* skip native interfaces */
1280 if (interface_opts
->if_type
!= IF_EXTCAP
)
1285 ws_debug("Extcap [%s] - Requesting stop PID: %"PRIdMAX
, interface_opts
->name
,
1286 (intmax_t)interface_opts
->extcap_pid
);
1289 if (interface_opts
->extcap_pid
!= WS_INVALID_PID
)
1291 kill(interface_opts
->extcap_pid
, SIGTERM
);
1296 capture_opts
->extcap_terminate_id
=
1297 g_timeout_add_seconds(EXTCAP_CLEANUP_TIMEOUT
, extcap_terminate_cb
, cap_session
);
1301 extcap_add_arg_and_remove_cb(void *key
, void *value
, void *data
)
1303 GPtrArray
*args
= (GPtrArray
*)data
;
1307 g_ptr_array_add(args
, g_strdup((const char *)key
));
1311 g_ptr_array_add(args
, g_strdup((const char *)value
));
1320 bool extcap_session_stop(capture_session
*cap_session
)
1322 capture_options
*capture_opts
= cap_session
->capture_opts
;
1323 interface_options
*interface_opts
;
1326 for (i
= 0; i
< capture_opts
->ifaces
->len
; i
++)
1328 interface_opts
= &g_array_index(capture_opts
->ifaces
, interface_options
, i
);
1329 if (interface_opts
->if_type
!= IF_EXTCAP
)
1334 if ((interface_opts
->extcap_pid
!= WS_INVALID_PID
) ||
1335 (interface_opts
->extcap_stdout_watch
> 0) ||
1336 (interface_opts
->extcap_stderr_watch
> 0))
1338 /* Capture session is not finished, wait for remaining watches */
1342 g_free(interface_opts
->extcap_pipedata
);
1343 interface_opts
->extcap_pipedata
= NULL
;
1346 if (interface_opts
->extcap_pipe_h
!= INVALID_HANDLE_VALUE
)
1348 ws_debug("Extcap [%s] - Closing pipe", interface_opts
->name
);
1349 FlushFileBuffers(interface_opts
->extcap_pipe_h
);
1350 DisconnectNamedPipe(interface_opts
->extcap_pipe_h
);
1351 CloseHandle(interface_opts
->extcap_pipe_h
);
1352 interface_opts
->extcap_pipe_h
= INVALID_HANDLE_VALUE
;
1354 if (interface_opts
->extcap_control_in_h
!= INVALID_HANDLE_VALUE
)
1356 ws_debug("Extcap [%s] - Closing control_in pipe", interface_opts
->name
);
1357 FlushFileBuffers(interface_opts
->extcap_control_in_h
);
1358 DisconnectNamedPipe(interface_opts
->extcap_control_in_h
);
1359 CloseHandle(interface_opts
->extcap_control_in_h
);
1360 interface_opts
->extcap_control_in_h
= INVALID_HANDLE_VALUE
;
1362 if (interface_opts
->extcap_control_out_h
!= INVALID_HANDLE_VALUE
)
1364 ws_debug("Extcap [%s] - Closing control_out pipe", interface_opts
->name
);
1365 FlushFileBuffers(interface_opts
->extcap_control_out_h
);
1366 DisconnectNamedPipe(interface_opts
->extcap_control_out_h
);
1367 CloseHandle(interface_opts
->extcap_control_out_h
);
1368 interface_opts
->extcap_control_out_h
= INVALID_HANDLE_VALUE
;
1371 if (interface_opts
->extcap_fifo
!= NULL
&& file_exists(interface_opts
->extcap_fifo
))
1373 /* If extcap didn't open the fifo, dumpcap would be waiting on it
1374 * until user manually stops capture. Simply open and close fifo
1375 * here to let dumpcap return from the select() call. This has no
1376 * effect if dumpcap is not waiting.
1378 int fd
= ws_open(interface_opts
->extcap_fifo
, O_WRONLY
|O_NONBLOCK
, 0000);
1382 /* the fifo will not be freed here, but with the other capture_opts in capture_sync */
1383 ws_unlink(interface_opts
->extcap_fifo
);
1384 get_dirname(interface_opts
->extcap_fifo
);
1385 rmdir(interface_opts
->extcap_fifo
);
1386 interface_opts
->extcap_fifo
= NULL
;
1388 if (interface_opts
->extcap_control_in
&& file_exists(interface_opts
->extcap_control_in
))
1390 ws_unlink(interface_opts
->extcap_control_in
);
1391 interface_opts
->extcap_control_in
= NULL
;
1393 if (interface_opts
->extcap_control_out
&& file_exists(interface_opts
->extcap_control_out
))
1395 ws_unlink(interface_opts
->extcap_control_out
);
1396 interface_opts
->extcap_control_out
= NULL
;
1401 /* All child processes finished */
1402 capture_opts
->wait_for_extcap_cbs
= false;
1403 if (capture_opts
->extcap_terminate_id
> 0)
1405 g_source_remove(capture_opts
->extcap_terminate_id
);
1406 capture_opts
->extcap_terminate_id
= 0;
1409 /* Nothing left to do, do not prevent capture session stop */
1414 extcap_watch_removed(capture_session
*cap_session
, interface_options
*interface_opts
)
1416 if ((interface_opts
->extcap_pid
== WS_INVALID_PID
) &&
1417 (interface_opts
->extcap_stdout_watch
== 0) &&
1418 (interface_opts
->extcap_stderr_watch
== 0))
1420 /* Close session if this was the last remaining process */
1421 capture_process_finished(cap_session
);
1425 static interface_options
*
1426 extcap_find_channel_interface(capture_session
*cap_session
, GIOChannel
*source
)
1428 capture_options
*capture_opts
= cap_session
->capture_opts
;
1429 interface_options
*interface_opts
;
1432 for (i
= 0; i
< capture_opts
->ifaces
->len
; i
++)
1434 ws_pipe_t
*pipedata
;
1435 interface_opts
= &g_array_index(capture_opts
->ifaces
, interface_options
, i
);
1436 pipedata
= (ws_pipe_t
*)interface_opts
->extcap_pipedata
;
1438 ((pipedata
->stdout_io
== source
) || (pipedata
->stderr_io
== source
)))
1440 return interface_opts
;
1444 ws_assert_not_reached();
1448 extcap_stdout_cb(GIOChannel
*source
, GIOCondition condition _U_
, void *data
)
1450 capture_session
*cap_session
= (capture_session
*)data
;
1451 interface_options
*interface_opts
= extcap_find_channel_interface(cap_session
, source
);
1453 size_t bytes_read
= 0;
1454 GIOStatus status
= G_IO_STATUS_EOF
;
1456 /* Discard data to prevent child process hanging on stdout write */
1457 if (condition
& G_IO_IN
)
1459 status
= g_io_channel_read_chars(source
, buf
, sizeof(buf
), &bytes_read
, NULL
);
1462 if ((bytes_read
== 0) || (status
!= G_IO_STATUS_NORMAL
))
1464 interface_opts
->extcap_stdout_watch
= 0;
1465 extcap_watch_removed(cap_session
, interface_opts
);
1466 return G_SOURCE_REMOVE
;
1468 return G_SOURCE_CONTINUE
;
1472 extcap_stderr_cb(GIOChannel
*source
, GIOCondition condition
, void *data
)
1474 capture_session
*cap_session
= (capture_session
*)data
;
1475 interface_options
*interface_opts
= extcap_find_channel_interface(cap_session
, source
);
1477 size_t bytes_read
= 0;
1478 GIOStatus status
= G_IO_STATUS_EOF
;
1480 if (condition
& G_IO_IN
)
1482 status
= g_io_channel_read_chars(source
, buf
, sizeof(buf
), &bytes_read
, NULL
);
1485 #define STDERR_BUFFER_SIZE 1024
1488 if (interface_opts
->extcap_stderr
== NULL
)
1490 interface_opts
->extcap_stderr
= g_string_new_len(buf
, bytes_read
);
1494 ssize_t remaining
= STDERR_BUFFER_SIZE
- interface_opts
->extcap_stderr
->len
;
1497 ssize_t bytes
= bytes_read
;
1498 bytes
= MIN(bytes
, remaining
);
1499 g_string_append_len(interface_opts
->extcap_stderr
, buf
, bytes
);
1504 if ((bytes_read
== 0) || (status
!= G_IO_STATUS_NORMAL
))
1506 interface_opts
->extcap_stderr_watch
= 0;
1507 extcap_watch_removed(cap_session
, interface_opts
);
1508 return G_SOURCE_REMOVE
;
1510 return G_SOURCE_CONTINUE
;
1513 static void extcap_child_watch_cb(GPid pid
, int status _U_
, void *user_data
)
1516 interface_options
*interface_opts
;
1517 capture_session
*cap_session
= (capture_session
*)(user_data
);
1518 capture_options
*capture_opts
= cap_session
->capture_opts
;
1520 /* Close handle to child process. */
1521 g_spawn_close_pid(pid
);
1523 /* Update extcap_pid in interface options structure. */
1524 for (i
= 0; i
< capture_opts
->ifaces
->len
; i
++)
1526 interface_opts
= &g_array_index(capture_opts
->ifaces
, interface_options
, i
);
1527 if (interface_opts
->extcap_pid
== pid
)
1529 ws_debug("Extcap [%s] - Closing spawned PID: %"PRIdMAX
, interface_opts
->name
,
1530 (intmax_t)interface_opts
->extcap_pid
);
1531 interface_opts
->extcap_pid
= WS_INVALID_PID
;
1532 extcap_watch_removed(cap_session
, interface_opts
);
1539 GPtrArray
*extcap_prepare_arguments(interface_options
*interface_opts
)
1541 GPtrArray
*result
= NULL
;
1543 if (interface_opts
->if_type
== IF_EXTCAP
)
1545 result
= g_ptr_array_new();
1547 #define add_arg(X) g_ptr_array_add(result, g_strdup(X))
1549 add_arg(interface_opts
->extcap
);
1550 add_arg(EXTCAP_ARGUMENT_RUN_CAPTURE
);
1551 add_arg(EXTCAP_ARGUMENT_INTERFACE
);
1552 add_arg(interface_opts
->name
);
1553 if (interface_opts
->cfilter
&& strlen(interface_opts
->cfilter
) > 0)
1555 add_arg(EXTCAP_ARGUMENT_CAPTURE_FILTER
);
1556 add_arg(interface_opts
->cfilter
);
1558 add_arg(EXTCAP_ARGUMENT_RUN_PIPE
);
1559 add_arg(interface_opts
->extcap_fifo
);
1560 if (interface_opts
->extcap_control_in
)
1562 add_arg(EXTCAP_ARGUMENT_CONTROL_OUT
);
1563 add_arg(interface_opts
->extcap_control_in
);
1565 if (interface_opts
->extcap_control_out
)
1567 add_arg(EXTCAP_ARGUMENT_CONTROL_IN
);
1568 add_arg(interface_opts
->extcap_control_out
);
1570 if (interface_opts
->extcap_args
== NULL
|| g_hash_table_size(interface_opts
->extcap_args
) == 0)
1572 /* User did not perform interface configuration.
1574 * Check if there are any boolean flags that are set by default
1575 * and hence their argument should be added.
1580 arglist
= extcap_get_if_configuration(interface_opts
->name
);
1581 for (elem
= g_list_first(arglist
); elem
; elem
= elem
->next
)
1584 extcap_arg
*arg_iter
;
1586 if (elem
->data
== NULL
)
1591 arg_list
= g_list_first((GList
*)elem
->data
);
1592 while (arg_list
!= NULL
)
1594 const char *stored
= NULL
;
1595 /* In case of boolflags only first element in arg_list is relevant. */
1596 arg_iter
= (extcap_arg
*)(arg_list
->data
);
1597 if (arg_iter
->pref_valptr
!= NULL
)
1599 stored
= *arg_iter
->pref_valptr
;
1602 if (arg_iter
->arg_type
== EXTCAP_ARG_BOOLFLAG
)
1604 if (!stored
&& extcap_complex_get_bool(arg_iter
->default_complex
))
1606 add_arg(arg_iter
->call
);
1608 else if (g_strcmp0(stored
, "true") == 0)
1610 add_arg(arg_iter
->call
);
1615 if (stored
&& strlen(stored
) > 0) {
1616 add_arg(arg_iter
->call
);
1621 arg_list
= arg_list
->next
;
1625 extcap_free_if_configuration(arglist
, true);
1629 g_hash_table_foreach_remove(interface_opts
->extcap_args
, extcap_add_arg_and_remove_cb
, result
);
1639 static void ptr_array_free(void *data
, void *user_data _U_
)
1645 static bool extcap_create_pipe(const char *ifname
, char **fifo
, HANDLE
*handle_out
, const char *pipe_prefix
)
1647 char timestr
[ 14 + 1 ];
1648 time_t current_time
;
1649 char *pipename
= NULL
;
1650 SECURITY_ATTRIBUTES security
;
1652 /* create pipename */
1653 current_time
= time(NULL
);
1655 * XXX - we trust Windows not to return a time before the Epoch here,
1656 * so we won't get a null pointer back from localtime().
1658 strftime(timestr
, sizeof(timestr
), "%Y%m%d%H%M%S", localtime(¤t_time
));
1659 pipename
= g_strconcat("\\\\.\\pipe\\", pipe_prefix
, "_", ifname
, "_", timestr
, NULL
);
1661 /* Security struct to enable Inheritable HANDLE */
1662 memset(&security
, 0, sizeof(SECURITY_ATTRIBUTES
));
1663 security
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
1664 security
.bInheritHandle
= false;
1665 security
.lpSecurityDescriptor
= NULL
;
1667 /* create a namedPipe */
1668 *handle_out
= CreateNamedPipe(
1669 utf_8to16(pipename
),
1670 PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
,
1671 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
1676 if (*handle_out
== INVALID_HANDLE_VALUE
)
1678 ws_debug("Error creating pipe => (%ld)", GetLastError());
1684 ws_debug("Wireshark Created pipe =>(%s) handle (%" PRIuMAX
")", pipename
, (uintmax_t)*handle_out
);
1685 *fifo
= g_strdup(pipename
);
1691 static bool extcap_create_pipe(const char *ifname
, char **fifo
, const char *temp_dir
, const char *pipe_prefix
)
1693 char *subdir_tmpl
= g_strdup_printf("%s_%s_XXXXXX", pipe_prefix
, ifname
);
1694 char *temp_subdir
= create_tempdir(temp_dir
, subdir_tmpl
, NULL
);
1696 g_free(subdir_tmpl
);
1697 if (temp_subdir
== NULL
)
1702 char *fifo_path
= g_build_path(G_DIR_SEPARATOR_S
, temp_subdir
, "fifo", NULL
);
1703 g_free(temp_subdir
);
1705 ws_debug("Extcap - Creating fifo: %s", fifo_path
);
1707 if (mkfifo(fifo_path
, 0600) == 0)
1719 /* call mkfifo for each extcap,
1720 * returns false if there's an error creating a FIFO */
1722 extcap_init_interfaces(capture_session
*cap_session
)
1724 capture_options
*capture_opts
= cap_session
->capture_opts
;
1726 interface_options
*interface_opts
;
1727 ws_pipe_t
*pipedata
;
1729 extcap_ensure_all_interfaces_loaded();
1731 for (i
= 0; i
< capture_opts
->ifaces
->len
; i
++)
1733 GPtrArray
*args
= NULL
;
1734 GPid pid
= WS_INVALID_PID
;
1736 interface_opts
= &g_array_index(capture_opts
->ifaces
, interface_options
, i
);
1738 /* skip native interfaces */
1739 if (interface_opts
->if_type
!= IF_EXTCAP
)
1744 /* create control pipes if having toolbar */
1745 if (extcap_has_toolbar(interface_opts
->name
))
1747 extcap_create_pipe(interface_opts
->name
, &interface_opts
->extcap_control_in
,
1749 &interface_opts
->extcap_control_in_h
,
1751 capture_opts
->temp_dir
,
1753 EXTCAP_CONTROL_IN_PREFIX
);
1754 extcap_create_pipe(interface_opts
->name
, &interface_opts
->extcap_control_out
,
1756 &interface_opts
->extcap_control_out_h
,
1758 capture_opts
->temp_dir
,
1760 EXTCAP_CONTROL_OUT_PREFIX
);
1763 /* create pipe for fifo */
1764 if (!extcap_create_pipe(interface_opts
->name
, &interface_opts
->extcap_fifo
,
1766 &interface_opts
->extcap_pipe_h
,
1768 capture_opts
->temp_dir
,
1770 EXTCAP_PIPE_PREFIX
))
1776 /* Create extcap call */
1777 args
= extcap_prepare_arguments(interface_opts
);
1779 pipedata
= g_new0(ws_pipe_t
, 1);
1781 pid
= ws_pipe_spawn_async(pipedata
, args
);
1783 g_ptr_array_foreach(args
, ptr_array_free
, NULL
);
1784 g_ptr_array_free(args
, true);
1786 if (pid
== WS_INVALID_PID
)
1792 g_io_channel_unref(pipedata
->stdin_io
);
1793 pipedata
->stdin_io
= NULL
;
1794 interface_opts
->extcap_pid
= pid
;
1796 g_child_watch_add_full(G_PRIORITY_HIGH
, pid
, extcap_child_watch_cb
,
1797 (void *)cap_session
, NULL
);
1798 interface_opts
->extcap_stdout_watch
=
1799 g_io_add_watch(pipedata
->stdout_io
, G_IO_IN
| G_IO_HUP
,
1800 extcap_stdout_cb
, (void *)cap_session
);
1801 interface_opts
->extcap_stderr_watch
=
1802 g_io_add_watch(pipedata
->stderr_io
, G_IO_IN
| G_IO_HUP
,
1803 extcap_stderr_cb
, (void *)cap_session
);
1805 /* Pipedata pointers are only used to match GIOChannel to interface.
1806 * GIOChannel watch holds the only remaining reference.
1808 g_io_channel_unref(pipedata
->stdout_io
);
1809 g_io_channel_unref(pipedata
->stderr_io
);
1812 /* On Windows, wait for extcap to connect to named pipe.
1813 * Some extcaps will present UAC screen to user.
1814 * 30 second timeout should be reasonable timeout for extcap to
1815 * connect to named pipe (including user interaction).
1816 * Wait on multiple object in case of extcap termination
1817 * without opening pipe.
1819 if (pid
!= WS_INVALID_PID
)
1821 HANDLE pipe_handles
[3];
1822 int num_pipe_handles
= 1;
1823 pipe_handles
[0] = interface_opts
->extcap_pipe_h
;
1825 if (extcap_has_toolbar(interface_opts
->name
))
1827 pipe_handles
[1] = interface_opts
->extcap_control_in_h
;
1828 pipe_handles
[2] = interface_opts
->extcap_control_out_h
;
1829 num_pipe_handles
+= 2;
1832 ws_pipe_wait_for_pipe(pipe_handles
, num_pipe_handles
, pid
);
1836 interface_opts
->extcap_pipedata
= (void *) pipedata
;
1841 #endif /* HAVE_LIBPCAP */
1843 /************* EXTCAP LOAD INTERFACE LIST ***************
1845 * The following code handles loading and reloading the interface list. It is explicitly
1846 * kept separate from the rest
1851 extcap_free_interface_info(void *data
)
1853 extcap_info
*info
= (extcap_info
*)data
;
1855 g_free(info
->basename
);
1856 g_free(info
->full_path
);
1857 g_free(info
->version
);
1860 extcap_free_interfaces(info
->interfaces
);
1865 static extcap_info
*
1866 extcap_ensure_interface(const char * toolname
, bool create_if_nonexist
)
1868 extcap_info
* element
= 0;
1870 if ( prefs
.capture_no_extcap
)
1876 if ( ! _loaded_interfaces
)
1877 _loaded_interfaces
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, extcap_free_interface
);
1879 element
= (extcap_info
*) g_hash_table_lookup(_loaded_interfaces
, toolname
);
1883 if ( ! element
&& create_if_nonexist
)
1885 g_hash_table_insert(_loaded_interfaces
, g_strdup(toolname
), g_new0(extcap_info
, 1));
1886 element
= (extcap_info
*) g_hash_table_lookup(_loaded_interfaces
, toolname
);
1893 extcap_get_tool_by_ifname(const char *ifname
)
1895 extcap_ensure_all_interfaces_loaded();
1897 if ( ifname
&& _tool_for_ifname
)
1899 char * toolname
= (char *)g_hash_table_lookup(_tool_for_ifname
, ifname
);
1901 return extcap_ensure_interface(toolname
, false);
1908 extcap_get_tool_info(const char * toolname
)
1910 extcap_ensure_all_interfaces_loaded();
1912 return extcap_ensure_interface(toolname
, false);
1915 static void remove_extcap_entry(void *entry
, void *data _U_
)
1917 extcap_interface
*int_iter
= (extcap_interface
*)entry
;
1919 if (int_iter
->if_type
== EXTCAP_SENTENCE_EXTCAP
)
1920 extcap_free_interface(entry
);
1924 process_new_extcap(const char *extcap
, char *output
)
1926 GList
* interfaces
= NULL
, * control_items
= NULL
, * walker
= NULL
;
1927 extcap_interface
* int_iter
= NULL
;
1928 extcap_info
* element
= NULL
;
1929 iface_toolbar
* toolbar_entry
= NULL
;
1930 char * toolname
= g_path_get_basename(extcap
);
1932 GList
* interface_keys
= g_hash_table_get_keys(_loaded_interfaces
);
1934 /* Load interfaces from utility */
1935 interfaces
= extcap_parse_interfaces(output
, &control_items
);
1937 ws_debug("Loading interface list for %s ", extcap
);
1939 /* Seems, that there where no interfaces to be loaded */
1940 if ( ! interfaces
|| g_list_length(interfaces
) == 0 )
1942 ws_debug("Cannot load interfaces for %s", extcap
);
1943 g_list_free(interface_keys
);
1948 /* Load or create the storage element for the tool */
1949 element
= extcap_ensure_interface(toolname
, true);
1950 if ( element
== NULL
)
1952 ws_warning("Cannot store interface %s, already loaded as personal plugin", extcap
);
1953 g_list_foreach(interfaces
, remove_extcap_entry
, NULL
);
1954 g_list_free(interfaces
);
1955 g_list_free(interface_keys
);
1962 toolbar_entry
= g_new0(iface_toolbar
, 1);
1963 toolbar_entry
->controls
= control_items
;
1966 walker
= interfaces
;
1968 while (walker
!= NULL
)
1970 int_iter
= (extcap_interface
*)walker
->data
;
1972 if (int_iter
->call
!= NULL
)
1973 ws_debug("Interface found %s\n", int_iter
->call
);
1975 /* Help is not necessarily stored with the interface, but rather with the version string.
1976 * As the version string always comes in front of the interfaces, this ensures, that it gets
1977 * properly stored with the interface */
1978 if (int_iter
->if_type
== EXTCAP_SENTENCE_EXTCAP
)
1980 if (int_iter
->call
!= NULL
)
1981 ws_debug(" Extcap [%s] ", int_iter
->call
);
1983 /* Only initialize values if none are set. Need to check only one element here */
1984 if ( ! element
->version
)
1986 element
->version
= g_strdup(int_iter
->version
);
1987 element
->basename
= g_strdup(toolname
);
1988 element
->full_path
= g_strdup(extcap
);
1989 element
->help
= g_strdup(int_iter
->help
);
1992 help
= int_iter
->help
;
1995 toolbar_entry
->menu_title
= g_strdup(int_iter
->display
);
1996 toolbar_entry
->help
= g_strdup(int_iter
->help
);
1999 walker
= g_list_next(walker
);
2003 /* Only interface definitions will be parsed here. help is already set by the extcap element,
2004 * which makes it necessary to have version in the list before the interfaces. This is normally
2005 * the case by design, but could be changed by separating the information in extcap-base. */
2006 if ( int_iter
->if_type
== EXTCAP_SENTENCE_INTERFACE
)
2008 if ( g_list_find(interface_keys
, int_iter
->call
) )
2010 ws_warning("Extcap interface \"%s\" is already provided by \"%s\" ",
2011 int_iter
->call
, extcap_if_executable(int_iter
->call
));
2012 walker
= g_list_next(walker
);
2016 if ((int_iter
->call
!= NULL
) && (int_iter
->display
))
2017 ws_debug(" Interface [%s] \"%s\" ", int_iter
->call
, int_iter
->display
);
2019 int_iter
->extcap_path
= g_strdup(extcap
);
2021 /* Only set the help, if it exists and no parsed help information is present */
2022 if ( ! int_iter
->help
&& help
)
2023 int_iter
->help
= g_strdup(help
);
2025 element
->interfaces
= g_list_append(element
->interfaces
, int_iter
);
2026 g_hash_table_insert(_tool_for_ifname
, g_strdup(int_iter
->call
), g_strdup(toolname
));
2030 if (!toolbar_entry
->menu_title
)
2032 toolbar_entry
->menu_title
= g_strdup(int_iter
->display
);
2034 toolbar_entry
->ifnames
= g_list_append(toolbar_entry
->ifnames
, g_strdup(int_iter
->call
));
2038 walker
= g_list_next(walker
);
2041 if (toolbar_entry
&& toolbar_entry
->menu_title
)
2043 iface_toolbar_add(toolbar_entry
);
2044 if (extcap_iface_toolbar_add(extcap
, toolbar_entry
))
2046 toolbar_entry
= NULL
;
2050 extcap_free_toolbar(toolbar_entry
);
2051 g_list_foreach(interfaces
, remove_extcap_entry
, NULL
);
2052 g_list_free(interfaces
);
2053 g_list_free(interface_keys
);
2058 /** Thread callback to save the output of a --extcap-config call. */
2060 extcap_process_config_cb(thread_pool_t
*pool _U_
, void *data
, char *output
)
2062 extcap_iface_info_t
*iface_info
= (extcap_iface_info_t
*)data
;
2063 iface_info
->output
= output
;
2067 * Thread callback to process discovered interfaces, scheduling more tasks to
2068 * retrieve the configuration for each interface. Called once for every extcap
2072 extcap_process_interfaces_cb(thread_pool_t
*pool
, void *data
, char *output
)
2074 extcap_run_extcaps_info_t
*info
= (extcap_run_extcaps_info_t
*)data
;
2076 unsigned num_interfaces
= 0;
2079 // No interfaces available, nothing to do.
2083 // Save output for process_new_extcap.
2084 info
->output
= output
;
2086 // Are there any interfaces to query information from?
2087 GList
*interfaces
= extcap_parse_interfaces(output
, NULL
);
2088 for (GList
*iface
= interfaces
; iface
; iface
= g_list_next(iface
)) {
2089 extcap_interface
*intf
= (extcap_interface
*)iface
->data
;
2090 if (intf
->if_type
== EXTCAP_SENTENCE_INTERFACE
) {
2094 if (num_interfaces
== 0) {
2096 g_list_free_full(interfaces
, extcap_free_interface
);
2100 /* GSList is not thread-safe, so pre-allocate an array instead. */
2101 info
->iface_infos
= g_new0(extcap_iface_info_t
, num_interfaces
);
2102 info
->num_interfaces
= num_interfaces
;
2104 // Schedule new commands to retrieve the configuration.
2105 for (GList
*iface
= interfaces
; iface
; iface
= g_list_next(iface
)) {
2106 extcap_interface
*intf
= (extcap_interface
*)iface
->data
;
2107 if (intf
->if_type
!= EXTCAP_SENTENCE_INTERFACE
) {
2111 const char *argv
[] = {
2112 EXTCAP_ARGUMENT_CONFIG
,
2113 EXTCAP_ARGUMENT_INTERFACE
,
2117 extcap_run_task_t
*task
= g_new0(extcap_run_task_t
, 1);
2118 extcap_iface_info_t
*iface_info
= &info
->iface_infos
[i
++];
2120 task
->extcap_path
= info
->extcap_path
;
2121 task
->argv
= g_strdupv((char **)argv
);
2122 task
->output_cb
= extcap_process_config_cb
;
2123 task
->data
= iface_info
;
2124 iface_info
->ifname
= g_strdup(intf
->call
);
2126 thread_pool_push(pool
, task
, NULL
);
2128 g_list_free_full(interfaces
, extcap_free_interface
);
2132 * Thread callback to check whether the new-style --list-interfaces call with an
2133 * explicit function succeeded. If not, schedule a call without the new version
2137 extcap_list_interfaces_cb(thread_pool_t
*pool
, void *data
, char *output
)
2139 extcap_run_extcaps_info_t
*info
= (extcap_run_extcaps_info_t
*)data
;
2142 /* No output available, schedule a fallback query. */
2143 const char *argv
[] = {
2144 EXTCAP_ARGUMENT_LIST_INTERFACES
,
2147 extcap_run_task_t
*task
= g_new0(extcap_run_task_t
, 1);
2149 task
->extcap_path
= info
->extcap_path
;
2150 task
->argv
= g_strdupv((char **)argv
);
2151 task
->output_cb
= extcap_process_interfaces_cb
;
2154 thread_pool_push(pool
, task
, NULL
);
2156 extcap_process_interfaces_cb(pool
, info
, output
);
2161 /* Handles loading of the interfaces. */
2163 extcap_load_interface_list(void)
2165 bool prefs_registered
= false;
2166 if (prefs
.capture_no_extcap
)
2171 // Remove existing interface toolbars here instead of in extcap_clear_interfaces()
2172 // to avoid flicker in shown toolbars when refreshing interfaces.
2173 GList
*toolbar_list
= g_hash_table_get_values (_toolbars
);
2174 for (GList
*walker
= toolbar_list
; walker
; walker
= walker
->next
)
2176 iface_toolbar
*toolbar
= (iface_toolbar
*) walker
->data
;
2177 iface_toolbar_remove(toolbar
->menu_title
);
2179 g_list_free(toolbar_list
);
2180 g_hash_table_remove_all(_toolbars
);
2182 _toolbars
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, extcap_free_toolbar
);
2185 if (_loaded_interfaces
== NULL
)
2190 extcap_run_extcaps_info_t
*infos
;
2192 _loaded_interfaces
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, extcap_free_interface_info
);
2193 /* Cleanup lookup table */
2194 if ( _tool_for_ifname
)
2196 g_hash_table_remove_all(_tool_for_ifname
);
2197 _tool_for_ifname
= 0;
2199 _tool_for_ifname
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
2202 get_ws_version_number(&major
, &minor
, NULL
);
2203 char *arg_version
= ws_strdup_printf("%s=%d.%d", EXTCAP_ARGUMENT_VERSION
, major
, minor
);
2204 const char *argv
[] = {
2205 EXTCAP_ARGUMENT_LIST_INTERFACES
,
2209 infos
= (extcap_run_extcaps_info_t
*)extcap_run_all(argv
,
2210 extcap_list_interfaces_cb
, sizeof(extcap_run_extcaps_info_t
),
2212 for (unsigned i
= 0; i
< count
; i
++) {
2213 if (!infos
[i
].output
) {
2217 // Save new extcap and each discovered interface.
2218 process_new_extcap(infos
[i
].extcap_path
, infos
[i
].output
);
2219 for (unsigned j
= 0; j
< infos
[i
].num_interfaces
; j
++) {
2220 extcap_iface_info_t
*iface_info
= &infos
[i
].iface_infos
[j
];
2222 if (!iface_info
->output
) {
2226 extcap_callback_info_t cb_info
= {
2227 .ifname
= iface_info
->ifname
,
2228 .output
= iface_info
->output
,
2231 prefs_registered
= cb_preference(cb_info
);
2234 extcap_free_extcaps_info_array(infos
, count
);
2235 g_free(arg_version
);
2238 if (prefs_registered
)
2240 prefs_read_module("extcap");
2245 * Editor modelines - https://www.wireshark.org/tools/modelines.html
2250 * indent-tabs-mode: nil
2253 * vi: set shiftwidth=4 tabstop=8 expandtab:
2254 * :indentSize=4:tabSize=8:noTabs=true: