Make the "Save only marked frames" button in the "Save As..." dialog box
[obnox/wireshark/wip.git] / packet-nbns.c
index 1aca505c758a198948ce581226e20dee8cb872ce..b64d3bc3cc674a4f34d4873ad8dad4dd7bf581bc 100644 (file)
@@ -1,15 +1,13 @@
 /* packet-nbns.c
- * Routines for NetBIOS Name Service, Datagram Service, and Session Service
- * packet disassembly (the name dates back to when it had only NBNS)
- * Gilbert Ramirez <gram@verdict.uthscsa.edu>
- * Much stuff added by Guy Harris <guy@netapp.com>
+ * Routines for NetBIOS-over-TCP packet disassembly (the name dates back
+ * to when it had only NBNS)
+ * Guy Harris <guy@alum.mit.edu>
  *
- * $Id: packet-nbns.c,v 1.19 1999/05/10 22:07:09 guy Exp $
+ * $Id: packet-nbns.c,v 1.66 2001/12/03 03:59:37 guy Exp $
  *
  * 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
 #include <stdio.h>
 #include <string.h>
 #include <glib.h>
+
 #include "packet.h"
 #include "packet-dns.h"
-#include "util.h"
+#include "packet-netbios.h"
+#include "packet-tcp.h"
+#include "prefs.h"
+
+static int proto_nbns = -1;
+static int hf_nbns_response = -1;
+static int hf_nbns_query = -1;
+static int hf_nbns_transaction_id = -1;
+static int hf_nbns_count_questions = -1;
+static int hf_nbns_count_answers = -1;
+static int hf_nbns_count_auth_rr = -1;
+static int hf_nbns_count_add_rr = -1;
+
+static gint ett_nbns = -1;
+static gint ett_nbns_qd = -1;
+static gint ett_nbns_flags = -1;
+static gint ett_nbns_nb_flags = -1;
+static gint ett_nbns_name_flags = -1;
+static gint ett_nbns_rr = -1;
+static gint ett_nbns_qry = -1;
+static gint ett_nbns_ans = -1;
+
+static int proto_nbdgm = -1;
+static int hf_nbdgm_type = -1;
+static int hf_nbdgm_fragment = -1;
+static int hf_nbdgm_first = -1;
+static int hf_nbdgm_node_type = -1;
+static int hf_nbdgm_datagram_id = -1;
+static int hf_nbdgm_src_ip = -1;
+static int hf_nbdgm_src_port = -1;
+
+static gint ett_nbdgm = -1;
+
+static int proto_nbss = -1;
+static int hf_nbss_type = -1;
+static int hf_nbss_flags = -1;
+
+static gint ett_nbss = -1;
+static gint ett_nbss_flags = -1;
+
+/* desegmentation of NBSS over TCP */
+static gboolean nbss_desegment = TRUE;
+
+/* See RFC 1001 and 1002 for information on the first three, and see
+
+       http://www.cifs.com/specs/draft-leach-cifs-v1-spec-01.txt
+
+   Appendix B, and various messages on the CIFS mailing list such as
+
+       http://discuss.microsoft.com/SCRIPTS/WA-MSD.EXE?A2=ind9811A&L=cifs&P=R386
+
+   for information on the fourth. */
+#define UDP_PORT_NBNS  137
+#define UDP_PORT_NBDGM 138
+#define TCP_PORT_NBSS  139
+#define TCP_PORT_CIFS  445
 
 /* Packet structure taken from RFC 1002. See also RFC 1001.
  * Opcode, flags, and rcode treated as "flags", similarly to DNS,
@@ -142,51 +196,21 @@ nbns_type_name (int type)
        return "unknown";
 }
 
-/* "Canonicalize" a 16-character NetBIOS name by:
- *
- *     removing and saving the last byte;
- *
- *     stripping trailing blanks;
- *
- *     appending the trailing byte, as a hex number, in square brackets. */
-static char *
-canonicalize_netbios_name(char *nbname)
-{
-       char *pnbname;
-       u_char lastchar;
-
-       /* Get the last character of the name, as it's a special number
-        * indicating the type of the name, rather than part of the name
-        * *per se*. */
-       pnbname = nbname + 15;  /* point to the 16th character */
-       lastchar = *(unsigned char *)pnbname;
-
-       /* Now strip off any trailing blanks used to pad it to
-        * 16 bytes. */
-       while (pnbname > &nbname[0]) {
-               if (*(pnbname - 1) != ' ')
-                       break;          /* found non-blank character */
-               pnbname--;              /* blank - skip over it */
-       }
-
-       /* Replace the last character with its hex value, in square
-        * brackets, to make it easier to tell what it is. */
-       sprintf(pnbname, "[%02X]", lastchar);
-       pnbname += 4;
-       return pnbname;
-}
+#define NBNAME_BUF_LEN 128
 
 static int
-get_nbns_name(const u_char *nbns_data_ptr, const u_char *pd,
-    int offset, char *name_ret)
+get_nbns_name(tvbuff_t *tvb, int offset, int nbns_data_offset,
+    char *name_ret, int *name_type_ret)
 {
        int name_len;
        char name[MAXDNAME];
-       char nbname[MAXDNAME+4];        /* 4 for [<last char>] */
+       char nbname[NBNAME_BUF_LEN];
        char *pname, *pnbname, cname, cnbname;
+       int name_type;
+
+       name_len = get_dns_name(tvb, offset, nbns_data_offset, name,
+           sizeof(name));
 
-       name_len = get_dns_name(nbns_data_ptr, pd, offset, name, sizeof(name));
-       
        /* OK, now undo the first-level encoding. */
        pname = &name[0];
        pnbname = &nbname[0];
@@ -226,49 +250,64 @@ get_nbns_name(const u_char *nbns_data_ptr, const u_char *pd,
                cnbname |= cname;
                pname++;
 
-               /* Store the character. */
-               *pnbname++ = cnbname;
+               /* Do we have room to store the character? */
+               if (pnbname < &nbname[NETBIOS_NAME_LEN]) {
+                       /* Yes - store the character. */
+                       *pnbname = cnbname;
+               }
+
+               /* We bump the pointer even if it's past the end of the
+                  name, so we keep track of how long the name is. */
+               pnbname++;
        }
 
        /* NetBIOS names are supposed to be exactly 16 bytes long. */
-       if (pnbname - nbname == 16) {
-               /* This one is; canonicalize its name. */
-               pnbname = canonicalize_netbios_name(nbname);
-       } else {
+       if (pnbname - nbname != NETBIOS_NAME_LEN) {
+               /* It's not. */
                sprintf(nbname, "Illegal NetBIOS name (%ld bytes long)",
                    (long)(pnbname - nbname));
                goto bad;
        }
+
+       /* This one is; make its name printable. */
+       name_type = process_netbios_name(nbname, name_ret);
+       name_ret += strlen(name_ret);
+       sprintf(name_ret, "<%02x>", name_type);
+       name_ret += 4;
        if (cname == '.') {
                /* We have a scope ID, starting at "pname"; append that to
                 * the decoded host name. */
-               strcpy(pnbname, pname);
-       } else {
-               /* Terminate the decoded host name. */
-               *pnbname = '\0';
+               strcpy(name_ret, pname);
        }
+       if (name_type_ret != NULL)
+               *name_type_ret = name_type;
+       return name_len;
 
 bad:
+       if (name_type_ret != NULL)
+               *name_type_ret = -1;
        strcpy (name_ret, nbname);
        return name_len;
 }
 
 
 static int
-get_nbns_name_type_class(const u_char *nbns_data_ptr, const u_char *pd,
-    int offset, char *name_ret, int *name_len_ret, int *type_ret,
+get_nbns_name_type_class(tvbuff_t *tvb, int offset, int nbns_data_offset,
+    char *name_ret, int *name_len_ret, int *name_type_ret, int *type_ret,
     int *class_ret)
 {
        int name_len;
        int type;
        int class;
 
-       name_len = get_nbns_name(nbns_data_ptr, pd, offset, name_ret);
+       name_len = get_nbns_name(tvb, offset, nbns_data_offset, name_ret,
+          name_type_ret);
        offset += name_len;
        
-       type = pntohs(&pd[offset]);
+       type = tvb_get_ntohs(tvb, offset);
        offset += 2;
-       class = pntohs(&pd[offset]);
+
+       class = tvb_get_ntohs(tvb, offset);
 
        *type_ret = type;
        *class_ret = class;
@@ -277,52 +316,69 @@ get_nbns_name_type_class(const u_char *nbns_data_ptr, const u_char *pd,
        return name_len + 4;
 }
 
+static void
+add_name_and_type(proto_tree *tree, tvbuff_t *tvb, int offset, int len,
+    char *tag, char *name, int name_type)
+{
+       if (name_type != -1) {
+               proto_tree_add_text(tree, tvb, offset, len, "%s: %s (%s)",
+                   tag, name, netbios_name_type_descr(name_type));
+       } else {
+               proto_tree_add_text(tree, tvb, offset, len, "%s: %s",
+                   tag, name);
+       }
+}
 
 static int
-dissect_nbns_query(const u_char *nbns_data_ptr, const u_char *pd, int offset,
-    proto_tree *nbns_tree)
+dissect_nbns_query(tvbuff_t *tvb, int offset, int nbns_data_offset,
+    frame_data *fd, proto_tree *nbns_tree)
 {
        int len;
-       char name[MAXDNAME];
+       char name[(NETBIOS_NAME_LEN - 1)*4 + MAXDNAME];
        int name_len;
+       int name_type;
        int type;
        int class;
        char *class_name;
        char *type_name;
-       const u_char *dptr;
-       const u_char *data_start;
+       int data_offset;
+       int data_start;
        proto_tree *q_tree;
        proto_item *tq;
 
-       data_start = dptr = pd + offset;
+       data_start = data_offset = offset;
 
-       len = get_nbns_name_type_class(nbns_data_ptr, pd, offset, name,
-           &name_len, &type, &class);
-       dptr += len;
+       len = get_nbns_name_type_class(tvb, offset, nbns_data_offset, name,
+           &name_len, &name_type, &type, &class);
+       data_offset += len;
 
        type_name = nbns_type_name(type);
        class_name = dns_class_name(class);
 
-       tq = proto_tree_add_item(nbns_tree, offset, len, "%s: type %s, class %s", 
-           name, type_name, class_name);
-       q_tree = proto_tree_new();
-       proto_item_add_subtree(tq, q_tree, ETT_NBNS_QD);
+       if (fd != NULL)
+               col_append_fstr(fd, COL_INFO, " %s %s", type_name, name);
+       if (nbns_tree != NULL) {
+               tq = proto_tree_add_text(nbns_tree, tvb, offset, len,
+                   "%s: type %s, class %s",  name, type_name, class_name);
+               q_tree = proto_item_add_subtree(tq, ett_nbns_qd);
 
-       proto_tree_add_item(q_tree, offset, name_len, "Name: %s", name);
-       offset += name_len;
+               add_name_and_type(q_tree, tvb, offset, name_len, "Name", name,
+                   name_type);
+               offset += name_len;
 
-       proto_tree_add_item(q_tree, offset, 2, "Type: %s", type_name);
-       offset += 2;
+               proto_tree_add_text(q_tree, tvb, offset, 2, "Type: %s", type_name);
+               offset += 2;
 
-       proto_tree_add_item(q_tree, offset, 2, "Class: %s", class_name);
-       offset += 2;
+               proto_tree_add_text(q_tree, tvb, offset, 2, "Class: %s", class_name);
+               offset += 2;
+       }
        
-       return dptr - data_start;
+       return data_offset - data_start;
 }
 
 static void
-nbns_add_nbns_flags(proto_tree *nbns_tree, int offset, u_short flags,
-    int is_wack)
+nbns_add_nbns_flags(proto_tree *nbns_tree, tvbuff_t *tvb, int offset,
+    u_short flags, int is_wack)
 {
        char buf[128+1];
        proto_tree *field_tree;
@@ -347,49 +403,48 @@ nbns_add_nbns_flags(proto_tree *nbns_tree, int offset, u_short flags,
                strcat(buf, val_to_str(flags & F_RCODE, rcode_vals,
                    "Unknown error"));
        }
-       tf = proto_tree_add_item(nbns_tree, offset, 2,
+       tf = proto_tree_add_text(nbns_tree, tvb, offset, 2,
                        "Flags: 0x%04x (%s)", flags, buf);
-       field_tree = proto_tree_new();
-       proto_item_add_subtree(tf, field_tree, ETT_NBNS_FLAGS);
-       proto_tree_add_item(field_tree, offset, 2, "%s",
+       field_tree = proto_item_add_subtree(tf, ett_nbns_flags);
+       proto_tree_add_text(field_tree, tvb, offset, 2, "%s",
                        decode_boolean_bitfield(flags, F_RESPONSE,
                                2*8, "Response", "Query"));
-       proto_tree_add_item(field_tree, offset, 2, "%s",
+       proto_tree_add_text(field_tree, tvb, offset, 2, "%s",
                        decode_enumerated_bitfield(flags, F_OPCODE,
                                2*8, opcode_vals, "%s"));
        if (flags & F_RESPONSE) {
-               proto_tree_add_item(field_tree, offset, 2,
+               proto_tree_add_text(field_tree, tvb, offset, 2,
                        "%s",
                        decode_boolean_bitfield(flags, F_AUTHORITATIVE,
                                2*8,
                                "Server is an authority for domain",
                                "Server isn't an authority for domain"));
        }
-       proto_tree_add_item(field_tree, offset, 2, "%s",
+       proto_tree_add_text(field_tree, tvb, offset, 2, "%s",
                        decode_boolean_bitfield(flags, F_TRUNCATED,
                                2*8,
                                "Message is truncated",
                                "Message is not truncated"));
-       proto_tree_add_item(field_tree, offset, 2, "%s",
+       proto_tree_add_text(field_tree, tvb, offset, 2, "%s",
                        decode_boolean_bitfield(flags, F_RECDESIRED,
                                2*8,
                                "Do query recursively",
                                "Don't do query recursively"));
        if (flags & F_RESPONSE) {
-               proto_tree_add_item(field_tree, offset, 2,
+               proto_tree_add_text(field_tree, tvb, offset, 2,
                        "%s",
                        decode_boolean_bitfield(flags, F_RECAVAIL,
                                2*8,
                                "Server can do recursive queries",
                                "Server can't do recursive queries"));
        }
-       proto_tree_add_item(field_tree, offset, 2, "%s",
+       proto_tree_add_text(field_tree, tvb, offset, 2, "%s",
                        decode_boolean_bitfield(flags, F_BROADCAST,
                                2*8,
                                "Broadcast packet",
                                "Not a broadcast packet"));
        if (flags & F_RESPONSE && !is_wack) {
-               proto_tree_add_item(field_tree, offset, 2,
+               proto_tree_add_text(field_tree, tvb, offset, 2,
                        "%s",
                        decode_enumerated_bitfield(flags, F_RCODE,
                                2*8,
@@ -398,7 +453,7 @@ nbns_add_nbns_flags(proto_tree *nbns_tree, int offset, u_short flags,
 }
 
 static void
-nbns_add_nb_flags(proto_tree *rr_tree, int offset, u_short flags)
+nbns_add_nb_flags(proto_tree *rr_tree, tvbuff_t *tvb, int offset, u_short flags)
 {
        char buf[128+1];
        proto_tree *field_tree;
@@ -418,22 +473,22 @@ nbns_add_nb_flags(proto_tree *rr_tree, int offset, u_short flags)
                strcat(buf, "group");
        else
                strcat(buf, "unique");
-       tf = proto_tree_add_item(rr_tree, offset, 2, "Flags: 0x%x (%s)", flags,
+       tf = proto_tree_add_text(rr_tree, tvb, offset, 2, "Flags: 0x%x (%s)", flags,
                        buf);
-       field_tree = proto_tree_new();
-       proto_item_add_subtree(tf, field_tree, ETT_NBNS_NB_FLAGS);
-       proto_tree_add_item(field_tree, offset, 2, "%s",
+       field_tree = proto_item_add_subtree(tf, ett_nbns_nb_flags);
+       proto_tree_add_text(field_tree, tvb, offset, 2, "%s",
                        decode_boolean_bitfield(flags, NB_FLAGS_G,
                                2*8,
                                "Group name",
                                "Unique name"));
-       proto_tree_add_item(field_tree, offset, 2, "%s",
+       proto_tree_add_text(field_tree, tvb, offset, 2, "%s",
                        decode_enumerated_bitfield(flags, NB_FLAGS_ONT,
                                2*8, nb_flags_ont_vals, "%s"));
 }
 
 static void
-nbns_add_name_flags(proto_tree *rr_tree, int offset, u_short flags)
+nbns_add_name_flags(proto_tree *rr_tree, tvbuff_t *tvb, int offset,
+    u_short flags)
 {
        char buf[128+1];
        proto_item *field_tree;
@@ -460,34 +515,33 @@ nbns_add_name_flags(proto_tree *rr_tree, int offset, u_short flags)
                strcat(buf, ", active");
        if (flags & NAME_FLAGS_PRM)
                strcat(buf, ", permanent node name");
-       tf = proto_tree_add_item(rr_tree, offset, 2, "Name flags: 0x%x (%s)",
+       tf = proto_tree_add_text(rr_tree, tvb, offset, 2, "Name flags: 0x%x (%s)",
                        flags, buf);
-       field_tree = proto_tree_new();
-       proto_item_add_subtree(tf, field_tree, ETT_NBNS_NAME_FLAGS);
-       proto_tree_add_item(field_tree, offset, 2, "%s",
+       field_tree = proto_item_add_subtree(tf, ett_nbns_name_flags);
+       proto_tree_add_text(field_tree, tvb, offset, 2, "%s",
                        decode_boolean_bitfield(flags, NAME_FLAGS_G,
                                2*8,
                                "Group name",
                                "Unique name"));
-       proto_tree_add_item(field_tree, offset, 2, "%s",
+       proto_tree_add_text(field_tree, tvb, offset, 2, "%s",
                        decode_enumerated_bitfield(flags, NAME_FLAGS_ONT,
                                2*8, name_flags_ont_vals, "%s"));
-       proto_tree_add_item(field_tree, offset, 2, "%s",
+       proto_tree_add_text(field_tree, tvb, offset, 2, "%s",
                        decode_boolean_bitfield(flags, NAME_FLAGS_DRG,
                                2*8,
                                "Name is being deregistered",
                                "Name is not being deregistered"));
-       proto_tree_add_item(field_tree, offset, 2, "%s",
+       proto_tree_add_text(field_tree, tvb, offset, 2, "%s",
                        decode_boolean_bitfield(flags, NAME_FLAGS_CNF,
                                2*8,
                                "Name is in conflict",
                                "Name is not in conflict"));
-       proto_tree_add_item(field_tree, offset, 2, "%s",
+       proto_tree_add_text(field_tree, tvb, offset, 2, "%s",
                        decode_boolean_bitfield(flags, NAME_FLAGS_ACT,
                                2*8,
                                "Name is active",
                                "Name is not active"));
-       proto_tree_add_item(field_tree, offset, 2, "%s",
+       proto_tree_add_text(field_tree, tvb, offset, 2, "%s",
                        decode_boolean_bitfield(flags, NAME_FLAGS_PRM,
                                2*8,
                                "Permanent node name",
@@ -495,369 +549,398 @@ nbns_add_name_flags(proto_tree *rr_tree, int offset, u_short flags)
 }
 
 static int
-dissect_nbns_answer(const u_char *nbns_data_ptr, const u_char *pd, int offset,
-    proto_tree *nbns_tree, int opcode)
+dissect_nbns_answer(tvbuff_t *tvb, int offset, int nbns_data_offset,
+    frame_data *fd, proto_tree *nbns_tree, int opcode)
 {
        int len;
-       char name[MAXDNAME];
+       char name[(NETBIOS_NAME_LEN - 1)*4 + MAXDNAME + 64];
        int name_len;
+       int name_type;
        int type;
        int class;
        char *class_name;
        char *type_name;
-       const u_char *dptr;
-       const u_char *data_start;
+       int data_offset;
+       int cur_offset;
+       int data_start;
        u_int ttl;
        u_short data_len;
        u_short flags;
        proto_tree *rr_tree;
        proto_item *trr;
+       char name_str[(NETBIOS_NAME_LEN - 1)*4 + 1];
+       u_int num_names;
+       char nbname[16+4+1];    /* 4 for [<last char>] */
+       u_short name_flags;
 
-       data_start = dptr = pd + offset;
+       data_start = data_offset = offset;
+       cur_offset = offset;
 
-       len = get_nbns_name_type_class(nbns_data_ptr, pd, offset, name,
-           &name_len, &type, &class);
-       dptr += len;
+       len = get_nbns_name_type_class(tvb, offset, nbns_data_offset, name,
+           &name_len, &name_type, &type, &class);
+       data_offset += len;
+       cur_offset += len;
 
        type_name = nbns_type_name(type);
        class_name = dns_class_name(class);
 
-       ttl = pntohl(dptr);
-       dptr += 4;
+       ttl = tvb_get_ntohl(tvb, data_offset);
+       data_offset += 4;
+       cur_offset += 4;
 
-       data_len = pntohs(dptr);
-       dptr += 2;
+       data_len = tvb_get_ntohs(tvb, data_offset);
+       data_offset += 2;
+       cur_offset += 2;
 
        switch (type) {
        case T_NB:              /* "NB" record */
-               trr = proto_tree_add_item(nbns_tree, offset,
-                   (dptr - data_start) + data_len,
+               if (fd != NULL) {
+                       if (opcode != OPCODE_WACK) {
+                               col_append_fstr(fd, COL_INFO, " %s %s",
+                                   type_name,
+                                   ip_to_str(tvb_get_ptr(tvb, data_offset+2, 4)));
+                       }
+               }
+               if (nbns_tree == NULL)
+                       break;
+               trr = proto_tree_add_text(nbns_tree, tvb, offset,
+                   (data_offset - data_start) + data_len,
                    "%s: type %s, class %s",
                    name, type_name, class_name);
-               rr_tree = add_rr_to_tree(trr, ETT_NBNS_RR, offset, name,
+               strcat(name, " (");
+               strcat(name, netbios_name_type_descr(name_type));
+               strcat(name, ")");
+               rr_tree = add_rr_to_tree(trr, ett_nbns_rr, tvb, offset, name,
                    name_len, type_name, class_name, ttl, data_len);
-               offset += (dptr - data_start);
                while (data_len > 0) {
                        if (opcode == OPCODE_WACK) {
                                /* WACK response.  This doesn't contain the
                                 * same type of RR data as other T_NB
                                 * responses.  */
                                if (data_len < 2) {
-                                       proto_tree_add_item(rr_tree, offset,
+                                       proto_tree_add_text(rr_tree, tvb, cur_offset,
                                            data_len, "(incomplete entry)");
                                        break;
                                }
-                               flags = pntohs(dptr);
-                               dptr += 2;
-                               nbns_add_nbns_flags(rr_tree, offset, flags, 1);
-                               offset += 2;
+                               flags = tvb_get_ntohs(tvb, cur_offset);
+                               nbns_add_nbns_flags(rr_tree, tvb, cur_offset,
+                                   flags, 1);
+                               cur_offset += 2;
                                data_len -= 2;
                        } else {
                                if (data_len < 2) {
-                                       proto_tree_add_item(rr_tree, offset,
+                                       proto_tree_add_text(rr_tree, tvb, cur_offset,
                                            data_len, "(incomplete entry)");
                                        break;
                                }
-                               flags = pntohs(dptr);
-                               dptr += 2;
-                               nbns_add_nb_flags(rr_tree, offset, flags);
-                               offset += 2;
+                               flags = tvb_get_ntohs(tvb, cur_offset);
+                               nbns_add_nb_flags(rr_tree, tvb, cur_offset,
+                                   flags);
+                               cur_offset += 2;
                                data_len -= 2;
 
                                if (data_len < 4) {
-                                       proto_tree_add_item(rr_tree, offset,
+                                       proto_tree_add_text(rr_tree, tvb, cur_offset,
                                            data_len, "(incomplete entry)");
                                        break;
                                }
-                               proto_tree_add_item(rr_tree, offset, 4,
+                               proto_tree_add_text(rr_tree, tvb, cur_offset, 4,
                                    "Addr: %s",
-                                   ip_to_str((guint8 *)dptr));
-                               dptr += 4;
-                               offset += 4;
+                                   ip_to_str(tvb_get_ptr(tvb, cur_offset, 4)));
+                               cur_offset += 4;
                                data_len -= 4;
                        }
                }
                break;
 
        case T_NBSTAT:  /* "NBSTAT" record */
-               {
-                       u_int num_names;
-                       char nbname[16+4+1];    /* 4 for [<last char>] */
-                       u_short name_flags;
-                       
-                       trr = proto_tree_add_item(nbns_tree, offset,
-                           (dptr - data_start) + data_len,
-                           "%s: type %s, class %s",
-                           name, type_name, class_name);
-                       rr_tree = add_rr_to_tree(trr, ETT_NBNS_RR, offset, name,
-                           name_len, type_name, class_name, ttl, data_len);
-                       offset += (dptr - data_start);
-                       if (data_len < 1) {
-                               proto_tree_add_item(rr_tree, offset,
-                                   data_len, "(incomplete entry)");
-                               break;
-                       }
-                       num_names = *dptr;
-                       dptr += 1;
-                       proto_tree_add_item(rr_tree, offset, 2,
-                           "Number of names: %u", num_names);
-                       offset += 1;
-
-                       while (num_names != 0) {
-                               if (data_len < 16) {
-                                       proto_tree_add_item(rr_tree, offset,
-                                           data_len, "(incomplete entry)");
-                                       goto out;
-                               }
-                               memcpy(nbname, dptr, 16);
-                               dptr += 16;
-                               canonicalize_netbios_name(nbname);
-                               proto_tree_add_item(rr_tree, offset, 16,
-                                   "Name: %s", nbname);
-                               offset += 16;
-                               data_len -= 16;
-
-                               if (data_len < 2) {
-                                       proto_tree_add_item(rr_tree, offset,
-                                           data_len, "(incomplete entry)");
-                                       goto out;
-                               }
-                               name_flags = pntohs(dptr);
-                               dptr += 2;
-                               nbns_add_name_flags(rr_tree, offset, name_flags);
-                               offset += 2;
-                               data_len -= 2;
-
-                               num_names--;
-                       }
-
-                       if (data_len < 6) {
-                               proto_tree_add_item(rr_tree, offset,
-                                   data_len, "(incomplete entry)");
-                               break;
-                       }
-                       proto_tree_add_item(rr_tree, offset, 6,
-                           "Unit ID: %s",
-                           ether_to_str((guint8 *)dptr));
-                       dptr += 6;
-                       offset += 6;
-                       data_len -= 6;
-
-                       if (data_len < 1) {
-                               proto_tree_add_item(rr_tree, offset,
-                                   data_len, "(incomplete entry)");
-                               break;
-                       }
-                       proto_tree_add_item(rr_tree, offset, 1,
-                           "Jumpers: 0x%x", *dptr);
-                       dptr += 1;
-                       offset += 1;
-                       data_len -= 1;
-
-                       if (data_len < 1) {
-                               proto_tree_add_item(rr_tree, offset,
-                                   data_len, "(incomplete entry)");
-                               break;
-                       }
-                       proto_tree_add_item(rr_tree, offset, 1,
-                           "Test result: 0x%x", *dptr);
-                       dptr += 1;
-                       offset += 1;
-                       data_len -= 1;
-
-                       if (data_len < 2) {
-                               proto_tree_add_item(rr_tree, offset,
-                                   data_len, "(incomplete entry)");
-                               break;
-                       }
-                       proto_tree_add_item(rr_tree, offset, 2,
-                           "Version number: 0x%x", pntohs(dptr));
-                       dptr += 2;
-                       offset += 2;
-                       data_len -= 2;
-
-                       if (data_len < 2) {
-                               proto_tree_add_item(rr_tree, offset,
-                                   data_len, "(incomplete entry)");
-                               break;
-                       }
-                       proto_tree_add_item(rr_tree, offset, 2,
-                           "Period of statistics: 0x%x", pntohs(dptr));
-                       dptr += 2;
-                       offset += 2;
-                       data_len -= 2;
-
-                       if (data_len < 2) {
-                               proto_tree_add_item(rr_tree, offset,
-                                   data_len, "(incomplete entry)");
-                               break;
-                       }
-                       proto_tree_add_item(rr_tree, offset, 2,
-                           "Number of CRCs: %u", pntohs(dptr));
-                       dptr += 2;
-                       offset += 2;
-                       data_len -= 2;
-
-                       if (data_len < 2) {
-                               proto_tree_add_item(rr_tree, offset,
-                                   data_len, "(incomplete entry)");
-                               break;
-                       }
-                       proto_tree_add_item(rr_tree, offset, 2,
-                           "Number of alignment errors: %u", pntohs(dptr));
-                       dptr += 2;
-                       offset += 2;
-                       data_len -= 2;
-
-                       if (data_len < 2) {
-                               proto_tree_add_item(rr_tree, offset,
-                                   data_len, "(incomplete entry)");
-                               break;
-                       }
-                       proto_tree_add_item(rr_tree, offset, 2,
-                           "Number of collisions: %u", pntohs(dptr));
-                       dptr += 2;
-                       offset += 2;
-                       data_len -= 2;
-
-                       if (data_len < 2) {
-                               proto_tree_add_item(rr_tree, offset,
-                                   data_len, "(incomplete entry)");
-                               break;
-                       }
-                       proto_tree_add_item(rr_tree, offset, 2,
-                           "Number of send aborts: %u", pntohs(dptr));
-                       dptr += 2;
-                       offset += 2;
-                       data_len -= 2;
-
-                       if (data_len < 4) {
-                               proto_tree_add_item(rr_tree, offset,
-                                   data_len, "(incomplete entry)");
-                               break;
-                       }
-                       proto_tree_add_item(rr_tree, offset, 4,
-                           "Number of good sends: %u", pntohl(dptr));
-                       dptr += 4;
-                       offset += 4;
-                       data_len -= 4;
-
-                       if (data_len < 4) {
-                               proto_tree_add_item(rr_tree, offset,
-                                   data_len, "(incomplete entry)");
-                               break;
-                       }
-                       proto_tree_add_item(rr_tree, offset, 4,
-                           "Number of good receives: %u", pntohl(dptr));
-                       dptr += 4;
-                       offset += 4;
-                       data_len -= 4;
-
-                       if (data_len < 2) {
-                               proto_tree_add_item(rr_tree, offset,
-                                   data_len, "(incomplete entry)");
-                               break;
-                       }
-                       proto_tree_add_item(rr_tree, offset, 2,
-                           "Number of retransmits: %u", pntohs(dptr));
-                       dptr += 2;
-                       offset += 2;
-                       data_len -= 2;
-
-                       if (data_len < 2) {
-                               proto_tree_add_item(rr_tree, offset,
-                                   data_len, "(incomplete entry)");
-                               break;
-                       }
-                       proto_tree_add_item(rr_tree, offset, 2,
-                           "Number of no resource conditions: %u", pntohs(dptr));
-                       dptr += 2;
-                       offset += 2;
-                       data_len -= 2;
-
-                       if (data_len < 2) {
-                               proto_tree_add_item(rr_tree, offset,
+               if (fd != NULL)
+                       col_append_fstr(fd, COL_INFO, " %s", type_name);
+               if (nbns_tree == NULL)
+                       break;
+               trr = proto_tree_add_text(nbns_tree, tvb, offset,
+                   (data_offset - data_start) + data_len,
+                   "%s: type %s, class %s",
+                   name, type_name, class_name);
+               rr_tree = add_rr_to_tree(trr, ett_nbns_rr, tvb, offset, name,
+                   name_len, type_name, class_name, ttl, data_len);
+               if (data_len < 1) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               num_names = tvb_get_guint8(tvb, cur_offset);
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 1,
+                   "Number of names: %u", num_names);
+               cur_offset += 1;
+
+               while (num_names != 0) {
+                       if (data_len < NETBIOS_NAME_LEN) {
+                               proto_tree_add_text(rr_tree, tvb, cur_offset,
                                    data_len, "(incomplete entry)");
-                               break;
+                               goto out;
                        }
-                       proto_tree_add_item(rr_tree, offset, 2,
-                           "Number of command blocks: %u", pntohs(dptr));
-                       dptr += 2;
-                       offset += 2;
-                       data_len -= 2;
+                       tvb_memcpy(tvb, (guint8 *)nbname, cur_offset,
+                           NETBIOS_NAME_LEN);
+                       name_type = process_netbios_name(nbname,
+                           name_str);
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           NETBIOS_NAME_LEN, "Name: %s<%02x> (%s)",
+                           name_str, name_type,
+                           netbios_name_type_descr(name_type));
+                       cur_offset += NETBIOS_NAME_LEN;
+                       data_len -= NETBIOS_NAME_LEN;
 
                        if (data_len < 2) {
-                               proto_tree_add_item(rr_tree, offset,
+                               proto_tree_add_text(rr_tree, tvb, cur_offset,
                                    data_len, "(incomplete entry)");
-                               break;
+                               goto out;
                        }
-                       proto_tree_add_item(rr_tree, offset, 2,
-                           "Number of pending sessions: %u", pntohs(dptr));
-                       dptr += 2;
-                       offset += 2;
+                       name_flags = tvb_get_ntohs(tvb, cur_offset);
+                       nbns_add_name_flags(rr_tree, tvb, cur_offset,
+                           name_flags);
+                       cur_offset += 2;
                        data_len -= 2;
 
-                       if (data_len < 2) {
-                               proto_tree_add_item(rr_tree, offset,
-                                   data_len, "(incomplete entry)");
-                               break;
-                       }
-                       proto_tree_add_item(rr_tree, offset, 2,
-                           "Max number of pending sessions: %u", pntohs(dptr));
-                       dptr += 2;
-                       offset += 2;
-
-                       proto_tree_add_item(rr_tree, offset, 2,
-                           "Max total sessions possible: %u", pntohs(dptr));
-                       dptr += 2;
-                       offset += 2;
-                       data_len -= 2;
+                       num_names--;
+               }
 
-                       if (data_len < 2) {
-                               proto_tree_add_item(rr_tree, offset,
-                                   data_len, "(incomplete entry)");
-                               break;
-                       }
-                       proto_tree_add_item(rr_tree, offset, 2,
-                           "Session data packet size: %u", pntohs(dptr));
-                       dptr += 2;
-                       offset += 2;
-                       data_len -= 2;
+               if (data_len < 6) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 6,
+                   "Unit ID: %s",
+                   ether_to_str(tvb_get_ptr(tvb, cur_offset, 6)));
+               cur_offset += 6;
+               data_len -= 6;
+
+               if (data_len < 1) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 1,
+                   "Jumpers: 0x%x", tvb_get_guint8(tvb, cur_offset));
+               cur_offset += 1;
+               data_len -= 1;
+
+               if (data_len < 1) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 1,
+                   "Test result: 0x%x", tvb_get_guint8(tvb, cur_offset));
+               cur_offset += 1;
+               data_len -= 1;
+
+               if (data_len < 2) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 2,
+                   "Version number: 0x%x", tvb_get_ntohs(tvb, cur_offset));
+               cur_offset += 2;
+               data_len -= 2;
+
+               if (data_len < 2) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 2,
+                   "Period of statistics: 0x%x",
+                   tvb_get_ntohs(tvb, cur_offset));
+               cur_offset += 2;
+               data_len -= 2;
+
+               if (data_len < 2) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 2,
+                   "Number of CRCs: %u", tvb_get_ntohs(tvb, cur_offset));
+               cur_offset += 2;
+               data_len -= 2;
+
+               if (data_len < 2) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 2,
+                   "Number of alignment errors: %u",
+                   tvb_get_ntohs(tvb, cur_offset));
+               cur_offset += 2;
+               data_len -= 2;
+
+               if (data_len < 2) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 2,
+                   "Number of collisions: %u", tvb_get_ntohs(tvb, cur_offset));
+               cur_offset += 2;
+               data_len -= 2;
+
+               if (data_len < 2) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 2,
+                   "Number of send aborts: %u", tvb_get_ntohs(tvb, cur_offset));
+               cur_offset += 2;
+               data_len -= 2;
+
+               if (data_len < 4) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 4,
+                   "Number of good sends: %u", tvb_get_ntohl(tvb, cur_offset));
+               cur_offset += 4;
+               data_len -= 4;
+
+               if (data_len < 4) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
                }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 4,
+                   "Number of good receives: %u",
+                   tvb_get_ntohl(tvb, cur_offset));
+               cur_offset += 4;
+               data_len -= 4;
+
+               if (data_len < 2) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 2,
+                   "Number of retransmits: %u", tvb_get_ntohs(tvb, cur_offset));
+               cur_offset += 2;
+               data_len -= 2;
+
+               if (data_len < 2) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 2,
+                   "Number of no resource conditions: %u",
+                   tvb_get_ntohs(tvb, cur_offset));
+               cur_offset += 2;
+               data_len -= 2;
+
+               if (data_len < 2) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 2,
+                   "Number of command blocks: %u",
+                   tvb_get_ntohs(tvb, cur_offset));
+               cur_offset += 2;
+               data_len -= 2;
+
+               if (data_len < 2) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 2,
+                   "Number of pending sessions: %u",
+                   tvb_get_ntohs(tvb, cur_offset));
+               cur_offset += 2;
+               data_len -= 2;
+
+               if (data_len < 2) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 2,
+                   "Max number of pending sessions: %u",
+                   tvb_get_ntohs(tvb, cur_offset));
+               cur_offset += 2;
+               data_len -= 2;
+
+               if (data_len < 2) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 2,
+                   "Max total sessions possible: %u",
+                   tvb_get_ntohs(tvb, cur_offset));
+               cur_offset += 2;
+               data_len -= 2;
+
+               if (data_len < 2) {
+                       proto_tree_add_text(rr_tree, tvb, cur_offset,
+                           data_len, "(incomplete entry)");
+                       break;
+               }
+               proto_tree_add_text(rr_tree, tvb, cur_offset, 2,
+                   "Session data packet size: %u",
+                   tvb_get_ntohs(tvb, cur_offset));
+               cur_offset += 2;
+               data_len -= 2;
        out:
                break;
 
        default:
-               trr = proto_tree_add_item(nbns_tree, offset,
-                   (dptr - data_start) + data_len,
+               if (fd != NULL)
+                       col_append_fstr(fd, COL_INFO, " %s", type_name);
+               if (nbns_tree == NULL)
+                       break;
+               trr = proto_tree_add_text(nbns_tree, tvb, offset,
+                   (data_offset - data_start) + data_len,
                     "%s: type %s, class %s",
                    name, type_name, class_name);
-               rr_tree = add_rr_to_tree(trr, ETT_NBNS_RR, offset, name,
+               rr_tree = add_rr_to_tree(trr, ett_nbns_rr, tvb, offset, name,
                    name_len, type_name, class_name, ttl, data_len);
-               offset += (dptr - data_start);
-               proto_tree_add_item(rr_tree, offset, data_len, "Data");
+               proto_tree_add_text(rr_tree, tvb, cur_offset, data_len, "Data");
+               cur_offset += data_len;
                break;
        }
-       dptr += data_len;
        
-       return dptr - data_start;
+       return cur_offset - data_start;
 }
 
 static int
-dissect_query_records(const u_char *nbns_data_ptr, int count, const u_char *pd, 
-    int cur_off, proto_tree *nbns_tree)
+dissect_query_records(tvbuff_t *tvb, int cur_off, int nbns_data_offset,
+    int count, frame_data *fd, proto_tree *nbns_tree)
 {
-       int start_off;
-       proto_tree *qatree;
-       proto_item *ti;
+       int start_off, add_off;
+       proto_tree *qatree = NULL;
+       proto_item *ti = NULL;
        
        start_off = cur_off;
-       ti = proto_tree_add_item(nbns_tree, start_off, 0, "Queries");
-       qatree = proto_tree_new();
-       proto_item_add_subtree(ti, qatree, ETT_NBNS_QRY);
-       while (count-- > 0)
-               cur_off += dissect_nbns_query(nbns_data_ptr, pd, cur_off, qatree);
-       proto_item_set_len(ti, cur_off - start_off);
+       if (nbns_tree != NULL) {
+               ti = proto_tree_add_text(nbns_tree, tvb, start_off, 0, "Queries");
+               qatree = proto_item_add_subtree(ti, ett_nbns_qry);
+       }
+       while (count-- > 0) {
+               add_off = dissect_nbns_query(tvb, cur_off, nbns_data_offset,
+                   fd, qatree);
+               if (add_off <= 0) {
+                       /* We ran past the end of the captured data in the
+                          packet. */
+                       break;
+               }
+               cur_off += add_off;
+       }
+       if (ti != NULL)
+               proto_item_set_len(ti, cur_off - start_off);
 
        return cur_off - start_off;
 }
@@ -865,96 +948,145 @@ dissect_query_records(const u_char *nbns_data_ptr, int count, const u_char *pd,
 
 
 static int
-dissect_answer_records(const u_char *nbns_data_ptr, int count,
-    const u_char *pd, int cur_off, proto_tree *nbns_tree, int opcode, char *name)
+dissect_answer_records(tvbuff_t *tvb, int cur_off, int nbns_data_offset,
+    int count, frame_data *fd, proto_tree *nbns_tree, int opcode, char *name)
 {
-       int start_off;
-       proto_tree *qatree;
-       proto_item *ti;
+       int start_off, add_off;
+       proto_tree *qatree = NULL;
+       proto_item *ti = NULL;
        
        start_off = cur_off;
-       ti = proto_tree_add_item(nbns_tree, start_off, 0, name);
-       qatree = proto_tree_new();
-       proto_item_add_subtree(ti, qatree, ETT_NBNS_ANS);
-       while (count-- > 0)
-               cur_off += dissect_nbns_answer(nbns_data_ptr, pd, cur_off,
-                                       qatree, opcode);
-       proto_item_set_len(ti, cur_off - start_off);
+       if (nbns_tree != NULL) {
+               ti = proto_tree_add_text(nbns_tree, tvb, start_off, 0, name);
+               qatree = proto_item_add_subtree(ti, ett_nbns_ans);
+       }
+       while (count-- > 0) {
+               add_off = dissect_nbns_answer(tvb, cur_off, nbns_data_offset,
+                                       fd, qatree, opcode);
+               if (add_off <= 0) {
+                       /* We ran past the end of the captured data in the
+                          packet. */
+                       break;
+               }
+               cur_off += add_off;
+       }
+       if (ti != NULL)
+               proto_item_set_len(ti, cur_off - start_off);
        return cur_off - start_off;
 }
 
-void
-dissect_nbns(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
+static void
+dissect_nbns(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
-       const u_char            *nbns_data_ptr;
-       proto_tree              *nbns_tree;
+       int                     offset = 0;
+       int                     nbns_data_offset;
+       frame_data              *fd;
+       proto_tree              *nbns_tree = NULL;
        proto_item              *ti;
        guint16                 id, flags, quest, ans, auth, add;
        int                     cur_off;
 
-       nbns_data_ptr = &pd[offset];
+       nbns_data_offset = offset;
+
+       if (check_col(pinfo->fd, COL_PROTOCOL))
+               col_set_str(pinfo->fd, COL_PROTOCOL, "NBNS");
+       if (check_col(pinfo->fd, COL_INFO))
+               col_clear(pinfo->fd, COL_INFO);
 
        /* To do: check for runts, errs, etc. */
-       id    = pntohs(&pd[offset + NBNS_ID]);
-       flags = pntohs(&pd[offset + NBNS_FLAGS]);
-       quest = pntohs(&pd[offset + NBNS_QUEST]);
-       ans   = pntohs(&pd[offset + NBNS_ANS]);
-       auth  = pntohs(&pd[offset + NBNS_AUTH]);
-       add   = pntohs(&pd[offset + NBNS_ADD]);
-
-       if (check_col(fd, COL_PROTOCOL))
-               col_add_str(fd, COL_PROTOCOL, "NBNS (UDP)");
-       if (check_col(fd, COL_INFO)) {
-               col_add_fstr(fd, COL_INFO, "%s%s",
+       id    = tvb_get_ntohs(tvb, offset + NBNS_ID);
+       flags = tvb_get_ntohs(tvb, offset + NBNS_FLAGS);
+
+       if (check_col(pinfo->fd, COL_INFO)) {
+               col_add_fstr(pinfo->fd, COL_INFO, "%s%s",
                    val_to_str(flags & F_OPCODE, opcode_vals,
                      "Unknown operation (%x)"),
                    (flags & F_RESPONSE) ? " response" : "");
+               fd = pinfo->fd;
+       } else {
+               /* Set "fd" to NULL; we pass a NULL "fd" to the query and
+                  answer dissectors, as a way of saying that they shouldn't
+                  add stuff to the COL_INFO column (a call to
+                  "check_col(fd, COL_INFO)" is more expensive than a check
+                  that a pointer isn't NULL). */
+               fd = NULL;
        }
 
        if (tree) {
-               ti = proto_tree_add_item(tree, offset, END_OF_FRAME,
-                               "NetBIOS Name Service");
-               nbns_tree = proto_tree_new();
-               proto_item_add_subtree(ti, nbns_tree, ETT_NBNS);
-
-               proto_tree_add_item(nbns_tree, offset + NBNS_ID, 2,
-                               "Transaction ID: 0x%04X", id);
-
-               nbns_add_nbns_flags(nbns_tree, offset + NBNS_FLAGS, flags, 0);
-               proto_tree_add_item(nbns_tree, offset + NBNS_QUEST, 2,
-                                       "Questions: %d",
-                                       quest);
-               proto_tree_add_item(nbns_tree, offset + NBNS_ANS, 2,
-                                       "Answer RRs: %d",
-                                       ans);
-               proto_tree_add_item(nbns_tree, offset + NBNS_AUTH, 2,
-                                       "Authority RRs: %d",
-                                       auth);
-               proto_tree_add_item(nbns_tree, offset + NBNS_ADD, 2,
-                                       "Additional RRs: %d",
-                                       add);
-
-               cur_off = offset + NBNS_HDRLEN;
+               ti = proto_tree_add_item(tree, proto_nbns, tvb, offset,
+                   tvb_length(tvb), FALSE);
+               nbns_tree = proto_item_add_subtree(ti, ett_nbns);
+
+               if (flags & F_RESPONSE) {
+                       proto_tree_add_boolean_hidden(nbns_tree, hf_nbns_response, tvb, 
+                                            0, 0, TRUE);
+               } else {
+                       proto_tree_add_boolean_hidden(nbns_tree, hf_nbns_query, tvb, 
+                                            0, 0, TRUE);
+               }
+
+               proto_tree_add_uint(nbns_tree, hf_nbns_transaction_id, tvb,
+                                   offset + NBNS_ID, 2, id);
+
+               nbns_add_nbns_flags(nbns_tree, tvb, offset + NBNS_FLAGS,
+                                   flags, 0);
+       }
+       quest = tvb_get_ntohs(tvb, offset + NBNS_QUEST);
+       if (tree) {
+               proto_tree_add_uint(nbns_tree, hf_nbns_count_questions, tvb,
+                                   offset + NBNS_QUEST, 2, quest);
+       }
+       ans = tvb_get_ntohs(tvb, offset + NBNS_ANS);
+       if (tree) {
+               proto_tree_add_uint(nbns_tree, hf_nbns_count_answers, tvb,
+                                   offset + NBNS_ANS, 2, ans);
+       }
+       auth = tvb_get_ntohs(tvb, offset + NBNS_AUTH);
+       if (tree) {
+               proto_tree_add_uint(nbns_tree, hf_nbns_count_auth_rr, tvb,
+                                   offset + NBNS_AUTH, 2, auth);
+       }
+       add = tvb_get_ntohs(tvb, offset + NBNS_ADD);
+       if (tree) {
+               proto_tree_add_uint(nbns_tree, hf_nbns_count_add_rr, tvb,
+                                   offset + NBNS_ADD, 2, add);
+       }
+
+       cur_off = offset + NBNS_HDRLEN;
     
-               if (quest > 0)
-                       cur_off += dissect_query_records(nbns_data_ptr,
-                                       quest, pd, cur_off, nbns_tree);
+       if (quest > 0) {
+               /* If this is a response, don't add information about the
+                  queries to the summary, just add information about the
+                  answers. */
+               cur_off += dissect_query_records(tvb, cur_off,
+                   nbns_data_offset, quest,
+                   (!(flags & F_RESPONSE) ? fd : NULL), nbns_tree);
+       }
 
-               if (ans > 0)
-                       cur_off += dissect_answer_records(nbns_data_ptr,
-                                       ans, pd, cur_off, nbns_tree,
-                                       flags & F_OPCODE,
-                                       "Answers");
+       if (ans > 0) {
+               /* If this is a request, don't add information about the
+                  answers to the summary, just add information about the
+                  queries. */
+               cur_off += dissect_answer_records(tvb, cur_off,
+                       nbns_data_offset, ans,
+                       ((flags & F_RESPONSE) ? fd : NULL), nbns_tree,
+                       flags & F_OPCODE, "Answers");
+       }
 
+       if (tree) {
+               /* Don't add information about the authoritative name
+                  servers, or the additional records, to the summary. */
                if (auth > 0)
-                       cur_off += dissect_answer_records(nbns_data_ptr,
-                                       auth, pd, cur_off, nbns_tree, 
+                       cur_off += dissect_answer_records(tvb, cur_off,
+                                       nbns_data_offset,
+                                       auth, NULL, nbns_tree,
                                        flags & F_OPCODE,
                                        "Authoritative nameservers");
 
                if (add > 0)
-                       cur_off += dissect_answer_records(nbns_data_ptr,
-                                       add, pd, cur_off, nbns_tree, 
+                       cur_off += dissect_answer_records(tvb, cur_off,
+                                       nbns_data_offset,
+                                       add, NULL, nbns_tree,
                                        flags & F_OPCODE,
                                        "Additional records");
        }
@@ -980,64 +1112,91 @@ struct nbdgm_header {
        guint8          error_code;
 };
 
-void
-dissect_nbdgm(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
-    int max_data)
+/*
+ * NBDS message types.
+ */
+#define NBDS_DIRECT_UNIQUE     0x10
+#define NBDS_DIRECT_GROUP      0x11
+#define NBDS_BROADCAST         0x12
+#define NBDS_ERROR             0x13
+#define NBDS_QUERY_REQUEST     0x14
+#define NBDS_POS_QUERY_RESPONSE        0x15
+#define NBDS_NEG_QUERY_RESPONSE        0x16
+
+static const value_string nbds_msgtype_vals[] = {
+       { NBDS_DIRECT_UNIQUE,      "Direct_unique datagram" },
+       { NBDS_DIRECT_GROUP,       "Direct_group datagram" },
+       { NBDS_BROADCAST,          "Broadcast datagram" },
+       { NBDS_ERROR,              "Datagram error" },
+       { NBDS_QUERY_REQUEST,      "Datagram query request" },
+       { NBDS_POS_QUERY_RESPONSE, "Datagram positive query response" },
+       { NBDS_NEG_QUERY_RESPONSE, "Datagram negative query response" },
+       { 0,                       NULL }
+};
+
+static const true_false_string yesno = {  
+       "Yes",
+       "No"
+};
+
+static const value_string node_type_vals[] = {
+       { 0, "B node" },
+       { 1, "P node" },
+       { 2, "M node" },
+       { 3, "NBDD" },
+       { 0, NULL }
+};
+
+static void
+dissect_nbdgm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
+       int                     offset = 0;
        proto_tree              *nbdgm_tree = NULL;
-       proto_item              *ti;
+       proto_item              *ti = NULL;
        struct nbdgm_header     header;
        int                     flags;
        int                     message_index;
+       tvbuff_t                *next_tvb;
 
-       char *message[] = {
-               "Unknown",
-               "Direct_unique datagram",
-               "Direct_group datagram",
-               "Broadcast datagram",
-               "Datagram error",
-               "Datagram query request",
-               "Datagram positive query response",
-               "Datagram negative query response"
-       };
-
-       char *node[] = {
-               "B node",
-               "P node",
-               "M node",
-               "NBDD"
-       };
-
-       static value_string error_codes[] = {
+       static const value_string error_codes[] = {
                { 0x82, "Destination name not present" },
                { 0x83, "Invalid source name format" },
                { 0x84, "Invalid destination name format" },
                { 0x00, NULL }
        };
 
-       char *yesno[] = { "No", "Yes" };
-
-       char name[32];
+       char name[(NETBIOS_NAME_LEN - 1)*4 + MAXDNAME];
+       int name_type;
        int len;
 
-       header.msg_type = pd[offset];
+       if (check_col(pinfo->fd, COL_PROTOCOL))
+               col_set_str(pinfo->fd, COL_PROTOCOL, "NBDS");
+       if (check_col(pinfo->fd, COL_INFO))
+               col_clear(pinfo->fd, COL_INFO);
+
+       header.msg_type = tvb_get_guint8(tvb, offset);
        
-       flags = pd[offset+1];
+       flags = tvb_get_guint8(tvb, offset+1);
        header.flags.more = flags & 1;
        header.flags.first = (flags & 2) >> 1;
        header.flags.node_type = (flags & 12) >> 2;
 
-       header.dgm_id = pntohs(&pd[offset+2]);
-       memcpy(&header.src_ip, &pd[offset+4], 4);
-       header.src_port = pntohs(&pd[offset+8]);
+       header.dgm_id = tvb_get_ntohs(tvb, offset+2);
+       tvb_memcpy(tvb, (guint8 *)&header.src_ip, offset+4, 4);
+       header.src_port = tvb_get_ntohs(tvb, offset+8);
 
-       if (header.msg_type == 0x10 ||
-                       header.msg_type == 0x11 || header.msg_type == 0x12) {
-               header.dgm_length = pntohs(&pd[offset+10]);
-               header.pkt_offset = pntohs(&pd[offset+12]);
-       }
-       else if (header.msg_type == 0x13) {
-               header.error_code = pntohs(&pd[offset+10]);
+       switch (header.msg_type) {
+
+       case NBDS_DIRECT_UNIQUE:
+       case NBDS_DIRECT_GROUP:
+       case NBDS_BROADCAST:
+               header.dgm_length = tvb_get_ntohs(tvb, offset+10);
+               header.pkt_offset = tvb_get_ntohs(tvb, offset+12);
+               break;
+
+       case NBDS_ERROR:
+               header.error_code = tvb_get_ntohs(tvb, offset+10);
+               break;
        }
 
        message_index = header.msg_type - 0x0f;
@@ -1045,89 +1204,107 @@ dissect_nbdgm(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
                message_index = 0;
        }
 
-       if (check_col(fd, COL_PROTOCOL))
-               col_add_str(fd, COL_PROTOCOL, "NBDS (UDP)");
-       if (check_col(fd, COL_INFO)) {
-               col_add_fstr(fd, COL_INFO, "%s", message[message_index]);
+       if (check_col(pinfo->fd, COL_INFO)) {
+               col_add_str(pinfo->fd, COL_INFO,
+                   val_to_str(header.msg_type, nbds_msgtype_vals,
+                     "Unknown message type (0x%02X)"));
        }
 
        if (tree) {
-               ti = proto_tree_add_item(tree, offset, header.dgm_length,
-                               "NetBIOS Datagram Service");
-               nbdgm_tree = proto_tree_new();
-               proto_item_add_subtree(ti, nbdgm_tree, ETT_NBDGM);
-
-               proto_tree_add_item(nbdgm_tree, offset, 1, "Message Type: %s",
-                               message[message_index]);
-               proto_tree_add_item(nbdgm_tree, offset+1, 1, "More fragments follow: %s",
-                               yesno[header.flags.more]);
-               proto_tree_add_item(nbdgm_tree, offset+1, 1, "This is first fragment: %s",
-                               yesno[header.flags.first]);
-               proto_tree_add_item(nbdgm_tree, offset+1, 1, "Node Type: %s",
-                               node[header.flags.node_type]);
-
-               proto_tree_add_item(nbdgm_tree, offset+2, 2, "Datagram ID: 0x%04X",
-                               header.dgm_id);
-               proto_tree_add_item(nbdgm_tree, offset+4, 4, "Source IP: %s",
-                               ip_to_str((guint8 *)&header.src_ip));
-               proto_tree_add_item(nbdgm_tree, offset+8, 2, "Source Port: %d",
-                               header.src_port);
+               ti = proto_tree_add_item(tree, proto_nbdgm, tvb, offset,
+                   tvb_length_remaining(tvb, offset), FALSE);
+               nbdgm_tree = proto_item_add_subtree(ti, ett_nbdgm);
+
+               proto_tree_add_uint(nbdgm_tree, hf_nbdgm_type, tvb,
+                                    offset, 1, 
+                                    header.msg_type);
+               proto_tree_add_boolean(nbdgm_tree, hf_nbdgm_fragment, tvb,
+                                      offset+1, 1, 
+                                      header.flags.more);
+               proto_tree_add_boolean(nbdgm_tree, hf_nbdgm_first, tvb,
+                                      offset+1, 1, 
+                                      header.flags.first);
+               proto_tree_add_uint(nbdgm_tree, hf_nbdgm_node_type, tvb,
+                                    offset+1, 1, 
+                                    header.flags.node_type);
+
+               proto_tree_add_uint(nbdgm_tree, hf_nbdgm_datagram_id, tvb,
+                                   offset+2, 2, header.dgm_id);
+               proto_tree_add_ipv4(nbdgm_tree, hf_nbdgm_src_ip, tvb,
+                                   offset+4, 4, header.src_ip);
+               proto_tree_add_uint(nbdgm_tree, hf_nbdgm_src_port, tvb,
+                                   offset+8, 2, header.src_port);
+
        }
 
        offset += 10;
-       max_data -= 10;
 
-       if (header.msg_type == 0x10 ||
-                       header.msg_type == 0x11 || header.msg_type == 0x12) {
+       switch (header.msg_type) {
 
+       case NBDS_DIRECT_UNIQUE:
+       case NBDS_DIRECT_GROUP:
+       case NBDS_BROADCAST:
                if (tree) {
-                       proto_tree_add_item(nbdgm_tree, offset, 2,
+                       proto_tree_add_text(nbdgm_tree, tvb, offset, 2,
                                        "Datagram length: %d bytes", header.dgm_length);
-                       proto_tree_add_item(nbdgm_tree, offset+2, 2,
+                       proto_tree_add_text(nbdgm_tree, tvb, offset+2, 2,
                                        "Packet offset: %d bytes", header.pkt_offset);
                }
 
                offset += 4;
-               max_data -= 4;
 
                /* Source name */
-               len = get_nbns_name(&pd[offset], pd, offset, name);
+               len = get_nbns_name(tvb, offset, offset, name, &name_type);
 
                if (tree) {
-                       proto_tree_add_item(nbdgm_tree, offset, len, "Source name: %s",
-                                       name);
+                       add_name_and_type(nbdgm_tree, tvb, offset, len,
+                           "Source name", name, name_type);
                }
                offset += len;
-               max_data -= len;
 
                /* Destination name */
-               len = get_nbns_name(&pd[offset], pd, offset, name);
+               len = get_nbns_name(tvb, offset, offset, name, &name_type);
 
                if (tree) {
-                       proto_tree_add_item(nbdgm_tree, offset, len, "Destination name: %s",
-                                       name);
+                       add_name_and_type(nbdgm_tree, tvb, offset, len,
+                           "Destination name", name, name_type);
                }
                offset += len;
-               max_data -= len;
 
-               /* here we can pass the packet off to the next protocol */
-               dissect_smb(pd, offset, fd, tree, max_data);
-       }
-       else if (header.msg_type == 0x13) {
+               /*
+                * Here we can pass the packet off to the next protocol.
+                * Set the length of our top-level tree item to include
+                * only our stuff.
+                *
+                * XXX - take the datagram length into account?
+                */
+               proto_item_set_len(ti, offset);
+               next_tvb = tvb_new_subset(tvb, offset, -1, -1);
+               dissect_netbios_payload(next_tvb, pinfo, tree);
+               break;
+
+       case NBDS_ERROR:
                if (tree) {
-                       proto_tree_add_item(nbdgm_tree, offset, 1, "Error code: %s",
+                       proto_tree_add_text(nbdgm_tree, tvb, offset, 1, "Error code: %s",
                                val_to_str(header.error_code, error_codes, "Unknown (0x%x)"));
                }
-       }
-       else if (header.msg_type == 0x14 ||
-                       header.msg_type == 0x15 || header.msg_type == 0x16) {
+               offset += 1;
+               proto_item_set_len(ti, offset);
+               break;
+
+       case NBDS_QUERY_REQUEST:
+       case NBDS_POS_QUERY_RESPONSE:
+       case NBDS_NEG_QUERY_RESPONSE:
                /* Destination name */
-               len = get_nbns_name(&pd[offset], pd, offset, name);
+               len = get_nbns_name(tvb, offset, offset, name, &name_type);
 
                if (tree) {
-                       proto_tree_add_item(nbdgm_tree, offset, len, "Destination name: %s",
-                                       name);
+                       add_name_and_type(nbdgm_tree, tvb, offset, len,
+                           "Destination name", name, name_type);
                }
+               offset += len;
+               proto_item_set_len(ti, offset);
+               break;
        }
 }
 
@@ -1166,136 +1343,477 @@ static const value_string error_codes[] = {
 };
 
 /*
- * Dissect a single NBSS packet (there may be more than one in a given TCP
- * segment). Hmmm, in my experience, I have never seen more than one NBSS
- * in a single segment, since they mostly contain SMBs which are essentially
- * a request response type protocol (RJS). Also, a single session message 
- * may be split over multiple segments.
+ * Dissect a single NBSS packet (there may be more than one in a given
+ * TCP segment).
+ *
+ * [ Hmmm, in my experience, I have never seen more than one NBSS in a
+ * single segment, since they mostly contain SMBs which are essentially
+ * a request response type protocol (RJS). ]
+ *
+ * [ However, under heavy load with many requests multiplexed on one
+ * session it is not unusual to see multiple requests in one TCP
+ * segment. Unfortunately, in this case a single session message is
+ * frequently split over multiple segments, which frustrates decoding
+ * (MMM). ]
  */
 static int
-dissect_nbss_packet(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, int max_data)
+dissect_nbss_packet(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, int max_data, int is_cifs)
 {
        proto_tree      *nbss_tree = NULL;
-       proto_item      *ti;
+       proto_item      *ti = NULL;
        proto_tree      *field_tree;
        proto_item      *tf;
        guint8          msg_type;
        guint8          flags;
-       guint16         length;
+       int             length;
        int             len;
-       char            name[32];
+       char            name[(NETBIOS_NAME_LEN - 1)*4 + MAXDNAME];
+       int             name_type;
+       gint            reported_len;
+       tvbuff_t        *next_tvb;
 
-       msg_type = pd[offset];
-       flags = pd[offset + 1];
-       length = pntohs(&pd[offset + 2]);
-       if (flags & NBSS_FLAGS_E)
-               length += 65536;
+       msg_type = tvb_get_guint8(tvb, offset);
+
+       if (is_cifs) {
+               flags = 0;
+               length = tvb_get_ntoh24(tvb, offset + 1);
+       } else {
+               flags = tvb_get_guint8(tvb, offset + 1);
+               length = tvb_get_ntohs(tvb, offset + 2);
+               if (flags & NBSS_FLAGS_E)
+                       length += 65536;
+       }
+
+       /*Desegmentation */
+       if (nbss_desegment) {
+               if (pinfo->can_desegment
+                   && length > tvb_length_remaining(tvb, offset+4)) {
+                       /*
+                        * This frame doesn't have all of the data for
+                        * this message, but we can do reassembly on it.
+                        *
+                        * Tell the TCP dissector where the data for this
+                        * message starts in the data it handed us, and
+                        * how many more bytes we need, and return.
+                        */
+                       pinfo->desegment_offset = offset;
+                       pinfo->desegment_len =
+                           length - tvb_length_remaining(tvb, offset+4);
+                       return max_data;
+               }
+       }
 
        if (tree) {
-         ti = proto_tree_add_item(tree, offset, length + 4,
-                                  "NetBIOS Session Service");
-         nbss_tree = proto_tree_new();
-         proto_item_add_subtree(ti, nbss_tree, ETT_NBSS);
+         ti = proto_tree_add_item(tree, proto_nbss, tvb, offset, length + 4, FALSE);
+         nbss_tree = proto_item_add_subtree(ti, ett_nbss);
          
-         proto_tree_add_item(nbss_tree, offset, 1, "Message Type: %s",
-                             val_to_str(msg_type, message_types, "Unknown (%x)"));
+         proto_tree_add_uint_format(nbss_tree, hf_nbss_type, tvb,
+                                    offset, 1, 
+                                    msg_type,
+                                    "Message Type: %s",
+                                    val_to_str(msg_type, message_types, 
+                                               "Unknown (%x)"));
        }
 
        offset += 1;
 
-       if (tree) {
-         tf = proto_tree_add_item(nbss_tree, offset, 1, "Flags: 0x%04x", flags);
-         field_tree = proto_tree_new();
-         proto_item_add_subtree(tf, field_tree, ETT_NBSS_FLAGS);
-         proto_tree_add_item(field_tree, offset, 1, "%s",
+       if (is_cifs) {
+               if (tree) {
+                 proto_tree_add_text(nbss_tree, tvb, offset, 3, "Length: %u", length);
+               }
+               offset += 3;
+       } else {
+               if (tree) {
+                 tf = proto_tree_add_uint(nbss_tree, hf_nbss_flags, tvb, offset, 1, flags);
+                 field_tree = proto_item_add_subtree(tf, ett_nbss_flags);
+                 proto_tree_add_text(field_tree, tvb, offset, 1, "%s",
                              decode_boolean_bitfield(flags, NBSS_FLAGS_E,
-                                                     8, "Add 65536 to length", "Add 0 to length"));
-       }
+                                                             8, "Add 65536 to length", "Add 0 to length"));
+               }
+               offset += 1;
 
-       offset += 1;
+               if (tree) {
+                 proto_tree_add_text(nbss_tree, tvb, offset, 2, "Length: %u", length);
+               }
 
-       if (tree) {
-         proto_tree_add_item(nbss_tree, offset, 2, "Length: %u", length);
+               offset += 2;
        }
 
-       offset += 2;
-
        switch (msg_type) {
 
        case SESSION_REQUEST:
-         len = get_nbns_name(&pd[offset], pd, offset, name);
+         len = get_nbns_name(tvb, offset, offset, name, &name_type);
          if (tree)
-           proto_tree_add_item(nbss_tree, offset, len,
-                               "Called name: %s", name);
+           add_name_and_type(nbss_tree, tvb, offset, len,
+                               "Called name", name, name_type);
          offset += len;
 
-         len = get_nbns_name(&pd[offset], pd, offset, name);
+         len = get_nbns_name(tvb, offset, offset, name, &name_type);
          
          if (tree)
-           proto_tree_add_item(nbss_tree, offset, len,
-                               "Calling name: %s", name);
+           add_name_and_type(nbss_tree, tvb, offset, len,
+                               "Calling name", name, name_type);
 
          break;
 
        case NEGATIVE_SESSION_RESPONSE:
          if (tree) 
-           proto_tree_add_item(nbss_tree, offset, 1,
+           proto_tree_add_text(nbss_tree, tvb, offset, 1,
                                "Error code: %s",
-                               val_to_str(pd[offset], error_codes, "Unknown (%x)"));
+                               val_to_str(tvb_get_guint8(tvb, offset),
+                                          error_codes, "Unknown (%x)"));
          break;
 
        case RETARGET_SESSION_RESPONSE:
          if (tree)
-           proto_tree_add_item(nbss_tree, offset, 4,
+           proto_tree_add_text(nbss_tree, tvb, offset, 4,
                                "Retarget IP address: %s",
-                               ip_to_str((guint8 *)&pd[offset]));
+                               ip_to_str(tvb_get_ptr(tvb, offset, 4)));
          
          offset += 4;
 
          if (tree)
-           proto_tree_add_item(nbss_tree, offset, 2,
-                               "Retarget port: %u", pntohs(&pd[offset]));
+           proto_tree_add_text(nbss_tree, tvb, offset, 2,
+                               "Retarget port: %u",
+                               tvb_get_ntohs(tvb, offset));
 
          break;
 
        case SESSION_MESSAGE:
          /*
-          * Here we can pass the packet off to the next protocol.
+          * Here we can pass the message off to the next protocol.
+          * Set the length of our top-level tree item to include
+          * only our stuff.
           */
-
-         dissect_smb(pd, offset, fd, tree, max_data - 4);
-
+         proto_item_set_len(ti, offset);
+         len = tvb_length_remaining(tvb, offset);
+         reported_len = tvb_reported_length_remaining(tvb, offset);
+         if (len > length)
+           len = length;
+         if (reported_len > length)
+           reported_len = length;
+         next_tvb = tvb_new_subset(tvb, offset, len, reported_len);
+         dissect_netbios_payload(next_tvb, pinfo, tree);
          break;
 
        }
        return length + 4;
 }
 
-void
-dissect_nbss(const u_char *pd, int offset, frame_data *fd, proto_tree *tree, int max_data)
+static void
+dissect_nbss(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
+       struct tcpinfo  *tcpinfo = pinfo->private_data;
+       int             offset = 0;
+       int             max_data;
        guint8          msg_type;
        guint8          flags;
-       guint16         length;
+       guint32         length;
        int             len;
+       gboolean        is_cifs;
+       proto_tree      *nbss_tree;
+       proto_item      *ti;
 
-       msg_type = pd[offset];
-       flags = pd[offset + 1];
-       length = pntohs(&pd[offset + 2]);
-       if (flags & NBSS_FLAGS_E)
-               length += 65536;
-
-       if (check_col(fd, COL_PROTOCOL))
-               col_add_str(fd, COL_PROTOCOL, "NBSS (TCP)");
-       if (check_col(fd, COL_INFO)) {
-               col_add_fstr(fd, COL_INFO,
-                   val_to_str(msg_type, message_types, "Unknown (%x)"));
+       if (check_col(pinfo->fd, COL_PROTOCOL))
+               col_set_str(pinfo->fd, COL_PROTOCOL, "NBSS");
+       if (check_col(pinfo->fd, COL_INFO))
+               col_clear(pinfo->fd, COL_INFO);
+
+       max_data = tvb_length(tvb);
+
+       msg_type = tvb_get_guint8(tvb, offset);
+
+       if (pinfo->match_port == TCP_PORT_CIFS) {
+               /*
+                * Windows 2000 CIFS clients can dispense completely
+                * with the NETBIOS encapsulation and directly use CIFS
+                * over TCP. As would be expected, the framing is
+                * identical, except that the length is 24 bits instead
+                * of 17. The only message types used are
+                * SESSION_MESSAGE and SESSION_KEEP_ALIVE.
+                */
+               is_cifs = TRUE;
+       } else {
+               is_cifs = FALSE;
        }
 
-       while (max_data > 0) { 
-         len = dissect_nbss_packet(pd, offset, fd, tree, max_data);
-         offset += len;
-         max_data -= len;
+       /*
+        * This might be a continuation of an earlier message.
+        * (Yes, that might be true even if we're doing TCP reassembly,
+        * as the first TCP segment in the capture might start in the
+        * middle of an NBNS message.)
+        */
+
+       /*
+        * If this isn't reassembled data, check to see whether it
+        * looks like a continuation of a message.
+        * (If it is reassembled data, it shouldn't be a continuation,
+        * as reassembly should've gathered the continuations together
+        * into a message.)
+        */
+       if (!tcpinfo->is_reassembled) {
+               if (max_data < 4) {
+                       /*
+                        * Not enough data for an NBSS header; assume
+                        * it's a continuation of a message.
+                        *
+                        * XXX - if there's not enough data, we should
+                        * attempt to reassemble the data, if the first byte
+                        * is a valid message type.
+                        */
+                       goto continuation;
+               }
+
+               /*
+                * We have enough data for an NBSS header.
+                * Get the flags and length of the message,
+                * and see if they're sane.
+                */
+               if (is_cifs) {
+                       flags = 0;
+                       length = tvb_get_ntoh24(tvb, offset + 1);
+               } else {
+                       flags = tvb_get_guint8(tvb, offset + 1);
+                       length = tvb_get_ntohs(tvb, offset + 2);
+                       if (flags & NBSS_FLAGS_E)
+                               length += 65536;
+               }
+               if ((flags & (~NBSS_FLAGS_E)) != 0) {
+                       /*
+                        * A bogus flag was set; assume it's a continuation.
+                        */
+                       goto continuation;
+               }
+
+               switch (msg_type) {
+
+               case SESSION_MESSAGE:
+                       /*
+                        * This is variable-length.
+                        * All we know is that it shouldn't be zero.
+                        * (XXX - can we get zero-length messages?
+                        * Not with SMB, but perhaps other NetBIOS-based
+                        * protocols have them.)
+                        */
+                       if (length == 0)
+                               goto continuation;
+                       break;
+
+               case SESSION_REQUEST:
+                       /*
+                        * This is variable-length.
+                        * The names are DNS-encoded 32-byte values;
+                        * we need at least 2 bytes (one for each name;
+                        * actually, we should have more for the first
+                        * name, as there's no name preceding it so
+                        * there should be no compression), and we
+                        * shouldn't have more than 128 bytes (actually,
+                        * we shouldn't have that many).
+                        *
+                        * XXX - actually, MacOS X 10.1 (yes, that's
+                        * redundant, but that's what Apple calls it,
+                        * not MacOS X.1) puts names longer than 16
+                        * characters into session request messages,
+                        * so we can have more than 32 bytes of
+                        * name value, so we can have more than 128
+                        * bytes of data.
+                        */
+                       if (length < 2 || length > 256)
+                               goto continuation;
+                       break;
+
+               case POSITIVE_SESSION_RESPONSE:
+                       /*
+                        * This has no data, so the length must be zero.
+                        */
+                       if (length != 0)
+                               goto continuation;
+                       break;
+
+               case NEGATIVE_SESSION_RESPONSE:
+                       /*
+                        * This has 1 byte of data.
+                        */
+                       if (length != 1)
+                               goto continuation;
+                       break;
+
+               case RETARGET_SESSION_RESPONSE:
+                       /*
+                        * This has 6 bytes of data.
+                        */
+                       if (length != 6)
+                               goto continuation;
+                       break;
+
+               case SESSION_KEEP_ALIVE:
+                       /*
+                        * This has no data, so the length must be zero.
+                        */
+                       if (length != 0)
+                               goto continuation;
+                       break;
+
+               default:
+                       /*
+                        * Unknown message type; assume it's a continuation.
+                        */
+                       goto continuation;
+               }
+       }
+       if (check_col(pinfo->fd, COL_INFO)) {
+               col_add_fstr(pinfo->fd, COL_INFO,
+                   val_to_str(msg_type, message_types, "Unknown (%02x)"));
+       }
+
+       while (max_data > 0) {
+               len = dissect_nbss_packet(tvb, offset, pinfo, tree, max_data,
+                   is_cifs);
+               offset += len;
+               max_data -= len;
+       }
+
+       return;
+
+continuation:
+       /*
+        * It looks like a continuation.
+        */
+       if (check_col(pinfo->fd, COL_INFO))
+               col_add_fstr(pinfo->fd, COL_INFO, "NBSS Continuation Message");
+
+       if (tree) {
+               ti = proto_tree_add_item(tree, proto_nbss, tvb, 0,
+                   max_data, FALSE);
+               nbss_tree = proto_item_add_subtree(ti, ett_nbss);
+               proto_tree_add_text(nbss_tree, tvb, 0, max_data,
+                   "Continuation data");
        }
+}
 
+void
+proto_register_nbt(void)
+{
+
+  static hf_register_info hf_nbns[] = {
+    { &hf_nbns_response,
+      { "Response",            "nbns.response",  
+       FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+       "TRUE if NBNS response", HFILL }},
+    { &hf_nbns_query,
+      { "Query",               "nbns.query",  
+       FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+       "TRUE if NBNS query", HFILL }},
+    { &hf_nbns_transaction_id,
+      { "Transaction ID",              "nbns.id",  
+       FT_UINT16, BASE_HEX, NULL, 0x0,
+       "Identification of transaction", HFILL }},
+    { &hf_nbns_count_questions,
+      { "Questions",           "nbns.count.queries",  
+       FT_UINT16, BASE_DEC, NULL, 0x0,
+       "Number of queries in packet", HFILL }},
+    { &hf_nbns_count_answers,
+      { "Answer RRs",          "nbns.count.answers",  
+       FT_UINT16, BASE_DEC, NULL, 0x0,
+       "Number of answers in packet", HFILL }},
+    { &hf_nbns_count_auth_rr,
+      { "Authority RRs",               "nbns.count.auth_rr",  
+       FT_UINT16, BASE_DEC, NULL, 0x0,
+       "Number of authoritative records in packet", HFILL }},
+    { &hf_nbns_count_add_rr,
+      { "Additional RRs",              "nbns.count.add_rr",  
+       FT_UINT16, BASE_DEC, NULL, 0x0,
+       "Number of additional records in packet", HFILL }}
+  };
+
+  static hf_register_info hf_nbdgm[] = {
+    { &hf_nbdgm_type,
+      { "Message Type",                "nbdgm.type",  
+       FT_UINT8, BASE_DEC, VALS(nbds_msgtype_vals), 0x0,
+       "NBDGM message type", HFILL }},
+    { &hf_nbdgm_fragment,
+      { "More fragments follow",       "nbdgm.next",  
+       FT_BOOLEAN, BASE_NONE, TFS(&yesno), 0x0,
+       "TRUE if more fragments follow", HFILL }},
+    { &hf_nbdgm_first,
+      { "This is first fragment",      "nbdgm.first",  
+       FT_BOOLEAN, BASE_NONE, TFS(&yesno), 0x0,
+       "TRUE if first fragment", HFILL }},
+    { &hf_nbdgm_node_type,
+      { "Node Type",           "nbdgm.node_type",  
+       FT_UINT8, BASE_DEC, VALS(node_type_vals), 0x0,
+       "Node type", HFILL }},
+    { &hf_nbdgm_datagram_id,
+      { "Datagram ID",         "nbdgm.dgram_id",  
+       FT_UINT16, BASE_HEX, NULL, 0x0,
+       "Datagram identifier", HFILL }},
+    { &hf_nbdgm_src_ip,
+      { "Source IP",           "nbdgm.src.ip",  
+       FT_IPv4, BASE_NONE, NULL, 0x0,
+       "Source IPv4 address", HFILL }},
+    { &hf_nbdgm_src_port,
+      { "Source Port",         "nbdgm.src.port",
+       FT_UINT16, BASE_DEC, NULL, 0x0,
+       "Source port", HFILL }}
+  };
+
+  static hf_register_info hf_nbss[] = {
+    { &hf_nbss_type,
+      { "Message Type",                "nbss.type",  
+       FT_UINT8, BASE_DEC, NULL, 0x0,
+       "NBSS message type", HFILL }},
+    { &hf_nbss_flags,
+      { "Flags",               "nbss.flags",  
+       FT_UINT8, BASE_HEX, NULL, 0x0,
+       "NBSS message flags", HFILL }}
+  };
+  static gint *ett[] = {
+    &ett_nbns,
+    &ett_nbns_qd,
+    &ett_nbns_flags,
+    &ett_nbns_nb_flags,
+    &ett_nbns_name_flags,
+    &ett_nbns_rr,
+    &ett_nbns_qry,
+    &ett_nbns_ans,
+    &ett_nbdgm,
+    &ett_nbss,
+    &ett_nbss_flags,
+  };
+  module_t *nbss_module;
+
+  proto_nbns = proto_register_protocol("NetBIOS Name Service", "NBNS", "nbns");
+  proto_register_field_array(proto_nbns, hf_nbns, array_length(hf_nbns));
+  
+  proto_nbdgm = proto_register_protocol("NetBIOS Datagram Service",
+                                       "NBDS", "nbdgm");
+  proto_register_field_array(proto_nbdgm, hf_nbdgm, array_length(hf_nbdgm));
+
+  proto_nbss = proto_register_protocol("NetBIOS Session Service",
+                                      "NBSS", "nbss");
+  proto_register_field_array(proto_nbss, hf_nbss, array_length(hf_nbss));
+
+  proto_register_subtree_array(ett, array_length(ett));
+
+  nbss_module = prefs_register_protocol(proto_nbss, NULL);
+  prefs_register_bool_preference(nbss_module, "desegment_nbss_commands",
+    "Desegment all NBSS packets spanning multiple TCP segments",
+    "Whether NBSS dissector should desegment all packets spanning multiple TCP segments",
+    &nbss_desegment);
+}
+
+void
+proto_reg_handoff_nbt(void)
+{
+  dissector_handle_t nbns_handle, nbdgm_handle, nbss_handle;
+
+  nbns_handle = create_dissector_handle(dissect_nbns, proto_nbns);
+  dissector_add("udp.port", UDP_PORT_NBNS, nbns_handle);
+  nbdgm_handle = create_dissector_handle(dissect_nbdgm, proto_nbdgm);
+  dissector_add("udp.port", UDP_PORT_NBDGM, nbdgm_handle);
+  nbss_handle = create_dissector_handle(dissect_nbss, proto_nbss);
+  dissector_add("tcp.port", TCP_PORT_NBSS, nbss_handle);
+  dissector_add("tcp.port", TCP_PORT_CIFS, nbss_handle);
 }