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 "ui/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
)
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 module_t
*extcap_module
= prefs_find_module("extcap");
846 char *pref_name
= g_regex_replace(regex_name
, arg
->call
, strlen(arg
->call
), 0, "", (GRegexMatchFlags
) 0, NULL
);
847 char *ifname_underscore
= g_regex_replace(regex_ifname
, ifname
, strlen(ifname
), 0, "_", (GRegexMatchFlags
) 0, NULL
);
848 char *ifname_lowercase
= g_ascii_strdown(ifname_underscore
, -1);
849 char *pref_ifname
= g_strconcat(ifname_lowercase
, ".", pref_name
, NULL
);
851 pref
= prefs_find_preference(extcap_module
, pref_ifname
);
854 g_free(ifname_underscore
);
855 g_free(ifname_lowercase
);
861 g_regex_unref(regex_name
);
865 g_regex_unref(regex_ifname
);
871 static bool cb_preference(extcap_callback_info_t cb_info
)
873 bool new_pref
= false;
874 GList
*arguments
= NULL
;
875 GList
**il
= (GList
**) cb_info
.data
;
876 module_t
*dev_module
= NULL
;
878 arguments
= extcap_parse_args(cb_info
.output
);
880 dev_module
= prefs_find_module("extcap");
884 GList
*walker
= arguments
;
886 GRegex
*regex_name
= g_regex_new("[-]+", G_REGEX_RAW
, (GRegexMatchFlags
) 0, NULL
);
887 GRegex
*regex_ifname
= g_regex_new("(?![a-zA-Z0-9_]).", G_REGEX_RAW
, (GRegexMatchFlags
) 0, NULL
);
888 if (regex_name
&& regex_ifname
)
890 while (walker
!= NULL
)
892 extcap_arg
*arg
= (extcap_arg
*)walker
->data
;
893 arg
->device_name
= g_strdup(cb_info
.ifname
);
897 char *pref_name
= g_regex_replace(regex_name
, arg
->call
, strlen(arg
->call
), 0, "", (GRegexMatchFlags
) 0, NULL
);
898 char *ifname_underscore
= g_regex_replace(regex_ifname
, cb_info
.ifname
, strlen(cb_info
.ifname
), 0, "_", (GRegexMatchFlags
) 0, NULL
);
899 char *ifname_lowercase
= g_ascii_strdown(ifname_underscore
, -1);
900 char *pref_ifname
= g_strconcat(ifname_lowercase
, ".", pref_name
, NULL
);
902 if (prefs_find_preference(dev_module
, pref_ifname
) == NULL
)
904 char *pref_name_for_prefs
;
905 char *pref_title
= wmem_strdup(wmem_epan_scope(), arg
->display
);
907 arg
->pref_valptr
= extcap_prefs_dynamic_valptr(pref_ifname
, &pref_name_for_prefs
);
908 /* Set an initial value if any (the string will be copied at registration) */
909 if (arg
->default_complex
)
911 *arg
->pref_valptr
= arg
->default_complex
->_val
;
914 if (arg
->arg_type
== EXTCAP_ARG_PASSWORD
)
916 prefs_register_password_preference(dev_module
, pref_name_for_prefs
,
917 pref_title
, pref_title
, (const char **)arg
->pref_valptr
);
919 prefs_register_string_preference(dev_module
, pref_name_for_prefs
,
920 pref_title
, pref_title
, (const char **)arg
->pref_valptr
);
925 /* Been here before, restore stored value */
926 if (arg
->pref_valptr
== NULL
)
928 arg
->pref_valptr
= (char**)g_hash_table_lookup(_extcap_prefs_dynamic_vals
, pref_ifname
);
933 g_free(ifname_underscore
);
934 g_free(ifname_lowercase
);
938 walker
= g_list_next(walker
);
943 g_regex_unref(regex_name
);
947 g_regex_unref(regex_ifname
);
952 *il
= g_list_append(*il
, arguments
);
954 extcap_free_arg_list(arguments
);
961 extcap_get_if_configuration(const char *ifname
)
963 GList
* arguments
= NULL
;
966 extcap_ensure_all_interfaces_loaded();
968 extcap_interface
*interface
= extcap_find_interface_for_ifname(ifname
);
971 ws_debug("Extcap path %s", get_extcap_dir());
973 arguments
= g_list_append(arguments
, g_strdup(EXTCAP_ARGUMENT_CONFIG
));
974 arguments
= g_list_append(arguments
, g_strdup(EXTCAP_ARGUMENT_INTERFACE
));
975 arguments
= g_list_append(arguments
, g_strdup(ifname
));
977 extcap_run_one(interface
, arguments
, cb_preference
, &ret
, NULL
);
979 g_list_free_full(arguments
, g_free
);
985 static bool cb_reload_preference(extcap_callback_info_t cb_info
)
987 GList
*arguments
= NULL
, * walker
= NULL
;
988 GList
**il
= (GList
**) cb_info
.data
;
990 arguments
= extcap_parse_values(cb_info
.output
);
992 walker
= g_list_first(arguments
);
993 while (walker
!= NULL
)
995 extcap_value
* val
= (extcap_value
*)walker
->data
;
996 *il
= g_list_append(*il
, val
);
997 walker
= g_list_next(walker
);
999 g_list_free(arguments
);
1005 extcap_get_if_configuration_values(const char * ifname
, const char * argname
, GHashTable
*arguments
)
1007 GList
* args
= NULL
;
1010 extcap_ensure_all_interfaces_loaded();
1012 extcap_interface
*interface
= extcap_find_interface_for_ifname(ifname
);
1015 ws_debug("Extcap path %s", get_extcap_dir());
1017 args
= g_list_append(args
, g_strdup(EXTCAP_ARGUMENT_CONFIG
));
1018 args
= g_list_append(args
, g_strdup(EXTCAP_ARGUMENT_INTERFACE
));
1019 args
= g_list_append(args
, g_strdup(ifname
));
1020 args
= g_list_append(args
, g_strdup(EXTCAP_ARGUMENT_RELOAD_OPTION
));
1021 args
= g_list_append(args
, g_strdup(argname
));
1025 GList
* keys
= g_hash_table_get_keys(arguments
);
1026 GList
* walker
= g_list_first(keys
);
1029 const char * key_data
= (const char *)walker
->data
;
1030 args
= g_list_append(args
, g_strdup(key_data
));
1031 args
= g_list_append(args
, g_strdup((const char *)g_hash_table_lookup(arguments
, key_data
)));
1032 walker
= g_list_next(walker
);
1037 extcap_run_one(interface
, args
, cb_reload_preference
, &ret
, NULL
);
1039 g_list_free_full(args
, g_free
);
1046 _extcap_requires_configuration_int(const char *ifname
, bool check_required
)
1048 GList
*arguments
= 0;
1049 GList
*walker
= 0, * item
= 0;
1052 extcap_ensure_all_interfaces_loaded();
1054 arguments
= extcap_get_if_configuration(ifname
);
1055 walker
= g_list_first(arguments
);
1057 while (walker
!= NULL
&& !found
)
1059 item
= g_list_first((GList
*)(walker
->data
));
1060 while (item
!= NULL
&& !found
)
1062 if ((extcap_arg
*)(item
->data
) != NULL
)
1064 extcap_arg
*arg
= (extcap_arg
*)(item
->data
);
1065 /* Should required options be present, or any kind of options */
1066 if (!check_required
)
1070 /* Following branch is executed when check of required items is requested */
1071 else if (arg
->is_required
)
1073 const char *stored
= NULL
;
1074 const char *defval
= NULL
;
1076 if (arg
->pref_valptr
!= NULL
)
1078 stored
= *arg
->pref_valptr
;
1081 if (arg
->default_complex
!= NULL
&& arg
->default_complex
->_val
!= NULL
)
1083 defval
= arg
->default_complex
->_val
;
1086 if (arg
->is_required
)
1088 if (!defval
&& (!stored
|| !*stored
))
1094 if (arg
->arg_type
== EXTCAP_ARG_FILESELECT
)
1096 if (arg
->fileexists
&& !(file_exists(defval
) || file_exists(stored
)))
1106 walker
= walker
->next
;
1108 extcap_free_if_configuration(arguments
, true);
1114 extcap_has_configuration(const char *ifname
)
1116 return _extcap_requires_configuration_int(ifname
, false);
1120 extcap_requires_configuration(const char *ifname
)
1122 return _extcap_requires_configuration_int(ifname
, true);
1125 static bool cb_verify_filter(extcap_callback_info_t cb_info
)
1127 extcap_filter_status
*status
= (extcap_filter_status
*)cb_info
.data
;
1128 size_t output_size
, i
;
1130 output_size
= strlen(cb_info
.output
);
1131 if (output_size
== 0) {
1132 *status
= EXTCAP_FILTER_VALID
;
1134 *status
= EXTCAP_FILTER_INVALID
;
1135 for (i
= 0; i
< output_size
; i
++) {
1136 if (cb_info
.output
[i
] == '\n' || cb_info
.output
[i
] == '\r') {
1137 cb_info
.output
[i
] = '\0';
1141 *cb_info
.err_str
= g_strdup(cb_info
.output
);
1147 extcap_filter_status
1148 extcap_verify_capture_filter(const char *ifname
, const char *filter
, char **err_str
)
1150 GList
* arguments
= NULL
;
1151 extcap_filter_status status
= EXTCAP_FILTER_UNKNOWN
;
1153 extcap_ensure_all_interfaces_loaded();
1155 extcap_interface
*interface
= extcap_find_interface_for_ifname(ifname
);
1158 ws_debug("Extcap path %s", get_extcap_dir());
1160 arguments
= g_list_append(arguments
, g_strdup(EXTCAP_ARGUMENT_CAPTURE_FILTER
));
1161 arguments
= g_list_append(arguments
, g_strdup(filter
));
1162 arguments
= g_list_append(arguments
, g_strdup(EXTCAP_ARGUMENT_INTERFACE
));
1163 arguments
= g_list_append(arguments
, g_strdup(ifname
));
1165 extcap_run_one(interface
, arguments
, cb_verify_filter
, &status
, err_str
);
1166 g_list_free_full(arguments
, g_free
);
1173 extcap_has_toolbar(const char *ifname
)
1175 if (!iface_toolbar_use())
1180 extcap_ensure_all_interfaces_loaded();
1182 GList
*toolbar_list
= g_hash_table_get_values (_toolbars
);
1183 for (GList
*walker
= toolbar_list
; walker
; walker
= walker
->next
)
1185 iface_toolbar
*toolbar
= (iface_toolbar
*) walker
->data
;
1186 if (g_list_find_custom(toolbar
->ifnames
, ifname
, (GCompareFunc
) g_strcmp0
))
1188 g_list_free(toolbar_list
);
1193 g_list_free(toolbar_list
);
1198 static gboolean
extcap_terminate_cb(void *user_data
)
1200 capture_session
*cap_session
= (capture_session
*)user_data
;
1201 capture_options
*capture_opts
= cap_session
->capture_opts
;
1202 interface_options
*interface_opts
;
1204 bool all_finished
= true;
1206 for (icnt
= 0; icnt
< capture_opts
->ifaces
->len
; icnt
++)
1208 interface_opts
= &g_array_index(capture_opts
->ifaces
, interface_options
,
1211 /* skip native interfaces */
1212 if (interface_opts
->if_type
!= IF_EXTCAP
)
1217 if (interface_opts
->extcap_pid
!= WS_INVALID_PID
)
1220 TerminateProcess(interface_opts
->extcap_pid
, 0);
1222 kill(interface_opts
->extcap_pid
, SIGKILL
);
1224 all_finished
= false;
1227 /* Do not care about stdout/stderr anymore */
1228 if (interface_opts
->extcap_stdout_watch
> 0)
1230 g_source_remove(interface_opts
->extcap_stdout_watch
);
1231 interface_opts
->extcap_stdout_watch
= 0;
1234 if (interface_opts
->extcap_stderr_watch
> 0)
1236 g_source_remove(interface_opts
->extcap_stderr_watch
);
1237 interface_opts
->extcap_stderr_watch
= 0;
1241 capture_opts
->wait_for_extcap_cbs
= true;
1242 capture_opts
->extcap_terminate_id
= 0;
1245 capture_process_finished(cap_session
);
1248 return G_SOURCE_REMOVE
;
1251 void extcap_request_stop(capture_session
*cap_session
)
1253 capture_options
*capture_opts
= cap_session
->capture_opts
;
1254 interface_options
*interface_opts
;
1257 if (capture_opts
->extcap_terminate_id
> 0)
1259 /* Already requested, do not extend timeout */
1263 if (capture_opts
->wait_for_extcap_cbs
)
1265 /* Terminate callback was called, waiting for child callbacks */
1269 if (extcap_session_stop(cap_session
))
1271 /* Nothing left to do, all extcaps have fully finished */
1275 for (icnt
= 0; icnt
< capture_opts
->ifaces
->len
; icnt
++)
1277 interface_opts
= &g_array_index(capture_opts
->ifaces
, interface_options
,
1280 /* skip native interfaces */
1281 if (interface_opts
->if_type
!= IF_EXTCAP
)
1286 ws_debug("Extcap [%s] - Requesting stop PID: %"PRIdMAX
, interface_opts
->name
,
1287 (intmax_t)interface_opts
->extcap_pid
);
1290 if (interface_opts
->extcap_pid
!= WS_INVALID_PID
)
1292 kill(interface_opts
->extcap_pid
, SIGTERM
);
1297 capture_opts
->extcap_terminate_id
=
1298 g_timeout_add_seconds(EXTCAP_CLEANUP_TIMEOUT
, extcap_terminate_cb
, cap_session
);
1302 extcap_add_arg_and_remove_cb(void *key
, void *value
, void *data
)
1304 GPtrArray
*args
= (GPtrArray
*)data
;
1308 g_ptr_array_add(args
, g_strdup((const char *)key
));
1312 g_ptr_array_add(args
, g_strdup((const char *)value
));
1321 bool extcap_session_stop(capture_session
*cap_session
)
1323 capture_options
*capture_opts
= cap_session
->capture_opts
;
1324 interface_options
*interface_opts
;
1327 for (i
= 0; i
< capture_opts
->ifaces
->len
; i
++)
1329 interface_opts
= &g_array_index(capture_opts
->ifaces
, interface_options
, i
);
1330 if (interface_opts
->if_type
!= IF_EXTCAP
)
1335 if ((interface_opts
->extcap_pid
!= WS_INVALID_PID
) ||
1336 (interface_opts
->extcap_stdout_watch
> 0) ||
1337 (interface_opts
->extcap_stderr_watch
> 0))
1339 /* Capture session is not finished, wait for remaining watches */
1343 g_free(interface_opts
->extcap_pipedata
);
1344 interface_opts
->extcap_pipedata
= NULL
;
1347 if (interface_opts
->extcap_pipe_h
!= INVALID_HANDLE_VALUE
)
1349 ws_debug("Extcap [%s] - Closing pipe", interface_opts
->name
);
1350 FlushFileBuffers(interface_opts
->extcap_pipe_h
);
1351 DisconnectNamedPipe(interface_opts
->extcap_pipe_h
);
1352 CloseHandle(interface_opts
->extcap_pipe_h
);
1353 interface_opts
->extcap_pipe_h
= INVALID_HANDLE_VALUE
;
1355 if (interface_opts
->extcap_control_in_h
!= INVALID_HANDLE_VALUE
)
1357 ws_debug("Extcap [%s] - Closing control_in pipe", interface_opts
->name
);
1358 FlushFileBuffers(interface_opts
->extcap_control_in_h
);
1359 DisconnectNamedPipe(interface_opts
->extcap_control_in_h
);
1360 CloseHandle(interface_opts
->extcap_control_in_h
);
1361 interface_opts
->extcap_control_in_h
= INVALID_HANDLE_VALUE
;
1363 if (interface_opts
->extcap_control_out_h
!= INVALID_HANDLE_VALUE
)
1365 ws_debug("Extcap [%s] - Closing control_out pipe", interface_opts
->name
);
1366 FlushFileBuffers(interface_opts
->extcap_control_out_h
);
1367 DisconnectNamedPipe(interface_opts
->extcap_control_out_h
);
1368 CloseHandle(interface_opts
->extcap_control_out_h
);
1369 interface_opts
->extcap_control_out_h
= INVALID_HANDLE_VALUE
;
1372 if (interface_opts
->extcap_fifo
!= NULL
&& file_exists(interface_opts
->extcap_fifo
))
1374 /* If extcap didn't open the fifo, dumpcap would be waiting on it
1375 * until user manually stops capture. Simply open and close fifo
1376 * here to let dumpcap return from the select() call. This has no
1377 * effect if dumpcap is not waiting.
1379 int fd
= ws_open(interface_opts
->extcap_fifo
, O_WRONLY
|O_NONBLOCK
, 0000);
1383 /* the fifo will not be freed here, but with the other capture_opts in capture_sync */
1384 ws_unlink(interface_opts
->extcap_fifo
);
1385 get_dirname(interface_opts
->extcap_fifo
);
1386 rmdir(interface_opts
->extcap_fifo
);
1387 interface_opts
->extcap_fifo
= NULL
;
1389 if (interface_opts
->extcap_control_in
&& file_exists(interface_opts
->extcap_control_in
))
1391 ws_unlink(interface_opts
->extcap_control_in
);
1392 interface_opts
->extcap_control_in
= NULL
;
1394 if (interface_opts
->extcap_control_out
&& file_exists(interface_opts
->extcap_control_out
))
1396 ws_unlink(interface_opts
->extcap_control_out
);
1397 interface_opts
->extcap_control_out
= NULL
;
1402 /* All child processes finished */
1403 capture_opts
->wait_for_extcap_cbs
= false;
1404 if (capture_opts
->extcap_terminate_id
> 0)
1406 g_source_remove(capture_opts
->extcap_terminate_id
);
1407 capture_opts
->extcap_terminate_id
= 0;
1410 /* Nothing left to do, do not prevent capture session stop */
1415 extcap_watch_removed(capture_session
*cap_session
, interface_options
*interface_opts
)
1417 if ((interface_opts
->extcap_pid
== WS_INVALID_PID
) &&
1418 (interface_opts
->extcap_stdout_watch
== 0) &&
1419 (interface_opts
->extcap_stderr_watch
== 0))
1421 /* Close session if this was the last remaining process */
1422 capture_process_finished(cap_session
);
1426 static interface_options
*
1427 extcap_find_channel_interface(capture_session
*cap_session
, GIOChannel
*source
)
1429 capture_options
*capture_opts
= cap_session
->capture_opts
;
1430 interface_options
*interface_opts
;
1433 for (i
= 0; i
< capture_opts
->ifaces
->len
; i
++)
1435 ws_pipe_t
*pipedata
;
1436 interface_opts
= &g_array_index(capture_opts
->ifaces
, interface_options
, i
);
1437 pipedata
= (ws_pipe_t
*)interface_opts
->extcap_pipedata
;
1439 ((pipedata
->stdout_io
== source
) || (pipedata
->stderr_io
== source
)))
1441 return interface_opts
;
1445 ws_assert_not_reached();
1449 extcap_stdout_cb(GIOChannel
*source
, GIOCondition condition _U_
, void *data
)
1451 capture_session
*cap_session
= (capture_session
*)data
;
1452 interface_options
*interface_opts
= extcap_find_channel_interface(cap_session
, source
);
1454 size_t bytes_read
= 0;
1455 GIOStatus status
= G_IO_STATUS_EOF
;
1457 /* Discard data to prevent child process hanging on stdout write */
1458 if (condition
& G_IO_IN
)
1460 status
= g_io_channel_read_chars(source
, buf
, sizeof(buf
), &bytes_read
, NULL
);
1463 if ((bytes_read
== 0) || (status
!= G_IO_STATUS_NORMAL
))
1465 interface_opts
->extcap_stdout_watch
= 0;
1466 extcap_watch_removed(cap_session
, interface_opts
);
1467 return G_SOURCE_REMOVE
;
1469 return G_SOURCE_CONTINUE
;
1473 extcap_stderr_cb(GIOChannel
*source
, GIOCondition condition
, void *data
)
1475 capture_session
*cap_session
= (capture_session
*)data
;
1476 interface_options
*interface_opts
= extcap_find_channel_interface(cap_session
, source
);
1478 size_t bytes_read
= 0;
1479 GIOStatus status
= G_IO_STATUS_EOF
;
1481 if (condition
& G_IO_IN
)
1483 status
= g_io_channel_read_chars(source
, buf
, sizeof(buf
), &bytes_read
, NULL
);
1486 #define STDERR_BUFFER_SIZE 1024
1489 if (interface_opts
->extcap_stderr
== NULL
)
1491 interface_opts
->extcap_stderr
= g_string_new_len(buf
, bytes_read
);
1495 ssize_t remaining
= STDERR_BUFFER_SIZE
- interface_opts
->extcap_stderr
->len
;
1498 ssize_t bytes
= bytes_read
;
1499 bytes
= MIN(bytes
, remaining
);
1500 g_string_append_len(interface_opts
->extcap_stderr
, buf
, bytes
);
1505 if ((bytes_read
== 0) || (status
!= G_IO_STATUS_NORMAL
))
1507 interface_opts
->extcap_stderr_watch
= 0;
1508 extcap_watch_removed(cap_session
, interface_opts
);
1509 return G_SOURCE_REMOVE
;
1511 return G_SOURCE_CONTINUE
;
1514 static void extcap_child_watch_cb(GPid pid
, int status _U_
, void *user_data
)
1517 interface_options
*interface_opts
;
1518 capture_session
*cap_session
= (capture_session
*)(user_data
);
1519 capture_options
*capture_opts
= cap_session
->capture_opts
;
1521 /* Close handle to child process. */
1522 g_spawn_close_pid(pid
);
1524 /* Update extcap_pid in interface options structure. */
1525 for (i
= 0; i
< capture_opts
->ifaces
->len
; i
++)
1527 interface_opts
= &g_array_index(capture_opts
->ifaces
, interface_options
, i
);
1528 if (interface_opts
->extcap_pid
== pid
)
1530 ws_debug("Extcap [%s] - Closing spawned PID: %"PRIdMAX
, interface_opts
->name
,
1531 (intmax_t)interface_opts
->extcap_pid
);
1532 interface_opts
->extcap_pid
= WS_INVALID_PID
;
1533 extcap_watch_removed(cap_session
, interface_opts
);
1540 GPtrArray
*extcap_prepare_arguments(interface_options
*interface_opts
)
1542 GPtrArray
*result
= NULL
;
1544 if (interface_opts
->if_type
== IF_EXTCAP
)
1546 result
= g_ptr_array_new();
1548 #define add_arg(X) g_ptr_array_add(result, g_strdup(X))
1550 add_arg(interface_opts
->extcap
);
1551 add_arg(EXTCAP_ARGUMENT_RUN_CAPTURE
);
1552 add_arg(EXTCAP_ARGUMENT_INTERFACE
);
1553 add_arg(interface_opts
->name
);
1554 if (interface_opts
->cfilter
&& strlen(interface_opts
->cfilter
) > 0)
1556 add_arg(EXTCAP_ARGUMENT_CAPTURE_FILTER
);
1557 add_arg(interface_opts
->cfilter
);
1559 add_arg(EXTCAP_ARGUMENT_RUN_PIPE
);
1560 add_arg(interface_opts
->extcap_fifo
);
1561 if (interface_opts
->extcap_control_in
)
1563 add_arg(EXTCAP_ARGUMENT_CONTROL_OUT
);
1564 add_arg(interface_opts
->extcap_control_in
);
1566 if (interface_opts
->extcap_control_out
)
1568 add_arg(EXTCAP_ARGUMENT_CONTROL_IN
);
1569 add_arg(interface_opts
->extcap_control_out
);
1571 if (interface_opts
->extcap_args
== NULL
|| g_hash_table_size(interface_opts
->extcap_args
) == 0)
1573 /* User did not perform interface configuration.
1575 * Check if there are any boolean flags that are set by default
1576 * and hence their argument should be added.
1581 arglist
= extcap_get_if_configuration(interface_opts
->name
);
1582 for (elem
= g_list_first(arglist
); elem
; elem
= elem
->next
)
1585 extcap_arg
*arg_iter
;
1587 if (elem
->data
== NULL
)
1592 arg_list
= g_list_first((GList
*)elem
->data
);
1593 while (arg_list
!= NULL
)
1595 const char *stored
= NULL
;
1596 /* In case of boolflags only first element in arg_list is relevant. */
1597 arg_iter
= (extcap_arg
*)(arg_list
->data
);
1598 if (arg_iter
->pref_valptr
!= NULL
)
1600 stored
= *arg_iter
->pref_valptr
;
1603 if (arg_iter
->arg_type
== EXTCAP_ARG_BOOLFLAG
)
1605 if (!stored
&& extcap_complex_get_bool(arg_iter
->default_complex
))
1607 add_arg(arg_iter
->call
);
1609 else if (g_strcmp0(stored
, "true") == 0)
1611 add_arg(arg_iter
->call
);
1616 if (stored
&& strlen(stored
) > 0) {
1617 add_arg(arg_iter
->call
);
1622 arg_list
= arg_list
->next
;
1626 extcap_free_if_configuration(arglist
, true);
1630 g_hash_table_foreach_remove(interface_opts
->extcap_args
, extcap_add_arg_and_remove_cb
, result
);
1640 static void ptr_array_free(void *data
, void *user_data _U_
)
1646 static bool extcap_create_pipe(const char *ifname
, char **fifo
, HANDLE
*handle_out
, const char *pipe_prefix
)
1648 char timestr
[ 14 + 1 ];
1649 time_t current_time
;
1650 char *pipename
= NULL
;
1651 SECURITY_ATTRIBUTES security
;
1653 /* create pipename */
1654 current_time
= time(NULL
);
1656 * XXX - we trust Windows not to return a time before the Epoch here,
1657 * so we won't get a null pointer back from localtime().
1659 strftime(timestr
, sizeof(timestr
), "%Y%m%d%H%M%S", localtime(¤t_time
));
1660 pipename
= g_strconcat("\\\\.\\pipe\\", pipe_prefix
, "_", ifname
, "_", timestr
, NULL
);
1662 /* Security struct to enable Inheritable HANDLE */
1663 memset(&security
, 0, sizeof(SECURITY_ATTRIBUTES
));
1664 security
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
1665 security
.bInheritHandle
= false;
1666 security
.lpSecurityDescriptor
= NULL
;
1668 /* create a namedPipe */
1669 *handle_out
= CreateNamedPipe(
1670 utf_8to16(pipename
),
1671 PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
,
1672 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
| PIPE_WAIT
,
1677 if (*handle_out
== INVALID_HANDLE_VALUE
)
1679 ws_debug("Error creating pipe => (%ld)", GetLastError());
1685 ws_debug("Wireshark Created pipe =>(%s) handle (%" PRIuMAX
")", pipename
, (uintmax_t)*handle_out
);
1686 *fifo
= g_strdup(pipename
);
1692 static bool extcap_create_pipe(const char *ifname
, char **fifo
, const char *temp_dir
, const char *pipe_prefix
)
1694 char *subdir_tmpl
= g_strdup_printf("%s_%s_XXXXXX", pipe_prefix
, ifname
);
1695 char *temp_subdir
= create_tempdir(temp_dir
, subdir_tmpl
, NULL
);
1697 g_free(subdir_tmpl
);
1698 if (temp_subdir
== NULL
)
1703 char *fifo_path
= g_build_path(G_DIR_SEPARATOR_S
, temp_subdir
, "fifo", NULL
);
1704 g_free(temp_subdir
);
1706 ws_debug("Extcap - Creating fifo: %s", fifo_path
);
1708 if (mkfifo(fifo_path
, 0600) == 0)
1720 /* call mkfifo for each extcap,
1721 * returns false if there's an error creating a FIFO */
1723 extcap_init_interfaces(capture_session
*cap_session
)
1725 capture_options
*capture_opts
= cap_session
->capture_opts
;
1727 interface_options
*interface_opts
;
1728 ws_pipe_t
*pipedata
;
1730 extcap_ensure_all_interfaces_loaded();
1732 for (i
= 0; i
< capture_opts
->ifaces
->len
; i
++)
1734 GPtrArray
*args
= NULL
;
1735 GPid pid
= WS_INVALID_PID
;
1737 interface_opts
= &g_array_index(capture_opts
->ifaces
, interface_options
, i
);
1739 /* skip native interfaces */
1740 if (interface_opts
->if_type
!= IF_EXTCAP
)
1745 /* create control pipes if having toolbar */
1746 if (extcap_has_toolbar(interface_opts
->name
))
1748 extcap_create_pipe(interface_opts
->name
, &interface_opts
->extcap_control_in
,
1750 &interface_opts
->extcap_control_in_h
,
1752 capture_opts
->temp_dir
,
1754 EXTCAP_CONTROL_IN_PREFIX
);
1755 extcap_create_pipe(interface_opts
->name
, &interface_opts
->extcap_control_out
,
1757 &interface_opts
->extcap_control_out_h
,
1759 capture_opts
->temp_dir
,
1761 EXTCAP_CONTROL_OUT_PREFIX
);
1764 /* create pipe for fifo */
1765 if (!extcap_create_pipe(interface_opts
->name
, &interface_opts
->extcap_fifo
,
1767 &interface_opts
->extcap_pipe_h
,
1769 capture_opts
->temp_dir
,
1771 EXTCAP_PIPE_PREFIX
))
1777 /* Create extcap call */
1778 args
= extcap_prepare_arguments(interface_opts
);
1780 pipedata
= g_new0(ws_pipe_t
, 1);
1782 pid
= ws_pipe_spawn_async(pipedata
, args
);
1784 g_ptr_array_foreach(args
, ptr_array_free
, NULL
);
1785 g_ptr_array_free(args
, true);
1787 if (pid
== WS_INVALID_PID
)
1793 g_io_channel_unref(pipedata
->stdin_io
);
1794 pipedata
->stdin_io
= NULL
;
1795 interface_opts
->extcap_pid
= pid
;
1797 g_child_watch_add_full(G_PRIORITY_HIGH
, pid
, extcap_child_watch_cb
,
1798 (void *)cap_session
, NULL
);
1799 interface_opts
->extcap_stdout_watch
=
1800 g_io_add_watch(pipedata
->stdout_io
, G_IO_IN
| G_IO_HUP
,
1801 extcap_stdout_cb
, (void *)cap_session
);
1802 interface_opts
->extcap_stderr_watch
=
1803 g_io_add_watch(pipedata
->stderr_io
, G_IO_IN
| G_IO_HUP
,
1804 extcap_stderr_cb
, (void *)cap_session
);
1806 /* Pipedata pointers are only used to match GIOChannel to interface.
1807 * GIOChannel watch holds the only remaining reference.
1809 g_io_channel_unref(pipedata
->stdout_io
);
1810 g_io_channel_unref(pipedata
->stderr_io
);
1813 /* On Windows, wait for extcap to connect to named pipe.
1814 * Some extcaps will present UAC screen to user.
1815 * 30 second timeout should be reasonable timeout for extcap to
1816 * connect to named pipe (including user interaction).
1817 * Wait on multiple object in case of extcap termination
1818 * without opening pipe.
1820 if (pid
!= WS_INVALID_PID
)
1822 HANDLE pipe_handles
[3];
1823 int num_pipe_handles
= 1;
1824 pipe_handles
[0] = interface_opts
->extcap_pipe_h
;
1826 if (extcap_has_toolbar(interface_opts
->name
))
1828 pipe_handles
[1] = interface_opts
->extcap_control_in_h
;
1829 pipe_handles
[2] = interface_opts
->extcap_control_out_h
;
1830 num_pipe_handles
+= 2;
1833 ws_pipe_wait_for_pipe(pipe_handles
, num_pipe_handles
, pid
);
1837 interface_opts
->extcap_pipedata
= (void *) pipedata
;
1842 #endif /* HAVE_LIBPCAP */
1844 /************* EXTCAP LOAD INTERFACE LIST ***************
1846 * The following code handles loading and reloading the interface list. It is explicitly
1847 * kept separate from the rest
1852 extcap_free_interface_info(void *data
)
1854 extcap_info
*info
= (extcap_info
*)data
;
1856 g_free(info
->basename
);
1857 g_free(info
->full_path
);
1858 g_free(info
->version
);
1861 extcap_free_interfaces(info
->interfaces
);
1866 static extcap_info
*
1867 extcap_ensure_interface(const char * toolname
, bool create_if_nonexist
)
1869 extcap_info
* element
= 0;
1871 if ( prefs
.capture_no_extcap
)
1877 if ( ! _loaded_interfaces
)
1878 _loaded_interfaces
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, extcap_free_interface
);
1880 element
= (extcap_info
*) g_hash_table_lookup(_loaded_interfaces
, toolname
);
1884 if ( ! element
&& create_if_nonexist
)
1886 g_hash_table_insert(_loaded_interfaces
, g_strdup(toolname
), g_new0(extcap_info
, 1));
1887 element
= (extcap_info
*) g_hash_table_lookup(_loaded_interfaces
, toolname
);
1894 extcap_get_tool_by_ifname(const char *ifname
)
1896 extcap_ensure_all_interfaces_loaded();
1898 if ( ifname
&& _tool_for_ifname
)
1900 char * toolname
= (char *)g_hash_table_lookup(_tool_for_ifname
, ifname
);
1902 return extcap_ensure_interface(toolname
, false);
1909 extcap_get_tool_info(const char * toolname
)
1911 extcap_ensure_all_interfaces_loaded();
1913 return extcap_ensure_interface(toolname
, false);
1916 static void remove_extcap_entry(void *entry
, void *data _U_
)
1918 extcap_interface
*int_iter
= (extcap_interface
*)entry
;
1920 if (int_iter
->if_type
== EXTCAP_SENTENCE_EXTCAP
)
1921 extcap_free_interface(entry
);
1925 process_new_extcap(const char *extcap
, char *output
)
1927 GList
* interfaces
= NULL
, * control_items
= NULL
, * walker
= NULL
;
1928 extcap_interface
* int_iter
= NULL
;
1929 extcap_info
* element
= NULL
;
1930 iface_toolbar
* toolbar_entry
= NULL
;
1931 char * toolname
= g_path_get_basename(extcap
);
1933 GList
* interface_keys
= g_hash_table_get_keys(_loaded_interfaces
);
1935 /* Load interfaces from utility */
1936 interfaces
= extcap_parse_interfaces(output
, &control_items
);
1938 ws_debug("Loading interface list for %s ", extcap
);
1940 /* Seems, that there where no interfaces to be loaded */
1941 if ( ! interfaces
|| g_list_length(interfaces
) == 0 )
1943 ws_debug("Cannot load interfaces for %s", extcap
);
1944 g_list_free(interface_keys
);
1949 /* Load or create the storage element for the tool */
1950 element
= extcap_ensure_interface(toolname
, true);
1951 if ( element
== NULL
)
1953 ws_warning("Cannot store interface %s, already loaded as personal plugin", extcap
);
1954 g_list_foreach(interfaces
, remove_extcap_entry
, NULL
);
1955 g_list_free(interfaces
);
1956 g_list_free(interface_keys
);
1963 toolbar_entry
= g_new0(iface_toolbar
, 1);
1964 toolbar_entry
->controls
= control_items
;
1967 walker
= interfaces
;
1969 while (walker
!= NULL
)
1971 int_iter
= (extcap_interface
*)walker
->data
;
1973 if (int_iter
->call
!= NULL
)
1974 ws_debug("Interface found %s\n", int_iter
->call
);
1976 /* Help is not necessarily stored with the interface, but rather with the version string.
1977 * As the version string always comes in front of the interfaces, this ensures, that it gets
1978 * properly stored with the interface */
1979 if (int_iter
->if_type
== EXTCAP_SENTENCE_EXTCAP
)
1981 if (int_iter
->call
!= NULL
)
1982 ws_debug(" Extcap [%s] ", int_iter
->call
);
1984 /* Only initialize values if none are set. Need to check only one element here */
1985 if ( ! element
->version
)
1987 element
->version
= g_strdup(int_iter
->version
);
1988 element
->basename
= g_strdup(toolname
);
1989 element
->full_path
= g_strdup(extcap
);
1990 element
->help
= g_strdup(int_iter
->help
);
1993 help
= int_iter
->help
;
1996 toolbar_entry
->menu_title
= g_strdup(int_iter
->display
);
1997 toolbar_entry
->help
= g_strdup(int_iter
->help
);
2000 walker
= g_list_next(walker
);
2004 /* Only interface definitions will be parsed here. help is already set by the extcap element,
2005 * which makes it necessary to have version in the list before the interfaces. This is normally
2006 * the case by design, but could be changed by separating the information in extcap-base. */
2007 if ( int_iter
->if_type
== EXTCAP_SENTENCE_INTERFACE
)
2009 if ( g_list_find(interface_keys
, int_iter
->call
) )
2011 ws_warning("Extcap interface \"%s\" is already provided by \"%s\" ",
2012 int_iter
->call
, extcap_if_executable(int_iter
->call
));
2013 walker
= g_list_next(walker
);
2017 if ((int_iter
->call
!= NULL
) && (int_iter
->display
))
2018 ws_debug(" Interface [%s] \"%s\" ", int_iter
->call
, int_iter
->display
);
2020 int_iter
->extcap_path
= g_strdup(extcap
);
2022 /* Only set the help, if it exists and no parsed help information is present */
2023 if ( ! int_iter
->help
&& help
)
2024 int_iter
->help
= g_strdup(help
);
2026 element
->interfaces
= g_list_append(element
->interfaces
, int_iter
);
2027 g_hash_table_insert(_tool_for_ifname
, g_strdup(int_iter
->call
), g_strdup(toolname
));
2031 if (!toolbar_entry
->menu_title
)
2033 toolbar_entry
->menu_title
= g_strdup(int_iter
->display
);
2035 toolbar_entry
->ifnames
= g_list_append(toolbar_entry
->ifnames
, g_strdup(int_iter
->call
));
2039 walker
= g_list_next(walker
);
2042 if (toolbar_entry
&& toolbar_entry
->menu_title
)
2044 iface_toolbar_add(toolbar_entry
);
2045 if (extcap_iface_toolbar_add(extcap
, toolbar_entry
))
2047 toolbar_entry
= NULL
;
2051 extcap_free_toolbar(toolbar_entry
);
2052 g_list_foreach(interfaces
, remove_extcap_entry
, NULL
);
2053 g_list_free(interfaces
);
2054 g_list_free(interface_keys
);
2059 /** Thread callback to save the output of a --extcap-config call. */
2061 extcap_process_config_cb(thread_pool_t
*pool _U_
, void *data
, char *output
)
2063 extcap_iface_info_t
*iface_info
= (extcap_iface_info_t
*)data
;
2064 iface_info
->output
= output
;
2068 * Thread callback to process discovered interfaces, scheduling more tasks to
2069 * retrieve the configuration for each interface. Called once for every extcap
2073 extcap_process_interfaces_cb(thread_pool_t
*pool
, void *data
, char *output
)
2075 extcap_run_extcaps_info_t
*info
= (extcap_run_extcaps_info_t
*)data
;
2077 unsigned num_interfaces
= 0;
2080 // No interfaces available, nothing to do.
2084 // Save output for process_new_extcap.
2085 info
->output
= output
;
2087 // Are there any interfaces to query information from?
2088 GList
*interfaces
= extcap_parse_interfaces(output
, NULL
);
2089 for (GList
*iface
= interfaces
; iface
; iface
= g_list_next(iface
)) {
2090 extcap_interface
*intf
= (extcap_interface
*)iface
->data
;
2091 if (intf
->if_type
== EXTCAP_SENTENCE_INTERFACE
) {
2095 if (num_interfaces
== 0) {
2097 g_list_free_full(interfaces
, extcap_free_interface
);
2101 /* GSList is not thread-safe, so pre-allocate an array instead. */
2102 info
->iface_infos
= g_new0(extcap_iface_info_t
, num_interfaces
);
2103 info
->num_interfaces
= num_interfaces
;
2105 // Schedule new commands to retrieve the configuration.
2106 for (GList
*iface
= interfaces
; iface
; iface
= g_list_next(iface
)) {
2107 extcap_interface
*intf
= (extcap_interface
*)iface
->data
;
2108 if (intf
->if_type
!= EXTCAP_SENTENCE_INTERFACE
) {
2112 const char *argv
[] = {
2113 EXTCAP_ARGUMENT_CONFIG
,
2114 EXTCAP_ARGUMENT_INTERFACE
,
2118 extcap_run_task_t
*task
= g_new0(extcap_run_task_t
, 1);
2119 extcap_iface_info_t
*iface_info
= &info
->iface_infos
[i
++];
2121 task
->extcap_path
= info
->extcap_path
;
2122 task
->argv
= g_strdupv((char **)argv
);
2123 task
->output_cb
= extcap_process_config_cb
;
2124 task
->data
= iface_info
;
2125 iface_info
->ifname
= g_strdup(intf
->call
);
2127 thread_pool_push(pool
, task
, NULL
);
2129 g_list_free_full(interfaces
, extcap_free_interface
);
2133 * Thread callback to check whether the new-style --list-interfaces call with an
2134 * explicit function succeeded. If not, schedule a call without the new version
2138 extcap_list_interfaces_cb(thread_pool_t
*pool
, void *data
, char *output
)
2140 extcap_run_extcaps_info_t
*info
= (extcap_run_extcaps_info_t
*)data
;
2143 /* No output available, schedule a fallback query. */
2144 const char *argv
[] = {
2145 EXTCAP_ARGUMENT_LIST_INTERFACES
,
2148 extcap_run_task_t
*task
= g_new0(extcap_run_task_t
, 1);
2150 task
->extcap_path
= info
->extcap_path
;
2151 task
->argv
= g_strdupv((char **)argv
);
2152 task
->output_cb
= extcap_process_interfaces_cb
;
2155 thread_pool_push(pool
, task
, NULL
);
2157 extcap_process_interfaces_cb(pool
, info
, output
);
2162 /* Handles loading of the interfaces. */
2164 extcap_load_interface_list(void)
2166 bool prefs_registered
= false;
2167 if (prefs
.capture_no_extcap
)
2172 // Remove existing interface toolbars here instead of in extcap_clear_interfaces()
2173 // to avoid flicker in shown toolbars when refreshing interfaces.
2174 GList
*toolbar_list
= g_hash_table_get_values (_toolbars
);
2175 for (GList
*walker
= toolbar_list
; walker
; walker
= walker
->next
)
2177 iface_toolbar
*toolbar
= (iface_toolbar
*) walker
->data
;
2178 iface_toolbar_remove(toolbar
->menu_title
);
2180 g_list_free(toolbar_list
);
2181 g_hash_table_remove_all(_toolbars
);
2183 _toolbars
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, extcap_free_toolbar
);
2186 if (_loaded_interfaces
== NULL
)
2191 extcap_run_extcaps_info_t
*infos
;
2193 _loaded_interfaces
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, extcap_free_interface_info
);
2194 /* Cleanup lookup table */
2195 if ( _tool_for_ifname
)
2197 g_hash_table_remove_all(_tool_for_ifname
);
2198 _tool_for_ifname
= 0;
2200 _tool_for_ifname
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
2203 get_ws_version_number(&major
, &minor
, NULL
);
2204 char *arg_version
= ws_strdup_printf("%s=%d.%d", EXTCAP_ARGUMENT_VERSION
, major
, minor
);
2205 const char *argv
[] = {
2206 EXTCAP_ARGUMENT_LIST_INTERFACES
,
2210 infos
= (extcap_run_extcaps_info_t
*)extcap_run_all(argv
,
2211 extcap_list_interfaces_cb
, sizeof(extcap_run_extcaps_info_t
),
2213 for (unsigned i
= 0; i
< count
; i
++) {
2214 if (!infos
[i
].output
) {
2218 // Save new extcap and each discovered interface.
2219 process_new_extcap(infos
[i
].extcap_path
, infos
[i
].output
);
2220 for (unsigned j
= 0; j
< infos
[i
].num_interfaces
; j
++) {
2221 extcap_iface_info_t
*iface_info
= &infos
[i
].iface_infos
[j
];
2223 if (!iface_info
->output
) {
2227 extcap_callback_info_t cb_info
= {
2228 .ifname
= iface_info
->ifname
,
2229 .output
= iface_info
->output
,
2232 prefs_registered
= cb_preference(cb_info
);
2235 extcap_free_extcaps_info_array(infos
, count
);
2236 g_free(arg_version
);
2239 if (prefs_registered
)
2241 prefs_read_module("extcap");
2246 * Editor modelines - https://www.wireshark.org/tools/modelines.html
2251 * indent-tabs-mode: nil
2254 * vi: set shiftwidth=4 tabstop=8 expandtab:
2255 * :indentSize=4:tabSize=8:noTabs=true: