aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/README.heuristic10
-rw-r--r--epan/packet.c24
-rw-r--r--epan/packet.h6
-rw-r--r--epan/packet_info.h1
-rw-r--r--epan/wslua/init_wslua.c96
-rw-r--r--epan/wslua/wslua.h4
-rw-r--r--epan/wslua/wslua_internals.c40
-rw-r--r--epan/wslua/wslua_pinfo.c19
-rw-r--r--epan/wslua/wslua_proto.c134
-rw-r--r--epan/wslua/wslua_tvb.c6
-rw-r--r--test/captures/dns_port.pcapbin671 -> 1318 bytes
-rw-r--r--test/lua/dissector.lua117
-rw-r--r--test/lua/proto.lua77
-rw-r--r--test/lua/verify_dissector.lua18
-rwxr-xr-xtest/suite-wslua.sh20
15 files changed, 540 insertions, 32 deletions
diff --git a/doc/README.heuristic b/doc/README.heuristic
index 544a64d484..c2830a82c6 100644
--- a/doc/README.heuristic
+++ b/doc/README.heuristic
@@ -103,7 +103,7 @@ Heuristic Code Example
----------------------
You can find a lot of code examples in the Wireshark sources, e.g.:
grep -l heur_dissector_add epan/dissectors/*.c
-returns 132 files (Feb 2013).
+returns 150 files (March 2014).
For the above example criteria, the following code example might do the work
(combine this with the dissector skeleton in README.developer):
@@ -150,7 +150,10 @@ dissect_PROTOABBREV_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, vo
/* Assume it's your packet ... */
- /* specify that dissect_PROTOABBREV is to be called directly from now on for packets for this "connection" ... */
+ /* specify that dissect_PROTOABBREV is to be called directly from now on for packets for this "connection" ...
+ * but ONLY do this if your heuristic sits directly on top of UDP or TCP (ie, you did heur_dissector_add("tcp",...)),
+ * otherwise you'll be overriding the dissector that called your heuristic dissector.
+ */
conversation = find_or_create_conversation(pinfo);
conversation_set_dissector(conversation, PROTOABBREV_handle);
@@ -188,5 +191,4 @@ tendency to reuse known port numbers for new protocols. But TCP and UDP are
not the only dissectors that provide support for HDs. You can find more
examples by searching the Wireshark sources as follows:
grep -l register_heur_dissector_list epan/dissectors/packet-*.c
-returns 38 files (Feb 2013).
-
+returns 43 files (March 2014).
diff --git a/epan/packet.c b/epan/packet.c
index e249c113fd..9ec656cb30 100644
--- a/epan/packet.c
+++ b/epan/packet.c
@@ -1831,6 +1831,11 @@ find_heur_dissector_list(const char *name)
return (heur_dissector_list_t *)g_hash_table_lookup(heur_dissector_lists, name);
}
+gboolean
+has_heur_dissector_list(const gchar *name) {
+ return (find_heur_dissector_list(name) != NULL);
+}
+
void
heur_dissector_add(const char *name, heur_dissector_t dissector, const int proto)
{
@@ -1859,6 +1864,7 @@ heur_dissector_add(const char *name, heur_dissector_t dissector, const int proto
hdtbl_entry = g_slice_new(heur_dtbl_entry_t);
hdtbl_entry->dissector = dissector;
hdtbl_entry->protocol = find_protocol_by_id(proto);
+ hdtbl_entry->list_name = g_strdup(name);
hdtbl_entry->enabled = TRUE;
/* do the table insertion */
@@ -1892,6 +1898,7 @@ heur_dissector_delete(const char *name, heur_dissector_t dissector, const int pr
found_entry = g_slist_find_custom(*sub_dissectors, (gpointer) &hdtbl_entry, find_matching_heur_dissector);
if (found_entry) {
+ g_free(((heur_dtbl_entry_t *)(found_entry->data))->list_name);
g_slice_free(heur_dtbl_entry_t, found_entry->data);
*sub_dissectors = g_slist_delete_link(*sub_dissectors, found_entry);
}
@@ -1924,7 +1931,8 @@ dissector_try_heuristic(heur_dissector_list_t sub_dissectors, tvbuff_t *tvb,
packet_info *pinfo, proto_tree *tree, void *data)
{
gboolean status;
- const char *saved_proto;
+ const char *saved_curr_proto;
+ const char *saved_heur_list_name;
GSList *entry;
heur_dtbl_entry_t *hdtbl_entry;
guint16 saved_can_desegment;
@@ -1944,7 +1952,8 @@ dissector_try_heuristic(heur_dissector_list_t sub_dissectors, tvbuff_t *tvb,
pinfo->can_desegment = saved_can_desegment-(saved_can_desegment>0);
status = FALSE;
- saved_proto = pinfo->current_proto;
+ saved_curr_proto = pinfo->current_proto;
+ saved_heur_list_name = pinfo->heur_list_name;
saved_layers_len = wmem_list_count(pinfo->layers);
@@ -1962,6 +1971,8 @@ dissector_try_heuristic(heur_dissector_list_t sub_dissectors, tvbuff_t *tvb,
}
if (hdtbl_entry->protocol != NULL) {
+ /* do NOT change this behavior - wslua uses the protocol short name set here in order
+ to determine which Lua-based heurisitc dissector to call */
pinfo->current_proto =
proto_get_protocol_short_name(hdtbl_entry->protocol);
@@ -1971,6 +1982,9 @@ dissector_try_heuristic(heur_dissector_list_t sub_dissectors, tvbuff_t *tvb,
*/
wmem_list_append(pinfo->layers, GINT_TO_POINTER(proto_get_id(hdtbl_entry->protocol)));
}
+
+ pinfo->heur_list_name = hdtbl_entry->list_name;
+
EP_CHECK_CANARY(("before calling heuristic dissector for protocol: %s",
proto_get_protocol_filter_name(proto_get_id(hdtbl_entry->protocol))));
if ((*hdtbl_entry->dissector)(tvb, pinfo, tree, data)) {
@@ -1992,8 +2006,10 @@ dissector_try_heuristic(heur_dissector_list_t sub_dissectors, tvbuff_t *tvb,
}
}
}
- pinfo->current_proto = saved_proto;
- pinfo->can_desegment=saved_can_desegment;
+
+ pinfo->current_proto = saved_curr_proto;
+ pinfo->heur_list_name = saved_heur_list_name;
+ pinfo->can_desegment = saved_can_desegment;
return status;
}
diff --git a/epan/packet.h b/epan/packet.h
index 438e4158b7..4958f396f3 100644
--- a/epan/packet.h
+++ b/epan/packet.h
@@ -338,7 +338,8 @@ typedef GSList *heur_dissector_list_t;
typedef struct {
heur_dissector_t dissector;
- protocol_t *protocol;
+ protocol_t *protocol; /* this entry's protocol */
+ gchar *list_name; /* the list name this entry is in the list of */
gboolean enabled;
} heur_dtbl_entry_t;
@@ -354,6 +355,9 @@ WS_DLL_PUBLIC void register_heur_dissector_list(const char *name,
WS_DLL_PUBLIC void dissector_all_heur_tables_foreach_table (DATFunc_heur_table func,
gpointer user_data);
+/* true if a heur_dissector list of that anme exists to be registered into */
+WS_DLL_PUBLIC gboolean has_heur_dissector_list(const gchar *name);
+
/** Try all the dissectors in a given heuristic dissector list. This is done,
* until we find one that recognizes the protocol.
* Call this while the parent dissector running.
diff --git a/epan/packet_info.h b/epan/packet_info.h
index f8f8108485..23bae69f7b 100644
--- a/epan/packet_info.h
+++ b/epan/packet_info.h
@@ -179,6 +179,7 @@ typedef struct _packet_info {
struct epan_session *epan;
nstime_t rel_ts; /**< Relative timestamp (yes, it can be negative) */
const gchar *pkt_comment; /**< NULL if not available */
+ const gchar *heur_list_name; /**< name of heur list if this packet is being heuristically dissected */
} packet_info;
/** @} */
diff --git a/epan/wslua/init_wslua.c b/epan/wslua/init_wslua.c
index 2d778b9b68..c37602f82f 100644
--- a/epan/wslua/init_wslua.c
+++ b/epan/wslua/init_wslua.c
@@ -47,10 +47,13 @@ static wslua_plugin *wslua_plugin_list = NULL;
static lua_State* L = NULL;
+/* XXX: global variables? Really?? Yuck. These could be done differently,
+ using the Lua registry */
packet_info* lua_pinfo;
struct _wslua_treeitem* lua_tree;
tvbuff_t* lua_tvb;
-int lua_dissectors_table_ref;
+int lua_dissectors_table_ref = LUA_NOREF;
+int lua_heur_dissectors_table_ref = LUA_NOREF;
static int proto_lua = -1;
static expert_field ei_lua_error = EI_INIT;
@@ -133,6 +136,95 @@ int dissect_lua(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data
}
+/** Type of a heuristic dissector, used in heur_dissector_add().
+ *
+ * @param tvb the tvbuff with the (remaining) packet data
+ * @param pinfo the packet info of this packet (additional info)
+ * @param tree the protocol tree to be build or NULL
+ * @return TRUE if the packet was recognized by the sub-dissector (stop dissection here)
+ */
+gboolean heur_dissect_lua(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data _U_) {
+ gboolean result = FALSE;
+ lua_tvb = tvb;
+ lua_pinfo = pinfo;
+
+ if (!tvb || !pinfo || !pinfo->heur_list_name || !pinfo->current_proto) {
+ report_failure("internal error in heur_dissect_lua: NULL packet info");
+ return FALSE;
+ }
+
+ /* heuristic functions are stored in a table in the registry; the registry has a
+ * table at reference lua_heur_dissectors_table_ref, and that table has keys for
+ * the heuristic listname (e.g., "udp", "tcp", etc.), and that key's value is a
+ * table of keys of the Proto->name, and their value is the function.
+ * So it's like registry[table_ref][heur_list_name][proto_name] = func
+ */
+
+ lua_settop(L,0);
+
+ /* get the table of all lua heuristic dissector lists */
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lua_heur_dissectors_table_ref);
+
+ /* get the table inside that, for the lua heuristic dissectors of the requested heur list */
+ if (!wslua_get_table(L, -1, pinfo->heur_list_name)) {
+ /* this shouldn't happen */
+ lua_settop(L,0);
+ report_failure("internal error in heur_dissect_lua: no %s heur list table", pinfo->heur_list_name);
+ return FALSE;
+ }
+
+ /* get the table inside that, for the specific lua heuristic dissector */
+ if (!wslua_get_field(L,-1,pinfo->current_proto)) {
+ /* this shouldn't happen */
+ lua_settop(L,0);
+ report_failure("internal error in heur_dissect_lua: no %s heuristic dissector for list %s",
+ pinfo->current_proto, pinfo->heur_list_name);
+ return FALSE;
+ }
+
+ /* remove the table of all lists (the one in the registry) */
+ lua_remove(L,1);
+ /* remove the heur_list_name heur list table */
+ lua_remove(L,1);
+
+ if (!lua_isfunction(L,-1)) {
+ /* this shouldn't happen */
+ lua_settop(L,0);
+ report_failure("internal error in heur_dissect_lua: %s heuristic dissector is not a function", pinfo->current_proto);
+ return FALSE;
+ }
+
+ lua_tree = (struct _wslua_treeitem *)g_malloc(sizeof(struct _wslua_treeitem));
+ lua_tree->tree = tree;
+ lua_tree->item = proto_tree_add_text(tree,tvb,0,0,"lua fake item");
+ lua_tree->expired = FALSE;
+ PROTO_ITEM_SET_HIDDEN(lua_tree->item);
+
+ push_Tvb(L,tvb);
+ push_Pinfo(L,pinfo);
+ push_TreeItem(L,lua_tree);
+
+ if ( lua_pcall(L,3,1,0) ) {
+ report_failure(" error calling %s heuristic dissector: %s", pinfo->current_proto, lua_tostring(L,-1));
+ lua_settop(L,0);
+ } else {
+ if (lua_isboolean(L, -1) || lua_isnil(L, -1)) {
+ result = lua_toboolean(L, -1);
+ } else {
+ report_failure(" invalid return value from Lua %s heuristic dissector", pinfo->current_proto);
+ }
+ lua_pop(L, 1);
+ }
+
+ register_frame_end_routine(pinfo, lua_frame_end);
+
+ lua_pinfo = NULL;
+ lua_tree = NULL;
+ lua_tvb = NULL;
+
+ return result;
+}
+
static void iter_table_and_call(lua_State* LS, const gchar* table_name, lua_CFunction error_handler) {
lua_settop(LS,0);
@@ -454,6 +546,8 @@ int wslua_init(register_cb cb, gpointer client_data) {
/* the dissectors table goes in the registry (not accessible) */
lua_newtable (L);
lua_dissectors_table_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+ lua_newtable (L);
+ lua_heur_dissectors_table_ref = luaL_ref(L, LUA_REGISTRYINDEX);
/* the preferences apply_cb table (accessible by the user) */
lua_newtable (L);
diff --git a/epan/wslua/wslua.h b/epan/wslua/wslua.h
index 9a41a714f0..840c61a28c 100644
--- a/epan/wslua/wslua.h
+++ b/epan/wslua/wslua.h
@@ -552,6 +552,7 @@ extern tvbuff_t* lua_tvb;
extern dissector_handle_t lua_data_handle;
extern gboolean lua_initialized;
extern int lua_dissectors_table_ref;
+extern int lua_heur_dissectors_table_ref;
WSLUA_DECLARE_CLASSES()
WSLUA_DECLARE_FUNCTIONS()
@@ -565,8 +566,11 @@ extern const gchar* lua_shiftstring(lua_State* L,int idx);
extern void wslua_setfuncs(lua_State *L, const luaL_Reg *l, int nup);
extern const gchar* wslua_typeof_unknown;
extern const gchar* wslua_typeof(lua_State *L, int idx);
+extern gboolean wslua_get_table(lua_State *L, int idx, const gchar *name);
+extern gboolean wslua_get_field(lua_State *L, int idx, const gchar *name);
extern void wslua_assert_table_field_new(lua_State *L, int idx, const gchar *name);
extern int dissect_lua(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data);
+extern int heur_dissect_lua(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data);
extern void wslua_prefs_changed(void);
extern void proto_register_lua(void);
extern GString* lua_register_all_taps(void);
diff --git a/epan/wslua/wslua_internals.c b/epan/wslua/wslua_internals.c
index 50cd8a3d40..15d62845ff 100644
--- a/epan/wslua/wslua_internals.c
+++ b/epan/wslua/wslua_internals.c
@@ -99,13 +99,17 @@ WSLUA_API void wslua_setfuncs(lua_State *L, const luaL_Reg *l, int nup) {
lua_pop(L, nup); /* remove upvalues */
}
-/* identical to lua_getfield but without triggering metamethods */
+/* identical to lua_getfield but without triggering metamethods
+ warning: cannot be used directly with negative index (and shouldn't be changed to)
+ decrement your negative index if you want to use this */
static void lua_rawgetfield(lua_State *L, int idx, const char *k) {
lua_pushstring(L, k);
lua_rawget(L, idx);
}
-/* identical to lua_setfield but without triggering metamethods */
+/* identical to lua_setfield but without triggering metamethods
+ warning: cannot be used with negative index (and shouldn't be changed to)
+ decrement your negative index if you want to use this */
static void lua_rawsetfield (lua_State *L, int idx, const char *k) {
lua_pushstring(L, k);
lua_insert(L, -2);
@@ -140,6 +144,38 @@ const gchar* wslua_typeof(lua_State *L, int idx) {
return classname;
}
+/* this gets a Lua table of the given name, from the table at the given
+ * location idx. If it does not get a table, it pops whatever it got
+ * and returns false.
+ * warning: cannot be used with pseudo-indeces like LUA_REGISTRYINDEX
+ */
+gboolean wslua_get_table(lua_State *L, int idx, const gchar *name) {
+ gboolean result = TRUE;
+ if (idx < 0) idx--;
+ lua_rawgetfield(L, idx, name);
+ if (!lua_istable(L,-1)) {
+ lua_pop(L,1);
+ result = FALSE;
+ }
+ return result;
+}
+
+/* this gets a table field of the given name, from the table at the given
+ * location idx. If it does not get a field, it pops whatever it got
+ * and returns false.
+ * warning: cannot be used with pseudo-indeces like LUA_REGISTRYINDEX
+ */
+gboolean wslua_get_field(lua_State *L, int idx, const gchar *name) {
+ gboolean result = TRUE;
+ if (idx < 0) idx--;
+ lua_rawgetfield(L, idx, name);
+ if (lua_isnil(L,-1)) {
+ lua_pop(L,1);
+ result = FALSE;
+ }
+ return result;
+}
+
/* This verifies/asserts that field 'name' doesn't already exist in table at location idx.
* If it does, this EXITS wireshark, because this is a fundamental programming error.
* As such, this function is only useful for special circumstances, notably
diff --git a/epan/wslua/wslua_pinfo.c b/epan/wslua/wslua_pinfo.c
index 13a2a33a7a..c5cbaa3802 100644
--- a/epan/wslua/wslua_pinfo.c
+++ b/epan/wslua/wslua_pinfo.c
@@ -36,6 +36,7 @@
#include "wslua.h"
#include <epan/addr_resolv.h>
+#include <epan/conversation.h>
#include <string.h>
@@ -1128,6 +1129,23 @@ static int Pinfo_get_lo(lua_State *L) {
return 1;
}
+/* WSLUA_ATTRIBUTE Pinfo_conversation WO sets the packet conversation to the given Proto object */
+static int Pinfo_set_conversation(lua_State *L) {
+ Pinfo pinfo = checkPinfo(L,1);
+ Proto proto = checkProto(L,2);
+ conversation_t *conversation;
+
+ if (!proto->handle) {
+ luaL_error(L,"Proto %s has no registered dissector", proto->name? proto->name:"<UKNOWN>");
+ return 0;
+ }
+
+ conversation = find_or_create_conversation(pinfo->ws_pinfo);
+ conversation_set_dissector(conversation,proto->handle);
+
+ return 0;
+}
+
/* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */
static int Pinfo__gc(lua_State* L) {
Pinfo pinfo = toPinfo(L,1);
@@ -1183,6 +1201,7 @@ WSLUA_ATTRIBUTES Pinfo_attributes[] = {
WSLUA_ATTRIBUTE_ROREG(Pinfo,fragmented),
WSLUA_ATTRIBUTE_ROREG(Pinfo,match_uint),
WSLUA_ATTRIBUTE_ROREG(Pinfo,match_string),
+ WSLUA_ATTRIBUTE_WOREG(Pinfo,conversation),
{ NULL, NULL, NULL }
};
diff --git a/epan/wslua/wslua_proto.c b/epan/wslua/wslua_proto.c
index 1f1e320cc9..172e049c40 100644
--- a/epan/wslua/wslua_proto.c
+++ b/epan/wslua/wslua_proto.c
@@ -1458,6 +1458,76 @@ WSLUA_FUNCTION wslua_register_postdissector(lua_State* L) {
return 0;
}
+WSLUA_METHOD Proto_register_heuristic(lua_State* L) {
+ /* Registers a heristic dissector function for this protocol, for the given heristic list name.
+ When later called, the passed-in function will be given (1) a Tvb object, (2) a Pinfo object,
+ and (3) a TreeItem object. The function must return true if the payload is for it, else false.
+ The function should perform as much verification as possible to ensure the payload is for it,
+ and dissect the packet (including setting TreeItem info and such) only if the payload is for it,
+ before returning true or false.*/
+#define WSLUA_ARG_Proto_register_heuristic_LISTNAME 2 /* the heristic list name this function is a heuristic for (e.g., "udp" or "infiniband.payload") */
+#define WSLUA_ARG_Proto_register_heuristic_FUNC 3 /* a Lua function that will be invoked for heuristic dissection */
+ Proto proto = checkProto(L,1);
+ const gchar *listname = luaL_checkstring(L, WSLUA_ARG_Proto_register_heuristic_LISTNAME);
+ const gchar *proto_name = proto->name;
+ const int top = lua_gettop(L);
+
+ if (!proto_name || proto->hfid == -1) {
+ /* this shouldn't happen - internal bug if it does */
+ luaL_error(L,"Proto_register_heuristic: got NULL proto name or invalid hfid");
+ return 0;
+ }
+
+ /* verify listname has a heuristic list */
+ if (!has_heur_dissector_list(listname)) {
+ luaL_error(L, "there is no heuristic list for '%s'", listname);
+ return 0;
+ }
+
+ /* heuristic functions are stored in a table in the registry; the registry has a
+ * table at reference lua_heur_dissectors_table_ref, and that table has keys for
+ * the heuristic listname (e.g., "udp", "tcp", etc.), and that key's value is a
+ * table of keys of the Proto->name, and their value is the function.
+ * So it's like registry[table_ref][heur_list_name][proto_name] = func
+ */
+ if (lua_isfunction(L,WSLUA_ARG_Proto_register_heuristic_FUNC)) {
+ /* insert the heur dissector into the heur dissectors table */
+ lua_rawgeti(L, LUA_REGISTRYINDEX, lua_heur_dissectors_table_ref);
+ /* the heuristic lists table is now at -1 */
+ if (!lua_istable(L,-1)) {
+ /* this shouldn't be possible */
+ luaL_error(L,"Proto_register_heuristic: could not get lua_heur_dissectors table from registry");
+ return 0;
+ }
+
+ if (!wslua_get_table(L,-1,listname)) {
+ /* no one's registered a lua heuristic for this list, so make a new list table */
+ lua_newtable(L);
+ lua_pushvalue(L,-1); /* duplicate the table so we can set it as a field */
+ lua_setfield(L,-3,listname); /* sets this new list table into the lists table */
+ }
+ else if (wslua_get_field(L,-1,proto_name)) {
+ luaL_error(L,"A heuristic dissector for Proto '%s' is already registered for the '%s' list", proto_name, listname);
+ return 0;
+ }
+
+ /* copy the func, set it as the value for key proto_name in listname's table */
+ lua_pushvalue(L,WSLUA_ARG_Proto_register_heuristic_FUNC);
+ lua_setfield(L,-2,proto_name);
+
+ /* ok, we're done with lua stuff, pop what we added to the stack */
+ lua_pop(L,2); /* pop the lists table and the listname table */
+ g_assert(top == lua_gettop(L));
+
+ /* now register the single/common heur_dissect_lua function */
+ heur_dissector_add(listname, heur_dissect_lua, proto->hfid);
+
+ } else {
+ luaL_argerror(L,3,"The heuristic dissector must be a function");
+ }
+ return 0;
+}
+
/* WSLUA_ATTRIBUTE Proto_dissector RW The protocol's dissector, a function you define.
The called dissector function will be given three arguments of (1) a Tvb object, (2) a Pinfo object, and (3) a TreeItem object. */
static int Proto_get_dissector(lua_State* L) {
@@ -1602,6 +1672,7 @@ WSLUA_ATTRIBUTES Proto_attributes[] = {
WSLUA_METHODS Proto_methods[] = {
WSLUA_CLASS_FNREG(Proto,new),
+ WSLUA_CLASS_FNREG(Proto,register_heuristic),
{ NULL, NULL }
};
@@ -1780,6 +1851,14 @@ WSLUA_METHOD Dissector_call(lua_State* L) {
return 0;
}
+WSLUA_METAMETHOD Dissector__call(lua_State* L) {
+ /* Calls a dissector against a given packet (or part of it) */
+#define WSLUA_ARG_Dissector__call_TVB 2 /* The buffer to dissect */
+#define WSLUA_ARG_Dissector__call_PINFO 3 /* The packet info */
+#define WSLUA_ARG_Dissector__call_TREE 4 /* The tree on which to add the protocol items */
+ return Dissector_call(L);
+}
+
WSLUA_METAMETHOD Dissector__tostring(lua_State* L) {
/* Gets the Dissector's protocol short name */
Dissector d = checkDissector(L,1);
@@ -1803,6 +1882,7 @@ WSLUA_METHODS Dissector_methods[] = {
WSLUA_META Dissector_meta[] = {
WSLUA_CLASS_MTREG(Dissector,tostring),
+ WSLUA_CLASS_MTREG(Dissector,call),
{ NULL, NULL }
};
@@ -1856,6 +1936,58 @@ WSLUA_CONSTRUCTOR DissectorTable_new (lua_State *L) {
return 0;
}
+/* this struct is used for passing ourselves user_data through dissector_all_tables_foreach_table() */
+typedef struct dissector_tables_foreach_table_info {
+ int num;
+ lua_State *L;
+} dissector_tables_foreach_table_info_t;
+
+/* this is the DATFunc_table function used for dissector_all_tables_foreach_table()
+ so we can get all dissector_table names. This pushes the name into a table at stack index 1 */
+static void
+dissector_tables_list_func(const gchar *table_name, const gchar *ui_name _U_, gpointer user_data) {
+ dissector_tables_foreach_table_info_t *data = (dissector_tables_foreach_table_info_t*) user_data;
+ lua_pushstring(data->L, table_name);
+ lua_rawseti(data->L, 1, data->num);
+ data->num = data->num + 1;
+}
+
+WSLUA_CONSTRUCTOR DissectorTable_list (lua_State *L) {
+ /* Gets a Lua array table of all DissectorTable names - i.e., the string names you can
+ use for the first argument to DissectorTable.get().
+ NOTE: this is an expensive operation, and should only be used for troubleshooting. */
+ dissector_tables_foreach_table_info_t data = { 1, L };
+
+ lua_newtable(L);
+
+ dissector_all_tables_foreach_table(dissector_tables_list_func, (gpointer)&data, (GCompareFunc)compare_dissector_key_name);
+
+ WSLUA_RETURN(1); /* The array table of registered DissectorTable names */
+}
+
+/* this is the DATFunc_heur_table function used for dissector_all_heur_tables_foreach_table()
+ so we can get all heuristic dissector list names. This pushes the name into a table at stack index 1 */
+static void
+heur_dissector_tables_list_func(const gchar *table_name, gpointer table _U_, gpointer user_data) {
+ dissector_tables_foreach_table_info_t *data = (dissector_tables_foreach_table_info_t*) user_data;
+ lua_pushstring(data->L, table_name);
+ lua_rawseti(data->L, 1, data->num);
+ data->num = data->num + 1;
+}
+
+WSLUA_CONSTRUCTOR DissectorTable_heuristic_list (lua_State *L) {
+ /* Gets a Lua array table of all heuristic list names - i.e., the string names you can
+ use for the first argument in Proto:register_heuristic().
+ NOTE: this is an expensive operation, and should only be used for troubleshooting. */
+ dissector_tables_foreach_table_info_t data = { 1, L };
+
+ lua_newtable(L);
+
+ dissector_all_heur_tables_foreach_table(heur_dissector_tables_list_func, (gpointer)&data);
+
+ WSLUA_RETURN(1); /* The array table of registered heuristic list names */
+}
+
WSLUA_CONSTRUCTOR DissectorTable_get (lua_State *L) {
/*
Obtain a reference to an existing dissector table.
@@ -2218,6 +2350,8 @@ static int DissectorTable__gc(lua_State* L _U_) {
WSLUA_METHODS DissectorTable_methods[] = {
WSLUA_CLASS_FNREG(DissectorTable,new),
WSLUA_CLASS_FNREG(DissectorTable,get),
+ WSLUA_CLASS_FNREG(DissectorTable,list),
+ WSLUA_CLASS_FNREG(DissectorTable,heuristic_list),
WSLUA_CLASS_FNREG(DissectorTable,add),
WSLUA_CLASS_FNREG(DissectorTable,set),
WSLUA_CLASS_FNREG(DissectorTable,remove),
diff --git a/epan/wslua/wslua_tvb.c b/epan/wslua/wslua_tvb.c
index c4746bc97f..1dae9c3461 100644
--- a/epan/wslua/wslua_tvb.c
+++ b/epan/wslua/wslua_tvb.c
@@ -495,7 +495,7 @@ static int Tvb__gc(lua_State* L) {
}
WSLUA_METHOD Tvb_reported_len(lua_State* L) {
- /* Obtain the reported length of a TVB */
+ /* Obtain the reported (not captured) length of a TVB */
Tvb tvb = checkTvb(L,1);
lua_pushnumber(L,tvb_reported_length(tvb->ws_tvb));
@@ -503,7 +503,7 @@ WSLUA_METHOD Tvb_reported_len(lua_State* L) {
}
WSLUA_METHOD Tvb_len(lua_State* L) {
- /* Obtain the length of a TVB */
+ /* Obtain the actual (captured) length of a TVB */
Tvb tvb = checkTvb(L,1);
lua_pushnumber(L,tvb_length(tvb->ws_tvb));
@@ -511,7 +511,7 @@ WSLUA_METHOD Tvb_len(lua_State* L) {
}
WSLUA_METHOD Tvb_reported_length_remaining(lua_State* L) {
- /* Obtain the reported length of packet data to end of a TVB or -1 if the offset is beyond the end of the TVB */
+ /* Obtain the reported (not captured) length of packet data to end of a TVB or -1 if the offset is beyond the end of the TVB */
#define Tvb_reported_length_remaining_OFFSET 2 /* offset */
Tvb tvb = checkTvb(L,1);
int offset = luaL_optint(L, Tvb_reported_length_remaining_OFFSET, 0);
diff --git a/test/captures/dns_port.pcap b/test/captures/dns_port.pcap
index 73bff58172..c19a79c8c9 100644
--- a/test/captures/dns_port.pcap
+++ b/test/captures/dns_port.pcap
Binary files differ
diff --git a/test/lua/dissector.lua b/test/lua/dissector.lua
index 635cdb5d49..632ae87f9b 100644
--- a/test/lua/dissector.lua
+++ b/test/lua/dissector.lua
@@ -35,8 +35,24 @@
-- automagically do it without doing "Decode As ...".
--
----------------------------------------
---print("Wireshark version = "..get_version())
---print("Lua version = ".._VERSION)
+-- debug printer, set DEBUG to true to enable printing debug info
+-- set DEBUG2 to true to enable really verbose printing
+local DEBUG, DEBUG2 = false, false
+
+local dprint = function() end
+local dprint2 = function() end
+if DEBUG or DEBUG2 then
+ dprint = function(...)
+ print(table.concat({"Lua:", ...}," "))
+ end
+
+ if DEBUG2 then
+ dprint2 = dprint
+ end
+end
+
+dprint2("Wireshark version = ", get_version())
+dprint2("Lua version = ", _VERSION)
----------------------------------------
-- Unfortunately, the older Wireshark/Tshark versions have bugs, and part of the point
@@ -177,6 +193,10 @@ local getQueryName
-- Whenever Wireshark dissects a packet that our Proto is hooked into, it will call
-- this function and pass it these arguments for the packet it's dissecting.
function dns.dissector(tvbuf,pktinfo,root)
+ dprint2("dns.dissector called")
+
+ -- set the protocol column to show our protocol name
+ pktinfo.cols.protocol:set("MYDNS")
-- We want to check that the packet size is rational during dissection, so let's get the length of the
-- packet buffer (Tvb).
@@ -195,6 +215,7 @@ function dns.dissector(tvbuf,pktinfo,root)
-- since we're going to add this protocol to a specific UDP port, we're going to
-- assume packets in this port are our protocol, so the packet being too short is an error
tree:add_expert_info(PI_MALFORMED, PI_ERROR, "packet too short")
+ dprint("packet length",pktlen,"too short")
return
end
@@ -216,7 +237,7 @@ function dns.dissector(tvbuf,pktinfo,root)
-- for our flags field, we want a sub-tree
local flag_tree = tree:add(pf_flags, flagrange)
- -- I'm indenting this for calarity, because it's adding to the flag's child-tree
+ -- I'm indenting this for clarity, because it's adding to the flag's child-tree
-- let's add the type of message (query vs. response)
flag_tree:add(pf_flag_response, flagrange)
@@ -312,16 +333,101 @@ function dns.dissector(tvbuf,pktinfo,root)
end
end
+ dprint2("dns.dissector returning",pos)
+
-- tell wireshark how much of tvbuff we dissected
return pos
end
----------------------------------------
--- we want to have our protocol disseciton invoked for a specific UDP port,
--- so get the udp dissecotr table and add our protocol to it
+-- we want to have our protocol dissection invoked for a specific UDP port,
+-- so get the udp dissector table and add our protocol to it
local udp_encap_table = DissectorTable.get("udp.port")
udp_encap_table:add(MYDNS_PROTO_UDP_PORT, dns)
+----------------------------------------
+-- we also want to add the heuristic dissector, for any UDP protocol
+-- first we need a heuristic dissection function
+-- this is that function - when wireshark invokes this, it will pass in the same
+-- things it passes in to the "dissector" function, but we only want to actually
+-- dissect it if it's for us, and we need to return true if it's for us, or else false
+-- figuring out if it's for us or not is not easy
+-- we need to try as hard as possible, or else we'll think it's for us when it's
+-- not and block other heuristic dissectors from getting their chanc
+--
+-- in practice, you'd never set a dissector like this to be heuristic, because there
+-- just isn't enough information to safely detect if it's DNS or not
+-- but I'm doing it to show how it would be done
+--
+-- Note: this heuristic stuff is new in 1.11.3
+local function heur_dissect_dns(tvbuf,pktinfo,root)
+ dprint2("heur_dissect_dns called")
+
+ if tvbuf:len() < DNS_HDR_LEN then
+ dprint("heur_dissect_dns: tvb shorter than DNS_HDR_LEN of:",DNS_HDR_LEN)
+ return false
+ end
+
+ local tvbr = tvbuf:range(0,DNS_HDR_LEN)
+
+ -- the first 2 bytes are tansaction id, which can be anything so no point in checking those
+ -- the next 2 bytes contain flags, a couple of which have some values we can check against
+
+ -- the opcode has to be 0, 1, 2, 4 or 5
+ -- the opcode field starts at bit offset 17 (in C-indexing), for 4 bits in length
+ local check = tvbr:bitfield(17,4)
+ if check == 3 or check > 5 then
+ dprint("heur_dissect_dns: invalid opcode:",check)
+ return false
+ end
+
+ -- the rcode has to be 0-10, 16-22 (we're ignoring private use rcodes here)
+ -- the rcode field starts at bit offset 28 (in C-indexing), for 4 bits in length
+ check = tvbr:bitfield(28,4)
+ if check > 22 or (check > 10 and check < 16) then
+ dprint("heur_dissect_dns: invalid rcode:",check)
+ return false
+ end
+
+ dprint2("heur_dissect_dns checking questions/answers")
+
+ -- now let's verify the number of questions/answers are reasonable
+ check = tvbr:range(4,2):uint() -- num questions
+ if check > 100 then return false end
+ check = tvbr:range(6,2):uint() -- num answers
+ if check > 100 then return false end
+ check = tvbr:range(8,2):uint() -- num authority
+ if check > 100 then return false end
+ check = tvbr:range(10,2):uint() -- num additional
+ if check > 100 then return false end
+
+ dprint2("heur_dissect_dns: everything looks good calling the real dissector")
+
+ -- don't do this line in your script - I'm just doing it so our testsuite can
+ -- verify this script
+ root:add("Heuristic dissector used"):set_generated()
+
+ -- ok, looks like it's ours, so go dissect it
+ -- note: calling the dissector directly like this is new in 1.11.3
+ -- also note that calling a Dissector objkect, as this does, means we don't
+ -- get back the return value of the dissector function we created previously
+ -- so it might be better to just call the function directly instead of doing
+ -- this, but this script is used for testing and this tests the call() function
+ dns.dissector(tvbuf,pktinfo,root)
+
+ -- since this is over a transport protocol, such as UDP, we can set the
+ -- conversation to make it sticky for our dissector, so that all future
+ -- packets to/from the same address:port pair will just call our dissector
+ -- function directly instead of this heuristic function
+ -- this is a new attribute of pinfo in 1.11.3
+ pktinfo.conversation = dns
+
+ return true
+end
+
+-- now register that heuristic dissector into the udp heuristic list
+dns:register_heuristic("udp",heur_dissect_dns)
+
-- We're done!
-- our protocol (Proto) gets automatically registered after this script finishes loading
----------------------------------------
@@ -361,6 +467,7 @@ getQueryName = function (tvbr)
end
pos = pos + 1 -- move past label length octet
-- append the label and a dot to name string
+ -- note: this uses the new method of ByteArray:raw(), added in 1.11.3
name = name .. barray:raw(pos, label_len) .. "."
len_remaining = len_remaining - (label_len + 1) -- subtract label and its length octet
label_count = label_count + 1
diff --git a/test/lua/proto.lua b/test/lua/proto.lua
index 1ade48bfe9..9b159e65a3 100644
--- a/test/lua/proto.lua
+++ b/test/lua/proto.lua
@@ -47,7 +47,7 @@ end
-- note ip only runs 3 times because it gets removed
-- and bootp only runs twice because the filter makes it run
-- once and then it gets replaced with a different one for the second time
-local taptests = { [FRAME]=2, [OTHER]=48 }
+local taptests = { [FRAME]=4, [OTHER]=48 }
local function getResults()
print("\n-----------------------------\n")
for k,v in pairs(taptests) do
@@ -497,6 +497,81 @@ end
local udp_encap_table = DissectorTable.get("udp.port")
udp_encap_table:add(MYDNS_PROTO_UDP_PORT, dns)
+----------------------------------------
+-- we also want to add the heuristic dissector, for any UDP protocol
+-- first we need a heuristic dissection function
+-- this is that function - when wireshark invokes this, it will pass in the same
+-- things it passes in to the "dissector" function, but we only want to actually
+-- dissect it if it's for us, and we need to return true if it's for us, or else false
+-- figuring out if it's for us or not is not easy
+-- we need to try as hard as possible, or else we'll think it's for us when it's
+-- not and block other heuristic dissectors from getting their chanc
+--
+-- in practice, you'd never set a dissector like this to be heuristic, because there
+-- just isn't enough information to safely detect if it's DNS or not
+-- but I'm doing it to show how it would be done
+--
+-- Note: this heuristic stuff is new in 1.11.3
+local function heur_dissect_dns(tvbuf,pktinfo,root)
+
+ if tvbuf:len() < DNS_HDR_LEN then
+ return false
+ end
+
+ local tvbr = tvbuf:range(0,DNS_HDR_LEN)
+
+ -- the first 2 bytes are tansaction id, which can be anything so no point in checking those
+ -- the next 2 bytes contain flags, a couple of which have some values we can check against
+
+ -- the opcode has to be 0, 1, 2, 4 or 5
+ -- the opcode field starts at bit offset 17 (in C-indexing), for 4 bits in length
+ local check = tvbr:bitfield(17,4)
+ if check == 3 or check > 5 then
+ return false
+ end
+
+ -- the rcode has to be 0-10, 16-22 (we're ignoring private use rcodes here)
+ -- the rcode field starts at bit offset 28 (in C-indexing), for 4 bits in length
+ check = tvbr:bitfield(28,4)
+ if check > 22 or (check > 10 and check < 16) then
+ return false
+ end
+
+ -- now let's verify the number of questions/answers are reasonable
+ check = tvbr:range(4,2):uint() -- num questions
+ if check > 100 then return false end
+ check = tvbr:range(6,2):uint() -- num answers
+ if check > 100 then return false end
+ check = tvbr:range(8,2):uint() -- num authority
+ if check > 100 then return false end
+ check = tvbr:range(10,2):uint() -- num additional
+ if check > 100 then return false end
+
+ -- don't do this line in your script - I'm just doing it so our testsuite can
+ -- verify this script
+ root:add("Heuristic dissector used"):set_generated()
+
+ -- ok, looks like it's ours, so go dissect it
+ -- note: calling the dissector directly like this is new in 1.11.3
+ -- also note that calling a Dissector objkect, as this does, means we don't
+ -- get back the return value of the dissector function we created previously
+ -- so it might be better to just call the function directly instead of doing
+ -- this, but this script is used for testing and this tests the call() function
+ dns.dissector(tvbuf,pktinfo,root)
+
+ -- since this is over a transport protocol, such as UDP, we can set the
+ -- conversation to make it sticky for our dissector, so that all future
+ -- packets to/from the same address:port pair will just call our dissector
+ -- function directly instead of this heuristic function
+ -- this is a new attribute of pinfo in 1.11.3
+ pktinfo.conversation = dns
+
+ return true
+end
+
+-- now register that heuristic dissector into the udp heuristic list
+dns:register_heuristic("udp",heur_dissect_dns)
+
-- We're done!
-- our protocol (Proto) gets automatically registered after this script finishes loading
----------------------------------------
diff --git a/test/lua/verify_dissector.lua b/test/lua/verify_dissector.lua
index 0494f22f0a..f65d1ece72 100644
--- a/test/lua/verify_dissector.lua
+++ b/test/lua/verify_dissector.lua
@@ -61,7 +61,10 @@ local lines = {
}
}
-local numtests = #lines[1] + #lines[2]
+-- we're going to see those two sets of output twice: both by the normal
+-- dissector, then the first one by the heuristic, then the second one by
+-- a conversation match
+local numtests = 1 + (2 * (#lines[1] + #lines[2]))
print("going to run "..numtests.." tests")
-- for an example of what we're reading through to verify, look at end of this file
@@ -71,6 +74,7 @@ local line = file:read()
local pktidx = 1
local total = 0
+local found = false
while line do
-- eat beginning whitespace
@@ -79,6 +83,18 @@ while line do
pktidx = line:match("^Frame (%d+):")
testing("Frame "..pktidx)
pktidx = tonumber(pktidx)
+ if pktidx > 2 then pktidx = pktidx - 2 end
+ line = file:read()
+ elseif line:find("%[Heuristic dissector used%]") then
+ -- start again, because it now repeats
+ -- but we should not see this [Heuristic dissector used] line again
+ -- or it's an error in setting the conversation
+ if found then
+ error("Heuristic dissector ran twice - conversation setting not working?")
+ return
+ end
+ found = true
+ total = total + 1
line = file:read()
elseif line == lines[pktidx][1] then
-- we've matched the first line of our section
diff --git a/test/suite-wslua.sh b/test/suite-wslua.sh
index f734f41aea..fa682b08d3 100755
--- a/test/suite-wslua.sh
+++ b/test/suite-wslua.sh
@@ -39,7 +39,7 @@ wslua_step_dissector_test() {
fi
# then run tshark again with the verification script. (it internally reads in testin.txt)
- $TSHARK -r $CAPTURE_DIR/dns_port.pcap -X lua_script:$TESTS_DIR/lua/verify_dissector.lua > testout.txt 2>&1
+ $TSHARK -r $CAPTURE_DIR/empty.pcap -X lua_script:$TESTS_DIR/lua/verify_dissector.lua > testout.txt 2>&1
if grep -q "All tests passed!" testout.txt; then
test_step_ok
else
@@ -121,7 +121,7 @@ wslua_step_proto_test() {
return
fi
- # First run tshark with the dissector script.
+ # First run tshark with the proto script.
$TSHARK -r $CAPTURE_DIR/dns_port.pcap -V -X lua_script:$TESTS_DIR/lua/proto.lua > testin.txt 2>&1
grep -q "All tests passed!" testin.txt
if [ $? -ne 0 ]; then
@@ -130,7 +130,7 @@ wslua_step_proto_test() {
fi
# then run tshark again with the verification script. (it internally reads in testin.txt)
- $TSHARK -r $CAPTURE_DIR/dns_port.pcap -X lua_script:$TESTS_DIR/lua/verify_dissector.lua > testout.txt 2>&1
+ $TSHARK -r $CAPTURE_DIR/empty.pcap -X lua_script:$TESTS_DIR/lua/verify_dissector.lua > testout.txt 2>&1
if grep -q "All tests passed!" testout.txt; then
test_step_ok
else
@@ -148,7 +148,7 @@ wslua_step_int64_test() {
fi
# Tshark catches lua script failures, so we have to parse the output.
- $TSHARK -r $CAPTURE_DIR/dhcp.pcap -X lua_script:$TESTS_DIR/lua/int64.lua > testout.txt 2>&1
+ $TSHARK -r $CAPTURE_DIR/empty.pcap -X lua_script:$TESTS_DIR/lua/int64.lua > testout.txt 2>&1
if grep -q "All tests passed!" testout.txt; then
test_step_ok
else
@@ -165,30 +165,30 @@ wslua_step_args_test() {
fi
# Tshark catches lua script failures, so we have to parse the output.
- $TSHARK -r $CAPTURE_DIR/dhcp.pcap -X lua_script:$TESTS_DIR/lua/script_args.lua -X lua_script1:1 > testout.txt 2>&1
+ $TSHARK -r $CAPTURE_DIR/empty.pcap -X lua_script:$TESTS_DIR/lua/script_args.lua -X lua_script1:1 > testout.txt 2>&1
grep -q "All tests passed!" testout.txt
if [ $? -ne 0 ]; then
cat testout.txt
test_step_failed "lua_args_test test 1 failed"
fi
- $TSHARK -r $CAPTURE_DIR/dhcp.pcap -X lua_script:$TESTS_DIR/lua/script_args.lua -X lua_script1:3 -X lua_script1:foo -X lua_script1:bar > testout.txt 2>&1
+ $TSHARK -r $CAPTURE_DIR/empty.pcap -X lua_script:$TESTS_DIR/lua/script_args.lua -X lua_script1:3 -X lua_script1:foo -X lua_script1:bar > testout.txt 2>&1
grep -q "All tests passed!" testout.txt
if [ $? -ne 0 ]; then
cat testout.txt
test_step_failed "lua_args_test test 2 failed"
fi
- $TSHARK -r $CAPTURE_DIR/dhcp.pcap -X lua_script:$TESTS_DIR/lua/script_args.lua -X lua_script:$TESTS_DIR/lua/script_args.lua -X lua_script1:3 -X lua_script2:1 -X lua_script1:foo -X lua_script1:bar > testout.txt 2>&1
+ $TSHARK -r $CAPTURE_DIR/empty.pcap -X lua_script:$TESTS_DIR/lua/script_args.lua -X lua_script:$TESTS_DIR/lua/script_args.lua -X lua_script1:3 -X lua_script2:1 -X lua_script1:foo -X lua_script1:bar > testout.txt 2>&1
grep -q "All tests passed!" testout.txt
if [ $? -ne 0 ]; then
cat testout.txt
test_step_failed "lua_args_test test 3 failed"
fi
- $TSHARK -r $CAPTURE_DIR/dhcp.pcap -X lua_script:$TESTS_DIR/lua/script_args.lua > testout.txt 2>&1
+ $TSHARK -r $CAPTURE_DIR/empty.pcap -X lua_script:$TESTS_DIR/lua/script_args.lua > testout.txt 2>&1
if grep -q "All tests passed!" testout.txt; then
cat testout.txt
test_step_failed "lua_args_test negative test 4 failed"
fi
- $TSHARK -r $CAPTURE_DIR/dhcp.pcap -X lua_script:$TESTS_DIR/lua/script_args.lua -X lua_script1:3 > testout.txt 2>&1
+ $TSHARK -r $CAPTURE_DIR/empty.pcap -X lua_script:$TESTS_DIR/lua/script_args.lua -X lua_script1:3 > testout.txt 2>&1
if grep -q "All tests passed!" testout.txt; then
cat testout.txt
test_step_failed "lua_args_test negative test 5 failed"
@@ -241,7 +241,7 @@ wslua_step_struct_test() {
fi
# Tshark catches lua script failures, so we have to parse the output.
- $TSHARK -r $CAPTURE_DIR/dhcp.pcap -X lua_script:$TESTS_DIR/lua/struct.lua > testout.txt 2>&1
+ $TSHARK -r $CAPTURE_DIR/empty.pcap -X lua_script:$TESTS_DIR/lua/struct.lua > testout.txt 2>&1
if grep -q "All tests passed!" testout.txt; then
test_step_ok
else