1 /* plugins.c
2 * plugin routines
3 *
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "config.h"
12 #define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
13
14 #include <time.h>
15
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <errno.h>
20
21 #include <glib.h>
22 #include <gmodule.h>
23
24 #include <wsutil/filesystem.h>
25 #include <wsutil/privileges.h>
26 #include <wsutil/file_util.h>
27 #include <wsutil/report_message.h>
28 #include <wsutil/wslog.h>
29
30 #include <wsutil/plugins.h>
31 #include <wsutil/ws_assert.h>
32
33 typedef struct _plugin {
34 GModule *handle; /* handle returned by g_module_open */
35 gchar *name; /* plugin name */
36 const gchar *version; /* plugin version */
37 const gchar *type_name; /* user-facing name (what it does). Should these be capitalized? */
38 } plugin;
39
40 #define TYPE_DIR_EPAN "epan"
41 #define TYPE_DIR_WIRETAP "wiretap"
42 #define TYPE_DIR_CODECS "codecs"
43
44 #define TYPE_NAME_DISSECTOR "dissector"
45 #define TYPE_NAME_FILE_TYPE "file type"
46 #define TYPE_NAME_CODEC "codec"
47
48
49 static GSList *plugins_module_list = NULL;
50
51
52 static inline const char *
type_to_dir(plugin_type_e type)53 type_to_dir(plugin_type_e type)
54 {
55 switch (type) {
56 case WS_PLUGIN_EPAN:
57 return TYPE_DIR_EPAN;
58 case WS_PLUGIN_WIRETAP:
59 return TYPE_DIR_WIRETAP;
60 case WS_PLUGIN_CODEC:
61 return TYPE_DIR_CODECS;
62 default:
63 ws_error("Unknown plugin type: %u. Aborting.", (unsigned) type);
64 break;
65 }
66 ws_assert_not_reached();
67 }
68
69 static inline const char *
type_to_name(plugin_type_e type)70 type_to_name(plugin_type_e type)
71 {
72 switch (type) {
73 case WS_PLUGIN_EPAN:
74 return TYPE_NAME_DISSECTOR;
75 case WS_PLUGIN_WIRETAP:
76 return TYPE_NAME_FILE_TYPE;
77 case WS_PLUGIN_CODEC:
78 return TYPE_NAME_CODEC;
79 default:
80 ws_error("Unknown plugin type: %u. Aborting.", (unsigned) type);
81 break;
82 }
83 ws_assert_not_reached();
84 }
85
86 static void
free_plugin(gpointer data)87 free_plugin(gpointer data)
88 {
89 plugin *p = (plugin *)data;
90 g_module_close(p->handle);
91 g_free(p->name);
92 g_free(p);
93 }
94
95 static gint
compare_plugins(gconstpointer a,gconstpointer b)96 compare_plugins(gconstpointer a, gconstpointer b)
97 {
98 return g_strcmp0((*(plugin *const *)a)->name, (*(plugin *const *)b)->name);
99 }
100
101 static gboolean
pass_plugin_version_compatibility(GModule * handle,const char * name)102 pass_plugin_version_compatibility(GModule *handle, const char *name)
103 {
104 gpointer symb;
105 int major, minor;
106
107 if(!g_module_symbol(handle, "plugin_want_major", &symb)) {
108 report_failure("The plugin '%s' has no \"plugin_want_major\" symbol", name);
109 return FALSE;
110 }
111 major = *(int *)symb;
112
113 if(!g_module_symbol(handle, "plugin_want_minor", &symb)) {
114 report_failure("The plugin '%s' has no \"plugin_want_minor\" symbol", name);
115 return FALSE;
116 }
117 minor = *(int *)symb;
118
119 if (major != VERSION_MAJOR || minor != VERSION_MINOR) {
120 report_failure("The plugin '%s' was compiled for Wireshark version %d.%d",
121 name, major, minor);
122 return FALSE;
123 }
124
125 return TRUE;
126 }
127
128 static void
scan_plugins_dir(GHashTable * plugins_module,const char * dirpath,plugin_type_e type,gboolean append_type)129 scan_plugins_dir(GHashTable *plugins_module, const char *dirpath, plugin_type_e type, gboolean append_type)
130 {
131 GDir *dir;
132 const char *name; /* current file name */
133 gchar *plugin_folder;
134 gchar *plugin_file; /* current file full path */
135 GModule *handle; /* handle returned by g_module_open */
136 gpointer symbol;
137 const char *plug_version;
138 plugin *new_plug;
139
140 if (append_type)
141 plugin_folder = g_build_filename(dirpath, type_to_dir(type), (gchar *)NULL);
142 else
143 plugin_folder = g_strdup(dirpath);
144
145 dir = g_dir_open(plugin_folder, 0, NULL);
146 if (dir == NULL) {
147 g_free(plugin_folder);
148 return;
149 }
150
151 while ((name = g_dir_read_name(dir)) != NULL) {
152 /* Skip anything but files with G_MODULE_SUFFIX. */
153 if (!g_str_has_suffix(name, "." G_MODULE_SUFFIX))
154 continue;
155
156 /*
157 * Check if the same name is already registered.
158 */
159 if (g_hash_table_lookup(plugins_module, name)) {
160 /* Yes, it is. */
161 report_warning("The plugin '%s' was found "
162 "in multiple directories", name);
163 continue;
164 }
165
166 plugin_file = g_build_filename(plugin_folder, name, (gchar *)NULL);
167 handle = g_module_open(plugin_file, G_MODULE_BIND_LOCAL);
168 g_free(plugin_file);
169 if (handle == NULL) {
170 /* g_module_error() provides file path. */
171 report_failure("Couldn't load plugin '%s': %s", name,
172 g_module_error());
173 continue;
174 }
175
176 if (!g_module_symbol(handle, "plugin_version", &symbol))
177 {
178 report_failure("The plugin '%s' has no \"plugin_version\" symbol", name);
179 g_module_close(handle);
180 continue;
181 }
182 plug_version = (const char *)symbol;
183
184 if (!pass_plugin_version_compatibility(handle, name)) {
185 g_module_close(handle);
186 continue;
187 }
188
189 /* Search for the entry point for the plugin registration function */
190 if (!g_module_symbol(handle, "plugin_register", &symbol)) {
191 report_failure("The plugin '%s' has no \"plugin_register\" symbol", name);
192 g_module_close(handle);
193 continue;
194 }
195
196 DIAG_OFF_PEDANTIC
197 /* Found it, call the plugin registration function. */
198 ((plugin_register_func)symbol)();
199 DIAG_ON_PEDANTIC
200
201 new_plug = g_new(plugin, 1);
202 new_plug->handle = handle;
203 new_plug->name = g_strdup(name);
204 new_plug->version = plug_version;
205 new_plug->type_name = type_to_name(type);
206
207 /* Add it to the list of plugins. */
208 g_hash_table_replace(plugins_module, new_plug->name, new_plug);
209 }
210 ws_dir_close(dir);
211 g_free(plugin_folder);
212 }
213
214 /*
215 * Scan for plugins.
216 */
217 plugins_t *
plugins_init(plugin_type_e type)218 plugins_init(plugin_type_e type)
219 {
220 if (!g_module_supported())
221 return NULL; /* nothing to do */
222
223 GHashTable *plugins_module = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, free_plugin);
224
225 /*
226 * Scan the global plugin directory.
227 */
228 scan_plugins_dir(plugins_module, get_plugins_dir_with_version(), type, TRUE);
229
230 /*
231 * If the program wasn't started with special privileges,
232 * scan the users plugin directory. (Even if we relinquish
233 * them, plugins aren't safe unless we've *permanently*
234 * relinquished them, and we can't do that in Wireshark as,
235 * if we need privileges to start capturing, we'd need to
236 * reclaim them before each time we start capturing.)
237 */
238 if (!started_with_special_privs()) {
239 scan_plugins_dir(plugins_module, get_plugins_pers_dir_with_version(), type, TRUE);
240 }
241
242 plugins_module_list = g_slist_prepend(plugins_module_list, plugins_module);
243
244 return plugins_module;
245 }
246
247 WS_DLL_PUBLIC void
plugins_get_descriptions(plugin_description_callback callback,void * callback_data)248 plugins_get_descriptions(plugin_description_callback callback, void *callback_data)
249 {
250 GPtrArray *plugins_array = g_ptr_array_new();
251 GHashTableIter iter;
252 gpointer value;
253
254 for (GSList *l = plugins_module_list; l != NULL; l = l->next) {
255 g_hash_table_iter_init (&iter, (GHashTable *)l->data);
256 while (g_hash_table_iter_next (&iter, NULL, &value)) {
257 g_ptr_array_add(plugins_array, value);
258 }
259 }
260
261 g_ptr_array_sort(plugins_array, compare_plugins);
262
263 for (guint i = 0; i < plugins_array->len; i++) {
264 plugin *plug = (plugin *)plugins_array->pdata[i];
265 callback(plug->name, plug->version, plug->type_name, g_module_name(plug->handle), callback_data);
266 }
267
268 g_ptr_array_free(plugins_array, TRUE);
269 }
270
271 static void
print_plugin_description(const char * name,const char * version,const char * description,const char * filename,void * user_data _U_)272 print_plugin_description(const char *name, const char *version,
273 const char *description, const char *filename,
274 void *user_data _U_)
275 {
276 printf("%-16s\t%s\t%s\t%s\n", name, version, description, filename);
277 }
278
279 void
plugins_dump_all(void)280 plugins_dump_all(void)
281 {
282 plugins_get_descriptions(print_plugin_description, NULL);
283 }
284
285 int
plugins_get_count(void)286 plugins_get_count(void)
287 {
288 guint count = 0;
289
290 for (GSList *l = plugins_module_list; l != NULL; l = l->next) {
291 count += g_hash_table_size((GHashTable *)l->data);
292 }
293 return count;
294 }
295
296 void
plugins_cleanup(plugins_t * plugins)297 plugins_cleanup(plugins_t *plugins)
298 {
299 if (!plugins)
300 return;
301
302 plugins_module_list = g_slist_remove(plugins_module_list, plugins);
303 g_hash_table_destroy((GHashTable *)plugins);
304 }
305
306 /*
307 * Editor modelines
308 *
309 * Local Variables:
310 * c-basic-offset: 4
311 * tab-width: 8
312 * indent-tabs-mode: nil
313 * End:
314 *
315 * ex: set shiftwidth=4 tabstop=8 expandtab:
316 * :indentSize=4:tabSize=8:noTabs=true:
317 */
318