From 4f32c5f793a5e300984fd826c14775d4932dfaa6 Mon Sep 17 00:00:00 2001 From: "Yuto KAWAMURA(kawamuray)" Date: Thu, 16 Jan 2014 02:06:58 +0900 Subject: [PATCH] Introduce Libvirt Wireshark dissector Introduce Wireshark dissector plugin which adds support to Wireshark for dissecting libvirt RPC protocol. Added following files to build Wireshark dissector from libvirt source tree. * tools/wireshark/*: Source tree of Wireshark dissector plugin. Added followings to configure.ac or Makefile.am. configure.ac * --with-wireshark-dissector: Enable support for building Wireshark dissector. * --with-ws-plugindir: Specify wireshark plugin directory that dissector will installed. * Added tools/wireshark/{Makefile,src/Makefile} to AC_CONFIG_FILES. Makefile.am * Added tools/wireshark/ to SUBDIR. --- .gitignore | 2 + Makefile.am | 3 +- cfg.mk | 8 +- configure.ac | 72 +- tools/wireshark/Makefile.am | 22 + tools/wireshark/README.md | 31 + tools/wireshark/src/Makefile.am | 42 + tools/wireshark/src/packet-libvirt.c | 520 ++++++++++++ tools/wireshark/src/packet-libvirt.h | 128 +++ tools/wireshark/util/genxdrstub.pl | 1011 +++++++++++++++++++++++ tools/wireshark/util/make-dissector-reg | 198 +++++ 11 files changed, 2031 insertions(+), 6 deletions(-) create mode 100644 tools/wireshark/Makefile.am create mode 100644 tools/wireshark/README.md create mode 100644 tools/wireshark/src/Makefile.am create mode 100644 tools/wireshark/src/packet-libvirt.c create mode 100644 tools/wireshark/src/packet-libvirt.h create mode 100755 tools/wireshark/util/genxdrstub.pl create mode 100755 tools/wireshark/util/make-dissector-reg diff --git a/.gitignore b/.gitignore index 7005500138..96b7211638 100644 --- a/.gitignore +++ b/.gitignore @@ -226,6 +226,8 @@ /tools/virsh-*-edit.c /tools/virt-*-validate /tools/virt-sanlock-cleanup +/tools/wireshark/src/plugin.c +/tools/wireshark/src/libvirt /update.log GPATH GRTAGS diff --git a/Makefile.am b/Makefile.am index eb88fee66c..9847ff0665 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,7 +22,8 @@ GENHTML = genhtml SUBDIRS = . gnulib/lib include src daemon tools docs gnulib/tests \ tests po examples/object-events examples/hellolibvirt \ examples/dominfo examples/domsuspend examples/apparmor \ - examples/xml/nwfilter examples/openauth examples/systemtap + examples/xml/nwfilter examples/openauth examples/systemtap \ + tools/wireshark ACLOCAL_AMFLAGS = -I m4 diff --git a/cfg.mk b/cfg.mk index e7515eaa5d..207dfebb8d 100644 --- a/cfg.mk +++ b/cfg.mk @@ -977,10 +977,10 @@ exclude_file_name_regexp--sc_prohibit_newline_at_end_of_diagnostic = \ ^src/rpc/gendispatch\.pl$$ exclude_file_name_regexp--sc_prohibit_nonreentrant = \ - ^((po|tests)/|docs/.*(py|html\.in)|run.in$$) + ^((po|tests)/|docs/.*(py|html\.in)|run.in$$|tools/wireshark/util/genxdrstub\.pl$$) exclude_file_name_regexp--sc_prohibit_raw_allocation = \ - ^(docs/hacking\.html\.in)|(src/util/viralloc\.[ch]|examples/.*|tests/securityselinuxhelper\.c|tests/vircgroupmock\.c)$$ + ^(docs/hacking\.html\.in)|(src/util/viralloc\.[ch]|examples/.*|tests/securityselinuxhelper\.c|tests/vircgroupmock\.c|tools/wireshark/src/packet-libvirt.c)$$ exclude_file_name_regexp--sc_prohibit_readlink = \ ^src/(util/virutil|lxc/lxc_container)\.c$$ @@ -988,7 +988,7 @@ exclude_file_name_regexp--sc_prohibit_readlink = \ exclude_file_name_regexp--sc_prohibit_setuid = ^src/util/virutil\.c$$ exclude_file_name_regexp--sc_prohibit_sprintf = \ - ^(docs/hacking\.html\.in)|(examples/systemtap/.*stp)|(src/dtrace2systemtap\.pl)|(src/rpc/gensystemtap\.pl)$$ + ^(docs/hacking\.html\.in)|(examples/systemtap/.*stp)|(src/dtrace2systemtap\.pl)|(src/rpc/gensystemtap\.pl)|(tools/wireshark/util/genxdrstub\.pl)$$ exclude_file_name_regexp--sc_prohibit_strncpy = ^src/util/virstring\.c$$ @@ -1021,7 +1021,7 @@ exclude_file_name_regexp--sc_correct_id_types = \ exclude_file_name_regexp--sc_m4_quote_check = m4/virt-lib.m4 exclude_file_name_regexp--sc_prohibit_include_public_headers_quote = \ - ^src/internal\.h$$ + ^(src/internal\.h$$|tools/wireshark/src/packet-libvirt.h$$) exclude_file_name_regexp--sc_prohibit_include_public_headers_brackets = \ ^(tools/|examples/|include/libvirt/(virterror|libvirt-(qemu|lxc))\.h$$) diff --git a/configure.ac b/configure.ac index 146418ff11..a58b73d949 100644 --- a/configure.ac +++ b/configure.ac @@ -2502,6 +2502,70 @@ AM_CONDITIONAL([HAVE_LIBNL], [test "$have_libnl" = "yes"]) AC_SUBST([LIBNL_CFLAGS]) AC_SUBST([LIBNL_LIBS]) +dnl wireshark dissector + +AC_ARG_WITH([wireshark-dissector], + [AS_HELP_STRING([--with-wireshark-dissector], + [enable wireshark dissector plugin support @<:@default=check@:>@])], + [ with_wireshark_dissector=$withval ], + [ with_wireshark_dissector=check ]) + +AC_DEFUN([LIBVIRT_WS_HANDLE_ERROR], [ + if test "$with_wireshark_dissector" = "yes"; then + AC_MSG_ERROR([$1]) + else + with_wireshark_dissector=no + fi +]) +if test "$with_wireshark_dissector" != "no"; then + dnl Check for XDR headers existence + AC_CHECK_HEADERS([rpc/types.h]) + + dnl Check for glib-2.0 existence + PKG_CHECK_MODULES([GLIB], [glib-2.0], [ + WS_DISSECTOR_CPPFLAGS="$WS_DISSECTOR_CPPFLAGS `$PKG_CONFIG --cflags glib-2.0`" + ], [ + LIBVIRT_WS_HANDLE_ERROR([pkg-config 'glib-2.0' is required for wireshark-dissector support]) + ]) + + dnl Search for wireshark(or tshark) command + AC_PATH_PROG([WIRESHARK], [wireshark]) + AC_PATH_PROG([WIRESHARK], [tshark]) + if test -z "$WIRESHARK"; then + LIBVIRT_WS_HANDLE_ERROR([command not found wireshark or tshark]) + else + dnl Check for wireshark headers + save_CPPFLAGS="$CPPFLAGS" + WS_DISSECTOR_CPPFLAGS="$WS_DISSECTOR_CPPFLAGS -I`dirname $WIRESHARK`/../include/wireshark" + CPPFLAGS="$CPPFLAGS $WS_DISSECTOR_CPPFLAGS" + AC_CHECK_HEADERS([wireshark/config.h],, [ + LIBVIRT_WS_HANDLE_ERROR([wireshark/config.h is required for wireshark-dissector support]) + ]) + AC_CHECK_HEADERS([wireshark/epan/packet.h wireshark/epan/dissectors/packet-tcp.h],, [ + LIBVIRT_WS_HANDLE_ERROR([wireshark/epan/{packet,packet-tcp}.h are required for wireshark-dissector support]) + ], [ + #include + ]) + CPPFLAGS="$save_CPPFLAGS" + fi + if test "$with_wireshark_dissector" != "no"; then + with_wireshark_dissector=yes + fi +fi +AC_SUBST([WS_DISSECTOR_CPPFLAGS]) +AM_CONDITIONAL([WITH_WIRESHARK_DISSECTOR], [test "$with_wireshark_dissector" = "yes"]) + +AC_ARG_WITH([ws-plugindir], + [AS_HELP_STRING([--with-ws-plugindir], + [wireshark plugins directory that plugin will installed])], + [ ws_plugindir=$withval ]) + +if test "$with_wireshark_dissector" != "no" && test -z "$ws_plugindir"; then + ws_version=`$WIRESHARK -v | head -1 | cut -f 2 -d' '` + ws_plugindir=`dirname $WIRESHARK`/../lib/wireshark/plugins/$ws_version +fi +AC_SUBST([ws_plugindir]) + # Check for Linux vs. BSD ifreq members AC_CHECK_MEMBERS([struct ifreq.ifr_newname, struct ifreq.ifr_ifindex, @@ -2585,7 +2649,9 @@ AC_CONFIG_FILES([\ examples/openauth/Makefile \ examples/hellolibvirt/Makefile \ examples/systemtap/Makefile \ - examples/xml/nwfilter/Makefile]) + examples/xml/nwfilter/Makefile \ + tools/wireshark/Makefile \ + tools/wireshark/src/Makefile]) AC_OUTPUT AC_MSG_NOTICE([]) @@ -2746,6 +2812,10 @@ AC_MSG_NOTICE([ XML Catalog: $XML_CATALOG_FILE]) AC_MSG_NOTICE([ Init script: $with_init_script]) AC_MSG_NOTICE([Char device locks: $with_chrdev_lock_files]) AC_MSG_NOTICE([]) +AC_MSG_NOTICE([Developer Tools]) +AC_MSG_NOTICE([]) +AC_MSG_NOTICE([Wireshark dissector: $with_wireshark_dissector]) +AC_MSG_NOTICE([]) AC_MSG_NOTICE([Privileges]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ QEMU: $QEMU_USER:$QEMU_GROUP]) diff --git a/tools/wireshark/Makefile.am b/tools/wireshark/Makefile.am new file mode 100644 index 0000000000..b6fa57cc20 --- /dev/null +++ b/tools/wireshark/Makefile.am @@ -0,0 +1,22 @@ +## Process this file with automake to produce Makefile.in + +# Copyright (C) 2013 Yuto KAWAMURA(kawamuray) +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see +# . +# +# Author: Yuto KAWAMURA(kawamuray) +if WITH_WIRESHARK_DISSECTOR +SUBDIRS = src +endif WITH_WIRESHARK_DISSECTOR diff --git a/tools/wireshark/README.md b/tools/wireshark/README.md new file mode 100644 index 0000000000..9f4c9b6876 --- /dev/null +++ b/tools/wireshark/README.md @@ -0,0 +1,31 @@ +About +===== +This is the project of Google Summer of Code 2013 accepted by QEMU.org and +libvirt community. The goal of this project is, provide Wireshark dissector for +Libvirt RPC protocol. It will provide Libvirt packet overview/detail analysing +in Wireshark. Furthermore, it will be able to build(generated) from RPC protocol +definition placed in Libvirt source tree to support latest protocol +specification. + +See also: +- http://www.google-melange.com/gsoc/project/google/gsoc2013/kawamuray/7001 +- http://wiki.qemu.org/Features/LibvirtWiresharkDissector + +Installation +============= +Run ./configure with --with-wireshark-dissector option enabled. +Then dissector will compiled with libvirt itself. + +Add/Remove protocol from dissector's support +-------------------------------------------- +Modify variable WS\_DISSECTOR\_PROTO\_FILES in tools/wireshark/src/Makefile.am. + +Changing installation directory +------------------------------- +You can change installation directory of pluggable shared object(libvirt.so) by +specifying --with-ws-plugindir=. + +You can install libvirt.so into your local wireshark plugin directory: + + ./configure --with-wireshark-dissector \ + --with-ws-plugindir=$HOME/.wireshark/plugins diff --git a/tools/wireshark/src/Makefile.am b/tools/wireshark/src/Makefile.am new file mode 100644 index 0000000000..81e704129f --- /dev/null +++ b/tools/wireshark/src/Makefile.am @@ -0,0 +1,42 @@ +## Process this file with automake to produce Makefile.in + +# Copyright (C) 2013 Yuto KAWAMURA(kawamuray) +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see +# . +# +# Author: Yuto KAWAMURA(kawamuray) +ws_plugin_LTLIBRARIES = libvirt.la +libvirt_la_SOURCES = packet-libvirt.c plugin.c +libvirt_la_CPPFLAGS = $(WS_DISSECTOR_CPPFLAGS) +libvirt_la_LDFLAGS = -avoid-version -module + +packet-libvirt.c: packet-libvirt.h libvirt/protocol.h + +plugin.c: packet-libvirt.c + $(srcdir)/../util/make-dissector-reg . plugin $< + +WS_DISSECTOR_PROTO_FILES = \ + $(top_srcdir)/src/remote/remote_protocol.x \ + $(top_srcdir)/src/remote/qemu_protocol.x \ + $(top_srcdir)/src/remote/lxc_protocol.x \ + $(top_srcdir)/src/rpc/virkeepaliveprotocol.x + +libvirt/protocol.h: $(srcdir)/../util/genxdrstub.pl $(WS_DISSECTOR_PROTO_FILES) + $(MKDIR_P) libvirt + LIBVIRT_VERSION=$(LIBVIRT_VERSION) \ + $(PERL) $(srcdir)/../util/genxdrstub.pl $(WS_DISSECTOR_PROTO_FILES) + +clean-local: + -rm -rf libvirt plugin.c diff --git a/tools/wireshark/src/packet-libvirt.c b/tools/wireshark/src/packet-libvirt.c new file mode 100644 index 0000000000..d64ecce404 --- /dev/null +++ b/tools/wireshark/src/packet-libvirt.c @@ -0,0 +1,520 @@ +/* packet-libvirt.c --- Libvirt packet dissector routines. + * + * Copyright (C) 2013 Yuto KAWAMURA(kawamuray) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Authors: + * Michal Privoznik + * Yuto KAWAMURA(kawamuray) + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_RPC_TYPES_H +# include +#endif +#include +#include "packet-libvirt.h" + +static int proto_libvirt = -1; +static int hf_libvirt_length = -1; +static int hf_libvirt_program = -1; +static int hf_libvirt_version = -1; +static int hf_libvirt_procedure = -1; +static int hf_libvirt_type = -1; +static int hf_libvirt_serial = -1; +static int hf_libvirt_status = -1; +static int hf_libvirt_stream = -1; +static int hf_libvirt_num_of_fds = -1; +static int hf_libvirt_unknown = -1; +static gint ett_libvirt = -1; + +#define XDR_PRIMITIVE_DISSECTOR(xtype, ctype, ftype) \ + static gboolean \ + dissect_xdr_##xtype(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf) \ + { \ + goffset start; \ + ctype val; \ + start = xdr_getpos(xdrs); \ + if (xdr_##xtype(xdrs, &val)) { \ + proto_tree_add_##ftype(tree, hf, tvb, start, xdr_getpos(xdrs) - start, val); \ + return TRUE; \ + } else { \ + proto_tree_add_item(tree, hf_libvirt_unknown, tvb, start, -1, ENC_NA); \ + return FALSE; \ + } \ + } + +XDR_PRIMITIVE_DISSECTOR(int, gint32, int) +XDR_PRIMITIVE_DISSECTOR(u_int, guint32, uint) +XDR_PRIMITIVE_DISSECTOR(short, gint16, int) +XDR_PRIMITIVE_DISSECTOR(u_short, guint16, uint) +XDR_PRIMITIVE_DISSECTOR(char, gchar, int) +XDR_PRIMITIVE_DISSECTOR(u_char, guchar, uint) +XDR_PRIMITIVE_DISSECTOR(hyper, gint64, int64) +XDR_PRIMITIVE_DISSECTOR(u_hyper, guint64, uint64) +XDR_PRIMITIVE_DISSECTOR(float, gfloat, float) +XDR_PRIMITIVE_DISSECTOR(double, gdouble, double) +XDR_PRIMITIVE_DISSECTOR(bool, bool_t, boolean) + +static gboolean +dissect_xdr_string(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, + guint32 maxlen) +{ + goffset start; + gchar *val = NULL; + + start = xdr_getpos(xdrs); + if (xdr_string(xdrs, &val, maxlen)) { + proto_tree_add_string(tree, hf, tvb, start, xdr_getpos(xdrs) - start, val); + xdr_free((xdrproc_t)xdr_string, (char *)&val); + return TRUE; + } else { + proto_tree_add_item(tree, hf_libvirt_unknown, tvb, start, -1, ENC_NA); + return FALSE; + } +} + +static gchar * +format_xdr_bytes(guint8 *bytes, guint32 length) +{ + gchar *buf; + guint32 i; + + if (length == 0) + return ""; + buf = ep_alloc(length*2 + 1); + for (i = 0; i < length; i++) { + /* We know that buf has enough size to contain + 2 * length + '\0' characters. */ + g_sprintf(buf, "%02x", bytes[i]); + buf += 2; + } + return buf - length*2; +} + +static gboolean +dissect_xdr_opaque(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, + guint32 size) +{ + goffset start; + gboolean rc; + guint8 *val; + + val = g_malloc(size); + start = xdr_getpos(xdrs); + if ((rc = xdr_opaque(xdrs, (caddr_t)val, size))) { + proto_tree_add_bytes_format_value(tree, hf, tvb, start, xdr_getpos(xdrs) - start, + NULL, "%s", format_xdr_bytes(val, size)); + } else { + proto_tree_add_item(tree, hf_libvirt_unknown, tvb, start, -1, ENC_NA); + } + + g_free(val); + return rc; +} + +static gboolean +dissect_xdr_bytes(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, + guint32 maxlen) +{ + goffset start; + guint8 *val = NULL; + guint32 length; + + start = xdr_getpos(xdrs); + if (xdr_bytes(xdrs, (char **)&val, &length, maxlen)) { + proto_tree_add_bytes_format_value(tree, hf, tvb, start, xdr_getpos(xdrs) - start, + NULL, "%s", format_xdr_bytes(val, length)); + /* Seems I can't call xdr_free() for this case. + It will raises SEGV by referencing out of bounds call stack */ + free(val); + return TRUE; + } else { + proto_tree_add_item(tree, hf_libvirt_unknown, tvb, start, -1, ENC_NA); + return FALSE; + } +} + +static gboolean +dissect_xdr_pointer(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, + vir_xdr_dissector_t dissect) +{ + goffset start; + bool_t not_null; + + start = xdr_getpos(xdrs); + if (!xdr_bool(xdrs, ¬_null)) { + proto_tree_add_item(tree, hf_libvirt_unknown, tvb, start, -1, ENC_NA); + return FALSE; + } + if (not_null) { + return dissect(tvb, tree, xdrs, hf); + } else { + proto_item *ti; + ti = proto_tree_add_item(tree, hf, tvb, start, xdr_getpos(xdrs) - start, ENC_NA); + proto_item_append_text(ti, ": (null)"); + return TRUE; + } +} + +static gboolean +dissect_xdr_iterable(tvbuff_t *tvb, proto_item *ti, XDR *xdrs, gint ett, int rhf, + guint32 length, vir_xdr_dissector_t dissect, goffset start) +{ + proto_tree *tree; + guint32 i; + + tree = proto_item_add_subtree(ti, ett); + for (i = 0; i < length; i++) { + if (!dissect(tvb, tree, xdrs, rhf)) + return FALSE; + } + proto_item_set_len(ti, xdr_getpos(xdrs) - start); + return TRUE; +} + +static gboolean +dissect_xdr_vector(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, gint ett, + int rhf, gchar *rtype, guint32 size, vir_xdr_dissector_t dissect) +{ + goffset start; + proto_item *ti; + + start = xdr_getpos(xdrs); + ti = proto_tree_add_item(tree, hf, tvb, start, -1, ENC_NA); + proto_item_append_text(ti, " :: %s[%u]", rtype, size); + return dissect_xdr_iterable(tvb, ti, xdrs, ett, rhf, size, dissect, start); +} + +static gboolean +dissect_xdr_array(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, gint ett, + int rhf, gchar *rtype, guint32 maxlen, vir_xdr_dissector_t dissect) +{ + goffset start; + proto_item *ti; + guint32 length; + + start = xdr_getpos(xdrs); + + if (!xdr_u_int(xdrs, &length)) + return FALSE; + if (length > maxlen) + return FALSE; + + ti = proto_tree_add_item(tree, hf, tvb, start, -1, ENC_NA); + proto_item_append_text(ti, " :: %s<%u>", rtype, length); + return dissect_xdr_iterable(tvb, ti, xdrs, ett, rhf, length, dissect, start); +} + +static vir_xdr_dissector_t +find_payload_dissector(guint32 proc, guint32 type, + const vir_dissector_index_t *pds, gsize length) +{ + const vir_dissector_index_t *pd; + guint32 first, last, direction; + + if (pds == NULL || length < 1) + return NULL; + + first = pds[0].proc; + last = pds[length-1].proc; + if (proc < first || proc > last) { + return NULL; + } + + pd = &pds[proc-first]; + /* There is no guarantee to proc numbers has no gap */ + if (pd->proc != proc) { + direction = (pd->proc < proc) ? 1 : -1; + while (pd->proc != proc) { + if (pd->proc == first || pd->proc == last) + return NULL; + pd += direction; + } + } + + switch (type) { + case VIR_NET_CALL: + case VIR_NET_CALL_WITH_FDS: + return pd->args; + case VIR_NET_REPLY: + case VIR_NET_REPLY_WITH_FDS: + return pd->ret; + case VIR_NET_MESSAGE: + return pd->msg; + } + return NULL; +} + +static void +dissect_libvirt_stream(tvbuff_t *tvb, proto_tree *tree, gint payload_length) +{ + proto_tree_add_item(tree, hf_libvirt_stream, tvb, VIR_HEADER_LEN, + payload_length - VIR_HEADER_LEN, ENC_NA); +} + +static gint32 +dissect_libvirt_num_of_fds(tvbuff_t *tvb, proto_tree *tree) +{ + gint32 nfds; + nfds = tvb_get_ntohl(tvb, VIR_HEADER_LEN); + proto_tree_add_int(tree, hf_libvirt_num_of_fds, tvb, VIR_HEADER_LEN, 4, nfds); + return nfds; +} + +static void +dissect_libvirt_fds(tvbuff_t *tvb, gint start, gint32 nfds) +{ + /* TODO: NOP for now */ +} + +static void +dissect_libvirt_payload_xdr_data(tvbuff_t *tvb, proto_tree *tree, gint payload_length, + gint32 status, vir_xdr_dissector_t dissect) +{ + gint32 nfds = 0; + gint start = VIR_HEADER_LEN; + tvbuff_t *payload_tvb; + caddr_t payload_data; + XDR xdrs; + + if (status == VIR_NET_CALL_WITH_FDS || + status == VIR_NET_REPLY_WITH_FDS) { + nfds = dissect_libvirt_num_of_fds(tvb, tree); + start += 4; + payload_length -= 4; + } + + payload_tvb = tvb_new_subset(tvb, start, -1, payload_length); + payload_data = (caddr_t)tvb_memdup(payload_tvb, 0, payload_length); + xdrmem_create(&xdrs, payload_data, payload_length, XDR_DECODE); + + dissect(payload_tvb, tree, &xdrs, -1); + + xdr_destroy(&xdrs); + g_free(payload_data); + + if (nfds != 0) { + dissect_libvirt_fds(tvb, start + payload_length, nfds); + } +} + +static void +dissect_libvirt_payload(tvbuff_t *tvb, proto_tree *tree, + guint32 prog, guint32 proc, guint32 type, guint32 status) +{ + gssize payload_length; + + payload_length = tvb_length(tvb) - VIR_HEADER_LEN; + if (payload_length <= 0) + return; /* No payload */ + + if (status == VIR_NET_OK) { + vir_xdr_dissector_t xd = find_payload_dissector(proc, type, get_program_data(prog, VIR_PROGRAM_DISSECTORS), + *(gsize *)get_program_data(prog, VIR_PROGRAM_DISSECTORS_LEN)); + if (xd == NULL) + goto unknown; + dissect_libvirt_payload_xdr_data(tvb, tree, payload_length, status, xd); + } else if (status == VIR_NET_ERROR) { + dissect_libvirt_payload_xdr_data(tvb, tree, payload_length, status, VIR_ERROR_MESSAGE_DISSECTOR); + } else if (type == VIR_NET_STREAM) { /* implicitly, status == VIR_NET_CONTINUE */ + dissect_libvirt_stream(tvb, tree, payload_length); + } else { + goto unknown; + } + return; + +unknown: + dbg("Cannot determine payload: Prog=%u, Proc=%u, Type=%u, Status=%u", prog, proc, type, status); + proto_tree_add_item(tree, hf_libvirt_unknown, tvb, VIR_HEADER_LEN, -1, ENC_NA); +} + +static void +dissect_libvirt_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + goffset offset; + guint32 prog, proc, type, serial, status; + const value_string *vs; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "Libvirt"); + col_clear(pinfo->cinfo, COL_INFO); + + offset = 4; /* End of length field */ + prog = tvb_get_ntohl(tvb, offset); offset += 4; + offset += 4; /* Ignore version header field */ + proc = tvb_get_ntohl(tvb, offset); offset += 4; + type = tvb_get_ntohl(tvb, offset); offset += 4; + serial = tvb_get_ntohl(tvb, offset); offset += 4; + status = tvb_get_ntohl(tvb, offset); offset += 4; + + col_add_fstr(pinfo->cinfo, COL_INFO, "Prog=%s", + val_to_str(prog, program_strings, "%x")); + + vs = get_program_data(prog, VIR_PROGRAM_PROCSTRINGS); + if (vs == NULL) { + col_append_fstr(pinfo->cinfo, COL_INFO, " Proc=%u", proc); + } else { + col_append_fstr(pinfo->cinfo, COL_INFO, " Proc=%s", val_to_str(proc, vs, "%d")); + } + + col_append_fstr(pinfo->cinfo, COL_INFO, " Type=%s Serial=%u Status=%s", + val_to_str(type, type_strings, "%d"), serial, + val_to_str(status, status_strings, "%d")); + + if (tree) { + gint *hf_proc; + proto_item *ti; + proto_tree *libvirt_tree; + + ti = proto_tree_add_item(tree, proto_libvirt, tvb, 0, tvb_length(tvb), ENC_NA); + libvirt_tree = proto_item_add_subtree(ti, ett_libvirt); + + offset = 0; + proto_tree_add_item(libvirt_tree, hf_libvirt_length, tvb, offset, 4, ENC_NA); offset += 4; + proto_tree_add_item(libvirt_tree, hf_libvirt_program, tvb, offset, 4, ENC_NA); offset += 4; + proto_tree_add_item(libvirt_tree, hf_libvirt_version, tvb, offset, 4, ENC_NA); offset += 4; + + hf_proc = (int *)get_program_data(prog, VIR_PROGRAM_PROCHFVAR); + if (hf_proc != NULL && *hf_proc != -1) { + proto_tree_add_item(libvirt_tree, *hf_proc, tvb, offset, 4, ENC_NA); + } else { + /* No string representation, but still useful displaying proc number */ + proto_tree_add_item(libvirt_tree, hf_libvirt_procedure, tvb, offset, 4, ENC_NA); + } + offset += 4; + + proto_tree_add_item(libvirt_tree, hf_libvirt_type, tvb, offset, 4, ENC_NA); offset += 4; + proto_tree_add_item(libvirt_tree, hf_libvirt_serial, tvb, offset, 4, ENC_NA); offset += 4; + proto_tree_add_item(libvirt_tree, hf_libvirt_status, tvb, offset, 4, ENC_NA); offset += 4; + + /* Dissect payload remaining */ + dissect_libvirt_payload(tvb, libvirt_tree, prog, proc, type, status); + } +} + +static guint32 +get_message_len(packet_info *pinfo __attribute__((unused)), tvbuff_t *tvb, int offset) +{ + return tvb_get_ntohl(tvb, offset); +} + +static void +dissect_libvirt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + /* Another magic const - 4; simply, how much bytes + * is needed to tell the length of libvirt packet. */ + tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 4, get_message_len, dissect_libvirt_message); +} + +void +proto_register_libvirt(void) +{ + static hf_register_info hf[] = { + { &hf_libvirt_length, + { "length", "libvirt.length", + FT_UINT32, BASE_DEC, + NULL, 0x0, + NULL, HFILL} + }, + { &hf_libvirt_program, + { "program", "libvirt.program", + FT_UINT32, BASE_HEX, + VALS(program_strings), 0x0, + NULL, HFILL} + }, + { &hf_libvirt_version, + { "version", "libvirt.version", + FT_UINT32, BASE_DEC, + NULL, 0x0, + NULL, HFILL} + }, + { &hf_libvirt_procedure, + { "procedure", "libvirt.procedure", + FT_INT32, BASE_DEC, + NULL, 0x0, + NULL, HFILL} + }, + { &hf_libvirt_type, + { "type", "libvirt.type", + FT_INT32, BASE_DEC, + VALS(type_strings), 0x0, + NULL, HFILL} + }, + { &hf_libvirt_serial, + { "serial", "libvirt.serial", + FT_UINT32, BASE_DEC, + NULL, 0x0, + NULL, HFILL} + }, + { &hf_libvirt_status, + { "status", "libvirt.status", + FT_INT32, BASE_DEC, + VALS(status_strings), 0x0, + NULL, HFILL} + }, + + VIR_DYNAMIC_HFSET + + { &hf_libvirt_stream, + { "stream", "libvirt.stream", + FT_BYTES, BASE_NONE, + NULL, 0x0, + NULL, HFILL} + }, + { &hf_libvirt_num_of_fds, + { "num_of_fds", "libvirt.num_of_fds", + FT_INT32, BASE_DEC, + NULL, 0x0, + NULL, HFILL} + }, + { &hf_libvirt_unknown, + { "unknown", "libvirt.unknown", + FT_BYTES, BASE_NONE, + NULL, 0x0, + NULL, HFILL} + }, + }; + + static gint *ett[] = { + VIR_DYNAMIC_ETTSET + &ett_libvirt + }; + + proto_libvirt = proto_register_protocol( + "Libvirt", /* name */ + "libvirt", /* short name */ + "libvirt" /* abbrev */ + ); + + proto_register_field_array(proto_libvirt, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); +} + +void +proto_reg_handoff_libvirt(void) +{ + static dissector_handle_t libvirt_handle; + + libvirt_handle = create_dissector_handle(dissect_libvirt, proto_libvirt); + dissector_add_uint("tcp.port", LIBVIRT_PORT, libvirt_handle); +} diff --git a/tools/wireshark/src/packet-libvirt.h b/tools/wireshark/src/packet-libvirt.h new file mode 100644 index 0000000000..0cab63728e --- /dev/null +++ b/tools/wireshark/src/packet-libvirt.h @@ -0,0 +1,128 @@ +/* packet-libvirt.h --- Libvirt packet dissector header file. + * + * Copyright (C) 2013 Yuto KAWAMURA(kawamuray) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Yuto KAWAMURA(kawamuray) + */ +#ifndef _PACKET_LIBVIRT_H_ +# define _PACKET_LIBVIRT_H_ + +# ifndef LIBVIRT_PORT +# define LIBVIRT_PORT 16509 +# endif + +# define VIR_HEADER_LEN 28 + +# ifdef DEBUG +# define dbg(fmt, ...) \ + g_print("[LIBVIRT] " fmt " at " __FILE__ " line %d\n", ##__VA_ARGS__, __LINE__) +# else +# define dbg(fmt, ...) +# endif + +typedef gboolean (*vir_xdr_dissector_t)(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); + +typedef struct vir_dissector_index vir_dissector_index_t; +struct vir_dissector_index { + guint32 proc; + vir_xdr_dissector_t args; + vir_xdr_dissector_t ret; + vir_xdr_dissector_t msg; +}; + +enum vir_net_message_type { + VIR_NET_CALL = 0, + VIR_NET_REPLY = 1, + VIR_NET_MESSAGE = 2, + VIR_NET_STREAM = 3, + VIR_NET_CALL_WITH_FDS = 4, + VIR_NET_REPLY_WITH_FDS = 5, +}; + +enum vir_net_message_status { + VIR_NET_OK = 0, + VIR_NET_ERROR = 1, + VIR_NET_CONTINUE = 2, +}; + +enum vir_program_data_index { + VIR_PROGRAM_PROCHFVAR, + VIR_PROGRAM_PROCSTRINGS, + VIR_PROGRAM_DISSECTORS, + VIR_PROGRAM_DISSECTORS_LEN, + VIR_PROGRAM_LAST, +}; + +static const value_string type_strings[] = { + { VIR_NET_CALL, "CALL" }, + { VIR_NET_REPLY, "REPLY" }, + { VIR_NET_MESSAGE, "MESSAGE" }, + { VIR_NET_STREAM, "STREAM" }, + { VIR_NET_CALL_WITH_FDS, "CALL_WITH_FDS" }, + { VIR_NET_REPLY_WITH_FDS, "REPLY_WITH_FDS" }, + { -1, NULL } +}; + +static const value_string status_strings[] = { + { VIR_NET_OK, "OK" }, + { VIR_NET_ERROR, "ERROR" }, + { VIR_NET_CONTINUE, "CONTINUE" }, + { -1, NULL } +}; + +/* TODO: These symbols will automatically included in generated headers in the feature */ +# define VIR_SECURITY_MODEL_BUFLEN (256 + 1) +# define VIR_SECURITY_LABEL_BUFLEN (4096 + 1) +# define VIR_SECURITY_DOI_BUFLEN (256 + 1) +# define VIR_UUID_BUFLEN (16) +enum { + VIR_TYPED_PARAM_INT = 1, /* integer case */ + VIR_TYPED_PARAM_UINT = 2, /* unsigned integer case */ + VIR_TYPED_PARAM_LLONG = 3, /* long long case */ + VIR_TYPED_PARAM_ULLONG = 4, /* unsigned long long case */ + VIR_TYPED_PARAM_DOUBLE = 5, /* double case */ + VIR_TYPED_PARAM_BOOLEAN = 6, /* boolean(character) case */ + VIR_TYPED_PARAM_STRING = 7, /* string case */ +}; +/* / */ + +# define VIR_ERROR_MESSAGE_DISSECTOR dissect_xdr_remote_error + +static gboolean dissect_xdr_int(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_u_int(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_short(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_u_short(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_char(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_u_char(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_hyper(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_u_hyper(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_float(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_double(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_bool(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_string(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, guint32 maxlen); +static gboolean dissect_xdr_opaque(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, guint32 size); +static gboolean dissect_xdr_bytes(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, guint32 maxlen); +static gboolean dissect_xdr_pointer(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, + vir_xdr_dissector_t dp); +static gboolean dissect_xdr_vector(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, gint ett, + int rhf, gchar *rtype, guint32 size, vir_xdr_dissector_t dp); +static gboolean dissect_xdr_array(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, gint ett, + int rhf, gchar *rtype, guint32 maxlen, vir_xdr_dissector_t dp); + +# include "libvirt/protocol.h" + +#endif /* _PACKET_LIBVIRT_H_ */ diff --git a/tools/wireshark/util/genxdrstub.pl b/tools/wireshark/util/genxdrstub.pl new file mode 100755 index 0000000000..877c3bbdcf --- /dev/null +++ b/tools/wireshark/util/genxdrstub.pl @@ -0,0 +1,1011 @@ +#!/usr/bin/env perl +# genxdrstub.pl --- Generate C header file which used by packet-libvirt.[ch] +# +# Copyright (C) 2013 Yuto KAWAMURA(kawamuray) +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library. If not, see +# . +# +# Author: Yuto KAWAMURA(kawamuray) +# +# For XDR syntax, see http://tools.ietf.org/html/rfc4506#section-6.3 +# This script does not strictly check syntax of xdr protocol specification. +# Make sure the specification files you have are correctly compilable with rpcgen(1). +# If something fails with this script in spite of you had confirmed that the `make' with libvirt was succeed, +# please report your error output to kawamuray. +use strict; +use warnings; +use File::Spec; + +my $DEBUG = 0; # Enable if you want to see debug output +sub dbg { print STDERR @_ if $DEBUG } + +die "ERROR: No arguments" unless @ARGV; + +# Context object referenced from entire this script +my $c = Context->new; + +for my $proto (@ARGV) { + # We need to do this heuristic parsing to determine + # variable name of enum _procedures. + my ($name) = $proto =~ m{(?:vir)?([^/]+?)_?protocol\.x$}; + unless ($name) { + warn "WARNING: Cannot extract protocol name from $proto, skipping."; + next; + } + $c->add_to_set(progs => $name); + + my $source; + { + open my $fh, '<', $proto + or die "Cannot open $proto: $!"; + local $/; + $source = <$fh>; + close $fh; + } + + $c->add_header_file($name, sub { + dbg "*** Start parsing $proto\n"; + my @lexs = Lexicalizer->parse($source); + for my $lex (@lexs) { + next if $lex->ident eq "enum $name\_procedure"; + + if ($lex->isa('Sym::Variable')) { + $c->print(sprintf "#define %s (%s)\n", $lex->ident, $lex->value); + } elsif ($lex->isa('Sym::Type')) { + # Top level of name path is type identification of itself + $lex->define_dissector($lex->idstrip); + } else { + die "Unkown lexical appeared: $lex"; + } + } + + my $procs = $c->symbol("enum $name\_procedure") + or die "Cannot find procedures enumeration: enum $name\_procedure"; + # Procedure numbers are expected to be containing gaps, but needed to be sorted in ascending order. + my @procedures = sort { $a->value <=> $b->value } @{ $procs->members }; + my @dissectors = map { + (my $ident = lc($_->ident)) =~ s/^$name\_proc/$name/; + +{ + value => $_->value, + map { $_ => $c->rinc($c->symbols->{"$ident\_$_"} ? "dissect_xdr_$ident\_$_" : 'NULL') } + qw{ args ret msg } + }; + } @procedures; + $c->print(PT->render('code.dissectorlist', { + name => $name, + dissectors => \@dissectors, + })); + $c->print(PT->render('code.procedure_strings', { + name => $name, + procedures => \@procedures, + })); + }); +} + +$c->add_header_file('protocol', sub { + for my $prog (@{ $c->get_set('progs') }) { + $c->print("#include \"libvirt/$prog.h\"\n"); + } + + # hf_ variables set + $c->print(PT->render('macro.hfvars', { + programs => $c->get_set('progs'), + hfvars => [ grep $_->{segment}{refcnt}, @{ $c->get_set('hfvars') } ], + })); + # ett_ variables set + $c->print(PT->render('macro.ettvars', { + ettvars => [ map $_->{sym}, grep $_->{refcnt}, @{ $c->get_set('ettvars') } ], + })); + # value_string program_strings + $c->print(PT->render('code.program_strings', { programs => $c->get_set('progs') })); + $c->print("static int hf_$_\_procedure = -1;\n") for @{ $c->get_set('progs') }; + $c->print(PT->render('code.program_data', { programs => $c->get_set('progs') })); +}); + +$c->finalize; exit 0; + +# Used for handy class building +sub register_profile { + my %prof = @_; + my $caller = caller; + no strict 'refs'; + if ($prof{isa}) { + push @{ "$caller\::ISA" }, $prof{isa}; + } + while (my ($name, $v) = each %{ $prof{consts} || {} }) { + *{ "$caller\::$name" } = sub { $v }; + } + for my $attr (@{ $prof{attrs} || [] }) { + *{ "$caller\::$attr" } = sub { + if (@_ > 1) { $_[0]->{$attr} = $_[1]; $_[0] } + else { $_[0]->{$attr} } + }; + } + while (my ($klass, $meths) = each %{ $prof{roles} || {} }) { + for my $meth (@$meths) { + # This assignment cannot be like: *{ "$caller\::$meth" } = \&{ "$klass\::$meth" }. + # "$klass\::$meth" maybe not defined yet(e.g. Methods defined by PT) + *{ "$caller\::$meth" } = sub { goto &{ "$klass\::$meth" } }; + } + } +} + +# Minimal template engine for code generating +package PT; # is PicoTemplate +our $Token; +our %Templates; +INIT { # Load templates from __END__ section + $Token = join '', map { chr(65 + rand(26)) } 1..64; + my $current; + while (my $l = ) { + if ($l =~ /^\@\@\s*(.+)/) { + $current = \($Templates{$1} = ''); + } else { + $$current .= $l if $current; + } + } + for my $name (keys %Templates) { + $Templates{$name} = __PACKAGE__->compile($Templates{$name}); + if ($name =~ /^([\w:]+)#([^#]+)$/) { + no strict 'refs'; + my $meth = "$1\::$2"; + unless (defined &$meth) { + *$meth = $Templates{$name}; + } + } + } +} +sub compile { + my ($class, $tmpl) = @_; + + $tmpl =~ s{<%(=)?(.*?)%>\n?|((?:(?!<%).)+)}{ + $2 ? $1 ? "\$$Token .= qq{\@{[do{ $2 }]}};" : $2 + : "\$$Token .= substr <<$Token, 0, -1;\n".quotemeta($3)."\n$Token\n"; + }gse; + eval "sub { my \$$Token = ''; $tmpl \$$Token }" + or die "ERROR: Cannot compile template: $@"; +} +sub render { + my ($class, $name, $vars, @args) = @_; + local $_ = $vars || {}; + my $renderer = $Templates{$name} + or die "No such template: $name"; + $renderer->(@args); +} +# / package PT + +package Sym; +BEGIN{::register_profile( + attrs => [qw[ ident ]], +)} + +sub new { + my ($class, %args) = @_; + + CORE::bless \%args, $class; +} + +sub bless { + my ($self, $klass) = @_; + + CORE::bless $self, "Sym::$klass" + if ref($self) ne "Sym::$klass"; + $self; +} + +sub idstrip { + my $ident = shift()->ident; + $ident =~ s/^(?:struct|enum|union)\s+// if $ident; + $ident; +} +# / package Sym + +package Sym::Type; +BEGIN{::register_profile( + isa => 'Sym', + attrs => [qw[ alias ]], +)} + +sub is_primitive { !(shift)->alias } + +sub dealias { + my ($self) = @_; + + $self->is_primitive ? $self : $self->alias->dealias; +} + +sub xdr_type { + my ($self) = @_; + + if (!$self->is_primitive) { + return $self->dealias->xdr_type; + } + + my $type = ref $self; + if ($type eq __PACKAGE__) { + $type = $self->ident; + } else { + $type =~ s/^.*:://; + } + uc($type); +} + +sub render_caller { + my ($self, $hfid) = @_; + my $name = $c->rinc( 'dissect_xdr_'.($self->idstrip || lc($self->xdr_type)) ); + "$name(tvb, tree, xdrs, hf)"; +} + +sub ft_type { + my ($self) = @_; + return $self->dealias->ft_type unless $self->is_primitive; + my $xt = $self->xdr_type; + +{ + INT => 'INT32', + U_INT => 'UINT32', + SHORT => 'INT16', + U_SHORT => 'UINT16', + CHAR => 'INT8', + U_CHAR => 'UINT8', + HYPER => 'INT64', + U_HYPER => 'UINT64', + BOOL => 'BOOLEAN', + }->{$xt} || $xt; +} + +sub hf_base { + my ($self) = @_; + $self->is_primitive + ? $self->ft_type =~ /INT/ ? 'DEC' : 'NONE' + : $self->dealias->hf_base; +} + +sub define_dissector { + my ($self, @path) = @_; + $self->declare_hfvar(@path); + my $path = join '__', @path; + my $code = $self->render_dissector($path); + $c->print({ sym => "dissect_xdr_$path", body => $code }) + if $code; +} + +sub declare_hfvar { + my ($self, @path) = @_; + my $path = join '__', @path; + $c->add_to_set(hfvars => { + segment => $c->print({ + sym => "hf_$path", + body => "static int hf_$path = -1;\n" + }), + name => $path[-1], + abbrev => join('.', @path), + ft_type => $self->ft_type, + hf_base => $self->hf_base, + }); +} +# / package Sym + +package Sym::Type::HasAnonTypes; # Types which possibly have anonymous subtypes +BEGIN{::register_profile( + isa => 'Sym::Type', +)} + +sub declare_anontypes { + my ($self, @path) = @_; + + for my $m (@{ $self->members }) { + unless (defined $m->type->ident) { + $m->type->ident(join '__', @path, $m->ident); + } + $m->type->define_dissector(@path, $m->ident); + } +} + +sub define_dissector { + my ($self, @path) = @_; + + $self->declare_anontypes(@path); + $self->SUPER::define_dissector(@path); +} + +package Sym::Type::HasSubtree; # Types which should be declare ett variables + +sub declare_ettvar { + my ($self) = @_; + my $ettvar = 'ett_'.$self->idstrip; + $c->add_to_set(ettvars => $c->print({ + sym => $ettvar, + body => "static gint $ettvar = -1;\n", + })); +} + +package Sym::Type::HasReference; # Types which references subtype +BEGIN{::register_profile( + attrs => [qw[ reftype ]], + consts => { ft_type => 'NONE' }, +)} + +sub render_caller { + my ($self) = @_; + my ($klass) = ref($self) =~ /([^:]+)$/; + sprintf '%s(tvb, tree, xdrs, hf, %s)', + $c->rinc('dissect_xdr_'.lc($klass)), + $c->rinc('dissect_xdr_'.$self->reftype->idstrip); +} + +package Sym::Type::HasLength; # Types which has length attribute +BEGIN{::register_profile( + attrs => [qw[ length ]], + consts => { ft_type => 'NONE' }, +)} + +sub render_caller { + my ($self, $hfid) = @_; + my ($klass) = ref($self) =~ /([^:]+)$/; + sprintf '%s(tvb, tree, xdrs, hf, %s)', + $c->rinc('dissect_xdr_'.lc($klass)), $self->length || '~0'; +} + +package Sym::Type::Struct; +BEGIN{::register_profile( + isa => 'Sym::Type', + attrs => [qw[ members ]], + consts => { ft_type => 'NONE' }, + roles => { + 'Sym::Type::HasAnonTypes' => [qw[ declare_anontypes ]], + 'Sym::Type::HasSubtree' => [qw[ declare_ettvar ]], + }, +)} + +sub define_dissector { + my ($self, @path) = @_; + $self->declare_anontypes(@path); + $self->declare_ettvar; + $self->SUPER::define_dissector(@path); +} + +package Sym::Type::Enum; +BEGIN{::register_profile( + isa => 'Sym::Type', + attrs => [qw[ members ]], + consts => { ft_type => 'UINT32' }, +)} +package Sym::Type::Union; +BEGIN{::register_profile( + isa => 'Sym::Type', + attrs => [qw[ decl case_specs ]], + consts => { ft_type => 'NONE' }, + roles => { + 'Sym::Type::HasAnonTypes' => [qw[ declare_anontypes define_dissector ]], + }, +)} +sub members { + my ($self) = @_; + [ map { $_->[1] } @{ $self->case_specs } ]; +} + +package Sym::Type::String; +BEGIN{::register_profile( + isa => 'Sym::Type', + consts => { ft_type => 'STRING' }, + roles => { + 'Sym::Type::HasLength' => [qw[ length render_caller ]], + }, +)} +package Sym::Type::Opaque; +BEGIN{::register_profile( + isa => 'Sym::Type', + consts => { ft_type => 'BYTES' }, + roles => { + 'Sym::Type::HasLength' => [qw[ length render_caller ]], + }, +)} +package Sym::Type::Bytes; +BEGIN{::register_profile( + isa => 'Sym::Type', + consts => { ft_type => 'BYTES' }, + roles => { + 'Sym::Type::HasLength' => [qw[ length render_caller ]], + }, +)} +package Sym::Type::Pointer; +BEGIN{::register_profile( + isa => 'Sym::Type', + roles => { + 'Sym::Type::HasReference' => [qw[ reftype render_caller ]], + }, +)} +sub ft_type { (shift)->reftype->ft_type } + +package Sym::Type::Array; # a.k.a Variable-Length Array +BEGIN{::register_profile( + isa => 'Sym::Type', + roles => { + 'Sym::Type::HasLength' => [qw[ length ft_type ]], + 'Sym::Type::HasReference' => [qw[ reftype ]], + 'Sym::Type::HasSubtree' => [qw[ declare_ettvar ]], + }, +)} + +sub render_caller { + my ($self, $hfid) = @_; + my ($pname) = reverse split /__/, $hfid; + sprintf 'dissect_xdr_array(tvb, tree, xdrs, hf, %s, %s, "%s", %s, %s)', + $c->rinc('ett_'.$self->idstrip), + $c->rinc("hf_$hfid\__$pname"), + $self->reftype->idstrip, + $self->length || '~0', + $c->rinc('dissect_xdr_'.$self->reftype->idstrip); +} + +sub define_dissector { + my ($self, @path) = @_; + $self->reftype->declare_hfvar(@path, $path[-1]); + $self->declare_ettvar; + $self->SUPER::define_dissector(@path); +} + +package Sym::Type::Vector; # a.k.a Fixed-Length Array +BEGIN{::register_profile( + isa => 'Sym::Type', + roles => { + 'Sym::Type::HasLength' => [qw[ length ft_type ]], + 'Sym::Type::HasReference' => [qw[ reftype ]], + 'Sym::Type::Array' => [qw[ define_dissector ]], + 'Sym::Type::HasSubtree' => [qw[ declare_ettvar ]], + }, +)} + +sub render_caller { + my ($self, $hfid) = @_; + my ($pname) = reverse split /__/, $hfid; + sprintf 'dissect_xdr_vector(tvb, tree, xdrs, hf, %s, %s, "%s", %s, %s)', + $c->rinc('ett_'.$self->idstrip), + $c->rinc("hf_$hfid\__$pname"), + $self->reftype->idstrip, + $self->length || '~0', + $c->rinc('dissect_xdr_'.$self->reftype->idstrip); +} + +package Sym::Variable; +BEGIN{::register_profile( + isa => 'Sym', + attrs => [qw[ type value ]], +)} + +package Context; +BEGIN{::register_profile( + attrs => [qw[ symbols ]], +)} + +sub new { + my ($class) = @_; + + bless { + symbols => {}, + segments => {}, + }, $class; +} + +sub symbol { + my ($self, $ident) = @_; + my $sym = $self->symbols->{$ident} ||= Sym->new; + $sym->ident($ident); + # In XDR syntax specification, defining struct/enum/union will automatically + # create alias having symbol which excludes its prefix type specifier. + # e.g: + # struct foo { int bar; }; will convert to: + # struct foo { int bar; }; typedef struct foo foo; + if ($ident =~ s/^(?:struct|enum|union)\s+//) { + $self->symbol($ident)->bless('Type')->alias($sym); + } + $sym; +} + +sub add_to_set { + my ($self, $set, @elems) = @_; + $self->{sets} ||= {}; + $self->{sets}{$set} ||= []; + push @{ $self->{sets}{$set} }, @elems; +} + +sub get_set { + my ($self, $set) = @_; + $self->{sets}{$set} || []; +} + +# $c->print(...string...); # Does work as regular 'print' +# $c->print({ sym => symbol, body => ...string... }); +# Does treat segment as code block should be referenced. +# It will not printed unless it is referenced from other code by $c->rinc(); +sub print { + my $self = shift; + my $content; + if (ref $_[0]) { + $content = $self->{segments}{ $_[0]{sym} } ||= $_[0]; + $content->{refcnt} //= 0; + $content->{body} = $_[0]{body}; + } else { + $content = join '', @_; + } + push @{ $self->{header_contents} }, $content; + $content; +} + +sub rinc { + my ($self, $sym) = @_; + ($self->{segments}{$sym} ||= { sym => $sym, refcnt => 0 })->{refcnt}++; + $sym; +} + +sub add_header_file { + my ($self, $name, $block) = @_; + + $self->{headers} ||= []; + + local $self->{header_contents} = []; + $self->print("/* *DO NOT MODIFY* this file directly.\n"); + $self->print(" * This file was generated by $0 from libvirt version $ENV{LIBVIRT_VERSION} */\n"); + my $ucname = uc $name; + $self->print("#ifndef _$ucname\_H_\n"); + $self->print("#define _$ucname\_H_\n"); + $block->(); + $self->print("#endif /* _$ucname\_H_ */"); + push @{ $self->{headers} }, [ $name, delete $self->{header_contents} ]; +} + +sub finalize { + my ($self) = @_; + + # Referenced from macro defined in packet-libvirt.h + $self->rinc('dissect_xdr_remote_error'); + + for my $header (@{ $self->{headers} || [] }) { + my ($name, $contents) = @$header; + my $file = File::Spec->catfile($ENV{PWD}, 'libvirt', "$name.h"); + open my $fh, '>', $file + or die "Cannot open file $file: $!"; + CORE::print $fh map { ref($_) ? ($_->{refcnt} ? $_->{body} : ()) : $_ } @$contents; + CORE::print $fh "\n"; + close $fh; + } +} +# / package Context + +package Lexicalizer; +our $Depth; + +INIT { # Wrap all lexicalizer subroutine by debugger function + $Depth = 0; + no strict 'refs'; + no warnings 'redefine'; + for my $name (keys %{ __PACKAGE__.'::' }) { + next if $name =~ /^(?:parse|adv)$/; + my $fullname = __PACKAGE__."::$name"; + next unless defined &$fullname; + my $sub = \&$fullname; + *$fullname = sub { + my (undef, undef, $line) = caller; + ::dbg ' 'x($Depth*2), "$name L$line", "\n"; + local $Depth = $Depth + 1; + $sub->(@_); + }; + } +} + +# Check if passed regexp does match to next token and advance position. +# Return matched string if matched. Die else. +sub adv { + my ($rx) = @_; + ::dbg ' 'x($Depth*2+1), "- adv( $rx ) = "; + # Remove Comments Comments C++ style, PP directives + s{\A(?:\s*(?:/\*.*?\*/|(?://|%).*?(?:\n+|\z)))*\s*}{}s; + if (s/^(?:$rx)//s) { + ::dbg "'$&'\n"; + return $&; + } + ::dbg "UNMATCH\n"; + die; +} + +sub lexor { + my $snapshot = $_; + while (my $handler = shift) { + my $ret = eval { $handler->() }; + if (defined $ret) { + return $ret; + } + $_ = $snapshot; + } + die; +} + +sub decimal_constant { + adv '\-?[0-9]+'; +} + +sub hexadecimal_constant { + adv '\-?0x[0-9A-Fa-f]+'; +} + +sub octal_constant { + adv '\-?0[0-9]+'; +} + +sub constant { + lexor \&hexadecimal_constant, \&octal_constant, \&decimal_constant; +} + +sub identifier { + adv '[_a-zA-Z][_a-zA-Z0-9]*'; +} + +sub value { + lexor \&constant, \&identifier; +} + +sub enum_type_spec { + adv 'enum'; + my $body = lexor \&enum_body, \&identifier; + if (ref $body eq 'ARRAY') { + Sym::Type::Enum->new(members => $body); + } else { + $c->symbol("enum $body")->bless('Type::Enum'); + } +} + +sub enum_body { + adv '{'; + my @members; + do { + my $ident = identifier(); + adv '='; + my $value = value(); + push @members, $c->symbol($ident)->bless('Variable')->value($value); + } while adv('[},]') eq ','; + \@members; +} + +sub struct_type_spec { + adv 'struct'; + my $body = lexor \&struct_body, \&identifier; + if (ref $body eq 'ARRAY') { + Sym::Type::Struct->new(members => $body); + } else { + $c->symbol("struct $body")->bless('Type::Struct'); + } +} + +sub struct_body { + adv '{'; + local $c->{symbols} = { %{ $c->{symbols} } }; + my @members; + while (my $decl = lexor \&declaration, sub { adv('}') }) { + last if $decl eq '}'; + adv ';'; + push @members, $decl; + } + \@members; +} + +sub case_spec { + my @cases; + while (my $case = eval { adv 'case' }) { + push @cases, value(); + adv ':'; + } + my $decl = declaration(); + adv ';'; + [ \@cases, $decl ]; +} + +sub union_type_spec { + adv 'union'; + local $c->{symbols} = { %{ $c->{symbols} } }; + my $body = lexor \&union_body, \&identifier; + if (ref $body eq 'ARRAY') { + Sym::Type::Union->new(decl => $body->[0], case_specs => $body->[1]); + } else { + $c->symbol("union $body")->bless('Type::Union'); + } +} + +sub union_body { + adv 'switch'; adv '\('; + my $decl = declaration(); + adv '\)'; adv '{'; + my @case_specs; + while (my $spec = eval { case_spec() }) { + push @case_specs, $spec; + } + # TODO: parse default + adv '}'; + [ $decl, \@case_specs ]; +} + +sub constant_def { + adv 'const'; + my $ident = identifier(); + adv '='; + my $value = lexor \&constant, \&identifier; + adv ';'; + + $c->symbol($ident)->bless('Variable')->value($value); +} + +sub type_def { + my $ret = lexor sub { + adv 'typedef'; + my $var = declaration(); + my $type = $var->type; + $var->bless('Type')->alias($type); + }, sub { + adv 'enum'; + my $ident = identifier(); + my $body = enum_body(); + $c->symbol("enum $ident")->bless('Type::Enum')->members($body); + }, sub { + adv 'struct'; + my $ident = identifier(); + my $body = struct_body(); + $c->symbol("struct $ident")->bless('Type::Struct')->members($body); + }, sub { + adv 'union'; + my $ident = identifier(); + my $body = union_body(); + $c->symbol("union $ident")->bless('Type::Union') + ->decl($body->[0])->case_specs($body->[1]); + }; + adv ';'; + $ret; +} + +sub type_specifier { + lexor sub { + my $ts = adv '(?:unsigned\s+)?(?:int|hyper|char|short)|float|double|quadruple|bool'; + $ts =~ s/^unsigned\s+/u_/; + $c->symbol($ts)->bless('Type'); + }, \&enum_type_spec, \&struct_type_spec, \&union_type_spec, sub { + my $ident = identifier(); + $c->symbol($ident)->bless('Type'); + }; +} + +sub declaration { + lexor sub { + my $type = lexor sub { + my $type = adv 'opaque|string'; + my $klass = ucfirst $type; + "Sym::Type::$klass"->new; + }, \&type_specifier; + my $ident = identifier(); + # I know that type 'string' does not accept '[]'(fixed length), but I don't care about that + if (my $ex = eval { adv '[<\[]' }) { + my $value = eval { value() }; + die if !$value && $ex ne '<'; # Length could be null if it is variable length + + adv($ex eq '<' ? '>' : '\]'); + if (ref($type) eq 'Sym::Type') { # Expect Array or Vector + my $vtype = ($ex eq '<') ? 'Array' : 'Vector'; + $type = "Sym::Type::$vtype"->new(length => $value, reftype => $type); + } else { + $type->length($value); + $type->bless('Type::Bytes') if $type->isa('Sym::Type::Opaque') && $ex eq '<'; + } + } elsif ($type->can('length')) { # Found String or Opaque but not followed by length specifier + die; + } + + $c->symbol($ident)->bless('Variable')->type($type); + }, sub { + my $type = type_specifier(); + adv '\*'; + my $ident = identifier(); + + $c->symbol($ident)->bless('Variable')->type( + Sym::Type::Pointer->new(reftype => $type)); + }, sub { + adv 'void'; + $c->symbol('void')->bless('Type'); + }; +} + +sub definition { + lexor \&type_def, \&constant_def; +} + +sub parse { + my ($class, $source) = @_; + + my $nlines = @{[$source =~ /\n/g]}; + my @lexs; + while ($source =~ /\S/s) { + (local $_ = $source) =~ s/\A\s*//s; + my $lex = eval { definition() }; + if (!$lex) { + my $line = $nlines - @{[/\n/g]} + 1; + my ($near) = /\A((?:.+?\n){0,5})/s; + die "ERROR: Unexpected character near line $line.\n", + "Please check debug output by enabling \$DEBUG flag at top of script.\n", + join("\n", map { ">> $_" } split /\n/, $near); + } + ::dbg ' 'x($Depth*2), sprintf "*** Found %s<%s>\n", ref($lex), $lex->ident; + push @lexs, $lex; + $source = $_; + } + @lexs; +} + +# Followings are code templates handled by PT +__END__<is_primitive; +%> +static gboolean dissect_xdr_<%= $ident %>(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf) +{ + return <%= $self->dealias->render_caller($self->ident eq $ident ? undef : $ident) %>; +} +@@ Sym::Type::Struct#render_dissector +<% my ($self, $ident) = @_; + my $hfvar = $c->rinc('hf_'.$self->idstrip); +%> +static gboolean dissect_xdr_<%= $ident %>(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf) +{ + goffset start; + proto_item *ti; + + start = xdr_getpos(xdrs); + if (hf == -1) { + ti = proto_tree_add_item(tree, <%= $hfvar %>, tvb, start, -1, ENC_NA); + } else { + header_field_info *hfinfo; + hfinfo = proto_registrar_get_nth(<%= $hfvar %>); + ti = proto_tree_add_item(tree, hf, tvb, start, -1, ENC_NA); + proto_item_append_text(ti, " :: %s", hfinfo->name); + } + tree = proto_item_add_subtree(ti, <%= $c->rinc('ett_'.$self->idstrip) %>); +<% for my $m (@{ $self->members }) { %> + + hf = <%= $c->rinc('hf_'.$ident.'__'.$m->ident) %>; + if (!<%= $m->type->render_caller($ident.'__'.$m->ident) %>) return FALSE; +<% } %> + proto_item_set_len(ti, xdr_getpos(xdrs) - start); + return TRUE; +} +@@ Sym::Type::Enum#render_dissector +<% my ($self, $ident) = @_; %> +static gboolean dissect_xdr_<%= $ident %>(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf) +{ + goffset start; + enum { DUMMY } es; + + start = xdr_getpos(xdrs); + if (xdr_enum(xdrs, (enum_t *)&es)) { + switch ((guint)es) { +<% for my $m (@{ $self->members }) { %> + case <%= $m->value %>: + proto_tree_add_uint_format_value(tree, hf, tvb, start, xdr_getpos(xdrs) - start, (guint)es, "<%= $m->idstrip %>(<%= $m->value %>)"); + return TRUE; +<% } %> + } + } else { + proto_tree_add_text(tree, tvb, start, -1, "(unknown)"); + } + return FALSE; +} +@@ Sym::Type::Union#render_dissector +<% +my ($self, $ident) = @_; +my $decl_type = $self->decl->type->idstrip; +%> +static gboolean dissect_xdr_<%= $ident %>(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf) +{ + gboolean rc = TRUE; + goffset start; + <%= $decl_type %> type = 0; + + start = xdr_getpos(xdrs); + if (!xdr_<%= $decl_type %>(xdrs, &type)) + return FALSE; + switch (type) { +<% for my $cs (@{ $self->case_specs }) { + my ($vals, $decl) = @$cs; +%> +<% for my $v (@$vals) { %> + case <%= $v %>: +<% } %> + hf = <%= $c->rinc('hf_'.$ident.'__'.$decl->ident) %>; + rc = <%= $decl->type->render_caller($ident.'__'.$decl->ident) %>; break; +<% } %> + } + if (!rc) { + proto_tree_add_text(tree, tvb, start, -1, "(unknown)"); + } + return rc; +} +@@ macro.hfvars +#define VIR_DYNAMIC_HFSET \ +<% for my $prog (@{ $_->{programs} }) { %> + { &hf_<%= $prog %>_procedure,\ + { "procedure", "libvirt.procedure",\ + FT_INT32, BASE_DEC,\ + VALS(<%= $prog %>_procedure_strings), 0x0,\ + NULL, HFILL}\ + },\ +<% } %> +<% for my $hf (@{ $_->{hfvars} }) { %> + { &<%= $hf->{segment}{sym} %>,\ + { "<%= $hf->{name} %>", "libvirt.<%= $hf->{abbrev} %>",\ + FT_<%= $hf->{ft_type} %>, BASE_<%= $hf->{hf_base} %>,\ + NULL, 0x0,\ + NULL, HFILL}\ + },\ +<% } %> +/* End of #define VIR_DYNAMIC_HFSET */ + +@@ macro.ettvars +#define VIR_DYNAMIC_ETTSET \ +<% for my $ett (@{ $_->{ettvars} }) { %> +&<%= $ett %>,\ +<% } %> +/* End of #define VIR_DYNAMIC_ETTSET */ + +@@ code.dissectorlist +static const vir_dissector_index_t <%= $_->{name} %>_dissectors[] = { +<% for my $d (@{ $_->{dissectors} }) { %> + { <%= $d->{value} %>, <%= $d->{args} %>, <%= $d->{ret} %>, <%= $d->{msg} %> }, +<% } %> +}; +static const gsize <%= $_->{name} %>_dissectors_len = array_length(<%= $_->{name} %>_dissectors); +@@ code.procedure_strings +static const value_string <%= $_->{name} %>_procedure_strings[] = { +<% for my $proc (@{ $_->{procedures} }) { + my $ident = $proc->ident; + $ident =~ s/^$_->{name}_proc_//i; +%> + { <%= $proc->value %>, "<%= $ident %>" }, +<% } %> + { 0, NULL } +}; +@@ code.program_strings +static const value_string program_strings[] = { +<% for my $prog (map uc, @{ $_->{programs} }) { %> + { <%= $c->symbol("$prog\_PROGRAM")->value %>, "<%= $prog %>" }, +<% } %> + { 0, NULL } +}; +@@ code.program_data +static const void *program_data[][VIR_PROGRAM_LAST] = { +<% for my $p (@{ $_->{programs} }) { %> + { &hf_<%= $p %>_procedure, <%= $p %>_procedure_strings, <%= $p %>_dissectors, &<%= $p %>_dissectors_len }, +<% } %> +}; + +static const void * +get_program_data(guint32 prog, enum vir_program_data_index index) +{ + if (index < VIR_PROGRAM_LAST) { + switch (prog) { +<% my $i = 0; %> +<% for my $prog (@{ $_->{programs} }) { %> + case <%= uc($prog) %>_PROGRAM: + return program_data[<%= $i++ %>][index]; +<% } %> + } + } + return NULL; +} diff --git a/tools/wireshark/util/make-dissector-reg b/tools/wireshark/util/make-dissector-reg new file mode 100755 index 0000000000..4f8b4f2c67 --- /dev/null +++ b/tools/wireshark/util/make-dissector-reg @@ -0,0 +1,198 @@ +#! /bin/sh +# 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, see . +# +# Copied from Wireshark(http://www.wireshark.org/) + +# +# The first argument is the directory in which the source files live. +# +srcdir="$1" +shift + +# +# The second argument is either "plugin" or "dissectors"; if it's +# "plugin", we build a plugin.c for a plugin, and if it's +# "dissectors", we build a register.c for libwireshark. +# +registertype="$1" +shift +if [ "$registertype" = plugin ] +then + outfile="plugin.c" +elif [ "$registertype" = dissectors ] +then + outfile="register.c" +else + echo "Unknown output type '$registertype'" 1>&2 + exit 1 +fi + +# +# All subsequent arguments are the files to scan. +# +rm -f ${outfile}-tmp +echo '/* Do not modify this file. */' >${outfile}-tmp +echo '/* It is created automatically by the Makefile. */'>>${outfile}-tmp +if [ "$registertype" = plugin ] +then + cat <<"EOF" >>${outfile}-tmp +#include "config.h" + +#include + +/* plugins are DLLs */ +#define WS_BUILD_DLL +#include "ws_symbol_export.h" + +#ifndef ENABLE_STATIC +WS_DLL_PUBLIC_NOEXTERN const gchar version[] = VERSION; + +/* Start the functions we need for the plugin stuff */ + +WS_DLL_PUBLIC_NOEXTERN void +plugin_register (void) +{ +EOF +# +# Build code to call all the protocol registration routines. +# +for f in "$@" +do + if [ -f $f ] + then + srcfile=$f + else + srcfile=$srcdir/$f + fi + grep '^proto_register_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';' +done | sed -e 's/^.*://' -e 's/^\([a-z_0-9A-Z]*\).*/ {extern void \1 (void); \1 ();}/' >>${outfile}-tmp +for f in "$@" +do + if [ -f $f ] + then + srcfile=$f + else + srcfile=$srcdir/$f + fi + grep '^void proto_register_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';' +done | sed -e 's/^.*://' -e 's/^void \([a-z_0-9A-Z]*\).*/ {extern void \1 (void); \1 ();}/' >>${outfile}-tmp +else + cat <<"EOF" >>${outfile}-tmp +#include "register.h" +void +register_all_protocols(register_cb cb, gpointer client_data) +{ +EOF +# +# Build code to call all the protocol registration routines. +# +for f in "$@" +do + if [ -f $f ] + then + srcfile=$f + else + srcfile=$srcdir/$f + fi + grep '^proto_register_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';' +done | sed -e 's/^.*://' -e 's/^\([a-z_0-9A-Z]*\).*/ {extern void \1 (void); if(cb) (*cb)(RA_REGISTER, \"\1\", client_data); \1 ();}/' >>${outfile}-tmp +for f in "$@" +do + if [ -f $f ] + then + srcfile=$f + else + srcfile=$srcdir/$f + fi + grep '^void proto_register_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';' +done | sed -e 's/^.*://' -e 's/^void \([a-z_0-9A-Z]*\).*/ {extern void \1 (void); if(cb) (*cb)(RA_REGISTER, \"\1\", client_data); \1 ();}/' >>${outfile}-tmp + +fi +echo '}' >>${outfile}-tmp + + +# +# Build code to call all the protocol handoff registration routines. +# +if [ "$registertype" = plugin ] +then + cat <<"EOF" >>${outfile}-tmp +WS_DLL_PUBLIC_NOEXTERN void +plugin_reg_handoff(void) +{ +EOF +for f in "$@" +do + if [ -f $f ] + then + srcfile=$f + else + srcfile=$srcdir/$f + fi + grep '^proto_reg_handoff_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';' +done | sed -e 's/^.*://' -e 's/^\([a-z_0-9A-Z]*\).*/ {extern void \1 (void); \1 ();}/' >>${outfile}-tmp +for f in "$@" +do + if [ -f $f ] + then + srcfile=$f + else + srcfile=$srcdir/$f + fi + grep '^void proto_reg_handoff_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';' +done | sed -e 's/^.*://' -e 's/^void \([a-z_0-9A-Z]*\).*/ {extern void \1 (void); \1 ();}/' >>${outfile}-tmp +else + cat <<"EOF" >>${outfile}-tmp +void +register_all_protocol_handoffs(register_cb cb, gpointer client_data) +{ +EOF +for f in "$@" +do + if [ -f $f ] + then + srcfile=$f + else + srcfile=$srcdir/$f + fi + grep '^proto_reg_handoff_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';' +done | sed -e 's/^.*://' -e 's/^\([a-z_0-9A-Z]*\).*/ {extern void \1 (void); if(cb) (*cb)(RA_HANDOFF, \"\1\", client_data); \1 ();}/' >>${outfile}-tmp +for f in "$@" +do + if [ -f $f ] + then + srcfile=$f + else + srcfile=$srcdir/$f + fi + grep '^void proto_reg_handoff_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';' +done | sed -e 's/^.*://' -e 's/^void \([a-z_0-9A-Z]*\).*/ {extern void \1 (void); if(cb) (*cb)(RA_HANDOFF, \"\1\", client_data); \1 ();}/' >>${outfile}-tmp +fi +echo '}' >>${outfile}-tmp +if [ "$registertype" = plugin ] +then + echo '#endif' >>${outfile}-tmp +else + cat <<"EOF" >>${outfile}-tmp +gulong register_count(void) +{ +EOF + proto_regs=`grep RA_REGISTER ${outfile}-tmp | wc -l` + handoff_regs=`grep RA_HANDOFF ${outfile}-tmp | wc -l` + echo " return $proto_regs + $handoff_regs;" >>${outfile}-tmp + echo '}' >>${outfile}-tmp +fi + +# Only overwrite outfile if it differs from newly generated file +mv ${outfile}-tmp ${outfile} -- GitLab