From: gram Date: Fri, 28 Jul 2000 20:03:59 +0000 (+0000) Subject: Add the re-write of the NetWare Core Protocol dissector. It's mostly X-Git-Url: http://git.samba.org/samba.git/?p=obnox%2Fwireshark%2Fwip.git;a=commitdiff_plain;h=f5ed01f9e9ccc86c152a597eb9407e0fa6d72a47 Add the re-write of the NetWare Core Protocol dissector. It's mostly 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 --- diff --git a/Makefile.am b/Makefile.am index 6b13ea78f6..88ff068c06 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 @@ -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 diff --git a/Makefile.nmake b/Makefile.nmake index db11da2094..fc7d4004b7 100644 --- a/Makefile.nmake +++ b/Makefile.nmake @@ -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 d175175d3b..c913edec4d 100644 --- 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 ) @@ -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 Gilbert Ramirez +Guy Harris diff --git a/config.nmake b/config.nmake index 00f7b1ff97..67d76c10d1 100644 --- a/config.nmake +++ b/config.nmake @@ -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 diff --git a/configure.in b/configure.in index d40a4b5aca..2db9feeea2 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/doc/Makefile.am b/doc/Makefile.am index b6edea1bc3..74c1c9e1ee 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -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 @@ -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 index 0000000000..45d42ce900 --- /dev/null +++ b/ncp2222.h @@ -0,0 +1,261 @@ +/* ncp2222.h + * Routines for NetWare Core Protocol + * Gilbert Ramirez + * + * $Id: ncp2222.h,v 1.1 2000/07/28 20:03:41 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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 index 0000000000..5464419c4d --- /dev/null +++ b/ncp2222.py @@ -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 +# +# 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 +#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 index 0000000000..c56f84458a --- /dev/null +++ b/packet-ncp-int.h @@ -0,0 +1,67 @@ +/* packet-ncp-int.h + * Structures and functions for NetWare Core Protocol. + * Gilbert Ramirez + * + * $Id: packet-ncp-int.h,v 1.1 2000/07/28 20:03:42 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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; diff --git a/packet-ncp.c b/packet-ncp.c index 9f18cd22c6..83e6b0334c 100644 --- a/packet-ncp.c +++ b/packet-ncp.c @@ -3,11 +3,11 @@ * Gilbert Ramirez * Modified to allow NCP over TCP/IP decodes by James Coe * - * $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 - * 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 @@ -42,10 +40,14 @@ #include #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 = π + 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 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 fac918c80c..7bdebcda68 100644 --- 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 @@ -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 index 0000000000..971fc0e0ee --- /dev/null +++ b/ptvcursor.c @@ -0,0 +1,78 @@ +/* ptvcursor.c + * + * Proto Tree TVBuff cursor + * Gilbert Ramirez + * + * $Id: ptvcursor.c,v 1.1 2000/07/28 20:03:43 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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 index 0000000000..b78a0ae3c2 --- /dev/null +++ b/ptvcursor.h @@ -0,0 +1,58 @@ +/* ptvcursor.h + * + * Proto Tree TVBuff cursor + * Gilbert Ramirez + * + * $Id: ptvcursor.h,v 1.1 2000/07/28 20:03:43 gram Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs + * 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 + +#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__ */