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.30 2000/01/22 06:22:16 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 "packet-ipx.h"
45 #include "packet-ncp.h"
47 static int proto_ncp = -1;
48 static int hf_ncp_ip_ver = -1;
49 static int hf_ncp_ip_sig = -1;
50 static int hf_ncp_type = -1;
51 static int hf_ncp_seq = -1;
52 static int hf_ncp_connection = -1;
53 static int hf_ncp_task = -1;
55 static gint ett_ncp = -1;
56 static gint ett_ncp_request_fields = -1;
57 static gint ett_ncp_reply_fields = -1;
62 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree);
65 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree);
67 static struct ncp2222_record *
68 ncp2222_find(guint8 func, guint8 subfunc);
71 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
72 struct svc_record *svc);
76 gint ncp_equal (gconstpointer v, gconstpointer v2);
77 guint ncp_hash (gconstpointer v);
79 int ncp_packet_init_count = 200;
81 /* These are the header structures to handle NCP over IP */
82 #define NCPIP_RQST 0x446d6454 /* "DmdT" */
83 #define NCPIP_RPLY 0x744e6350 /* "tNcP" */
85 struct ncp_ip_header {
90 /* This header only appears on NCP over IP request packets */
96 static const value_string ncp_ip_signature[] = {
97 { NCPIP_RQST, "Demand Transport (Request)" },
98 { NCPIP_RPLY, "Transport is NCP (Reply)" },
101 /* The information in this module comes from:
102 NetWare LAN Analysis, Second Edition
103 Laura A. Chappell and Dan E. Hakes
104 (c) 1994 Novell, Inc.
105 Novell Press, San Jose.
108 And from the ncpfs source code by Volker Lendecke
111 Programmer's Guide to the NetWare Core Protocol
112 Steve Conner & Diane Conner
113 (c) 1996 by Steve Conner & Diane Conner
114 Published by Annabooks, San Diego, California
119 /* Every NCP packet has this common header */
120 struct ncp_common_header {
128 /* NCP request packets */
129 struct ncp_request_header {
140 /* NCP reply packets */
141 struct ncp_reply_header {
147 guint8 completion_code;
148 guint8 connection_state;
152 static value_string request_reply_values[] = {
153 { 0x1111, "Create a service connection" },
154 { 0x2222, "Service request" },
155 { 0x3333, "Service reply" },
156 { 0x5555, "Destroy service connection" },
157 { 0x7777, "Burst mode transfer" },
158 { 0x9999, "Request being processed" },
162 /* These are the field types in an NCP packet */
164 nend, /* end of the NCP field list */
165 nbyte, /* one byte of data */
166 nhex, /* bytes to be shown as hex digits */
167 nbelong, /* 4-byte big-endian long int */
168 nbeshort, /* 2-byte big-endian short int */
169 ndata, /* unstructured data */
170 nbytevar, /* a variable number of bytes */
171 ndatetime, /* date-time stamp */
172 nasciile, /* length-encoded ASCII string. First byte is length */
173 nasciiz /* null-terminated string of ASCII characters */
176 /* These are the broad families that the different NCP request types belong
180 NCP_UNKNOWN_SERVICE, /* unknown or n/a */
181 NCP_QUEUE_SERVICES, /* print queues */
182 NCP_FILE_SERVICES, /* file serving */
183 NCP_BINDERY_SERVICES, /* bindery database */
184 NCP_CONNECTION_SERVICES /* communication */
187 /* I had to put this function prototype after the enum nfamily declaration */
189 ncp_completion_code(guint8 ccode, enum nfamily family);
192 /* Information on the NCP field */
193 typedef struct svc_record {
195 guint8 length; /* max-length for variable-sized fields */
199 typedef struct ncp2222_record {
202 guint8 submask; /* Does this function have subfunctions?
203 * SUBFUNC or NOSUB */
213 /* ------------------------------------------------------------ */
215 /* Get Bindery Object ID REQUEST */
216 static svc_record ncp_17_35_C[] = {
217 { nbeshort, 2, "Object Type: 0x%04x" },
218 { nasciile, 48, "Object Name: %.*s" },
221 /* Get Bindery Object ID REPLY has no fields*/
224 /* Service Queue Job REQUEST */
225 static svc_record ncp_17_7C_C[] = {
226 { nbelong, 4, "The queue the job resides in" },
227 { nbeshort, 2, "Job Type" },
230 /* Service Queue Job REPLY */
231 static svc_record ncp_17_7C_R[] = {
232 { nbelong, 4, "Client station number: %d" },
233 { nbelong, 4, "Task Number: %d" },
234 { nbelong, 4, "User: %d" },
235 { nbelong, 4, "Server specifed to service queue entry: %08X" },
236 { ndatetime, 6, "Earliest time to execute" },
237 { ndatetime, 6, "When job entered queue" },
238 { nbelong, 4, "Job Number" },
239 { nbeshort, 2, "Job Type" },
240 { nbeshort, 2, "Job Position" },
241 { nbeshort, 2, "Current status of job: 0x%02x" },
242 { nasciiz, 14, "Name of file" },
243 { nbelong, 4, "File handle" },
244 { nbelong, 4, "Client station number" },
245 { nbelong, 4, "Task number" },
246 { nbelong, 4, "Job server" },
252 /* Negotiate Buffer Size REQUEST */
253 static svc_record ncp_21_00_C[] = {
254 { nbeshort, 2, "Caller's maximum packet size: %d bytes" },
257 /* Negotiate Buffer Size RESPONSE */
258 static svc_record ncp_21_00_R[] = {
259 { nbeshort, 2, "Packet size decided upon by file server: %d bytes" },
264 /* Close File REQUEST */
265 static svc_record ncp_42_00_C[] = {
266 { nhex, 6, "File Handle: 02x:02x:02x:02x:02x:02x"},
269 /* Close File RESPONSE */
270 static svc_record ncp_42_00_R[] = {
275 /* Read from a file REQUEST */
276 static svc_record ncp_48_00_C[] = {
277 { nbyte, 1, "Unknown" },
278 { nhex, 6, "File Handle" },
279 { nbelong, 4, "Byte offset within file" },
280 { nbeshort, 2, "Maximum data bytes to return" },
284 static svc_record ncp_48_00_R[] = {
285 { nbeshort, 2, "Data bytes returned" },
286 { nbytevar, 1, "Padding" },
290 /* ------------------------------------------------------------ */
291 /* Any svc_record that has no fields is not created.
292 * Store a NULL in the ncp2222_record instead */
297 static ncp2222_record ncp2222[] = {
299 { 0x00, 0x00, NOSUB, "Create service connection",
300 NULL, NULL, NCP_CONNECTION_SERVICES
303 { 0x14, 0x00, NOSUB, "Get server's clock",
304 NULL, NULL, NCP_FILE_SERVICES
307 { 0x16, 0x01, SUBFUNC, "Get path of directory handle",
308 NULL, NULL, NCP_FILE_SERVICES
311 { 0x16, 0x13, SUBFUNC, "Create temporary directory handle",
312 NULL, NULL, NCP_BINDERY_SERVICES
315 { 0x16, 0x0A, SUBFUNC, "Create directory",
316 NULL, NULL, NCP_BINDERY_SERVICES
319 { 0x16, 0x0D, SUBFUNC, "Add trustee to directory",
320 NULL, NULL, NCP_BINDERY_SERVICES
323 { 0x17, 0x11, SUBFUNC, "Get fileserver information",
324 NULL, NULL, NCP_BINDERY_SERVICES
327 { 0x17, 0x37, SUBFUNC, "Scan bindery object",
328 NULL, NULL, NCP_BINDERY_SERVICES
331 { 0x17, 0x36, SUBFUNC, "Get bindery object name",
332 NULL, NULL, NCP_BINDERY_SERVICES
335 { 0x17, 0x32, SUBFUNC, "Create bindery object",
336 NULL, NULL, NCP_BINDERY_SERVICES
339 { 0x17, 0x39, SUBFUNC, "Create property",
340 NULL, NULL, NCP_BINDERY_SERVICES
343 { 0x17, 0x41, SUBFUNC, "Add bindery object to set",
344 NULL, NULL, NCP_BINDERY_SERVICES
347 { 0x17, 0x43, SUBFUNC, "Is bindery object in set",
348 NULL, NULL, NCP_BINDERY_SERVICES
351 { 0x17, 0x35, SUBFUNC, "Get Bindery Object ID",
352 ncp_17_35_C, NULL, NCP_BINDERY_SERVICES
355 { 0x17, 0x7C, SUBFUNC, "Service Queue Job",
356 ncp_17_7C_C, ncp_17_7C_R, NCP_QUEUE_SERVICES
359 { 0x17, 0x3D, SUBFUNC, "Read property value",
360 NULL, NULL, NCP_FILE_SERVICES
363 { 0x18, 0x00, NOSUB, "End of Job",
364 NULL, NULL, NCP_CONNECTION_SERVICES
367 { 0x19, 0x00, NOSUB, "Logout",
368 NULL, NULL, NCP_CONNECTION_SERVICES
371 { 0x21, 0x00, NOSUB, "Negotiate Buffer Size",
372 ncp_21_00_C, ncp_21_00_R, NCP_CONNECTION_SERVICES
375 { 0x24, 0x00, SUBFUNC, "Destroy service connection",
376 NULL, NULL, NCP_CONNECTION_SERVICES
379 { 0x3E, 0x53, SUBFUNC, "Get alternate directory search paths",
380 NULL, NULL, NCP_FILE_SERVICES
383 { 0x3F, 0x89, SUBFUNC, "File search continue",
384 NULL, NULL, NCP_FILE_SERVICES
387 { 0x42, 0x00, NOSUB, "Close File",
388 ncp_42_00_C, ncp_42_00_R, NCP_FILE_SERVICES
391 { 0x48, 0x00, NOSUB, "Read from a file",
392 ncp_48_00_C, ncp_48_00_R, NCP_FILE_SERVICES
395 { 0x4C, 0x11, SUBFUNC, "Open file",
396 NULL, NULL, NCP_FILE_SERVICES
399 { 0x61, 0x00, NOSUB, "Get big packet NCP max packet size",
400 NULL, NULL, NCP_FILE_SERVICES
403 { 0x68, 0x01, SUBFUNC, "Ping for NDS NCP",
404 NULL, NULL, NCP_FILE_SERVICES
407 { 0x68, 0x02, SUBFUNC, "Send NDS fragmented message",
408 NULL, NULL, NCP_FILE_SERVICES
411 { 0x00, 0x00, NOSUB, NULL,
412 NULL, NULL, NCP_UNKNOWN_SERVICE
418 /* NCP packets come in request/reply pairs. The request packets tell the type
419 * of NCP request and give a sequence ID. The response, unfortunately, only
420 * identifies itself via the sequence ID; you have to know what type of NCP
421 * request the request packet contained in order to successfully parse the NCP
422 * response. A global method for doing this does not exist in ethereal yet
423 * (NFS also requires it), so for now the NCP section will keep its own hash
424 * table keeping track of NCP packet types.
426 * The key representing the unique NCP request is composed of 3 variables:
428 * ServerIPXNetwork.Connection.SequenceNumber
429 * 4 bytes 2 bytes 1 byte
430 * guint32 guint16 guint8 (all are host order)
432 * This assumes that all NCP connection is between a client and server.
433 * Servers can be identified by having a 00:00:00:00:00:01 IPX Node address.
434 * We have to let the IPX layer pass us the ServerIPXNetwork via a global
435 * variable (nw_server_address). In the future, if we decode NCP over TCP/UDP,
436 * then nw_server_address will represent the IP address of the server, which
437 * conveniently, is also 4 bytes long.
439 * The value stored in the hash table is the ncp_request_val pointer. This
440 * struct tells us the NCP type and gives the ncp2222_record pointer, if
441 * ncp_type == 0x2222.
443 guint32 nw_server_address = 0; /* set by IPX layer */
444 guint16 nw_connection = 0; /* set by dissect_ncp */
445 guint8 nw_sequence = 0; /* set by dissect_ncp */
446 guint16 nw_ncp_type = 0; /* set by dissect_ncp */
448 struct ncp_request_key {
449 guint32 nw_server_address;
450 guint16 nw_connection;
454 struct ncp_request_val {
456 struct ncp2222_record* ncp_record;
459 GHashTable *ncp_request_hash = NULL;
460 GMemChunk *ncp_request_keys = NULL;
461 GMemChunk *ncp_request_records = NULL;
464 gint ncp_equal (gconstpointer v, gconstpointer v2)
466 struct ncp_request_key *val1 = (struct ncp_request_key*)v;
467 struct ncp_request_key *val2 = (struct ncp_request_key*)v2;
469 #if defined(DEBUG_NCP_HASH)
470 printf("Comparing %08X:%d:%d and %08X:%d:%d\n",
471 val1->nw_server_address, val1->nw_connection, val1->nw_sequence,
472 val2->nw_server_address, val2->nw_connection, val2->nw_sequence);
475 if (val1->nw_server_address == val2->nw_server_address &&
476 val1->nw_connection == val2->nw_connection &&
477 val1->nw_sequence == val2->nw_sequence ) {
483 guint ncp_hash (gconstpointer v)
485 struct ncp_request_key *ncp_key = (struct ncp_request_key*)v;
486 #if defined(DEBUG_NCP_HASH)
487 printf("hash calculated as %d\n", ncp_key->nw_server_address +
488 ((guint32) ncp_key->nw_connection << 16) +
489 ncp_key->nw_sequence);
491 return ncp_key->nw_server_address +
492 ((guint32) ncp_key->nw_connection << 16) +
493 ncp_key->nw_sequence;
496 /* Initializes the hash table and the mem_chunk area each time a new
497 * file is loaded or re-loaded in ethereal */
499 ncp_init_protocol(void)
501 #if defined(DEBUG_NCP_HASH)
502 printf("Initializing NCP hashtable and mem_chunk area\n");
504 if (ncp_request_hash)
505 g_hash_table_destroy(ncp_request_hash);
506 if (ncp_request_keys)
507 g_mem_chunk_destroy(ncp_request_keys);
508 if (ncp_request_records)
509 g_mem_chunk_destroy(ncp_request_records);
511 ncp_request_hash = g_hash_table_new(ncp_hash, ncp_equal);
512 ncp_request_keys = g_mem_chunk_new("ncp_request_keys",
513 sizeof(struct ncp_request_key),
514 ncp_packet_init_count * sizeof(struct ncp_request_key), G_ALLOC_AND_FREE);
515 ncp_request_records = g_mem_chunk_new("ncp_request_records",
516 sizeof(struct ncp_request_val),
517 ncp_packet_init_count * sizeof(struct ncp_request_val), G_ALLOC_AND_FREE);
520 static struct ncp2222_record *
521 ncp2222_find(guint8 func, guint8 subfunc)
523 struct ncp2222_record *ncp_record, *retval = NULL;
525 ncp_record = ncp2222;
527 while(ncp_record->func != 0 || ncp_record->subfunc != 0 ||
528 ncp_record->funcname != NULL ) {
529 if (ncp_record->func == func &&
530 ncp_record->subfunc == (subfunc & ncp_record->submask)) {
541 dissect_ncp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
543 proto_tree *ncp_tree = NULL;
545 int ncp_hdr_length = 0;
546 struct ncp_ip_header ncpiph;
547 struct ncp_ip_rqhdr ncpiphrq;
548 struct ncp_common_header header;
550 if ( pi.ptype == PT_TCP || pi.ptype == PT_UDP ) {
551 ncpiph.signature = pntohl(&pd[offset]);
552 ncpiph.length = pntohl(&pd[offset + 4]);
554 if ( ncpiph.signature == NCPIP_RQST ) {
555 ncpiphrq.version = pntohl(&pd[offset]);
556 ncpiphrq.rplybufsize = pntohl(&pd[offset + 4]);
561 memcpy(&header, &pd[offset], sizeof(header));
562 header.type = ntohs(header.type);
564 if (header.type == 0x1111 ||
565 header.type == 0x2222 ||
566 header.type == 0x5555 ||
567 header.type == 0x7777) {
570 else if (header.type == 0x3333 || header.type == 0x9999) {
574 if (check_col(fd, COL_PROTOCOL))
575 col_add_str(fd, COL_PROTOCOL, "NCP");
577 nw_connection = (header.conn_high << 16) + header.conn_low;
578 nw_sequence = header.sequence;
579 nw_ncp_type = header.type;
582 ti = proto_tree_add_item(tree, proto_ncp, offset, END_OF_FRAME, NULL);
583 ncp_tree = proto_item_add_subtree(ti, ett_ncp);
585 if ( pi.ptype == PT_TCP || pi.ptype == PT_UDP ) {
586 proto_tree_add_item(ncp_tree, hf_ncp_ip_sig, offset - 16, 4, ncpiph.signature);
587 proto_tree_add_text(ncp_tree, offset - 12, 4, "Length: %d", ncpiph.length);
588 if ( ncpiph.signature == NCPIP_RQST ) {
589 proto_tree_add_item(ncp_tree, hf_ncp_ip_ver, offset - 8, 4, ncpiphrq.version);
590 proto_tree_add_text(ncp_tree, offset - 4, 4, "Reply buffer size: %d", ncpiphrq.rplybufsize);
593 proto_tree_add_item_format(ncp_tree, hf_ncp_type,
597 val_to_str( header.type,
598 request_reply_values,
601 proto_tree_add_item(ncp_tree, hf_ncp_seq,
602 offset+2, 1, header.sequence);
604 proto_tree_add_item(ncp_tree, hf_ncp_connection,
605 offset+3, 3, nw_connection);
607 proto_tree_add_item(ncp_tree, hf_ncp_task,
608 offset+4, 1, header.task);
611 /* Note how I use ncp_tree *and* tree in my args for ncp request/reply */
612 if (ncp_hdr_length == 7)
613 dissect_ncp_request(pd, offset, fd, ncp_tree, tree);
614 else if (ncp_hdr_length == 8)
615 dissect_ncp_reply(pd, offset, fd, ncp_tree, tree);
617 dissect_data(pd, offset, fd, tree);
621 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd,
622 proto_tree *ncp_tree, proto_tree *tree) {
624 struct ncp_request_header request;
625 struct ncp2222_record *ncp_request;
626 gchar *description = "";
627 struct ncp_request_val *request_val;
628 struct ncp_request_key *request_key;
629 proto_tree *field_tree = NULL;
630 proto_item *ti = NULL;
632 /*memcpy(&request, &pd[offset], sizeof(request));*/
633 request.function = pd[offset+6];
634 if ( BYTES_ARE_IN_FRAME(offset, 9) ) {
635 request.subfunc = pd[offset+9];
640 ncp_request = ncp2222_find(request.function, request.subfunc);
643 description = ncp_request->funcname;
645 if (check_col(fd, COL_INFO)) {
646 if (description[0]) {
647 col_add_fstr(fd, COL_INFO, "C %s", description);
650 col_add_fstr(fd, COL_INFO, "C Unknown Function %02X/%02X",
651 request.function, request.subfunc);
656 proto_tree_add_text(ncp_tree, offset+6, 1,
657 "Function Code: 0x%02X (%s)",
658 request.function, description);
662 if (ncp_request->submask == SUBFUNC) {
663 proto_tree_add_text(ncp_tree, offset+7, 2,
664 "Packet Length: %d bytes", pntohs(&pd[offset+7]));
665 proto_tree_add_text(ncp_tree, offset+9, 1,
666 "Subfunction Code: 0x%02x", pd[offset+9]);
673 if (ncp_request->req) {
674 ti = proto_tree_add_text(ncp_tree, offset, END_OF_FRAME,
675 "NCP Request Packet");
676 field_tree = proto_item_add_subtree(ti, ett_ncp_request_fields);
678 parse_ncp_svc_fields(pd, field_tree, offset, ncp_request->req);
683 request_key = g_mem_chunk_alloc(ncp_request_keys);
684 request_key->nw_server_address = nw_server_address;
685 request_key->nw_connection = nw_connection;
686 request_key->nw_sequence = nw_sequence;
688 request_val = g_mem_chunk_alloc(ncp_request_records);
689 request_val->ncp_type = nw_ncp_type;
690 request_val->ncp_record = ncp2222_find(request.function, request.subfunc);
692 g_hash_table_insert(ncp_request_hash, request_key, request_val);
693 #if defined(DEBUG_NCP_HASH)
694 printf("Inserted server %08X connection %d sequence %d (val=%08X)\n",
695 nw_server_address, nw_connection, nw_sequence, request_val);
702 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd,
703 proto_tree *ncp_tree, proto_tree *tree) {
705 struct ncp_reply_header reply;
706 struct ncp2222_record *ncp_request = NULL;
707 struct ncp_request_val *request_val;
708 struct ncp_request_key request_key;
709 proto_tree *field_tree = NULL;
710 proto_item *ti = NULL;
712 memcpy(&reply, &pd[offset], sizeof(reply));
714 /* find the record telling us the request made that caused this reply */
715 request_key.nw_server_address = nw_server_address;
716 request_key.nw_connection = nw_connection;
717 request_key.nw_sequence = nw_sequence;
719 request_val = (struct ncp_request_val*)
720 g_hash_table_lookup(ncp_request_hash, &request_key);
722 #if defined(DEBUG_NCP_HASH)
723 printf("Looking for server %08X connection %d sequence %d (retval=%08X)\n",
724 nw_server_address, nw_connection, nw_sequence, request_val);
728 ncp_request = request_val->ncp_record;
730 if (check_col(fd, COL_INFO)) {
731 if (reply.completion_code == 0) {
732 col_add_fstr(fd, COL_INFO, "R OK");
735 col_add_fstr(fd, COL_INFO, "R Not OK");
740 /* A completion code of 0 always means OK. Other values have different
743 proto_tree_add_text(ncp_tree, offset+6, 1,
744 "Completion Code: 0x%02x (%s)", reply.completion_code,
745 ncp_completion_code(reply.completion_code, ncp_request->family));
748 proto_tree_add_text(ncp_tree, offset+6, 1,
749 "Completion Code: 0x%02x (%s)", reply.completion_code,
750 reply.completion_code == 0 ? "OK" : "Unknown");
753 proto_tree_add_text(ncp_tree, offset+7, 1,
754 "Connection Status: %d", reply.connection_state);
758 if (ncp_request->rep) {
759 ti = proto_tree_add_text(ncp_tree, offset+8, END_OF_FRAME,
761 field_tree = proto_item_add_subtree(ti, ett_ncp_reply_fields);
763 parse_ncp_svc_fields(pd, field_tree, offset+8, ncp_request->rep);
770 /* Populates the protocol tree with information about the svc_record fields */
772 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
773 struct svc_record *svc)
775 struct svc_record *rec = svc;
776 int field_offset = offset;
777 int field_length = 0;
779 while (rec->type != nend) {
783 proto_tree_add_text(ncp_tree, field_offset,
784 field_length, rec->description, pntohs(&pd[field_offset]));
788 field_length = pd[field_offset];
789 proto_tree_add_text(ncp_tree, field_offset,
790 field_length + 1, rec->description, field_length,
791 &pd[field_offset+1]);
795 field_length = rec->length;
796 proto_tree_add_text(ncp_tree, field_offset,
797 field_length, rec->description);
804 field_offset += field_length;
810 ncp_completion_code(guint8 ccode, enum nfamily family)
814 #define NCP_CCODE_MIN 0x7e
815 #define NCP_CCODE_MAX 0xff
817 /* From Appendix C of "Programmer's Guide to NetWare Core Protocol" */
818 static char *ccode_text[] = {
819 /* 7e */ "NCP boundary check failed",
821 /* 80 */ "Lock fail. The file is already open",
822 /* 81 */ "A file handle could not be allocated by the file server",
823 /* 82 */ "Unauthorized to open file",
824 /* 83 */ "Unable to read/write the volume. Possible bad sector on the file server",
825 /* 84 */ "Unauthorized to create the file",
828 /* 87 */ "An unexpected character was encountered in the filename",
829 /* 88 */ "FileHandle is not valid",
830 /* 89 */ "Unauthorized to search this directory",
831 /* 8a */ "Unauthorized to delete a file in this directory",
832 /* 8b */ "Unauthorized to rename a file in this directory",
833 /* 8c */ "Unauthorized to modify a file in this directory",
834 /* 8d */ "Some of the affected files are in use by another client",
835 /* 8e */ "All of the affected files are in use by another client",
836 /* 8f */ "Some of the affected file are read only",
838 /* 91 */ "Some of the affected files already exist",
839 /* 92 */ "All of the affected files already exist",
840 /* 93 */ "Unauthorized to read from this file",
841 /* 94 */ "Unauthorized to write to this file",
842 /* 95 */ "The affected file is detached",
843 /* 96 */ "The file server has run out of memory to service this request",
845 /* 98 */ "The affected volume is not mounted",
846 /* 99 */ "The file server has run out of directory space on the affected volume",
847 /* 9a */ "The request attempted to rename the affected file to another volume",
848 /* 9b */ "DirHandle is not associated with a valid directory path",
850 /* 9d */ "A directory handle was not available for allocation",
851 /* 9e */ "The filename does not conform to a legal name for this name space",
852 /* 9f */ "The request attempted to delete a directory that is in use by another client",
853 /* a0 */ "The request attempted to delete a directory that is not empty",
854 /* a1 */ "An unrecoverable error occurred on the affected directory",
855 /* a2 */ "The request attempted to read from a file region that is physically locked",
884 /* bf */ "Requests for this name space are not valid on this volume",
885 /* c0 */ "Unauthorized to retrieve accounting data",
886 /* c1 */ "The 'account balance' property does not exist",
887 /* c2 */ "The object has exceeded its credit limit",
888 /* c3 */ "Too many holds have been placed against this account",
889 /* c4 */ "The account for this bindery object has been disabled",
890 /* c5 */ "Access to the account has been denied because of intruder detections",
891 /* c6 */ "The caller does not have operator privileges",
901 /* d0 */ "Queue error",
902 /* d1 */ "The queue associated with Object ID does not exist",
903 /* d2 */ "A queue server is not associated with the selected queue",
904 /* d3 */ "No queue rights",
905 /* d4 */ "The queue associated with Object ID is full and cannot accept another request",
906 /* d5 */ "The job associated with Job Number does not exist in this queue",
909 /* d8 */ "Queue not active",
915 /* de */ "Attempted to login to the file server with an incorrect password",
916 /* df */ "Attempted to login to the file server with a password that has expired",
924 /* e7 */ "No disk track",
927 /* ea */ "The bindery object is not a member of this set",
928 /* eb */ "The property is not a set property",
929 /* ec */ "The set property does not exist",
930 /* ed */ "The property already exists",
931 /* ee */ "The bindery object already exists",
932 /* ef */ "Illegal characters in Object Name field",
933 /* f0 */ "A wildcard was detected in a field that does not support wildcards",
934 /* f1 */ "The client does not have the rights to access this bindery objecs",
935 /* f2 */ "Unauthorized to read from this object",
936 /* f3 */ "Unauthorized to rename this object",
937 /* f4 */ "Unauthorized to delete this object",
938 /* f5 */ "Unauthorized to create this object",
939 /* f6 */ "Unauthorized to delete the property of this object",
940 /* f7 */ "Unauthorized to create this property",
941 /* f8 */ "Unauthorized to write to this property",
942 /* f9 */ "Unauthorized to read this property",
943 /* fa */ "Temporary remap error",
957 return "Client not accepting messages";
961 if (ccode >= NCP_CCODE_MIN && ccode <= NCP_CCODE_MAX) {
962 text = ccode_text[ccode - NCP_CCODE_MIN];
963 /* If there really is text, return it */
971 /* We have a completion code with multiple translations. We'll use the
972 * nfamily that this request type belongs to to give the right
979 case NCP_QUEUE_SERVICES:
980 return "The message queue cannot accept another message";
982 case NCP_BINDERY_SERVICES:
983 return "The specified bindery object does not exist";
992 return "I don't know how to parse this completion code. Please send this packet trace to Gilbert Ramirez <gram@xiexie.org> for analysis";
997 proto_register_ncp(void)
1000 static hf_register_info hf[] = {
1002 { "NCP over IP signature", "ncp.ip.signature",
1003 FT_UINT32, BASE_HEX, VALS(ncp_ip_signature), 0x0,
1004 "NCP over IP transport signature"}},
1006 { "Version", "ncp.ip.version",
1007 FT_UINT32, BASE_DEC, NULL, 0x0,
1008 "NCP over IP verion"}},
1010 { "Type", "ncp.type",
1011 FT_UINT16, BASE_HEX, NULL, 0x0,
1012 "NCP message type" }},
1014 { "Sequence Number", "ncp.seq",
1015 FT_UINT8, BASE_DEC, NULL, 0x0,
1017 { &hf_ncp_connection,
1018 { "Connection Number", "ncp.connection",
1019 FT_UINT16, BASE_DEC, NULL, 0x0,
1022 { "Task Number", "ncp.task",
1023 FT_UINT8, BASE_DEC, NULL, 0x0,
1026 static gint *ett[] = {
1028 &ett_ncp_request_fields,
1029 &ett_ncp_reply_fields,
1032 proto_ncp = proto_register_protocol("NetWare Core Protocol", "ncp");
1033 proto_register_field_array(proto_ncp, hf, array_length(hf));
1034 proto_register_subtree_array(ett, array_length(ett));
1035 register_init_routine(&ncp_init_protocol);