#!/usr/bin/python """ 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. Novell provides info at: http://developer.novell.com/ndk (where you can download an *.exe file which installs a PDF) or http://developer.novell.com/ndk/doc/docui/index.htm#../ncp/ncp__enu/data/ for a badly-formatted HTML version of the same PDF. $Id: ncp2222.py,v 1.11 2001/11/13 23:55:29 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 import string errors = {} groups = {} packets = None compcode_lists = None ptvc_lists = None ############################################################################## # Global containers ############################################################################## class UniqueCollection: """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.""" def __init__(self, name): "Constructor" self.name = name self.members = [] def Add(self, object): """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.""" # 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 def Members(self): "Returns the list of members." return self.members def HasMember(self, object): "Does the list of members contain the object?" for member in self.members: if member == object: return 1 return 0 ############################################################################## class NamedList: "NamedList's keep track of PTVC's and Completion Codes" def __init__(self, name, list): "Constructor" self.name = name self.list = list def __cmp__(self, other): "Compare this NamedList to another" # 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 def __repr__(self): "String representation" return "NamedList: " + `self.list` def Name(self, new_name = None): "Get/Set name of list" if new_name != None: self.name = new_name return self.name def Records(self): "Returns record lists" return self.list def Null(self): "Is there no list (different from an empty list)?" return self.list == None def Empty(self): "It the list empty (different from a null list)?" assert(not self.Null()) if self.list: return 0 else: return 1 class PTVC(NamedList): """ProtoTree TVBuff Cursor List ("PTVC List") Class""" def __init__(self, name, records): "Constructor" self.list = [] NamedList.__init__(self, name, self.list) expected_offset = None # Make a PTVCRecord object for each list in 'records' for record in records: ptvc_rec = PTVCRecord(record) if expected_offset == None: expected_offset = ptvc_rec.Offset() elif expected_offset == -1: pass elif expected_offset != ptvc_rec.Offset(): sys.stderr.write("Expected offset in %s to be %d\n" % (name, expected_offset)) sys.exit(1) # We can't make a PTVC list from a variable-length # packet, unless it's FT_UINT_STRING if type(ptvc_rec.Length()) == type(()): if isinstance(ptvc_rec.Field(), nstring8): expected_offset = -1 pass else: self.list = None return elif expected_offset > -1: expected_offset = expected_offset + ptvc_rec.Length() self.list.append(ptvc_rec) class PTVCRecord: def __init__(self, record): "Constructor" self.offset = record[0] self.length = record[1] self.field = record[2] # Small sanity check field_length = self.field.Length() # if type(field_length) != type(self.length): # sys.stderr.write("Length types do not match") # sys.exit(1) # 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() def __cmp__(self, other): "Comparison operator" 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 def __repr__(self): "String representation" endianness = 'FALSE' if self.endianness == LE: endianness = 'TRUE' length = -1 if type(self.length) == type(0): length = self.length else: var_length = self.field.Length() if var_length > 0: length = var_length if length > -1: return "{ &%s, %d, %s }" % (self.field.HFName(), length, endianness) else: length = "PTVC_VARIABLE_LENGTH" return "{ &%s, %s, %s }" % (self.field.HFName(), length, endianness) def Offset(self): return self.offset def Length(self): return self.length def Field(self): return self.field ############################################################################## class NCP: "NCP Packet class" def __init__(self, func_code, description, group, has_length=1): "Constructor" self.func_code = func_code self.description = description self.group = group self.codes = None self.request_records = None self.reply_records = None self.has_length = has_length 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 def FunctionCode(self, part=None): "Returns the function code for this NCP packet." 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) def HasSubFunction(self): "Does this NPC packet require a subfunction field?" if self.func_code <= 0xff: return 0 else: return 1 def HasLength(self): return self.has_length 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(): if self.HasLength(): self.CheckRecords(size, records, "Request", 10) else: self.CheckRecords(size, records, "Request", 8) 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") def CheckRecords(self, size, records, descr, min_hdr_length): "Simple sanity check" 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) def MakePTVC(self, records, name_suffix): """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).""" name = "%s_%s" % (self.CName(), name_suffix) ptvc = PTVC(name, records) return ptvc_lists.Add(ptvc) def CName(self): "Returns a C symbol based on the NCP function code" return "ncp_0x%x" % (self.func_code) def Variables(self): """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).""" 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() def CompletionCodes(self, codes=None): """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.""" 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() def Finalize(self): """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.""" # 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) ############################################################################## LE = 1 # Little-Endian BE = 0 # Big-Endian NA = -1 # Not Applicable class Type: " Virtual class for NCP field types" 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) class nstring8(Type): """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.""" type = "nstring8" ftype = "FT_UINT_STRING" def __init__(self, abbrev, descr): Type.__init__(self, abbrev, descr, 1) class fw_string(Type): """A fixed-width string of n bytes.""" type = "fw_string" ftype = "FT_STRING" def __init__(self, abbrev, descr, bytes): Type.__init__(self, abbrev, descr, bytes) class stringz(Type): "NUL-terminated string, with a maximum length" type = "stringz" ftype = "FT_STRINGZ" def __init__(self, abbrev, descr): Type.__init__(self, abbrev, descr, -1) class val_string(Type): """Abstract class for val_stringN, where N is number of bits that key takes up.""" 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..." ############################################################################## AcceptedMaxSize = uint16("accepted_max_size", "Accepted Max Size") AcctVersion = byte("acct_version", "Acct Version") BufferSize = uint16("buffer_size", "Buffer Size") ConnectionNumber = uint32("connection_number", "Connection Number") ConnectionsSupportedMax = uint16("connections_supported_max", "Connections Supported Max") ConnectionsInUse = uint16("connections_in_use", "Connections In Use") ConnectionsMaxUsed = uint16("connections_max_used", "Connections Max Used") DirHandle = byte("dir_handle", "Directory Handle") EchoSocket = uint16("echo_socket", "Echo Socket") EchoSocket.Display('BASE_HEX') 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") InternetBridgeVersion = byte("internet_bridge_version", "Internet Bridge Version") JobType = uint16("job_type", "Job Type") LocalLoginInfoCcode = byte("local_login_info_ccode", "Local Login Info C Code") 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") MixedModePathFlag = byte("mixed_mode_path_flag", "Mixed Mode Path Flag") 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") ObjectName1 = fw_string("object_name1", "Object Name", 48) 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" ], ]) OSLanguageID = byte("os_language_id", "OS Language ID") OSMajorVersion = byte("os_major_version", "OS Major Version") OSMinorVersion = byte("os_minor_version", "OS Minor Version") OSRevision = byte("os_revision", "OS Revision") PingVersion = uint16("ping_version", "Ping Version", endianness=LE) PingVersion.Display("BASE_HEX") PrintServerVersion = byte("print_server_version", "Print Server Version") ProductMajorVersion = uint16("product_major_version", "Product Major Version") ProductMinorVersion = uint16("product_minor_version", "Product Minor Version") ProductRevisionVersion = byte("product_revision_version", "Product Revision Version") 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" ], ]) ProposedMaxSize = uint16("proposed_max_size", "Proposed Max Size") Reserved3 = bytes("reserved3", "Reserved", 3) Reserved51 = bytes("reserved51", "Reserved", 51) QMSVersion = byte("qms_version", "QMS Version") # XXX - needs bitfield SecurityFlag = byte("security_flag", "Security Flag") SecurityRestrictionVersion = byte("security_restriction_version", "Security Restriction Version") ServerName = stringz("server_name", "Server Name") SFTLevel = byte("sft_level", "SFT Level") TaskNumber = uint32("task_number", "Task Number") TimeoutLimit = uint16("timeout_limit", "Timeout Limit") TTSLevel = byte("tts_level", "TTS Level") UnknownByte = byte("unknown_byte", "Unknown Byte") VAPVersion = byte("vap_version", "VAP Version") VirtualConsoleVersion = byte("virtual_console_version", "Virtual Console Version") VolumesSupportedMax = uint16("volumes_supported_max", "Volumes Supported Max") ############################################################################## # NCP Groups ############################################################################## def define_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" groups['unknown'] = "Unknown" ############################################################################## # NCP Errors ############################################################################## def define_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[0xfb04] = "NDS NCP not available" 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[0xfe0c] = "Bad packet" 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" ############################################################################## # Produce C code ############################################################################## def produce_code(): global errors 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, "", HFILL }}, { &hf_ncp_length, { "Packet Length", "ncp.length", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL }}, { &hf_ncp_subfunc, { "SubFunction", "ncp.subfunc", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL }}, { &hf_ncp_completion_code, { "Completion Code", "ncp.completion_code", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL }}, { &hf_ncp_connection_status, { "Connection Status", "ncp.connection_status", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }}, """ # 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, \"\", HFILL }},\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 the group macros for group in groups_used_list: name = string.upper(group) print "#define NCP_GROUP_%s\t%d" % (name, groups_used_hash[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" # Functions without length parameter funcs_without_length = {} # Print ncp_record packet records print "#define SUBFUNC_WITH_LENGTH 0x02" print "#define SUBFUNC_NO_LENGTH 0x01" print "#define NO_SUBFUNC 0x00" print "/* ncp_record structs for packets */" print "static const ncp_record ncp_packets[] = {" for pkt in packets.Members(): if pkt.HasSubFunction(): func = pkt.FunctionCode('high') if pkt.HasLength(): subfunc_string = "SUBFUNC_WITH_LENGTH" # Ensure that the function either has a length param or not if funcs_without_length.has_key(func): sys.exit("Function 0x%04x sometimes has length param, sometimes not." \ % (pkt.FunctionCode(),)) else: subfunc_string = "SUBFUNC_NO_LENGTH" funcs_without_length[func] = 1 else: subfunc_string = "NO_SUBFUNC" 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, 0, NULL, NULL, NULL, NULL, 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 "/* ncp funs that have no length parameter */" print "static const guint8 ncp_func_has_no_length_parameter[] = {" funcs = funcs_without_length.keys() funcs.sort() for func in funcs: print "\t0x%02x," % (func,) print "\t0" print "};\n" print '#include "packet-ncp2222.inc"' def main(): global packets global compcode_lists global ptvc_lists packets = UniqueCollection('NCP Packet Descriptions') compcode_lists = UniqueCollection('Completion Code Lists') ptvc_lists = UniqueCollection('PTVC Lists') define_errors() define_groups() define_ncp2222() produce_code() def define_ncp2222(): ############################################################################## # NCP Packets. Here I list functions and subfunctions in hexadecimal like the # NCP book (and I believe LanAlyzer does this too). # However, Novell lists these in decimal in their on-line documentation. ############################################################################## # 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/1711 pkt = NCP(0x1711, "Get File Server Information", 'fileserver') pkt.Request(10) pkt.Reply(136, [ [ 8, 48, ServerName ], [ 56, 1, OSMajorVersion ], [ 57, 1, OSMinorVersion ], [ 58, 2, ConnectionsSupportedMax ], [ 60, 2, ConnectionsInUse ], [ 62, 2, VolumesSupportedMax ], [ 64, 1, OSRevision ], [ 65, 1, SFTLevel ], [ 66, 1, TTSLevel ], [ 67, 2, ConnectionsMaxUsed ], [ 69, 1, AcctVersion ], [ 70, 1, VAPVersion ], [ 71, 1, QMSVersion ], [ 72, 1, PrintServerVersion ], [ 73, 1, VirtualConsoleVersion ], [ 74, 1, SecurityRestrictionVersion ], [ 75, 1, InternetBridgeVersion ], [ 76, 1, MixedModePathFlag ], [ 77, 1, LocalLoginInfoCcode ], [ 78, 2, ProductMajorVersion ], [ 80, 2, ProductMinorVersion ], [ 82, 2, ProductRevisionVersion ], [ 84, 1, OSLanguageID ], [ 85, 51, Reserved51 ], ]) pkt.CompletionCodes([0x0000, 0x9600]) # 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, ObjectName1 ], ]) 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 ], [ 16, (1,48), ObjectName ], ]) pkt.Reply(65, [ [ 8, 4, ObjectID ], [ 12, 2, ObjectType ], [ 14, 48, ObjectName1 ], [ 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/72 - no info # 2222/61 pkt = NCP(0x61, "Get Big Packet NCP Max Packet Size", 'unknown') pkt.Request(10, [ [ 7, 2, ProposedMaxSize ], [ 9, 1, SecurityFlag ], ]) pkt.Reply(13, [ [ 8, 2, AcceptedMaxSize ], [ 10, 2, EchoSocket ], [ 12, 1, SecurityFlag ], ]) pkt.CompletionCodes([0x0000]) # 2222/6801 pkt = NCP(0x6801, "Ping for NDS NCP", "nds", has_length=0) pkt.Request(11, [ [ 8, 3, Reserved3 ] ]) # XXX - expand, Unicode pkt.Reply(10, [ [ 8, 2, PingVersion ], ]) pkt.CompletionCodes([0x0000, 0xfb04, 0xfe0c]) if __name__ == '__main__': main()