Have the IPX code set "pi.len" and "pi.captured_len" based on the length
[obnox/wireshark/wip.git] / packet-ncp.c
1 /* packet-ncp.c
2  * Routines for NetWare Core Protocol
3  * Gilbert Ramirez <gram@verdict.uthscsa.edu>
4  *
5  * $Id: packet-ncp.c,v 1.19 1999/09/02 23:17:57 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@unicom.net>
9  * Copyright 1998 Gerald Combs
10  *
11  * 
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.
16  * 
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.
21  * 
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.
25  */
26
27 #undef DEBUG_NCP_HASH
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #ifdef HAVE_SYS_TYPES_H
34 # include <sys/types.h>
35 #endif
36
37 #ifdef HAVE_NETINET_IN_H
38 # include <netinet/in.h>
39 #endif
40
41 #include <glib.h>
42 #include "packet.h"
43 #include "packet-ipx.h"
44 #include "packet-ncp.h"
45
46 static int proto_ncp = -1;
47
48 struct svc_record;
49
50 static void
51 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree);
52
53 static void
54 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree);
55
56 static struct ncp2222_record *
57 ncp2222_find(guint8 func, guint8 subfunc);
58
59 static void
60 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
61         struct svc_record *svc);
62
63
64 /* Hash functions */
65 gint  ncp_equal (gconstpointer v, gconstpointer v2);
66 guint ncp_hash  (gconstpointer v);
67
68 int ncp_packet_init_count = 200;
69
70 /* The information in this module comes from:
71         NetWare LAN Analysis, Second Edition
72         Laura A. Chappell and Dan E. Hakes
73         (c) 1994 Novell, Inc.
74         Novell Press, San Jose.
75         ISBN: 0-7821-1362-1
76
77   And from the ncpfs source code by Volker Lendecke
78
79   And:
80         Programmer's Guide to the NetWare Core Protocol
81         Steve Conner & Diane Conner
82         (c) 1996 by Steve Conner & Diane Conner
83         Published by Annabooks, San Diego, California
84         ISBN: 0-929392-31-0
85
86 */
87
88 /* Every NCP packet has this common header */
89 struct ncp_common_header {
90         guint16 type;
91         guint8  sequence;
92         guint8  conn_low;
93         guint8  task;
94         guint8  conn_high;
95 };
96
97 /* NCP request packets */
98 struct ncp_request_header {
99         guint16 type;
100         guint8  sequence;
101         guint8  conn_low;
102         guint8  task;
103         guint8  conn_high;
104         guint8  function;
105         guint16 length;
106         guint8  subfunc;
107 };
108
109 /* NCP reply packets */
110 struct ncp_reply_header {
111         guint16 type;
112         guint8  sequence;
113         guint8  conn_low;
114         guint8  task;
115         guint8  conn_high;
116         guint8  completion_code;
117         guint8  connection_state;
118 };
119
120
121 static value_string request_reply_values[] = {
122         { 0x1111, "Create a service connection" },
123         { 0x2222, "Service request" },
124         { 0x3333, "Service reply" },
125         { 0x5555, "Destroy service connection" },
126         { 0x7777, "Burst mode transfer" },
127         { 0x9999, "Request being processed" },
128         { 0x0000, NULL }
129 };
130
131 /* These are the field types in an NCP packet */
132 enum ntype {
133         nend,           /* end of the NCP field list */
134         nbyte,          /* one byte of data */
135         nhex,           /* bytes to be shown as hex digits */
136         nbelong,        /* 4-byte big-endian long int */
137         nbeshort,       /* 2-byte big-endian short int */
138         ndata,          /* unstructured data */
139         nbytevar,       /* a variable number of bytes */
140         ndatetime,      /* date-time stamp */
141         nasciile,       /* length-encoded ASCII string. First byte is length */
142         nasciiz         /* null-terminated string of ASCII characters */
143 };
144
145 /* These are the broad families that the different NCP request types belong
146  * to.
147  */
148 enum nfamily {
149                 NCP_UNKNOWN_SERVICE,    /* unknown or n/a */
150                 NCP_QUEUE_SERVICES,             /* print queues */
151                 NCP_FILE_SERVICES,              /* file serving */
152                 NCP_BINDERY_SERVICES,   /* bindery database */
153                 NCP_CONNECTION_SERVICES, /* communication */
154 };
155
156 /* I had to put this function prototype after the enum nfamily declaration */
157 static char*
158 ncp_completion_code(guint8 ccode, enum nfamily family);
159
160
161 /* Information on the NCP field */
162 typedef struct svc_record {
163         enum ntype      type;
164         guint8          length; /* max-length for variable-sized fields */
165         gchar           *description;
166 } svc_record;
167
168 typedef struct ncp2222_record {
169         guint8          func;
170         guint8          subfunc;
171         guint8          submask;        /* Does this function have subfunctions?
172                                          * SUBFUNC or NOSUB */
173         gchar           *funcname;
174
175         svc_record      *req;
176         svc_record      *rep;
177         enum nfamily    family;
178
179 } ncp2222_record;
180
181
182 /* ------------------------------------------------------------ */
183
184 /* Get Bindery Object ID REQUEST */
185 static svc_record ncp_17_35_C[] = {
186                 { nbeshort,     2,      "Object Type: 0x%04x" },
187                 { nasciile,     48,     "Object Name: %.*s" },
188                 { nend,         0,      NULL }
189 };
190 /* Get Bindery Object ID REPLY has no fields*/
191
192
193 /* Service Queue Job REQUEST */
194 static svc_record ncp_17_7C_C[] = {
195                 { nbelong,      4,      "The queue the job resides in" },
196                 { nbeshort,     2,      "Job Type" },
197                 { nend,         0,      NULL }
198 };
199 /* Service Queue Job REPLY */
200 static svc_record ncp_17_7C_R[] = {
201                 { nbelong,      4,      "Client station number: %d" },
202                 { nbelong,      4,      "Task Number: %d" },
203                 { nbelong,      4,      "User: %d" },
204                 { nbelong,      4,      "Server specifed to service queue entry: %08X" },
205                 { ndatetime,    6,      "Earliest time to execute" },
206                 { ndatetime,    6,      "When job entered queue" },
207                 { nbelong,      4,      "Job Number" },
208                 { nbeshort,     2,      "Job Type" },
209                 { nbeshort,     2,      "Job Position" },
210                 { nbeshort,     2,      "Current status of job: 0x%02x" },
211                 { nasciiz,      14,     "Name of file" },
212                 { nbelong,      4,      "File handle" },
213                 { nbelong,      4,      "Client station number" },
214                 { nbelong,      4,      "Task number" },
215                 { nbelong,      4,      "Job server" },
216                 { nend,         0,      NULL }
217 };
218
219
220
221 /* Negotiate Buffer Size REQUEST */
222 static svc_record ncp_21_00_C[] = {
223                 { nbeshort,     2,      "Caller's maximum packet size: %d bytes" },
224                 { nend,         0,      NULL }
225 };
226 /* Negotiate Buffer Size RESPONSE */
227 static svc_record ncp_21_00_R[] = {
228                 { nbeshort,     2,      "Packet size decided upon by file server: %d bytes" },
229                 { nend,         0,      NULL }
230 };
231
232
233 /* Close File REQUEST */
234 static svc_record ncp_42_00_C[] = {
235                 { nhex,         6,      "File Handle: 02x:02x:02x:02x:02x:02x"},
236                 { nend,         0,      NULL }
237 };
238 /* Close File RESPONSE */
239 static svc_record ncp_42_00_R[] = {
240                 { nend,         0,      NULL }
241 };
242
243
244 /* Read from a file REQUEST */
245 static svc_record ncp_48_00_C[] = {
246                 { nbyte,        1,      "Unknown" },
247                 { nhex,         6,      "File Handle" },
248                 { nbelong,      4,      "Byte offset within file" },
249                 { nbeshort,     2,      "Maximum data bytes to return" },
250                 { nend,         0,      NULL }
251 };
252 /* RESPONSE */
253 static svc_record ncp_48_00_R[] = {
254                 { nbeshort,     2,      "Data bytes returned" },
255                 { nbytevar,     1,      "Padding" },
256                 { ndata,        0,      NULL }
257 };
258
259 /* ------------------------------------------------------------ */
260 /* Any svc_record that has no fields is not created.
261  *  Store a NULL in the ncp2222_record instead */
262
263 #define SUBFUNC 0xff
264 #define NOSUB   0x00
265
266 static ncp2222_record ncp2222[] = {
267
268 { 0x17, 0x35, SUBFUNC, "Get Bindery Object ID",
269         ncp_17_35_C, NULL, NCP_BINDERY_SERVICES
270 },
271
272 { 0x17, 0x7C, SUBFUNC, "Service Queue Job",
273         ncp_17_7C_C, ncp_17_7C_R, NCP_QUEUE_SERVICES
274 },
275
276 { 0x18, 0x00, NOSUB, "End of Job",
277         NULL, NULL, NCP_CONNECTION_SERVICES
278 },
279
280 { 0x19, 0x00, NOSUB, "Logout",
281         NULL, NULL, NCP_CONNECTION_SERVICES
282 },
283
284 { 0x21, 0x00, NOSUB, "Negotiate Buffer Size",
285         ncp_21_00_C, ncp_21_00_R, NCP_CONNECTION_SERVICES
286 },
287
288 { 0x42, 0x00, NOSUB, "Close File",
289         ncp_42_00_C, ncp_42_00_R, NCP_FILE_SERVICES
290 },
291
292 { 0x48, 0x00, NOSUB, "Read from a file",
293         ncp_48_00_C, ncp_48_00_R, NCP_FILE_SERVICES
294 },
295
296 { 0x00, 0x00, NOSUB, NULL,
297         NULL, NULL, NCP_UNKNOWN_SERVICE
298 }
299
300 };
301
302
303 /* NCP packets come in request/reply pairs. The request packets tell the type
304  * of NCP request and give a sequence ID. The response, unfortunately, only
305  * identifies itself via the sequence ID; you have to know what type of NCP
306  * request the request packet contained in order to successfully parse the NCP
307  * response. A global method for doing this does not exist in ethereal yet
308  * (NFS also requires it), so for now the NCP section will keep its own hash
309  * table keeping track of NCP packet types.
310  *
311  * The key representing the unique NCP request is composed of 3 variables:
312  *
313  * ServerIPXNetwork.Connection.SequenceNumber
314  *     4 bytes        2 bytes      1 byte
315  *     guint32        guint16      guint8     (all are host order)
316  *
317  * This assumes that all NCP connection is between a client and server.
318  * Servers can be identified by having a 00:00:00:00:00:01 IPX Node address.
319  * We have to let the IPX layer pass us the ServerIPXNetwork via a global
320  * variable (nw_server_address). In the future, if we decode NCP over TCP/UDP,
321  * then nw_server_address will represent the IP address of the server, which
322  * conveniently, is also 4 bytes long.
323  *
324  * The value stored in the hash table is the ncp_request_val pointer. This
325  * struct tells us the NCP type and gives the ncp2222_record pointer, if
326  * ncp_type == 0x2222.
327  */
328 guint32 nw_server_address = 0; /* set by IPX layer */
329 guint16 nw_connection = 0; /* set by dissect_ncp */
330 guint8  nw_sequence = 0; /* set by dissect_ncp */
331 guint16 nw_ncp_type = 0; /* set by dissect_ncp */
332
333 struct ncp_request_key {
334         guint32 nw_server_address;
335         guint16 nw_connection;
336         guint8  nw_sequence;
337 };
338
339 struct ncp_request_val {
340         guint32                                 ncp_type;
341         struct ncp2222_record*  ncp_record;
342 };
343
344 GHashTable *ncp_request_hash = NULL;
345 GMemChunk *ncp_request_keys = NULL;
346 GMemChunk *ncp_request_records = NULL;
347
348 /* Hash Functions */
349 gint  ncp_equal (gconstpointer v, gconstpointer v2)
350 {
351         struct ncp_request_key  *val1 = (struct ncp_request_key*)v;
352         struct ncp_request_key  *val2 = (struct ncp_request_key*)v2;
353
354         #if defined(DEBUG_NCP_HASH)
355         printf("Comparing %08X:%d:%d and %08X:%d:%d\n",
356                 val1->nw_server_address, val1->nw_connection, val1->nw_sequence,
357                 val2->nw_server_address, val2->nw_connection, val2->nw_sequence);
358         #endif
359
360         if (val1->nw_server_address == val2->nw_server_address &&
361                 val1->nw_connection == val2->nw_connection &&
362                 val1->nw_sequence   == val2->nw_sequence ) {
363                 return 1;
364         }
365         return 0;
366 }
367
368 guint ncp_hash  (gconstpointer v)
369 {
370         struct ncp_request_key  *ncp_key = (struct ncp_request_key*)v;
371 #if defined(DEBUG_NCP_HASH)
372         printf("hash calculated as %d\n", ncp_key->nw_server_address +
373                         ((guint32) ncp_key->nw_connection << 16) +
374                         ncp_key->nw_sequence);
375 #endif
376         return ncp_key->nw_server_address +
377                         ((guint32) ncp_key->nw_connection << 16) +
378                         ncp_key->nw_sequence;
379 }
380
381 /* Initializes the hash table and the mem_chunk area each time a new
382  * file is loaded or re-loaded in ethereal */
383 void
384 ncp_init_protocol(void)
385 {
386         #if defined(DEBUG_NCP_HASH)
387         printf("Initializing NCP hashtable and mem_chunk area\n");
388         #endif
389         if (ncp_request_hash)
390                 g_hash_table_destroy(ncp_request_hash);
391         if (ncp_request_keys)
392                 g_mem_chunk_destroy(ncp_request_keys);
393         if (ncp_request_records)
394                 g_mem_chunk_destroy(ncp_request_records);
395
396         ncp_request_hash = g_hash_table_new(ncp_hash, ncp_equal);
397         ncp_request_keys = g_mem_chunk_new("ncp_request_keys",
398                         sizeof(struct ncp_request_key),
399                         ncp_packet_init_count * sizeof(struct ncp_request_key), G_ALLOC_AND_FREE);
400         ncp_request_records = g_mem_chunk_new("ncp_request_records",
401                         sizeof(struct ncp_request_val),
402                         ncp_packet_init_count * sizeof(struct ncp_request_val), G_ALLOC_AND_FREE);
403 }
404
405 static struct ncp2222_record *
406 ncp2222_find(guint8 func, guint8 subfunc)
407 {
408         struct ncp2222_record *ncp_record, *retval = NULL;
409
410         ncp_record = ncp2222;
411
412         while(ncp_record->func != 0) {
413                 if (ncp_record->func == func &&
414                         ncp_record->subfunc == (subfunc & ncp_record->submask)) {
415                         retval = ncp_record;
416                         break;
417                 }
418                 ncp_record++;
419         }
420
421         return retval;
422 }
423
424 void
425 dissect_ncp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
426
427         proto_tree      *ncp_tree = NULL;
428         proto_item      *ti;
429         int             ncp_hdr_length = 0;
430         struct ncp_common_header        header;
431
432         memcpy(&header, &pd[offset], sizeof(header));
433         header.type = ntohs(header.type);
434
435         if (header.type == 0x1111 ||
436                         header.type == 0x2222 ||
437                         header.type == 0x5555 ||
438                         header.type == 0x7777) {
439                 ncp_hdr_length = 7;
440         }
441         else if (header.type == 0x3333 || header.type == 0x9999) {
442                 ncp_hdr_length = 8;
443         }
444
445         if (check_col(fd, COL_PROTOCOL))
446                 col_add_str(fd, COL_PROTOCOL, "NCP");
447
448         nw_connection = (header.conn_high << 16) + header.conn_low;
449         nw_sequence = header.sequence;
450         nw_ncp_type = header.type;
451
452         if (tree) {
453                 ti = proto_tree_add_item(tree, proto_ncp, offset, END_OF_FRAME, NULL);
454                 ncp_tree = proto_item_add_subtree(ti, ETT_NCP);
455
456                 proto_tree_add_text(ncp_tree, offset,      2,
457                         "Type: %s", val_to_str( header.type,
458                         request_reply_values, "Unknown (%04X)"));
459
460                 proto_tree_add_text(ncp_tree, offset+2,    1,
461                         "Sequence Number: %d", header.sequence);
462
463                 proto_tree_add_text(ncp_tree, offset+3,    3,
464                         "Connection Number: %d", nw_connection);
465
466                 proto_tree_add_text(ncp_tree, offset+4,    1,
467                         "Task Number: %d", header.task);
468         }
469
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);
475         else
476                 dissect_data(pd, offset, fd, tree);
477 }
478
479 void
480 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd,
481         proto_tree *ncp_tree, proto_tree *tree) {
482
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;
490
491         /*memcpy(&request, &pd[offset], sizeof(request));*/
492         request.function = pd[offset+6];
493         request.subfunc = pd[offset+9];
494
495         ncp_request = ncp2222_find(request.function, request.subfunc);
496
497         if (ncp_request)
498                 description = ncp_request->funcname;
499
500         if (check_col(fd, COL_INFO)) {
501                 if (description[0]) {
502                         col_add_fstr(fd, COL_INFO, "C %s", description);
503                 }
504                 else {
505                         col_add_fstr(fd, COL_INFO, "C Unknown Function %02X/%02X",
506                                 request.function, request.subfunc);
507                 }
508         }
509
510         if (ncp_tree) {
511                 proto_tree_add_text(ncp_tree, offset+6, 1,
512                         "Function Code: 0x%02X (%s)",
513                         request.function, description);
514
515                 if (ncp_request) {
516
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]);
522                                 offset += 7 + 3;
523                         }
524                         else {
525                                 offset += 7;
526                         }
527
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);
532
533                                 parse_ncp_svc_fields(pd, field_tree, offset, ncp_request->req);
534                         }
535                 }
536         }
537         else { /* ! tree */
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;
542
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);
546
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);
551                 #endif
552         }
553
554 }
555
556 void
557 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd,
558         proto_tree *ncp_tree, proto_tree *tree) {
559
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;
566
567         memcpy(&reply, &pd[offset], sizeof(reply));
568
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;
573
574         request_val = (struct ncp_request_val*)
575                 g_hash_table_lookup(ncp_request_hash, &request_key);
576
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);
580         #endif
581
582         if (request_val)
583                 ncp_request = request_val->ncp_record;
584
585         if (check_col(fd, COL_INFO)) {
586                 if (reply.completion_code == 0) {
587                         col_add_fstr(fd, COL_INFO, "R OK");
588                 }
589                 else {
590                         col_add_fstr(fd, COL_INFO, "R Not OK");
591                 }
592         }
593
594         if (ncp_tree) {
595                 /* A completion code of 0 always means OK. Other values have different
596                  * meanings */
597                 if (ncp_request) {
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));
601                 }
602                 else {
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");
606                 }
607
608                 proto_tree_add_text(ncp_tree, offset+7,    1,
609                         "Connection Status: %d", reply.connection_state);
610
611                 if (ncp_request) {
612
613                         if (ncp_request->rep) {
614                                 ti = proto_tree_add_text(ncp_tree, offset+8, END_OF_FRAME,
615                                 "NCP Reply Packet");
616                                 field_tree = proto_item_add_subtree(ti, ETT_NCP_REPLY_FIELDS);
617
618                                 parse_ncp_svc_fields(pd, field_tree, offset+8, ncp_request->rep);
619                         }
620                 }
621         }
622
623 }
624
625 /* Populates the protocol tree with information about the svc_record fields */
626 static void
627 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
628         struct svc_record *svc)
629 {
630         struct svc_record *rec = svc;
631         int field_offset = offset;
632         int field_length = 0;
633
634         while (rec->type != nend) {
635                 switch(rec->type) {
636                         case nbeshort:
637                                 field_length = 2;
638                                 proto_tree_add_text(ncp_tree, field_offset,
639                                         field_length, rec->description, pntohs(&pd[field_offset]));
640                                 break;
641
642                         case nasciile:
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]);
647                                 break;
648
649                         case nhex:
650                                 field_length = rec->length;
651                                 proto_tree_add_text(ncp_tree, field_offset,
652                                         field_length, rec->description);
653                                 break;  
654
655                          default:
656                                 ; /* nothing */
657                                 break;
658                 }
659                 field_offset += field_length;
660                 rec++;
661         }       
662 }
663
664 static char*
665 ncp_completion_code(guint8 ccode, enum nfamily family)
666 {
667                 char    *text;
668
669 #define NCP_CCODE_MIN 0x7e
670 #define NCP_CCODE_MAX 0xff
671
672         /* From Appendix C of "Programmer's Guide to NetWare Core Protocol" */
673         static char     *ccode_text[] = {
674                 /* 7e */ "NCP boundary check failed",
675                 /* 7f */ "Unknown",
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",
681                 /* 85 */ "",
682                 /* 86 */ "Unknown",
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",
692                 /* 90 */ "",
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",
699                 /* 97 */ "Unknown",
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",
704                 /* 9c */ "",
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",
711                 /* a3 */ "Unknown",
712                 /* a4 */ "Unknown",
713                 /* a5 */ "Unknown",
714                 /* a6 */ "Unknown",
715                 /* a7 */ "Unknown",
716                 /* a8 */ "Unknown",
717                 /* a9 */ "Unknown",
718                 /* aa */ "Unknown",
719                 /* ab */ "Unknown",
720                 /* ac */ "Unknown",
721                 /* ad */ "Unknown",
722                 /* ae */ "Unknown",
723                 /* af */ "Unknown",
724                 /* b0 */ "Unknown",
725                 /* b1 */ "Unknown",
726                 /* b2 */ "Unknown",
727                 /* b3 */ "Unknown",
728                 /* b4 */ "Unknown",
729                 /* b5 */ "Unknown",
730                 /* b6 */ "Unknown",
731                 /* b7 */ "Unknown",
732                 /* b8 */ "Unknown",
733                 /* b9 */ "Unknown",
734                 /* ba */ "Unknown",
735                 /* bb */ "Unknown",
736                 /* bc */ "Unknown",
737                 /* bd */ "Unknown",
738                 /* be */ "Unknown",
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",
747                 /* c7 */ "Unknown",
748                 /* c8 */ "Unknown",
749                 /* c9 */ "Unknown",
750                 /* ca */ "Unknown",
751                 /* cb */ "Unknown",
752                 /* cc */ "Unknown",
753                 /* cd */ "Unknown",
754                 /* ce */ "Unknown",
755                 /* cf */ "Unknown",
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",
762                 /* d6 */ "",
763                 /* d7 */ "",
764                 /* d8 */ "Queue not active",
765                 /* d9 */ "",
766                 /* da */ "",
767                 /* db */ "",
768                 /* dc */ "Unknown",
769                 /* dd */ "Unknown",
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",
772                 /* e0 */ "Unknown",
773                 /* e1 */ "Unknown",
774                 /* e2 */ "Unknown",
775                 /* e3 */ "Unknown",
776                 /* e4 */ "Unknown",
777                 /* e5 */ "Unknown",
778                 /* e6 */ "Unknown",
779                 /* e7 */ "No disk track",
780                 /* e8 */ "",
781                 /* e9 */ "Unknown",
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",
799                 /* fb */ "",
800                 /* fc */ "",
801                 /* fd */ "",
802                 /* fe */ "",
803                 /* ff */ ""
804         };
805
806         switch (ccode) {
807                 case 0:
808                         return "OK";
809                         break;
810
811                 case 3:
812                         return "Client not accepting messages";
813                         break;
814         }
815
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 */
819                 if (text[0] != 0)
820                         return text;
821         }
822         else {
823                 return "Unknown";
824         }
825
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
828          * translation.
829          */
830         switch (ccode) {
831
832                 case 0xfc:
833                         switch(family) {
834                                 case NCP_QUEUE_SERVICES:
835                                         return "The message queue cannot accept another message";
836                                         break;
837                                 case NCP_BINDERY_SERVICES:
838                                         return "The specified bindery object does not exist";
839                                         break;
840                                 default:
841                                         return "Unknown";
842                                         break;
843                         }
844                         break;
845
846                 default:
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";
848         }
849 }
850
851 void
852 proto_register_ncp(void)
853 {
854 /*        static hf_register_info hf[] = {
855                 { &variable,
856                 { "Name",           "ncp.abbreviation", TYPE, VALS_POINTER }},
857         };*/
858
859         proto_ncp = proto_register_protocol("NetWare Core Protocol", "ncp");
860  /*       proto_register_field_array(proto_ncp, hf, array_length(hf));*/
861 }