Ethereal-dev: Re: [ethereal-dev] plugins support

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Olivier Abad <abad@xxxxxxxxxxxxx>
Date: Mon, 8 Nov 1999 22:46:11 +0100
On dim, nov 07, 1999 at 10:44:26 +0100, Olivier Abad wrote:
> 
> I should have an implementation ready for tomorrow.

Here it is.

This patch adds a "Plugins..." entry in the File menu. It opens a dialog
which lists available plugins. The user must enable the plugins he wants
to use.

Plugins are searched in /usr/share/ethereal/plugins,
/usr/local/share/ethereal/plugins and $HOME/.ethereal/plugins

The design is not perfect, and there is some more work to do :
- the "name" symbol provided by the plugin must match the file name of
the plugin (so the symbol is not very useful, I may remove it in the
future)
- I added a "version" string but I don't use it yet (it is displayed in
the dialog)
- the "Enable" button doesn't refresh the packet list
- the "Details" button does nothing. It should allow to change the
"filter_string" used by a plugin
- there is no way to disable a plugin
- if the same plugin is found in several directories, it will be listed
several times
- plugins should use ETT_NONE when creating subtrees (maybe we could
dynamically allocate ETT_xxx values).
- I only put the hooks for calling the plugins in dissect_tcp
- plugins are only enabled on platforms which support dlopen. I don't
know what could be done on other platforms.

I'm also attaching an http.c which should generate ... an http plugin !
It is the standard packet-http.c, slightly modified (dissect_http is
renamed dissector, proto_register_http is renamed proto_init, ETT_HTTP
is replaced with ETT_NONE).

To compile it :
$ gcc -DHAVE_CONFIG_H -Wall -g -O2 -I/usr/lib/glib/include -c http.c
$ ld -shared -o http http.o
$ mkdir ~/.ethereal/plugins
$ cp http ~/.ethereal/plugins

Remove dissect_http from packet-tcp.c if you want to be sure the plugin
works.

Olivier
-- 
Good day to avoid cops.  Crawl to school.
diff -Nru ethereal/Makefile.am ethereal.plug/Makefile.am
--- ethereal/Makefile.am	Fri Oct 29 03:04:16 1999
+++ ethereal.plug/Makefile.am	Mon Nov  8 21:47:15 1999
@@ -145,6 +145,8 @@
 	ipproto.c      \
 	packet.c       \
 	packet.h       \
+	plugins.c      \
+	plugins.h      \
 	prefs.c        \
 	prefs.h        \
 	print.c        \
diff -Nru ethereal/configure.in ethereal.plug/configure.in
--- ethereal/configure.in	Thu Oct 28 05:33:19 1999
+++ ethereal.plug/configure.in	Mon Nov  8 21:47:15 1999
@@ -131,6 +131,7 @@
 AC_CHECK_HEADERS(sys/stat.h sys/sockio.h sys/types.h netinet/in.h sys/socket.h net/if.h)
 AC_CHECK_HEADERS(sys/wait.h)
 AC_CHECK_HEADERS(stddef.h)
+AC_CHECK_HEADERS(dlfcn.h)
 
 dnl SNMP Check
 AC_ARG_ENABLE(snmp,
diff -Nru ethereal/file.c ethereal.plug/file.c
--- ethereal/file.c	Mon Nov  8 07:53:18 1999
+++ ethereal.plug/file.c	Mon Nov  8 21:47:15 1999
@@ -85,6 +85,10 @@
 #include "timestamp.h"
 #include "conversation.h"
 
+#ifdef HAVE_DLFCN_H
+#include "plugins.h"
+#endif
+
 #ifndef __RESOLV_H__
 #include "resolv.h"
 #endif
@@ -834,7 +838,7 @@
   gint          i, row;
   gint		crow;
   gint 		color;
-  proto_tree   *protocol_tree;
+  proto_tree   *protocol_tree = NULL;
 
   fdata->num = cf->count;
 
@@ -887,9 +891,17 @@
 	proto_tree_free(protocol_tree);
   }
   else {
-	dissect_packet(buf, fdata, NULL);
+#ifdef HAVE_DLFCN_H
+	if (plugin_list)
+	    protocol_tree = proto_tree_create_root();
+#endif
+	dissect_packet(buf, fdata, protocol_tree);
 	fdata->passed_dfilter = TRUE;
 	color = -1;
+#ifdef HAVE_DLFCN_H
+	if (protocol_tree)
+	    proto_tree_free(protocol_tree);
+#endif
   }
   if (fdata->passed_dfilter) {
     /* If we don't have the time stamp of the previous displayed packet,
diff -Nru ethereal/gtk/Makefile.am ethereal.plug/gtk/Makefile.am
--- ethereal/gtk/Makefile.am	Mon Nov  8 07:53:19 1999
+++ ethereal.plug/gtk/Makefile.am	Mon Nov  8 21:47:15 1999
@@ -53,6 +53,7 @@
 	menu.h		\
 	prefs_dlg.c	\
 	prefs_dlg.h	\
+	plugins_dlg.c	\
 	print_dlg.c	\
 	print_prefs.c   \
 	print_prefs.h	\
diff -Nru ethereal/gtk/main.h ethereal.plug/gtk/main.h
--- ethereal/gtk/main.h	Fri Oct  8 09:29:42 1999
+++ ethereal.plug/gtk/main.h	Mon Nov  8 21:47:15 1999
@@ -64,6 +64,9 @@
 void file_reload_cmd_cb(GtkWidget *, gpointer);
 void file_print_cmd_cb(GtkWidget *, gpointer);
 void file_print_packet_cmd_cb(GtkWidget *, gpointer);
+#ifdef HAVE_DLFCN_H
+void file_plugins_cmd_cb(GtkWidget *, gpointer);
+#endif
 void expand_all_cb(GtkWidget *, gpointer);
 void collapse_all_cb(GtkWidget *, gpointer);
 
diff -Nru ethereal/gtk/menu.c ethereal.plug/gtk/menu.c
--- ethereal/gtk/menu.c	Sun Nov  7 14:37:25 1999
+++ ethereal.plug/gtk/menu.c	Mon Nov  8 21:47:15 1999
@@ -82,6 +82,10 @@
   {"/File/Save _As...", NULL, GTK_MENU_FUNC(file_save_as_cmd_cb), 0, NULL},
   {"/File/_Reload", "<control>R", GTK_MENU_FUNC(file_reload_cmd_cb), 0, NULL},
   {"/File/<separator>", NULL, NULL, 0, "<Separator>"},
+#ifdef HAVE_DLFCN_H
+  {"/File/Plu_gins...", "<control>G", GTK_MENU_FUNC(file_plugins_cmd_cb), 0, NULL},
+  {"/File/<separator>", NULL, NULL, 0, "<Separator>"},
+#endif
   {"/File/Print...", NULL, GTK_MENU_FUNC(file_print_cmd_cb), 0, NULL},
   {"/File/Print Pac_ket", "<control>P", GTK_MENU_FUNC(file_print_packet_cmd_cb), 0, NULL},
   {"/File/<separator>", NULL, NULL, 0, "<Separator>"},
diff -Nru ethereal/gtk/plugins_dlg.c ethereal.plug/gtk/plugins_dlg.c
--- ethereal/gtk/plugins_dlg.c	Thu Jan  1 01:00:00 1970
+++ ethereal.plug/gtk/plugins_dlg.c	Mon Nov  8 21:47:33 1999
@@ -0,0 +1,334 @@
+/* plugins_dlg.c
+ * Dialog boxes for plugins
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_DLFCN_H
+
+#include <errno.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+#ifndef __GLOBALS_H__
+#include "globals.h"
+#endif
+
+#ifndef __PLUGINS_H__
+#include "plugins.h"
+#endif
+
+#ifndef __KEYS_H__
+#include "keys.h"
+#endif
+
+#ifndef __PREFS_DLG_H__
+#include "prefs_dlg.h"
+#endif
+
+#ifndef __UTIL_H__
+#include "util.h"
+#endif
+
+static gint selected_row;
+static gchar std_plug_dir[] = "/usr/share/ethereal/plugins";
+static gchar local_plug_dir[] = "/usr/local/share/ethereal/plugins";
+static gchar *user_plug_dir = NULL;
+
+static void plugins_close_cb(GtkWidget *, gpointer);
+static void plugins_scan(GtkWidget *, const char *);
+static void plugins_clist_select_cb(GtkWidget *, gint, gint,
+	GdkEventButton *, gpointer);
+static void plugins_clist_unselect_cb(GtkWidget *, gint, gint,
+	GdkEventButton *, gpointer);
+static void plugins_enable_cb(GtkWidget *, gpointer);
+static void plugins_details_cb(GtkWidget *, gpointer);
+
+void
+file_plugins_cmd_cb(GtkWidget *widget, gpointer data)
+{
+    GtkWidget *plugins_window;
+    GtkWidget *main_vbox;
+    GtkWidget *main_frame;
+    GtkWidget *frame_hbox;
+    GtkWidget *scrolledwindow;
+    GtkWidget *plugins_clist;
+    GtkWidget *frame_vbnbox;
+    GtkWidget *enable_bn, *details_bn;
+    GtkWidget *main_hbnbox;
+    GtkWidget *close_bn;
+    gchar     *titles[] = {"Name", "Description", "Version", "Enabled"};
+
+    plugins_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW(plugins_window), "Ethereal: Plugins");
+
+    main_vbox = gtk_vbox_new(FALSE, 0);
+    gtk_container_add(GTK_CONTAINER(plugins_window), main_vbox);
+    gtk_widget_show(main_vbox);
+
+    main_frame = gtk_frame_new("Plugins List");
+    gtk_box_pack_start(GTK_BOX(main_vbox), main_frame, TRUE, TRUE, 0);
+    gtk_container_set_border_width(GTK_CONTAINER(main_frame), 10);
+    gtk_widget_show(main_frame);
+
+    frame_hbox = gtk_hbox_new(FALSE,0);
+    gtk_container_add(GTK_CONTAINER(main_frame), frame_hbox);
+    gtk_container_set_border_width(GTK_CONTAINER(frame_hbox), 5);
+    gtk_widget_show(frame_hbox);
+
+    scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
+    gtk_box_pack_start(GTK_BOX(frame_hbox), scrolledwindow, TRUE, TRUE, 0);
+    gtk_widget_set_usize(scrolledwindow, 400, 150);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow),
+	    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+    gtk_widget_show(scrolledwindow);
+
+    plugins_clist = gtk_clist_new_with_titles(4, titles);
+    gtk_container_add(GTK_CONTAINER(scrolledwindow), plugins_clist);
+    gtk_clist_set_selection_mode(GTK_CLIST(plugins_clist), GTK_SELECTION_SINGLE);
+    gtk_clist_column_titles_passive(GTK_CLIST(plugins_clist));
+    gtk_clist_column_titles_show(GTK_CLIST(plugins_clist));
+    gtk_clist_set_column_auto_resize(GTK_CLIST(plugins_clist), 0, TRUE);
+    gtk_clist_set_column_auto_resize(GTK_CLIST(plugins_clist), 1, TRUE);
+    gtk_clist_set_column_auto_resize(GTK_CLIST(plugins_clist), 2, TRUE);
+    gtk_clist_set_column_auto_resize(GTK_CLIST(plugins_clist), 3, TRUE);
+    plugins_scan(plugins_clist, std_plug_dir);
+    plugins_scan(plugins_clist, local_plug_dir);
+    if (!user_plug_dir)
+    {
+	user_plug_dir = (gchar *)g_malloc(strlen(getenv("HOME")) + 19);
+	sprintf(user_plug_dir, "%s/.ethereal/plugins", getenv("HOME"));
+    }
+    plugins_scan(plugins_clist, user_plug_dir);
+    gtk_signal_connect(GTK_OBJECT(plugins_clist), "select_row",
+	    GTK_SIGNAL_FUNC(plugins_clist_select_cb), NULL);
+    gtk_signal_connect(GTK_OBJECT(plugins_clist), "unselect_row",
+	    GTK_SIGNAL_FUNC(plugins_clist_unselect_cb), NULL);
+    gtk_widget_show(plugins_clist);
+    selected_row = -1;
+
+    frame_vbnbox = gtk_vbutton_box_new();
+    gtk_box_pack_start(GTK_BOX(frame_hbox), frame_vbnbox, FALSE, TRUE, 0);
+    gtk_container_set_border_width(GTK_CONTAINER(frame_vbnbox), 20);
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(frame_vbnbox), GTK_BUTTONBOX_START);
+    gtk_widget_show(frame_vbnbox);
+
+    enable_bn = gtk_button_new_with_label("Enable");
+    gtk_container_add(GTK_CONTAINER(frame_vbnbox), enable_bn);
+    gtk_signal_connect(GTK_OBJECT(enable_bn), "clicked",
+	    GTK_SIGNAL_FUNC(plugins_enable_cb), GTK_OBJECT(plugins_clist));
+    gtk_widget_show(enable_bn);
+    details_bn = gtk_button_new_with_label("Details");
+    gtk_container_add(GTK_CONTAINER(frame_vbnbox), details_bn);
+    gtk_signal_connect(GTK_OBJECT(details_bn), "clicked",
+	    GTK_SIGNAL_FUNC(plugins_details_cb), GTK_OBJECT(plugins_clist));
+    gtk_widget_show(details_bn);
+
+    main_hbnbox = gtk_hbutton_box_new();
+    gtk_box_pack_start(GTK_BOX(main_vbox), main_hbnbox, FALSE, TRUE, 0);
+    gtk_container_set_border_width(GTK_CONTAINER(main_hbnbox), 10);
+    gtk_widget_show(main_hbnbox);
+
+    close_bn = gtk_button_new_with_label("Close");
+    gtk_container_add(GTK_CONTAINER(main_hbnbox), close_bn);
+    gtk_widget_show(close_bn);
+    gtk_signal_connect(GTK_OBJECT(close_bn), "clicked",
+	    GTK_SIGNAL_FUNC(plugins_close_cb), GTK_OBJECT(plugins_window));
+
+    gtk_widget_show(plugins_window);
+}
+
+/*
+ * scan /usr/share/ethereal/plugins, /usr/local/share/ethereal/plugins and
+ * ~/.ethereal/plugins and fill the clist widget
+ */
+static void
+plugins_scan(GtkWidget *clist, const char *dirname)
+{
+    DIR           *dir;             /* scanned directory */
+    struct dirent *file;            /* current file */
+    gchar          filename[512];   /* current file name */
+    void          *handle;          /* handle returned by dlopen */
+    gchar         *plugent[4];      /* new entry added in clist */
+    gint           row;             /* index of the new row */
+
+    if ((dir = opendir(dirname)) != NULL)
+    {
+	while ((file = readdir(dir)) != NULL)
+	{
+	    sprintf(filename, "%s/%s", dirname, file->d_name);
+
+	    if ((handle = dlopen(filename, RTLD_LAZY)) == NULL) continue;
+	    /* plugin name */
+	    if ((plugent[0] = (gchar *)dlsym(handle, "name")) == NULL)
+	    {
+		dlclose(handle);
+		continue;
+	    }
+	    /* plugin description */
+	    if ((plugent[1] = (gchar *)dlsym(handle, "desc")) == NULL)
+	    {
+		dlclose(handle);
+		continue;
+	    }
+	    /* plugin version */
+	    if ((plugent[2] = (gchar *)dlsym(handle, "version")) == NULL)
+	    {
+		dlclose(handle);
+		continue;
+	    }
+	    /* check if plugin already loaded */
+	    if (is_enabled(plugent[0], plugent[2]))
+		plugent[3] = "Yes";
+	    else
+		plugent[3] = "No";
+	    row = gtk_clist_append(GTK_CLIST(clist), plugent);
+	    gtk_clist_set_row_data(GTK_CLIST(clist), row, (gpointer)dirname);
+	    dlclose(handle);
+	}
+	closedir(dir);
+    }
+}
+
+static void
+plugins_close_cb(GtkWidget *close_bt, gpointer parent_w)
+{
+    gtk_grab_remove(GTK_WIDGET(parent_w));
+    gtk_widget_destroy(GTK_WIDGET(parent_w));
+}
+
+void plugins_clist_select_cb(GtkWidget *clist, gint row, gint column,
+	GdkEventButton *event, gpointer data)
+{
+    selected_row = row;
+}
+
+void plugins_clist_unselect_cb(GtkWidget *clist, gint row, gint column,
+	GdkEventButton *event, gpointer data)
+{
+    selected_row = -1;
+}
+
+static void
+plugins_enable_cb(GtkWidget *button, gpointer clist)
+{
+    gchar     *pl_name, *dirname, *filename;
+    void      *handle;
+    gchar     *name, *version, *protocol;
+    gchar     *filter_string;
+    dfilter   *filter = NULL;
+    void     (*dissector) (const u_char *, int, frame_data *, proto_tree *);
+    void     (*proto_init) ();
+
+    if (selected_row == -1) return;
+    dirname = (gchar *)gtk_clist_get_row_data(GTK_CLIST(clist), selected_row);
+    if (!dirname) return;
+    gtk_clist_get_text(GTK_CLIST(clist), selected_row, 0, &filename);
+    if (!filename) return;
+
+    pl_name = g_strdup_printf("%s/%s", dirname, filename);
+
+    if ((handle = dlopen(pl_name, RTLD_LAZY)) == NULL) {
+	simple_dialog(ESD_TYPE_WARN, NULL, "Can't load plugin");
+	g_free(pl_name);
+	return;
+    }
+
+    if ((name = (gchar *)dlsym(handle, "name")) == NULL)
+    {
+	simple_dialog(ESD_TYPE_WARN, NULL, "Invalid plugin");
+	dlclose(handle);
+	g_free(pl_name);
+	return;
+    }
+    if ((version = (gchar *)dlsym(handle, "version")) == NULL)
+    {
+	simple_dialog(ESD_TYPE_WARN, NULL, "Invalid plugin");
+	dlclose(handle);
+	g_free(pl_name);
+	return;
+    }
+    if ((protocol = (gchar *)dlsym(handle, "protocol")) == NULL)
+    {
+	simple_dialog(ESD_TYPE_WARN, NULL, "Invalid plugin");
+	dlclose(handle);
+	g_free(pl_name);
+	return;
+    }
+    if ((filter_string = (gchar *)dlsym(handle, "filter_string")) == NULL)
+    {
+	simple_dialog(ESD_TYPE_WARN, NULL, "Invalid plugin");
+	dlclose(handle);
+	g_free(pl_name);
+	return;
+    }
+    if (dfilter_compile(filter_string, &filter) != 0) {
+	simple_dialog(ESD_TYPE_WARN, NULL, dfilter_error_msg);
+	dlclose(handle);
+	g_free(pl_name);
+	return;
+    }
+    if ((dissector = (void (*)(const u_char *, int,
+			frame_data *,
+			proto_tree *)) dlsym(handle, "dissector")) == NULL)
+    {
+	simple_dialog(ESD_TYPE_WARN, NULL, "Invalid plugin");
+	if (filter != NULL)
+	    dfilter_destroy(filter);
+	dlclose(handle);
+	g_free(pl_name);
+	return;
+    }
+    if ((proto_init = (void (*)()) dlsym(handle, "proto_init")) == NULL)
+    {
+	simple_dialog(ESD_TYPE_WARN, NULL, "Invalid plugin");
+	if (filter != NULL)
+	    dfilter_destroy(filter);
+	dlclose(handle);
+	g_free(pl_name);
+	return;
+    }
+
+    if (enable_plugin(handle, name, version, protocol, filter, dissector)) {
+	simple_dialog(ESD_TYPE_WARN, NULL, "Can't load new plugin");
+	if (filter != NULL)
+	    dfilter_destroy(filter);
+	dlclose(handle);
+	g_free(pl_name);
+	return;
+    }
+    proto_init();
+
+    gtk_clist_set_text(GTK_CLIST(clist), selected_row, 3, "Yes");
+    g_free(pl_name);
+}
+
+static void
+plugins_details_cb(GtkWidget *button, gpointer clist)
+{
+}
+
+#endif
diff -Nru ethereal/packet-tcp.c ethereal.plug/packet-tcp.c
--- ethereal/packet-tcp.c	Tue Nov  2 08:04:46 1999
+++ ethereal.plug/packet-tcp.c	Mon Nov  8 21:47:15 1999
@@ -37,7 +37,7 @@
 
 #include <stdio.h>
 #include <glib.h>
-#include "packet.h"
+#include "globals.h"
 #include "resolv.h"
 #include "follow.h"
 #include "util.h"
@@ -51,6 +51,10 @@
 # include "snprintf.h"
 #endif
 
+#ifdef HAVE_DLFCN_H
+#include "plugins.h"
+#endif
+
 #ifndef __PACKET_IP_H__
 #include "packet-ip.h"
 #endif
@@ -468,6 +472,22 @@
   /* Check the packet length to see if there's more data
      (it could be an ACK-only packet) */
   if (packet_max > offset) {
+#ifdef HAVE_DLFCN_H
+    plugin *pt_plug = plugin_list;
+
+    if (pt_plug) {
+      while (pt_plug) {
+        if (!strcmp(pt_plug->protocol, "tcp")) {
+	  if (tree && dfilter_apply(pt_plug->filter, tree, pd)) {
+	    pt_plug->dissector(pd, offset, fd, tree);
+	    goto dissect_end;
+	  }
+	}
+	pt_plug = pt_plug->next;
+      }
+    }
+#endif
+
     /* XXX - this should be handled the way UDP handles this, with a table
        of port numbers to which stuff can be added */
 #define PORT_IS(port)	(th.th_sport == port || th.th_dport == port)
@@ -522,6 +542,8 @@
     }
   }
  
+dissect_end:
+
   if( data_out_file ) {
     reassemble_tcp( th.th_seq,		/* sequence number */
         ( pi.len - offset ),		/* data length */
diff -Nru ethereal/plugins.c ethereal.plug/plugins.c
--- ethereal/plugins.c	Thu Jan  1 01:00:00 1970
+++ ethereal.plug/plugins.c	Mon Nov  8 21:47:25 1999
@@ -0,0 +1,84 @@
+/* plugins.c
+ * definitions for plugins structures
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_DLFCN_H
+
+#include <time.h>
+
+#include "globals.h"
+
+#include "plugins.h"
+
+plugin *plugin_list;
+
+int
+enable_plugin(void *handle, gchar *name, gchar *version, gchar *protocol, dfilter *filter,
+	      void (*dissector) (const u_char *,
+	                         int,
+				 frame_data *,
+				 proto_tree *))
+{
+    plugin *new_plug, *pt_plug;
+
+    new_plug = (plugin *)g_malloc(sizeof(plugin));
+    if (new_plug == 0) return -1;
+
+    pt_plug = plugin_list;
+    if (!pt_plug)
+        plugin_list = new_plug;
+    else
+    {
+	while (pt_plug->next) pt_plug = pt_plug->next;
+	pt_plug->next = new_plug;
+    }
+
+    new_plug->handle = handle;
+    new_plug->name = name;
+    new_plug->version = version;
+    new_plug->protocol = protocol;
+    new_plug->filter = filter;
+    new_plug->dissector = dissector;
+    new_plug->next = NULL;
+    return 0;
+}
+
+gboolean
+is_enabled(const gchar *name, const gchar *version)
+{
+    plugin *pt_plug;
+
+    pt_plug = plugin_list;
+    while (pt_plug)
+    {
+	if (!strcmp(pt_plug->name, name) && !strcmp(pt_plug->version, version))
+	    return TRUE;
+	pt_plug = pt_plug->next;
+    }
+    return FALSE;
+}
+
+#endif
diff -Nru ethereal/plugins.h ethereal.plug/plugins.h
--- ethereal/plugins.h	Thu Jan  1 01:00:00 1970
+++ ethereal.plug/plugins.h	Mon Nov  8 21:47:15 1999
@@ -0,0 +1,46 @@
+/* plugins.h
+ * definitions for plugins structures
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __PLUGINS_H__
+#define __PLUGINS_H__
+
+typedef struct _plugin {
+    void    *handle;          /* handle returned by dlopen */
+    gchar   *name;            /* plugin name */
+    gchar   *version;         /* plugin version */
+    gchar   *protocol;        /* protocol which should call the dissector
+			       * for this plugin eg "tcp" */
+    dfilter *filter;          /* display filter matching frames for which
+			       * the dissector should be used */
+    /* the dissector */
+    void (*dissector) (const u_char *, int, frame_data *, proto_tree *);
+    struct _plugin *next;     /* forward link */
+} plugin;
+
+extern plugin *plugin_list;
+
+int enable_plugin(void *, gchar *, gchar *, gchar *, dfilter *,
+	          void (*) (const u_char *, int, frame_data *, proto_tree *));
+gboolean is_enabled(const gchar *, const gchar *);
+
+#endif /* __PLUGINS_H__ */
/* packet-http.c
 * Routines for HTTP packet disassembly
 *
 * Guy Harris <guy@xxxxxxxxxx>
 *
 * $Id: packet-http.c,v 1.10 1999/10/16 20:30:14 deniel Exp $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxx>
 * Copyright 1998 Gerald Combs
 *
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#include <string.h>
#include <ctype.h>

#include <glib.h>
#include "globals.h"
#include "dfilter.h"

gchar name[] = "http";
gchar version[] = "1.0";
gchar desc[] = "HTTP packet disassembly";
gchar protocol[] = "tcp";
gchar filter_string[] = "tcp.port == 80 || tcp.port == 8080 || tcp.port == 631";

static int proto_http = -1;
static int hf_http_response = -1;
static int hf_http_request = -1;

static proto_tree *http_tree;

static int is_http_request_or_reply(const u_char *data, int linelen);

void dissector(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
{
	gboolean	is_ipp = (pi.srcport == 631 || pi.destport == 631);
	proto_item	*ti;
	const u_char	*data, *dataend;
	const u_char	*linep, *lineend, *eol;
	int		linelen;
	u_char		c;

	data = &pd[offset];
	dataend = data + END_OF_FRAME;

	if (check_col(fd, COL_PROTOCOL))
		col_add_str(fd, COL_PROTOCOL, is_ipp ? "IPP" : "HTTP");
	if (check_col(fd, COL_INFO)) {
		/*
		 * Put the first line from the buffer into the summary,
		 * if it's an HTTP request or reply.
		 * Otherwise, just call it a continuation.
		 */
		lineend = find_line_end(data, dataend, &eol);
		linelen = lineend - data;
		if (is_http_request_or_reply(data, linelen))
			col_add_str(fd, COL_INFO, format_text(data, linelen));
		else
			col_add_str(fd, COL_INFO, "Continuation");
	}

	if (tree) {
		ti = proto_tree_add_item(tree, proto_http, offset, END_OF_FRAME, NULL);
		http_tree = proto_item_add_subtree(ti, ETT_NONE);

		while (data < dataend) {
			/*
			 * Find the end of the line.
			 */
			lineend = find_line_end(data, dataend, &eol);
			linelen = lineend - data;

			/*
			 * OK, does it look like an HTTP request or
			 * response?
			 */
			if (is_http_request_or_reply(data, linelen))
				goto is_http;

			/*
			 * No.  Does it look like a blank line (as would
			 * appear at the end of an HTTP request)?
			 */
			if (linelen == 1) {
				if (*data == '\n')
					goto is_http;
			}
			if (linelen == 2) {
				if (strncmp(data, "\r\n", 2) == 0 ||
				    strncmp(data, "\n\r", 2) == 0)
					goto is_http;
			}

			/*
			 * No.  Does it look like a MIME header?
			 */
			linep = data;
			while (linep < lineend) {
				c = *linep++;
				if (!isprint(c))
					break;	/* not printable, not a MIME header */
				switch (c) {

				case '(':
				case ')':
				case '<':
				case '>':
				case '@':
				case ',':
				case ';':
				case '\\':
				case '"':
				case '/':
				case '[':
				case ']':
				case '?':
				case '=':
				case '{':
				case '}':
					/*
					 * It's a tspecial, so it's not
					 * part of a token, so it's not
					 * a field name for the beginning
					 * of a MIME header.
					 */
					goto not_http;

				case ':':
					/*
					 * This ends the token; we consider
					 * this to be a MIME header.
					 */
					goto is_http;
				}
			}

		not_http:
			/*
			 * We don't consider this part of an HTTP request or
			 * reply, so we don't display it.
			 * (Yeah, that means we don't display, say, a
			 * text/http page, but you can get that from the
			 * data pane.)
			 */
			break;

		is_http:
			/*
			 * Put this line.
			 */
			proto_tree_add_text(http_tree, offset, linelen, "%s",
			    format_text(data, linelen));
			offset += linelen;
			data = lineend;
		}

		if (data < dataend) {
			if (is_ipp)
				dissect_ipp(pd, offset, fd, tree);
			else
				dissect_data(&pd[offset], offset, fd, http_tree);
		}
	}
}

