Set the svn:eol-style property on all text files to "native", so that
[obnox/wireshark/wip.git] / packet-ncp.c
index 8c62a446fea432c4b9f65341faefa9d84ac671f3..01b8729372c9e7899939c7548ccc278bb5fdcddf 100644 (file)
@@ -1,24 +1,30 @@
 /* packet-ncp.c
  * Routines for NetWare Core Protocol
- * Gilbert Ramirez <gram@verdict.uthscsa.edu>
+ * 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, packet signature,
+ * & NDS packets by Greg Morris <gmorris@novell.com>
  *
- * $Id: packet-ncp.c,v 1.8 1999/03/01 18:28:11 gram Exp $
+ * Portions Copyright (c) by Gilbert Ramirez 2000-2002
+ * Portions Copyright (c) by James Coe 2000-2002
+ * Portions Copyright (c) Novell, Inc. 2000-2003
+ *
+ * $Id$
  *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@unicom.net>
- * 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
  * as published by the Free Software Foundation; either version 2
  * of the License, or (at your option) any later version.
- * 
+ *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 # include "config.h"
 #endif
 
-#include <gtk/gtk.h>
-
-#include <stdio.h>
-
 #ifdef HAVE_SYS_TYPES_H
 # include <sys/types.h>
 #endif
 # include <netinet/in.h>
 #endif
 
-#include "ethereal.h"
-#include "packet.h"
+#include <string.h>
+#include <glib.h>
+#include <epan/packet.h>
+#include "prefs.h"
 #include "packet-ipx.h"
-#include "packet-ncp.h"
+#include "packet-tcp.h"
+#include "packet-ncp-int.h"
+#include "reassemble.h"
+#include <epan/conversation.h>
+
+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 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_system_flags_bsy = -1;
+static int hf_ncp_system_flags_lst = -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;
+static int hf_ncp_burst_command = -1;
+static int hf_ncp_burst_file_handle = -1;
+static int hf_ncp_burst_reserved = -1;
+
+gint ett_ncp = -1;
+gint ett_nds = -1;
+gint ett_nds_segments = -1;
+gint ett_nds_segment = -1;
+static gint ett_ncp_system_flags = -1;
+
+
+/* Tables for reassembly of fragments. */ 
+GHashTable *nds_fragment_table = NULL;
+GHashTable *nds_reassembled_table = NULL;
+dissector_handle_t nds_data_handle;
+
+/* 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
+
+#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);
+
+/* These are the header structures to handle NCP over IP */
+#define        NCPIP_RQST      0x446d6454      /* "DmdT" */
+#define NCPIP_RPLY     0x744e6350      /* "tNcP" */
+
+struct ncp_ip_header {
+       guint32 signature;
+       guint32 length;
+};
+
+
+/* This header only appears on NCP over IP request packets */
+struct ncp_ip_rqhdr {
+       guint32 version;
+       guint32 rplybufsize;
+};
+
+static const value_string ncp_ip_signature[] = {
+       { NCPIP_RQST, "Demand Transport (Request)" },
+       { NCPIP_RPLY, "Transport is NCP (Reply)" },
+       { 0, NULL },
+};
+
+static const value_string burst_command[] = {
+    { 0x01000000, "Burst Read" },
+    { 0x02000000, "Burst Write" },
+    { 0, NULL },
+};
 
 /* The information in this module comes from:
        NetWare LAN Analysis, Second Edition
        ISBN: 0-7821-1362-1
 
   And from the ncpfs source code by Volker Lendecke
-*/
 
+  And:
+       Programmer's Guide to the NetWare Core Protocol
+       Steve Conner & Diane Conner
+       (c) 1996 by Steve Conner & Diane Conner
+       Published by Annabooks, San Diego, California
+        ISBN: 0-929392-31-0
+        
+ And:
+    http:developer.novell.com
+    NCP documentation        
 
+*/
 
-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 }
+/*
+ * 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; /* type=0x5555 doesn't have this */
 };
 
 
-static value_string ncp2222_func[] = {
-       { 17,   "Print and Queue Services" },
-       { 21,   "Message Services" },
-       { 22,   "File and Directory Services" },
-       { 23,   "Binding and Rights Services" },
-       { 34,   "Transaction Tacking Services" },
-       { 35,   "Apple File Services" },
-       { 72,   "File Services" }, /* guess */
-       { 86,   "Extended Attributes Services" },
-       { 87,   "File and Directory Services" },
-       { 88,   "Auditing Services" },
-       { 104,  "Netware Directory Services" },
-       { 123,  "Netware 4.x Statistical Information Services" },
-       { 0,    NULL }
+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 }
 };
 
-static char*
-ncp2222_subfunc(u_short func, u_short subfunc) {
-       value_string    *info_ptr = NULL;
-
-       /* Accounting Services */
-       static value_string     ncp_23[] = {
-               { 150,  "Get Current Account Status" },
-               { 151,  "Submit Account Charge" },
-               { 152,  "Submit Account Hold" },
-               { 153,  "Submit Account Note" },
-               { 0,    NULL }
-       };
-
-       /* Apple File Services */
-       static value_string     ncp_35[] = {
-               { 1,    "AFP Create Directory" },
-               { 2,    "AFP Create File" },
-               { 3,    "AFP Delete" },
-               { 4,    "AFP Get Entry ID from Name" },
-               { 5,    "AFP Get File Information" },
-               { 6,    "AFP Get Entry ID From NetWare Handle" },
-               { 7,    "AFP Rename" },
-               { 8,    "AFP Open File Fork" },
-               { 9,    "AFP Set File Information" },
-               { 10,   "AFP Scan File Information" },
-               { 11,   "AFP 2.0 Alloc Temporary Directory Handle" },
-               { 12,   "AFP Get Entry ID from Name Path" },
-               { 13,   "AFP 2.0 Create Directory" },
-               { 14,   "AFP 2.0 Create File" },
-/* ??? { 15,   "AFP 2.0 Delete File" }, just guessing */
-               { 16,   "AFP 2.0 Set File Information" },
-               { 17,   "AFP 2.0 Scan File Information" },
-               { 18,   "AFP Get DOS Name from Entry ID" },
-               { 19,   "AFP Get Macintosh Info on Deleted File" },
-               { 0,    NULL }
-       };
-
-       /* File services */ /* guess */
-       static value_string ncp_72[] = {
-               { 0xbb, "Read" },
-               { 0,    NULL }
-       };
-
-       /* Auditing Services */
-       static value_string     ncp_88[] = {
-               { 1,    "Query Volume Audit Status" },
-               { 2,    "Add Audit Property" },
-               { 3,    "Add Auditor Access" },
-
-               { 0,    NULL }
-       };
-
-       switch (func) {
-               case 23:
-                       info_ptr = ncp_23;
-                       break;
-               case 35:
-                       info_ptr = ncp_35;
-                       break;
-               case 72:
-                       info_ptr = ncp_72;
-                       break;
-               case 88:
-                       info_ptr = ncp_88;
-                       break;
-               default:
-                       return "Unknown function";
+/* Conversation Struct so we can store whether the conversation is using Packet Signature */
+
+typedef struct {
+       conversation_t  *conversation;
+} mncp_rhash_key;
+
+typedef struct {
+        gboolean            packet_signature;
+} mncp_rhash_value;
+
+static GHashTable *mncp_rhash = NULL;
+static GMemChunk *mncp_rhash_keys = NULL;
+static GMemChunk *mncp_rhash_values = NULL;
+
+/* Hash Functions */
+gint
+mncp_equal(gconstpointer v, gconstpointer v2)
+{
+       const mncp_rhash_key    *val1 = (const mncp_rhash_key*)v;
+       const mncp_rhash_key    *val2 = (const mncp_rhash_key*)v2;
+
+       if (val1->conversation == val2->conversation ) {
+               return 1;
        }
+       return 0;
+}
 
-       return val_to_str(subfunc, info_ptr, "Unknown");
+guint
+mncp_hash(gconstpointer v)
+{
+       const mncp_rhash_key    *mncp_key = (const mncp_rhash_key*)v;
+       return GPOINTER_TO_UINT(mncp_key->conversation);
 }
 
+/* Initializes the hash table and the mem_chunk area each time a new
+ * file is loaded or re-loaded in ethereal */
+static void
+mncp_init_protocol(void)
+{
+       if (mncp_rhash)
+               g_hash_table_destroy(mncp_rhash);
+       if (mncp_rhash_keys)
+               g_mem_chunk_destroy(mncp_rhash_keys);
+       if (mncp_rhash_values)
+               g_mem_chunk_destroy(mncp_rhash_values);
 
-void
-ncp_read(GtkWidget *tree, const u_char *pd, int offset)
+       mncp_rhash = g_hash_table_new(mncp_hash, mncp_equal);
+       mncp_rhash_keys = g_mem_chunk_new("mncp_rhash_keys",
+                       sizeof(mncp_rhash_key),
+                       200 * sizeof(mncp_rhash_key),
+                       G_ALLOC_ONLY);
+       mncp_rhash_values = g_mem_chunk_new("mncp_rhash_values",
+                       sizeof(mncp_rhash_value),
+                       200 * sizeof(mncp_rhash_value),
+                       G_ALLOC_ONLY);
+}
+
+/* After the sequential run, we don't need the ncp_request hash and keys
+ * anymore; the lookups have already been done and the vital info
+ * saved in the reply-packets' private_data in the frame_data struct. */
+static void
+mncp_postseq_cleanup(void)
 {
-       struct ncp_read_header header;
+}
 
-       memcpy(header.handle, &pd[offset], 6);
-       header.offset = pntohl(&pd[offset+6]);
-       header.bytes = pntohs(&pd[offset+10]);
+mncp_rhash_value*
+mncp_hash_insert(conversation_t *conversation)
+{
+       mncp_rhash_key          *key;
+       mncp_rhash_value        *value;
 
-       add_item_to_tree(tree, offset,    6,
-                       "File Handle: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X ",
-                       header.handle[0], header.handle[1], header.handle[2],
-                       header.handle[3], header.handle[4], header.handle[5]);
-       
-       add_item_to_tree(tree, offset+6,    4,
-                       "Starting Offset: %d", header.offset);
+       /* Now remember the request, so we can find it if we later
+          a reply to it. */
+       key = g_mem_chunk_alloc(mncp_rhash_keys);
+       key->conversation = conversation;
 
-       add_item_to_tree(tree, offset+10,    2,
-                       "Bytes to Read: %d", header.bytes);
+       value = g_mem_chunk_alloc(mncp_rhash_values);
+       value->packet_signature = FALSE;
+       
+       g_hash_table_insert(mncp_rhash, key, value);
 
+       return value;
 }
 
-void
-dissect_ncp(const u_char *pd, int offset, frame_data *fd, GtkTree *tree) {
+/* Returns the ncp_rec*, or NULL if not found. */
+mncp_rhash_value*
+mncp_hash_lookup(conversation_t *conversation)
+{
+       mncp_rhash_key          key;
 
-       GtkWidget       *ncp_tree, *ti;
-       guint16         ncp_type;
-       int                     ncp_hdr;
+       key.conversation = conversation;
+
+       return g_hash_table_lookup(mncp_rhash, &key);
+}
+
+/*
+ * Burst packet system flags.
+ */
+#define ABT    0x04            /* Abort request */
+#define BSY 0x08        /* Server Busy */
+#define EOB    0x10            /* End of burst */
+#define LST 0x40        /* Include Fragment List */
+#define SYS    0x80            /* System packet */
+
+static void
+dissect_ncp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+    gboolean is_tcp)
+{
+       proto_tree                      *ncp_tree = NULL;
+       proto_item                      *ti;
+       struct ncp_ip_header            ncpiph;
+       struct ncp_ip_rqhdr             ncpiphrq;
        struct ncp_common_header        header;
-       struct ncp_request_header       request;
-       struct ncp_reply_header         reply;
-       char                                            *ncp_type_text[] = { "Unknown",
-               "Request", "Reply" };
-
-       ncp_type = pntohs(&pd[offset]);
-       header.type = ncp_type;
-       header.sequence = pd[offset+2];
-       header.conn_low = pd[offset+3];
-       header.task = pd[offset+4];
-       header.conn_high = pd[offset+5];
-
-       if (ncp_type == 0x1111 || ncp_type == 0x2222 || ncp_type == 0x5555 ||
-               ncp_type == 0x7777) {
-               ncp_hdr = 7;
-               request.function = pd[offset+6];
-       }
-       else if (ncp_type == 0x3333 || ncp_type == 0x9999) {
-               ncp_hdr = 8;
-               reply.completion_code = pd[offset+6];
-               reply.connection_state = pd[offset+7];
-       }
-       else {
-               ncp_hdr = 6; /* in order to get ncp_type_text[0] */
+       guint16                         nw_connection, ncp_burst_seqno, ncp_ack_seqno;
+       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;
+       int                             hdr_offset = 0;
+       int                             commhdr;
+       int                             offset;
+       gint                            length_remaining;
+       tvbuff_t                        *next_tvb;
+       guint32                         testvar = 0, ncp_burst_command, burst_len, burst_off, burst_file;
+       guint8                          subfunction;
+    mncp_rhash_value    *request_value = NULL;
+    conversation_t      *conversation;
+
+       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);
+
+       hdr_offset = 0;
+
+       if (is_tcp) 
+    {
+               if (tvb_get_ntohl(tvb, hdr_offset) != NCPIP_RQST && tvb_get_ntohl(tvb, hdr_offset) != NCPIP_RPLY) 
+                       hdr_offset += 1;
+               ncpiph.signature        = tvb_get_ntohl(tvb, hdr_offset);
+               ncpiph.length           = tvb_get_ntohl(tvb, hdr_offset+4);
+               hdr_offset += 8;
+               if (ncpiph.signature == NCPIP_RQST) 
+        {
+                       ncpiphrq.version        = tvb_get_ntohl(tvb, hdr_offset);
+                       hdr_offset += 4;
+                       ncpiphrq.rplybufsize    = tvb_get_ntohl(tvb, hdr_offset);
+                       hdr_offset += 4;
+               }
+        /* Ok, we need to track the conversation so that we can determine
+         * if packet signature is occuring for this connection. We will
+         * store the conversation the first time and that state of packet 
+         * signature will be stored later in our logic. This way when we 
+         * dissect reply packets we will be able to determine if we need 
+         * to also dissect with a signature.
+         */
+        conversation = find_conversation(&pinfo->src, &pinfo->dst,
+            PT_NCP, (guint32) pinfo->srcport, (guint32) pinfo->destport, 0);
+               if (ncpiph.length & 0x80000000 || ncpiph.signature == NCPIP_RPLY) 
+        {
+            /* First time through we will store packet signature state */
+                       if (!pinfo->fd->flags.visited) 
+            {
+                if (conversation != NULL) 
+                {
+                    /* find the record telling us the request made that caused
+                    this reply */
+                    request_value = mncp_hash_lookup(conversation);
+                    /* if for some reason we have no conversation in our hash, create one */
+                    if (request_value==NULL)
+                    {
+                        request_value = mncp_hash_insert(conversation);
+                    }
+                }
+                else
+                {
+                    /* It's not part of any conversation - create a new one. */
+                    conversation = conversation_new(&pinfo->src, &pinfo->dst,
+                        PT_NCP, (guint32) pinfo->srcport, (guint32) pinfo->destport, 0);
+                    request_value = mncp_hash_insert(conversation);
+                }
+                /* If this is a request packet then we know that we have a signature */
+                               if (ncpiph.signature == NCPIP_RQST) 
+                {
+                                       hdr_offset += 8;
+                                       ncpiph.length &= 0x7fffffff;
+                    request_value->packet_signature=TRUE;
+                               }
+                else
+                {
+                    /* Now on reply packets we have to use the state of the original request packet */
+                    /* So look up the request value and check the state of packet signature */
+                    request_value = mncp_hash_lookup(conversation);
+                                       if (request_value->packet_signature==TRUE) 
+                    {
+                                               hdr_offset += 8;
+                                               ncpiph.length &= 0x7fffffff;
+                        request_value->packet_signature=TRUE;
+                    }
+                    else
+                    {
+                        request_value->packet_signature=FALSE;
+                    }
+                               }
+                       }
+            else
+            {
+                /* Get request value data */
+                request_value = mncp_hash_lookup(conversation);
+                               if (request_value->packet_signature==TRUE) 
+                {
+                                       hdr_offset += 8;
+                                       ncpiph.length &= 0x7fffffff;
+                               }
+                       }
+               } 
+        else
+        {
+                       if (!pinfo->fd->flags.visited) 
+            {
+                if (conversation != NULL) 
+                {
+                    /* find the record telling us the request made that caused
+                    this reply */
+                    request_value = mncp_hash_lookup(conversation);
+                    /* if for some reason we have no conversation in our hash, create one */
+                    if (request_value==NULL)
+                    {
+                        request_value = mncp_hash_insert(conversation);
+                    }
+                }
+                else
+                {
+                    /* It's not part of any conversation - create a new one. */
+                    conversation = conversation_new(&pinfo->src, &pinfo->dst,
+                        PT_NCP, (guint32) pinfo->srcport, (guint32) pinfo->destport, 0);
+                    request_value = mncp_hash_insert(conversation);
+                }
+                /* find the record telling us the request made that caused
+                   this reply */
+                request_value->packet_signature=FALSE;
+            }
+            else
+            {
+                request_value = mncp_hash_lookup(conversation);
+            }
+        }
        }
 
-       if (check_col(fd, COL_PROTOCOL))
-               col_add_str(fd, COL_PROTOCOL, "NCP");
-       if (check_col(fd, COL_INFO))
-               /* I take advantage of the ncp_hdr length to use as an index into
-                * ncp_type_text[]. Ugly hack, but quick.  */
-               col_add_fstr(fd, COL_INFO, "%s", ncp_type_text[ncp_hdr - 6]);
+       /* Record the offset where the NCP common header starts */
+       commhdr = hdr_offset;
 
+       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);
+
+       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)"));
+
+       }
+
+       nw_connection = (header.conn_high << 16) + header.conn_low;
 
        if (tree) {
-               ti = add_item_to_tree(GTK_WIDGET(tree), offset, END_OF_FRAME,
-                       "NetWare Core Protocol");
-               ncp_tree = gtk_tree_new();
-               add_subtree(ti, ncp_tree, ETT_NCP);
+               ti = proto_tree_add_item(tree, proto_ncp, tvb, 0, -1, FALSE);
+               ncp_tree = proto_item_add_subtree(ti, ett_ncp);
 
-               add_item_to_tree(ncp_tree, offset,      2,
-                       "Type: %s", val_to_str( header.type, request_reply_values,
-                                       "Unknown (%04X)"));
+               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 (request_value->packet_signature==TRUE)
+                                       proto_tree_add_item(ncp_tree, hf_ncp_ip_packetsig, tvb, 16, 8, FALSE);
+                       } else {
+                               if (request_value->packet_signature==TRUE)
+                                       proto_tree_add_item(ncp_tree, hf_ncp_ip_packetsig, tvb, 8, 8, FALSE);
+                       }
+               }
+               proto_tree_add_uint(ncp_tree, hf_ncp_type,      tvb, commhdr + 0, 2, header.type);
+       }
 
-               add_item_to_tree(ncp_tree, offset+2,    1,
-                       "Sequence Number: %d", header.sequence);
 
-               add_item_to_tree(ncp_tree, offset+3,    1,
-                       "Connection Number Low: %d", header.conn_low);
+       /*
+        * Process the packet-type-specific header.
+        */
+       switch (header.type) {
 
-               add_item_to_tree(ncp_tree, offset+4,    1,
-                       "Task Number: %d", header.task);
+       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;
 
-               add_item_to_tree(ncp_tree, offset+5,    1,
-                       "Connection Number High: %d", header.conn_high);
+       case NCP_LIP_ECHO:    /* Lip Echo Packet */
+               proto_tree_add_item(ncp_tree, hf_lip_echo, tvb, commhdr, 13, FALSE);
+               break;
 
-               if (ncp_hdr == 8) {
-                       add_item_to_tree(ncp_tree, offset+6,    1,
-                               "Completion Code: %d", reply.completion_code);
+       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 & BSY) {
+                       strcat(flags_str, sep);
+                       strcat(flags_str, "BSY");
+                       sep = ",";
+               }
+               if (flags & EOB) {
+                       strcat(flags_str, sep);
+                       strcat(flags_str, "EOB");
+                       sep = ",";
+               }
+               if (flags & LST) {
+                       strcat(flags_str, sep);
+                       strcat(flags_str, "LST");
+                       sep = ",";
+               }
+               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_bsy,
+                   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_lst,
+                   tvb, commhdr + 2, 1, FALSE);
+               proto_tree_add_item(flags_tree, hf_ncp_system_flags_sys,
+                   tvb, commhdr + 2, 1, FALSE);
 
-                       add_item_to_tree(ncp_tree, offset+7,    1,
-                               "Connection Status: %d", reply.connection_state);
+               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);
+        ncp_burst_seqno = tvb_get_ntohs(tvb, commhdr+20);
+               proto_tree_add_item(ncp_tree, hf_ncp_burst_seqno,
+                   tvb, commhdr + 20, 2, FALSE);
+        ncp_ack_seqno = tvb_get_ntohs(tvb, commhdr+22);
+               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);
+        if (ncp_burst_seqno==ncp_ack_seqno)
+        {
+            ncp_burst_command = tvb_get_ntohl(tvb, commhdr+36);
+            proto_tree_add_item(ncp_tree, hf_ncp_burst_command,
+                tvb, commhdr + 36, 4, FALSE);
+            burst_file = tvb_get_ntohl(tvb, commhdr+40);
+            proto_tree_add_item(ncp_tree, hf_ncp_burst_file_handle,
+                tvb, commhdr + 40, 4, FALSE);
+            proto_tree_add_item(ncp_tree, hf_ncp_burst_reserved,
+                tvb, commhdr + 44, 8, FALSE);
+            burst_off = tvb_get_ntohl(tvb, commhdr+52);
+            proto_tree_add_item(ncp_tree, hf_ncp_data_offset,
+                tvb, commhdr + 52, 4, FALSE);
+            burst_len = tvb_get_ntohl(tvb, commhdr+56);
+            proto_tree_add_item(ncp_tree, hf_ncp_burst_len,
+                tvb, commhdr + 56, 4, FALSE);
+            if (check_col(pinfo->cinfo, COL_INFO)) {
+                col_set_str(pinfo->cinfo, COL_INFO, match_strval(ncp_burst_command, burst_command));
+                col_append_fstr(pinfo->cinfo, COL_INFO, " %d bytes starting at offset %d in file 0x%08x", burst_len, burst_off, burst_file);
+            }
+            return;
+        }
+               break;
+
+       case NCP_ALLOCATE_SLOT:         /* Allocate Slot Request */
+               length_remaining = tvb_length_remaining(tvb, commhdr + 4);
+               if (length_remaining > 4) { 
+                       testvar = tvb_get_ntohl(tvb, commhdr+4);
+                       if (testvar == 0x4c495020) {
+                               proto_tree_add_item(ncp_tree, hf_lip_echo, tvb, commhdr+4, 13, FALSE);
+                               break;
+                       }
                }
-               else {
-                       add_item_to_tree(ncp_tree, offset+6,            1,
-                               "Function Code: %s (%d)",
-                               val_to_str(request.function, ncp2222_func, "Unknown"),
-                               request.function);
+               /* otherwise fall through */
+    
+       case NCP_POSITIVE_ACK:          /* Positive Acknowledgement */
+       case NCP_SERVICE_REQUEST:       /* Server NCP Request */
+       case NCP_SERVICE_REPLY:         /* Server NCP Reply */
+       case NCP_WATCHDOG:              /* Watchdog Packet */
+       case NCP_DEALLOCATE_SLOT:       /* Deallocate Slot Request */
+       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;
+       }
+
+       /*
+        * Process the packet body.
+        */
+       switch (header.type) {
+
+       case NCP_ALLOCATE_SLOT:         /* Allocate Slot Request */
+               length_remaining = tvb_length_remaining(tvb, commhdr + 4);
+               if (length_remaining > 4) {
+                       testvar = tvb_get_ntohl(tvb, commhdr+4);
+                       if (testvar == 0x4c495020) {
+                               proto_tree_add_text(ncp_tree, tvb, commhdr, -1,
+                                   "Lip Echo Packet");
+                               /*break;*/
+                       }
+               }
+
+       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) {
+                       subfunction = tvb_get_guint8(tvb, commhdr+7);
+                       switch (subfunction) {
+
+                       case 0x02:      /* NDS Frag Packet to decode */
+                               dissect_nds_request(next_tvb, pinfo,
+                                   nw_connection, header.sequence,
+                                   header.type, ncp_tree);
+                               break;
+
+                       case 0x01:      /* NDS Ping */
+                               dissect_ping_req(next_tvb, pinfo,
+                                   nw_connection, header.sequence,
+                                   header.type, ncp_tree);
+                               break;
+
+                       default:
+                               dissect_ncp_request(next_tvb, pinfo,
+                                   nw_connection, header.sequence,
+                                   header.type, ncp_tree);
+                               break;
+                        }
+               } else {
+                       dissect_ncp_request(next_tvb, pinfo, nw_connection,
+                           header.sequence, header.type, ncp_tree);
                }
