2 * Routines for NetWare Core Protocol
3 * Gilbert Ramirez <gram@verdict.uthscsa.edu>
5 * $Id: packet-ncp.c,v 1.17 1999/07/13 02:52:52 gram Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@unicom.net>
9 * Copyright 1998 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
33 #ifdef HAVE_SYS_TYPES_H
34 # include <sys/types.h>
37 #ifdef HAVE_NETINET_IN_H
38 # include <netinet/in.h>
43 #include "packet-ipx.h"
44 #include "packet-ncp.h"
49 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree);
52 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree);
54 static struct ncp2222_record *
55 ncp2222_find(guint8 func, guint8 subfunc);
58 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
59 struct svc_record *svc);
63 gint ncp_equal (gconstpointer v, gconstpointer v2);
64 guint ncp_hash (gconstpointer v);
66 int ncp_packet_init_count = 200;
68 /* The information in this module comes from:
69 NetWare LAN Analysis, Second Edition
70 Laura A. Chappell and Dan E. Hakes
72 Novell Press, San Jose.
75 And from the ncpfs source code by Volker Lendecke
78 Programmer's Guide to the NetWare Core Protocol
79 Steve Conner & Diane Conner
80 (c) 1996 by Steve Conner & Diane Conner
81 Published by Annabooks, San Diego, California
86 /* Every NCP packet has this common header */
87 struct ncp_common_header {
95 /* NCP request packets */
96 struct ncp_request_header {
107 /* NCP reply packets */
108 struct ncp_reply_header {
114 guint8 completion_code;
115 guint8 connection_state;
119 static value_string request_reply_values[] = {
120 { 0x1111, "Create a service connection" },
121 { 0x2222, "Service request" },
122 { 0x3333, "Service reply" },
123 { 0x5555, "Destroy service connection" },
124 { 0x7777, "Burst mode transfer" },
125 { 0x9999, "Request being processed" },
129 /* These are the field types in an NCP packet */
131 nend, /* end of the NCP field list */
132 nbyte, /* one byte of data */
133 nhex, /* bytes to be shown as hex digits */
134 nbelong, /* 4-byte big-endian long int */
135 nbeshort, /* 2-byte big-endian short int */
136 ndata, /* unstructured data */
137 nbytevar, /* a variable number of bytes */
138 ndatetime, /* date-time stamp */
139 nasciile, /* length-encoded ASCII string. First byte is length */
140 nasciiz /* null-terminated string of ASCII characters */
143 /* These are the broad families that the different NCP request types belong
147 NCP_UNKNOWN_SERVICE, /* unknown or n/a */
148 NCP_QUEUE_SERVICES, /* print queues */
149 NCP_FILE_SERVICES, /* file serving */
150 NCP_BINDERY_SERVICES, /* bindery database */
151 NCP_CONNECTION_SERVICES, /* communication */
154 /* I had to put this function prototype after the enum nfamily declaration */
156 ncp_completion_code(guint8 ccode, enum nfamily family);
159 /* Information on the NCP field */
160 typedef struct svc_record {
162 guint8 length; /* max-length for variable-sized fields */
166 typedef struct ncp2222_record {
169 guint8 submask; /* Does this function have subfunctions?
170 * SUBFUNC or NOSUB */
180 /* ------------------------------------------------------------ */
182 /* Get Bindery Object ID REQUEST */
183 static svc_record ncp_17_35_C[] = {
184 { nbeshort, 2, "Object Type: 0x%04x" },
185 { nasciile, 48, "Object Name: %.*s" },
188 /* Get Bindery Object ID REPLY has no fields*/
191 /* Service Queue Job REQUEST */
192 static svc_record ncp_17_7C_C[] = {
193 { nbelong, 4, "The queue the job resides in" },
194 { nbeshort, 2, "Job Type" },
197 /* Service Queue Job REPLY */
198 static svc_record ncp_17_7C_R[] = {
199 { nbelong, 4, "Client station number: %d" },
200 { nbelong, 4, "Task Number: %d" },
201 { nbelong, 4, "User: %d" },
202 { nbelong, 4, "Server specifed to service queue entry: %08X" },
203 { ndatetime, 6, "Earliest time to execute" },
204 { ndatetime, 6, "When job entered queue" },
205 { nbelong, 4, "Job Number" },
206 { nbeshort, 2, "Job Type" },
207 { nbeshort, 2, "Job Position" },
208 { nbeshort, 2, "Current status of job: 0x%02x" },
209 { nasciiz, 14, "Name of file" },
210 { nbelong, 4, "File handle" },
211 { nbelong, 4, "Client station number" },
212 { nbelong, 4, "Task number" },
213 { nbelong, 4, "Job server" },
219 /* Negotiate Buffer Size REQUEST */
220 static svc_record ncp_21_00_C[] = {
221 { nbeshort, 2, "Caller's maximum packet size: %d bytes" },
224 /* Negotiate Buffer Size RESPONSE */
225 static svc_record ncp_21_00_R[] = {
226 { nbeshort, 2, "Packet size decided upon by file server: %d bytes" },
231 /* Close File REQUEST */
232 static svc_record ncp_42_00_C[] = {
233 { nhex, 6, "File Handle: 02x:02x:02x:02x:02x:02x"},
236 /* Close File RESPONSE */
237 static svc_record ncp_42_00_R[] = {
242 /* Read from a file REQUEST */
243 static svc_record ncp_48_00_C[] = {
244 { nbyte, 1, "Unknown" },
245 { nhex, 6, "File Handle" },
246 { nbelong, 4, "Byte offset within file" },
247 { nbeshort, 2, "Maximum data bytes to return" },
251 static svc_record ncp_48_00_R[] = {
252 { nbeshort, 2, "Data bytes returned" },
253 { nbytevar, 1, "Padding" },
257 /* ------------------------------------------------------------ */
258 /* Any svc_record that has no fields is not created.
259 * Store a NULL in the ncp2222_record instead */
264 static ncp2222_record ncp2222[] = {
266 { 0x17, 0x35, SUBFUNC, "Get Bindery Object ID",
267 ncp_17_35_C, NULL, NCP_BINDERY_SERVICES
270 { 0x17, 0x7C, SUBFUNC, "Service Queue Job",
271 ncp_17_7C_C, ncp_17_7C_R, NCP_QUEUE_SERVICES
274 { 0x18, 0x00, NOSUB, "End of Job",
275 NULL, NULL, NCP_CONNECTION_SERVICES
278 { 0x19, 0x00, NOSUB, "Logout",
279 NULL, NULL, NCP_CONNECTION_SERVICES
282 { 0x21, 0x00, NOSUB, "Negotiate Buffer Size",
283 ncp_21_00_C, ncp_21_00_R, NCP_CONNECTION_SERVICES
286 { 0x42, 0x00, NOSUB, "Close File",
287 ncp_42_00_C, ncp_42_00_R, NCP_FILE_SERVICES
290 { 0x48, 0x00, NOSUB, "Read from a file",
291 ncp_48_00_C, ncp_48_00_R, NCP_FILE_SERVICES
294 { 0x00, 0x00, NOSUB, NULL,
295 NULL, NULL, NCP_UNKNOWN_SERVICE
301 /* NCP packets come in request/reply pairs. The request packets tell the type
302 * of NCP request and give a sequence ID. The response, unfortunately, only
303 * identifies itself via the sequence ID; you have to know what type of NCP
304 * request the request packet contained in order to successfully parse the NCP
305 * response. A global method for doing this does not exist in ethereal yet
306 * (NFS also requires it), so for now the NCP section will keep its own hash
307 * table keeping track of NCP packet types.
309 * The key representing the unique NCP request is composed of 3 variables:
311 * ServerIPXNetwork.Connection.SequenceNumber
312 * 4 bytes 2 bytes 1 byte
313 * guint32 guint16 guint8 (all are host order)
315 * This assumes that all NCP connection is between a client and server.
316 * Servers can be identified by having a 00:00:00:00:00:01 IPX Node address.
317 * We have to let the IPX layer pass us the ServerIPXNetwork via a global
318 * variable (nw_server_address). In the future, if we decode NCP over TCP/UDP,
319 * then nw_server_address will represent the IP address of the server, which
320 * conveniently, is also 4 bytes long.
322 * The value stored in the hash table is the ncp_request_val pointer. This
323 * struct tells us the NCP type and gives the ncp2222_record pointer, if
324 * ncp_type == 0x2222.
326 guint32 nw_server_address = 0; /* set by IPX layer */
327 guint16 nw_connection = 0; /* set by dissect_ncp */
328 guint8 nw_sequence = 0; /* set by dissect_ncp */
329 guint16 nw_ncp_type = 0; /* set by dissect_ncp */
331 struct ncp_request_key {
332 guint32 nw_server_address;
333 guint16 nw_connection;
337 struct ncp_request_val {
339 struct ncp2222_record* ncp_record;
342 GHashTable *ncp_request_hash = NULL;
343 GMemChunk *ncp_request_keys = NULL;
344 GMemChunk *ncp_request_records = NULL;
347 gint ncp_equal (gconstpointer v, gconstpointer v2)
349 struct ncp_request_key *val1 = (struct ncp_request_key*)v;
350 struct ncp_request_key *val2 = (struct ncp_request_key*)v2;
352 #if defined(DEBUG_NCP_HASH)
353 printf("Comparing %08X:%d:%d and %08X:%d:%d\n",
354 val1->nw_server_address, val1->nw_connection, val1->nw_sequence,
355 val2->nw_server_address, val2->nw_connection, val2->nw_sequence);
358 if (val1->nw_server_address == val2->nw_server_address &&
359 val1->nw_connection == val2->nw_connection &&
360 val1->nw_sequence == val2->nw_sequence ) {
366 guint ncp_hash (gconstpointer v)
368 struct ncp_request_key *ncp_key = (struct ncp_request_key*)v;
369 #if defined(DEBUG_NCP_HASH)
370 printf("hash calculated as %d\n", ncp_key->nw_server_address +
371 ((guint32) ncp_key->nw_connection << 16) +
372 ncp_key->nw_sequence);
374 return ncp_key->nw_server_address +
375 ((guint32) ncp_key->nw_connection << 16) +
376 ncp_key->nw_sequence;
379 /* Initializes the hash table and the mem_chunk area each time a new
380 * file is loaded or re-loaded in ethereal */
382 ncp_init_protocol(void)
384 #if defined(DEBUG_NCP_HASH)
385 printf("Initializing NCP hashtable and mem_chunk area\n");
387 if (ncp_request_hash)
388 g_hash_table_destroy(ncp_request_hash);
389 if (ncp_request_keys)
390 g_mem_chunk_destroy(ncp_request_keys);
391 if (ncp_request_records)
392 g_mem_chunk_destroy(ncp_request_records);
394 ncp_request_hash = g_hash_table_new(ncp_hash, ncp_equal);
395 ncp_request_keys = g_mem_chunk_new("ncp_request_keys",
396 sizeof(struct ncp_request_key),
397 ncp_packet_init_count * sizeof(struct ncp_request_key), G_ALLOC_AND_FREE);
398 ncp_request_records = g_mem_chunk_new("ncp_request_records",
399 sizeof(struct ncp_request_val),
400 ncp_packet_init_count * sizeof(struct ncp_request_val), G_ALLOC_AND_FREE);
403 static struct ncp2222_record *
404 ncp2222_find(guint8 func, guint8 subfunc)
406 struct ncp2222_record *ncp_record, *retval = NULL;
408 ncp_record = ncp2222;
410 while(ncp_record->func != 0) {
411 if (ncp_record->func == func &&
412 ncp_record->subfunc == (subfunc & ncp_record->submask)) {
423 dissect_ncp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
426 proto_tree *ncp_tree = NULL;
428 int ncp_hdr_length = 0;
429 struct ncp_common_header header;
431 memcpy(&header, &pd[offset], sizeof(header));
432 header.type = ntohs(header.type);
434 if (header.type == 0x1111 ||
435 header.type == 0x2222 ||
436 header.type == 0x5555 ||
437 header.type == 0x7777) {
440 else if (header.type == 0x3333 || header.type == 0x9999) {
444 if (check_col(fd, COL_PROTOCOL))
445 col_add_str(fd, COL_PROTOCOL, "NCP");
447 nw_connection = (header.conn_high << 16) + header.conn_low;
448 nw_sequence = header.sequence;
449 nw_ncp_type = header.type;
452 ti = proto_tree_add_text(tree, offset, END_OF_FRAME,
453 "NetWare Core Protocol");
454 ncp_tree = proto_item_add_subtree(ti, ETT_NCP);
456 proto_tree_add_text(ncp_tree, offset, 2,
457 "Type: %s", val_to_str( header.type,
458 request_reply_values, "Unknown (%04X)"));
460 proto_tree_add_text(ncp_tree, offset+2, 1,
461 "Sequence Number: %d", header.sequence);
463 proto_tree_add_text(ncp_tree, offset+3, 3,
464 "Connection Number: %d", nw_connection);
466 proto_tree_add_text(ncp_tree, offset+4, 1,
467 "Task Number: %d", header.task);
470 /* Note how I use ncp_tree *and* tree in my args for ncp request/reply */
471 if (ncp_hdr_length == 7)
472 dissect_ncp_request(pd, offset, fd, ncp_tree, tree);
473 else if (ncp_hdr_length == 8)
474 dissect_ncp_reply(pd, offset, fd, ncp_tree, tree);
476 dissect_data(pd, offset, fd, tree);
480 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd,
481 proto_tree *ncp_tree, proto_tree *tree) {
483 struct ncp_request_header request;
484 struct ncp2222_record *ncp_request;
485 gchar *description = "";
486 struct ncp_request_val *request_val;
487 struct ncp_request_key *request_key;
488 proto_tree *field_tree = NULL;
489 proto_item *ti = NULL;
491 /*memcpy(&request, &pd[offset], sizeof(request));*/
492 request.function = pd[offset+6];
493 request.subfunc = pd[offset+9];
495 ncp_request = ncp2222_find(request.function, request.subfunc);
498 description = ncp_request->funcname;
500 if (check_col(fd, COL_INFO)) {
501 if (description[0]) {
502 col_add_fstr(fd, COL_INFO, "C %s", description);
505 col_add_fstr(fd, COL_INFO, "C Unknown Function %02X/%02X",
506 request.function, request.subfunc);
511 proto_tree_add_text(ncp_tree, offset+6, 1,
512 "Function Code: 0x%02X (%s)",
513 request.function, description);
517 if (ncp_request->submask == SUBFUNC) {
518 proto_tree_add_text(ncp_tree, offset+7, 2,
519 "Packet Length: %d bytes", pntohs(&pd[offset+7]));
520 proto_tree_add_text(ncp_tree, offset+9, 1,
521 "Subfunction Code: 0x%02x", pd[offset+9]);
528 if (ncp_request->req) {
529 ti = proto_tree_add_text(ncp_tree, offset, END_OF_FRAME,
530 "NCP Request Packet");
531 field_tree = proto_item_add_subtree(ti, ETT_NCP_REQUEST_FIELDS);
533 parse_ncp_svc_fields(pd, field_tree, offset, ncp_request->req);
538 request_key = g_mem_chunk_alloc(ncp_request_keys);
539 request_key->nw_server_address = nw_server_address;
540 request_key->nw_connection = nw_connection;
541 request_key->nw_sequence = nw_sequence;
543 request_val = g_mem_chunk_alloc(ncp_request_records);
544 request_val->ncp_type = nw_ncp_type;
545 request_val->ncp_record = ncp2222_find(request.function, request.subfunc);
547 g_hash_table_insert(ncp_request_hash, request_key, request_val);
548 #if defined(DEBUG_NCP_HASH)
549 printf("Inserted server %08X connection %d sequence %d (val=%08X)\n",
550 nw_server_address, nw_connection, nw_sequence, request_val);
557 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd,
558 proto_tree *ncp_tree, proto_tree *tree) {
560 struct ncp_reply_header reply;
561 struct ncp2222_record *ncp_request = NULL;
562 struct ncp_request_val *request_val;
563 struct ncp_request_key request_key;
564 proto_tree *field_tree = NULL;
565 proto_item *ti = NULL;
567 memcpy(&reply, &pd[offset], sizeof(reply));
569 /* find the record telling us the request made that caused this reply */
570 request_key.nw_server_address = nw_server_address;
571 request_key.nw_connection = nw_connection;
572 request_key.nw_sequence = nw_sequence;
574 request_val = (struct ncp_request_val*)
575 g_hash_table_lookup(ncp_request_hash, &request_key);
577 #if defined(DEBUG_NCP_HASH)
578 printf("Looking for server %08X connection %d sequence %d (retval=%08X)\n",
579 nw_server_address, nw_connection, nw_sequence, request_val);
583 ncp_request = request_val->ncp_record;
585 if (check_col(fd, COL_INFO)) {
586 if (reply.completion_code == 0) {
587 col_add_fstr(fd, COL_INFO, "R OK");
590 col_add_fstr(fd, COL_INFO, "R Not OK");
595 /* A completion code of 0 always means OK. Other values have different
598 proto_tree_add_text(ncp_tree, offset+6, 1,
599 "Completion Code: 0x%02x (%s)", reply.completion_code,
600 ncp_completion_code(reply.completion_code, ncp_request->family));
603 proto_tree_add_text(ncp_tree, offset+6, 1,
604 "Completion Code: 0x%02x (%s)", reply.completion_code,
605 reply.completion_code == 0 ? "OK" : "Unknown");
608 proto_tree_add_text(ncp_tree, offset+7, 1,
609 "Connection Status: %d", reply.connection_state);
613 if (ncp_request->rep) {
614 ti = proto_tree_add_text(ncp_tree, offset+8, END_OF_FRAME,
616 field_tree = proto_item_add_subtree(ti, ETT_NCP_REPLY_FIELDS);
618 parse_ncp_svc_fields(pd, field_tree, offset+8, ncp_request->rep);
625 /* Populates the protocol tree with information about the svc_record fields */
627 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
628 struct svc_record *svc)
630 struct svc_record *rec = svc;
631 int field_offset = offset;
632 int field_length = 0;
634 while (rec->type != nend) {
638 proto_tree_add_text(ncp_tree, field_offset,
639 field_length, rec->description, pntohs(&pd[field_offset]));
643 field_length = pd[field_offset];
644 proto_tree_add_text(ncp_tree, field_offset,
645 field_length + 1, rec->description, field_length,
646 &pd[field_offset+1]);
650 field_length = rec->length;
651 proto_tree_add_text(ncp_tree, field_offset,
652 field_length, rec->description);
659 field_offset += field_length;
665 ncp_completion_code(guint8 ccode, enum nfamily family)
669 #define NCP_CCODE_MIN 0x7e
670 #define NCP_CCODE_MAX 0xff
672 /* From Appendix C of "Programmer's Guide to NetWare Core Protocol" */
673 static char *ccode_text[] = {
674 /* 7e */ "NCP boundary check failed",
676 /* 80 */ "Lock fail. The file is already open",
677 /* 81 */ "A file handle could not be allocated by the file server",
678 /* 82 */ "Unauthorized to open file",
679 /* 83 */ "Unable to read/write the volume. Possible bad sector on the file server",
680 /* 84 */ "Unauthorized to create the file",
683 /* 87 */ "An unexpected character was encountered in the filename",
684 /* 88 */ "FileHandle is not valid",
685 /* 89 */ "Unauthorized to search this directory",
686 /* 8a */ "Unauthorized to delete a file in this directory",
687 /* 8b */ "Unauthorized to rename a file in this directory",
688 /* 8c */ "Unauthorized to modify a file in this directory",
689 /* 8d */ "Some of the affected files are in use by another client",
690 /* 8e */ "All of the affected files are in use by another client",
691 /* 8f */ "Some of the affected file are read only",
693 /* 91 */ "Some of the affected files already exist",
694 /* 92 */ "All of the affected files already exist",
695 /* 93 */ "Unauthorized to read from this file",
696 /* 94 */ "Unauthorized to write to this file",
697 /* 95 */ "The affected file is detached",
698 /* 96 */ "The file server has run out of memory to service this request",
700 /* 98 */ "The affected volume is not mounted",
701 /* 99 */ "The file server has run out of directory space on the affected volume",
702 /* 9a */ "The request attempted to rename the affected file to another volume",
703 /* 9b */ "DirHandle is not associated with a valid directory path",
705 /* 9d */ "A directory handle was not available for allocation",
706 /* 9e */ "The filename does not conform to a legal name for this name space",
707 /* 9f */ "The request attempted to delete a directory that is in use by another client",
708 /* a0 */ "The request attempted to delete a directory that is not empty",
709 /* a1 */ "An unrecoverable error occurred on the affected directory",
710 /* a2 */ "The request attempted to read from a file region that is physically locked",
739 /* bf */ "Requests for this name space are not valid on this volume",
740 /* c0 */ "Unauthorized to retrieve accounting data",
741 /* c1 */ "The 'account balance' property does not exist",
742 /* c2 */ "The object has exceeded its credit limit",
743 /* c3 */ "Too many holds have been placed against this account",
744 /* c4 */ "The account for this bindery object has been disabled",
745 /* c5 */ "Access to the account has been denied because of intruder detections",
746 /* c6 */ "The caller does not have operator privileges",
756 /* d0 */ "Queue error",
757 /* d1 */ "The queue associated with Object ID does not exist",
758 /* d2 */ "A queue server is not associated with the selected queue",
759 /* d3 */ "No queue rights",
760 /* d4 */ "The queue associated with Object ID is full and cannot accept another request",
761 /* d5 */ "The job associated with Job Number does not exist in this queue",
764 /* d8 */ "Queue not active",
770 /* de */ "Attempted to login to the file server with an incorrect password",
771 /* df */ "Attempted to login to the file server with a password that has expired",
779 /* e7 */ "No disk track",
782 /* ea */ "The bindery object is not a member of this set",
783 /* eb */ "The property is not a set property",
784 /* ec */ "The set property does not exist",
785 /* ed */ "The property already exists",
786 /* ee */ "The bindery object already exists",
787 /* ef */ "Illegal characters in Object Name field",
788 /* f0 */ "A wildcard was detected in a field that does not support wildcards",
789 /* f1 */ "The client does not have the rights to access this bindery objecs",
790 /* f2 */ "Unauthorized to read from this object",
791 /* f3 */ "Unauthorized to rename this object",
792 /* f4 */ "Unauthorized to delete this object",
793 /* f5 */ "Unauthorized to create this object",
794 /* f6 */ "Unauthorized to delete the property of this object",
795 /* f7 */ "Unauthorized to create this property",
796 /* f8 */ "Unauthorized to write to this property",
797 /* f9 */ "Unauthorized to read this property",
798 /* fa */ "Temporary remap error",
812 return "Client not accepting messages";
816 if (ccode >= NCP_CCODE_MIN && ccode <= NCP_CCODE_MAX) {
817 text = ccode_text[ccode - NCP_CCODE_MIN];
818 /* If there really is text, return it */
826 /* We have a completion code with multiple translations. We'll use the
827 * nfamily that this request type belongs to to give the right
834 case NCP_QUEUE_SERVICES:
835 return "The message queue cannot accept another message";
837 case NCP_BINDERY_SERVICES:
838 return "The specified bindery object does not exist";
847 return "I don't know how to parse this completion code. Please send this packet trace to Gilbert Ramirez <gram@xiexie.org> for analysis";