2 * Routines for NetWare Core Protocol
3 * Gilbert Ramirez <gram@xiexie.org>
4 * Modified to allow NCP over TCP/IP decodes by James Coe <jammer@cin.net>
6 * $Id: packet-ncp.c,v 1.34 2000/04/18 04:46:06 guy Exp $
8 * Ethereal - Network traffic analyzer
9 * By Gerald Combs <gerald@zing.org>
10 * Copyright 1998 Gerald Combs
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34 #ifdef HAVE_SYS_TYPES_H
35 # include <sys/types.h>
38 #ifdef HAVE_NETINET_IN_H
39 # include <netinet/in.h>
44 #include "conversation.h"
45 #include "packet-ipx.h"
46 #include "packet-ncp.h"
48 static int proto_ncp = -1;
49 static int hf_ncp_ip_ver = -1;
50 static int hf_ncp_ip_sig = -1;
51 static int hf_ncp_type = -1;
52 static int hf_ncp_seq = -1;
53 static int hf_ncp_connection = -1;
54 static int hf_ncp_task = -1;
56 static gint ett_ncp = -1;
57 static gint ett_ncp_request_fields = -1;
58 static gint ett_ncp_reply_fields = -1;
60 #define TCP_PORT_NCP 524
61 #define UDP_PORT_NCP 524
66 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd,
67 guint16 nw_connection, guint8 nw_sequence, guint16 nw_ncp_type,
68 proto_tree *ncp_tree, proto_tree *tree);
71 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd,
72 guint16 nw_connection, guint8 nw_sequence,
73 proto_tree *ncp_tree, proto_tree *tree);
75 static struct ncp2222_record *
76 ncp2222_find(guint8 func, guint8 subfunc);
79 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
80 struct svc_record *svc);
84 gint ncp_equal (gconstpointer v, gconstpointer v2);
85 guint ncp_hash (gconstpointer v);
87 int ncp_packet_init_count = 200;
89 /* These are the header structures to handle NCP over IP */
90 #define NCPIP_RQST 0x446d6454 /* "DmdT" */
91 #define NCPIP_RPLY 0x744e6350 /* "tNcP" */
93 struct ncp_ip_header {
98 /* This header only appears on NCP over IP request packets */
104 static const value_string ncp_ip_signature[] = {
105 { NCPIP_RQST, "Demand Transport (Request)" },
106 { NCPIP_RPLY, "Transport is NCP (Reply)" },
109 /* The information in this module comes from:
110 NetWare LAN Analysis, Second Edition
111 Laura A. Chappell and Dan E. Hakes
112 (c) 1994 Novell, Inc.
113 Novell Press, San Jose.
116 And from the ncpfs source code by Volker Lendecke
119 Programmer's Guide to the NetWare Core Protocol
120 Steve Conner & Diane Conner
121 (c) 1996 by Steve Conner & Diane Conner
122 Published by Annabooks, San Diego, California
127 /* Every NCP packet has this common header */
128 struct ncp_common_header {
136 /* NCP request packets */
137 struct ncp_request_header {
148 /* NCP reply packets */
149 struct ncp_reply_header {
155 guint8 completion_code;
156 guint8 connection_state;
160 static value_string request_reply_values[] = {
161 { 0x1111, "Create a service connection" },
162 { 0x2222, "Service request" },
163 { 0x3333, "Service reply" },
164 { 0x5555, "Destroy service connection" },
165 { 0x7777, "Burst mode transfer" },
166 { 0x9999, "Request being processed" },
170 /* These are the field types in an NCP packet */
172 nend, /* end of the NCP field list */
173 nbyte, /* one byte of data */
174 nhex, /* bytes to be shown as hex digits */
175 nbelong, /* 4-byte big-endian long int */
176 nbeshort, /* 2-byte big-endian short int */
177 ndata, /* unstructured data */
178 nbytevar, /* a variable number of bytes */
179 ndatetime, /* date-time stamp */
180 nasciile, /* length-encoded ASCII string. First byte is length */
181 nasciiz /* null-terminated string of ASCII characters */
184 /* These are the broad families that the different NCP request types belong
188 NCP_UNKNOWN_SERVICE, /* unknown or n/a */
189 NCP_QUEUE_SERVICES, /* print queues */
190 NCP_FILE_SERVICES, /* file serving */
191 NCP_BINDERY_SERVICES, /* bindery database */
192 NCP_CONNECTION_SERVICES /* communication */
195 /* I had to put this function prototype after the enum nfamily declaration */
197 ncp_completion_code(guint8 ccode, enum nfamily family);
200 /* Information on the NCP field */
201 typedef struct svc_record {
203 guint8 length; /* max-length for variable-sized fields */
207 typedef struct ncp2222_record {
210 guint8 submask; /* Does this function have subfunctions?
211 * SUBFUNC or NOSUB */
221 /* ------------------------------------------------------------ */
223 /* Get Bindery Object ID REQUEST */
224 static svc_record ncp_17_35_C[] = {
225 { nbeshort, 2, "Object Type: 0x%04x" },
226 { nasciile, 48, "Object Name: %.*s" },
229 /* Get Bindery Object ID REPLY has no fields*/
232 /* Service Queue Job REQUEST */
233 static svc_record ncp_17_7C_C[] = {
234 { nbelong, 4, "The queue the job resides in" },
235 { nbeshort, 2, "Job Type" },
238 /* Service Queue Job REPLY */
239 static svc_record ncp_17_7C_R[] = {
240 { nbelong, 4, "Client station number: %d" },
241 { nbelong, 4, "Task Number: %d" },
242 { nbelong, 4, "User: %d" },
243 { nbelong, 4, "Server specifed to service queue entry: %08X" },
244 { ndatetime, 6, "Earliest time to execute" },
245 { ndatetime, 6, "When job entered queue" },
246 { nbelong, 4, "Job Number" },
247 { nbeshort, 2, "Job Type" },
248 { nbeshort, 2, "Job Position" },
249 { nbeshort, 2, "Current status of job: 0x%02x" },
250 { nasciiz, 14, "Name of file" },
251 { nbelong, 4, "File handle" },
252 { nbelong, 4, "Client station number" },
253 { nbelong, 4, "Task number" },
254 { nbelong, 4, "Job server" },
260 /* Negotiate Buffer Size REQUEST */
261 static svc_record ncp_21_00_C[] = {
262 { nbeshort, 2, "Caller's maximum packet size: %d bytes" },
265 /* Negotiate Buffer Size RESPONSE */
266 static svc_record ncp_21_00_R[] = {
267 { nbeshort, 2, "Packet size decided upon by file server: %d bytes" },
272 /* Close File REQUEST */
273 static svc_record ncp_42_00_C[] = {
274 { nhex, 6, "File Handle: 02x:02x:02x:02x:02x:02x"},
277 /* Close File RESPONSE */
278 static svc_record ncp_42_00_R[] = {
283 /* Read from a file REQUEST */
284 static svc_record ncp_48_00_C[] = {
285 { nbyte, 1, "Unknown" },
286 { nhex, 6, "File Handle" },
287 { nbelong, 4, "Byte offset within file" },
288 { nbeshort, 2, "Maximum data bytes to return" },
292 static svc_record ncp_48_00_R[] = {
293 { nbeshort, 2, "Data bytes returned" },
294 { nbytevar, 1, "Padding" },
298 /* ------------------------------------------------------------ */
299 /* Any svc_record that has no fields is not created.
300 * Store a NULL in the ncp2222_record instead */
305 static ncp2222_record ncp2222[] = {
307 { 0x00, 0x00, NOSUB, "Create service connection",
308 NULL, NULL, NCP_CONNECTION_SERVICES
311 { 0x14, 0x00, NOSUB, "Get server's clock",
312 NULL, NULL, NCP_FILE_SERVICES
315 { 0x16, 0x01, SUBFUNC, "Get path of directory handle",
316 NULL, NULL, NCP_FILE_SERVICES
319 { 0x16, 0x13, SUBFUNC, "Create temporary directory handle",
320 NULL, NULL, NCP_BINDERY_SERVICES
323 { 0x16, 0x0A, SUBFUNC, "Create directory",
324 NULL, NULL, NCP_BINDERY_SERVICES
327 { 0x16, 0x0D, SUBFUNC, "Add trustee to directory",
328 NULL, NULL, NCP_BINDERY_SERVICES
331 { 0x17, 0x11, SUBFUNC, "Get fileserver information",
332 NULL, NULL, NCP_BINDERY_SERVICES
335 { 0x17, 0x37, SUBFUNC, "Scan bindery object",
336 NULL, NULL, NCP_BINDERY_SERVICES
339 { 0x17, 0x36, SUBFUNC, "Get bindery object name",
340 NULL, NULL, NCP_BINDERY_SERVICES
343 { 0x17, 0x32, SUBFUNC, "Create bindery object",
344 NULL, NULL, NCP_BINDERY_SERVICES
347 { 0x17, 0x39, SUBFUNC, "Create property",
348 NULL, NULL, NCP_BINDERY_SERVICES
351 { 0x17, 0x41, SUBFUNC, "Add bindery object to set",
352 NULL, NULL, NCP_BINDERY_SERVICES
355 { 0x17, 0x43, SUBFUNC, "Is bindery object in set",
356 NULL, NULL, NCP_BINDERY_SERVICES
359 { 0x17, 0x35, SUBFUNC, "Get Bindery Object ID",
360 ncp_17_35_C, NULL, NCP_BINDERY_SERVICES
363 { 0x17, 0x7C, SUBFUNC, "Service Queue Job",
364 ncp_17_7C_C, ncp_17_7C_R, NCP_QUEUE_SERVICES
367 { 0x17, 0x3D, SUBFUNC, "Read property value",
368 NULL, NULL, NCP_FILE_SERVICES
371 { 0x18, 0x00, NOSUB, "End of Job",
372 NULL, NULL, NCP_CONNECTION_SERVICES
375 { 0x19, 0x00, NOSUB, "Logout",
376 NULL, NULL, NCP_CONNECTION_SERVICES
379 { 0x21, 0x00, NOSUB, "Negotiate Buffer Size",
380 ncp_21_00_C, ncp_21_00_R, NCP_CONNECTION_SERVICES
383 { 0x24, 0x00, SUBFUNC, "Destroy service connection",
384 NULL, NULL, NCP_CONNECTION_SERVICES
387 { 0x3E, 0x53, SUBFUNC, "Get alternate directory search paths",
388 NULL, NULL, NCP_FILE_SERVICES
391 { 0x3F, 0x89, SUBFUNC, "File search continue",
392 NULL, NULL, NCP_FILE_SERVICES
395 { 0x42, 0x00, NOSUB, "Close File",
396 ncp_42_00_C, ncp_42_00_R, NCP_FILE_SERVICES
399 { 0x48, 0x00, NOSUB, "Read from a file",
400 ncp_48_00_C, ncp_48_00_R, NCP_FILE_SERVICES
403 { 0x4C, 0x11, SUBFUNC, "Open file",
404 NULL, NULL, NCP_FILE_SERVICES
407 { 0x61, 0x00, NOSUB, "Get big packet NCP max packet size",
408 NULL, NULL, NCP_FILE_SERVICES
411 { 0x68, 0x01, SUBFUNC, "Ping for NDS NCP",
412 NULL, NULL, NCP_FILE_SERVICES
415 { 0x68, 0x02, SUBFUNC, "Send NDS fragmented message",
416 NULL, NULL, NCP_FILE_SERVICES
419 { 0x00, 0x00, NOSUB, NULL,
420 NULL, NULL, NCP_UNKNOWN_SERVICE
426 /* NCP packets come in request/reply pairs. The request packets tell the type
427 * of NCP request and give a sequence ID. The response, unfortunately, only
428 * identifies itself via the sequence ID; you have to know what type of NCP
429 * request the request packet contained in order to successfully parse the NCP
430 * response. A global method for doing this does not exist in ethereal yet
431 * (NFS also requires it), so for now the NCP section will keep its own hash
432 * table keeping track of NCP packet types.
434 * We construct a conversation specified by the client and server
435 * addresses and the connection number; the key representing the unique
436 * NCP request then is composed of the pointer to the conversation
437 * structure, cast to a "guint" (which may throw away the upper 32
438 * bits of the pointer on a P64 platform, but the low-order 32 bits
439 * are more likely to differ between conversations than the upper 32 bits),
440 * and the sequence number.
442 * The value stored in the hash table is the ncp_request_val pointer. This
443 * struct tells us the NCP type and gives the ncp2222_record pointer, if
444 * ncp_type == 0x2222.
447 struct ncp_request_key {
448 conversation_t *conversation;
452 struct ncp_request_val {
454 struct ncp2222_record* ncp_record;
457 static GHashTable *ncp_request_hash = NULL;
458 static GMemChunk *ncp_request_keys = NULL;
459 static GMemChunk *ncp_request_records = NULL;
462 gint ncp_equal (gconstpointer v, gconstpointer v2)
464 struct ncp_request_key *val1 = (struct ncp_request_key*)v;
465 struct ncp_request_key *val2 = (struct ncp_request_key*)v2;
467 #if defined(DEBUG_NCP_HASH)
468 printf("Comparing %p:%d and %p:%d\n",
469 val1->conversation, val1->nw_sequence,
470 val2->conversation, val2->nw_sequence);
473 if (val1->conversation == val2->conversation &&
474 val1->nw_sequence == val2->nw_sequence ) {
480 guint ncp_hash (gconstpointer v)
482 struct ncp_request_key *ncp_key = (struct ncp_request_key*)v;
483 #if defined(DEBUG_NCP_HASH)
484 printf("hash calculated as %u\n",
485 GPOINTER_TO_UINT(ncp_key->conversation) + ncp_key->nw_sequence);
487 return GPOINTER_TO_UINT(ncp_key->conversation) + ncp_key->nw_sequence;
490 /* Initializes the hash table and the mem_chunk area each time a new
491 * file is loaded or re-loaded in ethereal */
493 ncp_init_protocol(void)
495 #if defined(DEBUG_NCP_HASH)
496 printf("Initializing NCP hashtable and mem_chunk area\n");
498 if (ncp_request_hash)
499 g_hash_table_destroy(ncp_request_hash);
500 if (ncp_request_keys)
501 g_mem_chunk_destroy(ncp_request_keys);
502 if (ncp_request_records)
503 g_mem_chunk_destroy(ncp_request_records);
505 ncp_request_hash = g_hash_table_new(ncp_hash, ncp_equal);
506 ncp_request_keys = g_mem_chunk_new("ncp_request_keys",
507 sizeof(struct ncp_request_key),
508 ncp_packet_init_count * sizeof(struct ncp_request_key), G_ALLOC_AND_FREE);
509 ncp_request_records = g_mem_chunk_new("ncp_request_records",
510 sizeof(struct ncp_request_val),
511 ncp_packet_init_count * sizeof(struct ncp_request_val), G_ALLOC_AND_FREE);
514 static struct ncp2222_record *
515 ncp2222_find(guint8 func, guint8 subfunc)
517 struct ncp2222_record *ncp_record, *retval = NULL;
519 ncp_record = ncp2222;
521 while(ncp_record->func != 0 || ncp_record->subfunc != 0 ||
522 ncp_record->funcname != NULL ) {
523 if (ncp_record->func == func &&
524 ncp_record->subfunc == (subfunc & ncp_record->submask)) {
535 dissect_ncp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
537 proto_tree *ncp_tree = NULL;
539 int ncp_hdr_length = 0;
540 struct ncp_ip_header ncpiph;
541 struct ncp_ip_rqhdr ncpiphrq;
542 struct ncp_common_header header;
543 guint16 nw_connection;
547 if ( pi.ptype == PT_TCP || pi.ptype == PT_UDP ) {
548 ncpiph.signature = pntohl(&pd[offset]);
549 ncpiph.length = pntohl(&pd[offset + 4]);
551 if ( ncpiph.signature == NCPIP_RQST ) {
552 ncpiphrq.version = pntohl(&pd[offset]);
553 ncpiphrq.rplybufsize = pntohl(&pd[offset + 4]);
558 memcpy(&header, &pd[offset], sizeof(header));
559 header.type = ntohs(header.type);
561 if (header.type == 0x1111 ||
562 header.type == 0x2222 ||
563 header.type == 0x5555 ||
564 header.type == 0x7777) {
567 else if (header.type == 0x3333 || header.type == 0x9999) {
571 if (check_col(fd, COL_PROTOCOL))
572 col_add_str(fd, COL_PROTOCOL, "NCP");
574 nw_connection = (header.conn_high << 16) + header.conn_low;
575 nw_sequence = header.sequence;
576 nw_ncp_type = header.type;
579 ti = proto_tree_add_item(tree, proto_ncp, offset, END_OF_FRAME, NULL);
580 ncp_tree = proto_item_add_subtree(ti, ett_ncp);
582 if ( pi.ptype == PT_TCP || pi.ptype == PT_UDP ) {
583 proto_tree_add_item(ncp_tree, hf_ncp_ip_sig, offset - 16, 4, ncpiph.signature);
584 proto_tree_add_text(ncp_tree, offset - 12, 4, "Length: %d", ncpiph.length);
585 if ( ncpiph.signature == NCPIP_RQST ) {
586 proto_tree_add_item(ncp_tree, hf_ncp_ip_ver, offset - 8, 4, ncpiphrq.version);
587 proto_tree_add_text(ncp_tree, offset - 4, 4, "Reply buffer size: %d", ncpiphrq.rplybufsize);
590 proto_tree_add_uint_format(ncp_tree, hf_ncp_type,
594 val_to_str( header.type,
595 request_reply_values,
598 proto_tree_add_item(ncp_tree, hf_ncp_seq,
599 offset+2, 1, header.sequence);
601 proto_tree_add_item(ncp_tree, hf_ncp_connection,
602 offset+3, 3, nw_connection);
604 proto_tree_add_item(ncp_tree, hf_ncp_task,
605 offset+4, 1, header.task);
608 /* Note how I use ncp_tree *and* tree in my args for ncp request/reply */
609 if (ncp_hdr_length == 7)
610 dissect_ncp_request(pd, offset, fd, nw_connection,
611 nw_sequence, nw_ncp_type, ncp_tree, tree);
612 else if (ncp_hdr_length == 8)
613 dissect_ncp_reply(pd, offset, fd, nw_connection,
614 nw_sequence, ncp_tree, tree);
616 dissect_data(pd, offset, fd, tree);
620 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd,
621 guint16 nw_connection, guint8 nw_sequence, guint16 nw_ncp_type,
622 proto_tree *ncp_tree, proto_tree *tree) {
624 struct ncp_request_header request;
625 struct ncp2222_record *ncp_request;
626 gchar *description = "";
627 conversation_t *conversation;
628 struct ncp_request_val *request_val;
629 struct ncp_request_key *request_key;
630 proto_tree *field_tree = NULL;
631 proto_item *ti = NULL;
633 /*memcpy(&request, &pd[offset], sizeof(request));*/
634 request.function = pd[offset+6];
635 if ( BYTES_ARE_IN_FRAME(offset, 9) ) {
636 request.subfunc = pd[offset+9];
641 ncp_request = ncp2222_find(request.function, request.subfunc);
644 description = ncp_request->funcname;
646 if (check_col(fd, COL_INFO)) {
647 if (description[0]) {
648 col_add_fstr(fd, COL_INFO, "C %s", description);
651 col_add_fstr(fd, COL_INFO, "C Unknown Function %02X/%02X",
652 request.function, request.subfunc);
656 if (!fd->flags.visited) {
657 /* This is the first time we've looked at this packet.
658 Keep track of the address and connection whence the request
659 came, and the address and connection to which the request
660 is being sent, so that we can match up calls with replies.
661 (We don't include the sequence number, as we may want
662 to have all packets over the same connection treated
663 as being part of a single conversation so that we can
664 let the user select that conversation to be displayed.) */
665 conversation = find_conversation(&pi.src, &pi.dst,
666 PT_NCP, nw_connection, nw_connection);
667 if (conversation == NULL) {
668 /* It's not part of any conversation - create a new one. */
669 conversation = conversation_new(&pi.src, &pi.dst,
670 PT_NCP, nw_connection, nw_connection, NULL);
673 /* Now remember the request, so we can find it if we later
675 request_key = g_mem_chunk_alloc(ncp_request_keys);
676 request_key->conversation = conversation;
677 request_key->nw_sequence = nw_sequence;
679 request_val = g_mem_chunk_alloc(ncp_request_records);
680 request_val->ncp_type = nw_ncp_type;
681 request_val->ncp_record = ncp2222_find(request.function, request.subfunc);
683 g_hash_table_insert(ncp_request_hash, request_key, request_val);
684 #if defined(DEBUG_NCP_HASH)
685 printf("Inserted conversation %p sequence %d (val=%p)\n",
686 conversation, nw_sequence, request_val);
691 proto_tree_add_text(ncp_tree, offset+6, 1,
692 "Function Code: 0x%02X (%s)",
693 request.function, description);
697 if (ncp_request->submask == SUBFUNC) {
698 proto_tree_add_text(ncp_tree, offset+7, 2,
699 "Packet Length: %d bytes", pntohs(&pd[offset+7]));
700 proto_tree_add_text(ncp_tree, offset+9, 1,
701 "Subfunction Code: 0x%02x", pd[offset+9]);
708 if (ncp_request->req) {
709 ti = proto_tree_add_text(ncp_tree, offset, END_OF_FRAME,
710 "NCP Request Packet");
711 field_tree = proto_item_add_subtree(ti, ett_ncp_request_fields);
713 parse_ncp_svc_fields(pd, field_tree, offset, ncp_request->req);
720 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd,
721 guint16 nw_connection, guint8 nw_sequence,
722 proto_tree *ncp_tree, proto_tree *tree) {
724 struct ncp_reply_header reply;
725 conversation_t *conversation;
726 struct ncp2222_record *ncp_request = NULL;
727 struct ncp_request_val *request_val;
728 struct ncp_request_key request_key;
729 proto_tree *field_tree = NULL;
730 proto_item *ti = NULL;
732 memcpy(&reply, &pd[offset], sizeof(reply));
734 /* Find the conversation whence the request would have come. */
736 conversation = find_conversation(&pi.src, &pi.dst,
737 PT_NCP, nw_connection, nw_connection);
738 if (conversation != NULL) {
739 /* find the record telling us the request made that caused
741 request_key.conversation = conversation;
742 request_key.nw_sequence = nw_sequence;
744 #if defined(DEBUG_NCP_HASH)
745 printf("Looking for conversation %p sequence %u (retval=%p)\n",
746 conversation, nw_sequence, request_val);
749 request_val = (struct ncp_request_val*)
750 g_hash_table_lookup(ncp_request_hash, &request_key);
752 /* We haven't seen an RPC call for that conversation,
753 so we can't check for a reply to that call. */
758 ncp_request = request_val->ncp_record;
760 if (check_col(fd, COL_INFO)) {
761 if (reply.completion_code == 0) {
762 col_add_fstr(fd, COL_INFO, "R OK");
765 col_add_fstr(fd, COL_INFO, "R Not OK");
770 /* A completion code of 0 always means OK. Other values have different
773 proto_tree_add_text(ncp_tree, offset+6, 1,
774 "Completion Code: 0x%02x (%s)", reply.completion_code,
775 ncp_completion_code(reply.completion_code, ncp_request->family));
778 proto_tree_add_text(ncp_tree, offset+6, 1,
779 "Completion Code: 0x%02x (%s)", reply.completion_code,
780 reply.completion_code == 0 ? "OK" : "Unknown");
783 proto_tree_add_text(ncp_tree, offset+7, 1,
784 "Connection Status: %d", reply.connection_state);
788 if (ncp_request->rep) {
789 ti = proto_tree_add_text(ncp_tree, offset+8, END_OF_FRAME,
791 field_tree = proto_item_add_subtree(ti, ett_ncp_reply_fields);
793 parse_ncp_svc_fields(pd, field_tree, offset+8, ncp_request->rep);
800 /* Populates the protocol tree with information about the svc_record fields */
802 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
803 struct svc_record *svc)
805 struct svc_record *rec = svc;
806 int field_offset = offset;
807 int field_length = 0;
809 while (rec->type != nend) {
813 proto_tree_add_text(ncp_tree, field_offset,
814 field_length, rec->description, pntohs(&pd[field_offset]));
818 field_length = pd[field_offset];
819 proto_tree_add_text(ncp_tree, field_offset,
820 field_length + 1, rec->description, field_length,
821 &pd[field_offset+1]);
825 field_length = rec->length;
826 proto_tree_add_text(ncp_tree, field_offset,
827 field_length, rec->description);
834 field_offset += field_length;
840 ncp_completion_code(guint8 ccode, enum nfamily family)
844 #define NCP_CCODE_MIN 0x7e
845 #define NCP_CCODE_MAX 0xff
847 /* From Appendix C of "Programmer's Guide to NetWare Core Protocol" */
848 static char *ccode_text[] = {
849 /* 7e */ "NCP boundary check failed",
851 /* 80 */ "Lock fail. The file is already open",
852 /* 81 */ "A file handle could not be allocated by the file server",
853 /* 82 */ "Unauthorized to open file",
854 /* 83 */ "Unable to read/write the volume. Possible bad sector on the file server",
855 /* 84 */ "Unauthorized to create the file",
858 /* 87 */ "An unexpected character was encountered in the filename",
859 /* 88 */ "FileHandle is not valid",
860 /* 89 */ "Unauthorized to search this directory",
861 /* 8a */ "Unauthorized to delete a file in this directory",
862 /* 8b */ "Unauthorized to rename a file in this directory",
863 /* 8c */ "Unauthorized to modify a file in this directory",
864 /* 8d */ "Some of the affected files are in use by another client",
865 /* 8e */ "All of the affected files are in use by another client",
866 /* 8f */ "Some of the affected file are read only",
868 /* 91 */ "Some of the affected files already exist",
869 /* 92 */ "All of the affected files already exist",
870 /* 93 */ "Unauthorized to read from this file",
871 /* 94 */ "Unauthorized to write to this file",
872 /* 95 */ "The affected file is detached",
873 /* 96 */ "The file server has run out of memory to service this request",
875 /* 98 */ "The affected volume is not mounted",
876 /* 99 */ "The file server has run out of directory space on the affected volume",
877 /* 9a */ "The request attempted to rename the affected file to another volume",
878 /* 9b */ "DirHandle is not associated with a valid directory path",
880 /* 9d */ "A directory handle was not available for allocation",
881 /* 9e */ "The filename does not conform to a legal name for this name space",
882 /* 9f */ "The request attempted to delete a directory that is in use by another client",
883 /* a0 */ "The request attempted to delete a directory that is not empty",
884 /* a1 */ "An unrecoverable error occurred on the affected directory",
885 /* a2 */ "The request attempted to read from a file region that is physically locked",
914 /* bf */ "Requests for this name space are not valid on this volume",
915 /* c0 */ "Unauthorized to retrieve accounting data",
916 /* c1 */ "The 'account balance' property does not exist",
917 /* c2 */ "The object has exceeded its credit limit",
918 /* c3 */ "Too many holds have been placed against this account",
919 /* c4 */ "The account for this bindery object has been disabled",
920 /* c5 */ "Access to the account has been denied because of intruder detections",
921 /* c6 */ "The caller does not have operator privileges",
931 /* d0 */ "Queue error",
932 /* d1 */ "The queue associated with Object ID does not exist",
933 /* d2 */ "A queue server is not associated with the selected queue",
934 /* d3 */ "No queue rights",
935 /* d4 */ "The queue associated with Object ID is full and cannot accept another request",
936 /* d5 */ "The job associated with Job Number does not exist in this queue",
939 /* d8 */ "Queue not active",
945 /* de */ "Attempted to login to the file server with an incorrect password",
946 /* df */ "Attempted to login to the file server with a password that has expired",
954 /* e7 */ "No disk track",
957 /* ea */ "The bindery object is not a member of this set",
958 /* eb */ "The property is not a set property",
959 /* ec */ "The set property does not exist",
960 /* ed */ "The property already exists",
961 /* ee */ "The bindery object already exists",
962 /* ef */ "Illegal characters in Object Name field",
963 /* f0 */ "A wildcard was detected in a field that does not support wildcards",
964 /* f1 */ "The client does not have the rights to access this bindery objecs",
965 /* f2 */ "Unauthorized to read from this object",
966 /* f3 */ "Unauthorized to rename this object",
967 /* f4 */ "Unauthorized to delete this object",
968 /* f5 */ "Unauthorized to create this object",
969 /* f6 */ "Unauthorized to delete the property of this object",
970 /* f7 */ "Unauthorized to create this property",
971 /* f8 */ "Unauthorized to write to this property",
972 /* f9 */ "Unauthorized to read this property",
973 /* fa */ "Temporary remap error",
987 return "Client not accepting messages";
991 if (ccode >= NCP_CCODE_MIN && ccode <= NCP_CCODE_MAX) {
992 text = ccode_text[ccode - NCP_CCODE_MIN];
993 /* If there really is text, return it */
1001 /* We have a completion code with multiple translations. We'll use the
1002 * nfamily that this request type belongs to to give the right
1009 case NCP_QUEUE_SERVICES:
1010 return "The message queue cannot accept another message";
1012 case NCP_BINDERY_SERVICES:
1013 return "The specified bindery object does not exist";
1022 return "I don't know how to parse this completion code. Please send this packet trace to Gilbert Ramirez <gram@xiexie.org> for analysis";
1027 proto_register_ncp(void)
1030 static hf_register_info hf[] = {
1032 { "NCP over IP signature", "ncp.ip.signature",
1033 FT_UINT32, BASE_HEX, VALS(ncp_ip_signature), 0x0,
1034 "NCP over IP transport signature"}},
1036 { "Version", "ncp.ip.version",
1037 FT_UINT32, BASE_DEC, NULL, 0x0,
1038 "NCP over IP verion"}},
1040 { "Type", "ncp.type",
1041 FT_UINT16, BASE_HEX, NULL, 0x0,
1042 "NCP message type" }},
1044 { "Sequence Number", "ncp.seq",
1045 FT_UINT8, BASE_DEC, NULL, 0x0,
1047 { &hf_ncp_connection,
1048 { "Connection Number", "ncp.connection",
1049 FT_UINT16, BASE_DEC, NULL, 0x0,
1052 { "Task Number", "ncp.task",
1053 FT_UINT8, BASE_DEC, NULL, 0x0,
1056 static gint *ett[] = {
1058 &ett_ncp_request_fields,
1059 &ett_ncp_reply_fields,
1062 proto_ncp = proto_register_protocol("NetWare Core Protocol", "ncp");
1063 proto_register_field_array(proto_ncp, hf, array_length(hf));
1064 proto_register_subtree_array(ett, array_length(ett));
1065 register_init_routine(&ncp_init_protocol);
1069 proto_reg_handoff_ncp(void)
1071 dissector_add("tcp.port", TCP_PORT_NCP, dissect_ncp);
1072 dissector_add("udp.port", UDP_PORT_NCP, dissect_ncp);