+               break;
+
+       case NCP_SERVICE_REPLY:         /* Server NCP Reply */
+               next_tvb = tvb_new_subset(tvb, hdr_offset, -1, -1);
+               nds_defrag(next_tvb, pinfo, nw_connection, header.sequence,
+                   header.type, ncp_tree);
+               break;
 
-               offset += ncp_hdr;
+       case NCP_POSITIVE_ACK:          /* Positive Acknowledgement */
+               /*
+                * XXX - this used to call "nds_defrag()", which would
+                * clear out "frags".  Was that the right thing to
+                * do?
+                */
+               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;
 
-               if (ncp_type == 0x2222) {
-                       /* my offset is different now */
-                       add_item_to_tree(ncp_tree, offset,      1,
-                               "Subfunction Code: %s (%d)",
-                               ncp2222_subfunc(pd[offset-1], pd[offset]), pd[offset]);
+       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);
+               }
+               break;
 
-                       if (request.function == 0x48) {
-                               ncp_read(ncp_tree, pd, offset+1);
+       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 {
-                               dissect_data(pd, offset, fd, tree);
+               } 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);
                        }
                }
-               else {
-                       dissect_data(pd, offset, fd, 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;
+       }
+}
+
+static void
+dissect_ncp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+       dissect_ncp_common(tvb, pinfo, tree, FALSE);
+}
+
+static guint
+get_ncp_pdu_len(tvbuff_t *tvb, int offset)
+{
+  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".
+   */
+  /*if (tvb_get_guint8(tvb, offset)==0xff) 
+  {
+      offset += 1;
+  }*/
+  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;
 }
