Add the re-write of the NetWare Core Protocol dissector. It's mostly
authorgram <gram@f5534014-38df-0310-8fa8-9805f1628bb7>
Fri, 28 Jul 2000 20:03:59 +0000 (20:03 +0000)
committergram <gram@f5534014-38df-0310-8fa8-9805f1628bb7>
Fri, 28 Jul 2000 20:03:59 +0000 (20:03 +0000)
a framework for the dissector; of the more than 400 NCP packet types, only
a handful are defined. But this dissector framework is much better than
the previous one.

git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@2173 f5534014-38df-0310-8fa8-9805f1628bb7

13 files changed:
Makefile.am
Makefile.nmake
README
config.nmake
configure.in
doc/Makefile.am
ncp2222.h [new file with mode: 0644]
ncp2222.py [new file with mode: 0755]
packet-ncp-int.h [new file with mode: 0644]
packet-ncp.c
proto.c
ptvcursor.c [new file with mode: 0644]
ptvcursor.h [new file with mode: 0644]

index 6b13ea78f60d4549672904a3dbbdea3a2cc9b28d..88ff068c066e20d946f621457d4a9036a500c554 100644 (file)
@@ -1,7 +1,7 @@
 # Makefile.am
 # Automake file for Ethereal
 #
-# $Id: Makefile.am,v 1.216 2000/07/28 16:30:15 gram Exp $
+# $Id: Makefile.am,v 1.217 2000/07/28 20:03:39 gram Exp $
 #
 # Ethereal - Network traffic analyzer
 # By Gerald Combs <gerald@zing.org>
@@ -96,6 +96,7 @@ DISSECTOR_SOURCES = \
        packet-nbipx.c \
        packet-nbns.c  \
        packet-ncp.c   \
+       packet-ncp2222.c   \
        packet-netbios.c \
        packet-nfs.c   \
        packet-nlm.c   \
@@ -200,6 +201,7 @@ noinst_HEADERS = \
        packet-llc.h   \
        packet-mount.h \
        packet-nbipx.h \
+       packet-ncp-int.h \
        packet-netbios.h \
        packet-nfs.h   \
        packet-nlm.h   \
@@ -270,6 +272,7 @@ ETHEREAL_COMMON_SOURCES = \
        ipv4.c         \
        ipv4.h         \
        llcsaps.h      \
+       ncp2222.h       \
        nlpid.h        \
        oui.h          \
        packet.c       \
@@ -286,6 +289,8 @@ ETHEREAL_COMMON_SOURCES = \
        proto.h        \
        ps.c           \
        ps.h           \
+       ptvcursor.c     \
+       ptvcursor.h     \
        register.c     \
        register.h     \
        resolv.c       \
@@ -506,6 +511,7 @@ EXTRA_DIST = \
        Makefile.nmake          \
        make-reg-dotc           \
        manuf                   \
+       ncp2222.py              \
        print.ps                \
        README.aix              \
        README.bsd              \
@@ -564,6 +570,9 @@ editcap.1: doc/editcap.pod
 dfilter-scanner.c : dfilter-scanner.l
        $(LEX) -Pdfilter_ -odfilter-scanner.c $(srcdir)/dfilter-scanner.l
 
+packet-ncp2222.c : ncp2222.py
+       $(PYTHON) $(srcdir)/ncp2222.py > $@
+
 libtool: $(LIBTOOL_DEPS)
        $(SHELL) ./config.status --recheck
 
index db11da20946bd11e26963a26b147c87390881a47..fc7d4004b774f29b5464b628defd213e8ef233ea 100644 (file)
@@ -1,7 +1,7 @@
 ## Makefile for building ethereal.exe with Microsoft C and nmake
 ## Use: nmake -f makefile.nmake
 #
-# $Id: Makefile.nmake,v 1.47 2000/07/27 11:00:48 girlich Exp $
+# $Id: Makefile.nmake,v 1.48 2000/07/28 20:03:40 gram Exp $
 
 include config.nmake
 
@@ -81,6 +81,7 @@ DISSECTOR_SOURCES = \
        packet-nbipx.c \
        packet-nbns.c  \
        packet-ncp.c   \
+       packet-ncp2222.c   \
        packet-netbios.c \
        packet-nfs.c   \
        packet-nlm.c   \
@@ -241,6 +242,9 @@ config.h    : config.h.win32
 ps.c   : rdps.exe print.ps
        rdps print.ps ps.c
 
+packet-ncp2222.c : ncp2222.py
+       $(PYTHON) ncp2222.py > packet-ncp2222.c
+
 dfilter-scanner.obj : dfilter-scanner.c dfilter-grammar.h
 
 dfilter-scanner.c : dfilter-scanner.l
diff --git a/README b/README
index d175175d3b6fe54e44320a79a99671819570a11f..c913edec4d104d1502c68fced02cb855c3c2b805 100644 (file)
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-$Id: README,v 1.38 2000/06/08 03:09:26 gram Exp $
+$Id: README,v 1.39 2000/07/28 20:03:40 gram Exp $
 
 General Information
 ------- -----------
@@ -29,7 +29,7 @@ Installation
 
 Ethereal is known to compile and run on the following systems:
 
-  - Linux (2.0.x, 2.1.x, 2.2.x, 2.3.x)
+  - Linux (2.0.x, 2.1.x, 2.2.x, 2.3.x, 2.4.x)
   - Solaris (2.5.1, 2.6, 7)
   - FreeBSD (2.2.5, 2.2.6, 3.1, 3.2, 3.3)
   - Sequent PTX v4.4.5  (Nick Williams <njw@sequent.com>)
@@ -49,6 +49,10 @@ you need "flex" - it cannot be built with vanilla "lex" -
 and either "bison" or the Berkeley "yacc". Your flex
 version must be 2.5.1 or greater. Check this with 'flex -V'.
 
+If you decide to modify the NetWare Core Protocol dissector, you
+will need python, as the data for packet types is stored in a python
+script, ncp2222.py.
+
 You must therefore install Perl, GNU "make", "flex", and either "bison" or
 Berkeley "yacc" on systems that lack them.
 
@@ -158,6 +162,15 @@ The "Follow TCP Stream" feature only supports TCP over IPv4. Support for TCP
 over IPv6 is planned.
 
 
+NetWare Core Protocol
+---------------------
+There are over 400 different NCP packet types. The NCP dissector does
+not understand all of these; support is being added little by little. If
+you have some NCP packets that are not dissected by Ethereal, send
+a trace file to ethereal-dev@zing.org and if possible, we will add support
+for those packets types.
+
+
 SNMP
 ----
 Ethereal can do some basic decoding of SNMP packets; it can also use an
@@ -214,3 +227,4 @@ Use at your own risk.
 
 Gerald Combs <gerald@zing.org>
 Gilbert Ramirez <gram@xiexie.org>
+Guy Harris <guy@alum.mit.edu>
index 00f7b1ff97e95f24760aadfe3c14e3a4022f250c..67d76c10d10b3d0a5fc0c99bc86432ff10ed6d10 100644 (file)
@@ -11,6 +11,8 @@ LOCAL_CFLAGS=-Zi
 LOCAL_LDFLAGS=/DEBUG
 
 PATH=t:\w32-ix86\cygnus\cygwin-b20\H-i586-cygwin32\bin;$(PATH)
+PERL=perl
+PYTHON=python
 LEX=flex
 YACC=bison
 YACC_OPTS=-S t:\w32-ix86\cygnus\cygwin-b20\share\bison.simple 
index d40a4b5aca73e7b3ff510624cd68c93721d41050..2db9feeea2b1ef1286624c4fb4c335a6fd4bc930 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: configure.in,v 1.98 2000/07/28 16:30:17 gram Exp $
+# $Id: configure.in,v 1.99 2000/07/28 20:03:41 gram Exp $
 dnl
 dnl Process this file with autoconf 2.13 or later to produce a
 dnl configure script; 2.12 doesn't generate a "configure" script that
@@ -26,11 +26,13 @@ AC_PROG_CPP
 AC_PROG_RANLIB
 AC_PROG_YACC
 AM_PROG_LEX
-AC_PATH_PROG(PERL_PATH, perl)
+AC_PATH_PROG(PERL, perl)
 AC_PATH_PROG(LEX, flex)
+AC_PATH_PROG(PYTHON, python)
 
-AC_SUBST(PERL_PATH)
-AC_SUBST(FLEX_PATH)
+AC_SUBST(PERL)
+AC_SUBST(LEX)
+AC_SUBST(PYTHON)
 
 
 # Check for packaging utilities
index b6edea1bc3b086e567180e74d5624f1588cf3d2b..74c1c9e1eebb9a43bfa281d33498dcfca40094a6 100644 (file)
@@ -1,7 +1,7 @@
 # Makefile.am
 # Automake file for Ethereal documentation
 #
-# $Id: Makefile.am,v 1.7 2000/07/28 16:30:28 gram Exp $
+# $Id: Makefile.am,v 1.8 2000/07/28 20:03:59 gram Exp $
 #
 # Ethereal - Network traffic analyzer
 # By Gerald Combs <gerald@zing.org>
@@ -22,8 +22,6 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
-PERL=@PERL_PATH@
-
 ../ethereal.1: ethereal.pod
        pod2man ethereal.pod                     \
        --center="The Ethereal Network Analyzer" \
diff --git a/ncp2222.h b/ncp2222.h
new file mode 100644 (file)
index 0000000..45d42ce
--- /dev/null
+++ b/ncp2222.h
@@ -0,0 +1,261 @@
+/* ncp2222.h
+ * Routines for NetWare Core Protocol
+ * Gilbert Ramirez <gram@xiexie.org>
+ *
+ * $Id: ncp2222.h,v 1.1 2000/07/28 20:03:41 gram Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.org>
+ * 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.
+ */
+
+/* Does NCP func require a subfunction code? */
+static gboolean
+ncp_requires_subfunc(guint8 func)
+{
+       const guint8 *ncp_func_requirement = ncp_func_requires_subfunc;
+
+       while (*ncp_func_requirement != 0) {
+               if (*ncp_func_requirement == func) {
+                       return TRUE;
+               }
+               ncp_func_requirement++;
+       }
+       return FALSE;
+}
+
+/* Return a ncp_record* based on func and possibly subfunc */
+static const ncp_record *
+ncp_record_find(guint8 func, guint8 subfunc)
+{
+       const ncp_record *ncp_rec = ncp_packets;
+
+       while(ncp_rec->func != 0 || ncp_rec->subfunc != 0 ||
+               ncp_rec->name != NULL ) {
+               if (ncp_rec->func == func &&
+                       ncp_rec->subfunc == (subfunc & ncp_rec->submask)) {
+                       return ncp_rec;
+               }
+               ncp_rec++;
+       }
+       return NULL;
+}
+
+/* Run through the table of ptv_record's and add info to the tree */
+static void
+process_ptvc_record(ptvcursor_t *ptvc, const ptvc_record *rec)
+{
+       while(rec->hf_ptr != NULL) {
+               ptvcursor_add(ptvc, *rec->hf_ptr, rec->length,
+                               rec->endianness);
+               rec++;
+       }
+}
+
+
+/* Given an error_equivalency table and a completion code, return
+ * the string representing the error. */
+static const char*
+ncp_error_string(const error_equivalency *errors, guint8 completion_code)
+{
+       while (errors->ncp_error_index != -1) {
+               if (errors->error_in_packet == completion_code) {
+                       return ncp_errors[errors->ncp_error_index];
+               }
+               errors++;
+       }
+
+       return "Unknown";
+}
+
+void
+dissect_ncp_request(tvbuff_t *tvb, packet_info *pinfo,
+               guint16 nw_connection, guint8 sequence,
+               guint16 type, proto_tree *ncp_tree, proto_tree *tree)
+{
+       guint8                  func, subfunc = 0;
+       gboolean                requires_subfunc;
+       const ncp_record        *ncp_rec;
+       conversation_t          *conversation;
+       ptvcursor_t             *ptvc = NULL;
+
+       func = tvb_get_guint8(tvb, 6);
+
+       requires_subfunc = ncp_requires_subfunc(func);
+       if (requires_subfunc) {
+               subfunc = tvb_get_guint8(tvb, 9);
+       }
+
+       ncp_rec = ncp_record_find(func, subfunc);
+
+       if (check_col(pinfo->fd, COL_INFO)) {
+               if (ncp_rec) {
+                       col_add_fstr(pinfo->fd, COL_INFO, "C %s", ncp_rec->name);
+               }
+               else {
+                       if (requires_subfunc) {
+                               col_add_fstr(pinfo->fd, COL_INFO,
+                                               "C Unknown Function 0x%02X/0x%02x",
+                                               func, subfunc);
+                       }
+                       else {
+                               col_add_fstr(pinfo->fd, COL_INFO,
+                                               "C Unknown Function 0x%02x",
+                                               func);
+                       }
+               }
+       }
+
+       if (!pinfo->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);
+               }
+               ncp_hash_insert(conversation, sequence, 0x2222, ncp_rec);
+       }
+
+       if (ncp_tree) {
+               proto_tree_add_uint_format(ncp_tree, hf_ncp_func, tvb, 6, 1,
+                       func, "Function Code: 0x%02X (%s)",
+                       func, ncp_rec ? ncp_rec->name : "Unknown");
+
+               if (requires_subfunc) {
+                       proto_tree_add_item(ncp_tree, hf_ncp_length, tvb, 7,
+                                       2, FALSE);
+                       proto_tree_add_item(ncp_tree, hf_ncp_subfunc, tvb, 9,
+                                       1, FALSE);
+                       ptvc = ptvcursor_new(ncp_tree, tvb, 10);
+               }
+               else {
+                       ptvc = ptvcursor_new(ncp_tree, tvb, 7);
+               }
+
+               /* The group is not part of the packet, but it's useful
+                * information to display anyway. */
+               if (ncp_rec) {
+                       proto_tree_add_text(ncp_tree, tvb, 6, 1, "Group: %s",
+                                       ncp_groups[ncp_rec->group]);
+               }
+
+               if (ncp_rec && ncp_rec->request_ptvc) {
+                       process_ptvc_record(ptvc, ncp_rec->request_ptvc);
+               }
+               ptvcursor_free(ptvc);
+       }
+}
+
+
+void
+dissect_ncp_reply(tvbuff_t *tvb, packet_info *pinfo,
+       guint16 nw_connection, guint8 sequence,
+       proto_tree *ncp_tree, proto_tree *tree) {
+
+       conversation_t                  *conversation;
+       const ncp_record                *ncp_rec = NULL;
+       guint16                         ncp_type;
+       gboolean                        found_request = FALSE;
+       guint8                          completion_code;
+       guint                           length;
+       ptvcursor_t                     *ptvc = NULL;
+       const char                      *error_string;
+
+       /* 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 */
+               found_request = ncp_hash_lookup(conversation, sequence,
+                               &ncp_type, &ncp_rec);
+       }
+       /* else... we haven't seen an NCP Request for that conversation and sequence. */
+
+       /* A completion code of 0 always means OK. Non-zero means failure,
+        * but each non-zero value has a different meaning. And the same value
+        * can have different meanings, depending on the ncp.func (and ncp.subfunc)
+        * value. */
+       completion_code = tvb_get_guint8(tvb, 6);
+       if (ncp_rec && ncp_rec->errors) {
+               error_string = ncp_error_string(ncp_rec->errors, completion_code);
+       }
+       else if (completion_code == 0) {
+               error_string = "OK";
+       }
+       else {
+               error_string = "Not OK";
+       }
+
+       if (check_col(pinfo->fd, COL_INFO)) {
+               col_add_fstr(pinfo->fd, COL_INFO, "R %s", error_string);
+       }
+
+       if (ncp_tree) {
+
+               /* Put the func (and maybe subfunc) from the request packet
+                * in the proto tree, but hidden. That way filters on ncp.func
+                * or ncp.subfunc will find both the requests and the replies.
+                */
+               if (ncp_rec) {
+                       proto_tree_add_uint_hidden(ncp_tree, hf_ncp_func, tvb,
+                                       6, 1, ncp_rec->func);
+                       if (ncp_requires_subfunc(ncp_rec->func)) {
+                               proto_tree_add_uint_hidden(ncp_tree, hf_ncp_subfunc,
+                                               tvb, 6, 1, ncp_rec->subfunc);
+                       }
+               }
+
+               proto_tree_add_uint_format(ncp_tree, hf_ncp_completion_code, tvb, 6, 1,
+                       completion_code, "Completion Code: 0x%02x (%s)",
+                       completion_code, error_string);
+
+               proto_tree_add_item(ncp_tree, hf_ncp_connection_status, tvb, 7, 1, FALSE);
+
+               length = tvb_length(tvb);
+               if (!ncp_rec && length > 8) {
+                       proto_tree_add_text(ncp_tree, tvb, 8, length - 8,
+                                       "No request record found. Parsing is impossible.");
+               }
+               else if (ncp_rec && ncp_rec->reply_ptvc) {
+                       /* If a non-zero completion code was found, it is
+                        * legal to not have any fields, even if the packet
+                        * type is defined as having fields. */
+                       if (completion_code != 0 && tvb_length(tvb) == 8) {
+                               return;
+                       }
+
+                       if (ncp_requires_subfunc(ncp_rec->func)) {
+                               ptvc = ptvcursor_new(ncp_tree, tvb, 9);
+                       }
+                       else {
+                               ptvc = ptvcursor_new(ncp_tree, tvb, 8);
+                       }
+                       process_ptvc_record(ptvc, ncp_rec->reply_ptvc);
+                       ptvcursor_free(ptvc);
+               }
+       }
+}
diff --git a/ncp2222.py b/ncp2222.py
new file mode 100755 (executable)
index 0000000..5464419
--- /dev/null
@@ -0,0 +1,1406 @@
+#!/usr/bin/python
+#
+# ncp2222.py
+#
+# Creates C code from a table of NCP type 0x2222 packet types.
+# (And 0x3333, which are the replies, but the packets are more commonly
+# refered to as type 0x2222; the 0x3333 replies are understood to be
+# part of the 0x2222 "family")
+#
+# Data comes from "Programmer's Guide to the NetWare Core Protocol"
+# by Steve Conner and Dianne Conner.
+#
+# $Id: ncp2222.py,v 1.1 2000/07/28 20:03:42 gram Exp $
+#
+# Copyright (c) 2000 by Gilbert Ramirez <gram@xiexie.org>
+#
+# 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.
+
+import sys
+
+##############################################################################
+# Global containers
+##############################################################################
+
+# The UniqueCollection class stores objects which can be compared to other
+# objects of the same class. If two objects in the collection are equivalent,
+# only one is stored.
+class UniqueCollection:
+
+       # Constructor
+       def __init__(self, name):
+               self.name = name
+               self.members = []
+
+       # Add an object to the members lists, if a comparable object
+       # doesn't already exist. The object that is in the member list, that is
+       # either the object that was added or the comparable object that was
+       # already in the member list, is returned.
+       def Add(self, object):
+               # Is 'object' a duplicate of some other member?
+               for member in self.members:
+                       if member == object:
+                               return member
+
+               # Store object in our members list.
+               self.members.append(object)
+               return object
+
+       # Returns the list of members.
+       def Members(self):
+               return self.members
+
+       # Does the list of members contain the object?
+       def HasMember(self, object):
+               for member in self.members:
+                       if member == object:
+                               return 1
+               return 0
+
+
+packets                = UniqueCollection('NCP Packet Descriptions')
+compcode_lists = UniqueCollection('Completion Code Lists')
+ptvc_lists     = UniqueCollection('PTVC Lists')
+
+
+##############################################################################
+# NamedList's keep track of PTVC's and Completion Codes
+##############################################################################
+class NamedList:
+       # Constructor
+       def __init__(self, name, list):
+               self.name = name
+               self.list = list
+
+       # Compare this NamedList to another
+       def __cmp__(self, other):
+               # Python will do a deep comparison of lists within lists.
+               if self.list < other.list:
+                       return -1
+               elif self.list > other.list:
+                       return 1
+               else:
+                       return 0
+
+       # String representation
+       def __repr__(self):
+               return "NamedList: " + `self.list`
+
+       # Get/Set name of list
+       def Name(self, new_name = None):
+               if new_name != None:
+                       self.name = new_name
+               return self.name
+
+       # Returns record lists
+       def Records(self):
+               return self.list
+
+       # Is there no list (different from an empty list)?
+       def Null(self):
+               return self.list == None
+
+       # It the list empty (different from a null list)?
+       def Empty(self):
+               assert(not self.Null())
+
+               if self.list:
+                       return 0
+               else:
+                       return 1
+
+
+# ProtoTree TVBuff Cursor List ("PTVC List") Class
+class PTVC(NamedList):
+       # Constructor
+       def __init__(self, name, records):
+               self.list = []
+               NamedList.__init__(self, name, self.list)
+
+               # Make a PTVCRecord object for each list in 'records'
+               for record in records:
+                       ptvc_rec = PTVCRecord(record)
+       
+                       # We can't make a PTVC list from a variable-length
+                       # packet. XXX - unless it's FT_NSTRING
+#                      if type(ptvc_rec.Length()) == type(()):
+#                              if ptvc_rec.Field() == nstring8:
+#                                      pass
+#                              else:
+#                                      self.list = None
+#                                      return
+
+                       self.list.append(ptvc_rec)
+
+class PTVCRecord:
+       # Constructor
+       def __init__(self, record):
+               self.length     = record[1]
+               self.field      = record[2]
+
+               # Small sanity check
+               field_length = self.field.Length()
+
+               if type(field_length) == type(0) and field_length > 0:
+                       if field_length != self.length:
+                               sys.stderr.write("Length %d does not match field length %d for field %s\n" % (self.length, field_length, self.field.Abbreviation()))
+                               sys.exit(1)
+
+               # Check if an endianness override is given
+               try:
+                       self.endianness = record[3]
+
+               # If no endianness was given in the record, then
+               # use the field's default endianness.
+               except IndexError:
+                       self.endianness = self.field.Endianness()
+
+       # Comparison operator
+       def __cmp__(self, other):
+               if self.length < other.length:
+                       return -1
+               elif self.length > other.length:
+                       return 1
+
+               if self.field != other.field:
+                       return 1
+               elif self.endianness != other.endianness:
+                       return 1
+               else:
+                       return 0
+
+       # String representation
+       def __repr__(self):
+               endianness = 'FALSE'
+               if self.endianness == LE:
+                       endianness = 'TRUE'
+
+               if type(self.length) == type(0):
+                       length = self.length
+                       return "{ &%s, %d, %s }" % (self.field.HFName(),
+                                       length, endianness)
+               else:
+                       length = "PTVC_VARIABLE_LENGTH"
+                       return "{ &%s, %s, %s }" % (self.field.HFName(),
+                                       length, endianness)
+
+
+       def Length(self):
+               return self.length
+
+       def Field(self):
+               return self.field
+
+
+##############################################################################
+# NCP Packet class
+##############################################################################
+class NCP:
+       # Constructor
+       def __init__(self, func_code, description, group):
+               self.func_code          = func_code
+               self.description        = description
+               self.group              = group
+               self.codes              = None
+               self.request_records    = None
+               self.reply_records      = None
+
+               if not groups.has_key(group):
+                       sys.stderr.write("NCP 0x%x has invalid group '%s'\n" % (self.func_code, group))
+                       sys.exit(1)
+
+               if self.HasSubFunction():
+                       # NCP Function with SubFunction
+                       self.start_offset = 10
+               else:
+                       # Simple NCP Function
+                       self.start_offset = 7
+
+       # Returns the function code for this NCP packet.
+       def FunctionCode(self, part=None):
+               if part == None:
+                       return self.func_code
+               elif part == 'high':
+                       if self.HasSubFunction():
+                               return (self.func_code & 0xff00) >> 8
+                       else:
+                               return self.func_code
+               elif part == 'low':
+                       if self.HasSubFunction():
+                               return self.func_code & 0x00ff
+                       else:
+                               return 0x00
+               else:
+                       sys.stderr.write("Unknown directive '%s' for function_code()\n" % (part))
+                       sys.exit(1)
+
+       # Does this NPC packet require a subfunction field?
+       def HasSubFunction(self):
+               if self.func_code <= 0xff:
+                       return 0
+               else:
+                       return 1
+
+       def Description(self):
+               return self.description
+
+       def Group(self):
+               return self.group
+
+       def PTVCRequest(self):
+               return self.ptvc_request
+
+       def PTVCReply(self):
+               return self.ptvc_reply
+
+       def Request(self, size, records=[]):
+               self.request_size = size
+               self.request_records = records
+               if self.HasSubFunction():
+                       self.CheckRecords(size, records, "Request", 10)
+               else:
+                       self.CheckRecords(size, records, "Request", 7)
+               self.ptvc_request = self.MakePTVC(records, "request")
+
+       def Reply(self, size, records=[]):
+               self.reply_size = size
+               self.reply_records = records
+               self.CheckRecords(size, records, "Reply", 8)
+               self.ptvc_reply = self.MakePTVC(records, "reply")
+
+       # Simple sanity check
+       def CheckRecords(self, size, records, descr, min_hdr_length):
+               min = size
+               max = size
+               if type(size) == type(()):
+                       min = size[0]
+                       max = size[1]
+
+               lower = min_hdr_length
+               upper = min_hdr_length
+
+               for record in records:
+                       rec_size = record[1]
+                       rec_lower = rec_size
+                       rec_upper = rec_size
+                       if type(rec_size) == type(()):
+                               rec_lower = rec_size[0]
+                               rec_upper = rec_size[1]
+
+                       lower = lower + rec_lower
+                       upper = upper + rec_upper
+
+               error = 0
+               if min != lower:
+                       sys.stderr.write("%s records for 2222/0x%x sum to %d bytes minimum, but param1 shows %d\n" \
+                               % (descr, self.FunctionCode(), lower, min))
+                       error = 1
+               if max != upper:
+                       sys.stderr.write("%s records for 2222/0x%x sum to %d bytes maximum, but param1 shows %d\n" \
+                               % (descr, self.FunctionCode(), upper, max))
+                       error = 1
+
+               if error == 1:
+                       sys.exit(1)
+
+
+       # Makes a PTVC out of a request or reply record list. Possibly adds
+       # it to the global list of PTVCs (the global list is a UniqueCollection,
+       # so an equivalent PTVC may already be in the global list).
+       def MakePTVC(self, records, name_suffix):
+               name = "%s_%s" % (self.CName(), name_suffix)
+               ptvc = PTVC(name, records)
+               return ptvc_lists.Add(ptvc)
+
+       # Returns a C symbol based on the NCP function code
+       def CName(self):
+               return "ncp_0x%x" % (self.func_code)
+
+       # Returns a list of variables used in the request and reply records.
+       # A variable is listed only once, even if it is used twice (once in
+       # the request, once in the reply).
+       def Variables(self):
+               variables = {}
+               if self.request_records:
+                       for record in self.request_records:
+                               var = record[2]
+                               variables[var] = 1
+
+               if self.reply_records:
+                       for record in self.reply_records:
+                               var = record[2]
+                               variables[var] = 1
+
+               return variables.keys()
+
+
+       # Sets or returns the list of completion codes. Internally, a NamedList
+       # is used to store the completion codes, but the caller of this function
+       # never realizes that because Python lists are the input and output.
+       def CompletionCodes(self, codes=None):
+               if codes == None:
+                       return self.codes
+
+               # Sanity check
+               okay = 1
+               for code in codes:
+                       if not errors.has_key(code):
+                               sys.stderr.write("Errors table does not have key 0x%04x for NCP=0x%x\n" % (code,
+                                       self.func_code))
+                               okay = 0
+
+               # Delay the exit until here so that the programmer can get the complete
+               # list of missing error codes
+               if not okay:
+                       sys.exit(1)
+
+               # Create CompletionCode (NamedList) object and possible add it to
+               # the global list of completion code lists.
+               name = "%s_errors" % (self.CName())
+               codes.sort()
+               codes_list = NamedList(name, codes)
+               self.codes = compcode_lists.Add(codes_list)
+
+               self.Finalize()
+
+       # Adds the NCP object to the global collection of NCP objects. This
+       # is done automatically after setting the CompletionCode list. Yes, this
+       # is a shortcut, but it makes our list of NCP packet definitions look
+       # neater, since an explicit "add to global list of packets" is not needed.
+       def Finalize(self):
+               # Add packet to global collection of packets
+               if packets.HasMember(self):
+                       sys.stderr.write("Already have NCP Function Code 0x%x\n" % \
+                               (self.func_code))
+                       sys.exit(1)
+               else:
+                       packets.Add(self)
+
+
+
+##############################################################################
+# Classes for NCP field types
+##############################################################################
+LE             = 1             # Little-Endian
+BE             = 0             # Big-Endian
+NA             = -1            # Not Applicable
+
+class Type:
+       type            = "Type"
+       ftype           = None
+       disp            = "BASE_DEC"
+       endianness      = NA
+       values          = []
+
+       def __init__(self, abbrev, descr, bytes, endianness = NA):
+               self.abbrev = abbrev
+               self.descr = descr
+               self.bytes = bytes
+
+       def Length(self):
+               return self.bytes
+
+       def Abbreviation(self):
+               return self.abbrev
+
+       def Description(self):
+               return self.descr
+
+       def HFName(self):
+               return "hf_ncp_" + self.abbrev
+
+       def DFilter(self):
+               return "ncp." + self.abbrev
+
+       def EtherealFType(self):
+               return self.ftype
+
+       def Display(self, newval=None):
+               if newval != None:
+                       self.disp = newval
+               return self.disp
+
+       def ValuesName(self):
+               return "NULL"
+
+       def Mask(self):
+               return 0
+
+       def Endianness(self):
+               return self.endianness
+
+class byte(Type):
+       type    = "byte"
+       ftype   = "FT_UINT8"
+       def __init__(self, abbrev, descr):
+               Type.__init__(self, abbrev, descr, 1)
+
+# Same as above. Both are provided for convenience
+class uint8(Type):
+       type    = "uint8"
+       ftype   = "FT_UINT8"
+       def __init__(self, abbrev, descr):
+               Type.__init__(self, abbrev, descr, 1)
+
+class uint16(Type):
+       type    = "uint16"
+       ftype   = "FT_UINT16"
+       def __init__(self, abbrev, descr, endianness = BE):
+               Type.__init__(self, abbrev, descr, 2, endianness)
+
+class uint32(Type):
+       type    = "uint32"
+       ftype   = "FT_UINT32"
+       def __init__(self, abbrev, descr, endianness = BE):
+               Type.__init__(self, abbrev, descr, 4, endianness)
+
+# A string of up to 255 characters. The first byte
+# gives the string length. Thus, the total length of
+# this data structure is from 1 to 256 bytes, including
+# the first byte.
+class nstring8(Type):
+       type    = "nstring8"
+       ftype   = "FT_NSTRING_UINT8"
+       def __init__(self, abbrev, descr):
+               Type.__init__(self, abbrev, descr, -1)
+
+# NUL-terminated string.
+class stringz(Type):
+       type    = "stringz"
+       ftype   = "FT_STRING"
+       def __init__(self, abbrev, descr):
+               Type.__init__(self, abbrev, descr, -1)
+
+# Abstract class for val_stringN, where N is number
+# of bits that key takes up
+class val_string(Type):
+       type    = "val_string"
+       disp    = 'BASE_HEX'
+
+       def __init__(self, abbrev, descr, val_string_array, endianness = BE):
+               Type.__init__(self, abbrev, descr, self.bytes, endianness)
+               self.values = val_string_array
+
+       def __repr__(self):
+               result = "static const value_string %s[] = {\n" \
+                               % (self.ValuesCName())
+               for val_record in self.values:
+                       value   = val_record[0]
+                       text    = val_record[1]
+                       value_repr = self.value_format % value
+                       result = result + '\t{ %s,\t"%s" },\n' \
+                                       % (value_repr, text)
+
+               value_repr = self.value_format % 0
+               result = result + "\t{ %s,\tNULL },\n" % (value_repr)
+               result = result + "};\n"
+
+               return result
+
+       def ValuesCName(self):
+               return "ncp_%s_vals" % (self.abbrev)
+
+       def ValuesName(self):
+               return "VALS(%s)" % (self.ValuesCName())
+
+class val_string8(val_string):
+       type            = "val_string8"
+       ftype           = "FT_UINT8"
+       bytes           = 1
+       value_format    = "0x%02x"
+
+class val_string16(val_string):
+       type            = "val_string16"
+       ftype           = "FT_UINT16"
+       bytes           = 2
+       value_format    = "0x%04x"
+
+class bytes(Type):
+       type    = 'bytes'
+       ftype   = 'FT_BYTES'
+
+       def __init__(self, abbrev, descr, bytes):
+               Type.__init__(self, abbrev, descr, bytes, NA)
+
+#class data(Type):
+#      type    = "data"
+#      ftype   = "FT_BYTES"
+#      def __init__(self, abbrev, descr):
+#              Type.__init__(self, abbrev, descr, -1)
+#
+#      def length_var(self, length_var):
+#              self.length_var = length_var
+
+##############################################################################
+# NCP Field Types. Defined in Appendix A of "Programmer's Guide..."
+##############################################################################
+BufferSize     = uint16("buffer_size", "Buffer Size")
+ConnectionNumber       = uint32("connection_number", "Connection Number")
+DirHandle      = byte("dir_handle", "Directory Handle")
+
+FileHandle     = bytes("file_handle", "File Handle", 6)
+
+FileLock       = val_string8("file_lock", "File Lock", [
+       [ 0x00, "Not Locked" ],
+       [ 0xfe, "Locked by file lock" ],
+       [ 0xff, "Unknown" ],
+])
+
+FileOffset     = uint32("file_offset", "File Offset")
+FilePath       = nstring8("file_path", "File Path")
+FileSize       = uint32("file_size", "File Size")
+JobType                = uint16("job_type", "Job Type")
+
+LogicalLockType        = val_string8("logical_lock_type", "Logical Lock Type", [
+       [ 0x00, "Log file" ],
+       [ 0x01, "Log and lock file for exclusive read/write use" ],
+       [ 0x03, "Log and lock with shareable read-only use" ],
+])
+
+LogicalRecordName      = nstring8("logical_record_name", "Logical Record Name")
+LogLockType    = byte("log_lock_type", "Log Lock Type")
+
+MaxBytes       = uint16("max_bytes", "Maximum Number of Bytes")
+NumBytes       = uint16("num_bytes", "Number of Bytes")
+
+ObjectFlags    = val_string8("object_flags", "Object Flags", [
+       [ 0x00, "Dynamic object" ],
+       [ 0x01, "Static object" ],
+])
+
+ObjectHasProperties = val_string8("object_has_properites", "Object Has Properties", [
+       [ 0x00, "No properties" ],
+       [ 0xff, "One or more properties" ],
+])
+
+ObjectID       = uint32("object_id", "Object ID")
+ObjectID.Display('BASE_HEX')
+
+ObjectName     = nstring8("object_name", "Object Name")
+ObjectNameZ    = stringz("object_nameZ", "Object Name")
+
+ObjectSecurity = val_string8("object_security", "Object Security", [
+       [ 0x00, "Anyone can read or modify the object" ],
+       [ 0x01, "Client logged into the file server can read the object" ],
+       [ 0x02, "Client logged into the file server with the object's name, type and password can read the object" ],
+       [ 0x03, "Client with supervisor equivalence can read the object" ],
+       [ 0x04, "Only the operating system can read the object" ],
+       [ 0x10, "Client logged into the file server can modify the object" ],
+       [ 0x20, "Client logged into the file server with the object's name, type and password can modify the object" ],
+       [ 0x30, "Client with supervisor equivalence can modify the object" ],
+       [ 0x40, "Only the operating system can modify the object" ],
+])
+
+ObjectType     = val_string16("object_type", "Object Type", [
+       [ 0x0000,       "Unknown" ],
+       [ 0x0001,       "User" ],
+       [ 0x0002,       "User group" ],
+       [ 0x0003,       "Print queue" ],
+       [ 0x0004,       "NetWare file server" ],
+       [ 0x0005,       "Job server" ],
+       [ 0x0006,       "Gateway" ],
+       [ 0x0007,       "Print server" ],
+       [ 0x0008,       "Archive queue" ],
+       [ 0x0009,       "Archive server" ],
+       [ 0x000a,       "Job queue" ],
+       [ 0x000b,       "Administration" ],
+       [ 0x0021,       "NAS SNA gateway" ],
+       [ 0x0026,       "Remote bridge server" ],
+       [ 0x0027,       "TCP/IP gateway" ],
+])
+
+PropertyHasMoreSegments        = val_string8("property_has_more_segments",
+       "Property Has More Segments", [
+       [ 0x00, "Is last segment" ],
+       [ 0xff, "More segments are available" ],
+])
+
+PropertyName   = nstring8("property_name", "Property Name")
+PropertyData   = bytes("property_data", "Property Data", 128)
+PropertySegment        = uint8("property_segment", "Property Segment")
+
+PropertyType   = val_string8("property_type", "Property Type", [
+       [ 0x00, "Static item" ],
+       [ 0x01, "Dynamic item" ],
+       [ 0x02, "Static set" ],
+       [ 0x03, "Dynamic set" ],
+])
+
+TaskNumber     = uint32("task_number", "Task Number")
+TimeoutLimit   = uint16("timeout_limit", "Timeout Limit")
+UnknownByte    = byte("unknown_byte", "Unknown Byte")
+
+
+##############################################################################
+# NCP Groups
+##############################################################################
+groups = {}
+groups['accounting']   = "Accounting"
+groups['afp']          = "AFP"
+groups['auditing']     = "Auditing"
+groups['bindery']      = "Bindery"
+groups['connection']   = "Connection"
+groups['directory']    = "Directory"
+groups['extended']     = "Extended Attribute"
+groups['file']         = "File"
+groups['fileserver']   = "File Server"
+groups['message']      = "Message"
+groups['migration']    = "Data Migration"
+groups['misc']         = "Miscellaneous"
+groups['name']         = "Name Space"
+groups['nds']          = "NetWare Directory"
+groups['print']                = "Print"
+groups['queue']                = "Queue"
+groups['sync']         = "Synchronization"
+groups['tss']          = "Transaction Tracking"
+
+##############################################################################
+# NCP Errors
+##############################################################################
+errors = {}
+errors[0x0000] = "Ok"
+errors[0x0001] = "Transaction tracking is available"
+errors[0x0002] = "Ok. The data has been written"
+
+errors[0x0100] = "One or more of the ConnectionNumbers in the send list are invalid"
+errors[0x0101] = "Invalid space limit"
+errors[0x0102] = "Insufficient disk space"
+errors[0x0103] = "Queue server cannot add jobs"
+errors[0x0104] = "Out of disk space"
+errors[0x0105] = "Semaphore overflow"
+
+errors[0x0200] = "One or more clients in the send list are not logged in"
+errors[0x0201] = "Queue server cannot attach"
+
+errors[0x0300] = "One or more clients in the send list are not accepting messages"
+
+errors[0x0400] = "Client already has message"
+errors[0x0401] = "Queue server cannot service job"
+
+errors[0x7e00] = "NCP failed boundary check"
+
+errors[0x8000] = "Lock fail"
+errors[0x8100] = "A file handle could not be allocated by the file server"
+errors[0x8200] = "Unauthorized to open the file"
+errors[0x8300] = "Unable to read/write the volume. Possible bad sector on the file server"
+
+errors[0x8400] = "Unauthorized to create the directory"
+errors[0x8401] = "Unauthorized to create the file"
+
+errors[0x8500] = "Unauthorized to delete the specified file"
+errors[0x8501] = "Unauthorized to overwrite an existing file in this directory"
+
+errors[0x8700] = "An unexpected character was encountered in the filename"
+errors[0x8800] = "Invalid file handle"
+errors[0x8900] = "Unauthorized to search this directory"
+errors[0x8a00] = "Unauthorized to delete this directory"
+errors[0x8b00] = "Unauthorized to rename a file in this directory"
+
+errors[0x8c00] = "No set privileges"
+errors[0x8c01] = "Unauthorized to modify a file in this directory"
+errors[0x8c02] = "Unauthorized to change the restriction on this volume"
+
+errors[0x8d00] = "Some of the affected files are in use by another client"
+errors[0x8d01] = "The affected file is in use"
+
+errors[0x8e00] = "All of the affected files are in use by another client"
+errors[0x8f00] = "Some of the affected files are read-only"
+
+errors[0x9000] = "An attempt to modify a read-only volume occurred"
+errors[0x9001] = "All of the affected files are read-only"
+
+errors[0x9100] = "Some of the affected files already exist"
+
+errors[0x9200] = "Directory with the new name already exists"
+errors[0x9201] = "All of the affected files already exist"
+
+errors[0x9300] = "Unauthorized to read from this file"
+errors[0x9400] = "Unauthorized to write to this file"
+errors[0x9500] = "The affected file is detached"
+
+errors[0x9600] = "The file server has run out of memory to service this request"
+errors[0x9601] = "No alloc space for message"
+
+errors[0x9800] = "The affected volume is not mounted"
+errors[0x9801] = "The volume associated with VolumeNumber is not mounted"
+errors[0x9802] = "The resulting voume does not exist"
+errors[0x9803] = "The destination volume is not mounted"
+
+errors[0x9900] = "The file server has run out of directory space on the affected volume"
+errors[0x9a00] = "The request attempted to rename the affected file to another volume"
+
+errors[0x9b00] = "DirHandle is not associated with a valid directory path"
+errors[0x9b01] = "A resulting directory handle is not associated with a valid directory path"
+errors[0x9b02] = "The directory associated with DirHandle does not exist"
+errors[0x9b03] = "Bad directory handle"
+
+errors[0x9c00] = "The resulting path is not valid"
+errors[0x9c01] = "The resulting file path is not valid"
+errors[0x9c02] = "The resulting directory path is not valid"
+errors[0x9c03] = "Invalid path"
+
+errors[0x9d00] = "A directory handle was not available for allocation"
+
+errors[0x9e00] = "The name of the directory does not conform to a legal name for this name space"
+errors[0x9e01] = "The new directory name does not conform to a legal name for this name space"
+
+errors[0x9f00] = "The request attempted to delete a directory that is in use by another client"
+
+errors[0xa000] = "The request attempted to delete a directory that is not empty"
+errors[0xa100] = "An unrecoverable error occured on the affected directory"
+errors[0xa200] = "The request attempted to read from a file region that is physically locked"
+errors[0xa400] = "Invalid directory rename attempted"
+
+errors[0xbf00] = "Requests for this name space are not valid on this volume"
+
+errors[0xc000] = "Unauthorized to retrieve accounting data"
+errors[0xc100] = "The ACCOUNT_BALANCE property does not exist"
+errors[0xc200] = "The object has exceeded its credit limit"
+errors[0xc300] = "Too many holds have been placed against this account"
+errors[0xc400] = "The client account has been disabled"
+
+errors[0xc500] = "Access to the account has been denied because of intruder detection"
+errors[0xc501] = "Login lockout"
+
+errors[0xc600] = "The caller does not have operator priviliges"
+errors[0xc601] = "The client does not have operator priviliges"
+
+errors[0xd000] = "Queue error"
+errors[0xd100] = "The queue does not exist"
+
+errors[0xd200] = "A queue server is not associated with this queue"
+errors[0xd201] = "A queue server is not associated with the selected queue"
+errors[0xd202] = "No queue server"
+
+errors[0xd300] = "No queue rights"
+
+errors[0xd400] = "The queue is full and cannot accept another request"
+errors[0xd401] = "The queue associated with ObjectId is full and cannot accept another request"
+
+errors[0xd500] = "A job does not exist in this queue"
+errors[0xd501] = "No queue job"
+errors[0xd502] = "The job associated with JobNumber does not exist in this queue"
+
+errors[0xd600] = "The file server does not allow unencrypted passwords"
+errors[0xd601] = "No job right"
+
+errors[0xd700] = "Bad account"
+errors[0xd701] = "The old and new password strings are identical"
+errors[0xd702] = "The job is currently being serviced"
+errors[0xd703] = "The queue is currently servicing a job"
+errors[0xd704] = "Queue servicing"
+
+errors[0xd800] = "Queue not active"
+
+errors[0xd900] = "The file server cannot accept another connection as it has reached its limit"
+errors[0xd901] = "The client is not security equivalent to one of the objects in the Q_SERVERS group property of the target queue"
+errors[0xd902] = "Station is not a server"
+
+errors[0xda00] = "Attempted to login to the file server during a restricted time period"
+errors[0xda01] = "Queue halted"
+
+errors[0xdb00] = "Attempted to login to the file server from an unauthorized workstation or network"
+errors[0xdb01] = "The queue cannot attach another queue server"
+errors[0xdb02] = "Maximum queue servers"
+
+errors[0xde00] = "Attempted to login to the file server with an incorrect password"
+errors[0xdf00] = "Attempted to login to the file server with a password that has expired"
+
+errors[0xe700] = "No disk track"
+errors[0xe800] = "Write to group"
+errors[0xe900] = "The object is already a member of the group property"
+
+errors[0xea00] = "No such member"
+errors[0xea01] = "The bindery object is not a member of the set"
+errors[0xea02] = "Non-existent member"
+
+errors[0xeb00] = "The property is not a set property"
+
+errors[0xec00] = "No such set"
+errors[0xec01] = "The set property does not exist"
+
+errors[0xed00] = "Property exists"
+errors[0xed01] = "The property already exists"
+errors[0xed02] = "An attempt was made to create a bindery object property that already exists"
+
+errors[0xee00] = "The object already exists"
+errors[0xee01] = "The bindery object already exists"
+
+errors[0xef00] = "Illegal name"
+errors[0xef01] = "Illegal characters in ObjectName field"
+errors[0xef02] = "Invalid name"
+
+errors[0xf000] = "A wildcard was detected in a field that does not support wildcards"
+errors[0xf001] = "An illegal wildcard was detected in ObjectName"
+
+errors[0xf100] = "The client does not have the rights to access this bindery object"
+errors[0xf101] = "Bindery security"
+errors[0xf102] = "Invalid bindery security"
+
+errors[0xf200] = "Unauthorized to read from this object"
+errors[0xf300] = "Unauthorized to rename this object"
+
+errors[0xf400] = "Unauthorized to delete this object"
+errors[0xf401] = "No object delete privileges"
+errors[0xf402] = "Unauthorized to delete this queue"
+
+errors[0xf500] = "Unauthorized to create this object"
+errors[0xf501] = "No object create"
+
+errors[0xf600] = "No property delete"
+errors[0xf601] = "Unauthorized to delete the property of this object"
+errors[0xf602] = "Unauthorized to delete this property"
+
+errors[0xf700] = "Unauthorized to create this property"
+errors[0xf701] = "No property create privilege"
+
+errors[0xf800] = "Unauthorized to write to this property"
+errors[0xf900] = "Unauthorized to read this property"
+errors[0xfa00] = "Temporary remap error"
+
+errors[0xfb00] = "No such property"
+errors[0xfb01] = "The file server does not support this request"
+errors[0xfb02] = "The specified property does not exist"
+errors[0xfb03] = "The PASSWORD property does not exist for this bindery object"
+
+errors[0xfc00] = "The message queue cannot accept another message"
+errors[0xfc01] = "The trustee associated with ObjectId does not exist"
+errors[0xfc02] = "The specified bindery object does not exist"
+errors[0xfc03] = "The bindery object associated with ObjectID does not exist"
+errors[0xfc04] = "A bindery object does not exist that matches"
+errors[0xfc05] = "The specified queue does not exist"
+errors[0xfc06] = "No such object"
+errors[0xfc07] = "The queue associated with ObjectID does not exist"
+
+errors[0xfd00] = "Bad station number"
+errors[0xfd01] = "The connection associated with ConnectionNumber is not active"
+errors[0xfd02] = "Lock collision"
+errors[0xfd03] = "Transacktion tracking is disabled"
+
+errors[0xfe00] = "I/O failure"
+errors[0xfe01] = "The files containing the bindery on the file server are locked"
+errors[0xfe02] = "A file with the specified name already exists in this directory"
+errors[0xfe03] = "No more restrictions were found"
+errors[0xfe04] = "The file server was unable to lock the file within the specified time limit"
+errors[0xfe05] = "The file server was unable to lock all files within the specified time limit"
+errors[0xfe06] = "The bindery object associated with ObjectID is not a valid trustee"
+errors[0xfe07] = "Directory locked"
+errors[0xfe08] = "Bindery locked"
+errors[0xfe09] = "Invalid semaphore name length"
+errors[0xfe0a] = "The file server was unable to complete the operation within the specified time limit"
+errors[0xfe0b] = "Transaction restart"
+
+errors[0xff00] = "Failure"
+errors[0xff01] = "Lock error"
+errors[0xff02] = "File not found"
+errors[0xff03] = "The file not found or cannot be unlocked"
+errors[0xff04] = "Record not found"
+errors[0xff05] = "The logical record was not found"
+errors[0xff06] = "The printer associated with PrinterNumber does not exist"
+errors[0xff07] = "No such printer"
+errors[0xff08] = "Unable to complete the request"
+errors[0xff09] = "Unauthorized to change privileges of this trustee"
+errors[0xff0a] = "No files matching the search criteria were found"
+errors[0xff0b] = "A file matching the search criteria was not found"
+errors[0xff0c] = "Verification failed"
+errors[0xff0d] = "Object associated with ObjectID is not a manager"
+errors[0xff0e] = "Invalid initial semaphore value"
+errors[0xff0f] = "The semaphore handle is not valid"
+errors[0xff10] = "SemaphoreHandle is not associated with a valid sempahore"
+errors[0xff11] = "Invalid semaphore handle"
+errors[0xff12] = "Transaction tracking is not available"
+errors[0xff13] = "The transaction has not yet been written to disk"
+errors[0xff14] = "Directory already exists"
+errors[0xff15] = "The file already exists and the deletion flag was not set"
+errors[0xff16] = "No matching files or directories were found"
+errors[0xff17] = "A file or directory matching the search criteria was not found"
+errors[0xff18] = "The file already exists"
+errors[0xff19] = "No files found"
+
+##############################################################################
+# NCP Packets
+##############################################################################
+# 2222/02
+pkt = NCP(0x02, "File Release Lock", 'sync')
+pkt.Request(7)
+pkt.Reply(8)
+pkt.CompletionCodes([0x0000, 0xff00])
+
+#
+# Untested
+#
+# 2222/03
+#pkt = NCP(0x03, "Log File", 'sync')
+#pkt.request( (12, 267), [
+#      [ 7, 1, DirHandle ],
+#      [ 8, 1, LogLockType ],
+#      [ 9, 2, TimeoutLimit, LE ],
+#      [ 11, (1, 256), FilePath ],
+#      ])
+#pkt.completion_codes([0x0000, 0x8200, 0x9600, 0xfe00, 0xff01])
+#
+## 2222/04
+#pkt = NCP(0x04, "Lock File Set", 'sync')
+#pkt.request([
+#      [ 7, TimeoutLimit ],
+#      ])
+#pkt.completion_codes([0xfe, 0xff01])
+#
+## 2222/05
+#pkt = NCP(0x05, "Release File", 'sync')
+#pkt.request([
+#      [ 7, DirHandle ],
+#      [ 8, FilePath ],
+#      ])
+#pkt.completion_codes([0x7e, 0x98, 0x9b, 0x9c, 0xff02])
+#
+## 2222/06
+#pkt = NCP(0x06, "Release File Set", 'sync')
+#pkt.request([
+#      [ 7, UnknownByte ],
+#      ])
+#pkt.completion_codes()
+#
+## 2222/07
+#pkt = NCP(0x07, "Clear File", 'sync')
+#pkt.request([
+#      [ 7, DirHandle ],
+#      [ 8, FilePath ],
+#      ])
+#pkt.completion_codes([0x7e, 0x96, 0x98, 0x9b, 0x9c,
+#      0xa1, 0xfd, 0xff])
+#
+## 2222/08
+#pkt = NCP(0x08, "Clear File Set", 'sync')
+#pkt.request([
+#      [ 7, FileLock ],
+#      ])
+#pkt.completion_codes([0x7e])
+#
+## 2222/09
+#pkt = NCP(0x09, "Log Logical Record", 'sync')
+#pkt.request([
+#      [ 7, LogicalLockType ],
+#      [ 8, TimeoutLimit_be ],
+#      [ 10, LogicalRecordName ],
+#      ])
+#pkt.completion_codes([0x96, 0xfe, 0xff])
+#
+## 2222/0a
+#pkt = NCP(0x0a, "Lock Logical Record Set", 'sync')
+#pkt.request([
+#      [ 7, LogicalLockType ],
+#      [ 8, TimeoutLimit_le ],
+#      ])
+#pkt.completion_codes([0xfe, 0xff])
+#
+## 2222/0b
+#pkt = NCP(0x0b, "Clear Logical Record", 'sync')
+#pkt.request([
+#      [7, LogicalRecordName ],
+#      ])
+#pkt.completion_codes([0xff]
+## 2222/0c
+## 2222/0d
+## 2222/0e
+## 2222/0f
+## 2222/11
+#
+## 2222/1100
+#pkt = NCP(0x1100, "Lock Logical Record Set", 'sync')
+#pkt.request([
+#      [ 10, var_length_data("data").length_var("packetlength") ]
+#      ])
+#pkt.completion_codes()
+#
+
+# 2222/1735
+pkt = NCP(0x1735, "Get Bindery Object ID", 'bindery')
+pkt.Request((13,60), [
+       [ 10, 2, ObjectType ],
+       [ 12, (1,48), ObjectName ],
+])
+pkt.Reply(62, [
+       [ 8, 4, ObjectID ],
+       [ 12, 2, ObjectType ],
+       [ 14, 48, ObjectName ], # XXX
+])
+pkt.CompletionCodes([0x0000, 0x9600, 0xef01, 0xf000, 0xfc02,
+       0xfe01, 0xff00])
+
+# 2222/1737
+pkt = NCP(0x1737, "Scan Bindery Object", 'bindery')
+pkt.Request((17,64), [
+       [ 10, 4, ObjectID ],
+       [ 14, 2, ObjectType ],
+       [ 12, (1,48), ObjectName ],
+])
+pkt.Reply(65, [
+       [ 8, 4, ObjectID ],
+       [ 12, 2, ObjectType ],
+       [ 14, 48, ObjectNameZ ], # XXX
+       [ 62, 1, ObjectFlags ],
+       [ 63, 1, ObjectSecurity ],
+       [ 64, 1, ObjectHasProperties ],
+])
+pkt.CompletionCodes([0x0000, 0x9600, 0xef01, 0xfc02,
+       0xfe01, 0xff00])
+
+# 2222/173D
+pkt = NCP(0x173D, "Read Property Value", 'bindery')
+pkt.Request((15,77), [
+       [ 10, 2, ObjectType ],
+       [ 12, (1,48), ObjectName ],
+       [ -1, 1, PropertySegment ],
+       [ -1, (1,16), PropertyName ],
+])
+pkt.Reply(138, [
+       [ 8, 128, PropertyData ],
+       [ 136, 1, PropertyHasMoreSegments ],
+       [ 137, 1, PropertyType ],
+])
+pkt.CompletionCodes([0x0000, 0x8800, 0x9300, 0x9600, 0xec01,
+       0xf000, 0xf100, 0xf900, 0xfb02, 0xfc02, 0xfe01, 0xff00 ])
+
+# 2222/177C
+pkt = NCP(0x177C, "Service Queue Job", 'queue')
+pkt.Request(16, [
+       [ 10, 4, ObjectID ],
+       [ 14, 2, JobType ],
+])
+pkt.Reply(24, [ # XXX - 76, [
+       [ 8, 4, ConnectionNumber ],
+       [ 12, 4, TaskNumber ],
+       [ 16, 4, ObjectID ],
+       [ 20, 4, ObjectID ],
+       # XXX - DateTime
+])
+# These completion codes are not documented, but guessed.
+pkt.CompletionCodes([0x0000, 0x9900, 0xd000, 0xd100, 0xd201, 0xd300,
+       0xd401, 0xd502, 0xd601, 0xd704, 0xd800, 0xd901, 0xda01, 0xdb01,
+       0xff00 ])
+
+# 2222/18
+pkt = NCP(0x18, "End of Job", 'connection')
+pkt.Request(7)
+pkt.Reply(8)
+pkt.CompletionCodes([0x0000])
+
+# 2222/19
+pkt = NCP(0x19, "Logout", 'connection')
+pkt.Request(7)
+pkt.Reply(8)
+pkt.CompletionCodes([0x0000])
+
+# 2222/21
+pkt = NCP(0x21, "Negotiate Buffer Size", 'connection')
+pkt.Request(9, [
+       [ 7, 2, BufferSize ],
+])
+pkt.Reply(10, [
+       [ 8, 2, BufferSize ],
+])
+pkt.CompletionCodes([0x0000])
+
+# 2222/42
+pkt = NCP(0x42, "Close File", 'file')
+pkt.Request(13, [
+       [ 7, 6, FileHandle ],
+])
+pkt.Reply(8)
+pkt.CompletionCodes([0x0000, 0xff00])
+
+# 2222/47
+pkt = NCP(0x47, "Get Current Size of File", 'file')
+pkt.Request(13, [
+       [ 7, 6, FileHandle ],
+])
+pkt.Reply(12, [
+       [ 8, 4, FileSize ],
+])
+pkt.CompletionCodes([0x0000, 0x8800])
+
+# 2222/48
+pkt = NCP(0x48, "Read From A File", 'file')
+pkt.Request(20, [
+       [ 7, 1, UnknownByte ],
+       [ 8, 6, FileHandle ],
+       [ 14, 4, FileOffset ],  # my nomenclature
+       [ 18, 2, MaxBytes ],    # my nomenclature
+])
+pkt.Reply(10, [ # XXX - (10,-1), [
+       [ 8, 2, NumBytes ],     # my nomenclature
+       # XXX
+])
+pkt.CompletionCodes([0x0000, 0x8800, 0x9300, 0xff00])
+
+# 2222/5701    - no info
+# 2222/5702    - no info
+# 2222/5706    - no info
+# 2222/5714    - no info
+# 2222/68      - no info
+# 2222/72      - no info
+
+##############################################################################
+# Produce C code
+##############################################################################
+
+print "/*"
+print " * Generated automatically from %s" % (sys.argv[0])
+print " * Do not edit this file manually, as all changes will be lost."
+print " */\n"
+
+print """
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib.h>
+#include "packet.h"
+#include "conversation.h"
+#include "ptvcursor.h"
+#include "packet-ncp-int.h"
+    
+static int hf_ncp_func = -1;
+static int hf_ncp_length = -1;
+static int hf_ncp_subfunc = -1;
+static int hf_ncp_completion_code = -1;
+static int hf_ncp_connection_status = -1;
+"""
+
+# Look at all packet types in the packets collection, and cull information
+# from them.
+packet_keys = []
+for packet in packets.Members():
+       packet_keys.append(packet.FunctionCode())
+packet_keys.sort()
+
+errors_used_list = []
+errors_used_hash = {}
+groups_used_list = []
+groups_used_hash = {}
+variables_used_hash = {}
+
+for pkt in packets.Members():
+       # Determine which error codes are used.
+       codes = pkt.CompletionCodes()
+       for code in codes.Records():
+               if not errors_used_hash.has_key(code):
+                       errors_used_hash[code] = len(errors_used_list)
+                       errors_used_list.append(code)
+
+       # Determine which groups are used.
+       group = pkt.Group()
+       if not groups_used_hash.has_key(group):
+               groups_used_hash[group] = len(groups_used_list)
+               groups_used_list.append(group)
+
+       # Determine which variables are used.
+       vars = pkt.Variables()
+       for var in vars:
+               variables_used_hash[var] = 1
+
+
+
+# Print the hf variable declarations
+for var in variables_used_hash.keys():
+       print "static int " + var.HFName() + " = -1;"
+
+
+# Print the value_string's
+for var in variables_used_hash.keys():
+       if var.type == "val_string8" or var.type == "val_string16":
+               print ""
+               print `var`
+
+
+print """
+void
+proto_register_ncp2222(void)
+{
+
+       static hf_register_info hf[] = {
+       { &hf_ncp_func,
+       { "Function", "ncp.func", FT_UINT8, BASE_HEX, NULL, 0x0, "" }},
+
+       { &hf_ncp_length,
+       { "Packet Length", "ncp.length", FT_UINT16, BASE_DEC, NULL, 0x0, "" }},
+
+       { &hf_ncp_subfunc,
+       { "SubFunction", "ncp.subfunc", FT_UINT8, BASE_HEX, NULL, 0x0, "" }},
+
+       { &hf_ncp_completion_code,
+       { "Completion Code", "ncp.completion_code", FT_UINT8, BASE_HEX, NULL, 0x0, "" }},
+
+       { &hf_ncp_connection_status,
+       { "Connection Status", "ncp.connection_status", FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
+"""
+
+# Print the registration code for the hf variables
+for var in variables_used_hash.keys():
+       print "\t{ &%s," % (var.HFName())
+       print "\t{ \"%s\", \"%s\", %s, %s, %s, 0x%x, \"\" }},\n" % \
+               (var.Description(), var.DFilter(),
+               var.EtherealFType(), var.Display(), var.ValuesName(),
+               var.Mask())
+
+print """\t};
+
+       proto_register_field_array(proto_ncp, hf, array_length(hf));
+}
+"""
+
+
+# Determine which error codes are not used
+errors_not_used = {}
+# Copy the keys from the error list...
+for code in errors.keys():
+       errors_not_used[code] = 1
+# ... and remove the ones that *were* used.
+for code in errors_used_list:
+       del errors_not_used[code]
+
+# Print a remark showing errors not used
+list_errors_not_used = errors_not_used.keys()
+list_errors_not_used.sort()
+for code in list_errors_not_used:
+       print "/* Error 0x%04x not used: %s */" % (code, errors[code])
+print "\n"
+
+# Print the errors table
+print "/* Error strings. */"
+print "static const char *ncp_errors[] = {"
+for code in errors_used_list:
+       print '\t/* %02d (0x%04x) */ "%s",' % (errors_used_hash[code], code, errors[code])
+print "};\n"
+
+
+
+
+# Determine which groups are not used
+groups_not_used = {}
+# Copy the keys from the group list...
+for group in groups.keys():
+       groups_not_used[group] = 1
+# ... and remove the ones that *were* used.
+for group in groups_used_list:
+       del groups_not_used[group]
+
+# Print a remark showing groups not used
+list_groups_not_used = groups_not_used.keys()
+list_groups_not_used.sort()
+for group in list_groups_not_used:
+       print "/* Group not used: %s = %s */" % (group, groups[group])
+print "\n"
+
+# Print the groups table
+print "/* Group strings. */"
+print "static const char *ncp_groups[] = {"
+for group in groups_used_list:
+       print '\t/* %02d (%s) */ "%s",' % (groups_used_hash[group], group, groups[group])
+print "};\n"
+
+# Print PTVC's
+print "/* PTVC records. These are re-used to save space. */"
+for ptvc in ptvc_lists.Members():
+       if not ptvc.Null() and not ptvc.Empty():
+               print "static const ptvc_record %s[] = {" % (ptvc.Name())
+               records = ptvc.Records()
+               for ptvc_rec in records:
+                       print "\t%s," % (ptvc_rec)
+               print "\t{ NULL, 0, 0 }"
+               print "};\n"
+
+# Print error_equivalency tables
+print "/* Error-Equivalency Tables. These are re-used to save space. */"
+for compcodes in compcode_lists.Members():
+       errors = compcodes.Records()
+       # Make sure the record for error = 0x00 comes last.
+       print "static const error_equivalency %s[] = {" % (compcodes.Name())
+       for error in errors:
+               error_in_packet = error >> 8;
+               ncp_error_index = errors_used_hash[error]
+               print "\t{ 0x%02x, %d }, /* 0x%04x */" % (error_in_packet,
+                       ncp_error_index, error)
+       print "\t{ 0x00, -1 }\n};\n"
+
+
+# Print ncp_record packet records
+print "#define SUBFUNC 0xff"
+print "#define NOSUB   0x00"
+
+print "/* ncp_record structs for packets */"
+print "static const ncp_record ncp_packets[] = {"
+for pkt in packets.Members():
+       if pkt.HasSubFunction():
+               subfunc_string = "SUBFUNC"
+       else:
+               subfunc_string = "NOSUB"
+       print '\t{ 0x%02x, 0x%02x, %s, "%s",' % (pkt.FunctionCode('high'),
+               pkt.FunctionCode('low'), subfunc_string, pkt.Description()),
+
+       print '\t%d /* %s */,' % (groups_used_hash[pkt.Group()], pkt.Group())
+
+       ptvc = pkt.PTVCRequest()
+       if not ptvc.Null() and not ptvc.Empty():
+               ptvc_request = ptvc.Name()
+       else:
+               ptvc_request = 'NULL'
+
+       ptvc = pkt.PTVCReply()
+       if not ptvc.Null() and not ptvc.Empty():
+               ptvc_reply = ptvc.Name()
+       else:
+               ptvc_reply = 'NULL'
+
+       errors = pkt.CompletionCodes()
+       print '\t\t%s, NULL, %s, NULL,' % (ptvc_request, ptvc_reply)
+       print '\t\t%s },\n' % (errors.Name())
+
+print '\t{ 0, 0, 0, NULL }'
+print "};\n"
+
+print "/* ncp funcs that require a subfunc */"
+print "static const guint8 ncp_func_requires_subfunc[] = {"
+hi_seen = {}
+for pkt in packets.Members():
+       if pkt.HasSubFunction():
+               hi_func = pkt.FunctionCode('high')
+               if not hi_seen.has_key(hi_func):
+                       print "\t0x%02x," % (hi_func)
+                       hi_seen[hi_func] = 1
+print "\t0"
+print "};\n"
+
+
+print '#include "ncp2222.h"'
+
+
diff --git a/packet-ncp-int.h b/packet-ncp-int.h
new file mode 100644 (file)
index 0000000..c56f844
--- /dev/null
@@ -0,0 +1,67 @@
+/* packet-ncp-int.h
+ * Structures and functions for NetWare Core Protocol.
+ * Gilbert Ramirez <gram@xiexie.org>
+ *
+ * $Id: packet-ncp-int.h,v 1.1 2000/07/28 20:03:42 gram Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.org>
+ * 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.
+ */
+
+typedef struct {
+       int             *hf_ptr;
+       gint            length;
+       gboolean        endianness;
+} ptvc_record;
+
+typedef struct {
+       guint8          error_in_packet;
+       gint            ncp_error_index;
+} error_equivalency;
+
+typedef struct {
+       guint8                  func;
+       guint8                  subfunc;
+       guint8                  submask;
+       gchar*                  name;
+       gint                    group;
+       const ptvc_record       *request_ptvc;
+       void                    *requst_func;
+       const ptvc_record       *reply_ptvc;
+       void                    *reply_func;
+       const error_equivalency *errors;
+} ncp_record;
+
+
+void dissect_ncp_request(tvbuff_t*, packet_info*, guint16,
+               guint8, guint16, proto_tree*, proto_tree*);
+
+void dissect_ncp_reply(tvbuff_t *, packet_info*, guint16,
+               guint8, proto_tree*, proto_tree*);
+
+void ncp_hash_insert(conversation_t *conversation, guint8 nw_sequence,
+               guint16 ncp_type, const ncp_record *ncp_rec);
+
+/* Returns TRUE or FALSE. If TRUE, the record was found and
+ * ncp_type and ncp_rec are set. */
+gboolean ncp_hash_lookup(conversation_t*, guint8 nw_sequence,
+               guint16 *ncp_type, const ncp_record **ncp_rec);
+
+
+extern int proto_ncp;
index 9f18cd22c6698750c3d77f6c680fa41a6c4de889..83e6b0334ccc6581975f43480b3f8c005fcc867f 100644 (file)
@@ -3,11 +3,11 @@
  * Gilbert Ramirez <gram@xiexie.org>
  * Modified to allow NCP over TCP/IP decodes by James Coe <jammer@cin.net>
  *
