1 /* extcap.c
2  *
3  * Routines for extcap external capture
4  * Copyright 2013, Mike Ryan <mikeryan@lacklustre.net>
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later
11  */
12 
13 #include <config.h>
14 #define WS_LOG_DOMAIN LOG_DOMAIN_CAPCHILD
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 
20 #ifdef _WIN32
21 #include <windows.h>
22 #include <process.h>
23 #include <time.h>
24 #else
25 #include <sys/wait.h>
26 /* Include for unlink */
27 #include <unistd.h>
28 #endif
29 
30 #include <sys/types.h>
31 #ifdef HAVE_SYS_WAIT_H
32 #include <sys/wait.h>
33 #endif
34 
35 #include <glib.h>
36 
37 #include <epan/prefs.h>
38 
39 #include "ui/iface_toolbar.h"
40 
41 #include <wsutil/file_util.h>
42 #include <wsutil/filesystem.h>
43 #include <wsutil/ws_pipe.h>
44 #include <wsutil/tempfile.h>
45 #include <wsutil/wslog.h>
46 #include <wsutil/ws_assert.h>
47 
48 #include "capture_opts.h"
49 
50 #include "extcap.h"
51 #include "extcap_parser.h"
52 
53 #include "ui/version_info.h"
54 
55 static void extcap_child_watch_cb(GPid pid, gint status, gpointer user_data);
56 
57 /* internal container, for all the extcap executables that have been found.
58  * Will be reset if extcap_clear_interfaces() is being explicitly called
59  * and is being used for printing information about all extcap interfaces found,
60  * as well as storing all sub-interfaces
61  */
62 static GHashTable * _loaded_interfaces = NULL;
63 
64 /* Internal container, which maps each ifname to the tool providing it, for faster
65  * lookup. The key and string value are owned by this table.
66  */
67 static GHashTable * _tool_for_ifname = NULL;
68 
69 /* internal container, for all the extcap executables that have been found
70  * and that provides a toolbar with controls to be added to a Interface Toolbar
71  */
72 static GHashTable *_toolbars = NULL;
73 
74 /* internal container, to map preference names to pointers that hold preference
75  * values. These ensure that preferences can survive extcap if garbage
76  * collection, and does not lead to dangling pointers in the prefs subsystem.
77  */
78 static GHashTable *_extcap_prefs_dynamic_vals = NULL;
79 
80 typedef struct _extcap_callback_info_t
81 {
82     const gchar * extcap;
83     const gchar * ifname;
84     gchar * output;
85     void * data;
86     gchar ** err_str;
87 } extcap_callback_info_t;
88 
89 /* Callback definition for extcap_foreach */
90 typedef gboolean(*extcap_cb_t)(extcap_callback_info_t info_structure);
91 
92 /** GThreadPool does not support pushing new work from a thread while waiting
93  * for the thread pool to finish. This data structure tracks ongoing work.
94  * See https://gitlab.gnome.org/GNOME/glib/issues/1598 */
95 typedef struct thread_pool {
96     GThreadPool    *pool;
97     gint            count;  /**< Number of tasks that have not finished. */
98     GCond           cond;
99     GMutex          data_mutex;
100 } thread_pool_t;
101 
102 /**
103  * Callback definition for extcap_run_all, invoked with a thread pool (to
104  * schedule new tasks), an opaque data parameter, and the output from last task
105  * (or NULL if it failed). The output must be freed by the callback function.
106  * The implementation MUST be thread-safe.
107  */
108 typedef void (*extcap_run_cb_t)(thread_pool_t *pool, void *data, char *output);
109 
110 typedef struct extcap_run_task {
111     const char     *extcap_path;
112     char          **argv;       /**< NULL-terminated arguments list, freed when the task is completed. */
113     extcap_run_cb_t output_cb;
114     void           *data;       /** Parameter to be passed to output_cb. */
115 } extcap_run_task_t;
116 
117 typedef struct extcap_iface_info {
118     char *ifname;                       /**< Interface name. */
119     char *output;                       /**< Output of --extcap-config. */
120 } extcap_iface_info_t;
121 
122 typedef struct extcap_run_extcaps_info {
123     char    *extcap_path;               /**< Extcap program path, MUST be the first member.  */
124     char    *output;                    /**< Output of --extcap-interfaces. */
125     guint   num_interfaces;             /**< Number of discovered interfaces. */
126     extcap_iface_info_t *iface_infos;   /**< Per-interface information. */
127 } extcap_run_extcaps_info_t;
128 
129 
130 static void extcap_load_interface_list(void);
131 
132 /* Used for lazily loading our interfaces. */
extcap_ensure_all_interfaces_loaded(void)133 static void extcap_ensure_all_interfaces_loaded(void) {
134     if ( !_loaded_interfaces || g_hash_table_size(_loaded_interfaces) == 0 )
135         extcap_load_interface_list();
136 }
137 
138 static gboolean
thread_pool_push(thread_pool_t * pool,gpointer data,GError ** error)139 thread_pool_push(thread_pool_t *pool, gpointer data, GError **error)
140 {
141     g_mutex_lock(&pool->data_mutex);
142     ++pool->count;
143     g_mutex_unlock(&pool->data_mutex);
144     return g_thread_pool_push(pool->pool, data, error);
145 }
146 
147 static void
thread_pool_wait(thread_pool_t * pool)148 thread_pool_wait(thread_pool_t *pool)
149 {
150     g_mutex_lock(&pool->data_mutex);
151     while (pool->count != 0) {
152         g_cond_wait(&pool->cond, &pool->data_mutex);
153     }
154     g_mutex_unlock(&pool->data_mutex);
155 }
156 
157 static GHashTable *
extcap_loaded_interfaces(void)158 extcap_loaded_interfaces(void)
159 {
160     if (prefs.capture_no_extcap)
161         return NULL;
162 
163     extcap_ensure_all_interfaces_loaded();
164 
165     return _loaded_interfaces;
166 }
167 
168 void
extcap_clear_interfaces(void)169 extcap_clear_interfaces(void)
170 {
171     if ( _loaded_interfaces )
172         g_hash_table_destroy(_loaded_interfaces);
173     _loaded_interfaces = NULL;
174 
175     if ( _tool_for_ifname )
176         g_hash_table_destroy(_tool_for_ifname);
177     _tool_for_ifname = NULL;
178 }
179 
180 static gint
compare_tools(gconstpointer a,gconstpointer b)181 compare_tools(gconstpointer a, gconstpointer b)
182 {
183     return g_strcmp0((*(extcap_info *const *)a)->basename, (*(extcap_info *const *)b)->basename);
184 }
185 
186 void
extcap_get_descriptions(plugin_description_callback callback,void * callback_data)187 extcap_get_descriptions(plugin_description_callback callback, void *callback_data)
188 {
189     extcap_ensure_all_interfaces_loaded();
190 
191     GHashTable * tools = extcap_loaded_interfaces();
192     GPtrArray *tools_array = g_ptr_array_new();
193 
194     if (tools && g_hash_table_size(tools) > 0) {
195         GList * keys = g_hash_table_get_keys(tools);
196         GList * walker = g_list_first(keys);
197         while (walker && walker->data) {
198             extcap_info * tool = (extcap_info *)g_hash_table_lookup(tools, walker->data);
199             if (tool) {
200                 g_ptr_array_add(tools_array, tool);
201             }
202             walker = g_list_next(walker);
203         }
204         g_list_free(keys);
205     }
206 
207     g_ptr_array_sort(tools_array, compare_tools);
208 
209     for (guint i = 0; i < tools_array->len; i++) {
210         extcap_info *tool = (extcap_info *)tools_array->pdata[i];
211         callback(tool->basename, tool->version, "extcap", tool->full_path, callback_data);
212     }
213 
214     g_ptr_array_free(tools_array, TRUE);
215 }
216 
217 static void
print_extcap_description(const char * basename,const char * version,const char * description,const char * filename,void * user_data _U_)218 print_extcap_description(const char *basename, const char *version,
219                         const char *description, const char *filename,
220                         void *user_data _U_)
221 {
222     printf("%-16s\t%s\t%s\t%s\n", basename, version, description, filename);
223 }
224 
225 void
extcap_dump_all(void)226 extcap_dump_all(void)
227 {
228     extcap_get_descriptions(print_extcap_description, NULL);
229 }
230 
231 static GSList *
extcap_get_extcap_paths_from_dir(GSList * list,const char * dirname)232 extcap_get_extcap_paths_from_dir(GSList * list, const char * dirname)
233 {
234     GDir * dir;
235     const char * file;
236 
237     GSList * paths = list;
238 
239     if ((dir = g_dir_open(dirname, 0, NULL)) != NULL) {
240         while ((file = g_dir_read_name(dir)) != NULL) {
241             /* full path to extcap binary */
242             gchar *extcap_path = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dirname, file);
243             /* treat anything executable as an extcap binary */
244             if (g_file_test(extcap_path, G_FILE_TEST_IS_REGULAR) &&
245                 g_file_test(extcap_path, G_FILE_TEST_IS_EXECUTABLE)) {
246                 paths = g_slist_append(paths, extcap_path);
247             } else {
248                 g_free(extcap_path);
249             }
250 
251         }
252         g_dir_close(dir);
253     }
254 
255     return paths;
256 }
257 
258 /**
259  * Obtains a list of extcap program paths. Use g_slist_free_full(paths, g_free)
260  * to destroy the list.
261  */
262 static GSList *
extcap_get_extcap_paths(void)263 extcap_get_extcap_paths(void)
264 {
265     GSList *paths = NULL;
266 
267     char *persconffile_path = get_persconffile_path("extcap", FALSE);
268     paths = extcap_get_extcap_paths_from_dir(paths, persconffile_path);
269     g_free(persconffile_path);
270 
271     paths = extcap_get_extcap_paths_from_dir(paths, get_extcap_dir());
272 
273     return paths;
274 }
275 
276 static extcap_interface *
extcap_find_interface_for_ifname(const gchar * ifname)277 extcap_find_interface_for_ifname(const gchar *ifname)
278 {
279     extcap_interface * result = NULL;
280 
281     if ( !ifname || ! _tool_for_ifname || ! _loaded_interfaces )
282         return result;
283 
284     gchar * extcap_util = (gchar *)g_hash_table_lookup(_tool_for_ifname, ifname);
285     if ( ! extcap_util )
286         return result;
287 
288     extcap_info * element = (extcap_info *)g_hash_table_lookup(_loaded_interfaces, extcap_util);
289     if ( ! element )
290         return result;
291 
292     GList * walker = element->interfaces;
293     while ( walker && walker->data && ! result )
294     {
295         extcap_interface * interface = (extcap_interface *)walker->data;
296         if ( g_strcmp0(interface->call, ifname) == 0 )
297         {
298             result = interface;
299             break;
300         }
301 
302         walker = g_list_next ( walker );
303     }
304 
305     return result;
306 }
307 
308 static void
extcap_free_toolbar(gpointer data)309 extcap_free_toolbar(gpointer data)
310 {
311     if (!data)
312     {
313         return;
314     }
315 
316     iface_toolbar *toolbar = (iface_toolbar *)data;
317 
318     g_free(toolbar->menu_title);
319     g_free(toolbar->help);
320     g_list_free_full(toolbar->ifnames, g_free);
321     g_list_free_full(toolbar->controls, (GDestroyNotify)extcap_free_toolbar_control);
322     g_free(toolbar);
323 }
324 
325 static gchar *
extcap_if_executable(const gchar * ifname)326 extcap_if_executable(const gchar *ifname)
327 {
328     extcap_interface *interface = extcap_find_interface_for_ifname(ifname);
329     return interface != NULL ? interface->extcap_path : NULL;
330 }
331 
332 static gboolean
extcap_iface_toolbar_add(const gchar * extcap,iface_toolbar * toolbar_entry)333 extcap_iface_toolbar_add(const gchar *extcap, iface_toolbar *toolbar_entry)
334 {
335     char *toolname;
336     gboolean ret = FALSE;
337 
338     if (!extcap || !toolbar_entry)
339     {
340         return ret;
341     }
342 
343     toolname = g_path_get_basename(extcap);
344 
345     if (!g_hash_table_lookup(_toolbars, toolname))
346     {
347         g_hash_table_insert(_toolbars, g_strdup(toolname), toolbar_entry);
348         ret = TRUE;
349     }
350 
351     g_free(toolname);
352     return ret;
353 }
354 
355 static gchar **
extcap_convert_arguments_to_array(GList * arguments)356 extcap_convert_arguments_to_array(GList * arguments)
357 {
358     gchar ** result = NULL;
359     if ( arguments )
360     {
361         GList * walker = g_list_first(arguments);
362         int cnt = 0;
363 
364         result = (gchar **) g_malloc0(sizeof(gchar *) * (g_list_length(arguments)));
365 
366         while(walker)
367         {
368             result[cnt] = g_strdup((const gchar *)walker->data);
369             walker = g_list_next(walker);
370             cnt++;
371         }
372     }
373     return result;
374 }
375 
extcap_free_array(gchar ** args,int argc)376 static void extcap_free_array(gchar ** args, int argc)
377 {
378     int cnt = 0;
379 
380     for ( cnt = 0; cnt < argc; cnt++ )
381         g_free(args[cnt]);
382     g_free(args);
383 }
384 
385 static void
extcap_free_extcaps_info_array(extcap_run_extcaps_info_t * infos,guint count)386 extcap_free_extcaps_info_array(extcap_run_extcaps_info_t *infos, guint count)
387 {
388     for (guint i = 0; i < count; i++) {
389         g_free(infos[i].extcap_path);
390         g_free(infos[i].output);
391         for (guint j = 0; j < infos[i].num_interfaces; j++) {
392             extcap_iface_info_t *iface_info = &infos[i].iface_infos[j];
393             g_free(iface_info->ifname);
394             g_free(iface_info->output);
395         }
396         g_free(infos[i].iface_infos);
397     }
398     g_free(infos);
399 }
400 
401 static void
extcap_run_one(const extcap_interface * interface,GList * arguments,extcap_cb_t cb,void * user_data,char ** err_str)402 extcap_run_one(const extcap_interface *interface, GList *arguments, extcap_cb_t cb, void *user_data, char **err_str)
403 {
404     const char *dirname = get_extcap_dir();
405     gchar **args = extcap_convert_arguments_to_array(arguments);
406     int cnt = g_list_length(arguments);
407     gchar *command_output;
408     if (ws_pipe_spawn_sync(dirname, interface->extcap_path, cnt, args, &command_output)) {
409         extcap_callback_info_t cb_info = {
410             .ifname = interface->call,
411             .extcap = interface->extcap_path,
412             .output = command_output,
413             .data = user_data,
414             .err_str = err_str,
415         };
416         cb(cb_info);
417         g_free(command_output);
418     }
419     extcap_free_array(args, cnt);
420 }
421 
422 /** Thread callback to run an extcap program and pass its output. */
423 static void
extcap_thread_callback(gpointer data,gpointer user_data)424 extcap_thread_callback(gpointer data, gpointer user_data)
425 {
426     extcap_run_task_t *task = (extcap_run_task_t *)data;
427     thread_pool_t *pool = (thread_pool_t *)user_data;
428     const char *dirname = get_extcap_dir();
429 
430     char *command_output;
431     if (ws_pipe_spawn_sync(dirname, task->extcap_path, g_strv_length(task->argv), task->argv, &command_output)) {
432         task->output_cb(pool, task->data, command_output);
433     } else {
434         task->output_cb(pool, task->data, NULL);
435     }
436     g_strfreev(task->argv);
437     g_free(task);
438 
439     // Notify when all tasks are completed and no new subtasks were created.
440     g_mutex_lock(&pool->data_mutex);
441     if (--pool->count == 0) {
442         g_cond_signal(&pool->cond);
443     }
444     g_mutex_unlock(&pool->data_mutex);
445 }
446 
447 /*
448  * Run all extcap programs with the given arguments list, invoke the callback to
449  * do some processing and return the results.
450  *
451  * @param [IN] argv NULL-terminated arguments list.
452  * @param [IN] output_cb Thread callback function that receives the output.
453  * @param [IN] data_size Size of the per-program information that will be returned.
454  * @param [OUT] count Size of the returned array.
455  * @return Array of information or NULL if there are none. The first member of
456  * each element (char *extcap_path) must be freed.
457  */
458 static gpointer
extcap_run_all(const char * argv[],extcap_run_cb_t output_cb,gsize data_size,guint * count)459 extcap_run_all(const char *argv[], extcap_run_cb_t output_cb, gsize data_size, guint *count)
460 {
461     /* Need enough space for at least 'extcap_path'. */
462     ws_assert(data_size >= sizeof(char *));
463 
464     GSList *paths = extcap_get_extcap_paths();
465     int i = 0;
466     int max_threads = (int)g_get_num_processors();
467 
468     if (!paths) {
469         *count = 0;
470         return NULL;
471     }
472 
473     guint64 start_time = g_get_monotonic_time();
474     guint paths_count = g_slist_length(paths);
475     /* GSList is not thread-safe, so pre-allocate an array instead. */
476     gpointer infos = g_malloc0_n(paths_count, data_size);
477 
478     thread_pool_t pool;
479     pool.pool = g_thread_pool_new(extcap_thread_callback, &pool, max_threads, FALSE, NULL);
480     pool.count = 0;
481     g_cond_init(&pool.cond);
482     g_mutex_init(&pool.data_mutex);
483 
484     for (GSList *path = paths; path; path = g_slist_next(path), i++) {
485         extcap_run_task_t *task = g_new0(extcap_run_task_t, 1);
486 
487         task->extcap_path = (char *)path->data;
488         task->argv = g_strdupv((char **)argv);
489         task->output_cb = output_cb;
490         task->data = ((char *)infos) + (i * data_size);
491         *((char **)task->data) = (char *)path->data;
492 
493         thread_pool_push(&pool, task, NULL);
494     }
495     g_slist_free(paths);    /* Note: the contents are transferred to 'infos'. */
496 
497     /* Wait for all (sub)tasks to complete. */
498     thread_pool_wait(&pool);
499 
500     g_mutex_clear(&pool.data_mutex);
501     g_cond_clear(&pool.cond);
502     g_thread_pool_free(pool.pool, FALSE, TRUE);
503 
504     ws_debug("extcap: completed discovery of %d tools in %.3fms",
505             paths_count, (g_get_monotonic_time() - start_time) / 1000.0);
506     *count = paths_count;
507     return infos;
508 }
509 
extcap_free_dlt(gpointer d,gpointer user_data _U_)510 static void extcap_free_dlt(gpointer d, gpointer user_data _U_)
511 {
512     if (d == NULL)
513     {
514         return;
515     }
516 
517     g_free(((extcap_dlt *)d)->name);
518     g_free(((extcap_dlt *)d)->display);
519     g_free(d);
520 }
521 
extcap_free_dlts(GList * dlts)522 static void extcap_free_dlts(GList *dlts)
523 {
524     g_list_foreach(dlts, extcap_free_dlt, NULL);
525     g_list_free(dlts);
526 }
527 
cb_dlt(extcap_callback_info_t cb_info)528 static gboolean cb_dlt(extcap_callback_info_t cb_info)
529 {
530     GList *dlts = NULL, *temp = NULL;
531 
532     if_capabilities_t *caps;
533     GList *linktype_list = NULL;
534     data_link_info_t *data_link_info;
535     extcap_dlt *dlt_item;
536 
537     dlts = extcap_parse_dlts(cb_info.output);
538     temp = dlts;
539 
540     ws_debug("Extcap pipe %s ", cb_info.extcap);
541 
542     /*
543      * Allocate the interface capabilities structure.
544      */
545     caps = (if_capabilities_t *) g_malloc(sizeof * caps);
546     caps->can_set_rfmon = FALSE;
547     caps->timestamp_types = NULL;
548 
549     while (dlts)
550     {
551         dlt_item = (extcap_dlt *)dlts->data;
552         if (dlt_item)
553         {
554             ws_debug("  DLT %d name=\"%s\" display=\"%s\" ", dlt_item->number,
555                   dlt_item->name, dlt_item->display);
556 
557             data_link_info = g_new(data_link_info_t, 1);
558             data_link_info->dlt = dlt_item->number;
559             data_link_info->name = g_strdup(dlt_item->name);
560             data_link_info->description = g_strdup(dlt_item->display);
561             linktype_list = g_list_append(linktype_list, data_link_info);
562         }
563 
564         dlts = g_list_next(dlts);
565     }
566 
567     /* Check to see if we built a list */
568     if (linktype_list != NULL && cb_info.data != NULL)
569     {
570         caps->data_link_types = linktype_list;
571         *(if_capabilities_t **) cb_info.data = caps;
572     }
573     else
574     {
575         if (cb_info.err_str)
576         {
577             ws_debug("  returned no DLTs");
578             *(cb_info.err_str) = g_strdup("Extcap returned no DLTs");
579         }
580         g_free(caps);
581     }
582 
583     extcap_free_dlts(temp);
584 
585     return FALSE;
586 }
587 
588 if_capabilities_t *
extcap_get_if_dlts(const gchar * ifname,char ** err_str)589 extcap_get_if_dlts(const gchar *ifname, char **err_str)
590 {
591     GList * arguments = NULL;
592     if_capabilities_t *caps = NULL;
593 
594     if (err_str != NULL)
595     {
596         *err_str = NULL;
597     }
598 
599     /* Update the extcap interfaces and get a list of their if_infos */
600     extcap_ensure_all_interfaces_loaded();
601 
602     extcap_interface *interface = extcap_find_interface_for_ifname(ifname);
603     if (interface)
604     {
605         arguments = g_list_append(arguments, g_strdup(EXTCAP_ARGUMENT_LIST_DLTS));
606         arguments = g_list_append(arguments, g_strdup(EXTCAP_ARGUMENT_INTERFACE));
607         arguments = g_list_append(arguments, g_strdup(ifname));
608 
609         extcap_run_one(interface, arguments, cb_dlt, &caps, err_str);
610 
611         g_list_free_full(arguments, g_free);
612     }
613 
614     return caps;
615 }
616 
extcap_free_interface(gpointer i)617 static void extcap_free_interface(gpointer i)
618 {
619 
620     extcap_interface *interface = (extcap_interface *)i;
621 
622     if (i == NULL)
623     {
624         return;
625     }
626 
627     g_free(interface->call);
628     g_free(interface->display);
629     g_free(interface->version);
630     g_free(interface->help);
631     g_free(interface->extcap_path);
632     g_free(interface);
633 }
634 
extcap_free_interfaces(GList * interfaces)635 static void extcap_free_interfaces(GList *interfaces)
636 {
637     if (interfaces == NULL)
638     {
639         return;
640     }
641 
642     g_list_free_full(interfaces, extcap_free_interface);
643 }
644 
645 static gint
if_info_compare(gconstpointer a,gconstpointer b)646 if_info_compare(gconstpointer a, gconstpointer b)
647 {
648     gint comp = 0;
649     const if_info_t *if_a = (const if_info_t *)a;
650     const if_info_t *if_b = (const if_info_t *)b;
651 
652     if ((comp = g_strcmp0(if_a->name, if_b->name)) == 0)
653     {
654         return g_strcmp0(if_a->friendly_name, if_b->friendly_name);
655     }
656 
657     return comp;
658 }
659 
660 gchar *
extcap_get_help_for_ifname(const char * ifname)661 extcap_get_help_for_ifname(const char *ifname)
662 {
663     extcap_ensure_all_interfaces_loaded();
664 
665     extcap_interface *interface = extcap_find_interface_for_ifname(ifname);
666     return interface != NULL ? interface->help : NULL;
667 }
668 
669 GList *
append_extcap_interface_list(GList * list,char ** err_str _U_)670 append_extcap_interface_list(GList *list, char **err_str _U_)
671 {
672     GList *interface_list = NULL;
673     extcap_interface *data = NULL;
674     GList *ifutilkeys_head = NULL, *ifutilkeys = NULL;
675 
676     if (prefs.capture_no_extcap)
677         return list;
678 
679     /* Update the extcap interfaces and get a list of their if_infos */
680     extcap_ensure_all_interfaces_loaded();
681 
682     ifutilkeys_head = g_hash_table_get_keys(_loaded_interfaces);
683     ifutilkeys = ifutilkeys_head;
684     while ( ifutilkeys && ifutilkeys->data )
685     {
686         extcap_info * extinfo =
687                 (extcap_info *) g_hash_table_lookup(_loaded_interfaces, (gchar *)ifutilkeys->data);
688         GList * walker = extinfo->interfaces;
689         while ( walker && walker->data )
690         {
691             interface_list = g_list_append(interface_list, walker->data);
692             walker = g_list_next(walker);
693         }
694 
695         ifutilkeys = g_list_next(ifutilkeys);
696     }
697     g_list_free(ifutilkeys_head);
698 
699     /* Sort that list */
700     interface_list = g_list_sort(interface_list, if_info_compare);
701 
702     /* Append the interfaces in that list to the list we're handed. */
703     while (interface_list != NULL)
704     {
705         GList *entry = g_list_first(interface_list);
706         data = (extcap_interface *)entry->data;
707         interface_list = g_list_delete_link(interface_list, entry);
708 
709         if_info_t * if_info = g_new0(if_info_t, 1);
710         if_info->name = g_strdup(data->call);
711         if_info->friendly_name = g_strdup(data->display);
712 
713         if_info->type = IF_EXTCAP;
714 
715         if_info->extcap = g_strdup(data->extcap_path);
716 
717         list = g_list_append(list, if_info);
718     }
719 
720     return list;
721 }
722 
extcap_register_preferences(void)723 void extcap_register_preferences(void)
724 {
725     if (prefs.capture_no_extcap)
726         return;
727 
728     module_t *dev_module = prefs_find_module("extcap");
729 
730     if (!dev_module)
731     {
732         return;
733     }
734 
735     // Will load information about extcaps and their supported config.
736     extcap_ensure_all_interfaces_loaded();
737 }
738 
739 /**
740  * Releases the dynamic preference value pointers. Must not be called before
741  * prefs_cleanup since these pointers could still be in use.
742  */
extcap_cleanup(void)743 void extcap_cleanup(void)
744 {
745     if (_extcap_prefs_dynamic_vals)
746         g_hash_table_destroy(_extcap_prefs_dynamic_vals);
747 
748     if (_loaded_interfaces)
749         g_hash_table_destroy(_loaded_interfaces);
750 
751     if (_tool_for_ifname)
752         g_hash_table_destroy(_tool_for_ifname);
753 }
754 
755 /**
756  * Obtains a pointer which can store a value for the given preference name.
757  * The preference name that can be passed to the prefs API is stored into
758  * 'prefs_name'.
759  *
760  * Extcap interfaces (and their preferences) are dynamic, they can be created
761  * and destroyed at will. Thus their data structures are insufficient to pass to
762  * the preferences APIs which require pointers which are valid until the
763  * preferences are removed (at exit).
764  */
extcap_prefs_dynamic_valptr(const char * name,char ** pref_name)765 static gchar **extcap_prefs_dynamic_valptr(const char *name, char **pref_name)
766 {
767     gchar **valp;
768     if (!_extcap_prefs_dynamic_vals)
769     {
770         /* Initialize table only as needed, most preferences are not dynamic */
771         _extcap_prefs_dynamic_vals = g_hash_table_new_full(g_str_hash, g_str_equal,
772                                     g_free, g_free);
773     }
774     if (!g_hash_table_lookup_extended(_extcap_prefs_dynamic_vals, name,
775                                       (gpointer *)pref_name, (gpointer *)&valp))
776     {
777         /* New dynamic pref, allocate, initialize and store. */
778         valp = g_new0(gchar *, 1);
779         *pref_name = g_strdup(name);
780         g_hash_table_insert(_extcap_prefs_dynamic_vals, *pref_name, valp);
781     }
782     return valp;
783 }
784 
extcap_free_if_configuration(GList * list,gboolean free_args)785 void extcap_free_if_configuration(GList *list, gboolean free_args)
786 {
787     GList *elem, *sl;
788 
789     for (elem = g_list_first(list); elem; elem = elem->next)
790     {
791         if (elem->data != NULL)
792         {
793             sl = g_list_first((GList *)elem->data);
794             if (free_args)
795             {
796                 extcap_free_arg_list(sl);
797             }
798             else
799             {
800                 g_list_free(sl);
801             }
802         }
803     }
804     g_list_free(list);
805 }
806 
807 struct preference *
extcap_pref_for_argument(const gchar * ifname,struct _extcap_arg * arg)808 extcap_pref_for_argument(const gchar *ifname, struct _extcap_arg *arg)
809 {
810     struct preference *pref = NULL;
811 
812     extcap_ensure_all_interfaces_loaded();
813 
814     GRegex *regex_name = g_regex_new("[-]+", G_REGEX_RAW, (GRegexMatchFlags) 0, NULL);
815     GRegex *regex_ifname = g_regex_new("(?![a-zA-Z0-9_]).", G_REGEX_RAW, (GRegexMatchFlags) 0, NULL);
816     if (regex_name && regex_ifname)
817     {
818         if (prefs_find_module("extcap"))
819         {
820             gchar *pref_name = g_regex_replace(regex_name, arg->call, strlen(arg->call), 0, "", (GRegexMatchFlags) 0, NULL);
821             gchar *ifname_underscore = g_regex_replace(regex_ifname, ifname, strlen(ifname), 0, "_", (GRegexMatchFlags) 0, NULL);
822             gchar *ifname_lowercase = g_ascii_strdown(ifname_underscore, -1);
823             gchar *pref_ifname = g_strconcat(ifname_lowercase, ".", pref_name, NULL);
824 
825             pref = prefs_find_preference(prefs_find_module("extcap"), pref_ifname);
826 
827             g_free(pref_name);
828             g_free(ifname_underscore);
829             g_free(ifname_lowercase);
830             g_free(pref_ifname);
831         }
832     }
833     if (regex_name)
834     {
835         g_regex_unref(regex_name);
836     }
837     if (regex_ifname)
838     {
839         g_regex_unref(regex_ifname);
840     }
841 
842     return pref;
843 }
844 
cb_preference(extcap_callback_info_t cb_info)845 static gboolean cb_preference(extcap_callback_info_t cb_info)
846 {
847     GList *arguments = NULL;
848     GList **il = (GList **) cb_info.data;
849     module_t *dev_module = NULL;
850 
851     arguments = extcap_parse_args(cb_info.output);
852 
853     dev_module = prefs_find_module("extcap");
854 
855     if (dev_module)
856     {
857         GList *walker = arguments;
858 
859         GRegex *regex_name = g_regex_new("[-]+", G_REGEX_RAW, (GRegexMatchFlags) 0, NULL);
860         GRegex *regex_ifname = g_regex_new("(?![a-zA-Z0-9_]).", G_REGEX_RAW, (GRegexMatchFlags) 0, NULL);
861         if (regex_name && regex_ifname)
862         {
863             while (walker != NULL)
864             {
865                 extcap_arg *arg = (extcap_arg *)walker->data;
866                 arg->device_name = g_strdup(cb_info.ifname);
867 
868                 if (arg->save)
869                 {
870                     gchar *pref_name = g_regex_replace(regex_name, arg->call, strlen(arg->call), 0, "", (GRegexMatchFlags) 0, NULL);
871                     gchar *ifname_underscore = g_regex_replace(regex_ifname, cb_info.ifname, strlen(cb_info.ifname), 0, "_", (GRegexMatchFlags) 0, NULL);
872                     gchar *ifname_lowercase = g_ascii_strdown(ifname_underscore, -1);
873                     gchar *pref_ifname = g_strconcat(ifname_lowercase, ".", pref_name, NULL);
874 
875                     if (prefs_find_preference(dev_module, pref_ifname) == NULL)
876                     {
877                         char *pref_name_for_prefs;
878                         char *pref_title = wmem_strdup(wmem_epan_scope(), arg->display);
879 
880                         arg->pref_valptr = extcap_prefs_dynamic_valptr(pref_ifname, &pref_name_for_prefs);
881                         /* Set an initial value if any (the string will be copied at registration) */
882                         if (arg->default_complex)
883                         {
884                             *arg->pref_valptr = arg->default_complex->_val;
885                         }
886 
887                         prefs_register_string_preference(dev_module, pref_name_for_prefs,
888                                                          pref_title, pref_title, (const char **)arg->pref_valptr);
889                     }
890                     else
891                     {
892                         /* Been here before, restore stored value */
893                         if (arg->pref_valptr == NULL)
894                         {
895                             arg->pref_valptr = (gchar**)g_hash_table_lookup(_extcap_prefs_dynamic_vals, pref_ifname);
896                         }
897                     }
898 
899                     g_free(pref_name);
900                     g_free(ifname_underscore);
901                     g_free(ifname_lowercase);
902                     g_free(pref_ifname);
903                 }
904 
905                 walker = g_list_next(walker);
906             }
907         }
908         if (regex_name)
909         {
910             g_regex_unref(regex_name);
911         }
912         if (regex_ifname)
913         {
914             g_regex_unref(regex_ifname);
915         }
916     }
917 
918     *il = g_list_append(*il, arguments);
919 
920     /* By returning false, extcap_foreach will break on first found */
921     return TRUE;
922 }
923 
924 GList *
extcap_get_if_configuration(const char * ifname)925 extcap_get_if_configuration(const char *ifname)
926 {
927     GList * arguments = NULL;
928     GList *ret = NULL;
929 
930     extcap_ensure_all_interfaces_loaded();
931 
932     extcap_interface *interface = extcap_find_interface_for_ifname(ifname);
933     if (interface)
934     {
935         ws_debug("Extcap path %s", get_extcap_dir());
936 
937         arguments = g_list_append(arguments, g_strdup(EXTCAP_ARGUMENT_CONFIG));
938         arguments = g_list_append(arguments, g_strdup(EXTCAP_ARGUMENT_INTERFACE));
939         arguments = g_list_append(arguments, g_strdup(ifname));
940 
941         extcap_run_one(interface, arguments, cb_preference, &ret, NULL);
942 
943         g_list_free_full(arguments, g_free);
944     }
945 
946     return ret;
947 }
948 
cb_reload_preference(extcap_callback_info_t cb_info)949 static gboolean cb_reload_preference(extcap_callback_info_t cb_info)
950 {
951     GList *arguments = NULL, * walker = NULL;
952     GList **il = (GList **) cb_info.data;
953 
954     arguments = extcap_parse_values(cb_info.output);
955 
956     walker = g_list_first(arguments);
957     while (walker != NULL)
958     {
959         extcap_value * val = (extcap_value *)walker->data;
960         *il = g_list_append(*il, val);
961         walker = g_list_next(walker);
962     }
963     g_list_free(arguments);
964 
965     /* By returning false, extcap_foreach will break on first found */
966     return FALSE;
967 }
968 
969 GList *
extcap_get_if_configuration_values(const char * ifname,const char * argname,GHashTable * arguments)970 extcap_get_if_configuration_values(const char * ifname, const char * argname, GHashTable *arguments)
971 {
972     GList * args = NULL;
973     GList *ret = NULL;
974 
975     extcap_ensure_all_interfaces_loaded();
976 
977     extcap_interface *interface = extcap_find_interface_for_ifname(ifname);
978     if (interface)
979     {
980         ws_debug("Extcap path %s", get_extcap_dir());
981 
982         args = g_list_append(args, g_strdup(EXTCAP_ARGUMENT_CONFIG));
983         args = g_list_append(args, g_strdup(EXTCAP_ARGUMENT_INTERFACE));
984         args = g_list_append(args, g_strdup(ifname));
985         args = g_list_append(args, g_strdup(EXTCAP_ARGUMENT_RELOAD_OPTION));
986         args = g_list_append(args, g_strdup(argname));
987 
988         if ( arguments )
989         {
990             GList * keys = g_hash_table_get_keys(arguments);
991             GList * walker = g_list_first(keys);
992             while ( walker )
993             {
994                 const gchar * key_data = (const gchar *)walker->data;
995                 args = g_list_append(args, g_strdup(key_data));
996                 args = g_list_append(args, g_strdup((const gchar *)g_hash_table_lookup(arguments, key_data)));
997                 walker = g_list_next(walker);
998             }
999             g_list_free(keys);
1000         }
1001 
1002         extcap_run_one(interface, args, cb_reload_preference, &ret, NULL);
1003 
1004         g_list_free_full(args, g_free);
1005     }
1006 
1007     return ret;
1008 }
1009 
1010 gboolean
extcap_has_configuration(const char * ifname,gboolean is_required)1011 extcap_has_configuration(const char *ifname, gboolean is_required)
1012 {
1013     GList *arguments = 0;
1014     GList *walker = 0, * item = 0;
1015     gboolean found = FALSE;
1016 
1017     extcap_ensure_all_interfaces_loaded();
1018 
1019     arguments = extcap_get_if_configuration(ifname);
1020     walker = g_list_first(arguments);
1021 
1022     while (walker != NULL && !found)
1023     {
1024         item = g_list_first((GList *)(walker->data));
1025         while (item != NULL && !found)
1026         {
1027             if ((extcap_arg *)(item->data) != NULL)
1028             {
1029                 extcap_arg *arg = (extcap_arg *)(item->data);
1030                 /* Should required options be present, or any kind of options */
1031                 if (!is_required)
1032                 {
1033                     found = TRUE;
1034                 }
1035                 else if (arg->is_required)
1036                 {
1037                     const gchar *stored = NULL;
1038                     const gchar *defval = NULL;
1039 
1040                     if (arg->pref_valptr != NULL)
1041                     {
1042                         stored = *arg->pref_valptr;
1043                     }
1044 
1045                     if (arg->default_complex != NULL && arg->default_complex->_val != NULL)
1046                     {
1047                         defval = arg->default_complex->_val;
1048                     }
1049 
1050                     if (arg->is_required)
1051                     {
1052                         /* If stored and defval is identical and the argument is required,
1053                          * configuration is needed */
1054                         if (defval && stored && g_strcmp0(stored, defval) == 0)
1055                         {
1056                             found = TRUE;
1057                         }
1058                         else if (!defval && (!stored || !*stored))
1059                         {
1060                             found = TRUE;
1061                         }
1062                     }
1063 
1064                     if (arg->arg_type == EXTCAP_ARG_FILESELECT)
1065                     {
1066                         if (arg->fileexists && !(file_exists(defval) || file_exists(stored)))
1067                         {
1068                             found = TRUE;
1069                         }
1070                     }
1071                 }
1072             }
1073 
1074             item = item->next;
1075         }
1076         walker = walker->next;
1077     }
1078     extcap_free_if_configuration(arguments, TRUE);
1079 
1080     return found;
1081 }
1082 
cb_verify_filter(extcap_callback_info_t cb_info)1083 static gboolean cb_verify_filter(extcap_callback_info_t cb_info)
1084 {
1085     extcap_filter_status *status = (extcap_filter_status *)cb_info.data;
1086     size_t output_size, i;
1087 
1088     output_size = strlen(cb_info.output);
1089     if (output_size == 0) {
1090         *status = EXTCAP_FILTER_VALID;
1091     } else {
1092         *status = EXTCAP_FILTER_INVALID;
1093         for (i = 0; i < output_size; i++) {
1094             if (cb_info.output[i] == '\n' || cb_info.output[i] == '\r') {
1095                 cb_info.output[i] = '\0';
1096                 break;
1097             }
1098         }
1099         *cb_info.err_str = g_strdup(cb_info.output);
1100     }
1101 
1102     return TRUE;
1103 }
1104 
1105 extcap_filter_status
extcap_verify_capture_filter(const char * ifname,const char * filter,gchar ** err_str)1106 extcap_verify_capture_filter(const char *ifname, const char *filter, gchar **err_str)
1107 {
1108     GList * arguments = NULL;
1109     extcap_filter_status status = EXTCAP_FILTER_UNKNOWN;
1110 
1111     extcap_ensure_all_interfaces_loaded();
1112 
1113     extcap_interface *interface = extcap_find_interface_for_ifname(ifname);
1114     if (interface)
1115     {
1116         ws_debug("Extcap path %s", get_extcap_dir());
1117 
1118         arguments = g_list_append(arguments, g_strdup(EXTCAP_ARGUMENT_CAPTURE_FILTER));
1119         arguments = g_list_append(arguments, g_strdup(filter));
1120         arguments = g_list_append(arguments, g_strdup(EXTCAP_ARGUMENT_INTERFACE));
1121         arguments = g_list_append(arguments, g_strdup(ifname));
1122 
1123         extcap_run_one(interface, arguments, cb_verify_filter, &status, err_str);
1124         g_list_free_full(arguments, g_free);
1125     }
1126 
1127     return status;
1128 }
1129 
1130 gboolean
extcap_has_toolbar(const char * ifname)1131 extcap_has_toolbar(const char *ifname)
1132 {
1133     if (!iface_toolbar_use())
1134     {
1135         return FALSE;
1136     }
1137 
1138     extcap_ensure_all_interfaces_loaded();
1139 
1140     GList *toolbar_list = g_hash_table_get_values (_toolbars);
1141     for (GList *walker = toolbar_list; walker; walker = walker->next)
1142     {
1143         iface_toolbar *toolbar = (iface_toolbar *) walker->data;
1144         if (g_list_find_custom(toolbar->ifnames, ifname, (GCompareFunc) g_strcmp0))
1145         {
1146             g_list_free(toolbar_list);
1147             return TRUE;
1148         }
1149     }
1150 
1151     g_list_free(toolbar_list);
1152     return FALSE;
1153 }
1154 
extcap_if_cleanup(capture_options * capture_opts,gchar ** errormsg)1155 void extcap_if_cleanup(capture_options *capture_opts, gchar **errormsg)
1156 {
1157     interface_options *interface_opts;
1158     ws_pipe_t *pipedata;
1159     guint icnt = 0;
1160     gboolean overwrite_exitcode;
1161     gchar *buffer;
1162 #define STDERR_BUFFER_SIZE 1024
1163 
1164     for (icnt = 0; icnt < capture_opts->ifaces->len; icnt++)
1165     {
1166         interface_opts = &g_array_index(capture_opts->ifaces, interface_options,
1167                                        icnt);
1168 
1169         /* skip native interfaces */
1170         if (interface_opts->if_type != IF_EXTCAP)
1171         {
1172             continue;
1173         }
1174 
1175         overwrite_exitcode = FALSE;
1176 
1177         ws_debug("Extcap [%s] - Cleaning up fifo: %s; PID: %d", interface_opts->name,
1178               interface_opts->extcap_fifo, interface_opts->extcap_pid);
1179 #ifdef _WIN32
1180         if (interface_opts->extcap_pipe_h != INVALID_HANDLE_VALUE)
1181         {
1182             ws_debug("Extcap [%s] - Closing pipe", interface_opts->name);
1183             FlushFileBuffers(interface_opts->extcap_pipe_h);
1184             DisconnectNamedPipe(interface_opts->extcap_pipe_h);
1185             CloseHandle(interface_opts->extcap_pipe_h);
1186             interface_opts->extcap_pipe_h = INVALID_HANDLE_VALUE;
1187         }
1188         if (interface_opts->extcap_control_in_h != INVALID_HANDLE_VALUE)
1189         {
1190             ws_debug("Extcap [%s] - Closing control_in pipe", interface_opts->name);
1191             FlushFileBuffers(interface_opts->extcap_control_in_h);
1192             DisconnectNamedPipe(interface_opts->extcap_control_in_h);
1193             CloseHandle(interface_opts->extcap_control_in_h);
1194             interface_opts->extcap_control_in_h = INVALID_HANDLE_VALUE;
1195         }
1196         if (interface_opts->extcap_control_out_h != INVALID_HANDLE_VALUE)
1197         {
1198             ws_debug("Extcap [%s] - Closing control_out pipe", interface_opts->name);
1199             FlushFileBuffers(interface_opts->extcap_control_out_h);
1200             DisconnectNamedPipe(interface_opts->extcap_control_out_h);
1201             CloseHandle(interface_opts->extcap_control_out_h);
1202             interface_opts->extcap_control_out_h = INVALID_HANDLE_VALUE;
1203         }
1204 #else
1205         if (interface_opts->extcap_fifo != NULL && file_exists(interface_opts->extcap_fifo))
1206         {
1207             /* the fifo will not be freed here, but with the other capture_opts in capture_sync */
1208             ws_unlink(interface_opts->extcap_fifo);
1209             interface_opts->extcap_fifo = NULL;
1210         }
1211         if (interface_opts->extcap_control_in && file_exists(interface_opts->extcap_control_in))
1212         {
1213             ws_unlink(interface_opts->extcap_control_in);
1214             interface_opts->extcap_control_in = NULL;
1215         }
1216         if (interface_opts->extcap_control_out && file_exists(interface_opts->extcap_control_out))
1217         {
1218             ws_unlink(interface_opts->extcap_control_out);
1219             interface_opts->extcap_control_out = NULL;
1220         }
1221         /* Send termination signal to child. On Linux and OSX the child will not notice that the
1222          * pipe has been closed before writing to the pipe.
1223          */
1224         if (interface_opts->extcap_pid != WS_INVALID_PID)
1225         {
1226             kill(interface_opts->extcap_pid, SIGTERM);
1227         }
1228 #endif
1229         /* Maybe the client closed and removed fifo, but ws should check if
1230          * pid should be closed */
1231         ws_debug("Extcap [%s] - Closing spawned PID: %d", interface_opts->name,
1232               interface_opts->extcap_pid);
1233 
1234         pipedata = (ws_pipe_t *) interface_opts->extcap_pipedata;
1235         if (pipedata)
1236         {
1237             if (pipedata->stderr_fd > 0)
1238             {
1239                 buffer = (gchar *)g_malloc0(STDERR_BUFFER_SIZE + 1);
1240                 ws_read_string_from_pipe(ws_get_pipe_handle(pipedata->stderr_fd), buffer, STDERR_BUFFER_SIZE + 1);
1241                 if (strlen(buffer) > 0)
1242                 {
1243                     pipedata->stderr_msg = g_strdup(buffer);
1244                     pipedata->exitcode = 1;
1245                 }
1246                 g_free(buffer);
1247             }
1248 
1249 #ifndef _WIN32
1250             /* Final child watch may not have been called */
1251             if (interface_opts->extcap_child_watch > 0)
1252             {
1253                 extcap_child_watch_cb(pipedata->pid, 0, capture_opts);
1254                 /* it will have changed in extcap_child_watch_cb */
1255                 interface_opts = &g_array_index(capture_opts->ifaces, interface_options,
1256                                                icnt);
1257             }
1258 #endif
1259 
1260             if (pipedata->stderr_msg != NULL)
1261             {
1262                 overwrite_exitcode = TRUE;
1263             }
1264 
1265             if (overwrite_exitcode || pipedata->exitcode != 0)
1266             {
1267                 if (pipedata->stderr_msg != NULL)
1268                 {
1269                     if (*errormsg == NULL)
1270                     {
1271                         *errormsg = g_strdup_printf("Error by extcap pipe: %s", pipedata->stderr_msg);
1272                     }
1273                     else
1274                     {
1275                         gchar *temp = g_strconcat(*errormsg, "\nError by extcap pipe: " , pipedata->stderr_msg, NULL);
1276                         g_free(*errormsg);
1277                         *errormsg = temp;
1278                     }
1279                     g_free(pipedata->stderr_msg);
1280                 }
1281 
1282                 pipedata->stderr_msg = NULL;
1283                 pipedata->exitcode = 0;
1284             }
1285         }
1286 
1287         if (interface_opts->extcap_child_watch > 0)
1288         {
1289             g_source_remove(interface_opts->extcap_child_watch);
1290             interface_opts->extcap_child_watch = 0;
1291         }
1292 
1293         if (pipedata) {
1294             if (pipedata->stdout_fd > 0)
1295             {
1296                 ws_close(pipedata->stdout_fd);
1297             }
1298 
1299             if (pipedata->stderr_fd > 0)
1300             {
1301                 ws_close(pipedata->stderr_fd);
1302             }
1303 
1304             if (interface_opts->extcap_pid != WS_INVALID_PID)
1305             {
1306                 ws_pipe_close(pipedata);
1307                 interface_opts->extcap_pid = WS_INVALID_PID;
1308 
1309                 g_free(pipedata);
1310                 interface_opts->extcap_pipedata = NULL;
1311             }
1312         }
1313     }
1314 }
1315 
1316 static gboolean
extcap_add_arg_and_remove_cb(gpointer key,gpointer value,gpointer data)1317 extcap_add_arg_and_remove_cb(gpointer key, gpointer value, gpointer data)
1318 {
1319     GPtrArray *args = (GPtrArray *)data;
1320 
1321     if (key != NULL)
1322     {
1323         g_ptr_array_add(args, g_strdup((const gchar *)key));
1324 
1325         if (value != NULL)
1326         {
1327             g_ptr_array_add(args, g_strdup((const gchar *)value));
1328         }
1329 
1330         return TRUE;
1331     }
1332 
1333     return FALSE;
1334 }
1335 
extcap_child_watch_cb(GPid pid,gint status,gpointer user_data)1336 void extcap_child_watch_cb(GPid pid, gint status, gpointer user_data)
1337 {
1338     guint i;
1339     interface_options *interface_opts;
1340     ws_pipe_t *pipedata = NULL;
1341     capture_options *capture_opts = (capture_options *)(user_data);
1342 
1343     if (capture_opts == NULL || capture_opts->ifaces == NULL || capture_opts->ifaces->len == 0)
1344     {
1345         return;
1346     }
1347 
1348     /* Close handle to child process. */
1349     g_spawn_close_pid(pid);
1350 
1351     /* Update extcap_pid in interface options structure. */
1352     for (i = 0; i < capture_opts->ifaces->len; i++)
1353     {
1354         interface_opts = &g_array_index(capture_opts->ifaces, interface_options, i);
1355         if (interface_opts->extcap_pid == pid)
1356         {
1357             pipedata = (ws_pipe_t *)interface_opts->extcap_pipedata;
1358             if (pipedata != NULL)
1359             {
1360                 interface_opts->extcap_pid = WS_INVALID_PID;
1361                 pipedata->exitcode = 0;
1362 #ifndef _WIN32
1363                 if (WIFEXITED(status))
1364                 {
1365                     if (WEXITSTATUS(status) != 0)
1366                     {
1367                         pipedata->exitcode = WEXITSTATUS(status);
1368                     }
1369                 }
1370                 else
1371                 {
1372                     pipedata->exitcode = G_SPAWN_ERROR_FAILED;
1373                 }
1374 #else
1375                 if (status != 0)
1376                 {
1377                     pipedata->exitcode = status;
1378                 }
1379 #endif
1380                 if (status == 0 && pipedata->stderr_msg != NULL)
1381                 {
1382                     pipedata->exitcode = 1;
1383                 }
1384             }
1385             g_source_remove(interface_opts->extcap_child_watch);
1386             interface_opts->extcap_child_watch = 0;
1387             break;
1388         }
1389     }
1390 }
1391 
1392 static
extcap_prepare_arguments(interface_options * interface_opts)1393 GPtrArray *extcap_prepare_arguments(interface_options *interface_opts)
1394 {
1395     GPtrArray *result = NULL;
1396 
1397     if (interface_opts->if_type == IF_EXTCAP)
1398     {
1399         result = g_ptr_array_new();
1400 
1401 #define add_arg(X) g_ptr_array_add(result, g_strdup(X))
1402 
1403         add_arg(interface_opts->extcap);
1404         add_arg(EXTCAP_ARGUMENT_RUN_CAPTURE);
1405         add_arg(EXTCAP_ARGUMENT_INTERFACE);
1406         add_arg(interface_opts->name);
1407         if (interface_opts->cfilter && strlen(interface_opts->cfilter) > 0)
1408         {
1409             add_arg(EXTCAP_ARGUMENT_CAPTURE_FILTER);
1410             add_arg(interface_opts->cfilter);
1411         }
1412         add_arg(EXTCAP_ARGUMENT_RUN_PIPE);
1413         add_arg(interface_opts->extcap_fifo);
1414         if (interface_opts->extcap_control_in)
1415         {
1416             add_arg(EXTCAP_ARGUMENT_CONTROL_OUT);
1417             add_arg(interface_opts->extcap_control_in);
1418         }
1419         if (interface_opts->extcap_control_out)
1420         {
1421             add_arg(EXTCAP_ARGUMENT_CONTROL_IN);
1422             add_arg(interface_opts->extcap_control_out);
1423         }
1424         if (interface_opts->extcap_args == NULL || g_hash_table_size(interface_opts->extcap_args) == 0)
1425         {
1426             /* User did not perform interface configuration.
1427              *
1428              * Check if there are any boolean flags that are set by default
1429              * and hence their argument should be added.
1430              */
1431             GList *arglist;
1432             GList *elem;
1433 
1434             arglist = extcap_get_if_configuration(interface_opts->name);
1435             for (elem = g_list_first(arglist); elem; elem = elem->next)
1436             {
1437                 GList *arg_list;
1438                 extcap_arg *arg_iter;
1439 
1440                 if (elem->data == NULL)
1441                 {
1442                     continue;
1443                 }
1444 
1445                 arg_list = g_list_first((GList *)elem->data);
1446                 while (arg_list != NULL)
1447                 {
1448                     const gchar *stored = NULL;
1449                     /* In case of boolflags only first element in arg_list is relevant. */
1450                     arg_iter = (extcap_arg *)(arg_list->data);
1451                     if (arg_iter->pref_valptr != NULL)
1452                     {
1453                         stored = *arg_iter->pref_valptr;
1454                     }
1455 
1456                     if (arg_iter->arg_type == EXTCAP_ARG_BOOLFLAG)
1457                     {
1458                         if (!stored && extcap_complex_get_bool(arg_iter->default_complex))
1459                         {
1460                             add_arg(arg_iter->call);
1461                         }
1462                         else if (g_strcmp0(stored, "true") == 0)
1463                         {
1464                             add_arg(arg_iter->call);
1465                         }
1466                     }
1467                     else
1468                     {
1469                         if (stored && strlen(stored) > 0) {
1470                             add_arg(arg_iter->call);
1471                             add_arg(stored);
1472                         }
1473                     }
1474 
1475                     arg_list = arg_list->next;
1476                 }
1477             }
1478 
1479             extcap_free_if_configuration(arglist, TRUE);
1480         }
1481         else
1482         {
1483             g_hash_table_foreach_remove(interface_opts->extcap_args, extcap_add_arg_and_remove_cb, result);
1484         }
1485         add_arg(NULL);
1486 #undef add_arg
1487 
1488     }
1489 
1490     return result;
1491 }
1492 
ptr_array_free(gpointer data,gpointer user_data _U_)1493 static void ptr_array_free(gpointer data, gpointer user_data _U_)
1494 {
1495     g_free(data);
1496 }
1497 
1498 #ifdef _WIN32
extcap_create_pipe(const gchar * ifname,gchar ** fifo,HANDLE * handle_out,const gchar * pipe_prefix)1499 static gboolean extcap_create_pipe(const gchar *ifname, gchar **fifo, HANDLE *handle_out, const gchar *pipe_prefix)
1500 {
1501     gchar timestr[ 14 + 1 ];
1502     time_t current_time;
1503     gchar *pipename = NULL;
1504     SECURITY_ATTRIBUTES security;
1505 
1506     /* create pipename */
1507     current_time = time(NULL);
1508     /*
1509      * XXX - we trust Windows not to return a time before the Epoch here,
1510      * so we won't get a null pointer back from localtime().
1511      */
1512     strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S", localtime(&current_time));
1513     pipename = g_strconcat("\\\\.\\pipe\\", pipe_prefix, "_", ifname, "_", timestr, NULL);
1514 
1515     /* Security struct to enable Inheritable HANDLE */
1516     memset(&security, 0, sizeof(SECURITY_ATTRIBUTES));
1517     security.nLength = sizeof(SECURITY_ATTRIBUTES);
1518     security.bInheritHandle = TRUE;
1519     security.lpSecurityDescriptor = NULL;
1520 
1521     /* create a namedPipe */
1522     *handle_out = CreateNamedPipe(
1523                  utf_8to16(pipename),
1524                  PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
1525                  PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1526                  1, 65536, 65536,
1527                  300,
1528                  &security);
1529 
1530     if (*handle_out == INVALID_HANDLE_VALUE)
1531     {
1532         ws_debug("Error creating pipe => (%d)", GetLastError());
1533         g_free (pipename);
1534         return FALSE;
1535     }
1536     else
1537     {
1538         ws_debug("Wireshark Created pipe =>(%s) handle (%" G_GUINTPTR_FORMAT ")", pipename, *handle_out);
1539         *fifo = g_strdup(pipename);
1540     }
1541 
1542     return TRUE;
1543 }
1544 #else
extcap_create_pipe(const gchar * ifname,gchar ** fifo,const gchar * pipe_prefix)1545 static gboolean extcap_create_pipe(const gchar *ifname, gchar **fifo, const gchar *pipe_prefix)
1546 {
1547     gchar *temp_name = NULL;
1548     int fd = 0;
1549 
1550     gchar *pfx = g_strconcat(pipe_prefix, "_", ifname, NULL);
1551     if ((fd = create_tempfile(&temp_name, pfx, NULL, NULL)) < 0)
1552     {
1553         g_free(pfx);
1554         return FALSE;
1555     }
1556     g_free(pfx);
1557 
1558     ws_close(fd);
1559 
1560     ws_debug("Extcap - Creating fifo: %s", temp_name);
1561 
1562     if (file_exists(temp_name))
1563     {
1564         ws_unlink(temp_name);
1565     }
1566 
1567     if (mkfifo(temp_name, 0600) == 0)
1568     {
1569         *fifo = temp_name;
1570     }
1571     else
1572     {
1573         g_free(temp_name);
1574     }
1575     return TRUE;
1576 }
1577 #endif
1578 
1579 /* call mkfifo for each extcap,
1580  * returns FALSE if there's an error creating a FIFO */
1581 gboolean
extcap_init_interfaces(capture_options * capture_opts)1582 extcap_init_interfaces(capture_options *capture_opts)
1583 {
1584     guint i;
1585     interface_options *interface_opts;
1586     ws_pipe_t *pipedata;
1587 
1588     extcap_ensure_all_interfaces_loaded();
1589 
1590     for (i = 0; i < capture_opts->ifaces->len; i++)
1591     {
1592         GPtrArray *args = NULL;
1593         GPid pid = WS_INVALID_PID;
1594 
1595         interface_opts = &g_array_index(capture_opts->ifaces, interface_options, i);
1596 
1597         /* skip native interfaces */
1598         if (interface_opts->if_type != IF_EXTCAP)
1599         {
1600             continue;
1601         }
1602 
1603         /* create control pipes if having toolbar */
1604         if (extcap_has_toolbar(interface_opts->name))
1605         {
1606             extcap_create_pipe(interface_opts->name, &interface_opts->extcap_control_in,
1607 #ifdef _WIN32
1608                                &interface_opts->extcap_control_in_h,
1609 #endif
1610                                EXTCAP_CONTROL_IN_PREFIX);
1611             extcap_create_pipe(interface_opts->name, &interface_opts->extcap_control_out,
1612 #ifdef _WIN32
1613                                &interface_opts->extcap_control_out_h,
1614 #endif
1615                                EXTCAP_CONTROL_OUT_PREFIX);
1616         }
1617 
1618         /* create pipe for fifo */
1619         if (!extcap_create_pipe(interface_opts->name, &interface_opts->extcap_fifo,
1620 #ifdef _WIN32
1621                                 &interface_opts->extcap_pipe_h,
1622 #endif
1623                                 EXTCAP_PIPE_PREFIX))
1624         {
1625             return FALSE;
1626         }
1627 
1628 
1629         /* Create extcap call */
1630         args = extcap_prepare_arguments(interface_opts);
1631 
1632         pipedata = g_new0(ws_pipe_t, 1);
1633 
1634         pid = ws_pipe_spawn_async(pipedata, args);
1635 
1636         g_ptr_array_foreach(args, ptr_array_free, NULL);
1637         g_ptr_array_free(args, TRUE);
1638 
1639         if (pid == WS_INVALID_PID)
1640         {
1641             g_free(pipedata);
1642             continue;
1643         }
1644 
1645         ws_close(pipedata->stdin_fd);
1646         interface_opts->extcap_pid = pid;
1647 
1648         interface_opts->extcap_child_watch =
1649             g_child_watch_add(pid, extcap_child_watch_cb, (gpointer)capture_opts);
1650 
1651 #ifdef _WIN32
1652         /* On Windows, wait for extcap to connect to named pipe.
1653          * Some extcaps will present UAC screen to user.
1654          * 30 second timeout should be reasonable timeout for extcap to
1655          * connect to named pipe (including user interaction).
1656          * Wait on multiple object in case of extcap termination
1657          * without opening pipe.
1658          */
1659         if (pid != WS_INVALID_PID)
1660         {
1661             HANDLE pipe_handles[3];
1662             int num_pipe_handles = 1;
1663             pipe_handles[0] = interface_opts->extcap_pipe_h;
1664 
1665             if (extcap_has_toolbar(interface_opts->name))
1666             {
1667                 pipe_handles[1] = interface_opts->extcap_control_in_h;
1668                 pipe_handles[2] = interface_opts->extcap_control_out_h;
1669                 num_pipe_handles += 2;
1670              }
1671 
1672             ws_pipe_wait_for_pipe(pipe_handles, num_pipe_handles, pid);
1673         }
1674 #endif
1675 
1676         interface_opts->extcap_pipedata = (gpointer) pipedata;
1677     }
1678 
1679     return TRUE;
1680 }
1681 
1682 /************* EXTCAP LOAD INTERFACE LIST ***************
1683  *
1684  * The following code handles loading and reloading the interface list. It is explicitly
1685  * kept separate from the rest
1686  */
1687 
1688 
1689 static void
extcap_free_interface_info(gpointer data)1690 extcap_free_interface_info(gpointer data)
1691 {
1692     extcap_info *info = (extcap_info *)data;
1693 
1694     g_free(info->basename);
1695     g_free(info->full_path);
1696     g_free(info->version);
1697     g_free(info->help);
1698 
1699     extcap_free_interfaces(info->interfaces);
1700 
1701     g_free(info);
1702 }
1703 
1704 static extcap_info *
extcap_ensure_interface(const gchar * toolname,gboolean create_if_nonexist)1705 extcap_ensure_interface(const gchar * toolname, gboolean create_if_nonexist)
1706 {
1707     extcap_info * element = 0;
1708 
1709     if ( prefs.capture_no_extcap )
1710         return NULL;
1711 
1712     if ( ! toolname )
1713         return element;
1714 
1715     if ( ! _loaded_interfaces )
1716         _loaded_interfaces = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, extcap_free_interface);
1717 
1718     element = (extcap_info *) g_hash_table_lookup(_loaded_interfaces, toolname );
1719     if ( element )
1720         return NULL;
1721 
1722     if ( ! element && create_if_nonexist )
1723     {
1724         g_hash_table_insert(_loaded_interfaces, g_strdup(toolname), g_new0(extcap_info, 1));
1725         element = (extcap_info *) g_hash_table_lookup(_loaded_interfaces, toolname );
1726     }
1727 
1728     return element;
1729 }
1730 
1731 extcap_info *
extcap_get_tool_by_ifname(const gchar * ifname)1732 extcap_get_tool_by_ifname(const gchar *ifname)
1733 {
1734     extcap_ensure_all_interfaces_loaded();
1735 
1736     if ( ifname && _tool_for_ifname )
1737     {
1738         gchar * toolname = (gchar *)g_hash_table_lookup(_tool_for_ifname, ifname);
1739         if ( toolname )
1740             return extcap_ensure_interface(toolname, FALSE);
1741     }
1742 
1743     return NULL;
1744 }
1745 
1746 extcap_info *
extcap_get_tool_info(const gchar * toolname)1747 extcap_get_tool_info(const gchar * toolname)
1748 {
1749     extcap_ensure_all_interfaces_loaded();
1750 
1751     return extcap_ensure_interface(toolname, FALSE);
1752 }
1753 
remove_extcap_entry(gpointer entry,gpointer data _U_)1754 static void remove_extcap_entry(gpointer entry, gpointer data _U_)
1755 {
1756     extcap_interface *int_iter = (extcap_interface*)entry;
1757 
1758     if (int_iter->if_type == EXTCAP_SENTENCE_EXTCAP)
1759         extcap_free_interface(entry);
1760 }
1761 
1762 static void
process_new_extcap(const char * extcap,char * output)1763 process_new_extcap(const char *extcap, char *output)
1764 {
1765     GList * interfaces = NULL, * control_items = NULL, * walker = NULL;
1766     extcap_interface * int_iter = NULL;
1767     extcap_info * element = NULL;
1768     iface_toolbar * toolbar_entry = NULL;
1769     gchar * toolname = g_path_get_basename(extcap);
1770 
1771     GList * interface_keys = g_hash_table_get_keys(_loaded_interfaces);
1772 
1773     /* Load interfaces from utility */
1774     interfaces = extcap_parse_interfaces(output, &control_items);
1775 
1776     ws_debug("Loading interface list for %s ", extcap);
1777 
1778     /* Seems, that there where no interfaces to be loaded */
1779     if ( ! interfaces || g_list_length(interfaces) == 0 )
1780     {
1781         ws_debug("Cannot load interfaces for %s", extcap );
1782         g_list_free(interface_keys);
1783         g_free(toolname);
1784         return;
1785     }
1786 
1787     /* Load or create the storage element for the tool */
1788     element = extcap_ensure_interface(toolname, TRUE);
1789     if ( element == NULL )
1790     {
1791         ws_warning("Cannot store interface %s, already loaded as personal plugin", extcap );
1792         g_list_foreach(interfaces, remove_extcap_entry, NULL);
1793         g_list_free(interfaces);
1794         g_list_free(interface_keys);
1795         g_free(toolname);
1796         return;
1797     }
1798 
1799     if (control_items)
1800     {
1801         toolbar_entry = g_new0(iface_toolbar, 1);
1802         toolbar_entry->controls = control_items;
1803     }
1804 
1805     walker = interfaces;
1806     gchar* help = NULL;
1807     while (walker != NULL)
1808     {
1809         int_iter = (extcap_interface *)walker->data;
1810 
1811         if (int_iter->call != NULL)
1812             ws_debug("Interface found %s\n", int_iter->call);
1813 
1814         /* Help is not necessarily stored with the interface, but rather with the version string.
1815          * As the version string allways comes in front of the interfaces, this ensures, that it get's
1816          * properly stored with the interface */
1817         if (int_iter->if_type == EXTCAP_SENTENCE_EXTCAP)
1818         {
1819             if (int_iter->call != NULL)
1820                 ws_debug("  Extcap [%s] ", int_iter->call);
1821 
1822             /* Only initialize values if none are set. Need to check only one element here */
1823             if ( ! element->version )
1824             {
1825                 element->version = g_strdup(int_iter->version);
1826                 element->basename = g_strdup(toolname);
1827                 element->full_path = g_strdup(extcap);
1828                 element->help = g_strdup(int_iter->help);
1829             }
1830 
1831             help = int_iter->help;
1832             if (toolbar_entry)
1833             {
1834                 toolbar_entry->menu_title = g_strdup(int_iter->display);
1835                 toolbar_entry->help = g_strdup(int_iter->help);
1836             }
1837 
1838             walker = g_list_next(walker);
1839             continue;
1840         }
1841 
1842         /* Only interface definitions will be parsed here. help is already set by the extcap element,
1843          * which makes it necessary to have version in the list before the interfaces. This is normally
1844          * the case by design, but could be changed by separating the information in extcap-base. */
1845         if ( int_iter->if_type == EXTCAP_SENTENCE_INTERFACE )
1846         {
1847             if ( g_list_find(interface_keys, int_iter->call) )
1848             {
1849                 ws_warning("Extcap interface \"%s\" is already provided by \"%s\" ",
1850                       int_iter->call, extcap_if_executable(int_iter->call));
1851                 walker = g_list_next(walker);
1852                 continue;
1853             }
1854 
1855             if ((int_iter->call != NULL) && (int_iter->display))
1856                 ws_debug("  Interface [%s] \"%s\" ", int_iter->call, int_iter->display);
1857 
1858             int_iter->extcap_path = g_strdup(extcap);
1859 
1860             /* Only set the help, if it exists and no parsed help information is present */
1861             if ( ! int_iter->help && help )
1862                 int_iter->help = g_strdup(help);
1863 
1864             element->interfaces = g_list_append(element->interfaces, int_iter);
1865             g_hash_table_insert(_tool_for_ifname, g_strdup(int_iter->call), g_strdup(toolname));
1866 
1867             if (toolbar_entry)
1868             {
1869                 if (!toolbar_entry->menu_title)
1870                 {
1871                     toolbar_entry->menu_title = g_strdup(int_iter->display);
1872                 }
1873                 toolbar_entry->ifnames = g_list_append(toolbar_entry->ifnames, g_strdup(int_iter->call));
1874             }
1875         }
1876 
1877         walker = g_list_next(walker);
1878     }
1879 
1880     if (toolbar_entry && toolbar_entry->menu_title)
1881     {
1882         iface_toolbar_add(toolbar_entry);
1883         if (extcap_iface_toolbar_add(extcap, toolbar_entry))
1884         {
1885             toolbar_entry = NULL;
1886         }
1887     }
1888 
1889     extcap_free_toolbar(toolbar_entry);
1890     g_list_foreach(interfaces, remove_extcap_entry, NULL);
1891     g_list_free(interfaces);
1892     g_list_free(interface_keys);
1893     g_free(toolname);
1894 }
1895 
1896 
1897 /** Thread callback to save the output of a --extcap-config call. */
1898 static void
extcap_process_config_cb(thread_pool_t * pool _U_,void * data,char * output)1899 extcap_process_config_cb(thread_pool_t *pool _U_, void *data, char *output)
1900 {
1901     extcap_iface_info_t *iface_info = (extcap_iface_info_t *)data;
1902     iface_info->output = output;
1903 }
1904 
1905 /**
1906  * Thread callback to process discovered interfaces, scheduling more tasks to
1907  * retrieve the configuration for each interface. Called once for every extcap
1908  * program.
1909  */
1910 static void
extcap_process_interfaces_cb(thread_pool_t * pool,void * data,char * output)1911 extcap_process_interfaces_cb(thread_pool_t *pool, void *data, char *output)
1912 {
1913     extcap_run_extcaps_info_t *info = (extcap_run_extcaps_info_t *)data;
1914     guint i = 0;
1915     guint num_interfaces = 0;
1916 
1917     if (!output) {
1918         // No interfaces available, nothing to do.
1919         return;
1920     }
1921 
1922     // Save output for process_new_extcap.
1923     info->output = output;
1924 
1925     // Are there any interfaces to query information from?
1926     GList *interfaces = extcap_parse_interfaces(output, NULL);
1927     for (GList *iface = interfaces; iface; iface = g_list_next(iface)) {
1928         extcap_interface *intf = (extcap_interface *)iface->data;
1929         if (intf->if_type == EXTCAP_SENTENCE_INTERFACE) {
1930             ++num_interfaces;
1931         }
1932     }
1933     if (num_interfaces == 0) {
1934         // nothing to do.
1935         g_list_free_full(interfaces, extcap_free_interface);
1936         return;
1937     }
1938 
1939     /* GSList is not thread-safe, so pre-allocate an array instead. */
1940     info->iface_infos = g_new0(extcap_iface_info_t, num_interfaces);
1941     info->num_interfaces = num_interfaces;
1942 
1943     // Schedule new commands to retrieve the configuration.
1944     for (GList *iface = interfaces; iface; iface = g_list_next(iface)) {
1945         extcap_interface *intf = (extcap_interface *)iface->data;
1946         if (intf->if_type != EXTCAP_SENTENCE_INTERFACE) {
1947             continue;
1948         }
1949 
1950         const char *argv[] = {
1951             EXTCAP_ARGUMENT_CONFIG,
1952             EXTCAP_ARGUMENT_INTERFACE,
1953             intf->call,
1954             NULL
1955         };
1956         extcap_run_task_t *task = g_new0(extcap_run_task_t, 1);
1957         extcap_iface_info_t *iface_info = &info->iface_infos[i++];
1958 
1959         task->extcap_path = info->extcap_path;
1960         task->argv = g_strdupv((char **)argv);
1961         task->output_cb = extcap_process_config_cb;
1962         task->data = iface_info;
1963         iface_info->ifname = g_strdup(intf->call);
1964 
1965         thread_pool_push(pool, task, NULL);
1966     }
1967     g_list_free_full(interfaces, extcap_free_interface);
1968 }
1969 
1970 /**
1971  * Thread callback to check whether the new-style --list-interfaces call with an
1972  * explicit function succeeded. If not, schedule a call without the new version
1973  * argument.
1974  */
1975 static void
extcap_list_interfaces_cb(thread_pool_t * pool,void * data,char * output)1976 extcap_list_interfaces_cb(thread_pool_t *pool, void *data, char *output)
1977 {
1978     extcap_run_extcaps_info_t *info = (extcap_run_extcaps_info_t *)data;
1979 
1980     if (!output) {
1981         /* No output available, schedule a fallback query. */
1982         const char *argv[] = {
1983             EXTCAP_ARGUMENT_LIST_INTERFACES,
1984             NULL
1985         };
1986         extcap_run_task_t *task = g_new0(extcap_run_task_t, 1);
1987 
1988         task->extcap_path = info->extcap_path;
1989         task->argv = g_strdupv((char **)argv);
1990         task->output_cb = extcap_process_interfaces_cb;
1991         task->data = info;
1992 
1993         thread_pool_push(pool, task, NULL);
1994     } else {
1995         extcap_process_interfaces_cb(pool, info, output);
1996     }
1997 }
1998 
1999 
2000 /* Handles loading of the interfaces. */
2001 static void
extcap_load_interface_list(void)2002 extcap_load_interface_list(void)
2003 {
2004     if (prefs.capture_no_extcap)
2005         return;
2006 
2007     if (_toolbars)
2008     {
2009         // Remove existing interface toolbars here instead of in extcap_clear_interfaces()
2010         // to avoid flicker in shown toolbars when refreshing interfaces.
2011         GList *toolbar_list = g_hash_table_get_values (_toolbars);
2012         for (GList *walker = toolbar_list; walker; walker = walker->next)
2013         {
2014             iface_toolbar *toolbar = (iface_toolbar *) walker->data;
2015             iface_toolbar_remove(toolbar->menu_title);
2016         }
2017         g_list_free(toolbar_list);
2018         g_hash_table_remove_all(_toolbars);
2019     } else {
2020         _toolbars = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, extcap_free_toolbar);
2021     }
2022 
2023     if (_loaded_interfaces == NULL)
2024     {
2025         int major = 0;
2026         int minor = 0;
2027         guint count = 0;
2028         extcap_run_extcaps_info_t *infos;
2029         GList *unused_arguments = NULL;
2030 
2031         _loaded_interfaces = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, extcap_free_interface_info);
2032         /* Cleanup lookup table */
2033         if ( _tool_for_ifname )
2034         {
2035             g_hash_table_remove_all(_tool_for_ifname);
2036             _tool_for_ifname = 0;
2037         } else {
2038             _tool_for_ifname = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
2039         }
2040 
2041         get_ws_version_number(&major, &minor, NULL);
2042         char *arg_version = g_strdup_printf("%s=%d.%d", EXTCAP_ARGUMENT_VERSION, major, minor);
2043         const char *argv[] = {
2044             EXTCAP_ARGUMENT_LIST_INTERFACES,
2045             arg_version,
2046             NULL
2047         };
2048         infos = (extcap_run_extcaps_info_t *)extcap_run_all(argv,
2049                 extcap_list_interfaces_cb, sizeof(extcap_run_extcaps_info_t),
2050                 &count);
2051         for (guint i = 0; i < count; i++) {
2052             if (!infos[i].output) {
2053                 continue;
2054             }
2055 
2056             // Save new extcap and each discovered interface.
2057             process_new_extcap(infos[i].extcap_path, infos[i].output);
2058             for (guint j = 0; j < infos[i].num_interfaces; j++) {
2059                 extcap_iface_info_t *iface_info = &infos[i].iface_infos[j];
2060 
2061                 if (!iface_info->output) {
2062                     continue;
2063                 }
2064 
2065                 extcap_callback_info_t cb_info = {
2066                     .ifname = iface_info->ifname,
2067                     .output = iface_info->output,
2068                     .data = &unused_arguments,
2069                 };
2070                 cb_preference(cb_info);
2071             }
2072         }
2073         /* XXX rework cb_preference such that this unused list can be removed. */
2074         extcap_free_if_configuration(unused_arguments, TRUE);
2075         extcap_free_extcaps_info_array(infos, count);
2076         g_free(arg_version);
2077     }
2078 }
2079 
2080 /*
2081  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
2082  *
2083  * Local variables:
2084  * c-basic-offset: 4
2085  * tab-width: 8
2086  * indent-tabs-mode: nil
2087  * End:
2088  *
2089  * vi: set shiftwidth=4 tabstop=8 expandtab:
2090  * :indentSize=4:tabSize=8:noTabs=true:
2091  */
2092