+
+static void
+dissect_ncp_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+  dissect_ncp_common(tvb, pinfo, tree, TRUE);
+}
+
+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
+proto_register_ncp(void)
+{
+
+  static hf_register_info hf[] = {
+    { &hf_ncp_ip_sig,
+      { "NCP over IP signature",       "ncp.ip.signature",
+        FT_UINT32, BASE_HEX, VALS(ncp_ip_signature), 0x0,
+        "", HFILL }},
+    { &hf_ncp_ip_length,
+      { "NCP over IP length",          "ncp.ip.length",
+        FT_UINT32, BASE_DEC, NULL, 0x0,
+        "", HFILL }},
+    { &hf_ncp_ip_ver,
+      { "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,
+        "", 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, 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_system_flags_bsy,
+      { "BSY",         "ncp.system_flags.bsy",
+       FT_BOOLEAN, 8, NULL, BSY,
+       "Is the server busy?", HFILL }},
+    { &hf_ncp_system_flags_lst,
+      { "LST",         "ncp.system_flags.lst",
+       FT_BOOLEAN, 8, NULL, LST,
+       "Return Fragment List?", 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 }},
+    { &hf_ncp_burst_command,
+      { "Burst Command",    "ncp.burst_command",
+       FT_UINT32, BASE_HEX, VALS(burst_command), 0x0,
+       "Packet Burst Command", HFILL }},
+    { &hf_ncp_burst_file_handle,
+      { "Burst File Handle",    "ncp.file_handle",
+       FT_UINT32, BASE_HEX, NULL, 0x0,
+       "Packet Burst File Handle", HFILL }},
+       { &hf_ncp_burst_reserved,
+       { "Reserved", "ncp.burst_reserved", 
+    FT_BYTES, BASE_HEX, NULL, 0x0, "", HFILL }},
+  
+  };
+  static gint *ett[] = {
+    &ett_ncp,
+    &ett_ncp_system_flags,
+    &ett_nds,
+       &ett_nds_segments,
+       &ett_nds_segment,
+  };
+  module_t *ncp_module;
+
+  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));
+
+  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);
+  prefs_register_bool_preference(ncp_module, "defragment_nds",
+    "Defragment all NDS messages spanning multiple packets",
+    "Whether the NCP dissector should defragment all NDS messages spanning multiple packets",
+    &nds_defragment);
+  register_init_routine(&mncp_init_protocol);
+  register_postseq_cleanup_routine(&mncp_postseq_cleanup);
+}
+
+void
+proto_reg_handoff_ncp(void)
+{
+  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");
+}
+
+