- * $Id: packet-ncp.c,v 1.37 2000/05/31 05:07:22 guy Exp $
+ * $Id: packet-ncp.c,v 1.38 2000/07/28 20:03:42 gram Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@zing.org>
- * Copyright 1998 Gerald Combs
+ * Copyright 2000 Gerald Combs
  *
  * 
  * This program is free software; you can redistribute it and/or
@@ -25,8 +25,6 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
-#undef DEBUG_NCP_HASH
-
 #ifdef HAVE_CONFIG_H
 # include "config.h"
 #endif
 #include <glib.h>
 #include "packet.h"
 #include "conversation.h"
+#include "prefs.h"
 #include "packet-ipx.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_type = -1;
 static int hf_ncp_seq = -1;
@@ -53,37 +55,18 @@ 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;
 
 #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;
+static guint ncp_packet_init_count = 200;
 
 /* These are the header structures to handle NCP over IP */
 #define        NCPIP_RQST      0x446d6454      /* "DmdT" */
@@ -129,34 +112,11 @@ struct ncp_common_header {
        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;
+       guint8  conn_high; /* type=0x5555 doesn't have this */
 };
 
 
-static value_string request_reply_values[] = {
+static value_string ncp_type_vals[] = {
        { 0x1111, "Create a service connection" },
        { 0x2222, "Service request" },
        { 0x3333, "Service reply" },
@@ -166,261 +126,6 @@ static value_string request_reply_values[] = {
        { 0x0000, 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.
- */
-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 }
-};
-
-
-/* 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
@@ -442,16 +147,16 @@ static ncp2222_record ncp2222[] = {
  * struct tells us the NCP type and gives the ncp2222_record pointer, if
  * ncp_type == 0x2222.
  */
-
-struct ncp_request_key {
+typedef struct {
        conversation_t  *conversation;
        guint8          nw_sequence;
-};
+} ncp_request_key;
+
+typedef struct {
+       guint16                 ncp_type;
+       const ncp_record        *ncp_record;
+} ncp_request_val;
 
-struct ncp_request_val {
-       guint32                 ncp_type;
-       struct ncp2222_record*  ncp_record;
-};
 
 static GHashTable *ncp_request_hash = NULL;
 static GMemChunk *ncp_request_keys = NULL;
@@ -460,14 +165,8 @@ 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
+       ncp_request_key *val1 = (ncp_request_key*)v;
+       ncp_request_key *val2 = (ncp_request_key*)v2;
 
        if (val1->conversation == val2->conversation &&
            val1->nw_sequence  == val2->nw_sequence ) {
@@ -478,11 +177,7 @@ gint  ncp_equal (gconstpointer v, gconstpointer v2)
 
 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
+       ncp_request_key *ncp_key = (ncp_request_key*)v;
        return GPOINTER_TO_UINT(ncp_key->conversation) + ncp_key->nw_sequence;
 }
 
@@ -491,9 +186,6 @@ guint ncp_hash  (gconstpointer v)
 static void
 ncp_init_protocol(void)
 {
-       #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)
@@ -503,524 +195,153 @@ ncp_init_protocol(void)
 
        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);
+                       sizeof(ncp_request_key),
+                       ncp_packet_init_count * sizeof(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);
+                       sizeof(ncp_request_val),
+                       ncp_packet_init_count * sizeof(ncp_request_val), G_ALLOC_AND_FREE);
 }
 
