2 * Routines for NetWare Core Protocol
3 * Gilbert Ramirez <gram@verdict.uthscsa.edu>
4 * Modified to allow NCP over TCP/IP decodes by James Coe <jammer@cin.net>
6 * $Id: packet-ncp.c,v 1.24 1999/12/07 06:09:59 guy Exp $
8 * Ethereal - Network traffic analyzer
9 * By Gerald Combs <gerald@unicom.net>
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 { 0x17, 0x35, SUBFUNC, "Get Bindery Object ID",
300 ncp_17_35_C, NULL, NCP_BINDERY_SERVICES
303 { 0x17, 0x7C, SUBFUNC, "Service Queue Job",
304 ncp_17_7C_C, ncp_17_7C_R, NCP_QUEUE_SERVICES
307 { 0x18, 0x00, NOSUB, "End of Job",
308 NULL, NULL, NCP_CONNECTION_SERVICES
311 { 0x19, 0x00, NOSUB, "Logout",
312 NULL, NULL, NCP_CONNECTION_SERVICES
315 { 0x21, 0x00, NOSUB, "Negotiate Buffer Size",
316 ncp_21_00_C, ncp_21_00_R, NCP_CONNECTION_SERVICES
319 { 0x42, 0x00, NOSUB, "Close File",
320 ncp_42_00_C, ncp_42_00_R, NCP_FILE_SERVICES
323 { 0x48, 0x00, NOSUB, "Read from a file",
324 ncp_48_00_C, ncp_48_00_R, NCP_FILE_SERVICES
327 { 0x00, 0x00, NOSUB, NULL,
328 NULL, NULL, NCP_UNKNOWN_SERVICE
334 /* NCP packets come in request/reply pairs. The request packets tell the type
335 * of NCP request and give a sequence ID. The response, unfortunately, only
336 * identifies itself via the sequence ID; you have to know what type of NCP
337 * request the request packet contained in order to successfully parse the NCP
338 * response. A global method for doing this does not exist in ethereal yet
339 * (NFS also requires it), so for now the NCP section will keep its own hash
340 * table keeping track of NCP packet types.
342 * The key representing the unique NCP request is composed of 3 variables:
344 * ServerIPXNetwork.Connection.SequenceNumber
345 * 4 bytes 2 bytes 1 byte
346 * guint32 guint16 guint8 (all are host order)
348 * This assumes that all NCP connection is between a client and server.
349 * Servers can be identified by having a 00:00:00:00:00:01 IPX Node address.
350 * We have to let the IPX layer pass us the ServerIPXNetwork via a global
351 * variable (nw_server_address). In the future, if we decode NCP over TCP/UDP,
352 * then nw_server_address will represent the IP address of the server, which
353 * conveniently, is also 4 bytes long.
355 * The value stored in the hash table is the ncp_request_val pointer. This
356 * struct tells us the NCP type and gives the ncp2222_record pointer, if
357 * ncp_type == 0x2222.
359 guint32 nw_server_address = 0; /* set by IPX layer */
360 guint16 nw_connection = 0; /* set by dissect_ncp */
361 guint8 nw_sequence = 0; /* set by dissect_ncp */
362 guint16 nw_ncp_type = 0; /* set by dissect_ncp */
364 struct ncp_request_key {
365 guint32 nw_server_address;
366 guint16 nw_connection;
370 struct ncp_request_val {
372 struct ncp2222_record* ncp_record;
375 GHashTable *ncp_request_hash = NULL;
376 GMemChunk *ncp_request_keys = NULL;
377 GMemChunk *ncp_request_records = NULL;
380 gint ncp_equal (gconstpointer v, gconstpointer v2)
382 struct ncp_request_key *val1 = (struct ncp_request_key*)v;
383 struct ncp_request_key *val2 = (struct ncp_request_key*)v2;
385 #if defined(DEBUG_NCP_HASH)
386 printf("Comparing %08X:%d:%d and %08X:%d:%d\n",
387 val1->nw_server_address, val1->nw_connection, val1->nw_sequence,
388 val2->nw_server_address, val2->nw_connection, val2->nw_sequence);
391 if (val1->nw_server_address == val2->nw_server_address &&
392 val1->nw_connection == val2->nw_connection &&
393 val1->nw_sequence == val2->nw_sequence ) {
399 guint ncp_hash (gconstpointer v)
401 struct ncp_request_key *ncp_key = (struct ncp_request_key*)v;
402 #if defined(DEBUG_NCP_HASH)
403 printf("hash calculated as %d\n", ncp_key->nw_server_address +
404 ((guint32) ncp_key->nw_connection << 16) +
405 ncp_key->nw_sequence);
407 return ncp_key->nw_server_address +
408 ((guint32) ncp_key->nw_connection << 16) +
409 ncp_key->nw_sequence;
412 /* Initializes the hash table and the mem_chunk area each time a new
413 * file is loaded or re-loaded in ethereal */
415 ncp_init_protocol(void)
417 #if defined(DEBUG_NCP_HASH)
418 printf("Initializing NCP hashtable and mem_chunk area\n");
420 if (ncp_request_hash)
421 g_hash_table_destroy(ncp_request_hash);
422 if (ncp_request_keys)
423 g_mem_chunk_destroy(ncp_request_keys);
424 if (ncp_request_records)
425 g_mem_chunk_destroy(ncp_request_records);
427 ncp_request_hash = g_hash_table_new(ncp_hash, ncp_equal);
428 ncp_request_keys = g_mem_chunk_new("ncp_request_keys",
429 sizeof(struct ncp_request_key),
430 ncp_packet_init_count * sizeof(struct ncp_request_key), G_ALLOC_AND_FREE);
431 ncp_request_records = g_mem_chunk_new("ncp_request_records",
432 sizeof(struct ncp_request_val),
433 ncp_packet_init_count * sizeof(struct ncp_request_val), G_ALLOC_AND_FREE);
436 static struct ncp2222_record *
437 ncp2222_find(guint8 func, guint8 subfunc)
439 struct ncp2222_record *ncp_record, *retval = NULL;
441 ncp_record = ncp2222;
443 while(ncp_record->func != 0) {
444 if (ncp_record->func == func &&
445 ncp_record->subfunc == (subfunc & ncp_record->submask)) {
456 dissect_ncp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
458 proto_tree *ncp_tree = NULL;
460 int ncp_hdr_length = 0;
461 struct ncp_ip_header ncpiph;
462 struct ncp_ip_rqhdr ncpiphrq;
463 struct ncp_common_header header;
465 if ( pi.ptype == PT_TCP || pi.ptype == PT_UDP ) {
466 memcpy(&ncpiph, &pd[offset], sizeof(ncpiph));
467 ncpiph.signature = ntohl(ncpiph.signature);
468 ncpiph.length = ntohl(ncpiph.length);
470 if ( ncpiph.signature == NCPIP_RQST ) {
471 memcpy(&ncpiphrq, &pd[offset], sizeof(ncpiphrq));
472 ncpiphrq.rplybufsize = ntohl(ncpiphrq.rplybufsize);
476 memcpy(&header, &pd[offset], sizeof(header));
477 header.type = ntohs(header.type);
479 if (header.type == 0x1111 ||
480 header.type == 0x2222 ||
481 header.type == 0x5555 ||
482 header.type == 0x7777) {
485 else if (header.type == 0x3333 || header.type == 0x9999) {
489 if (check_col(fd, COL_PROTOCOL))
490 col_add_str(fd, COL_PROTOCOL, "NCP");
492 nw_connection = (header.conn_high << 16) + header.conn_low;
493 nw_sequence = header.sequence;
494 nw_ncp_type = header.type;
497 ti = proto_tree_add_item(tree, proto_ncp, offset, END_OF_FRAME, NULL);
498 ncp_tree = proto_item_add_subtree(ti, ett_ncp);
500 if ( pi.ptype == PT_TCP || pi.ptype == PT_UDP ) {
501 proto_tree_add_item(ncp_tree, hf_ncp_ip_sig, offset - 16, 4, ncpiph.signature);
502 proto_tree_add_text(ncp_tree, offset - 12, 4, "Length: %d", ncpiph.length);
503 if ( ncpiph.signature == NCPIP_RQST ) {
504 proto_tree_add_item(ncp_tree, hf_ncp_ip_ver, offset - 8, 4, ncpiphrq.version);
505 proto_tree_add_text(ncp_tree, offset - 4, 4, "Reply buffer size: %d", ncpiphrq.rplybufsize);
508 proto_tree_add_item_format(ncp_tree, hf_ncp_type,
512 val_to_str( header.type,
513 request_reply_values,
516 proto_tree_add_item(ncp_tree, hf_ncp_seq,
517 offset+2, 1, header.sequence);
519 proto_tree_add_item(ncp_tree, hf_ncp_connection,
520 offset+3, 3, nw_connection);
522 proto_tree_add_item(ncp_tree, hf_ncp_task,
523 offset+4, 1, header.task);
526 /* Note how I use ncp_tree *and* tree in my args for ncp request/reply */
527 if (ncp_hdr_length == 7)
528 dissect_ncp_request(pd, offset, fd, ncp_tree, tree);
529 else if (ncp_hdr_length == 8)
530 dissect_ncp_reply(pd, offset, fd, ncp_tree, tree);
532 dissect_data(pd, offset, fd, tree);
536 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd,
537 proto_tree *ncp_tree, proto_tree *tree) {
539 struct ncp_request_header request;
540 struct ncp2222_record *ncp_request;
541 gchar *description = "";
542 struct ncp_request_val *request_val;
543 struct ncp_request_key *request_key;
544 proto_tree *field_tree = NULL;
545 proto_item *ti = NULL;
547 /*memcpy(&request, &pd[offset], sizeof(request));*/
548 request.function = pd[offset+6];
549 request.subfunc = pd[offset+9];
551 ncp_request = ncp2222_find(request.function, request.subfunc);
554 description = ncp_request->funcname;
556 if (check_col(fd, COL_INFO)) {
557 if (description[0]) {
558 col_add_fstr(fd, COL_INFO, "C %s", description);
561 col_add_fstr(fd, COL_INFO, "C Unknown Function %02X/%02X",
562 request.function, request.subfunc);
567 proto_tree_add_text(ncp_tree, offset+6, 1,
568 "Function Code: 0x%02X (%s)",
569 request.function, description);
573 if (ncp_request->submask == SUBFUNC) {
574 proto_tree_add_text(ncp_tree, offset+7, 2,
575 "Packet Length: %d bytes", pntohs(&pd[offset+7]));
576 proto_tree_add_text(ncp_tree, offset+9, 1,
577 "Subfunction Code: 0x%02x", pd[offset+9]);
584 if (ncp_request->req) {
585 ti = proto_tree_add_text(ncp_tree, offset, END_OF_FRAME,
586 "NCP Request Packet");
587 field_tree = proto_item_add_subtree(ti, ett_ncp_request_fields);
589 parse_ncp_svc_fields(pd, field_tree, offset, ncp_request->req);
594 request_key = g_mem_chunk_alloc(ncp_request_keys);
595 request_key->nw_server_address = nw_server_address;
596 request_key->nw_connection = nw_connection;
597 request_key->nw_sequence = nw_sequence;
599 request_val = g_mem_chunk_alloc(ncp_request_records);
600 request_val->ncp_type = nw_ncp_type;
601 request_val->ncp_record = ncp2222_find(request.function, request.subfunc);
603 g_hash_table_insert(ncp_request_hash, request_key, request_val);
604 #if defined(DEBUG_NCP_HASH)
605 printf("Inserted server %08X connection %d sequence %d (val=%08X)\n",
606 nw_server_address, nw_connection, nw_sequence, request_val);
613 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd,
614 proto_tree *ncp_tree, proto_tree *tree) {
616 struct ncp_reply_header reply;
617 struct ncp2222_record *ncp_request = NULL;
618 struct ncp_request_val *request_val;
619 struct ncp_request_key request_key;
620 proto_tree *field_tree = NULL;
621 proto_item *ti = NULL;
623 memcpy(&reply, &pd[offset], sizeof(reply));
625 /* find the record telling us the request made that caused this reply */
626 request_key.nw_server_address = nw_server_address;
627 request_key.nw_connection = nw_connection;
628 request_key.nw_sequence = nw_sequence;
630 request_val = (struct ncp_request_val*)
631 g_hash_table_lookup(ncp_request_hash, &request_key);
633 #if defined(DEBUG_NCP_HASH)
634 printf("Looking for server %08X connection %d sequence %d (retval=%08X)\n",
635 nw_server_address, nw_connection, nw_sequence, request_val);
639 ncp_request = request_val->ncp_record;
641 if (check_col(fd, COL_INFO)) {
642 if (reply.completion_code == 0) {
643 col_add_fstr(fd, COL_INFO, "R OK");
646 col_add_fstr(fd, COL_INFO, "R Not OK");
651 /* A completion code of 0 always means OK. Other values have different
654 proto_tree_add_text(ncp_tree, offset+6, 1,
655 "Completion Code: 0x%02x (%s)", reply.completion_code,
656 ncp_completion_code(reply.completion_code, ncp_request->family));
659 proto_tree_add_text(ncp_tree, offset+6, 1,
660 "Completion Code: 0x%02x (%s)", reply.completion_code,
661 reply.completion_code == 0 ? "OK" : "Unknown");
664 proto_tree_add_text(ncp_tree, offset+7, 1,
665 "Connection Status: %d", reply.connection_state);
669 if (ncp_request->rep) {
670 ti = proto_tree_add_text(ncp_tree, offset+8, END_OF_FRAME,
672 field_tree = proto_item_add_subtree(ti, ett_ncp_reply_fields);
674 parse_ncp_svc_fields(pd, field_tree, offset+8, ncp_request->rep);
681 /* Populates the protocol tree with information about the svc_record fields */
683 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
684 struct svc_record *svc)
686 struct svc_record *rec = svc;
687 int field_offset = offset;
688 int field_length = 0;
690 while (rec->type != nend) {
694 proto_tree_add_text(ncp_tree, field_offset,
695 field_length, rec->description, pntohs(&pd[field_offset]));
699 field_length = pd[field_offset];
700 proto_tree_add_text(ncp_tree, field_offset,
701 field_length + 1, rec->description, field_length,
702 &pd[field_offset+1]);
706 field_length = rec->length;
707 proto_tree_add_text(ncp_tree, field_offset,
708 field_length, rec->description);
715 field_offset += field_length;
721 ncp_completion_code(guint8 ccode, enum nfamily family)
725 #define NCP_CCODE_MIN 0x7e
726 #define NCP_CCODE_MAX 0xff
728 /* From Appendix C of "Programmer's Guide to NetWare Core Protocol" */
729 static char *ccode_text[] = {
730 /* 7e */ "NCP boundary check failed",
732 /* 80 */ "Lock fail. The file is already open",
733 /* 81 */ "A file handle could not be allocated by the file server",
734 /* 82 */ "Unauthorized to open file",
735 /* 83 */ "Unable to read/write the volume. Possible bad sector on the file server",
736 /* 84 */ "Unauthorized to create the file",
739 /* 87 */ "An unexpected character was encountered in the filename",
740 /* 88 */ "FileHandle is not valid",
741 /* 89 */ "Unauthorized to search this directory",
742 /* 8a */ "Unauthorized to delete a file in this directory",
743 /* 8b */ "Unauthorized to rename a file in this directory",
744 /* 8c */ "Unauthorized to modify a file in this directory",
745 /* 8d */ "Some of the affected files are in use by another client",
746 /* 8e */ "All of the affected files are in use by another client",
747 /* 8f */ "Some of the affected file are read only",
749 /* 91 */ "Some of the affected files already exist",
750 /* 92 */ "All of the affected files already exist",
751 /* 93 */ "Unauthorized to read from this file",
752 /* 94 */ "Unauthorized to write to this file",
753 /* 95 */ "The affected file is detached",
754 /* 96 */ "The file server has run out of memory to service this request",
756 /* 98 */ "The affected volume is not mounted",
757 /* 99 */ "The file server has run out of directory space on the affected volume",
758 /* 9a */ "The request attempted to rename the affected file to another volume",
759 /* 9b */ "DirHandle is not associated with a valid directory path",
761 /* 9d */ "A directory handle was not available for allocation",
762 /* 9e */ "The filename does not conform to a legal name for this name space",
763 /* 9f */ "The request attempted to delete a directory that is in use by another client",
764 /* a0 */ "The request attempted to delete a directory that is not empty",
765 /* a1 */ "An unrecoverable error occurred on the affected directory",
766 /* a2 */ "The request attempted to read from a file region that is physically locked",
795 /* bf */ "Requests for this name space are not valid on this volume",
796 /* c0 */ "Unauthorized to retrieve accounting data",
797 /* c1 */ "The 'account balance' property does not exist",
798 /* c2 */ "The object has exceeded its credit limit",
799 /* c3 */ "Too many holds have been placed against this account",
800 /* c4 */ "The account for this bindery object has been disabled",
801 /* c5 */ "Access to the account has been denied because of intruder detections",
802 /* c6 */ "The caller does not have operator privileges",
812 /* d0 */ "Queue error",
813 /* d1 */ "The queue associated with Object ID does not exist",
814 /* d2 */ "A queue server is not associated with the selected queue",
815 /* d3 */ "No queue rights",
816 /* d4 */ "The queue associated with Object ID is full and cannot accept another request",
817 /* d5 */ "The job associated with Job Number does not exist in this queue",
820 /* d8 */ "Queue not active",
826 /* de */ "Attempted to login to the file server with an incorrect password",
827 /* df */ "Attempted to login to the file server with a password that has expired",
835 /* e7 */ "No disk track",
838 /* ea */ "The bindery object is not a member of this set",
839 /* eb */ "The property is not a set property",
840 /* ec */ "The set property does not exist",
841 /* ed */ "The property already exists",
842 /* ee */ "The bindery object already exists",
843 /* ef */ "Illegal characters in Object Name field",
844 /* f0 */ "A wildcard was detected in a field that does not support wildcards",
845 /* f1 */ "The client does not have the rights to access this bindery objecs",
846 /* f2 */ "Unauthorized to read from this object",
847 /* f3 */ "Unauthorized to rename this object",
848 /* f4 */ "Unauthorized to delete this object",
849 /* f5 */ "Unauthorized to create this object",
850 /* f6 */ "Unauthorized to delete the property of this object",
851 /* f7 */ "Unauthorized to create this property",
852 /* f8 */ "Unauthorized to write to this property",
853 /* f9 */ "Unauthorized to read this property",
854 /* fa */ "Temporary remap error",
868 return "Client not accepting messages";
872 if (ccode >= NCP_CCODE_MIN && ccode <= NCP_CCODE_MAX) {
873 text = ccode_text[ccode - NCP_CCODE_MIN];
874 /* If there really is text, return it */
882 /* We have a completion code with multiple translations. We'll use the
883 * nfamily that this request type belongs to to give the right
890 case NCP_QUEUE_SERVICES:
891 return "The message queue cannot accept another message";
893 case NCP_BINDERY_SERVICES:
894 return "The specified bindery object does not exist";
903 return "I don't know how to parse this completion code. Please send this packet trace to Gilbert Ramirez <gram@xiexie.org> for analysis";
908 proto_register_ncp(void)
911 static hf_register_info hf[] = {
913 { "NCP over IP signature", "ncp.ip.signature",
914 FT_UINT32, BASE_HEX, VALS(ncp_ip_signature), 0x0,
915 "NCP over IP transport signature"}},
917 { "Version", "ncp.ip.version",
918 FT_UINT32, BASE_DEC, NULL, 0x0,
919 "NCP over IP verion"}},
921 { "Type", "ncp.type",
922 FT_UINT16, BASE_HEX, NULL, 0x0,
923 "NCP message type" }},
925 { "Sequence Number", "ncp.seq",
926 FT_UINT8, BASE_DEC, NULL, 0x0,
928 { &hf_ncp_connection,
929 { "Connection Number", "ncp.connection",
930 FT_UINT16, BASE_DEC, NULL, 0x0,
933 { "Task Number", "ncp.task",
934 FT_UINT8, BASE_DEC, NULL, 0x0,
937 static gint *ett[] = {
939 &ett_ncp_request_fields,
940 &ett_ncp_reply_fields,
943 proto_ncp = proto_register_protocol("NetWare Core Protocol", "ncp");
944 proto_register_field_array(proto_ncp, hf, array_length(hf));
945 proto_register_subtree_array(ett, array_length(ett));
946 register_init_routine(&ncp_init_protocol);