Move the declarations of the routines in "gtk/file_dlg.c" out of
[obnox/wireshark/wip.git] / packet-ncp.c
1 /* packet-ncp.c
2  * Routines for NetWare Core Protocol
3  * Gilbert Ramirez <gram@xiexie.org>
4  * Modified to allow NCP over TCP/IP decodes by James Coe <jammer@cin.net>
5  *
6  * $Id: packet-ncp.c,v 1.30 2000/01/22 06:22:16 guy Exp $
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@zing.org>
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 { 0x00, 0x00, NOSUB, "Create service connection",
300         NULL, NULL, NCP_CONNECTION_SERVICES
301 },
302
303 { 0x14, 0x00, NOSUB, "Get server's clock",
304         NULL, NULL, NCP_FILE_SERVICES
305 },
306
307 { 0x16, 0x01, SUBFUNC, "Get path of directory handle",
308         NULL, NULL, NCP_FILE_SERVICES
309 },
310
311 { 0x16, 0x13, SUBFUNC, "Create temporary directory handle",
312         NULL, NULL, NCP_BINDERY_SERVICES
313 },
314
315 { 0x16, 0x0A, SUBFUNC, "Create directory",
316         NULL, NULL, NCP_BINDERY_SERVICES
317 },
318
319 { 0x16, 0x0D, SUBFUNC, "Add trustee to directory",
320         NULL, NULL, NCP_BINDERY_SERVICES
321 },
322
323 { 0x17, 0x11, SUBFUNC, "Get fileserver information",
324         NULL, NULL, NCP_BINDERY_SERVICES
325 },
326
327 { 0x17, 0x37, SUBFUNC, "Scan bindery object",
328         NULL, NULL, NCP_BINDERY_SERVICES
329 },
330
331 { 0x17, 0x36, SUBFUNC, "Get bindery object name",
332         NULL, NULL, NCP_BINDERY_SERVICES
333 },
334
335 { 0x17, 0x32, SUBFUNC, "Create bindery object",
336         NULL, NULL, NCP_BINDERY_SERVICES
337 },
338
339 { 0x17, 0x39, SUBFUNC, "Create property",
340         NULL, NULL, NCP_BINDERY_SERVICES
341 },
342
343 { 0x17, 0x41, SUBFUNC, "Add bindery object to set",
344         NULL, NULL, NCP_BINDERY_SERVICES
345 },
346
347 { 0x17, 0x43, SUBFUNC, "Is bindery object in set",
348         NULL, NULL, NCP_BINDERY_SERVICES
349 },
350
351 { 0x17, 0x35, SUBFUNC, "Get Bindery Object ID",
352         ncp_17_35_C, NULL, NCP_BINDERY_SERVICES
353 },
354
355 { 0x17, 0x7C, SUBFUNC, "Service Queue Job",
356         ncp_17_7C_C, ncp_17_7C_R, NCP_QUEUE_SERVICES
357 },
358
359 { 0x17, 0x3D, SUBFUNC, "Read property value",
360         NULL, NULL, NCP_FILE_SERVICES
361 },
362
363 { 0x18, 0x00, NOSUB, "End of Job",
364         NULL, NULL, NCP_CONNECTION_SERVICES
365 },
366
367 { 0x19, 0x00, NOSUB, "Logout",
368         NULL, NULL, NCP_CONNECTION_SERVICES
369 },
370
371 { 0x21, 0x00, NOSUB, "Negotiate Buffer Size",
372         ncp_21_00_C, ncp_21_00_R, NCP_CONNECTION_SERVICES
373 },
374
375 { 0x24, 0x00, SUBFUNC, "Destroy service connection",
376         NULL, NULL, NCP_CONNECTION_SERVICES
377 },
378
379 { 0x3E, 0x53, SUBFUNC, "Get alternate directory search paths",
380         NULL, NULL, NCP_FILE_SERVICES
381 },
382
383 { 0x3F, 0x89, SUBFUNC, "File search continue",
384         NULL, NULL, NCP_FILE_SERVICES
385 },
386
387 { 0x42, 0x00, NOSUB, "Close File",
388         ncp_42_00_C, ncp_42_00_R, NCP_FILE_SERVICES
389 },
390
391 { 0x48, 0x00, NOSUB, "Read from a file",
392         ncp_48_00_C, ncp_48_00_R, NCP_FILE_SERVICES
393 },
394
395 { 0x4C, 0x11, SUBFUNC, "Open file",
396         NULL, NULL, NCP_FILE_SERVICES
397 },
398
399 { 0x61, 0x00, NOSUB, "Get big packet NCP max packet size",
400         NULL, NULL, NCP_FILE_SERVICES
401 },
402
403 { 0x68, 0x01, SUBFUNC, "Ping for NDS NCP",
404         NULL, NULL, NCP_FILE_SERVICES
405 },
406
407 { 0x68, 0x02, SUBFUNC, "Send NDS fragmented message",
408         NULL, NULL, NCP_FILE_SERVICES
409 },
410
411 { 0x00, 0x00, NOSUB, NULL,
412         NULL, NULL, NCP_UNKNOWN_SERVICE
413 }
414
415 };
416
417
418 /* NCP packets come in request/reply pairs. The request packets tell the type
419  * of NCP request and give a sequence ID. The response, unfortunately, only
420  * identifies itself via the sequence ID; you have to know what type of NCP
421  * request the request packet contained in order to successfully parse the NCP
422  * response. A global method for doing this does not exist in ethereal yet
423  * (NFS also requires it), so for now the NCP section will keep its own hash
424  * table keeping track of NCP packet types.
425  *
426  * The key representing the unique NCP request is composed of 3 variables:
427  *
428  * ServerIPXNetwork.Connection.SequenceNumber
429  *     4 bytes        2 bytes      1 byte
430  *     guint32        guint16      guint8     (all are host order)
431  *
432  * This assumes that all NCP connection is between a client and server.
433  * Servers can be identified by having a 00:00:00:00:00:01 IPX Node address.
434  * We have to let the IPX layer pass us the ServerIPXNetwork via a global
435  * variable (nw_server_address). In the future, if we decode NCP over TCP/UDP,
436  * then nw_server_address will represent the IP address of the server, which
437  * conveniently, is also 4 bytes long.
438  *
439  * The value stored in the hash table is the ncp_request_val pointer. This
440  * struct tells us the NCP type and gives the ncp2222_record pointer, if
441  * ncp_type == 0x2222.
442  */
443 guint32 nw_server_address = 0; /* set by IPX layer */
444 guint16 nw_connection = 0; /* set by dissect_ncp */
445 guint8  nw_sequence = 0; /* set by dissect_ncp */
446 guint16 nw_ncp_type = 0; /* set by dissect_ncp */
447
448 struct ncp_request_key {
449         guint32 nw_server_address;
450         guint16 nw_connection;
451         guint8  nw_sequence;
452 };
453
454 struct ncp_request_val {
455         guint32                                 ncp_type;
456         struct ncp2222_record*  ncp_record;
457 };
458
459 GHashTable *ncp_request_hash = NULL;
460 GMemChunk *ncp_request_keys = NULL;
461 GMemChunk *ncp_request_records = NULL;
462
463 /* Hash Functions */
464 gint  ncp_equal (gconstpointer v, gconstpointer v2)
465 {
466         struct ncp_request_key  *val1 = (struct ncp_request_key*)v;
467         struct ncp_request_key  *val2 = (struct ncp_request_key*)v2;
468
469         #if defined(DEBUG_NCP_HASH)
470         printf("Comparing %08X:%d:%d and %08X:%d:%d\n",
471                 val1->nw_server_address, val1->nw_connection, val1->nw_sequence,
472                 val2->nw_server_address, val2->nw_connection, val2->nw_sequence);
473         #endif
474
475         if (val1->nw_server_address == val2->nw_server_address &&
476                 val1->nw_connection == val2->nw_connection &&
477                 val1->nw_sequence   == val2->nw_sequence ) {
478                 return 1;
479         }
480         return 0;
481 }
482
483 guint ncp_hash  (gconstpointer v)
484 {
485         struct ncp_request_key  *ncp_key = (struct ncp_request_key*)v;
486 #if defined(DEBUG_NCP_HASH)
487         printf("hash calculated as %d\n", ncp_key->nw_server_address +
488                         ((guint32) ncp_key->nw_connection << 16) +
489                         ncp_key->nw_sequence);
490 #endif
491         return ncp_key->nw_server_address +
492                         ((guint32) ncp_key->nw_connection << 16) +
493                         ncp_key->nw_sequence;
494 }
495
496 /* Initializes the hash table and the mem_chunk area each time a new
497  * file is loaded or re-loaded in ethereal */
498 static void
499 ncp_init_protocol(void)
500 {
501         #if defined(DEBUG_NCP_HASH)
502         printf("Initializing NCP hashtable and mem_chunk area\n");
503         #endif
504         if (ncp_request_hash)
505                 g_hash_table_destroy(ncp_request_hash);
506         if (ncp_request_keys)
507                 g_mem_chunk_destroy(ncp_request_keys);
508         if (ncp_request_records)
509                 g_mem_chunk_destroy(ncp_request_records);
510
511         ncp_request_hash = g_hash_table_new(ncp_hash, ncp_equal);
512         ncp_request_keys = g_mem_chunk_new("ncp_request_keys",
513                         sizeof(struct ncp_request_key),
514                         ncp_packet_init_count * sizeof(struct ncp_request_key), G_ALLOC_AND_FREE);
515         ncp_request_records = g_mem_chunk_new("ncp_request_records",
516                         sizeof(struct ncp_request_val),
517                         ncp_packet_init_count * sizeof(struct ncp_request_val), G_ALLOC_AND_FREE);
518 }
519
520 static struct ncp2222_record *
521 ncp2222_find(guint8 func, guint8 subfunc)
522 {
523         struct ncp2222_record *ncp_record, *retval = NULL;
524
525         ncp_record = ncp2222;
526
527         while(ncp_record->func != 0 || ncp_record->subfunc != 0 ||
528                 ncp_record->funcname != NULL ) {
529                 if (ncp_record->func == func &&
530                         ncp_record->subfunc == (subfunc & ncp_record->submask)) {
531                         retval = ncp_record;
532                         break;
533                 }
534                 ncp_record++;
535         }
536
537         return retval;
538 }
539
540 void
541 dissect_ncp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
542
543         proto_tree      *ncp_tree = NULL;
544         proto_item      *ti;
545         int             ncp_hdr_length = 0;
546         struct ncp_ip_header            ncpiph;
547         struct ncp_ip_rqhdr             ncpiphrq;
548         struct ncp_common_header        header;
549
550         if ( pi.ptype == PT_TCP || pi.ptype == PT_UDP ) {
551                 ncpiph.signature = pntohl(&pd[offset]);
552                 ncpiph.length = pntohl(&pd[offset + 4]);
553                 offset += 8;
554                 if ( ncpiph.signature == NCPIP_RQST ) {
555                         ncpiphrq.version = pntohl(&pd[offset]);
556                         ncpiphrq.rplybufsize = pntohl(&pd[offset + 4]);
557                         offset += 8;
558                 };
559         };
560
561         memcpy(&header, &pd[offset], sizeof(header));
562         header.type = ntohs(header.type);
563
564         if (header.type == 0x1111 ||
565                         header.type == 0x2222 ||
566                         header.type == 0x5555 ||
567                         header.type == 0x7777) {
568                 ncp_hdr_length = 7;
569         }
570         else if (header.type == 0x3333 || header.type == 0x9999) {
571                 ncp_hdr_length = 8;
572         }
573
574         if (check_col(fd, COL_PROTOCOL))
575                 col_add_str(fd, COL_PROTOCOL, "NCP");
576
577         nw_connection = (header.conn_high << 16) + header.conn_low;
578         nw_sequence = header.sequence;
579         nw_ncp_type = header.type;
580
581         if (tree) {
582                 ti = proto_tree_add_item(tree, proto_ncp, offset, END_OF_FRAME, NULL);
583                 ncp_tree = proto_item_add_subtree(ti, ett_ncp);
584
585                 if ( pi.ptype == PT_TCP || pi.ptype == PT_UDP ) {
586                         proto_tree_add_item(ncp_tree, hf_ncp_ip_sig, offset - 16, 4, ncpiph.signature);
587                         proto_tree_add_text(ncp_tree, offset - 12, 4, "Length: %d", ncpiph.length);
588                         if ( ncpiph.signature == NCPIP_RQST ) {
589                                 proto_tree_add_item(ncp_tree, hf_ncp_ip_ver, offset - 8, 4, ncpiphrq.version);
590                                 proto_tree_add_text(ncp_tree, offset - 4, 4, "Reply buffer size: %d", ncpiphrq.rplybufsize);
591                         };
592                 };
593                 proto_tree_add_item_format(ncp_tree, hf_ncp_type, 
594                                            offset,      2,
595                                            header.type,
596                                            "Type: %s", 
597                                            val_to_str( header.type,
598                                                        request_reply_values,
599                                                        "Unknown (%04X)"));
600
601                 proto_tree_add_item(ncp_tree, hf_ncp_seq, 
602                                     offset+2,    1, header.sequence);
603
604                 proto_tree_add_item(ncp_tree, hf_ncp_connection,
605                                     offset+3,    3, nw_connection);
606
607                 proto_tree_add_item(ncp_tree, hf_ncp_task, 
608                                     offset+4,    1, header.task);
609         }
610
611         /* Note how I use ncp_tree *and* tree in my args for ncp request/reply */
612         if (ncp_hdr_length == 7)
613                 dissect_ncp_request(pd, offset, fd, ncp_tree, tree);
614         else if (ncp_hdr_length == 8)
615                 dissect_ncp_reply(pd, offset, fd, ncp_tree, tree);
616         else
617                 dissect_data(pd, offset, fd, tree);
618 }
619
620 void
621 dissect_ncp_request(const u_char *pd, int offset, frame_data *fd,
622         proto_tree *ncp_tree, proto_tree *tree) {
623
624         struct ncp_request_header       request;
625         struct ncp2222_record           *ncp_request;
626         gchar                           *description = "";
627         struct ncp_request_val          *request_val;
628         struct ncp_request_key          *request_key;
629         proto_tree                      *field_tree = NULL;
630         proto_item                      *ti = NULL;
631
632         /*memcpy(&request, &pd[offset], sizeof(request));*/
633         request.function = pd[offset+6];
634         if ( BYTES_ARE_IN_FRAME(offset, 9) ) {
635                 request.subfunc = pd[offset+9];
636         } else {
637                 request.subfunc = 0;
638         }
639
640         ncp_request = ncp2222_find(request.function, request.subfunc);
641
642         if (ncp_request)
643                 description = ncp_request->funcname;
644
645         if (check_col(fd, COL_INFO)) {
646                 if (description[0]) {
647                         col_add_fstr(fd, COL_INFO, "C %s", description);
648                 }
649                 else {
650                         col_add_fstr(fd, COL_INFO, "C Unknown Function %02X/%02X",
651                                 request.function, request.subfunc);
652                 }
653         }
654
655         if (ncp_tree) {
656                 proto_tree_add_text(ncp_tree, offset+6, 1,
657                         "Function Code: 0x%02X (%s)",
658                         request.function, description);
659
660                 if (ncp_request) {
661
662                         if (ncp_request->submask == SUBFUNC) {
663                                 proto_tree_add_text(ncp_tree, offset+7, 2,
664                                         "Packet Length: %d bytes", pntohs(&pd[offset+7]));
665                                 proto_tree_add_text(ncp_tree, offset+9, 1,
666                                         "Subfunction Code: 0x%02x", pd[offset+9]);
667                                 offset += 7 + 3;
668                         }
669                         else {
670                                 offset += 7;
671                         }
672
673                         if (ncp_request->req) {
674                                 ti = proto_tree_add_text(ncp_tree, offset, END_OF_FRAME,
675                                 "NCP Request Packet");
676                                 field_tree = proto_item_add_subtree(ti, ett_ncp_request_fields);
677
678                                 parse_ncp_svc_fields(pd, field_tree, offset, ncp_request->req);
679                         }
680                 }
681         }
682         else { /* ! tree */
683                 request_key = g_mem_chunk_alloc(ncp_request_keys);
684                 request_key->nw_server_address = nw_server_address;
685                 request_key->nw_connection = nw_connection;
686                 request_key->nw_sequence = nw_sequence;
687
688                 request_val = g_mem_chunk_alloc(ncp_request_records);
689                 request_val->ncp_type = nw_ncp_type;
690                 request_val->ncp_record = ncp2222_find(request.function, request.subfunc);
691
692                 g_hash_table_insert(ncp_request_hash, request_key, request_val);
693                 #if defined(DEBUG_NCP_HASH)
694                 printf("Inserted server %08X connection %d sequence %d (val=%08X)\n",
695                         nw_server_address, nw_connection, nw_sequence, request_val);
696                 #endif
697         }
698
699 }
700
701 void
702 dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd,
703         proto_tree *ncp_tree, proto_tree *tree) {
704
705         struct ncp_reply_header         reply;
706         struct ncp2222_record           *ncp_request = NULL;
707         struct ncp_request_val          *request_val;
708         struct ncp_request_key          request_key;
709         proto_tree                      *field_tree = NULL;
710         proto_item                      *ti = NULL;
711
712         memcpy(&reply, &pd[offset], sizeof(reply));
713
714         /* find the record telling us the request made that caused this reply */
715         request_key.nw_server_address = nw_server_address;
716         request_key.nw_connection = nw_connection;
717         request_key.nw_sequence = nw_sequence;
718
719         request_val = (struct ncp_request_val*)
720                 g_hash_table_lookup(ncp_request_hash, &request_key);
721
722         #if defined(DEBUG_NCP_HASH)
723         printf("Looking for server %08X connection %d sequence %d (retval=%08X)\n",
724                 nw_server_address, nw_connection, nw_sequence, request_val);
725         #endif
726
727         if (request_val)
728                 ncp_request = request_val->ncp_record;
729
730         if (check_col(fd, COL_INFO)) {
731                 if (reply.completion_code == 0) {
732                         col_add_fstr(fd, COL_INFO, "R OK");
733                 }
734                 else {
735                         col_add_fstr(fd, COL_INFO, "R Not OK");
736                 }
737         }
738
739         if (ncp_tree) {
740                 /* A completion code of 0 always means OK. Other values have different
741                  * meanings */
742                 if (ncp_request) {
743                         proto_tree_add_text(ncp_tree, offset+6,    1,
744                                 "Completion Code: 0x%02x (%s)", reply.completion_code,
745                                 ncp_completion_code(reply.completion_code, ncp_request->family));
746                 }
747                 else {
748                         proto_tree_add_text(ncp_tree, offset+6,    1,
749                                 "Completion Code: 0x%02x (%s)", reply.completion_code,
750                                 reply.completion_code == 0 ? "OK" : "Unknown");
751                 }
752
753                 proto_tree_add_text(ncp_tree, offset+7,    1,
754                         "Connection Status: %d", reply.connection_state);
755
756                 if (ncp_request) {
757
758                         if (ncp_request->rep) {
759                                 ti = proto_tree_add_text(ncp_tree, offset+8, END_OF_FRAME,
760                                 "NCP Reply Packet");
761                                 field_tree = proto_item_add_subtree(ti, ett_ncp_reply_fields);
762
763                                 parse_ncp_svc_fields(pd, field_tree, offset+8, ncp_request->rep);
764                         }
765                 }
766         }
767
768 }
769
770 /* Populates the protocol tree with information about the svc_record fields */
771 static void
772 parse_ncp_svc_fields(const u_char *pd, proto_tree *ncp_tree, int offset,
773         struct svc_record *svc)
774 {
775         struct svc_record *rec = svc;
776         int field_offset = offset;
777         int field_length = 0;
778
779         while (rec->type != nend) {
780                 switch(rec->type) {
781                         case nbeshort:
782                                 field_length = 2;
783                                 proto_tree_add_text(ncp_tree, field_offset,
784                                         field_length, rec->description, pntohs(&pd[field_offset]));
785                                 break;
786
787                         case nasciile:
788                                 field_length = pd[field_offset];
789                                 proto_tree_add_text(ncp_tree, field_offset,
790                                         field_length + 1, rec->description, field_length,
791                                         &pd[field_offset+1]);
792                                 break;
793
794                         case nhex:
795                                 field_length = rec->length;
796                                 proto_tree_add_text(ncp_tree, field_offset,
797                                         field_length, rec->description);
798                                 break;  
799
800                          default:
801                                 ; /* nothing */
802                                 break;
803                 }
804                 field_offset += field_length;
805                 rec++;
806         }       
807 }
808
809 static char*
810 ncp_completion_code(guint8 ccode, enum nfamily family)
811 {
812                 char    *text;
813
814 #define NCP_CCODE_MIN 0x7e
815 #define NCP_CCODE_MAX 0xff
816
817         /* From Appendix C of "Programmer's Guide to NetWare Core Protocol" */
818         static char     *ccode_text[] = {
819                 /* 7e */ "NCP boundary check failed",
820                 /* 7f */ "Unknown",
821                 /* 80 */ "Lock fail. The file is already open",
822                 /* 81 */ "A file handle could not be allocated by the file server",
823                 /* 82 */ "Unauthorized to open file",
824                 /* 83 */ "Unable to read/write the volume. Possible bad sector on the file server",
825                 /* 84 */ "Unauthorized to create the file",
826                 /* 85 */ "",
827                 /* 86 */ "Unknown",
828                 /* 87 */ "An unexpected character was encountered in the filename",
829                 /* 88 */ "FileHandle is not valid",
830                 /* 89 */ "Unauthorized to search this directory",
831                 /* 8a */ "Unauthorized to delete a file in this directory",
832                 /* 8b */ "Unauthorized to rename a file in this directory",
833                 /* 8c */ "Unauthorized to modify a file in this directory",
834                 /* 8d */ "Some of the affected files are in use by another client",
835                 /* 8e */ "All of the affected files are in use by another client",
836                 /* 8f */ "Some of the affected file are read only",
837                 /* 90 */ "",
838                 /* 91 */ "Some of the affected files already exist",
839                 /* 92 */ "All of the affected files already exist",
840                 /* 93 */ "Unauthorized to read from this file",
841                 /* 94 */ "Unauthorized to write to this file",
842                 /* 95 */ "The affected file is detached",
843                 /* 96 */ "The file server has run out of memory to service this request",
844                 /* 97 */ "Unknown",
845                 /* 98 */ "The affected volume is not mounted",
846                 /* 99 */ "The file server has run out of directory space on the affected volume",
847                 /* 9a */ "The request attempted to rename the affected file to another volume",
848                 /* 9b */ "DirHandle is not associated with a valid directory path",
849                 /* 9c */ "",
850                 /* 9d */ "A directory handle was not available for allocation",
851                 /* 9e */ "The filename does not conform to a legal name for this name space",
852                 /* 9f */ "The request attempted to delete a directory that is in use by another client",
853                 /* a0 */ "The request attempted to delete a directory that is not empty",
854                 /* a1 */ "An unrecoverable error occurred on the affected directory",
855                 /* a2 */ "The request attempted to read from a file region that is physically locked",
856                 /* a3 */ "Unknown",
857                 /* a4 */ "Unknown",
858                 /* a5 */ "Unknown",
859                 /* a6 */ "Unknown",
860                 /* a7 */ "Unknown",
861                 /* a8 */ "Unknown",
862                 /* a9 */ "Unknown",
863                 /* aa */ "Unknown",
864                 /* ab */ "Unknown",
865                 /* ac */ "Unknown",
866                 /* ad */ "Unknown",
867                 /* ae */ "Unknown",
868                 /* af */ "Unknown",
869                 /* b0 */ "Unknown",
870                 /* b1 */ "Unknown",
871                 /* b2 */ "Unknown",
872                 /* b3 */ "Unknown",
873                 /* b4 */ "Unknown",
874                 /* b5 */ "Unknown",
875                 /* b6 */ "Unknown",
876                 /* b7 */ "Unknown",
877                 /* b8 */ "Unknown",
878                 /* b9 */ "Unknown",
879                 /* ba */ "Unknown",
880                 /* bb */ "Unknown",
881                 /* bc */ "Unknown",
882                 /* bd */ "Unknown",
883                 /* be */ "Unknown",
884                 /* bf */ "Requests for this name space are not valid on this volume",
885                 /* c0 */ "Unauthorized to retrieve accounting data",
886                 /* c1 */ "The 'account balance' property does not exist",
887                 /* c2 */ "The object has exceeded its credit limit",
888                 /* c3 */ "Too many holds have been placed against this account",
889                 /* c4 */ "The account for this bindery object has been disabled",
890                 /* c5 */ "Access to the account has been denied because of intruder detections",
891                 /* c6 */ "The caller does not have operator privileges",
892                 /* c7 */ "Unknown",
893                 /* c8 */ "Unknown",
894                 /* c9 */ "Unknown",
895                 /* ca */ "Unknown",
896                 /* cb */ "Unknown",
897                 /* cc */ "Unknown",
898                 /* cd */ "Unknown",
899                 /* ce */ "Unknown",
900                 /* cf */ "Unknown",
901                 /* d0 */ "Queue error",
902                 /* d1 */ "The queue associated with Object ID does not exist",
903                 /* d2 */ "A queue server is not associated with the selected queue",
904                 /* d3 */ "No queue rights",
905                 /* d4 */ "The queue associated with Object ID is full and cannot accept another request",
906                 /* d5 */ "The job associated with Job Number does not exist in this queue",
907                 /* d6 */ "",
908                 /* d7 */ "",
909                 /* d8 */ "Queue not active",
910                 /* d9 */ "",
911                 /* da */ "",
912                 /* db */ "",
913                 /* dc */ "Unknown",
914                 /* dd */ "Unknown",
915                 /* de */ "Attempted to login to the file server with an incorrect password",
916                 /* df */ "Attempted to login to the file server with a password that has expired",
917                 /* e0 */ "Unknown",
918                 /* e1 */ "Unknown",
919                 /* e2 */ "Unknown",
920                 /* e3 */ "Unknown",
921                 /* e4 */ "Unknown",
922                 /* e5 */ "Unknown",
923                 /* e6 */ "Unknown",
924                 /* e7 */ "No disk track",
925                 /* e8 */ "",
926                 /* e9 */ "Unknown",
927                 /* ea */ "The bindery object is not a member of this set",
928                 /* eb */ "The property is not a set property",
929                 /* ec */ "The set property does not exist",
930                 /* ed */ "The property already exists",
931                 /* ee */ "The bindery object already exists",
932                 /* ef */ "Illegal characters in Object Name field",
933                 /* f0 */ "A wildcard was detected in a field that does not support wildcards",
934                 /* f1 */ "The client does not have the rights to access this bindery objecs",
935                 /* f2 */ "Unauthorized to read from this object",
936                 /* f3 */ "Unauthorized to rename this object",
937                 /* f4 */ "Unauthorized to delete this object",
938                 /* f5 */ "Unauthorized to create this object",
939                 /* f6 */ "Unauthorized to delete the property of this object",
940                 /* f7 */ "Unauthorized to create this property",
941                 /* f8 */ "Unauthorized to write to this property",
942                 /* f9 */ "Unauthorized to read this property",
943                 /* fa */ "Temporary remap error",
944                 /* fb */ "",
945                 /* fc */ "",
946                 /* fd */ "",
947                 /* fe */ "",
948                 /* ff */ ""
949         };
950
951         switch (ccode) {
952                 case 0:
953                         return "OK";
954                         break;
955
956                 case 3:
957                         return "Client not accepting messages";
958                         break;
959         }
960
961         if (ccode >= NCP_CCODE_MIN && ccode <= NCP_CCODE_MAX) {
962                 text = ccode_text[ccode - NCP_CCODE_MIN];
963                 /* If there really is text, return it */
964                 if (text[0] != 0)
965                         return text;
966         }
967         else {
968                 return "Unknown";
969         }
970
971         /* We have a completion code with multiple translations. We'll use the
972          * nfamily that this request type belongs to to give the right
973          * translation.
974          */
975         switch (ccode) {
976
977                 case 0xfc:
978                         switch(family) {
979                                 case NCP_QUEUE_SERVICES:
980                                         return "The message queue cannot accept another message";
981                                         break;
982                                 case NCP_BINDERY_SERVICES:
983                                         return "The specified bindery object does not exist";
984                                         break;
985                                 default:
986                                         return "Unknown";
987                                         break;
988                         }
989                         break;
990
991                 default:
992                         return "I don't know how to parse this completion code. Please send this packet trace to Gilbert Ramirez <gram@xiexie.org> for analysis";
993         }
994 }
995
996 void
997 proto_register_ncp(void)
998 {
999
1000   static hf_register_info hf[] = {
1001     { &hf_ncp_ip_sig,
1002       { "NCP over IP signature",                "ncp.ip.signature",
1003         FT_UINT32, BASE_HEX, VALS(ncp_ip_signature), 0x0,
1004         "NCP over IP transport signature"}},
1005     { &hf_ncp_ip_ver,
1006       { "Version",              "ncp.ip.version",
1007         FT_UINT32, BASE_DEC, NULL, 0x0,
1008         "NCP over IP verion"}},
1009     { &hf_ncp_type,
1010       { "Type",                 "ncp.type",
1011         FT_UINT16, BASE_HEX, NULL, 0x0,
1012         "NCP message type" }},
1013     { &hf_ncp_seq,
1014       { "Sequence Number",      "ncp.seq",
1015         FT_UINT8, BASE_DEC, NULL, 0x0,
1016         "" }},
1017     { &hf_ncp_connection,
1018       { "Connection Number",    "ncp.connection",
1019         FT_UINT16, BASE_DEC, NULL, 0x0,
1020         "" }},
1021     { &hf_ncp_task,
1022       { "Task Number",          "ncp.task",
1023         FT_UINT8, BASE_DEC, NULL, 0x0,
1024         "" }}
1025   };
1026   static gint *ett[] = {
1027     &ett_ncp,
1028     &ett_ncp_request_fields,
1029     &ett_ncp_reply_fields,
1030   };
1031
1032   proto_ncp = proto_register_protocol("NetWare Core Protocol", "ncp");
1033   proto_register_field_array(proto_ncp, hf, array_length(hf));
1034   proto_register_subtree_array(ett, array_length(ett));
1035   register_init_routine(&ncp_init_protocol);
1036 }