/*
 * XXX - this won't handle HTTP 0.9 replies, but they're all data
 * anyway.
 */
static int
is_http_request_or_reply(const u_char *data, int linelen)
{
	if (linelen >= 3) {
		if (strncasecmp(data, "GET", 3) == 0 ||
		    strncasecmp(data, "PUT", 3) == 0) {
			proto_tree_add_item_hidden(http_tree, 
						   hf_http_request, 0, 0, 1);
			return TRUE;
		}
	}
	if (linelen >= 4) {
		if (strncasecmp(data, "HEAD", 4) == 0 ||
		    strncasecmp(data, "POST", 4) == 0) {
			proto_tree_add_item_hidden(http_tree, 
						   hf_http_request, 0, 0, 1);
			return TRUE;
		}
	}
	if (linelen >= 5) {
		if (strncasecmp(data, "TRACE", 5) == 0)
			return TRUE;
		if (strncasecmp(data, "HTTP/", 5) == 0) {
			proto_tree_add_item_hidden(http_tree, 
						   hf_http_response, 0, 0, 1);
			return TRUE;	/* response */
		}
	}
	if (linelen >= 6) {
		if (strncasecmp(data, "DELETE", 6) == 0)
			return TRUE;
	}
	if (linelen >= 7) {
		if (strncasecmp(data, "OPTIONS", 7) == 0)
			return TRUE;
	}
	return FALSE;
}

void
proto_init(void)
{

  static hf_register_info hf[] = {
    { &hf_http_response,
      { "Response",		"http.response",  
	FT_BOOLEAN, BASE_NONE, NULL, 0x0,
	"TRUE if HTTP response" }},
    { &hf_http_request,
      { "Request",		"http.request",
	FT_BOOLEAN, BASE_NONE, NULL, 0x0,
	"TRUE if HTTP request (GET, PUT, HEAD, POST)" }},
  };

  dfilter_cleanup();
  proto_http = proto_register_protocol("Hypertext Transfer Protocol", "http");
  proto_register_field_array(proto_http, hf, array_length(hf));
  dfilter_init();
}