4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
12 #define WS_LOG_DOMAIN LOG_DOMAIN_PLUGINS
23 #include <wsutil/filesystem.h>
24 #include <wsutil/privileges.h>
25 #include <wsutil/file_util.h>
26 #include <wsutil/report_message.h>
27 #include <wsutil/wslog.h>
29 typedef struct _plugin
{
30 GModule
*handle
; /* handle returned by g_module_open */
31 char *name
; /* plugin name */
32 const char *version
; /* plugin version */
33 uint32_t flags
; /* plugin flags */
36 #define TYPE_DIR_EPAN "epan"
37 #define TYPE_DIR_WIRETAP "wiretap"
38 #define TYPE_DIR_CODECS "codecs"
40 static GSList
*plugins_module_list
;
43 static inline const char *
44 type_to_dir(plugin_type_e type
)
49 case WS_PLUGIN_WIRETAP
:
50 return TYPE_DIR_WIRETAP
;
52 return TYPE_DIR_CODECS
;
54 ws_error("Unknown plugin type: %u. Aborting.", (unsigned) type
);
57 ws_assert_not_reached();
60 static inline const char *
61 flags_to_str(uint32_t flags
)
63 /* XXX: Allow joining multiple types? Our plugins only implement a
64 * single type but out in the wild this may not be true. */
65 if (flags
& WS_PLUGIN_DESC_DISSECTOR
)
67 else if (flags
& WS_PLUGIN_DESC_FILE_TYPE
)
69 else if (flags
& WS_PLUGIN_DESC_CODEC
)
71 else if (flags
& WS_PLUGIN_DESC_EPAN
)
73 else if (flags
& WS_PLUGIN_DESC_TAP_LISTENER
)
74 return "tap listener";
75 else if (flags
& WS_PLUGIN_DESC_DFILTER
)
82 free_plugin(void * data
)
84 plugin
*p
= (plugin
*)data
;
85 g_module_close(p
->handle
);
91 compare_plugins(const void *a
, const void *b
)
93 return g_strcmp0((*(plugin
*const *)a
)->name
, (*(plugin
*const *)b
)->name
);
97 pass_plugin_version_compatibility(GModule
*handle
, const char *name
)
102 if(!g_module_symbol(handle
, "plugin_want_major", &symb
)) {
103 report_failure("The plugin '%s' has no \"plugin_want_major\" symbol", name
);
106 major
= *(int *)symb
;
108 if(!g_module_symbol(handle
, "plugin_want_minor", &symb
)) {
109 report_failure("The plugin '%s' has no \"plugin_want_minor\" symbol", name
);
112 minor
= *(int *)symb
;
114 if (major
!= VERSION_MAJOR
|| minor
!= VERSION_MINOR
) {
115 report_failure("The plugin '%s' was compiled for Wireshark version %d.%d",
123 // GLib and Qt allow ".dylib" and ".so" on macOS. Should we do the same?
125 #define MODULE_SUFFIX ".dll"
127 #define MODULE_SUFFIX ".so"
131 scan_plugins_dir(GHashTable
*plugins_module
, const char *dirpath
, plugin_type_e type
, bool append_type
)
134 const char *name
; /* current file name */
136 char *plugin_file
; /* current file full path */
137 GModule
*handle
; /* handle returned by g_module_open */
139 const char *plug_version
;
144 plugin_folder
= g_build_filename(dirpath
, type_to_dir(type
), (char *)NULL
);
146 plugin_folder
= g_strdup(dirpath
);
148 dir
= g_dir_open(plugin_folder
, 0, NULL
);
150 g_free(plugin_folder
);
154 ws_debug("Scanning plugins folder \"%s\"", plugin_folder
);
156 while ((name
= g_dir_read_name(dir
)) != NULL
) {
157 /* Skip anything but files with .dll or .so. */
158 if (!g_str_has_suffix(name
, MODULE_SUFFIX
))
162 * Check if the same name is already registered.
164 if (g_hash_table_lookup(plugins_module
, name
)) {
166 report_warning("The plugin '%s' was found "
167 "in multiple directories", name
);
171 plugin_file
= g_build_filename(plugin_folder
, name
, (char *)NULL
);
172 handle
= g_module_open(plugin_file
, G_MODULE_BIND_LOCAL
);
173 if (handle
== NULL
) {
174 /* g_module_error() provides file path. */
175 report_failure("Couldn't load plugin '%s': %s", name
,
181 if (!g_module_symbol(handle
, "plugin_version", &symbol
))
183 report_failure("The plugin '%s' has no \"plugin_version\" symbol", name
);
184 g_module_close(handle
);
188 plug_version
= (const char *)symbol
;
190 if (!pass_plugin_version_compatibility(handle
, name
)) {
191 g_module_close(handle
);
196 /* Search for the entry point for the plugin registration function */
197 if (!g_module_symbol(handle
, "plugin_register", &symbol
)) {
198 report_failure("The plugin '%s' has no \"plugin_register\" symbol", name
);
199 g_module_close(handle
);
205 /* Found it, call the plugin registration function. */
206 ((plugin_register_func
)symbol
)();
209 /* Search for the (optional) description flag registration function */
210 if (g_module_symbol(handle
, "plugin_describe", &symbol
))
211 flags
= ((plugin_describe_func
)symbol
)();
215 new_plug
= g_new(plugin
, 1);
216 new_plug
->handle
= handle
;
217 new_plug
->name
= g_strdup(name
);
218 new_plug
->version
= plug_version
;
219 new_plug
->flags
= flags
;
221 /* Add it to the list of plugins. */
222 g_hash_table_replace(plugins_module
, new_plug
->name
, new_plug
);
223 ws_info("Registered plugin: %s (%s)", new_plug
->name
, plugin_file
);
227 g_free(plugin_folder
);
234 plugins_init(plugin_type_e type
)
236 if (!g_module_supported())
237 return NULL
; /* nothing to do */
239 GHashTable
*plugins_module
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
, free_plugin
);
242 * Scan the global plugin directory.
244 scan_plugins_dir(plugins_module
, get_plugins_dir_with_version(), type
, true);
247 * If the program wasn't started with special privileges,
248 * scan the users plugin directory. (Even if we relinquish
249 * them, plugins aren't safe unless we've *permanently*
250 * relinquished them, and we can't do that in Wireshark as,
251 * if we need privileges to start capturing, we'd need to
252 * reclaim them before each time we start capturing.)
254 if (!started_with_special_privs()) {
255 scan_plugins_dir(plugins_module
, get_plugins_pers_dir_with_version(), type
, true);
258 plugins_module_list
= g_slist_prepend(plugins_module_list
, plugins_module
);
260 return plugins_module
;
264 plugins_get_descriptions(plugin_description_callback callback
, void *callback_data
)
266 GPtrArray
*plugins_array
= g_ptr_array_new();
270 for (GSList
*l
= plugins_module_list
; l
!= NULL
; l
= l
->next
) {
271 g_hash_table_iter_init (&iter
, (GHashTable
*)l
->data
);
272 while (g_hash_table_iter_next (&iter
, NULL
, &value
)) {
273 g_ptr_array_add(plugins_array
, value
);
277 g_ptr_array_sort(plugins_array
, compare_plugins
);
279 for (unsigned i
= 0; i
< plugins_array
->len
; i
++) {
280 plugin
*plug
= (plugin
*)plugins_array
->pdata
[i
];
281 callback(plug
->name
, plug
->version
, plug
->flags
, g_module_name(plug
->handle
), callback_data
);
284 g_ptr_array_free(plugins_array
, true);
288 print_plugin_description(const char *name
, const char *version
,
289 uint32_t flags
, const char *filename
,
292 printf("%-16s\t%s\t%s\t%s\n", name
, version
, flags_to_str(flags
), filename
);
296 plugins_dump_all(void)
298 plugins_get_descriptions(print_plugin_description
, NULL
);
302 plugins_get_count(void)
306 for (GSList
*l
= plugins_module_list
; l
!= NULL
; l
= l
->next
) {
307 count
+= g_hash_table_size((GHashTable
*)l
->data
);
313 plugins_cleanup(plugins_t
*plugins
)
318 plugins_module_list
= g_slist_remove(plugins_module_list
, plugins
);
319 g_hash_table_destroy((GHashTable
*)plugins
);
323 plugins_supported(void)
325 return g_module_supported();
334 * indent-tabs-mode: nil
337 * ex: set shiftwidth=4 tabstop=8 expandtab:
338 * :indentSize=4:tabSize=8:noTabs=true: