Removed trailing whitespaces from .h and .c files using the
[obnox/wireshark/wip.git] / packet-ncp.c
index b8c04b521a74c2100d9f02f0f46408892d91befd..946d5da7f8fcaba522d85f41ecb62660489d8ead 100644 (file)
@@ -1,14 +1,15 @@
 /* packet-ncp.c
  * Routines for NetWare Core Protocol
- * Gilbert Ramirez <gram@xiexie.org>
+ * Gilbert Ramirez <gram@alumni.rice.edu>
  * Modified to allow NCP over TCP/IP decodes by James Coe <jammer@cin.net>
+ * Modified to decode server op-lock 
+ * & NDS packets by Greg Morris <gmorris@novell.com>
  *
- * $Id: packet-ncp.c,v 1.36 2000/05/30 03:35:53 guy Exp $
+ * $Id: packet-ncp.c,v 1.67 2002/08/23 21:54:30 guy Exp $
  *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
- * Copyright 1998 Gerald Combs
- *
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 2000 Gerald Combs
  * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -25,9 +26,7 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
-#undef DEBUG_NCP_HASH
-
-#ifdef HAVE_CONFIG_H
+#ifdef HAVE_CONFIG_H 
 # include "config.h"
 #endif
 
 # include <netinet/in.h>
 #endif
 
+#include <string.h>
 #include <glib.h>
-#include "packet.h"
-#include "conversation.h"
+#include <epan/packet.h>
+#include <epan/conversation.h>
+#include "prefs.h"
 #include "packet-ipx.h"
+#include "packet-tcp.h"
+#include "packet-ncp-int.h"
 
-static int proto_ncp = -1;
+int proto_ncp = -1;
 static int hf_ncp_ip_ver = -1;
+static int hf_ncp_ip_length = -1;
+static int hf_ncp_ip_rplybufsize = -1;
 static int hf_ncp_ip_sig = -1;
+static int hf_ncp_ip_packetsig = -1;
 static int hf_ncp_type = -1;
 static int hf_ncp_seq = -1;
 static int hf_ncp_connection = -1;
 static int hf_ncp_task = -1;
-
-static gint ett_ncp = -1;
-static gint ett_ncp_request_fields = -1;
-static gint ett_ncp_reply_fields = -1;
+static int hf_ncp_stream_type = -1;
+static int hf_ncp_system_flags = -1;
+static int hf_ncp_system_flags_abt = -1;
+static int hf_ncp_system_flags_eob = -1;
+static int hf_ncp_system_flags_sys = -1;
+static int hf_ncp_src_connection = -1;
+static int hf_ncp_dst_connection = -1;
+static int hf_ncp_packet_seqno = -1;
+static int hf_ncp_delay_time = -1;
+static int hf_ncp_burst_seqno = -1;
+static int hf_ncp_ack_seqno = -1;
+static int hf_ncp_burst_len = -1;
+static int hf_ncp_data_offset = -1;
+static int hf_ncp_data_bytes = -1;
+static int hf_ncp_missing_fraglist_count = -1;
+static int hf_ncp_missing_data_offset = -1;
+static int hf_ncp_missing_data_count = -1;
+static int hf_ncp_oplock_flag = -1;
+static int hf_ncp_oplock_handle = -1;
+static int hf_ncp_completion_code = -1;
+static int hf_ncp_connection_status = -1;
+static int hf_ncp_slot = -1;
+static int hf_ncp_control_code = -1;
+static int hf_ncp_fragment_handle = -1;
+static int hf_lip_echo = -1;
+
+
+gint ett_ncp = -1;
+static gint ett_ncp_system_flags = -1;
+
+/* desegmentation of NCP over TCP */
+static gboolean ncp_desegment = TRUE;
+
+static dissector_handle_t data_handle;
 
 #define TCP_PORT_NCP           524
 #define UDP_PORT_NCP           524
 
-struct svc_record;
-
-static void
-dissect_ncp_request(const u_char *pd, int offset, frame_data *fd,
-       guint16 nw_connection, guint8 nw_sequence, guint16 nw_ncp_type,
-       proto_tree *ncp_tree, proto_tree *tree);
-
-static void
-dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd,
-       guint16 nw_connection, guint8 nw_sequence,
-       proto_tree *ncp_tree, proto_tree *tree);
-
-static struct ncp2222_record *
-ncp2222_find(guint8 func, guint8 subfunc);
-
-static void
-parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
-       struct svc_record *svc);
-
+#define NCP_RQST_HDR_LENGTH    7
+#define NCP_RPLY_HDR_LENGTH    8
 
 /* Hash functions */
 gint  ncp_equal (gconstpointer v, gconstpointer v2);
 guint ncp_hash  (gconstpointer v);
 
-int ncp_packet_init_count = 200;
-
 /* These are the header structures to handle NCP over IP */
 #define        NCPIP_RQST      0x446d6454      /* "DmdT" */
 #define NCPIP_RPLY     0x744e6350      /* "tNcP" */
@@ -103,6 +120,7 @@ struct ncp_ip_rqhdr {
 static const value_string ncp_ip_signature[] = {
        { NCPIP_RQST, "Demand Transport (Request)" },
        { NCPIP_RPLY, "Transport is NCP (Reply)" },
+       { 0, NULL },
 };
 
 /* The information in this module comes from:
@@ -123,903 +141,403 @@ static const value_string ncp_ip_signature[] = {
 
 */
 
-/* Every NCP packet has this common header */
+/*
+ * Every NCP packet has this common header (except for burst packets).
+ */
 struct ncp_common_header {
        guint16 type;
        guint8  sequence;
        guint8  conn_low;
        guint8  task;
-       guint8  conn_high;
-};
-
-/* NCP request packets */
-struct ncp_request_header {
-       guint16 type;
-       guint8  sequence;
-       guint8  conn_low;
-       guint8  task;
-       guint8  conn_high;
-       guint8  function;
-       guint16 length;
-       guint8  subfunc;
-};
-
-/* NCP reply packets */
-struct ncp_reply_header {
-       guint16 type;
-       guint8  sequence;
-       guint8  conn_low;
-       guint8  task;
-       guint8  conn_high;
-       guint8  completion_code;
-       guint8  connection_state;
-};
-
-
-static value_string request_reply_values[] = {
-       { 0x1111, "Create a service connection" },
-       { 0x2222, "Service request" },
-       { 0x3333, "Service reply" },
-       { 0x5555, "Destroy service connection" },
-       { 0x7777, "Burst mode transfer" },
-       { 0x9999, "Request being processed" },
-       { 0x0000, NULL }
+       guint8  conn_high; /* type=0x5555 doesn't have this */
+};                         
+
+
+static value_string ncp_type_vals[] = {
+       { NCP_ALLOCATE_SLOT,    "Create a service connection" },
+       { NCP_SERVICE_REQUEST,  "Service request" },
+       { NCP_SERVICE_REPLY,    "Service reply" },
+       { NCP_WATCHDOG,         "Watchdog" },
+       { NCP_DEALLOCATE_SLOT,  "Destroy service connection" },
+       { NCP_BROADCAST_SLOT,   "Server Broadcast" },
+       { NCP_BURST_MODE_XFER,  "Burst mode transfer" },
+       { NCP_POSITIVE_ACK,     "Request being processed" },
+       { NCP_LIP_ECHO, "Large Internet Packet Echo" },
+       { 0,                    NULL }
 };
 
-/* These are the field types in an NCP packet */
-enum ntype {
-       nend,           /* end of the NCP field list */
-       nbyte,          /* one byte of data */
-       nhex,           /* bytes to be shown as hex digits */
-       nbelong,        /* 4-byte big-endian long int */
-       nbeshort,       /* 2-byte big-endian short int */
-       ndata,          /* unstructured data */
-       nbytevar,       /* a variable number of bytes */
-       ndatetime,      /* date-time stamp */
-       nasciile,       /* length-encoded ASCII string. First byte is length */
-       nasciiz         /* null-terminated string of ASCII characters */
-};
 
-/* These are the broad families that the different NCP request types belong
- * to.
+/*
+ * Burst packet system flags.
  */
-enum nfamily {
-               NCP_UNKNOWN_SERVICE,    /* unknown or n/a */
-               NCP_QUEUE_SERVICES,             /* print queues */
-               NCP_FILE_SERVICES,              /* file serving */
-               NCP_BINDERY_SERVICES,   /* bindery database */
-               NCP_CONNECTION_SERVICES /* communication */
-};
-
-/* I had to put this function prototype after the enum nfamily declaration */
-static char*
-ncp_completion_code(guint8 ccode, enum nfamily family);
-
-
-/* Information on the NCP field */
-typedef struct svc_record {
-       enum ntype      type;
-       guint8          length; /* max-length for variable-sized fields */
-       gchar           *description;
-} svc_record;
-
-typedef struct ncp2222_record {
-       guint8          func;
-       guint8          subfunc;
-       guint8          submask;        /* Does this function have subfunctions?
-                                        * SUBFUNC or NOSUB */
-       gchar           *funcname;
-
-       svc_record      *req;
-       svc_record      *rep;
-       enum nfamily    family;
-
-} ncp2222_record;
-
-
-/* ------------------------------------------------------------ */
-
-/* Get Bindery Object ID REQUEST */
-static svc_record ncp_17_35_C[] = {
-               { nbeshort,     2,      "Object Type: 0x%04x" },
-               { nasciile,     48,     "Object Name: %.*s" },
-               { nend,         0,      NULL }
-};
-/* Get Bindery Object ID REPLY has no fields*/
-
-
-/* Service Queue Job REQUEST */
-static svc_record ncp_17_7C_C[] = {
-               { nbelong,      4,      "The queue the job resides in" },
-               { nbeshort,     2,      "Job Type" },
-               { nend,         0,      NULL }
-};
-/* Service Queue Job REPLY */
-static svc_record ncp_17_7C_R[] = {
-               { nbelong,      4,      "Client station number: %d" },
-               { nbelong,      4,      "Task Number: %d" },
-               { nbelong,      4,      "User: %d" },
-               { nbelong,      4,      "Server specifed to service queue entry: %08X" },
-               { ndatetime,    6,      "Earliest time to execute" },
-               { ndatetime,    6,      "When job entered queue" },
-               { nbelong,      4,      "Job Number" },
-               { nbeshort,     2,      "Job Type" },
-               { nbeshort,     2,      "Job Position" },
-               { nbeshort,     2,      "Current status of job: 0x%02x" },
-               { nasciiz,      14,     "Name of file" },
-               { nbelong,      4,      "File handle" },
-               { nbelong,      4,      "Client station number" },
-               { nbelong,      4,      "Task number" },
-               { nbelong,      4,      "Job server" },
-               { nend,         0,      NULL }
-};
-
-
-
-/* Negotiate Buffer Size REQUEST */
-static svc_record ncp_21_00_C[] = {
-               { nbeshort,     2,      "Caller's maximum packet size: %d bytes" },
-               { nend,         0,      NULL }
-};
-/* Negotiate Buffer Size RESPONSE */
-static svc_record ncp_21_00_R[] = {
-               { nbeshort,     2,      "Packet size decided upon by file server: %d bytes" },
-               { nend,         0,      NULL }
-};
+#define ABT    0x04            /* Abort request */
+#define EOB    0x10            /* End of burst */
+#define SYS    0x80            /* System packet */
 
-
-/* Close File REQUEST */
-static svc_record ncp_42_00_C[] = {
-               { nhex,         6,      "File Handle: 02x:02x:02x:02x:02x:02x"},
-               { nend,         0,      NULL }
-};
-/* Close File RESPONSE */
-static svc_record ncp_42_00_R[] = {
-               { nend,         0,      NULL }
-};
-
-
-/* Read from a file REQUEST */
-static svc_record ncp_48_00_C[] = {
-               { nbyte,        1,      "Unknown" },
-               { nhex,         6,      "File Handle" },
-               { nbelong,      4,      "Byte offset within file" },
-               { nbeshort,     2,      "Maximum data bytes to return" },
-               { nend,         0,      NULL }
-};
-/* RESPONSE */
-static svc_record ncp_48_00_R[] = {
-               { nbeshort,     2,      "Data bytes returned" },
-               { nbytevar,     1,      "Padding" },
-               { ndata,        0,      NULL }
-};
-
-/* ------------------------------------------------------------ */
-/* Any svc_record that has no fields is not created.
- *  Store a NULL in the ncp2222_record instead */
-
-#define SUBFUNC        0xff
-#define NOSUB  0x00
-
-static ncp2222_record ncp2222[] = {
-
-{ 0x00, 0x00, NOSUB, "Create service connection",
-       NULL, NULL, NCP_CONNECTION_SERVICES
-},
-
-{ 0x14, 0x00, NOSUB, "Get server's clock",
-       NULL, NULL, NCP_FILE_SERVICES
-},
-
-{ 0x16, 0x01, SUBFUNC, "Get path of directory handle",
-       NULL, NULL, NCP_FILE_SERVICES
-},
-
-{ 0x16, 0x13, SUBFUNC, "Create temporary directory handle",
-       NULL, NULL, NCP_BINDERY_SERVICES
-},
-
-{ 0x16, 0x0A, SUBFUNC, "Create directory",
-       NULL, NULL, NCP_BINDERY_SERVICES
-},
-
-{ 0x16, 0x0D, SUBFUNC, "Add trustee to directory",
-       NULL, NULL, NCP_BINDERY_SERVICES
-},
-
-{ 0x17, 0x11, SUBFUNC, "Get fileserver information",
-       NULL, NULL, NCP_BINDERY_SERVICES
-},
-
-{ 0x17, 0x37, SUBFUNC, "Scan bindery object",
-       NULL, NULL, NCP_BINDERY_SERVICES
-},
-
-{ 0x17, 0x36, SUBFUNC, "Get bindery object name",
-       NULL, NULL, NCP_BINDERY_SERVICES
-},
-
-{ 0x17, 0x32, SUBFUNC, "Create bindery object",
-       NULL, NULL, NCP_BINDERY_SERVICES
-},
-
-{ 0x17, 0x39, SUBFUNC, "Create property",
-       NULL, NULL, NCP_BINDERY_SERVICES
-},
-
-{ 0x17, 0x41, SUBFUNC, "Add bindery object to set",
-       NULL, NULL, NCP_BINDERY_SERVICES
-},
-
-{ 0x17, 0x43, SUBFUNC, "Is bindery object in set",
-       NULL, NULL, NCP_BINDERY_SERVICES
-},
-
-{ 0x17, 0x35, SUBFUNC, "Get Bindery Object ID",
-       ncp_17_35_C, NULL, NCP_BINDERY_SERVICES
-},
-
-{ 0x17, 0x7C, SUBFUNC, "Service Queue Job",
-       ncp_17_7C_C, ncp_17_7C_R, NCP_QUEUE_SERVICES
-},
-
-{ 0x17, 0x3D, SUBFUNC, "Read property value",
-       NULL, NULL, NCP_FILE_SERVICES
-},
-
-{ 0x18, 0x00, NOSUB, "End of Job",
-       NULL, NULL, NCP_CONNECTION_SERVICES
-},
-
-{ 0x19, 0x00, NOSUB, "Logout",
-       NULL, NULL, NCP_CONNECTION_SERVICES
-},
-
-{ 0x21, 0x00, NOSUB, "Negotiate Buffer Size",
-       ncp_21_00_C, ncp_21_00_R, NCP_CONNECTION_SERVICES
-},
-
-{ 0x24, 0x00, SUBFUNC, "Destroy service connection",
-       NULL, NULL, NCP_CONNECTION_SERVICES
-},
-
-{ 0x3E, 0x53, SUBFUNC, "Get alternate directory search paths",
-       NULL, NULL, NCP_FILE_SERVICES
-},
-
-{ 0x3F, 0x89, SUBFUNC, "File search continue",
-       NULL, NULL, NCP_FILE_SERVICES
-},
-
-{ 0x42, 0x00, NOSUB, "Close File",
-       ncp_42_00_C, ncp_42_00_R, NCP_FILE_SERVICES
-},
-
-{ 0x48, 0x00, NOSUB, "Read from a file",
-       ncp_48_00_C, ncp_48_00_R, NCP_FILE_SERVICES
-},
-
-{ 0x4C, 0x11, SUBFUNC, "Open file",
-       NULL, NULL, NCP_FILE_SERVICES
-},
-
-{ 0x61, 0x00, NOSUB, "Get big packet NCP max packet size",
-       NULL, NULL, NCP_FILE_SERVICES
-},
-
-{ 0x68, 0x01, SUBFUNC, "Ping for NDS NCP",
-       NULL, NULL, NCP_FILE_SERVICES
-},
-
-{ 0x68, 0x02, SUBFUNC, "Send NDS fragmented message",
-       NULL, NULL, NCP_FILE_SERVICES
-},
-
-{ 0x00, 0x00, NOSUB, NULL,
-       NULL, NULL, NCP_UNKNOWN_SERVICE
-}
-
-};
-
-
-/* NCP packets come in request/reply pairs. The request packets tell the type
- * of NCP request and give a sequence ID. The response, unfortunately, only
- * identifies itself via the sequence ID; you have to know what type of NCP
- * request the request packet contained in order to successfully parse the NCP
- * response. A global method for doing this does not exist in ethereal yet
- * (NFS also requires it), so for now the NCP section will keep its own hash
- * table keeping track of NCP packet types.
- *
- * We construct a conversation specified by the client and server
- * addresses and the connection number; the key representing the unique
- * NCP request then is composed of the pointer to the conversation
- * structure, cast to a "guint" (which may throw away the upper 32
- * bits of the pointer on a P64 platform, but the low-order 32 bits
- * are more likely to differ between conversations than the upper 32 bits),
- * and the sequence number.
- *
- * The value stored in the hash table is the ncp_request_val pointer. This
- * struct tells us the NCP type and gives the ncp2222_record pointer, if
- * ncp_type == 0x2222.
- */
-
-struct ncp_request_key {
-       conversation_t  *conversation;
-       guint8          nw_sequence;
-};
-
-struct ncp_request_val {
-       guint32                 ncp_type;
-       struct ncp2222_record*  ncp_record;
-};
-
-static GHashTable *ncp_request_hash = NULL;
-static GMemChunk *ncp_request_keys = NULL;
-static GMemChunk *ncp_request_records = NULL;
-
-/* Hash Functions */
-gint  ncp_equal (gconstpointer v, gconstpointer v2)
-{
-       struct ncp_request_key  *val1 = (struct ncp_request_key*)v;
-       struct ncp_request_key  *val2 = (struct ncp_request_key*)v2;
-
-       #if defined(DEBUG_NCP_HASH)
-       printf("Comparing %p:%d and %p:%d\n",
-               val1->conversation, val1->nw_sequence,
-               val2->conversation, val2->nw_sequence);
-       #endif
-
-       if (val1->conversation == val2->conversation &&
-           val1->nw_sequence  == val2->nw_sequence ) {
-               return 1;
-       }
-       return 0;
-}
-
-guint ncp_hash  (gconstpointer v)
-{
-       struct ncp_request_key  *ncp_key = (struct ncp_request_key*)v;
-#if defined(DEBUG_NCP_HASH)
-       printf("hash calculated as %u\n",
-               GPOINTER_TO_UINT(ncp_key->conversation) + ncp_key->nw_sequence);
-#endif
-       return GPOINTER_TO_UINT(ncp_key->conversation) + ncp_key->nw_sequence;
-}
-
-/* Initializes the hash table and the mem_chunk area each time a new
- * file is loaded or re-loaded in ethereal */
 static void
-ncp_init_protocol(void)
+dissect_ncp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+    gboolean is_tcp)
 {
-       #if defined(DEBUG_NCP_HASH)
-       printf("Initializing NCP hashtable and mem_chunk area\n");
-       #endif
-       if (ncp_request_hash)
-               g_hash_table_destroy(ncp_request_hash);
-       if (ncp_request_keys)
-               g_mem_chunk_destroy(ncp_request_keys);
-       if (ncp_request_records)
-               g_mem_chunk_destroy(ncp_request_records);
-
-       ncp_request_hash = g_hash_table_new(ncp_hash, ncp_equal);
-       ncp_request_keys = g_mem_chunk_new("ncp_request_keys",
-                       sizeof(struct ncp_request_key),
-                       ncp_packet_init_count * sizeof(struct ncp_request_key), G_ALLOC_AND_FREE);
-       ncp_request_records = g_mem_chunk_new("ncp_request_records",
-                       sizeof(struct ncp_request_val),
-                       ncp_packet_init_count * sizeof(struct ncp_request_val), G_ALLOC_AND_FREE);
-}
-
-static struct ncp2222_record *
-ncp2222_find(guint8 func, guint8 subfunc)
-{
-       struct ncp2222_record *ncp_record, *retval = NULL;
-
-       ncp_record = ncp2222;
-
-       while(ncp_record->func != 0 || ncp_record->subfunc != 0 ||
-               ncp_record->funcname != NULL ) {
-               if (ncp_record->func == func &&
-                       ncp_record->subfunc == (subfunc & ncp_record->submask)) {
-                       retval = ncp_record;
-                       break;
-               }
-               ncp_record++;
-       }
-
-       return retval;
-}
-
-static void
-dissect_ncp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
-
-       proto_tree      *ncp_tree = NULL;
-       proto_item      *ti;
-       int             ncp_hdr_length = 0;
+       proto_tree                      *ncp_tree = NULL;
+       proto_item                      *ti;
        struct ncp_ip_header            ncpiph;
        struct ncp_ip_rqhdr             ncpiphrq;
+       gboolean                        is_signed = FALSE;
        struct ncp_common_header        header;
-       guint16         nw_connection;
-       guint8          nw_sequence;
-       guint16         nw_ncp_type;
-
-       if ( pi.ptype == PT_TCP || pi.ptype == PT_UDP ) {
-               ncpiph.signature = pntohl(&pd[offset]);
-               ncpiph.length = pntohl(&pd[offset + 4]);
-               offset += 8;
+       guint16                         nw_connection;
+       guint16                         flags = 0;
+       char                            flags_str[1+3+1+3+1+3+1+1];
+       char                            *sep;
+       proto_tree                      *flags_tree = NULL;
+       guint16                         data_len = 0;
+       guint16                         missing_fraglist_count = 0;
+       guint16                         ncp_nds_verb;
+       int                             hdr_offset = 0;
+       int                             commhdr;
+       int                             offset;
+       gint                            length_remaining;
+       tvbuff_t                        *next_tvb;
+
+       if (check_col(pinfo->cinfo, COL_PROTOCOL))
+               col_set_str(pinfo->cinfo, COL_PROTOCOL, "NCP");
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_clear(pinfo->cinfo, COL_INFO);
+
+       if (is_tcp) {
+               ncpiph.signature        = tvb_get_ntohl(tvb, 0);
+               ncpiph.length           = tvb_get_ntohl(tvb, 4);
+               hdr_offset += 8;
                if ( ncpiph.signature == NCPIP_RQST ) {
-                       ncpiphrq.version = pntohl(&pd[offset]);
-                       ncpiphrq.rplybufsize = pntohl(&pd[offset + 4]);
-                       offset += 8;
-               };
-       };
-
-       memcpy(&header, &pd[offset], sizeof(header));
-       header.type = ntohs(header.type);
-
-       if (header.type == 0x1111 ||
-                       header.type == 0x2222 ||
-                       header.type == 0x5555 ||
-                       header.type == 0x7777) {
-               ncp_hdr_length = 7;
-       }
-       else if (header.type == 0x3333 || header.type == 0x9999) {
-               ncp_hdr_length = 8;
-       }
-
-       if (check_col(fd, COL_PROTOCOL))
-               col_add_str(fd, COL_PROTOCOL, "NCP");
-
-       nw_connection = (header.conn_high << 16) + header.conn_low;
-       nw_sequence = header.sequence;
-       nw_ncp_type = header.type;
-
-       if (tree) {
-               ti = proto_tree_add_item(tree, proto_ncp, NullTVB, offset, END_OF_FRAME, NULL);
-               ncp_tree = proto_item_add_subtree(ti, ett_ncp);
-
-               if ( pi.ptype == PT_TCP || pi.ptype == PT_UDP ) {
-                       proto_tree_add_item(ncp_tree, hf_ncp_ip_sig, NullTVB, offset - 16, 4, ncpiph.signature);
-                       proto_tree_add_text(ncp_tree, NullTVB, offset - 12, 4, "Length: %d", ncpiph.length);
-                       if ( ncpiph.signature == NCPIP_RQST ) {
-                               proto_tree_add_item(ncp_tree, hf_ncp_ip_ver, NullTVB, offset - 8, 4, ncpiphrq.version);
-                               proto_tree_add_text(ncp_tree, NullTVB, offset - 4, 4, "Reply buffer size: %d", ncpiphrq.rplybufsize);
-                       };
-               };
-               proto_tree_add_uint_format(ncp_tree, hf_ncp_type, NullTVB, 
-                                          offset,      2,
-                                          header.type,
-                                          "Type: %s", 
-                                          val_to_str( header.type,
-                                                      request_reply_values,
-                                                      "Unknown (%04X)"));
-
-               proto_tree_add_item(ncp_tree, hf_ncp_seq, NullTVB, 
-                                   offset+2,    1, header.sequence);
-
-               proto_tree_add_item(ncp_tree, hf_ncp_connection, NullTVB,
-                                   offset+3,    3, nw_connection);
-
-               proto_tree_add_item(ncp_tree, hf_ncp_task, NullTVB, 
-                                   offset+4,    1, header.task);
-       }
-
-       /* Note how I use ncp_tree *and* tree in my args for ncp request/reply */
-       if (ncp_hdr_length == 7)
-               dissect_ncp_request(pd, offset, fd, nw_connection,
-                               nw_sequence, nw_ncp_type, ncp_tree, tree);
-       else if (ncp_hdr_length == 8)
-               dissect_ncp_reply(pd, offset, fd, nw_connection,
-                               nw_sequence, ncp_tree, tree);
-       else
-               dissect_data(pd, offset, fd, tree);
-}
-
-static void
-dissect_ncp_request(const u_char *pd, int offset, frame_data *fd,
-       guint16 nw_connection, guint8 nw_sequence, guint16 nw_ncp_type,
-       proto_tree *ncp_tree, proto_tree *tree) {
-
-       struct ncp_request_header       request;
-       struct ncp2222_record           *ncp_request;
-       gchar                           *description = "";
-       conversation_t                  *conversation;
-       struct ncp_request_val          *request_val;
-       struct ncp_request_key          *request_key;
-       proto_tree                      *field_tree = NULL;
-       proto_item                      *ti = NULL;
-
-       /*memcpy(&request, &pd[offset], sizeof(request));*/
-       request.function = pd[offset+6];
-       if ( BYTES_ARE_IN_FRAME(offset, 9) ) {
-               request.subfunc = pd[offset+9];
-       } else {
-               request.subfunc = 0;
-       }
-
-       ncp_request = ncp2222_find(request.function, request.subfunc);
-
-       if (ncp_request)
-               description = ncp_request->funcname;
-
-       if (check_col(fd, COL_INFO)) {
-               if (description[0]) {
-                       col_add_fstr(fd, COL_INFO, "C %s", description);
+                       ncpiphrq.version        = tvb_get_ntohl(tvb, hdr_offset);
+                       hdr_offset += 4;
+                       ncpiphrq.rplybufsize    = tvb_get_ntohl(tvb, hdr_offset);
+                       hdr_offset += 4;
                }
-               else {
-                       col_add_fstr(fd, COL_INFO, "C Unknown Function %02X/%02X",
-                               request.function, request.subfunc);
+               if (ncpiph.length & 0x80000000) {
+                       /*
+                        * This appears to indicate that this packet
+                        * is signed; the signature is 8 bytes long.
+                        *
+                        * XXX - that bit does *not* appear to be set
+                        * in signed replies, and we can't dissect the
+                        * reply enough to find the matching request
+                        * without knowing whether the reply is
+                        * signed.
+                        *
+                        * XXX - what about NCP-over-IPX signed
+                        * messages?
+                        */
+                       is_signed = TRUE;
+                       hdr_offset += 8;
+                       ncpiph.length &= 0x7fffffff;
                }
        }
 
-       if (!fd->flags.visited) {
-               /* This is the first time we've looked at this packet.
-                  Keep track of the address and connection whence the request
-                  came, and the address and connection to which the request
-                  is being sent, so that we can match up calls with replies.
-                  (We don't include the sequence number, as we may want
-                  to have all packets over the same connection treated
-                  as being part of a single conversation so that we can
-                  let the user select that conversation to be displayed.) */
-               conversation = find_conversation(&pi.src, &pi.dst,
-                   PT_NCP, nw_connection, nw_connection);
-               if (conversation == NULL) {
-                       /* It's not part of any conversation - create a new one. */
-                       conversation = conversation_new(&pi.src, &pi.dst,
-                           PT_NCP, nw_connection, nw_connection, NULL);
-               }
-
-               /* Now remember the request, so we can find it if we later
-                  a reply to it. */
-               request_key = g_mem_chunk_alloc(ncp_request_keys);
-               request_key->conversation = conversation;
-               request_key->nw_sequence = nw_sequence;
+       /* Record the offset where the NCP common header starts */
+       commhdr = hdr_offset;
 
-               request_val = g_mem_chunk_alloc(ncp_request_records);
-               request_val->ncp_type = nw_ncp_type;
-               request_val->ncp_record = ncp2222_find(request.function, request.subfunc);
+       header.type             = tvb_get_ntohs(tvb, commhdr);
+       header.sequence         = tvb_get_guint8(tvb, commhdr+2);
+       header.conn_low         = tvb_get_guint8(tvb, commhdr+3);
+       header.conn_high        = tvb_get_guint8(tvb, commhdr+5);
 
-               g_hash_table_insert(ncp_request_hash, request_key, request_val);
-               #if defined(DEBUG_NCP_HASH)
-               printf("Inserted conversation %p sequence %d (val=%p)\n",
-                       conversation, nw_sequence, request_val);
-               #endif
+       if (check_col(pinfo->cinfo, COL_INFO)) {
+               col_add_fstr(pinfo->cinfo, COL_INFO,
+                   "%s",
+                   val_to_str(header.type, ncp_type_vals, "Unknown type (0x%04x)"));
        }
 
-       if (ncp_tree) {
-               proto_tree_add_text(ncp_tree, NullTVB, offset+6, 1,
-                       "Function Code: 0x%02X (%s)",
-                       request.function, description);
-
-               if (ncp_request) {
-
-                       if (ncp_request->submask == SUBFUNC) {
-                               proto_tree_add_text(ncp_tree, NullTVB, offset+7, 2,
-                                       "Packet Length: %d bytes", pntohs(&pd[offset+7]));
-                               proto_tree_add_text(ncp_tree, NullTVB, offset+9, 1,
-                                       "Subfunction Code: 0x%02x", pd[offset+9]);
-                               offset += 7 + 3;
-                       }
-                       else {
-                               offset += 7;
-                       }
+       nw_connection = (header.conn_high << 16) + header.conn_low;
 
-                       if (ncp_request->req) {
-                               ti = proto_tree_add_text(ncp_tree, NullTVB, offset, END_OF_FRAME,
-                               "NCP Request Packet");
-                               field_tree = proto_item_add_subtree(ti, ett_ncp_request_fields);
+       if (tree) {
+               ti = proto_tree_add_item(tree, proto_ncp, tvb, 0, -1, FALSE);
+               ncp_tree = proto_item_add_subtree(ti, ett_ncp);
 
-                               parse_ncp_svc_fields(pd, field_tree, offset, ncp_request->req);
+               if (is_tcp) {
+                       proto_tree_add_uint(ncp_tree, hf_ncp_ip_sig, tvb, 0, 4, ncpiph.signature);
+                       proto_tree_add_uint(ncp_tree, hf_ncp_ip_length, tvb, 4, 4, ncpiph.length);
+                       if (ncpiph.signature == NCPIP_RQST) {
+                               proto_tree_add_uint(ncp_tree, hf_ncp_ip_ver, tvb, 8, 4, ncpiphrq.version);
+                               proto_tree_add_uint(ncp_tree, hf_ncp_ip_rplybufsize, tvb, 12, 4, ncpiphrq.rplybufsize);
                        }
+                       if (is_signed)
+                               proto_tree_add_item(ncp_tree, hf_ncp_ip_packetsig, tvb, 16, 8, FALSE);
                }
-       }
-}
-
-static void
-dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd,
-       guint16 nw_connection, guint8 nw_sequence,
-       proto_tree *ncp_tree, proto_tree *tree) {
-
-       struct ncp_reply_header         reply;
-       conversation_t                  *conversation;
-       struct ncp2222_record           *ncp_request = NULL;
-       struct ncp_request_val          *request_val;
-       struct ncp_request_key          request_key;
-       proto_tree                      *field_tree = NULL;
-       proto_item                      *ti = NULL;
-
-       memcpy(&reply, &pd[offset], sizeof(reply));
-
-       /* Find the conversation whence the request would have come. */
-
-       conversation = find_conversation(&pi.src, &pi.dst,
-                   PT_NCP, nw_connection, nw_connection);
-       if (conversation != NULL) {
-               /* find the record telling us the request made that caused
-                  this reply */
-               request_key.conversation = conversation;
-               request_key.nw_sequence = nw_sequence;
-
-               #if defined(DEBUG_NCP_HASH)
-               printf("Looking for conversation %p sequence %u (retval=%p)\n",
-                       conversation, nw_sequence, request_val);
-               #endif
-
-               request_val = (struct ncp_request_val*)
-                       g_hash_table_lookup(ncp_request_hash, &request_key);
-       } else {
-               /* We haven't seen an RPC call for that conversation,
-                  so we can't check for a reply to that call. */
-               request_val = NULL;
+               proto_tree_add_uint(ncp_tree, hf_ncp_type,      tvb, commhdr + 0, 2, header.type);
        }
 
-       if (request_val)
-               ncp_request = request_val->ncp_record;
 
-       if (check_col(fd, COL_INFO)) {
-               if (reply.completion_code == 0) {
-                       col_add_fstr(fd, COL_INFO, "R OK");
+       /*
+        * Process the packet-type-specific header.
+        */
+       switch (header.type) {
+
+       case NCP_BROADCAST_SLOT:        /* Server Broadcast */
+               proto_tree_add_uint(ncp_tree, hf_ncp_seq,       tvb, commhdr + 2, 1, header.sequence);
+               proto_tree_add_uint(ncp_tree, hf_ncp_connection,tvb, commhdr + 3, 3, nw_connection);
+               proto_tree_add_item(ncp_tree, hf_ncp_task,      tvb, commhdr + 4, 1, FALSE);
+               proto_tree_add_item(ncp_tree, hf_ncp_oplock_flag, tvb, commhdr + 9, 1, FALSE);
+               proto_tree_add_item(ncp_tree, hf_ncp_oplock_handle, tvb, commhdr + 10, 4, FALSE);
+               break;
+
+       case NCP_LIP_ECHO:              /* Lip Echo Packet */
+               proto_tree_add_item(ncp_tree, hf_lip_echo, tvb, commhdr, 13, FALSE);
+               break;
+
+       case NCP_BURST_MODE_XFER:       /* Packet Burst Packet */
+               /*
+                * XXX - we should keep track of whether there's a burst
+                * outstanding on a connection and, if not, treat the
+                * beginning of the data as a burst header.
+                *
+                * The burst header contains:
+                *
+                *      4 bytes of little-endian function number:
+                *          1 = read, 2 = write;
+                *
+                *      4 bytes of file handle;
+                *
+                *      8 reserved bytes;
+                *
+                *      4 bytes of big-endian file offset;
+                *
+                *      4 bytes of big-endian byte count.
+                *
+                * The data follows for a burst write operation.
+                *
+                * The first packet of a burst read reply contains:
+                *
+                *      4 bytes of little-endian result code:
+                *         0: No error
+                *         1: Initial error
+                *         2: I/O error
+                *         3: No data read;
+                *
+                *      4 bytes of returned byte count (big-endian?).
+                *
+                * The data follows.
+                *
+                * Each burst of a write request is responded to with a
+                * burst packet with a 2-byte little-endian result code:
+                *
+                *      0: Write successful
+                *      4: Write error
+                */
+               flags = tvb_get_guint8(tvb, commhdr + 2);
+               strcpy(flags_str, "");
+               sep = " (";
+               if (flags & ABT) {
+                       strcat(flags_str, sep);
+                       strcat(flags_str, "ABT");
+                       sep = ",";
+               }
+               if (flags & EOB) {
+                       strcat(flags_str, sep);
+                       strcat(flags_str, "EOB");
+                       sep = ",";
                }
-               else {
-                       col_add_fstr(fd, COL_INFO, "R Not OK");
+               if (flags & SYS) {
+                       strcat(flags_str, sep);
+                       strcat(flags_str, "SYS");
                }
+               if (flags_str[0] != '\0')
+                       strcat(flags_str, ")");
+               ti = proto_tree_add_uint_format(ncp_tree, hf_ncp_system_flags,
+                   tvb, commhdr + 2, 1, flags, "Flags: 0x%04x%s", flags,
+                   flags_str);
+               flags_tree = proto_item_add_subtree(ti, ett_ncp_system_flags);
+               proto_tree_add_item(flags_tree, hf_ncp_system_flags_abt,
+                   tvb, commhdr + 2, 1, FALSE);
+               proto_tree_add_item(flags_tree, hf_ncp_system_flags_eob,
+                   tvb, commhdr + 2, 1, FALSE);
+               proto_tree_add_item(flags_tree, hf_ncp_system_flags_sys,
+                   tvb, commhdr + 2, 1, FALSE);
+
+               proto_tree_add_item(ncp_tree, hf_ncp_stream_type,
+                   tvb, commhdr + 3, 1, FALSE);
+               proto_tree_add_item(ncp_tree, hf_ncp_src_connection,
+                   tvb, commhdr + 4, 4, FALSE);
+               proto_tree_add_item(ncp_tree, hf_ncp_dst_connection,
+                   tvb, commhdr + 8, 4, FALSE);
+               proto_tree_add_item(ncp_tree, hf_ncp_packet_seqno,
+                   tvb, commhdr + 12, 4, FALSE);
+               proto_tree_add_item(ncp_tree, hf_ncp_delay_time,
+                   tvb, commhdr + 16, 4, FALSE);
+               proto_tree_add_item(ncp_tree, hf_ncp_burst_seqno,
+                   tvb, commhdr + 20, 2, FALSE);
+               proto_tree_add_item(ncp_tree, hf_ncp_ack_seqno,
+                   tvb, commhdr + 22, 2, FALSE);
+               proto_tree_add_item(ncp_tree, hf_ncp_burst_len,
+                   tvb, commhdr + 24, 4, FALSE);
+               proto_tree_add_item(ncp_tree, hf_ncp_data_offset,
+                   tvb, commhdr + 28, 4, FALSE);
+               data_len = tvb_get_ntohs(tvb, commhdr + 32);
+               proto_tree_add_uint(ncp_tree, hf_ncp_data_bytes,
+                   tvb, commhdr + 32, 2, data_len);
+               missing_fraglist_count = tvb_get_ntohs(tvb, commhdr + 34);
+               proto_tree_add_item(ncp_tree, hf_ncp_missing_fraglist_count,
+                   tvb, commhdr + 34, 2, FALSE);
+               break;
+
+       case NCP_SERVICE_REQUEST:       /* Server NCP Request */
+       case NCP_SERVICE_REPLY:         /* Server NCP Reply */
+       case NCP_ALLOCATE_SLOT:         /* Allocate Slot Request */
+       case NCP_WATCHDOG:              /* Watchdog Packet */
+       case NCP_DEALLOCATE_SLOT:       /* Deallocate Slot Request */
+       case NCP_POSITIVE_ACK:          /* Positive Acknowledgement */
+       default:
+               proto_tree_add_uint(ncp_tree, hf_ncp_seq,       tvb, commhdr + 2, 1, header.sequence);
+               proto_tree_add_uint(ncp_tree, hf_ncp_connection,tvb, commhdr + 3, 3, nw_connection);
+               proto_tree_add_item(ncp_tree, hf_ncp_task,      tvb, commhdr + 4, 1, FALSE);
+               break;
        }
 
-       if (ncp_tree) {
-               /* A completion code of 0 always means OK. Other values have different
-                * meanings */
-               if (ncp_request) {
-                       proto_tree_add_text(ncp_tree, NullTVB, offset+6,    1,
-                               "Completion Code: 0x%02x (%s)", reply.completion_code,
-                               ncp_completion_code(reply.completion_code, ncp_request->family));
+       /*
+        * Process the packet body.
+        */
+       switch (header.type) {
+
+       case NCP_ALLOCATE_SLOT:         /* Allocate Slot Request */
+       case NCP_SERVICE_REQUEST:       /* Server NCP Request */
+       case NCP_DEALLOCATE_SLOT:       /* Deallocate Slot Request */
+       case NCP_BROADCAST_SLOT:        /* Server Broadcast Packet */
+               next_tvb = tvb_new_subset(tvb, hdr_offset, -1, -1);
+               if (tvb_get_guint8(tvb, commhdr+6) == 0x68) {
+                       ncp_nds_verb = tvb_get_ntohl(tvb, commhdr+4);
+                       if (tvb_get_guint8(tvb, commhdr+7) == 0x02) {  /* NDS Packet to decode */
+                               dissect_nds_request(next_tvb, pinfo, nw_connection, 
+                                   header.sequence, header.type, ncp_tree);
+                       } else {
+                               dissect_ncp_request(next_tvb, pinfo, nw_connection,
+                                   header.sequence, header.type, ncp_tree);
+                       }
+               } else {
+                       dissect_ncp_request(next_tvb, pinfo, nw_connection,
+                           header.sequence, header.type, ncp_tree);
                }
-               else {
-                       proto_tree_add_text(ncp_tree, NullTVB, offset+6,    1,
-                               "Completion Code: 0x%02x (%s)", reply.completion_code,
-                               reply.completion_code == 0 ? "OK" : "Unknown");
+               break;
+
+       case NCP_SERVICE_REPLY:         /* Server NCP Reply */
+       case NCP_POSITIVE_ACK:          /* Positive Acknowledgement */
+               next_tvb = tvb_new_subset(tvb, hdr_offset, -1, -1);
+               dissect_ncp_reply(next_tvb, pinfo, nw_connection,
+                       header.sequence, header.type, ncp_tree);
+               break;
+
+       case NCP_WATCHDOG:              /* Watchdog Packet */
+               /*
+                * XXX - should the completion code be interpreted as
+                * it is in "packet-ncp2222.inc"?  If so, this
+                * packet should be handled by "dissect_ncp_reply()".
+                */
+               proto_tree_add_item(ncp_tree, hf_ncp_completion_code,
+                   tvb, commhdr + 6, 1, TRUE);
+               proto_tree_add_item(ncp_tree, hf_ncp_connection_status,
+                   tvb, commhdr + 7, 1, TRUE);
+               proto_tree_add_item(ncp_tree, hf_ncp_slot,
+                   tvb, commhdr + 8, 1, TRUE);
+               proto_tree_add_item(ncp_tree, hf_ncp_control_code,
+                   tvb, commhdr + 9, 1, TRUE);
+               /*
+                * Display the rest of the packet as data.
+                */
+               if (tvb_offset_exists(tvb, commhdr + 10)) {
+                       call_dissector(data_handle,
+                           tvb_new_subset(tvb, commhdr + 10, -1, -1),
+                           pinfo, ncp_tree);
                }
-
-               proto_tree_add_text(ncp_tree, NullTVB, offset+7,    1,
-                       "Connection Status: %d", reply.connection_state);
-
-               if (ncp_request) {
-
-                       if (ncp_request->rep) {
-                               ti = proto_tree_add_text(ncp_tree, NullTVB, offset+8, END_OF_FRAME,
-                               "NCP Reply Packet");
-                               field_tree = proto_item_add_subtree(ti, ett_ncp_reply_fields);
-
-                               parse_ncp_svc_fields(pd, field_tree, offset+8, ncp_request->rep);
+               break;
+
+       case NCP_BURST_MODE_XFER:       /* Packet Burst Packet */
+               if (flags & SYS) {
+                       /*
+                        * System packet; show missing fragments if there
+                        * are any.
+                        */
+                       offset = commhdr + 36;
+                       while (missing_fraglist_count != 0) {
+                               proto_tree_add_item(ncp_tree, hf_ncp_missing_data_offset,
+                                   tvb, offset, 4, FALSE);
+                               proto_tree_add_item(ncp_tree, hf_ncp_missing_data_count,
+                                   tvb, offset, 2, FALSE);
+                               missing_fraglist_count--;
+                       }
+               } else {
+                       /*
+                        * XXX - do this by using -1 and -1 as the length
+                        * arguments to "tvb_new_subset()" and then calling
+                        * "tvb_set_reported_length()"?  That'll throw an
+                        * exception if "data_len" goes past the reported
+                        * length of the packet, but that's arguably a
+                        * feature in this case.
+                        */
+                       length_remaining = tvb_length_remaining(tvb, commhdr + 36);
+                       if (length_remaining > data_len)
+                               length_remaining = data_len;
+                       if (data_len != 0) {
+                               call_dissector(data_handle,
+                                   tvb_new_subset(tvb, commhdr + 36,
+                                       length_remaining, data_len),
+                                   pinfo, ncp_tree);
                        }
                }
-       }
-
+               break;
+
+       case NCP_LIP_ECHO:              /* LIP Echo Packet */
+               proto_tree_add_text(ncp_tree, tvb, commhdr, -1,
+                   "Lip Echo Packet");
+               break;
+
+       default:
+               if (tree) {
+                       proto_tree_add_text(ncp_tree, tvb, commhdr + 6, -1,
+                           "%s packets not supported yet",
+                           val_to_str(header.type, ncp_type_vals,
+                               "Unknown type (0x%04x)"));
+               }
+               break;
+       }
 }
 
-/* Populates the protocol tree with information about the svc_record fields */
 static void
-parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
-       struct svc_record *svc)
+dissect_ncp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
-       struct svc_record *rec = svc;
-       int field_offset = offset;
-       int field_length = 0;
-
-       while (rec->type != nend) {
-               switch(rec->type) {
-                       case nbeshort:
-                               field_length = 2;
-                               proto_tree_add_text(ncp_tree, NullTVB, field_offset,
-                                       field_length, rec->description, pntohs(&pd[field_offset]));
-                               break;
-
-                       case nasciile:
-                               field_length = pd[field_offset];
-                               proto_tree_add_text(ncp_tree, NullTVB, field_offset,
-                                       field_length + 1, rec->description, field_length,
-                                       &pd[field_offset+1]);
-                               break;
-
-                       case nhex:
-                               field_length = rec->length;
-                               proto_tree_add_text(ncp_tree, NullTVB, field_offset,
-                                       field_length, rec->description);
-                               break;  
-
-                        default:
-                               ; /* nothing */
-                               break;
-               }
-               field_offset += field_length;
-               rec++;
-       }       
+       dissect_ncp_common(tvb, pinfo, tree, FALSE);
 }
 
-static char*
-ncp_completion_code(guint8 ccode, enum nfamily family)
+static guint
+get_ncp_pdu_len(tvbuff_t *tvb, int offset)
 {
-               char    *text;
-
-#define NCP_CCODE_MIN 0x7e
-#define NCP_CCODE_MAX 0xff
-
-       /* From Appendix C of "Programmer's Guide to NetWare Core Protocol" */
-       static char     *ccode_text[] = {
-               /* 7e */ "NCP boundary check failed",
-               /* 7f */ "Unknown",
-               /* 80 */ "Lock fail. The file is already open",
-               /* 81 */ "A file handle could not be allocated by the file server",
-               /* 82 */ "Unauthorized to open file",
-               /* 83 */ "Unable to read/write the volume. Possible bad sector on the file server",
-               /* 84 */ "Unauthorized to create the file",
-               /* 85 */ "",
-               /* 86 */ "Unknown",
-               /* 87 */ "An unexpected character was encountered in the filename",
-               /* 88 */ "FileHandle is not valid",
-               /* 89 */ "Unauthorized to search this directory",
-               /* 8a */ "Unauthorized to delete a file in this directory",
-               /* 8b */ "Unauthorized to rename a file in this directory",
-               /* 8c */ "Unauthorized to modify a file in this directory",
-               /* 8d */ "Some of the affected files are in use by another client",
-               /* 8e */ "All of the affected files are in use by another client",
-               /* 8f */ "Some of the affected file are read only",
-               /* 90 */ "",
-               /* 91 */ "Some of the affected files already exist",
-               /* 92 */ "All of the affected files already exist",
-               /* 93 */ "Unauthorized to read from this file",
-               /* 94 */ "Unauthorized to write to this file",
-               /* 95 */ "The affected file is detached",
-               /* 96 */ "The file server has run out of memory to service this request",
-               /* 97 */ "Unknown",
-               /* 98 */ "The affected volume is not mounted",
-               /* 99 */ "The file server has run out of directory space on the affected volume",
-               /* 9a */ "The request attempted to rename the affected file to another volume",
-               /* 9b */ "DirHandle is not associated with a valid directory path",
-               /* 9c */ "",
-               /* 9d */ "A directory handle was not available for allocation",
-               /* 9e */ "The filename does not conform to a legal name for this name space",
-               /* 9f */ "The request attempted to delete a directory that is in use by another client",
-               /* a0 */ "The request attempted to delete a directory that is not empty",
-               /* a1 */ "An unrecoverable error occurred on the affected directory",
-               /* a2 */ "The request attempted to read from a file region that is physically locked",
-               /* a3 */ "Unknown",
-               /* a4 */ "Unknown",
-               /* a5 */ "Unknown",
-               /* a6 */ "Unknown",
-               /* a7 */ "Unknown",
-               /* a8 */ "Unknown",
-               /* a9 */ "Unknown",
-               /* aa */ "Unknown",
-               /* ab */ "Unknown",
-               /* ac */ "Unknown",
-               /* ad */ "Unknown",
-               /* ae */ "Unknown",
-               /* af */ "Unknown",
-               /* b0 */ "Unknown",
-               /* b1 */ "Unknown",
-               /* b2 */ "Unknown",
-               /* b3 */ "Unknown",
-               /* b4 */ "Unknown",
-               /* b5 */ "Unknown",
-               /* b6 */ "Unknown",
-               /* b7 */ "Unknown",
-               /* b8 */ "Unknown",
-               /* b9 */ "Unknown",
-               /* ba */ "Unknown",
-               /* bb */ "Unknown",
-               /* bc */ "Unknown",
-               /* bd */ "Unknown",
-               /* be */ "Unknown",
-               /* bf */ "Requests for this name space are not valid on this volume",
-               /* c0 */ "Unauthorized to retrieve accounting data",
-               /* c1 */ "The 'account balance' property does not exist",
-               /* c2 */ "The object has exceeded its credit limit",
-               /* c3 */ "Too many holds have been placed against this account",
-               /* c4 */ "The account for this bindery object has been disabled",
-               /* c5 */ "Access to the account has been denied because of intruder detections",
-               /* c6 */ "The caller does not have operator privileges",
-               /* c7 */ "Unknown",
-               /* c8 */ "Unknown",
-               /* c9 */ "Unknown",
-               /* ca */ "Unknown",
-               /* cb */ "Unknown",
-               /* cc */ "Unknown",
-               /* cd */ "Unknown",
-               /* ce */ "Unknown",
-               /* cf */ "Unknown",
-               /* d0 */ "Queue error",
-               /* d1 */ "The queue associated with Object ID does not exist",
-               /* d2 */ "A queue server is not associated with the selected queue",
-               /* d3 */ "No queue rights",
-               /* d4 */ "The queue associated with Object ID is full and cannot accept another request",
-               /* d5 */ "The job associated with Job Number does not exist in this queue",
-               /* d6 */ "",
-               /* d7 */ "",
-               /* d8 */ "Queue not active",
-               /* d9 */ "",
-               /* da */ "",
-               /* db */ "",
-               /* dc */ "Unknown",
-               /* dd */ "Unknown",
-               /* de */ "Attempted to login to the file server with an incorrect password",
-               /* df */ "Attempted to login to the file server with a password that has expired",
-               /* e0 */ "Unknown",
-               /* e1 */ "Unknown",
-               /* e2 */ "Unknown",
-               /* e3 */ "Unknown",
-               /* e4 */ "Unknown",
-               /* e5 */ "Unknown",
-               /* e6 */ "Unknown",
-               /* e7 */ "No disk track",
-               /* e8 */ "",
-               /* e9 */ "Unknown",
-               /* ea */ "The bindery object is not a member of this set",
-               /* eb */ "The property is not a set property",
-               /* ec */ "The set property does not exist",
-               /* ed */ "The property already exists",
-               /* ee */ "The bindery object already exists",
-               /* ef */ "Illegal characters in Object Name field",
-               /* f0 */ "A wildcard was detected in a field that does not support wildcards",
-               /* f1 */ "The client does not have the rights to access this bindery objecs",
-               /* f2 */ "Unauthorized to read from this object",
-               /* f3 */ "Unauthorized to rename this object",
-               /* f4 */ "Unauthorized to delete this object",
-               /* f5 */ "Unauthorized to create this object",
-               /* f6 */ "Unauthorized to delete the property of this object",
-               /* f7 */ "Unauthorized to create this property",
-               /* f8 */ "Unauthorized to write to this property",
-               /* f9 */ "Unauthorized to read this property",
-               /* fa */ "Temporary remap error",
-               /* fb */ "",
-               /* fc */ "",
-               /* fd */ "",
-               /* fe */ "",
-               /* ff */ ""
-       };
-
-       switch (ccode) {
-               case 0:
-                       return "OK";
-                       break;
-
-               case 3:
-                       return "Client not accepting messages";
-                       break;
-       }
-
-       if (ccode >= NCP_CCODE_MIN && ccode <= NCP_CCODE_MAX) {
-               text = ccode_text[ccode - NCP_CCODE_MIN];
-               /* If there really is text, return it */
-               if (text[0] != 0)
-                       return text;
-       }
-       else {
-               return "Unknown";
-       }
-
-       /* We have a completion code with multiple translations. We'll use the
-        * nfamily that this request type belongs to to give the right
-        * translation.
-        */
-       switch (ccode) {
+  guint32 signature;
+
+  /*
+   * Check the NCP-over-TCP header signature, to make sure it's there.
+   * If it's not there, we cannot trust the next 4 bytes to be a
+   * packet length+"has signature" flag, so we just say the length is
+   * "what remains in the packet".
+   */
+  signature = tvb_get_ntohl(tvb, offset);
+  if (signature != NCPIP_RQST && signature != NCPIP_RPLY)
+    return tvb_length_remaining(tvb, offset);
+  
+  /*
+   * Get the length of the NCP-over-TCP packet.  Strip off the "has
+   * signature" flag.
+   */
+  return tvb_get_ntohl(tvb, offset + 4) & 0x7fffffff;
+}
 
-               case 0xfc:
-                       switch(family) {
-                               case NCP_QUEUE_SERVICES:
-                                       return "The message queue cannot accept another message";
-                                       break;
-                               case NCP_BINDERY_SERVICES:
-                                       return "The specified bindery object does not exist";
-                                       break;
-                               default:
-                                       return "Unknown";
-                                       break;
-                       }
-                       break;
+static void
+dissect_ncp_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+  dissect_ncp_common(tvb, pinfo, tree, TRUE);
+}
 
-               default:
-                       return "I don't know how to parse this completion code. Please send this packet trace to Gilbert Ramirez <gram@xiexie.org> for analysis";
-       }
+static void
+dissect_ncp_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+  tcp_dissect_pdus(tvb, pinfo, tree, ncp_desegment, 8, get_ncp_pdu_len,
+       dissect_ncp_tcp_pdu);
 }
 
 void
@@ -1028,47 +546,172 @@ proto_register_ncp(void)
 
   static hf_register_info hf[] = {
     { &hf_ncp_ip_sig,
-      { "NCP over IP signature",               "ncp.ip.signature",
+      { "NCP over IP signature",       "ncp.ip.signature",
         FT_UINT32, BASE_HEX, VALS(ncp_ip_signature), 0x0,
-        "NCP over IP transport signature"}},
+        "", HFILL }},
+    { &hf_ncp_ip_length,
+      { "NCP over IP length",          "ncp.ip.length",
+        FT_UINT32, BASE_DEC, NULL, 0x0,
+        "", HFILL }},
     { &hf_ncp_ip_ver,
-      { "Version",             "ncp.ip.version",
+      { "NCP over IP Version",         "ncp.ip.version",
+        FT_UINT32, BASE_DEC, NULL, 0x0,
+        "", HFILL }},
+    { &hf_ncp_ip_rplybufsize,
+      { "NCP over IP Reply Buffer Size",       "ncp.ip.replybufsize",
         FT_UINT32, BASE_DEC, NULL, 0x0,
-        "NCP over IP verion"}},
+        "", HFILL }},
+    { &hf_ncp_ip_packetsig,
+      { "NCP over IP Packet Signature",        "ncp.ip.packetsig",
+        FT_BYTES, BASE_NONE, NULL, 0x0,
+        "", HFILL }},
     { &hf_ncp_type,
       { "Type",                        "ncp.type",
-       FT_UINT16, BASE_HEX, NULL, 0x0,
-       "NCP message type" }},
+       FT_UINT16, BASE_HEX, VALS(ncp_type_vals), 0x0,
+       "NCP message type", HFILL }},
     { &hf_ncp_seq,
       { "Sequence Number",             "ncp.seq",
        FT_UINT8, BASE_DEC, NULL, 0x0,
-       "" }},
+       "", HFILL }},
     { &hf_ncp_connection,
       { "Connection Number",    "ncp.connection",
        FT_UINT16, BASE_DEC, NULL, 0x0,
-       "" }},
+       "", HFILL }},
     { &hf_ncp_task,
       { "Task Number",         "ncp.task",
        FT_UINT8, BASE_DEC, NULL, 0x0,
-       "" }}
+       "", HFILL }},
+    { &hf_ncp_oplock_flag,
+      { "Oplock Flag",    "ncp.oplock_flag",
+       FT_UINT8, BASE_HEX, NULL, 0x0,
+       "", HFILL }},
+    { &hf_ncp_oplock_handle,
+      { "File Handle",    "ncp.oplock_handle",
+       FT_UINT16, BASE_HEX, NULL, 0x0,
+       "", HFILL }},
+    { &hf_ncp_stream_type,
+      { "Stream Type",         "ncp.stream_type",
+       FT_UINT8, BASE_HEX, NULL, 0x0,
+       "Type of burst", HFILL }},
+    { &hf_ncp_system_flags,
+      { "System Flags",        "ncp.system_flags",
+       FT_UINT8, BASE_HEX, NULL, 0x0,
+       "", HFILL }},
+    { &hf_ncp_system_flags_abt,
+      { "ABT",         "ncp.system_flags.abt",
+       FT_BOOLEAN, 8, NULL, ABT,
+       "Is this an abort request?", HFILL }},
+    { &hf_ncp_system_flags_eob,
+      { "EOB",         "ncp.system_flags.eob",
+       FT_BOOLEAN, 8, NULL, EOB,
+       "Is this the last packet of the burst?", HFILL }},
+    { &hf_ncp_system_flags_sys,
+      { "SYS",         "ncp.system_flags.sys",
+       FT_BOOLEAN, 8, NULL, SYS,
+       "Is this a system packet?", HFILL }},
+    { &hf_ncp_src_connection,
+      { "Source Connection ID",    "ncp.src_connection",
+       FT_UINT32, BASE_DEC, NULL, 0x0,
+       "The workstation's connection identification number", HFILL }},
+    { &hf_ncp_dst_connection,
+      { "Destination Connection ID",    "ncp.dst_connection",
+       FT_UINT32, BASE_DEC, NULL, 0x0,
+       "The server's connection identification number", HFILL }},
+    { &hf_ncp_packet_seqno,
+      { "Packet Sequence Number",    "ncp.packet_seqno",
+       FT_UINT32, BASE_DEC, NULL, 0x0,
+       "Sequence number of this packet in a burst", HFILL }},
+    { &hf_ncp_delay_time,
+      { "Delay Time",    "ncp.delay_time",     /* in 100 us increments */
+       FT_UINT32, BASE_DEC, NULL, 0x0,
+       "Delay time between consecutive packet sends (100 us increments)", HFILL }},
+    { &hf_ncp_burst_seqno,
+      { "Burst Sequence Number",    "ncp.burst_seqno",
+       FT_UINT16, BASE_DEC, NULL, 0x0,
+       "Sequence number of this packet in the burst", HFILL }},
+    { &hf_ncp_ack_seqno,
+      { "ACK Sequence Number",    "ncp.ack_seqno",
+       FT_UINT16, BASE_DEC, NULL, 0x0,
+       "Next expected burst sequence number", HFILL }},
+    { &hf_ncp_burst_len,
+      { "Burst Length",    "ncp.burst_len",
+       FT_UINT32, BASE_DEC, NULL, 0x0,
+       "Total length of data in this burst", HFILL }},
+    { &hf_ncp_data_offset,
+      { "Data Offset",    "ncp.data_offset",
+       FT_UINT32, BASE_DEC, NULL, 0x0,
+       "Offset of this packet in the burst", HFILL }},
+    { &hf_ncp_data_bytes,
+      { "Data Bytes",    "ncp.data_bytes",
+       FT_UINT16, BASE_DEC, NULL, 0x0,
+       "Number of data bytes in this packet", HFILL }},
+    { &hf_ncp_missing_fraglist_count,
+      { "Missing Fragment List Count",    "ncp.missing_fraglist_count",
+       FT_UINT16, BASE_DEC, NULL, 0x0,
+       "Number of missing fragments reported", HFILL }},
+    { &hf_ncp_missing_data_offset,
+      { "Missing Data Offset",    "ncp.missing_data_offset",
+       FT_UINT32, BASE_DEC, NULL, 0x0,
+       "Offset of beginning of missing data", HFILL }},
+    { &hf_ncp_missing_data_count,
+      { "Missing Data Count",    "ncp.missing_data_count",
+       FT_UINT16, BASE_DEC, NULL, 0x0,
+       "Number of bytes of missing data", HFILL }},
+    { &hf_ncp_completion_code,
+      { "Completion Code",    "ncp.completion_code",
+       FT_UINT8, BASE_DEC, NULL, 0x0,
+       "", HFILL }},
+    { &hf_ncp_connection_status,
+      { "Connection Status",    "ncp.connection_status",
+       FT_UINT8, BASE_DEC, NULL, 0x0,
+       "", HFILL }},
+    { &hf_ncp_slot,
+      { "Slot",    "ncp.slot",
+       FT_UINT8, BASE_DEC, NULL, 0x0,
+       "", HFILL }},
+    { &hf_ncp_control_code,
+      { "Control Code",    "ncp.control_code",
+       FT_UINT8, BASE_DEC, NULL, 0x0,
+       "", HFILL }},
+    { &hf_ncp_fragment_handle,
+      { "Fragment Handle",    "ncp.fragger_hndl",
+    FT_UINT16, BASE_HEX, NULL, 0x0,
+    "", HFILL }},
+    { &hf_lip_echo,
+      { "Large Internet Packet Echo",    "ncp.lip_echo",
+    FT_STRING, BASE_NONE, NULL, 0x0,
+    "", HFILL }},
   };
   static gint *ett[] = {
     &ett_ncp,
-    &ett_ncp_request_fields,
-    &ett_ncp_reply_fields,
+    &ett_ncp_system_flags,
   };
+  module_t *ncp_module;
 
-  proto_ncp = proto_register_protocol("NetWare Core Protocol", "ncp");
+  proto_ncp = proto_register_protocol("NetWare Core Protocol", "NCP", "ncp");
   proto_register_field_array(proto_ncp, hf, array_length(hf));
   proto_register_subtree_array(ett, array_length(ett));
-  register_init_routine(&ncp_init_protocol);
+
+  ncp_module = prefs_register_protocol(proto_ncp, NULL);
+  prefs_register_obsolete_preference(ncp_module, "initial_hash_size");
+  prefs_register_bool_preference(ncp_module, "desegment",
+    "Desegment all NCP-over-TCP messages spanning multiple segments",
+    "Whether the NCP dissector should desegment all messages spanning multiple TCP segments",
+    &ncp_desegment);
 }
 
 void
 proto_reg_handoff_ncp(void)
 {
-  dissector_add("tcp.port", TCP_PORT_NCP, dissect_ncp);
-  dissector_add("udp.port", UDP_PORT_NCP, dissect_ncp);
-  dissector_add("ipx.packet_type", IPX_PACKET_TYPE_NCP, dissect_ncp);
-  dissector_add("ipx.socket", IPX_SOCKET_NCP, dissect_ncp);
+  dissector_handle_t ncp_handle;
+  dissector_handle_t ncp_tcp_handle;
+
+  ncp_handle = create_dissector_handle(dissect_ncp, proto_ncp);
+  ncp_tcp_handle = create_dissector_handle(dissect_ncp_tcp, proto_ncp);
+  dissector_add("tcp.port", TCP_PORT_NCP, ncp_tcp_handle);
+  dissector_add("udp.port", UDP_PORT_NCP, ncp_handle);
+  dissector_add("ipx.packet_type", IPX_PACKET_TYPE_NCP, ncp_handle);
+  dissector_add("ipx.socket", IPX_SOCKET_NCP, ncp_handle);
+
+  data_handle = find_dissector("data");
 }