Set "v120len" regardless of whether "tree" is null or not; we use it
[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  * Modified to allow NCP over TCP/IP decodes by James Coe <jammer@cin.net>
5  *
6  * $Id: packet-ncp.c,v 1.24 1999/12/07 06:09:59 guy Exp $
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@unicom.net>
10  * Copyright 1998 Gerald Combs
11  *
12  * 
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.
17  * 
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.
22  * 
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.
26  */
27
28 #undef DEBUG_NCP_HASH
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #ifdef HAVE_SYS_TYPES_H
35 # include <sys/types.h>
36 #endif
37
38 #ifdef HAVE_NETINET_IN_H
39 # include <netinet/in.h>
40 #endif
41
42 #include <glib.h>
43 #include "packet.h"
44 #include "packet-ipx.h"
45 #include "packet-ncp.h"
46
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;
54
55 static gint ett_ncp = -1;
56 static gint ett_ncp_request_fields = -1;
57 static gint ett_ncp_reply_fields = -1;
58
59 struct svc_record;
60
61 static void
62 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree);
63
64 static void
65 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree);
66
67 static struct ncp2222_record *
68 ncp2222_find(guint8 func, guint8 subfunc);
69
70 static void
71 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
72         struct svc_record *svc);
73
74
75 /* Hash functions */
76 gint  ncp_equal (gconstpointer v, gconstpointer v2);
77 guint ncp_hash  (gconstpointer v);
78
79 int ncp_packet_init_count = 200;
80
81 /* These are the header structures to handle NCP over IP */
82 #define NCPIP_RQST      0x446d6454      // "DmdT"
83 #define NCPIP_RPLY      0x744e6350      // "tNcP"
84
85 struct ncp_ip_header {
86         guint32 signature;
87         guint32 length;
88 };
89
90 /* This header only appears on NCP over IP request packets */
91 struct ncp_ip_rqhdr {
92         guint32 version;
93         guint32 rplybufsize;
94 };
95
96 static const value_string ncp_ip_signature[] = {
97         { NCPIP_RQST, "Demand Transport (Request)" },
98         { NCPIP_RPLY, "Transport is NCP (Reply)" },
99 };
100
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.
106         ISBN: 0-7821-1362-1
107
108   And from the ncpfs source code by Volker Lendecke
109
110   And:
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
115         ISBN: 0-929392-31-0
116
117 */
118
119 /* Every NCP packet has this common header */
120 struct ncp_common_header {
121         guint16 type;
122         guint8  sequence;
123         guint8  conn_low;
124         guint8  task;
125         guint8  conn_high;
126 };
127
128 /* NCP request packets */
129 struct ncp_request_header {
130         guint16 type;
131         guint8  sequence;
132         guint8  conn_low;
133         guint8  task;
134         guint8  conn_high;
135         guint8  function;
136         guint16 length;
137         guint8  subfunc;
138 };
139
140 /* NCP reply packets */
141 struct ncp_reply_header {
142         guint16 type;
143         guint8  sequence;
144         guint8  conn_low;
145         guint8  task;
146         guint8  conn_high;
147         guint8  completion_code;
148         guint8  connection_state;
149 };
150
151
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" },
159         { 0x0000, NULL }
160 };
161
162 /* These are the field types in an NCP packet */
163 enum ntype {
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 */
174 };
175
176 /* These are the broad families that the different NCP request types belong
177  * to.
178  */
179 enum nfamily {
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 */
185 };
186
187 /* I had to put this function prototype after the enum nfamily declaration */
188 static char*
189 ncp_completion_code(guint8 ccode, enum nfamily family);
190
191
192 /* Information on the NCP field */
193 typedef struct svc_record {
194         enum ntype      type;
195         guint8          length; /* max-length for variable-sized fields */
196         gchar           *description;
197 } svc_record;
198
199 typedef struct ncp2222_record {
200         guint8          func;
201         guint8          subfunc;
202         guint8          submask;        /* Does this function have subfunctions?
203                                          * SUBFUNC or NOSUB */
204         gchar           *funcname;
205
206         svc_record      *req;
207         svc_record      *rep;
208         enum nfamily    family;
209
210 } ncp2222_record;
211
212
213 /* ------------------------------------------------------------ */
214
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" },
219                 { nend,         0,      NULL }
220 };
221 /* Get Bindery Object ID REPLY has no fields*/
222
223
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" },
228                 { nend,         0,      NULL }
229 };
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" },
247                 { nend,         0,      NULL }
248 };
249
250
251
252 /* Negotiate Buffer Size REQUEST */
253 static svc_record ncp_21_00_C[] = {
254                 { nbeshort,     2,      "Caller's maximum packet size: %d bytes" },
255                 { nend,         0,      NULL }
256 };
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" },
260                 { nend,         0,      NULL }
261 };
262
263
264 /* Close File REQUEST */
265 static svc_record ncp_42_00_C[] = {
266                 { nhex,         6,      "File Handle: 02x:02x:02x:02x:02x:02x"},
267                 { nend,         0,      NULL }
268 };
269 /* Close File RESPONSE */
270 static svc_record ncp_42_00_R[] = {
271                 { nend,         0,      NULL }
272 };
273
274
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" },
281                 { nend,         0,      NULL }
282 };
283 /* RESPONSE */
284 static svc_record ncp_48_00_R[] = {
285                 { nbeshort,     2,      "Data bytes returned" },
286                 { nbytevar,     1,      "Padding" },
287                 { ndata,        0,      NULL }
288 };
289
290 /* ------------------------------------------------------------ */
291 /* Any svc_record that has no fields is not created.
292  *  Store a NULL in the ncp2222_record instead */
293
294 #define SUBFUNC 0xff
295 #define NOSUB   0x00
296
297 static ncp2222_record ncp2222[] = {
298
299 { 0x17, 0x35, SUBFUNC, "Get Bindery Object ID",
300         ncp_17_35_C, NULL, NCP_BINDERY_SERVICES
301 },
302
303 { 0x17, 0x7C, SUBFUNC, "Service Queue Job",
304         ncp_17_7C_C, ncp_17_7C_R, NCP_QUEUE_SERVICES
305 },
306
307 { 0x18, 0x00, NOSUB, "End of Job",
308         NULL, NULL, NCP_CONNECTION_SERVICES
309 },
310
311 { 0x19, 0x00, NOSUB, "Logout",
312         NULL, NULL, NCP_CONNECTION_SERVICES
313 },
314
315 { 0x21, 0x00, NOSUB, "Negotiate Buffer Size",
316         ncp_21_00_C, ncp_21_00_R, NCP_CONNECTION_SERVICES
317 },
318
319 { 0x42, 0x00, NOSUB, "Close File",
320         ncp_42_00_C, ncp_42_00_R, NCP_FILE_SERVICES
321 },
322
323 { 0x48, 0x00, NOSUB, "Read from a file",
324         ncp_48_00_C, ncp_48_00_R, NCP_FILE_SERVICES
325 },
326
327 { 0x00, 0x00, NOSUB, NULL,
328         NULL, NULL, NCP_UNKNOWN_SERVICE
329 }
330
331 };
332
333
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.
341  *
342  * The key representing the unique NCP request is composed of 3 variables:
343  *
344  * ServerIPXNetwork.Connection.SequenceNumber
345  *     4 bytes        2 bytes      1 byte
346  *     guint32        guint16      guint8     (all are host order)
347  *
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.
354  *
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.
358  */
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 */
363
364 struct ncp_request_key {
365         guint32 nw_server_address;
366         guint16 nw_connection;
367         guint8  nw_sequence;
368 };
369
370 struct ncp_request_val {
371         guint32                                 ncp_type;
372         struct ncp2222_record*  ncp_record;
373 };
374
375 GHashTable *ncp_request_hash = NULL;
376 GMemChunk *ncp_request_keys = NULL;
377 GMemChunk *ncp_request_records = NULL;
378
379 /* Hash Functions */
380 gint  ncp_equal (gconstpointer v, gconstpointer v2)
381 {
382         struct ncp_request_key  *val1 = (struct ncp_request_key*)v;
383         struct ncp_request_key  *val2 = (struct ncp_request_key*)v2;
384
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);
389         #endif
390
391         if (val1->nw_server_address == val2->nw_server_address &&
392                 val1->nw_connection == val2->nw_connection &&
393                 val1->nw_sequence   == val2->nw_sequence ) {
394                 return 1;
395         }
396         return 0;
397 }
398
399 guint ncp_hash  (gconstpointer v)
400 {
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);
406 #endif
407         return ncp_key->nw_server_address +
408                         ((guint32) ncp_key->nw_connection << 16) +
409                         ncp_key->nw_sequence;
410 }
411
412 /* Initializes the hash table and the mem_chunk area each time a new
413  * file is loaded or re-loaded in ethereal */
414 static void
415 ncp_init_protocol(void)
416 {
417         #if defined(DEBUG_NCP_HASH)
418         printf("Initializing NCP hashtable and mem_chunk area\n");
419         #endif
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);
426
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);
434 }
435
436 static struct ncp2222_record *
437 ncp2222_find(guint8 func, guint8 subfunc)
438 {
439         struct ncp2222_record *ncp_record, *retval = NULL;
440
441         ncp_record = ncp2222;
442
443         while(ncp_record->func != 0) {
444                 if (ncp_record->func == func &&
445                         ncp_record->subfunc == (subfunc & ncp_record->submask)) {
446                         retval = ncp_record;
447                         break;
448                 }
449                 ncp_record++;
450         }
451
452         return retval;
453 }
454
455 void
456 dissect_ncp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
457
458         proto_tree      *ncp_tree = NULL;
459         proto_item      *ti;
460         int             ncp_hdr_length = 0;
461         struct ncp_ip_header            ncpiph;
462         struct ncp_ip_rqhdr             ncpiphrq;
463         struct ncp_common_header        header;
464
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);
469                 offset += 8;
470                 if ( ncpiph.signature == NCPIP_RQST ) {
471                         memcpy(&ncpiphrq, &pd[offset], sizeof(ncpiphrq));
472                         ncpiphrq.rplybufsize = ntohl(ncpiphrq.rplybufsize);
473                         offset += 8;
474                 };
475         };
476         memcpy(&header, &pd[offset], sizeof(header));
477         header.type = ntohs(header.type);
478
479         if (header.type == 0x1111 ||
480                         header.type == 0x2222 ||
481                         header.type == 0x5555 ||
482                         header.type == 0x7777) {
483                 ncp_hdr_length = 7;
484         }
485         else if (header.type == 0x3333 || header.type == 0x9999) {
486                 ncp_hdr_length = 8;
487         }
488
489         if (check_col(fd, COL_PROTOCOL))
490                 col_add_str(fd, COL_PROTOCOL, "NCP");
491
492         nw_connection = (header.conn_high << 16) + header.conn_low;
493         nw_sequence = header.sequence;
494         nw_ncp_type = header.type;
495
496         if (tree) {
497                 ti = proto_tree_add_item(tree, proto_ncp, offset, END_OF_FRAME, NULL);
498                 ncp_tree = proto_item_add_subtree(ti, ett_ncp);
499
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);
506                         };
507                 };
508                 proto_tree_add_item_format(ncp_tree, hf_ncp_type, 
509                                            offset,      2,
510                                            header.type,
511                                            "Type: %s", 
512                                            val_to_str( header.type,
513                                                        request_reply_values,
514                                                        "Unknown (%04X)"));
515
516                 proto_tree_add_item(ncp_tree, hf_ncp_seq, 
517                                     offset+2,    1, header.sequence);
518
519                 proto_tree_add_item(ncp_tree, hf_ncp_connection,
520                                     offset+3,    3, nw_connection);
521
522                 proto_tree_add_item(ncp_tree, hf_ncp_task, 
523                                     offset+4,    1, header.task);
524         }
525
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);
531         else
532                 dissect_data(pd, offset, fd, tree);
533 }
534
535 void
536 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd,
537         proto_tree *ncp_tree, proto_tree *tree) {
538
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;
546
547         /*memcpy(&request, &pd[offset], sizeof(request));*/
548         request.function = pd[offset+6];
549         request.subfunc = pd[offset+9];
550
551         ncp_request = ncp2222_find(request.function, request.subfunc);
552
553         if (ncp_request)
554                 description = ncp_request->funcname;
555
556         if (check_col(fd, COL_INFO)) {
557                 if (description[0]) {
558                         col_add_fstr(fd, COL_INFO, "C %s", description);
559                 }
560                 else {
561                         col_add_fstr(fd, COL_INFO, "C Unknown Function %02X/%02X",
562                                 request.function, request.subfunc);
563                 }
564         }
565
566         if (ncp_tree) {
567                 proto_tree_add_text(ncp_tree, offset+6, 1,
568                         "Function Code: 0x%02X (%s)",
569                         request.function, description);
570
571                 if (ncp_request) {
572
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]);
578                                 offset += 7 + 3;
579                         }
580                         else {
581                                 offset += 7;
582                         }
583
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);
588
589                                 parse_ncp_svc_fields(pd, field_tree, offset, ncp_request->req);
590                         }
591                 }
592         }
593         else { /* ! tree */
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;
598
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);
602
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);
607                 #endif
608         }
609
610 }
611
612 void
613 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd,
614         proto_tree *ncp_tree, proto_tree *tree) {
615
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;
622
623         memcpy(&reply, &pd[offset], sizeof(reply));
624
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;
629
630         request_val = (struct ncp_request_val*)
631                 g_hash_table_lookup(ncp_request_hash, &request_key);
632
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);
636         #endif
637
638         if (request_val)
639                 ncp_request = request_val->ncp_record;
640
641         if (check_col(fd, COL_INFO)) {
642                 if (reply.completion_code == 0) {
643                         col_add_fstr(fd, COL_INFO, "R OK");
644                 }
645                 else {
646                         col_add_fstr(fd, COL_INFO, "R Not OK");
647                 }
648         }
649
650         if (ncp_tree) {
651                 /* A completion code of 0 always means OK. Other values have different
652                  * meanings */
653                 if (ncp_request) {
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));
657                 }
658                 else {
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");
662                 }
663
664                 proto_tree_add_text(ncp_tree, offset+7,    1,
665                         "Connection Status: %d", reply.connection_state);
666
667                 if (ncp_request) {
668
669                         if (ncp_request->rep) {
670                                 ti = proto_tree_add_text(ncp_tree, offset+8, END_OF_FRAME,
671                                 "NCP Reply Packet");
672                                 field_tree = proto_item_add_subtree(ti, ett_ncp_reply_fields);
673
674                                 parse_ncp_svc_fields(pd, field_tree, offset+8, ncp_request->rep);
675                         }
676                 }
677         }
678
679 }
680
681 /* Populates the protocol tree with information about the svc_record fields */
682 static void
683 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
684         struct svc_record *svc)
685 {
686         struct svc_record *rec = svc;
687         int field_offset = offset;
688         int field_length = 0;
689
690         while (rec->type != nend) {
691                 switch(rec->type) {
692                         case nbeshort:
693                                 field_length = 2;
694                                 proto_tree_add_text(ncp_tree, field_offset,
695                                         field_length, rec->description, pntohs(&pd[field_offset]));
696                                 break;
697
698                         case nasciile:
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]);
703                                 break;
704
705                         case nhex:
706                                 field_length = rec->length;
707                                 proto_tree_add_text(ncp_tree, field_offset,
708                                         field_length, rec->description);
709                                 break;  
710
711                          default:
712                                 ; /* nothing */
713                                 break;
714                 }
715                 field_offset += field_length;
716                 rec++;
717         }       
718 }
719
720 static char*
721 ncp_completion_code(guint8 ccode, enum nfamily family)
722 {
723                 char    *text;
724
725 #define NCP_CCODE_MIN 0x7e
726 #define NCP_CCODE_MAX 0xff
727
728         /* From Appendix C of "Programmer's Guide to NetWare Core Protocol" */
729         static char     *ccode_text[] = {
730                 /* 7e */ "NCP boundary check failed",
731                 /* 7f */ "Unknown",
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",
737                 /* 85 */ "",
738                 /* 86 */ "Unknown",
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",
748                 /* 90 */ "",
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",
755                 /* 97 */ "Unknown",
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",
760                 /* 9c */ "",
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",
767                 /* a3 */ "Unknown",
768                 /* a4 */ "Unknown",
769                 /* a5 */ "Unknown",
770                 /* a6 */ "Unknown",
771                 /* a7 */ "Unknown",
772                 /* a8 */ "Unknown",
773                 /* a9 */ "Unknown",
774                 /* aa */ "Unknown",
775                 /* ab */ "Unknown",
776                 /* ac */ "Unknown",
777                 /* ad */ "Unknown",
778                 /* ae */ "Unknown",
779                 /* af */ "Unknown",
780                 /* b0 */ "Unknown",
781                 /* b1 */ "Unknown",
782                 /* b2 */ "Unknown",
783                 /* b3 */ "Unknown",
784                 /* b4 */ "Unknown",
785                 /* b5 */ "Unknown",
786                 /* b6 */ "Unknown",
787                 /* b7 */ "Unknown",
788                 /* b8 */ "Unknown",
789                 /* b9 */ "Unknown",
790                 /* ba */ "Unknown",
791                 /* bb */ "Unknown",
792                 /* bc */ "Unknown",
793                 /* bd */ "Unknown",
794                 /* be */ "Unknown",
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",
803                 /* c7 */ "Unknown",
804                 /* c8 */ "Unknown",
805                 /* c9 */ "Unknown",
806                 /* ca */ "Unknown",
807                 /* cb */ "Unknown",
808                 /* cc */ "Unknown",
809                 /* cd */ "Unknown",
810                 /* ce */ "Unknown",
811                 /* cf */ "Unknown",
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",
818                 /* d6 */ "",
819                 /* d7 */ "",
820                 /* d8 */ "Queue not active",
821                 /* d9 */ "",
822                 /* da */ "",
823                 /* db */ "",
824                 /* dc */ "Unknown",
825                 /* dd */ "Unknown",
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",
828                 /* e0 */ "Unknown",
829                 /* e1 */ "Unknown",
830                 /* e2 */ "Unknown",
831                 /* e3 */ "Unknown",
832                 /* e4 */ "Unknown",
833                 /* e5 */ "Unknown",
834                 /* e6 */ "Unknown",
835                 /* e7 */ "No disk track",
836                 /* e8 */ "",
837                 /* e9 */ "Unknown",
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",
855                 /* fb */ "",
856                 /* fc */ "",
857                 /* fd */ "",
858                 /* fe */ "",
859                 /* ff */ ""
860         };
861
862         switch (ccode) {
863                 case 0:
864                         return "OK";
865                         break;
866
867                 case 3:
868                         return "Client not accepting messages";
869                         break;
870         }
871
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 */
875                 if (text[0] != 0)
876                         return text;
877         }
878         else {
879                 return "Unknown";
880         }
881
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
884          * translation.
885          */
886         switch (ccode) {
887
888                 case 0xfc:
889                         switch(family) {
890                                 case NCP_QUEUE_SERVICES:
891                                         return "The message queue cannot accept another message";
892                                         break;
893                                 case NCP_BINDERY_SERVICES:
894                                         return "The specified bindery object does not exist";
895                                         break;
896                                 default:
897                                         return "Unknown";
898                                         break;
899                         }
900                         break;
901
902                 default:
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";
904         }
905 }
906
907 void
908 proto_register_ncp(void)
909 {
910
911   static hf_register_info hf[] = {
912     { &hf_ncp_ip_sig,
913       { "NCP over IP signature",                "ncp.ip.signature",
914         FT_UINT32, BASE_HEX, VALS(ncp_ip_signature), 0x0,
915         "NCP over IP transport signature"}},
916     { &hf_ncp_ip_ver,
917       { "Version",              "ncp.ip.version",
918         FT_UINT32, BASE_DEC, NULL, 0x0,
919         "NCP over IP verion"}},
920     { &hf_ncp_type,
921       { "Type",                 "ncp.type",
922         FT_UINT16, BASE_HEX, NULL, 0x0,
923         "NCP message type" }},
924     { &hf_ncp_seq,
925       { "Sequence Number",      "ncp.seq",
926         FT_UINT8, BASE_DEC, NULL, 0x0,
927         "" }},
928     { &hf_ncp_connection,
929       { "Connection Number",    "ncp.connection",
930         FT_UINT16, BASE_DEC, NULL, 0x0,
931         "" }},
932     { &hf_ncp_task,
933       { "Task Number",          "ncp.task",
934         FT_UINT8, BASE_DEC, NULL, 0x0,
935         "" }}
936   };
937   static gint *ett[] = {
938     &ett_ncp,
939     &ett_ncp_request_fields,
940     &ett_ncp_reply_fields,
941   };
942
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);
947 }