-static struct ncp2222_record *
-ncp2222_find(guint8 func, guint8 subfunc)
+void
+ncp_hash_insert(conversation_t *conversation, guint8 nw_sequence,
+               guint16 ncp_type, const ncp_record *ncp_rec)
 {
-       struct ncp2222_record *ncp_record, *retval = NULL;
+       ncp_request_val         *request_val;
+       ncp_request_key         *request_key;
 
-       ncp_record = ncp2222;
+       /* 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;
 
-       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++;
-       }
+       request_val = g_mem_chunk_alloc(ncp_request_records);
+       request_val->ncp_type = ncp_type;
+       request_val->ncp_record = ncp_rec;
 
-       return retval;
+       g_hash_table_insert(ncp_request_hash, request_key, request_val);
 }
 
-static void
-dissect_ncp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
+/* Returns TRUE or FALSE. If TRUE, the record was found and
+ * ncp_type and ncp_rec are set. */
+gboolean
+ncp_hash_lookup(conversation_t *conversation, guint8 nw_sequence,
+               guint16 *ncp_type, const ncp_record **ncp_rec)
+{
+       ncp_request_val         *request_val;
+       ncp_request_key         request_key;
+
+       request_key.conversation = conversation;
+       request_key.nw_sequence = nw_sequence;
+
+       request_val = (ncp_request_val*)
+               g_hash_table_lookup(ncp_request_hash, &request_key);
+
+       if (request_val) {
+               *ncp_type       = request_val->ncp_type;
+               *ncp_rec        = request_val->ncp_record;
+               return TRUE;
+       }
+       else {
+               return FALSE;
+       }
+}
 
-       proto_tree      *ncp_tree = NULL;
-       proto_item      *ti;
-       int             ncp_hdr_length = 0;
+#if 0
+void
+dissect_ncp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+#else
+void
+dissect_ncp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree)
+{
+       packet_info     *pinfo = &pi;
+       tvbuff_t        *tvb = tvb_create_from_top(offset);
+#endif
+       proto_tree                      *ncp_tree = NULL;
+       proto_item                      *ti;
        struct ncp_ip_header            ncpiph;
        struct ncp_ip_rqhdr             ncpiphrq;
        struct ncp_common_header        header;
-       guint16         nw_connection;
-       guint8          nw_sequence;
-       guint16         nw_ncp_type;
+       guint16                         nw_connection;
+       int                             hdr_offset = 0;
+       int                             commhdr;
+
+       pinfo->current_proto = "NCP";
+       if (check_col(pinfo->fd, COL_PROTOCOL))
+               col_add_str(pinfo->fd, COL_PROTOCOL, "NCP");
 
        if ( pi.ptype == PT_TCP || pi.ptype == PT_UDP ) {
-               ncpiph.signature = pntohl(&pd[offset]);
-               ncpiph.length = pntohl(&pd[offset + 4]);
-               offset += 8;
+               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;
+                       ncpiphrq.version        = tvb_get_ntohl(tvb, hdr_offset);
+                       hdr_offset += 4;
+                       ncpiphrq.rplybufsize    = tvb_get_ntohl(tvb, hdr_offset);
+                       hdr_offset += 4;
                };
        };
 
