/* packet-netbios.c
* Routines for NetBIOS protocol packet disassembly
- * Jeff Foster <jfoste@woodward.com>
+ * Jeff Foster <jfoste@woodward.com>
* Copyright 1999 Jeffrey C. Foster
- *
+ *
* derived from the packet-nbns.c
*
- * $Id: packet-netbios.c,v 1.30 2001/01/15 04:39:28 guy Exp $
+ * $Id$
*
* Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
+ * By Gerald Combs <gerald@ethereal.com>
* Copyright 1998 Gerald Combs
*
- *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-
-
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-
#include <stdio.h>
#include <string.h>
#include <glib.h>
-#include "packet.h"
+#include <epan/packet.h>
#include "llcsaps.h"
+#include "reassemble.h"
+#include "prefs.h"
#include "packet-netbios.h"
-#include "packet-smb.h"
/* Netbios command numbers */
#define NB_ADD_GROUP 0x00
static int hf_netb_send_no_ack = -1;
static int hf_netb_version = -1;
static int hf_netb_largest_frame = -1;
-static int hf_netb_name = -1;
+static int hf_netb_nb_name = -1;
+static int hf_netb_nb_name_type = -1;
+static int hf_netb_status_buffer_len = -1;
+static int hf_netb_status = -1;
static int hf_netb_name_type = -1;
+static int hf_netb_max_data_recv_size = -1;
+static int hf_netb_termination_indicator = -1;
+static int hf_netb_num_data_bytes_accepted = -1;
static int hf_netb_local_ses_no = -1;
static int hf_netb_remote_ses_no = -1;
+static int hf_netb_data1 = -1;
static int hf_netb_data2 = -1;
+static int hf_netb_fragments = -1;
+static int hf_netb_fragment = -1;
+static int hf_netb_fragment_overlap = -1;
+static int hf_netb_fragment_overlap_conflict = -1;
+static int hf_netb_fragment_multiple_tails = -1;
+static int hf_netb_fragment_too_long_fragment = -1;
+static int hf_netb_fragment_error = -1;
static gint ett_netb = -1;
static gint ett_netb_name = -1;
static gint ett_netb_flags = -1;
static gint ett_netb_status = -1;
+static gint ett_netb_fragments = -1;
+static gint ett_netb_fragment = -1;
+
+static const fragment_items netbios_frag_items = {
+ &ett_netb_fragment,
+ &ett_netb_fragments,
+ &hf_netb_fragments,
+ &hf_netb_fragment,
+ &hf_netb_fragment_overlap,
+ &hf_netb_fragment_overlap_conflict,
+ &hf_netb_fragment_multiple_tails,
+ &hf_netb_fragment_too_long_fragment,
+ &hf_netb_fragment_error,
+ NULL,
+ "fragments"
+};
+
+static dissector_handle_t data_handle;
/* The strings for the station type, used by get_netbios_name function;
many of them came from the file "NetBIOS.txt" in the Zip archive at
http://www.net3group.com/ftp/browser.zip
*/
-static const value_string name_type_vals[] = {
+static const value_string nb_name_type_vals[] = {
{0x00, "Workstation/Redirector"},
{0x01, "Browser"},
- {0x02, "Workstation/Redirector"},
+ {0x02, "Workstation/Redirector"},
/* not sure what 0x02 is, I'm seeing alot of them however */
- /* i'm seeing them with workstation/redirection host
+ /* i'm seeing them with workstation/redirection host
announcements */
{0x03, "Messenger service/Main name"},
{0x05, "Forwarded name"},
{0x00, NULL}
};
+/* Tables for reassembly of fragments. */
+static GHashTable *netbios_fragment_table = NULL;
+static GHashTable *netbios_reassembled_table = NULL;
+
+/* defragmentation of NetBIOS Frame */
+static gboolean netbios_defragment = TRUE;
+
/* See
-
+
http://www.s390.ibm.com/bookmgr-cgi/bookmgr.cmd/BOOKS/BK8P7001/CCONTENTS
and
/* the strings for the command types */
-static char *CommandName[] = {
- "Add Group Name Query", /* 0x00 */
- "Add Name Query", /* 0x01 */
- "Name In Conflict", /* 0x02 */
- "Status Query", /* 0x03 */
- "Unknown",
- "Unknown",
- "Unknown",
- "Terminate Trace", /* 0x07 */
- "Datagram", /* 0x08 */
- "Broadcast Datagram", /* 0x09 */
- "Name Query", /* 0x0A */
- "Unknown",
- "Unknown",
- "Add Name Response", /* 0x0D */
- "Name Recognized", /* 0x0E */
- "Status Response", /* 0x0F */
- "Unknown",
- "Unknown",
- "Unknown",
- "Terminate Trace", /* 0x13 */
- "Data Ack", /* 0x14 */
- "Data First Middle", /* 0x15 */
- "Data Only Last", /* 0x16 */
- "Session Confirm", /* 0x17 */
- "Session End", /* 0x18 */
- "Session Initialize", /* 0x19 */
- "No Receive", /* 0x1a */
- "Receive Outstanding", /* 0x1b */
- "Receive Continue", /* 0x1c */
- "Unknown",
- "Unknown",
- "Session Alive", /* 0x1f */
- "Unknown",
+static const value_string cmd_vals[] = {
+ { NB_ADD_GROUP, "Add Group Name Query" },
+ { NB_ADD_NAME, "Add Name Query" },
+ { NB_NAME_IN_CONFLICT, "Name In Conflict" },
+ { NB_STATUS_QUERY, "Status Query" },
+ { NB_TERMINATE_TRACE_R, "Terminate Trace" },
+ { NB_DATAGRAM, "Datagram" },
+ { NB_DATAGRAM_BCAST, "Broadcast Datagram" },
+ { NB_NAME_QUERY, "Name Query" },
+ { NB_ADD_NAME_RESP, "Add Name Response" },
+ { NB_NAME_RESP, "Name Recognized" },
+ { NB_STATUS_RESP, "Status Response" },
+ { NB_TERMINATE_TRACE_LR, "Terminate Trace" },
+ { NB_DATA_ACK, "Data Ack" },
+ { NB_DATA_FIRST_MIDDLE, "Data First Middle" },
+ { NB_DATA_ONLY_LAST, "Data Only Last" },
+ { NB_SESSION_CONFIRM, "Session Confirm" },
+ { NB_SESSION_END, "Session End" },
+ { NB_SESSION_INIT, "Session Initialize" },
+ { NB_NO_RECEIVE, "No Receive" },
+ { NB_RECEIVE_OUTSTANDING, "Receive Outstanding" },
+ { NB_RECEIVE_CONTINUE, "Receive Continue" },
+ { NB_KEEP_ALIVE, "Session Alive" },
+ { 0, NULL }
};
+static const value_string name_types[] = {
+ { 0, "Unique name" },
+ { 1, "Group name" },
+ { 0, NULL }
+};
static const true_false_string flags_set = {
"Set",
};
static const true_false_string flags_allowed = {
"Allowed",
- "Not allowrd"
+ "Not allowed"
};
static const true_false_string flags_yes_no = {
"Yes",
"1.xx"
};
+static const value_string termination_indicator_vals[] = {
+ { 0x0000, "Normal session end" },
+ { 0x0001, "Abnormal session end" },
+ { 0, NULL }
+};
+
+static const value_string status_vals[] = {
+ { 0, "Add name not in process" },
+ { 1, "Add name in process" },
+ { 0, NULL }
+};
-void capture_netbios(const u_char *pd, int offset, packet_counts *ld)
+static const value_string max_frame_size_vals[] = {
+ { 0, "516" },
+ { 1, "1500" },
+ { 2, "2052" },
+ { 3, "4472" },
+ { 4, "8144" },
+ { 5, "11407" },
+ { 6, "17800" }, /* 17800 in TR spec, 17749 in NBF spec */
+ { 7, "65535" },
+ { 0, NULL }
+};
+
+
+void capture_netbios(packet_counts *ld)
{
ld->netbios++;
}
int
-process_netbios_name(const u_char *name_ptr, char *name_ret)
+process_netbios_name(const guchar *name_ptr, char *name_ret)
{
int i;
int name_type = *(name_ptr + NETBIOS_NAME_LEN - 1);
- u_char name_char;
+ guchar name_char;
static const char hex_digits[16] = "0123456789abcdef";
for (i = 0; i < NETBIOS_NAME_LEN - 1; i++) {
}
}
*name_ret = '\0';
+
+ /* Remove trailing space characters from name. */
+
+ name_ret--;
+
+ for (i = 0; i < NETBIOS_NAME_LEN - 1; i++) {
+ if (*name_ret != ' ') {
+ *(name_ret + 1) = 0;
+ break;
+ }
+ name_ret--;
+ }
+
return name_type;
}
char *
netbios_name_type_descr(int name_type)
{
- return val_to_str(name_type, name_type_vals, "Unknown");
+ return val_to_str(name_type, nb_name_type_vals, "Unknown");
}
void netbios_add_name(char* label, tvbuff_t *tvb, int offset,
proto_tree *tree)
{/* add a name field display tree. Display the name and station type in sub-tree */
-
+
proto_tree *field_tree;
proto_item *tf;
char name_str[(NETBIOS_NAME_LEN - 1)*4 + 1];
"%s: %s<%02x> (%s)", label, name_str, name_type, name_type_str);
field_tree = proto_item_add_subtree( tf, ett_netb_name);
- proto_tree_add_string_format( field_tree, hf_netb_name, tvb, offset,
+ proto_tree_add_string_format( field_tree, hf_netb_nb_name, tvb, offset,
15, name_str, "%s", name_str);
- proto_tree_add_uint_format( field_tree, hf_netb_name_type, tvb, offset + 15, 1, name_type,
+ proto_tree_add_uint_format( field_tree, hf_netb_nb_name_type, tvb, offset + 15, 1, name_type,
"0x%02x (%s)", name_type, name_type_str);
}
{/* display the transmit correlator */
- proto_tree_add_int( tree, hf_netb_xmit_corrl, tvb, offset + NB_XMIT_CORL,
- 2, tvb_get_letohs( tvb, offset + NB_XMIT_CORL));
+ proto_tree_add_item( tree, hf_netb_xmit_corrl, tvb, offset + NB_XMIT_CORL,
+ 2, TRUE);
}
{/* display the response correlator */
- proto_tree_add_int( tree, hf_netb_resp_corrl, tvb, offset + NB_RESP_CORL,
- 2, tvb_get_letohs( tvb, offset + NB_RESP_CORL));
+ proto_tree_add_item( tree, hf_netb_resp_corrl, tvb, offset + NB_RESP_CORL,
+ 2, TRUE);
}
{/* display the call name type */
+ proto_tree_add_item( tree, hf_netb_call_name_type, tvb, offset + NB_CALL_NAME_TYPE,
+ 1, TRUE);
- int name_type_value = tvb_get_guint8( tvb, offset + NB_CALL_NAME_TYPE);
+}
- switch (name_type_value) {
- case 0x00:
- proto_tree_add_int_format( tree, hf_netb_call_name_type, tvb, offset + NB_CALL_NAME_TYPE,
- 2, tvb_get_letohs( tvb, offset + NB_CALL_NAME_TYPE), "Caller's Name Type: Unique name");
- break;
+static guint8 nb_local_session( tvbuff_t *tvb, int offset, proto_tree *tree)
- case 0x01:
- proto_tree_add_int_format( tree, hf_netb_call_name_type, tvb, offset + NB_CALL_NAME_TYPE,
- 2, tvb_get_letohs( tvb, offset + NB_CALL_NAME_TYPE), "Caller's Name Type: Group name");
- break;
+{/* add the local session to tree, and return its value */
- default:
- proto_tree_add_int_format( tree, hf_netb_call_name_type, tvb, offset + NB_CALL_NAME_TYPE,
- 2, tvb_get_letohs( tvb, offset + NB_CALL_NAME_TYPE),
- "Caller's Name Type: 0x%02x (should be 0 or 1)", name_type_value);
- break;
- }
+ guint8 local_session = tvb_get_guint8( tvb, offset + NB_LOCAL_SES);
+
+ proto_tree_add_uint( tree, hf_netb_local_ses_no, tvb, offset + NB_LOCAL_SES, 1,
+ local_session);
+
+ return local_session;
}
-static void nb_local_session( tvbuff_t *tvb, int offset, proto_tree *tree)
+static guint8 nb_remote_session( tvbuff_t *tvb, int offset, proto_tree *tree)
+
+{/* add the remote session to tree, and return its value */
-{/* add the local session to tree */
+ guint8 remote_session = tvb_get_guint8( tvb, offset + NB_RMT_SES);
- proto_tree_add_uint( tree, hf_netb_local_ses_no, tvb, offset + NB_LOCAL_SES, 1,
- tvb_get_guint8( tvb, offset + NB_LOCAL_SES));
+ proto_tree_add_uint( tree, hf_netb_remote_ses_no, tvb, offset + NB_RMT_SES, 1,
+ remote_session);
+ return remote_session;
}
-static void nb_remote_session( tvbuff_t *tvb, int offset, proto_tree *tree)
+static void nb_data1(int hf, tvbuff_t *tvb, int offset, proto_tree *tree)
-{/* add the remote session to tree */
+{/* add the DATA1 to tree with specified hf_ value */
- proto_tree_add_uint( tree, hf_netb_remote_ses_no, tvb, offset + NB_RMT_SES, 1,
- tvb_get_guint8( tvb, offset + NB_RMT_SES));
+ proto_tree_add_item( tree, hf, tvb, offset + NB_DATA1, 1, TRUE);
}
-static void nb_data2(char *label, int len, tvbuff_t *tvb, int offset,
- proto_tree *tree)
+static void nb_data2(int hf, tvbuff_t *tvb, int offset, proto_tree *tree)
-{/* add the DATA2 to tree with format string = label and length of len */
+{/* add the DATA2 to tree with specified hf_ value */
- int value = (len == 1 ? tvb_get_guint8( tvb, offset + NB_DATA2)
- : tvb_get_letohs( tvb, offset + NB_DATA2));
-
- proto_tree_add_uint( tree, hf_netb_data2, tvb, offset + NB_DATA2, 2, value);
+ proto_tree_add_item( tree, hf, tvb, offset + NB_DATA2, 2, TRUE);
}
/* */
/************************************************************************/
-static void dissect_netb_unknown( tvbuff_t *tvb, int offset, proto_tree *tree)
+static guint32
+dissect_netb_unknown( tvbuff_t *tvb, int offset, proto_tree *tree)
-{/* Handle any unknow commands, do nothing */
+{/* Handle any unknown commands, do nothing */
-/* old_dissect_data( data_ptr, offset + NB_COMMAND + 1, fd, tree); */
+ proto_tree_add_text(tree, tvb, offset + NB_COMMAND + 1, -1,
+ "Unknown NetBIOS command data");
+
+ return 0;
}
-static void dissect_netb_add_group_name( tvbuff_t *tvb, int offset,
- proto_tree *tree)
+static guint32
+dissect_netb_add_group_name( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the ADD GROUP NAME QUERY command */
- nb_resp_corrl( tvb, offset, tree);
+ nb_resp_corrl( tvb, offset, tree);
netbios_add_name("Group name to add", tvb, offset + NB_SENDER_NAME,
tree);
+
+ return 0;
}
-static void dissect_netb_add_name( tvbuff_t *tvb, int offset,
- proto_tree *tree)
+static guint32
+dissect_netb_add_name( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the ADD NAME QUERY command */
- nb_resp_corrl( tvb, offset, tree);
+ nb_resp_corrl( tvb, offset, tree);
netbios_add_name("Name to add", tvb, offset + NB_SENDER_NAME, tree);
+
+ return 0;
}
-static void dissect_netb_name_in_conflict( tvbuff_t *tvb, int offset,
- proto_tree *tree)
+static guint32
+dissect_netb_name_in_conflict( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the NAME IN CONFLICT command */
netbios_add_name("Name In Conflict", tvb, offset + NB_RECVER_NAME,
tree);
netbios_add_name("Sender's Name", tvb, offset + NB_SENDER_NAME, tree);
+
+ return 0;
}
-static void dissect_netb_status_query( tvbuff_t *tvb, int offset, proto_tree *tree)
+static guint32
+dissect_netb_status_query( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the STATUS QUERY command */
status_request);
break;
}
- nb_data2("Length of status buffer: %u", 2, tvb, offset, tree);
- nb_resp_corrl( tvb, offset, tree);
+ nb_data2( hf_netb_status_buffer_len, tvb, offset, tree);
+ nb_resp_corrl( tvb, offset, tree);
netbios_add_name("Receiver's Name", tvb, offset + NB_RECVER_NAME, tree);
- netbios_add_name("Sender's Name", tvb, offset + NB_SENDER_NAME,
- tree);
+ netbios_add_name("Sender's Name", tvb, offset + NB_SENDER_NAME, tree);
+
+ return 0;
}
-static u_char zeroes[10];
+static guint32
+dissect_netb_terminate_trace( tvbuff_t *tvb _U_, int offset _U_, proto_tree *tree _U_)
-static void dissect_netb_datagram( tvbuff_t *tvb, int offset, proto_tree *tree)
+{/* Handle the TERMINATE TRACE command */
+
+ /*
+ * XXX - are any of the fields in this message significant?
+ * The IBM NetBIOS document shows them as "Reserved".
+ */
+
+ return 0;
+}
+
+
+static guchar zeroes[10];
+
+static guint32
+dissect_netb_datagram( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the DATAGRAM command */
netbios_add_name("Sender's Name", tvb, offset + NB_SENDER_NAME,
tree);
}
+
+ return 0;
}
-static void dissect_netb_datagram_bcast( tvbuff_t *tvb, int offset,
- proto_tree *tree)
+static guint32
+dissect_netb_datagram_bcast( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the DATAGRAM BROADCAST command */
netbios_add_name("Sender's Name", tvb, offset + NB_SENDER_NAME,
tree);
}
+
+ return 0;
}
-static void dissect_netb_name_query( tvbuff_t *tvb, int offset, proto_tree *tree)
+static guint32
+dissect_netb_name_query( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the NAME QUERY command */
guint8 local_session_number = tvb_get_guint8( tvb, offset + NB_DATA2);
netbios_add_name("Sender's Name", tvb, offset + NB_SENDER_NAME,
tree);
}
+
+ return 0;
}
-static void dissect_netb_add_name_resp( tvbuff_t *tvb, int offset,
- proto_tree *tree)
+static guint32
+dissect_netb_add_name_resp( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the ADD NAME RESPONSE command */
- guint8 status = tvb_get_guint8( tvb, offset + NB_DATA1);
- guint16 name_type = tvb_get_letohs( tvb, offset + NB_DATA2);
-
- switch (status) {
-
- case 0:
- proto_tree_add_text( tree, tvb, offset + NB_DATA1, 1,
- "Status: Add name not in process");
- break;
- case 1:
- proto_tree_add_text( tree, tvb, offset + NB_DATA1, 1,
- "Status: Add name in process");
- break;
-
- default:
- proto_tree_add_text( tree, tvb, offset + NB_DATA1, 1,
- "Status: 0x%02x (should be 0 or 1)", status);
- break;
- }
-
- switch (name_type) {
-
- case 0:
- proto_tree_add_text( tree, tvb, offset + NB_DATA2, 2,
- "Name type: Unique name");
- break;
-
- case 1:
- proto_tree_add_text( tree, tvb, offset + NB_DATA2, 2,
- "Name type: Group name");
- break;
-
- default:
- proto_tree_add_text( tree, tvb, offset + NB_DATA2, 2,
- "Name type: 0x%04x (should be 0 or 1)", name_type);
- break;
- }
-
- nb_xmit_corrl( tvb, offset, tree);
+ nb_data1( hf_netb_status, tvb, offset, tree);
+ nb_data2( hf_netb_name_type, tvb, offset, tree);
+ nb_xmit_corrl( tvb, offset, tree);
netbios_add_name("Name to be added", tvb, offset + NB_RECVER_NAME,
tree);
netbios_add_name("Name to be added", tvb, offset + NB_SENDER_NAME,
tree);
+
+ return 0;
}
-static void dissect_netb_name_resp( tvbuff_t *tvb, int offset,
- proto_tree *tree)
+static guint32
+dissect_netb_name_resp( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the NAME RECOGNIZED command */
guint8 local_session_number = tvb_get_guint8( tvb, offset + NB_DATA2);
netbios_add_name("Sender's Name", tvb, offset + NB_SENDER_NAME,
tree);
}
+
+ return 0;
}
-static void dissect_netb_status_resp( tvbuff_t *tvb, int offset, proto_tree *tree)
+static guint32
+dissect_netb_status_resp( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the STATUS RESPONSE command */
guint8 status_response = tvb_get_guint8( tvb, offset + NB_DATA1);
proto_tree_add_text(data2_tree, tvb, offset, 2, "%s",
decode_numeric_bitfield(data2, 0x3FFF, 2*8,
"Status data length = %u"));
- nb_xmit_corrl( tvb, offset, tree);
+ nb_xmit_corrl( tvb, offset, tree);
netbios_add_name("Receiver's Name", tvb, offset + NB_RECVER_NAME, tree);
netbios_add_name("Sender's Name", tvb, offset + NB_SENDER_NAME,
tree);
+
+ return 0;
}
-static void dissect_netb_data_ack( tvbuff_t* tvb, int offset, proto_tree *tree)
+static guint32
+dissect_netb_data_ack( tvbuff_t* tvb, int offset, proto_tree *tree)
{/* Handle the DATA ACK command */
nb_xmit_corrl( tvb, offset, tree);
nb_remote_session( tvb, offset, tree);
nb_local_session( tvb, offset, tree);
+
+ return 0;
}
-static void dissect_netb_data_first_middle( tvbuff_t *tvb, int offset,
- proto_tree *tree)
+static guint32
+dissect_netb_data_first_middle( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the DATA FIRST MIDDLE command */
+ guint8 remote_session, local_session;
+
+ /*
+ * This is the first frame, or the middle frame, of a fragmented
+ * packet.
+ *
+ * XXX - there are no sequence numbers, so we have to assume
+ * that fragments arrive in order with no duplicates.
+ * In fact, 802.2 LLC is supposed to handle that, so we
+ * might have to have the LLC dissector do so (but the TCP
+ * dissector doesn't currently handle out-of-order or duplicate
+ * data, either).
+ */
+
netbios_data_first_middle_flags( tvb, tree, offset + NB_FLAGS);
nb_resync_indicator( tvb, offset, tree, "DATA FIRST MIDDLE");
nb_xmit_corrl( tvb, offset, tree);
nb_resp_corrl( tvb, offset, tree);
- nb_remote_session( tvb, offset, tree);
- nb_local_session( tvb, offset, tree);
+ remote_session = nb_remote_session( tvb, offset, tree);
+ local_session = nb_local_session( tvb, offset, tree);
+
+ /*
+ * Return a combination of the remote and local session numbers,
+ * for use when reassembling.
+ */
+ return (remote_session << 8) + local_session;
}
-static void dissect_netb_data_only_last( tvbuff_t *tvb, int offset,
- proto_tree *tree)
+static guint32
+dissect_netb_data_only_last( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the DATA ONLY LAST command */
+ guint8 remote_session, local_session;
+
+ /*
+ * This is a complete packet, or the last frame of a fragmented
+ * packet.
+ */
+
netbios_data_only_flags( tvb, tree, offset + NB_FLAGS);
nb_resync_indicator( tvb, offset, tree, "DATA ONLY LAST");
nb_xmit_corrl( tvb, offset, tree);
nb_resp_corrl( tvb, offset, tree);
- nb_remote_session( tvb, offset, tree);
- nb_local_session( tvb, offset, tree);
+ remote_session = nb_remote_session( tvb, offset, tree);
+ local_session = nb_local_session( tvb, offset, tree);
+
+ /*
+ * Return a combination of the remote and local session numbers,
+ * for use when reassembling.
+ */
+ return (remote_session << 8) + local_session;
}
-static void dissect_netb_session_confirm( tvbuff_t *tvb, int offset,
- proto_tree *tree)
+static guint32
+dissect_netb_session_confirm( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the SESSION CONFIRM command */
netbios_add_ses_confirm_flags( tvb, tree, offset + NB_FLAGS);
- nb_data2("Max data recv size: %u", 2, tvb, offset, tree);
+ nb_data2( hf_netb_max_data_recv_size, tvb, offset, tree);
nb_xmit_corrl( tvb, offset, tree);
nb_resp_corrl( tvb, offset, tree);
nb_remote_session( tvb, offset, tree);
nb_local_session( tvb, offset, tree);
+
+ return 0;
}
-static void dissect_netb_session_end( tvbuff_t *tvb, int offset,
- proto_tree *tree)
+static guint32
+dissect_netb_session_end( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the SESSION END command */
- guint16 termination_indicator = tvb_get_letohs( tvb, offset + NB_DATA2);
-
- switch (termination_indicator) {
-
- case 0x0000:
- proto_tree_add_text( tree, tvb, offset + NB_DATA2, 2,
- "Termination indicator: Normal session end");
- break;
-
- case 0x0001:
- proto_tree_add_text( tree, tvb, offset + NB_DATA2, 2,
- "Termination indicator: Abormal session end");
- break;
-
- default:
- proto_tree_add_text( tree, tvb, offset + NB_DATA2, 2,
- "Termination indicator: 0x%04x (should be 0x0000 or 0x0001)",
- termination_indicator);
- break;
- }
+ nb_data2( hf_netb_termination_indicator, tvb, offset, tree);
nb_remote_session( tvb, offset, tree);
nb_local_session( tvb, offset, tree);
+
+ return 0;
}
-static void dissect_netb_session_init( tvbuff_t *tvb, int offset,
- proto_tree *tree)
+static guint32
+dissect_netb_session_init( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the SESSION INITIALIZE command */
netbios_add_session_init_flags( tvb, tree, offset + NB_FLAGS);
- nb_data2("Max data recv size: %u", 2, tvb, offset, tree);
+ nb_data2( hf_netb_max_data_recv_size, tvb, offset, tree);
nb_resp_corrl( tvb, offset, tree);
nb_xmit_corrl( tvb, offset, tree);
nb_remote_session( tvb, offset, tree);
nb_local_session( tvb, offset, tree);
+
+ return 0;
}
-static void dissect_netb_no_receive( tvbuff_t *tvb, int offset,
- proto_tree *tree)
+static guint32
+dissect_netb_no_receive( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the NO RECEIVE command */
netbios_no_receive_flags( tvb, tree, offset + NB_FLAGS);
- nb_data2("Number of data bytes accepted: %u", 2, tvb, offset, tree);
+ nb_data2( hf_netb_num_data_bytes_accepted, tvb, offset, tree);
nb_remote_session( tvb, offset, tree);
nb_local_session( tvb, offset, tree);
+
+ return 0;
}
-static void dissect_netb_receive_outstanding( tvbuff_t *tvb, int offset,
- proto_tree *tree)
+static guint32
+dissect_netb_receive_outstanding( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the RECEIVE OUTSTANDING command */
- nb_data2("Number of data bytes accepted: %u", 2, tvb, offset, tree);
+ nb_data2( hf_netb_num_data_bytes_accepted, tvb, offset, tree);
nb_remote_session( tvb, offset, tree);
nb_local_session( tvb, offset, tree);
+
+ return 0;
}
-static void dissect_netb_receive_continue( tvbuff_t *tvb, int offset,
- proto_tree *tree)
+static guint32
+dissect_netb_receive_continue( tvbuff_t *tvb, int offset, proto_tree *tree)
{/* Handle the RECEIVE CONTINUE command */
nb_xmit_corrl( tvb, offset, tree);
nb_remote_session( tvb, offset, tree);
nb_local_session( tvb, offset, tree);
+
+ return 0;
+}
+
+
+static guint32
+dissect_netb_session_alive( tvbuff_t *tvb, int offset, proto_tree *tree)
+
+{/* Handle the SESSION ALIVE command */
+
+ /*
+ * XXX - all the fields are claimed to be "Reserved", but
+ * the session numbers appear to be non-zero in at least
+ * one capture, and they do appear to match session numbers
+ * in other messages, and I'd expect that you had to identify
+ * sessions in this message in any case.
+ *
+ * We show only those fields.
+ */
+ nb_remote_session( tvb, offset, tree);
+ nb_local_session( tvb, offset, tree);
+
+ return 0;
}
/************************************************************************/
-void (*dissect_netb[])(tvbuff_t *, int, proto_tree *) = {
+static guint32 (*dissect_netb[])(tvbuff_t *, int, proto_tree *) = {
dissect_netb_add_group_name, /* Add Group Name 0x00 */
dissect_netb_add_name, /* Add Name 0x01 */
dissect_netb_unknown, /* unknown 0x04 */
dissect_netb_unknown, /* unknown 0x05 */
dissect_netb_unknown, /* unknown 0x06 */
- dissect_netb_unknown, /* Terminate Trace 0x07 */
+ dissect_netb_terminate_trace, /* Terminate Trace 0x07 */
dissect_netb_datagram, /* Datagram 0x08 */
dissect_netb_datagram_bcast, /* Datagram Broadcast 0x09 */
dissect_netb_name_query, /* Name Query 0x0A */
dissect_netb_unknown, /* unknown 0x10 */
dissect_netb_unknown, /* unknown 0x11 */
dissect_netb_unknown, /* unknown 0x12 */
- dissect_netb_unknown, /* Terminate Trace 0x13 */
+ dissect_netb_terminate_trace, /* Terminate Trace 0x13 */
dissect_netb_data_ack, /* Data Ack 0x14 */
dissect_netb_data_first_middle,/* Data First Middle 0x15 */
dissect_netb_data_only_last, /* Data Only Last 0x16 */
dissect_netb_receive_continue,/* Receive Continue 0x1C */
dissect_netb_unknown, /* unknown 0x1D */
dissect_netb_unknown, /* unknown 0x1E */
-
- dissect_netb_unknown, /* Session Alive 0x1f (nothing to do) */
- dissect_netb_unknown,
+ dissect_netb_session_alive, /* Session Alive 0x1f */
+ dissect_netb_unknown,
};
+static heur_dissector_list_t netbios_heur_subdissector_list;
+
+void
+dissect_netbios_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ /*
+ * Try the heuristic dissectors for NetBIOS; if none of them
+ * accept the packet, dissect it as data.
+ */
+ if (!dissector_try_heuristic(netbios_heur_subdissector_list,
+ tvb, pinfo, tree))
+ call_dissector(data_handle,tvb, pinfo, tree);
+}
static void
dissect_netbios(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
- proto_tree *netb_tree;
+ proto_tree *netb_tree = NULL;
proto_item *ti;
guint16 hdr_len, command;
+ char *command_name;
char name[(NETBIOS_NAME_LEN - 1)*4 + 1];
int name_type;
+ guint16 session_id;
+ gboolean save_fragmented;
+ int len;
+ fragment_data *fd_head;
+ tvbuff_t *next_tvb;
int offset = 0;
- CHECK_DISPLAY_AS_DATA(proto_netbios, tvb, pinfo, tree);
-
-
/* load the display labels */
- pinfo->current_proto = "NetBIOS";
- if (check_col(pinfo->fd, COL_PROTOCOL))
- col_set_str(pinfo->fd, COL_PROTOCOL, "NetBIOS");
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "NetBIOS");
/* Find NetBIOS marker EFFF, this is done because I have seen an extra LLC */
++offset;
if ( 0xefff != tvb_get_letohs(tvb, 3)){
- if (check_col( pinfo->fd, COL_INFO)) /* print bad packet */
- col_set_str( pinfo->fd, COL_INFO, "Bad packet, no 0xEFFF marker");
+ if (check_col( pinfo->cinfo, COL_INFO)) /* print bad packet */
+ col_set_str( pinfo->cinfo, COL_INFO, "Bad packet, no 0xEFFF marker");
return; /* this is an unknow packet, no marker */
}
/* limit command so no table overflows */
command = MIN( command, sizeof( dissect_netb)/ sizeof(void *));
- if (check_col( pinfo->fd, COL_INFO)) { /* print command name */
+ if (check_col( pinfo->cinfo, COL_INFO)) { /* print command name */
+ command_name = val_to_str(command, cmd_vals, "Unknown (0x%02x)");
switch ( command ) {
case NB_NAME_QUERY:
name_type = get_netbios_name( tvb, offset + 12, name);
- col_add_fstr( pinfo->fd, COL_INFO, "%s for %s<%02x>",
- CommandName[ command], name, name_type);
+ col_add_fstr( pinfo->cinfo, COL_INFO, "%s for %s<%02x>",
+ command_name, name, name_type);
break;
case NB_NAME_RESP:
case NB_ADD_NAME:
case NB_ADD_GROUP:
name_type = get_netbios_name( tvb, offset + 28, name);
- col_add_fstr( pinfo->fd, COL_INFO, "%s - %s<%02x>",
- CommandName[ command], name, name_type);
+ col_add_fstr( pinfo->cinfo, COL_INFO, "%s - %s<%02x>",
+ command_name, name, name_type);
break;
default:
- col_add_fstr( pinfo->fd, COL_INFO, "%s", CommandName[ command]);
+ col_add_fstr( pinfo->cinfo, COL_INFO, "%s",
+ command_name);
break;
}
}
if ( tree) {
- ti = proto_tree_add_item(tree, proto_netbios, tvb, 0, tvb_length(tvb), FALSE);
+ ti = proto_tree_add_item(tree, proto_netbios, tvb, 0, hdr_len, FALSE);
netb_tree = proto_item_add_subtree(ti, ett_netb);
proto_tree_add_uint_format(netb_tree, hf_netb_hdr_len, tvb, offset, 2, hdr_len,
proto_tree_add_text(netb_tree, tvb, offset + 2, 2,
"Delimiter: EFFF (NetBIOS)");
- proto_tree_add_uint_format(netb_tree, hf_netb_cmd, tvb, offset + NB_COMMAND, 1, command,
- "Command: 0x%02x (%s)", command, CommandName[ command]);
-
- /* if command in table range */
- if ( command < sizeof( dissect_netb)/ sizeof(void *))
-
- /* branch to handle commands */
- (dissect_netb[ command])( tvb, offset, netb_tree);
+ proto_tree_add_uint(netb_tree, hf_netb_cmd, tvb, offset + NB_COMMAND, 1, command);
}
- /* Test for SMB data */
- if ( tvb_length(tvb)> ( hdr_len + 4)){ /* if enough data */
+ /* if command in table range */
+ if ( command < sizeof( dissect_netb)/ sizeof(void *)) {
+
+ /* branch to handle commands */
+ session_id = (dissect_netb[ command])( tvb, offset, netb_tree);
offset += hdr_len; /* move past header */
- if (( tvb_get_guint8( tvb, offset) == 0xff) && /* if SMB marker */
- ( tvb_get_guint8( tvb, offset + 1) == 'S') &&
- ( tvb_get_guint8( tvb, offset + 2) == 'M') &&
- ( tvb_get_guint8( tvb, offset + 3) == 'B')) {
+ save_fragmented = pinfo->fragmented;
- tvbuff_t *next_tvb;
- const guint8 *next_pd;
- int next_offset;
+ /*
+ * Process user data in frames that have it.
+ */
+ switch (command) {
+ case NB_DATAGRAM:
+ case NB_DATAGRAM_BCAST:
+ /*
+ * No fragmentation here.
+ */
next_tvb = tvb_new_subset(tvb, offset, -1, -1);
- tvb_compat(next_tvb, &next_pd, &next_offset);
-
- /* decode SMB */
- dissect_smb( next_pd, next_offset, pinfo->fd, tree,
- END_OF_FRAME - next_offset);
+ dissect_netbios_payload(next_tvb, pinfo, tree);
+ break;
+ case NB_DATA_FIRST_MIDDLE:
+ case NB_DATA_ONLY_LAST:
+ /*
+ * Possibly fragmented.
+ */
+ len = tvb_reported_length_remaining(tvb, offset);
+ if (netbios_defragment &&
+ tvb_bytes_exist(tvb, offset, len)) {
+ fd_head = fragment_add_seq_next(tvb, offset,
+ pinfo, session_id,
+ netbios_fragment_table,
+ netbios_reassembled_table,
+ len, command == NB_DATA_FIRST_MIDDLE);
+ if (fd_head != NULL) {
+ if (fd_head->next != NULL) {
+ next_tvb = tvb_new_real_data(fd_head->data,
+ fd_head->len, fd_head->len);
+ tvb_set_child_real_data_tvbuff(tvb,
+ next_tvb);
+ add_new_data_source(pinfo,
+ next_tvb,
+ "Reassembled NetBIOS");
+ /* Show all fragments. */
+ if (tree) {
+ show_fragment_seq_tree(fd_head,
+ &netbios_frag_items,
+ netb_tree, pinfo,
+ next_tvb);
+ }
+ } else {
+ next_tvb = tvb_new_subset(tvb,
+ offset, -1, -1);
+ }
+ } else {
+ next_tvb = NULL;
+ }
+ } else {
+ /*
+ * Dissect this, regardless of whether
+ * it's NB_DATA_FIRST_MIDDLE or
+ * NB_DATA_ONLY_LAST.
+ *
+ * XXX - it'd be nice to show
+ * NB_DATA_FIRST_MIDDLE as a fragment
+ * if it's not the first fragment (i.e.,
+ * MIDDLE rather than FIRST), and show
+ * NB_DATA_ONLY_LAST as a fragment if
+ * it's part of a fragmented datagram
+ * (i.e, LAST rather than ONLY), but
+ * we'd have to do reassembly to
+ * be able to determine that.
+ */
+ next_tvb = tvb_new_subset(tvb, offset, -1, -1);
+ }
+ if (next_tvb != NULL)
+ dissect_netbios_payload(next_tvb, pinfo, tree);
+ else {
+ next_tvb = tvb_new_subset (tvb, offset, -1, -1);
+ call_dissector(data_handle, next_tvb, pinfo,
+ tree);
+ }
+ break;
}
}
}
+static void
+netbios_init(void)
+{
+ /*
+ * Initialize the fragment and reassembly tables.
+ */
+ fragment_table_init(&netbios_fragment_table);
+ reassembled_table_init(&netbios_reassembled_table);
+}
void proto_register_netbios(void)
{
&ett_netb_name,
&ett_netb_flags,
&ett_netb_status,
+ &ett_netb_fragments,
+ &ett_netb_fragment,
};
static hf_register_info hf_netb[] = {
{ &hf_netb_cmd,
- { "Command", "netbios.command", FT_UINT16, BASE_HEX, NULL, 0x0,
- "" }},
+ { "Command", "netbios.command", FT_UINT8, BASE_HEX, VALS(cmd_vals), 0x0,
+ "", HFILL }},
+
{ &hf_netb_hdr_len,
{ "Header Length", "netbios.hdr_len", FT_UINT16, BASE_DEC, NULL, 0x0,
- "" }},
+ "", HFILL }},
{ &hf_netb_xmit_corrl,
- { "Transmit Correlator", "netbios.xmit_corrl", FT_INT16, BASE_HEX, NULL, 0x0,
- "" }},
+ { "Transmit Correlator", "netbios.xmit_corrl", FT_UINT16, BASE_HEX, NULL, 0x0,
+ "", HFILL }},
{ &hf_netb_resp_corrl,
- { "Response Correlator", "netbios.resp_corrl", FT_INT16, BASE_HEX, NULL, 0x0,
- "" }},
+ { "Response Correlator", "netbios.resp_corrl", FT_UINT16, BASE_HEX, NULL, 0x0,
+ "", HFILL }},
+
{ &hf_netb_call_name_type,
- { "Call Name Type", "netbios.call_name_type", FT_INT16, BASE_HEX, NULL, 0x0,
- "" }},
- { &hf_netb_name_type,
- { "Netbios Name Type", "netbios.name_type", FT_UINT16, BASE_HEX, NULL, 0x0,
- "" }},
- { &hf_netb_name,
- { "Netbios Name", "netbios.name", FT_STRING, BASE_NONE, NULL, 0x0,
- "" }},
+ { "Caller's Name Type", "netbios.call_name_type", FT_UINT8, BASE_HEX, VALS(name_types), 0x0,
+ "", HFILL }},
+
+ { &hf_netb_nb_name_type,
+ { "NetBIOS Name Type", "netbios.nb_name_type", FT_UINT8, BASE_HEX, VALS(nb_name_type_vals), 0x0,
+ "", HFILL }},
+
+ { &hf_netb_nb_name,
+ { "NetBIOS Name", "netbios.nb_name", FT_STRING, BASE_NONE, NULL, 0x0,
+ "", HFILL }},
{ &hf_netb_ack,
{ "Acknowledge", "netbios.ack", FT_BOOLEAN, 8, TFS( &flags_set), 0x08,
- "" }},
+ "", HFILL }},
{ &hf_netb_ack_with_data,
- { "Acknowledge with data", "netbios.ack_with_data", FT_BOOLEAN, 8, TFS( &flags_allowed), 0x04,
- "" }},
+ { "Acknowledge with data", "netbios.ack_with_data", FT_BOOLEAN, 8, TFS( &flags_allowed), 0x04,
+ "", HFILL }},
{ &hf_netb_ack_expected,
{ "Acknowledge expected", "netbios.ack_expected", FT_BOOLEAN, 8,
- TFS( &flags_yes_no), 0x02, "" }},
+ TFS( &flags_yes_no), 0x02, "", HFILL }},
{ &hf_netb_recv_cont_req,
{ "RECEIVE_CONTINUE requested", "netbios.recv_cont_req", FT_BOOLEAN, 8,
- TFS( &flags_yes_no), 0x01, "" }},
+ TFS( &flags_yes_no), 0x01, "", HFILL }},
{ &hf_netb_send_no_ack,
{ "Handle SEND.NO.ACK", "netbios.send_no_ack", FT_BOOLEAN, 8,
- TFS( &flags_yes_no), 0x80, "" }},
+ TFS( &flags_yes_no), 0x80, "", HFILL }},
{ &hf_netb_version,
{ "NetBIOS Version", "netbios.version", FT_BOOLEAN, 8,
- TFS( &netb_version_str), 0x01, "" }},
+ TFS( &netb_version_str), 0x01, "", HFILL }},
{ &hf_netb_largest_frame,
- { "Largest Frame", "netbios.largest_frame", FT_UINT8, BASE_HEX, NULL, 0x0E,
- "" }},
+ { "Largest Frame", "netbios.largest_frame", FT_UINT8, BASE_DEC, VALS(max_frame_size_vals), 0x0E,
+ "", HFILL }},
+
+ { &hf_netb_status_buffer_len,
+ { "Length of status buffer", "netbios.status_buffer_len", FT_UINT16, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+
+ { &hf_netb_status,
+ { "Status", "netbios.status", FT_UINT8, BASE_DEC, VALS(status_vals), 0x0,
+ "", HFILL }},
+
+ { &hf_netb_name_type,
+ { "Name type", "netbios.name_type", FT_UINT16, BASE_DEC, VALS(name_types), 0x0,
+ "", HFILL }},
+
+ { &hf_netb_max_data_recv_size,
+ { "Maximum data receive size", "netbios.max_data_recv_size", FT_UINT16, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+
+ { &hf_netb_termination_indicator,
+ { "Termination indicator", "netbios.termination_indicator", FT_UINT16, BASE_HEX, VALS(termination_indicator_vals), 0x0,
+ "", HFILL }},
+
+ { &hf_netb_num_data_bytes_accepted,
+ { "Number of data bytes accepted", "netbios.num_data_bytes_accepted", FT_UINT16, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
{ &hf_netb_local_ses_no,
- { "Local Session No. ", "netbios.local_session", FT_UINT8, BASE_HEX, NULL, 0x0,
- "" }},
+ { "Local Session No.", "netbios.local_session", FT_UINT8, BASE_HEX, NULL, 0x0,
+ "", HFILL }},
{ &hf_netb_remote_ses_no,
- { "Remote Session No. ", "netbios.remote_session", FT_UINT8, BASE_HEX, NULL, 0x0,
- "" }},
+ { "Remote Session No.", "netbios.remote_session", FT_UINT8, BASE_HEX, NULL, 0x0,
+ "", HFILL }},
+
+ { &hf_netb_data1,
+ { "DATA1 value", "netbios.data1", FT_UINT8, BASE_HEX, NULL, 0x0,
+ "", HFILL }},
{ &hf_netb_data2,
- { "DATA2 value ", "netbios.data2", FT_UINT16, BASE_HEX, NULL, 0x0,
- "" }},
- };
+ { "DATA2 value", "netbios.data2", FT_UINT16, BASE_HEX, NULL, 0x0,
+ "", HFILL }},
+
+ { &hf_netb_fragment_overlap,
+ { "Fragment overlap", "netbios.fragment.overlap", FT_BOOLEAN, BASE_NONE,
+ NULL, 0x0, "Fragment overlaps with other fragments", HFILL }},
+
+ { &hf_netb_fragment_overlap_conflict,
+ { "Conflicting data in fragment overlap", "netbios.fragment.overlap.conflict",
+ FT_BOOLEAN, BASE_NONE,
+ NULL, 0x0, "Overlapping fragments contained conflicting data", HFILL }},
+
+ { &hf_netb_fragment_multiple_tails,
+ { "Multiple tail fragments found", "netbios.fragment.multipletails",
+ FT_BOOLEAN, BASE_NONE,
+ NULL, 0x0, "Several tails were found when defragmenting the packet", HFILL }},
+
+ { &hf_netb_fragment_too_long_fragment,
+ { "Fragment too long", "netbios.fragment.toolongfragment", FT_BOOLEAN, BASE_NONE,
+ NULL, 0x0, "Fragment contained data past end of packet", HFILL }},
+
+ { &hf_netb_fragment_error,
+ {"Defragmentation error", "netbios.fragment.error", FT_FRAMENUM, BASE_NONE,
+ NULL, 0x0, "Defragmentation error due to illegal fragments", HFILL }},
+
+ { &hf_netb_fragment,
+ { "NetBIOS Fragment", "netbios.fragment", FT_FRAMENUM, BASE_NONE,
+ NULL, 0x0, "NetBIOS Fragment", HFILL }},
+
+ { &hf_netb_fragments,
+ { "NetBIOS Fragments", "netbios.fragments", FT_NONE, BASE_NONE,
+ NULL, 0x0, "NetBIOS Fragments", HFILL }},
+ };
+ module_t *netbios_module;
proto_netbios = proto_register_protocol("NetBIOS", "NetBIOS", "netbios");
- proto_register_subtree_array(ett, array_length(ett));
+ proto_register_subtree_array(ett, array_length(ett));
proto_register_field_array(proto_netbios, hf_netb, array_length(hf_netb));
+
+ register_heur_dissector_list("netbios", &netbios_heur_subdissector_list);
+
+ netbios_module = prefs_register_protocol(proto_netbios, NULL);
+ prefs_register_bool_preference(netbios_module, "defragment",
+ "Defragment all NetBIOS messages spanning multiple frames",
+ "Whether the NetBIOS dissector should defragment all messages spanning multiple frames",
+ &netbios_defragment);
+
+ register_init_routine(netbios_init);
}
void
proto_reg_handoff_netbios(void)
{
- dissector_add("llc.dsap", SAP_NETBIOS, dissect_netbios, proto_netbios);
+ dissector_handle_t netbios_handle;
+
+ netbios_handle = create_dissector_handle(dissect_netbios,
+ proto_netbios);
+ dissector_add("llc.dsap", SAP_NETBIOS, netbios_handle);
+ data_handle = find_dissector("data");
}