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