-       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;
-       }
+       /* Record the offset where the NCP common header starts */
+       commhdr = hdr_offset;
 
-       if (check_col(fd, COL_PROTOCOL))
-               col_add_str(fd, COL_PROTOCOL, "NCP");
+       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);
 
        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, FALSE);
+               ti = proto_tree_add_item(tree, proto_ncp, tvb, 0, tvb_length(tvb), FALSE);
                ncp_tree = proto_item_add_subtree(ti, ett_ncp);
 
                if ( pi.ptype == PT_TCP || pi.ptype == PT_UDP ) {
-                       proto_tree_add_uint(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);
+                       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, 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(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);
                        };
                };
-               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_uint(ncp_tree, hf_ncp_seq, NullTVB, 
-                                   offset+2,    1, header.sequence);
-
-               proto_tree_add_uint(ncp_tree, hf_ncp_connection, NullTVB,
-                                   offset+3,    3, nw_connection);
-
-               proto_tree_add_uint(ncp_tree, hf_ncp_task, NullTVB, 
-                                   offset+4,    1, header.task);
+               proto_tree_add_uint(ncp_tree, hf_ncp_type,      tvb, commhdr + 0, 2, header.type);
+               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);
        }
 
-       /* 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;
+       if (header.type == 0x1111 || header.type == 0x2222) {
+               dissect_ncp_request(tvb, pinfo, nw_connection,
+                       header.sequence, header.type, ncp_tree, tree);
        }
-
-       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);
-               }
-               else {
-                       col_add_fstr(fd, COL_INFO, "C Unknown Function %02X/%02X",
-                               request.function, request.subfunc);
-               }
+       else if (header.type == 0x3333) {
+               dissect_ncp_reply(tvb, pinfo, nw_connection,
+                       header.sequence, ncp_tree, tree);
        }
+       else if (       header.type == 0x5555 ||
+                       header.type == 0x7777 ||
+                       header.type == 0x9999           ) {
 
-       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);
+               if (check_col(pinfo->fd, COL_INFO)) {
+                       col_add_fstr(pinfo->fd, COL_INFO, "Type 0x%04x", header.type);
                }
 
-               /* 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;
-
-               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);
-
-               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 (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;
-                       }
-
-                       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);
-
-                               parse_ncp_svc_fields(pd, field_tree, offset, ncp_request->req);
-                       }
+               if (tree) {
+                       proto_tree_add_text(ncp_tree, tvb, commhdr + 0, 2, "Type 0x%04x not supported yet", header.type);
                }
-       }
-}
-
-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;
-       }
-
-       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");
-               }
-               else {
-                       col_add_fstr(fd, COL_INFO, "R Not OK");
-               }
+               return;
        }
-
-       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));
-               }
-               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");
-               }
-
-               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);
-                       }
-               }
-       }
-
-}
-
-/* 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)
-{
-       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++;
-       }       
+       else {
+               /* The value_string for hf_ncp_type already indicates that this type is unknown.
+                * Just return and do no more parsing. */
+               return;
+       }
 }
 
-static char*
-ncp_completion_code(guint8 ccode, enum nfamily family)
-{
-               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) {
-
-               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;
-
-               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";
-       }
-}
 
 void
 proto_register_ncp(void)
@@ -1030,14 +351,22 @@ proto_register_ncp(void)
     { &hf_ncp_ip_sig,
       { "NCP over IP signature",               "ncp.ip.signature",
         FT_UINT32, BASE_HEX, VALS(ncp_ip_signature), 0x0,
-        "NCP over IP transport signature"}},
+        "" }},
+    { &hf_ncp_ip_length,
+      { "NCP over IP length",          "ncp.ip.length",
+        FT_UINT32, BASE_HEX, NULL, 0x0,
+        "" }},
     { &hf_ncp_ip_ver,
-      { "Version",             "ncp.ip.version",
+      { "NCP over IP Version",         "ncp.ip.version",
         FT_UINT32, BASE_DEC, NULL, 0x0,
-        "NCP over IP verion"}},
+        "" }},
+    { &hf_ncp_ip_rplybufsize,
+      { "NCP over IP Reply Buffer Size",       "ncp.ip.replybufsize",
+        FT_UINT32, BASE_DEC, NULL, 0x0,
+        "" }},
     { &hf_ncp_type,
       { "Type",                        "ncp.type",
-       FT_UINT16, BASE_HEX, NULL, 0x0,
+       FT_UINT16, BASE_HEX, VALS(ncp_type_vals), 0x0,
        "NCP message type" }},
     { &hf_ncp_seq,
       { "Sequence Number",             "ncp.seq",
@@ -1054,14 +383,20 @@ proto_register_ncp(void)
   };
   static gint *ett[] = {
     &ett_ncp,
-    &ett_ncp_request_fields,
-    &ett_ncp_reply_fields,
   };
+  module_t *ncp_module;
 
   proto_ncp = proto_register_protocol("NetWare Core Protocol", "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);
+
+  /* Register a configuration option for initial size of NCP hash */
+  ncp_module = prefs_register_module("ncp", "NCP", NULL);
+  prefs_register_uint_preference(ncp_module, "initial_hash_size",
+       "Initial Hash Size",
+       "Number of entries initially allocated for NCP hash",
+       10, &ncp_packet_init_count);
 }
 
 void
diff --git a/proto.c b/proto.c
index fac918c80ce6044e4d4228f95f32435aa8e4b5fb..7bdebcda68707e18d97d8eae3dcc956e2c37bfa1 100644 (file)
--- a/proto.c
+++ b/proto.c
@@ -1,7 +1,7 @@
 /* proto.c
  * Routines for protocol tree
  *
- * $Id: proto.c,v 1.71 2000/07/27 06:41:58 gram Exp $
+ * $Id: proto.c,v 1.72 2000/07/28 20:03:43 gram Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@zing.org>
@@ -2359,6 +2359,9 @@ proto_registrar_dump(void)
                        case FT_RELATIVE_TIME:
                                enum_name = "FT_RELATIVE_TIME";
                                break;
+                       case FT_NSTRING_UINT8:
+                               enum_name = "FT_NSTRING_UINT8";
+                               break;
                        case FT_STRING:
                                enum_name = "FT_STRING";
                                break;
@@ -2381,8 +2384,7 @@ proto_registrar_dump(void)
                                enum_name = "FT_TEXT_ONLY";
                                break;
                        default:
-                               enum_name = "UNKNOWN";
-                               break;
+                               g_assert_not_reached();
                        }
                        printf("F\t%s\t%s\t%s\t%s\n", hfinfo->name, hfinfo->abbrev,
                                enum_name,parent_hfinfo->abbrev);
diff --git a/ptvcursor.c b/ptvcursor.c
new file mode 100644 (file)
index 0000000..971fc0e
--- /dev/null
@@ -0,0 +1,78 @@
+/* ptvcursor.c
+ * 
+ * Proto Tree TVBuff cursor
+ * Gilbert Ramirez <gram@xiexie.org>
+ *
+ * $Id: ptvcursor.c,v 1.1 2000/07/28 20:03:43 gram Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.org>
+ * 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.
+ */
+
+#ifndef __PTVCURSOR_H__
+#include "ptvcursor.h"
+#endif
+
+
+struct ptvcursor {
+       proto_tree      *tree;
+       tvbuff_t        *tvb;
+       gint            offset;
+};
+
+
+/* Allocates an initializes a ptvcursor_t with 3 variables:
+ *     proto_tree, tvbuff, and offset. */
+ptvcursor_t*
+ptvcursor_new(proto_tree *tree, tvbuff_t *tvb, gint offset)
+{
+       ptvcursor_t     *ptvc;
+
+       ptvc = g_new(ptvcursor_t, 1);
+       ptvc->tree      = tree;
+       ptvc->tvb       = tvb;
+       ptvc->offset    = offset;
+       return ptvc;
+}
+
+
+/* Gets data from tvbuff, adds it to proto_tree, increments offset,
+ * and returns proto_item* */
+proto_item*
+ptvcursor_add(ptvcursor_t *ptvc, int hf, gint length, gboolean endianness)
+{
+       proto_item      *item;
+
+       item = proto_tree_add_item(ptvc->tree, hf, ptvc->tvb, ptvc->offset,
+                       length, endianness);
+
+       if (length == PTVC_VARIABLE_LENGTH) {
+               ptvc->offset += proto_item_get_len(item);
+       }
+       else {
+               ptvc->offset += length;
+       }
+       return item;
+}
+
+/* Frees memory for ptvcursor_t, but nothing deeper than that. */
+void
+ptvcursor_free(ptvcursor_t *ptvc)
+{
+       g_free(ptvc);
+}
diff --git a/ptvcursor.h b/ptvcursor.h
new file mode 100644 (file)
index 0000000..b78a0ae
--- /dev/null
@@ -0,0 +1,58 @@
+/* ptvcursor.h
+ * 
+ * Proto Tree TVBuff cursor
+ * Gilbert Ramirez <gram@xiexie.org>
+ *
+ * $Id: ptvcursor.h,v 1.1 2000/07/28 20:03:43 gram Exp $
+ *
+ * Ethereal - Network traffic analyzer
+ * By Gerald Combs <gerald@zing.org>
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib.h>
+
+#ifndef __PACKET_H__
+#include "packet.h"
+#endif
+
+#ifndef __PTVCURSOR_H__
+#define __PTVCURSOR_H__
+
+typedef struct ptvcursor ptvcursor_t;
+
+#define PTVC_VARIABLE_LENGTH   -1
+
+/* Allocates an initializes a ptvcursor_t with 3 variables:
+ *     proto_tree, tvbuff, and offset. */
+ptvcursor_t*
+ptvcursor_new(proto_tree*, tvbuff_t*, gint);
+
+/* Gets data from tvbuff, adds it to proto_tree, increments offset,
+ * and returns proto_item* */
+proto_item*
+ptvcursor_add(ptvcursor_t*, int hf, gint length, gboolean endianness);
+
+/* Frees memory for ptvcursor_t, but nothing deeper than that. */
+void
+ptvcursor_free(ptvcursor_t*);
+
+#endif /* __PTVCURSOR_H__ */